Merge "Change NetworkStack Permission Test file owner" into qt-dev
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index d85f067..2513993e 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -25,5 +25,7 @@
tests/tests/view/
tests/tests/widget/
common/device-side/util/
+ hostsidetests/stagedinstall/
+ tests/tests/packageinstaller/atomicinstall/
ktlint_hook = ${REPO_ROOT}/prebuilts/ktlint/ktlint.py -f ${PREUPLOAD_FILES}
diff --git a/apps/CameraITS/tests/scene1/test_param_shading_mode.py b/apps/CameraITS/tests/scene1/test_param_shading_mode.py
index ee32bd4..a65c630 100644
--- a/apps/CameraITS/tests/scene1/test_param_shading_mode.py
+++ b/apps/CameraITS/tests/scene1/test_param_shading_mode.py
@@ -24,7 +24,9 @@
import numpy
NAME = os.path.basename(__file__).split('.')[0]
+NUM_FRAMES = 4 # number of frames for temporal info to settle
NUM_SHADING_MODE_SWITCH_LOOPS = 3
+SHADING_MODES = ['OFF', 'FAST', 'HQ']
THRESHOLD_DIFF_RATIO = 0.15
@@ -59,13 +61,14 @@
# Get the reference lens shading maps for OFF, FAST, and HIGH_QUALITY
# in different sessions.
# reference_maps[mode]
- reference_maps = [[] for mode in range(3)]
+ num_shading_modes = len(SHADING_MODES)
+ reference_maps = [[] for mode in range(num_shading_modes)]
num_map_gains = 0
- for mode in range(1, 3):
+ for mode in range(1, num_shading_modes):
req = its.objects.auto_capture_request()
req['android.statistics.lensShadingMapMode'] = 1
req['android.shading.mode'] = mode
- cap_res = cam.do_capture(req)['metadata']
+ cap_res = cam.do_capture([req]*NUM_FRAMES)[NUM_FRAMES-1]['metadata']
lsc_map = cap_res['android.statistics.lensShadingCorrectionMap']
assert(lsc_map.has_key('width') and
lsc_map.has_key('height') and
@@ -79,25 +82,26 @@
# Get the lens shading maps while switching modes in one session.
reqs = []
for i in range(NUM_SHADING_MODE_SWITCH_LOOPS):
- for mode in range(3):
- req = its.objects.auto_capture_request()
- req['android.statistics.lensShadingMapMode'] = 1
- req['android.shading.mode'] = mode
- reqs.append(req)
+ for mode in range(num_shading_modes):
+ for _ in range(NUM_FRAMES):
+ req = its.objects.auto_capture_request()
+ req['android.statistics.lensShadingMapMode'] = 1
+ req['android.shading.mode'] = mode
+ reqs.append(req)
caps = cam.do_capture(reqs)
# shading_maps[mode][loop]
shading_maps = [[[] for loop in range(NUM_SHADING_MODE_SWITCH_LOOPS)]
- for mode in range(3)]
+ for mode in range(num_shading_modes)]
# Get the shading maps out of capture results
- for i in range(len(caps)):
- shading_maps[i % 3][i / 3] = \
- caps[i]['metadata']['android.statistics.lensShadingCorrectionMap']['map']
+ for i in range(len(caps)/NUM_FRAMES):
+ shading_maps[i%num_shading_modes][i/NUM_SHADING_MODE_SWITCH_LOOPS] = \
+ caps[(i+1)*NUM_FRAMES-1]['metadata']['android.statistics.lensShadingCorrectionMap']['map']
# Draw the maps
- for mode in range(3):
+ for mode in range(num_shading_modes):
for i in range(NUM_SHADING_MODE_SWITCH_LOOPS):
pylab.clf()
pylab.figure(figsize=(5, 5))
@@ -128,15 +132,16 @@
pylab.tight_layout()
matplotlib.pyplot.savefig('%s.png' % name)
- print 'Verifying lens shading maps with mode OFF are all 1.0'
- for i in range(NUM_SHADING_MODE_SWITCH_LOOPS):
- assert numpy.allclose(shading_maps[0][i], reference_maps[0])
-
- for mode in range(1, 3):
- print 'Verifying lens shading maps with mode', mode, 'are similar'
+ for mode in range(num_shading_modes):
+ if mode == 0:
+ print 'Verifying lens shading maps with mode %s are all 1.0' % (
+ SHADING_MODES[mode])
+ else:
+ print 'Verifying lens shading maps with mode %s are similar' % (
+ SHADING_MODES[mode])
for i in range(NUM_SHADING_MODE_SWITCH_LOOPS):
- e_msg = 'FAIL mode: %d, loop: %d, THRESH: %.2f' % (
- mode, i, THRESHOLD_DIFF_RATIO)
+ e_msg = 'FAIL mode: %s, loop: %d, THRESH: %.2f' % (
+ SHADING_MODES[mode], i, THRESHOLD_DIFF_RATIO)
assert (numpy.allclose(shading_maps[mode][i],
reference_maps[mode],
rtol=THRESHOLD_DIFF_RATIO)), e_msg
diff --git a/apps/CtsVerifier/Android.mk b/apps/CtsVerifier/Android.mk
index 50f9d5a..af6dc5b0 100644
--- a/apps/CtsVerifier/Android.mk
+++ b/apps/CtsVerifier/Android.mk
@@ -23,7 +23,8 @@
LOCAL_MULTILIB := both
-LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-Iaidl-files-under, src)
+LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-Iaidl-files-under, src) \
+ ../ForceStopHelperApp/src/com/android/cts/forcestophelper/Constants.java
LOCAL_AIDL_INCLUDES := \
frameworks/native/aidl/gui
@@ -109,6 +110,7 @@
CtsEmptyDeviceAdmin \
CtsEmptyDeviceOwner \
CtsPermissionApp \
+ CtsForceStopHelper \
NotificationBot
# Apps to be installed as Instant App using adb install --instant
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index c2af735..62d297e 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -22,10 +22,12 @@
<uses-sdk android:minSdkVersion="19" android:targetSdkVersion="28"/>
+ <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"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
+ <uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BODY_SENSORS"/>
@@ -152,6 +154,16 @@
android:theme="@style/OverlayTheme"
android:label="Overlaying Activity"/>
+ <activity android:name=".forcestop.RecentTaskRemovalTestActivity"
+ android:label="@string/remove_from_recents_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_required_configs" android:value="config_has_recents"/>
+ </activity>
+
<activity android:name=".companion.CompanionDeviceTestActivity"
android:label="@string/companion_test"
android:configChanges="keyboardHidden|orientation|screenSize">
@@ -2582,6 +2594,24 @@
android:value="android.hardware.type.television:android.software.leanback" />
</activity>
-->
+
+ <activity
+ android:name="com.android.cts.verifier.sensors.StepSensorPermissionTestActivity"
+ android:label="@string/snsr_step_permission_test"
+ android:screenOrientation="nosensor" >
+ <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_sensors" />
+ <meta-data android:name="test_required_features"
+ android:value="android.hardware.sensor.stepcounter:android.hardware.sensor.stepdetector" />
+ </activity>
+
<activity
android:name="com.android.cts.verifier.sensors.DeviceSuspendTestActivity"
android:label="@string/snsr_device_suspend_test"
@@ -3594,10 +3624,12 @@
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.DIAL" />
+ <category android:name="android.intent.category.DEFAULT" />
<data android:scheme="tel" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.DIAL" />
+ <category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<meta-data android:name="test_category" android:value="@string/test_category_telephony"/>
<meta-data
@@ -3864,6 +3896,22 @@
</intent-filter>
<meta-data android:name="test_category" android:value="@string/test_category_instant_apps" />
</activity>
+ <activity android:name=".instantapps.RecentAppsTestActivity"
+ android:label="@string/ia_recents">
+ <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_instant_apps" />
+ </activity>
+ <activity android:name=".instantapps.AppInfoTestActivity"
+ android:label="@string/ia_app_info">
+ <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_instant_apps" />
+ </activity>
</application>
</manifest>
diff --git a/apps/CtsVerifier/res/layout/ci_main.xml b/apps/CtsVerifier/res/layout/ci_main.xml
index 21235b0..b9647f3 100644
--- a/apps/CtsVerifier/res/layout/ci_main.xml
+++ b/apps/CtsVerifier/res/layout/ci_main.xml
@@ -70,6 +70,14 @@
android:enabled="false"
android:text="@string/ci_start_test_button_caption" />
+ <Button
+ android:id="@+id/settings_button"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:enabled="false"
+ android:text="@string/ci_settings_button_caption" />
+
+
</LinearLayout>
</LinearLayout>
<include layout="@layout/pass_fail_buttons" />
diff --git a/apps/CtsVerifier/res/layout/force_stop_recents_main.xml b/apps/CtsVerifier/res/layout/force_stop_recents_main.xml
new file mode 100644
index 0000000..ef5664e
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/force_stop_recents_main.xml
@@ -0,0 +1,151 @@
+<?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"
+ style="@style/RootLayoutPadding"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <!-- Install test app -->
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <ImageView
+ android:id="@+id/fs_test_app_install_status"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentTop="true"
+ android:layout_marginTop="10dip"
+ android:padding="10dip"/>
+
+ <TextView
+ android:id="@+id/fs_test_app_install_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/fs_test_app_install_status"
+ android:layout_marginTop="10dip"
+ android:text="@string/fs_test_app_install_instructions"/>
+ </RelativeLayout>
+
+ <!-- Launch test activity -->
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <ImageView
+ android:id="@+id/fs_test_app_launch_status"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentTop="true"
+ android:layout_marginTop="10dip"
+ android:padding="10dip"/>
+
+ <TextView
+ android:id="@+id/fs_test_app_launch_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/fs_test_app_launch_status"
+ android:layout_marginTop="10dip"
+ android:text="@string/fs_test_app_launch_instructions"/>
+
+ <Button
+ android:id="@+id/fs_launch_test_app_button"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true"
+ android:layout_below="@id/fs_test_app_launch_instructions"
+ android:layout_marginTop="10dip"
+ android:layout_marginLeft="20dip"
+ android:layout_marginRight="20dip"
+ android:layout_toRightOf="@id/fs_test_app_launch_status"
+ android:text="@string/fs_launch_test_app_button_text"/>
+ </RelativeLayout>
+
+ <!-- Remove test activity task from recents -->
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <ImageView
+ android:id="@+id/fs_test_app_recents_status"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentTop="true"
+ android:layout_marginTop="10dip"
+ android:padding="10dip"
+ android:visibility="visible"/>
+
+ <TextView
+ android:id="@+id/fs_test_app_recents_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/fs_test_app_recents_status"
+ android:layout_marginTop="10dip"
+ android:visibility="visible"
+ android:text="@string/fs_test_app_recents_instructions"/>
+ </RelativeLayout>
+
+ <!-- Verify that app wasn't force-stopped -->
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <ImageView
+ android:id="@+id/fs_force_stop_status"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentTop="true"
+ android:layout_marginTop="10dip"
+ android:visibility="gone"
+ android:padding="10dip"/>
+
+ <TextView
+ android:id="@+id/fs_force_stop_verification"
+ 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/fs_force_stop_status"
+ android:layout_marginTop="10dip"
+ android:visibility="gone"
+ android:text="@string/fs_force_stop_verification_pending"/>
+ </RelativeLayout>
+ </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/layout/instant_apps.xml b/apps/CtsVerifier/res/layout/instant_apps.xml
index f41250c..b0bc3ff 100644
--- a/apps/CtsVerifier/res/layout/instant_apps.xml
+++ b/apps/CtsVerifier/res/layout/instant_apps.xml
@@ -49,8 +49,7 @@
<TextView
android:id="@+id/instruction_extra_text"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/ia_notification_instruction_label" />
+ android:layout_height="wrap_content" />
<Button
android:id="@+id/start_test_button"
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index dac1778..706335e 100755
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -50,7 +50,7 @@
<string name="test_results_cleared">Test results cleared.</string>
<string name="view">View</string>
<string name="test_results_error">Couldn\'t create test results report.</string>
- <string name="runtime_permissions_error">Cannot continue. Please grant runtime permissions</string>
+ <string name="runtime_permissions_error">Please grant runtime permissions, otherwise, tests might fail.</string>
<string name="export">Export</string>
<string name="no_storage">Cannot save report to external storage, see log for details.</string>
<string name="report_saved">Report saved to: %s</string>
@@ -62,6 +62,9 @@
<!-- Strings for ReportViewerActivity -->
<string name="report_viewer">Report Viewer</string>
+ <string name="result_success">Test passed!</string>
+ <string name="result_failure">Test failed!</string>
+
<!-- String shared between BackupTestActivity and BackupAccessibilityTestActivity -->
<string name="bu_loading">Loading...</string>
<string name="bu_generate_error">Error occurred while generating test data...</string>
@@ -150,6 +153,24 @@
</string>
<string name="da_tapjacking_button_text">Enable device admin</string>
+ <!-- Strings for RecentTaskRemovalTestActivity -->
+ <string name="remove_from_recents_test">Recent Task Removal Test</string>
+ <string name="remove_from_recents_test_info">
+ This test verifies that an app whose task is removed from recents is not also force-stopped
+ without explicit user consent. This test requires CtsForceStopHelper.apk to be installed.
+ </string>
+ <string name="fs_test_app_install_instructions">Please install the \'Force stop helper app\' on the device.</string>
+ <string name="fs_test_app_installed_text">\'Force stop helper app\' installed on device. Proceed to the following steps.</string>
+ <string name="fs_test_app_launch_instructions">
+ Tap the button to launch the helper activity. Then return to this screen.
+ </string>
+ <string name="fs_launch_test_app_button_text">Launch test activity</string>
+ <string name="fs_test_app_recents_instructions">
+ Open recents and remove the task of the activity started in the previous step and return to this screen.
+ Deny any dialog that is shown asking for permission to force-stop or kill the app.
+ </string>
+ <string name="fs_force_stop_verification_pending">Verifying... Please wait.</string>
+
<!-- Strings for BiometricTest -->
<string name="biometric_test">Biometric Test</string>
<string name="biometric_test_info">
@@ -854,9 +875,6 @@
<string name="nfc_reading_tag">Reading NFC tag...</string>
<string name="nfc_reading_tag_error">Error reading NFC tag...</string>
- <string name="nfc_result_success">Test passed!</string>
- <string name="nfc_result_failure">Test failed!</string>
-
<string name="nfc_result_message">Written data:\n%1$s\n\nRead data:\n%2$s</string>
<string name="nfc_ndef_content">Id: %1$s\nMime: %2$s\nPayload: %3$s</string>
@@ -1099,6 +1117,12 @@
<string name="snsr_step_counter_event">%1$d | Step Counter event. count=%2$d.</string>
<string name="snsr_step_detector_event">%1$d | Step Detector event.</string>
+ <!-- Step Counter and Detector Permission -->
+ <string name="snsr_step_permission_test">Step Permission Test</string>
+ <string name="snsr_step_permission_disable">Please change the \'Physical Activity\' permission for CtsVerifier to \'Deny\'</string>
+ <string name="snsr_step_permission_enable">Please change the \'Physical Activity\' permission for CtsVerifier to \'Allow\'</string>
+ <string name="snsr_step_permission_walk">Please begin walking while holding the device. A sound will play when you may stop walking</string>
+
<!-- Device suspend tests -->
<string name="snsr_device_suspend_test">Device Suspend Tests</string>
<string name="snsr_device_did_not_go_into_suspend">Device did not go into suspend mode during test execution </string>
@@ -1220,8 +1244,12 @@
This test verifies that the default camera app is firing intents
after pictures/videos are taken. It also verifies that when the
default camera app is invoked via intents, the launch intents work,
- and the broadcast intents are received when appropriate per the SDK
- documentation.\n\n
+ ,broadcast intents are received when appropriate per the SDK
+ documentation and also, that the intent results do not have location
+ information in them. Before starting with the tests, please go to the
+ Settings app and deny location permissions to the CtsVerifier app, and
+ after finishing with the camera intent tests, please go to the Settings app and
+ restore location permissions, otherwise, other tests may fail.\n\n
- Read the message above the \"Start Test\" button for
step-by-step instructions.
</string>
@@ -1231,16 +1259,21 @@
<string name="ci_intents_label">Intents Test</string>
<string name="ci_intents_direction_label">clockwise</string>
<string name="ci_instruction_heading_label">Instructions:</string>
+ <string name="ci_location_permissions_error">Please give CTS Verifier location permissions before clicking on the pass / fail button</string>
+ <string name="ci_location_permissions_fail_error">Please give CTS Verifier location permissions if the fail button needs to be clicked</string>
+ <string name="ci_directory_creation_error">CTS Verifier debug directory could not be created, please try again</string>
<string name="ci_instruction_text_photo_label">READ BEFORE STARTING TEST</string>
<string name="ci_instruction_text_passfail_label">Choose \"Pass\" if the right intent is fired after taking a photo from the camera app. Otherwise, choose \"Fail\".</string>
<string name="ci_instruction_text_app_picture_label">\n
- 1. Click Start Test. \n
- 2. Go to home screen (HOME key). \n
- 3. Launch Camera application. \n
- 4. Capture photo within 1 minute. \n
- 5. Return to CTS verifier app. \n
- 6. Pass button will light up if URI trigger was fired.\n
- 7. Click "Pass" if possible.
+
+ 1. Click Open Settings and deny location permissions to CTS Verifier and return. \n
+ 2. Click Start Test. \n
+ 3. Go to home screen (HOME key). \n
+ 4. Launch Camera application. \n
+ 5. Capture photo within 1 minute. \n
+ 6. Return to CTS verifier app. \n
+ 7. Pass button will light up if URI trigger was fired.\n
+ 8. Click "Pass" if possible, otherwise open settings app, allow location again and click "Fail".
</string>
<string name="ci_instruction_text_app_video_label">\n
1. Click Start Test. \n
@@ -1249,23 +1282,25 @@
4. Capture video within 1 minute. \n
5. Return to CTS verifier app. \n
6. Pass button will light up if URI trigger was fired.\n
- 7. Click "Pass" if possible.
+ 7. Click "Pass" if possible, otherwise open settings app, allow location again and click "Fail".
</string>
<string name="ci_instruction_text_intent_picture_label">\n
1. Click Start Test.\n
2. Camera app will launch, prompting to take photo.\n
3. Capture/confirm photo using camera app controls within 1 minute.\n
4. Pass button will light up if URI trigger was NOT received.\n
- 5. Click "Pass" if possible.
+ 5. Click "Pass" if possible, otherwise open settings app, allow location again and click "Fail".
</string>
<string name="ci_instruction_text_intent_video_label">\n
1. Click Start Test.\n
2. Camera app will launch, prompting to take video.\n
3. Capture/confirm video using camera app controls within 1 minute.\n
- 4. Pass button will light up if URI trigger was received.\n
+ 4. Return to the CTS Verifier app. Click Open Settings and give back CTS Verifier location permissions \n
+ (Note this must be done before clicking on Pass / Fail).\n
5. Click "Pass" if possible.
</string>
<string name="ci_start_test_button_caption">Start Test</string>
+ <string name="ci_settings_button_caption">Open Settings</string>
<!-- Strings for Camera Formats -->
<string name="camera_format">Camera Formats</string>
@@ -2228,7 +2263,7 @@
3. Verify that the background image contains a suitcase.\n
4. Verify that the background color of the remaining image is blue.\n
5. Verify that the header text says \"CtsVerifier\".\n
- 6. Confirm your credentials and verify that the credentials you entered previously work.
+ 6. Confirm your credentials and verify that the credentials you entered previously work.\n
7. The work app should be launched.
</string>
<string name="provisioning_byod_confirm_work_credentials_header">
@@ -2240,10 +2275,11 @@
1. Verify that you get sent to the page for Choosing a new work lock.\n
2. Set a pattern lock.\n
- 3. Open a work app.\n
- 4. Verify that a screen asking you for your work credentials is shown.\n
- 5. Confirm your credentials and verify that the credentials you entered previously work.\n
- 6. The work app should be launched.
+ 3. Press the power button to turn the screen off and then back on and swipe to unlock.\n
+ 4. Open a work app.\n
+ 5. Verify that a screen asking you for your work credentials is shown.\n
+ 6. Confirm your credentials and verify that the credentials you entered previously work.\n
+ 7. The work app should be launched.
</string>
<string name="provisioning_byod_recents">Recents redaction test</string>
<string name="provisioning_byod_recents_info">
@@ -2475,7 +2511,7 @@
1. Please press the Go button to start the provisioning.\n
2. Press \"Accept and continue\" button to start work profile provisioning\n
3. Check that the CtsVerifier logo is displayed during provisioning\n
- 4. After successful provisioning, you should be automatically redirected back to this page
+ 4. After successful provisioning, come back to this page. You might need to press a button on the final provisioning screen.
</string>
<string name="provisioning_tests_byod_custom_terms">Custom terms</string>
<string name="provisioning_tests_byod_custom_terms_instructions">
@@ -3522,7 +3558,7 @@
<string name="dummy_accessibility_service_label">Dummy accessibility service</string>
<string name="policy_transparency_test_instructions">
1. <xliff:g id="set_step" example="Set policy by turning the switch on">%1$s</xliff:g>\n
- 2. Open Settings app by clicking the "Open settings" button below. (If this device has a separate app for work settings, ignore the button and open that app manually from the launcher, if necessary)\n
+ 2. Open Settings app by clicking the "Open settings" button below. Note that this will open the personal Settings app. If this device has the work settings in a different app or screen, then navigate there, ignoring the button if necessary.\n
3. Verify that performing the following action will trigger a support dialog:\n
<xliff:g id="user_action" example="Adding an account">%2$s</xliff:g>.\n
4. Verify that the support dialog displays the short support message set earlier.\n
@@ -4296,7 +4332,7 @@
<string name="error_screen_pinning_did_not_exit">Screen was not unpinned.</string>
<string name="error_screen_pinning_couldnt_exit">Could not exit screen pinning through API.</string>
- <!-- Audio Devices Notifcations Tests -->
+ <!-- Audio Devices Notifications Tests -->
<string name="audio_out_devices_notifications_test">Audio Output Devices Notifications Test</string>
<string name="audio_out_devices_notification_instructions">
Click the "Clear Messages" button then connect and disconnect a wired headset.
@@ -4950,7 +4986,9 @@
<string name="bubbles_notification_title">Bubble Notification Tests</string>
<string name="bubbles_notification_description">This test checks the behaviour of bubble
notifications, ensuring that notifications appear or disappear appropriately based on how
- BubbleMetadata is configured on the notification and user actions.</string>
+ BubbleMetadata is configured on the notification and user actions. Bubbles are special
+ notifications that appear as a floating button on the screen, in addition to the notification
+ in the notification shade.</string>
<string name="bubbles_notification_test_title_1">Step 1: send a bubble with notification</string>
<string name="bubbles_notification_test_verify_1">Click the button below and verify that there is a
bubble on the screen and a notification in the notification shade.
@@ -4980,6 +5018,23 @@
<string name="bubbles_notification_test_verify_6">Tap on the bubble to open it, then tap on the
bubble again to collapse it and return to this screen. Verify that after opening the bubble,
there is no longer a notification for it visible in the notification shade.</string>
+ <string name="bubbles_notification_test_title_7">Step 7: drag and dismiss bubble</string>
+ <string name="bubbles_notification_test_verify_7">Click the button below and verify that the bubble
+ is still on screen, and the notification is visible in the notification shade.\n\n
+ Drag the bubble, while dragging a UI affordance should show. Verify that: \n\n
+ 1. Dragging and dropping the bubble on that UI affordance removes it from the screen.\n\n
+ 2. The notification should remain in the notification shade.</string>
+ <string name="bubbles_notification_test_button_7">Update bubble to show notification</string>
+ <string name="bubbles_notification_test_title_8">Step 8: dismiss notification</string>
+ <string name="bubbles_notification_test_verify_8">Click the button below and verify that a bubble
+ appears on screen, and the notification is visible in the notification shade.\n\n
+ Dismiss the notification from the notification shade and verify that the bubble remains on
+ screen.</string>
+ <string name="bubbles_notification_test_button_8">Send bubble notification</string>
+ <string name="bubbles_notification_test_title_9">Step 9: auto expand bubble</string>
+ <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_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>
@@ -5009,4 +5064,32 @@
\u0020\u0020\u0020b. It provides an action allowing the user to launch the associated link with web browser. \n\n
5. Click Pass button if all checks in step 4 passed, otherwise click Fail button.
</string>
+ <string name="ia_recents">Instant Apps Recents Test</string>
+ <string name="ia_recents_info">
+ This test verifies that Instant App can be accessed in Recent Apps screen.\n\n
+ - Read the message above the \"Start Test\" button for
+ step-by-step instructions.
+ </string>
+ <string name="ia_recents_instruction_label">\n
+ 1. Click Start Test. \n\n
+ 2. An alert dialog with install instruction will be shown if the sample Instant App has not been installed, otherwise, the sample Instant App will be opened automatically. \n\n
+ 3. Tap Recents button, verify the sample Instant App is shown in Recent Apps screen. \n\n
+ 4. Tap the sample Instant App in Recent Apps screen, verify the Instant App is opened in the foreground. \n\n
+ 5. Click Pass button if checks in step 3 and 4 passed, otherwise click Fail button.
+ </string>
+ <string name="ia_app_info">View/Delete Instant Apps Test</string>
+ <string name="ia_app_info_info">
+ This test verifies that Instant App can be viewed and deleted in Settings.\n\n
+ - Read the message above the \"Start Test\" button for
+ step-by-step instructions.
+ </string>
+ <string name="ia_app_info_instruction_label">\n
+ 1. Click Start Test. \n\n
+ 2. An alert dialog with install instruction will be shown if the sample Instant App has not been installed, otherwise, the sample Instant App will be opened automatically. \n\n
+ 3. Open Settings App. \n\n
+ 4. Tap Apps & Notifications. \n\n
+ 5. Verify the sample Instant App can be found and it is shown as \"Instant App\". \n\n
+ 6. Verify there is an action allowing user to clear/delete app. \n\n
+ 7. Click Pass button if checks in step 5 and 6 passed, otherwise click Fail button.
+ </string>
</resources>
diff --git a/apps/CtsVerifier/res/xml/filepaths.xml b/apps/CtsVerifier/res/xml/filepaths.xml
index c33bd0e..6abe165 100644
--- a/apps/CtsVerifier/res/xml/filepaths.xml
+++ b/apps/CtsVerifier/res/xml/filepaths.xml
@@ -1,5 +1,6 @@
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<files-path path="images/" name="images" />
+ <files-path path="debug" name="debug/" />
<external-path name="external_files" path="."/>
<root-path name="external_files" path="/data/local/tmp/" />
</paths>
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/ManifestTestListAdapter.java b/apps/CtsVerifier/src/com/android/cts/verifier/ManifestTestListAdapter.java
index a339a43..47514b8 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/ManifestTestListAdapter.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/ManifestTestListAdapter.java
@@ -21,6 +21,7 @@
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
import android.os.Bundle;
import android.telephony.TelephonyManager;
import android.util.Log;
@@ -31,7 +32,6 @@
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
-import java.util.Iterator;
import java.util.List;
import java.util.Map;
@@ -104,6 +104,8 @@
private static final String CONFIG_VOICE_CAPABLE = "config_voice_capable";
+ private static final String CONFIG_HAS_RECENTS = "config_has_recents";
+
private final HashSet<String> mDisabledTests;
private Context mContext;
@@ -326,14 +328,23 @@
private boolean matchAllConfigs(String[] configs) {
if (configs != null) {
- TelephonyManager telephonyManager =
- (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
for (String config : configs) {
- switch(config) {
+ switch (config) {
case CONFIG_VOICE_CAPABLE:
+ TelephonyManager telephonyManager = mContext.getSystemService(
+ TelephonyManager.class);
if (!telephonyManager.isVoiceCapable()) {
return false;
}
+ break;
+ case CONFIG_HAS_RECENTS:
+ final Resources systemRes = mContext.getResources().getSystem();
+ final int id = systemRes.getIdentifier("config_hasRecents", "bool",
+ "android");
+ if (id == Resources.ID_NULL || !systemRes.getBoolean(id)) {
+ return false;
+ }
+ break;
default:
break;
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/camera/intents/CameraIntentsActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/camera/intents/CameraIntentsActivity.java
index 161cceb..8adb8a4 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/camera/intents/CameraIntentsActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/camera/intents/CameraIntentsActivity.java
@@ -22,7 +22,10 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.pm.PermissionInfo;
import android.hardware.Camera;
+import android.media.MediaMetadataRetriever;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
@@ -34,13 +37,22 @@
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.TextView;
+import androidx.core.content.FileProvider;
+import android.Manifest;
import com.android.cts.verifier.camera.intents.CameraContentJobService;
import com.android.cts.verifier.PassFailButtons;
import com.android.cts.verifier.R;
import com.android.cts.verifier.TestResult;
+import android.widget.Toast;
+import static android.media.MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO;
+import static android.media.MediaMetadataRetriever.METADATA_KEY_LOCATION;
+
+import java.io.File;
import java.util.TreeSet;
+import java.util.Date;
+import java.text.SimpleDateFormat;
/**
* Tests for manual verification of uri trigger being fired.
@@ -80,7 +92,9 @@
private ImageButton mPassButton;
private ImageButton mFailButton;
private Button mStartTestButton;
-
+ private Button mSettingsButton;
+ private File mVideoTargetDir = null;
+ private File mVideoTarget = null;
private int mState = STATE_OFF;
private boolean mActivityResult = false;
@@ -198,7 +212,9 @@
mPassButton = (ImageButton) findViewById(R.id.pass_button);
mFailButton = (ImageButton) findViewById(R.id.fail_button);
mStartTestButton = (Button) findViewById(R.id.start_test_button);
+ mSettingsButton = (Button) findViewById(R.id.settings_button);
mStartTestButton.setOnClickListener(this);
+ mSettingsButton.setOnClickListener(this);
// This activity is reused multiple times
// to test each camera/intents combination
@@ -236,6 +252,7 @@
cameraExtraLabel.setText(getStageInstructionLabel(getStageIndex()));
mStartTestButton.setEnabled(true);
+ mSettingsButton.setEnabled(true);
}
@Override
@@ -247,6 +264,40 @@
@Override
public void onResume() {
super.onResume();
+ mFailButton.setEnabled(false);
+ /**
+ * If location is not enabled, fail buttons should be disabled, since they take us back to
+ * the original CTS Verifier activity where other tests might depend on these
+ * If we're in STAGE_INTENT_VIDEO even the pass button should be disabled till location
+ * access is turned back on for CTS Verifier.
+ */
+ Boolean locationEnabled = (checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) ==
+ PackageManager.PERMISSION_GRANTED);
+
+ if (getStageIndex() == STAGE_INTENT_VIDEO) {
+ /**
+ * Don't enable the pass /fail button till the user grants CTS verifier location
+ * access again.
+ */
+ mPassButton.setEnabled(false);
+ if (locationEnabled) {
+ if (mState == STATE_SUCCESSFUL) {
+ mPassButton.setEnabled(true);
+ } else {
+ mFailButton.setEnabled(true);
+ }
+ } else if (mState != STATE_OFF) {
+ Toast.makeText(this, R.string.ci_location_permissions_error,
+ Toast.LENGTH_SHORT).show();
+ }
+ } else {
+ if (locationEnabled) {
+ mFailButton.setEnabled(true);
+ } else {
+ Toast.makeText(this, R.string.ci_location_permissions_fail_error,
+ Toast.LENGTH_SHORT).show();
+ }
+ }
}
@Override
@@ -274,13 +325,38 @@
if (requestCode == 1337 + getStageIndex()) {
Log.v(TAG, "Activity we launched was finished");
mActivityResult = true;
-
+ int stageIndex = getStageIndex();
if (mState != STATE_FAILED
- && getStageIndex() == STAGE_INTENT_PICTURE) {
- mPassButton.setEnabled(true);
- mFailButton.setEnabled(false);
-
+ && (stageIndex == STAGE_INTENT_PICTURE || stageIndex == STAGE_INTENT_VIDEO)) {
mState = STATE_SUCCESSFUL;
+ /**
+ * For images, we don't need to do more checks, since location in image exif is
+ * checked by cts test: MediaStoreUiTest .
+ */
+ if (stageIndex == STAGE_INTENT_PICTURE) {
+ setPassButton(true);
+ return;
+ }
+ if (mVideoTarget == null) {
+ Log.d(TAG, "Video target was not set");
+ return;
+ }
+ /**
+ * Check that there is no location data in video.
+ */
+ MediaMetadataRetriever mediaRetriever = new MediaMetadataRetriever();
+ mediaRetriever.setDataSource(mVideoTarget.toString());
+ if (mediaRetriever.extractMetadata(METADATA_KEY_HAS_VIDEO) == null ||
+ mediaRetriever.extractMetadata(METADATA_KEY_LOCATION) != null) {
+ mState = STATE_FAILED;
+ } else {
+ mVideoTarget.delete();
+ }
+ Log.d(TAG, "METADATA_KEY_HAS_VIDEO: " +
+ mediaRetriever.extractMetadata(METADATA_KEY_HAS_VIDEO) +
+ " METADATA_KEY_LOCATION: " +
+ mediaRetriever.extractMetadata(METADATA_KEY_LOCATION));
+ mediaRetriever.release();
/* successful, unless we get the URI trigger back
at some point later on */
}
@@ -292,6 +368,11 @@
return mReportBuilder.toString();
}
+ private void setPassButton(Boolean pass) {
+ mPassButton.setEnabled(pass);
+ mFailButton.setEnabled(!pass);
+ }
+
private class WaitForTriggerTask extends AsyncTask<Void, Void, Boolean> {
protected Boolean doInBackground(Void... param) {
try {
@@ -336,13 +417,7 @@
}
protected void onPostExecute(Boolean pass) {
- if (pass) {
- mPassButton.setEnabled(true);
- mFailButton.setEnabled(false);
- } else {
- mPassButton.setEnabled(false);
- mFailButton.setEnabled(true);
- }
+ setPassButton(pass);
}
}
@@ -351,11 +426,14 @@
Log.v(TAG, "Click detected");
final int stageIndex = getStageIndex();
+ if (view == mSettingsButton) {
+ Log.v(TAG, "Opening up Settings app");
+ startActivity(new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS));
+ }
if (view == mStartTestButton) {
Log.v(TAG, "Starting testing... ");
-
mState = STATE_STARTED;
JobScheduler jobScheduler = (JobScheduler) getSystemService(
@@ -366,13 +444,22 @@
mTestEnv.setUp();
- JobInfo job = makeJobInfo(TEST_JOB_TYPES[stageIndex]);
- jobScheduler.schedule(job);
+ /**
+ * Video intents do not need to wait on a ContentProvider broadcast since we're starting
+ * the intent activity with EXTRA_OUTPUT set
+ */
+ if (stageIndex != STAGE_INTENT_VIDEO) {
+ JobInfo job = makeJobInfo(TEST_JOB_TYPES[stageIndex]);
+ jobScheduler.schedule(job);
+ new WaitForTriggerTask().execute();
+ }
- new WaitForTriggerTask().execute();
-
- /* we can allow user to fail immediately */
- mFailButton.setEnabled(true);
+ /* we can allow user to fail immediately if location is on, otherwise they must
+ * enable location */
+ if (checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) ==
+ PackageManager.PERMISSION_GRANTED) {
+ mFailButton.setEnabled(true);
+ }
/* trigger an ACTION_IMAGE_CAPTURE intent
which will run the camera app itself */
@@ -387,6 +474,21 @@
if (intentStr != null) {
cameraIntent = new Intent(intentStr);
+ if (stageIndex == STAGE_INTENT_VIDEO) {
+ String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
+ mVideoTargetDir = new File(this.getFilesDir(), "debug");
+ mVideoTarget = new File(mVideoTargetDir, timeStamp + "video.mp4");
+ mVideoTargetDir.mkdirs();
+ if (!mVideoTargetDir.exists()) {
+ Toast.makeText(this, R.string.ci_directory_creation_error,
+ Toast.LENGTH_SHORT).show();
+ Log.v(TAG, "Could not create directory");
+ return;
+ }
+ cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, FileProvider.getUriForFile(this,
+ "com.android.cts.verifier.managedprovisioning.fileprovider",
+ mVideoTarget));
+ }
startActivityForResult(cameraIntent, 1337 + getStageIndex());
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/forcestop/RecentTaskRemovalTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/forcestop/RecentTaskRemovalTestActivity.java
new file mode 100644
index 0000000..0718d41
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/forcestop/RecentTaskRemovalTestActivity.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 com.android.cts.verifier.forcestop;
+
+import android.app.PendingIntent;
+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.PackageManager;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.view.View;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.android.cts.forcestophelper.Constants;
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+/**
+ * Tests that an app is not killed when it is swiped away from recents.
+ * Requires CtsForceStopHelper.apk to be installed.
+ */
+public class RecentTaskRemovalTestActivity extends PassFailButtons.Activity implements
+ View.OnClickListener {
+ private static final String HELPER_APP_NAME = Constants.PACKAGE_NAME;
+ private static final String HELPER_ACTIVITY_NAME = Constants.ACTIVITY_CLASS_NAME;
+
+ private static final String HELPER_APP_INSTALLED_KEY = "helper_installed";
+
+ private static final String ACTION_REPORT_TASK_REMOVED = "report_task_removed";
+ private static final String ACTION_REPORT_ALARM = "report_alarm";
+
+ private static final long EXTRA_WAIT_FOR_ALARM = 2_000;
+
+ private ImageView mInstallStatus;
+ private TextView mInstallTestAppText;
+
+ private ImageView mLaunchStatus;
+ private Button mLaunchTestAppButton;
+
+ private ImageView mRemoveFromRecentsStatus;
+ private TextView mRemoveFromRecentsInstructions;
+
+ private ImageView mForceStopStatus;
+ private TextView mForceStopVerificationResult;
+
+ private volatile boolean mTestAppInstalled;
+ private volatile boolean mTestTaskLaunched;
+ private volatile boolean mTestTaskRemoved;
+ private volatile boolean mTestAppForceStopped;
+ private volatile boolean mTestAlarmReceived;
+ private volatile boolean mWaitingForAlarm;
+
+ private final PackageStateReceiver mPackageChangesListener = new PackageStateReceiver();
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.force_stop_recents_main);
+ setInfoResources(R.string.remove_from_recents_test, R.string.remove_from_recents_test_info,
+ -1);
+ setPassFailButtonClickListeners();
+
+ if (savedInstanceState != null) {
+ mTestAppInstalled = savedInstanceState.getBoolean(HELPER_APP_INSTALLED_KEY, false);
+ } else {
+ mTestAppInstalled = isPackageInstalled();
+ }
+ mInstallStatus = findViewById(R.id.fs_test_app_install_status);
+ mInstallTestAppText = findViewById(R.id.fs_test_app_install_instructions);
+
+ mRemoveFromRecentsStatus = findViewById(R.id.fs_test_app_recents_status);
+ mRemoveFromRecentsInstructions = findViewById(R.id.fs_test_app_recents_instructions);
+
+ mLaunchStatus = findViewById(R.id.fs_test_app_launch_status);
+ mLaunchTestAppButton = findViewById(R.id.fs_launch_test_app_button);
+ mLaunchTestAppButton.setOnClickListener(this);
+
+ mForceStopStatus = findViewById(R.id.fs_force_stop_status);
+ mForceStopVerificationResult = findViewById(R.id.fs_force_stop_verification);
+
+ mPackageChangesListener.register(mForceStopStatus.getHandler());
+ }
+
+ private boolean isPackageInstalled() {
+ PackageInfo packageInfo = null;
+ try {
+ packageInfo = getPackageManager().getPackageInfo(HELPER_APP_NAME, 0);
+ } catch (PackageManager.NameNotFoundException exc) {
+ // fall through
+ }
+ return packageInfo != null;
+ }
+
+ @Override
+ public void onClick(View v) {
+ if (v == mLaunchTestAppButton) {
+ mTestTaskLaunched = true;
+
+ final Intent reportTaskRemovedIntent = new Intent(ACTION_REPORT_TASK_REMOVED)
+ .setPackage(getPackageName())
+ .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ final PendingIntent onTaskRemoved = PendingIntent.getBroadcast(this, 0,
+ reportTaskRemovedIntent, 0);
+
+ final Intent reportAlarmIntent = new Intent(ACTION_REPORT_ALARM)
+ .setPackage(getPackageName())
+ .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ final PendingIntent onAlarm = PendingIntent.getBroadcast(this, 0, reportAlarmIntent, 0);
+
+ final Intent testActivity = new Intent()
+ .setClassName(HELPER_APP_NAME, HELPER_ACTIVITY_NAME)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ .putExtra(Constants.EXTRA_ON_TASK_REMOVED, onTaskRemoved)
+ .putExtra(Constants.EXTRA_ON_ALARM, onAlarm);
+ startActivity(testActivity);
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ updateWidgets();
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle icicle) {
+ icicle.putBoolean(HELPER_APP_INSTALLED_KEY, mTestAppInstalled);
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ mPackageChangesListener.unregister();
+ }
+
+ private void updateWidgets() {
+ mInstallStatus.setImageResource(
+ mTestAppInstalled ? R.drawable.fs_good : R.drawable.fs_indeterminate);
+ mInstallTestAppText.setText(mTestAppInstalled ? R.string.fs_test_app_installed_text
+ : R.string.fs_test_app_install_instructions);
+ mInstallStatus.invalidate();
+
+ mLaunchStatus.setImageResource(
+ mTestTaskLaunched ? R.drawable.fs_good : R.drawable.fs_indeterminate);
+ mLaunchTestAppButton.setEnabled(mTestAppInstalled && !mTestTaskLaunched);
+ mLaunchStatus.invalidate();
+
+ mRemoveFromRecentsStatus.setImageResource(
+ mTestTaskRemoved ? R.drawable.fs_good : R.drawable.fs_indeterminate);
+ mRemoveFromRecentsInstructions.setText(R.string.fs_test_app_recents_instructions);
+ mRemoveFromRecentsStatus.invalidate();
+
+ if (mTestTaskRemoved) {
+ if (mWaitingForAlarm) {
+ mForceStopStatus.setImageResource(R.drawable.fs_clock);
+ mForceStopVerificationResult.setText(R.string.fs_force_stop_verification_pending);
+ } else {
+ mForceStopStatus.setImageResource(
+ (mTestAppForceStopped || !mTestAlarmReceived) ? R.drawable.fs_error
+ : R.drawable.fs_good);
+ mForceStopVerificationResult.setText((mTestAppForceStopped || !mTestAlarmReceived)
+ ? R.string.result_failure
+ : R.string.result_success);
+ }
+ mForceStopStatus.invalidate();
+ mForceStopStatus.setVisibility(View.VISIBLE);
+ mForceStopVerificationResult.setVisibility(View.VISIBLE);
+ } else {
+ mForceStopStatus.setVisibility(View.GONE);
+ mForceStopVerificationResult.setVisibility(View.GONE);
+ }
+
+ getPassButton().setEnabled(mTestAlarmReceived && !mTestAppForceStopped);
+ }
+
+ private final class PackageStateReceiver extends BroadcastReceiver {
+
+ void register(Handler handler) {
+ final IntentFilter packageFilter = new IntentFilter();
+ packageFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
+ packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+ packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ packageFilter.addDataScheme("package");
+ registerReceiver(this, packageFilter);
+
+ final IntentFilter commsFilter = new IntentFilter();
+ commsFilter.addAction(ACTION_REPORT_TASK_REMOVED);
+ commsFilter.addAction(ACTION_REPORT_ALARM);
+ registerReceiver(this, commsFilter, null, handler);
+ }
+
+ void unregister() {
+ unregisterReceiver(this);
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final Uri uri = intent.getData();
+ boolean testPackageAffected = (uri != null && HELPER_APP_NAME.equals(
+ uri.getSchemeSpecificPart()));
+ switch (intent.getAction()) {
+ case Intent.ACTION_PACKAGE_ADDED:
+ case Intent.ACTION_PACKAGE_REMOVED:
+ if (testPackageAffected) {
+ mTestAppInstalled = Intent.ACTION_PACKAGE_ADDED.equals(intent.getAction());
+ }
+ break;
+ case Intent.ACTION_PACKAGE_RESTARTED:
+ if (testPackageAffected) {
+ mTestAppForceStopped = true;
+ }
+ break;
+ case ACTION_REPORT_TASK_REMOVED:
+ mTestTaskRemoved = true;
+ mWaitingForAlarm = true;
+ mForceStopStatus.postDelayed(() -> {
+ mWaitingForAlarm = false;
+ updateWidgets();
+ }, Constants.ALARM_DELAY + EXTRA_WAIT_FOR_ALARM);
+ break;
+ case ACTION_REPORT_ALARM:
+ mTestAlarmReceived = true;
+ mWaitingForAlarm = false;
+ break;
+ }
+ updateWidgets();
+ }
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/instantapps/AppInfoTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/instantapps/AppInfoTestActivity.java
new file mode 100644
index 0000000..c1d37ed
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/instantapps/AppInfoTestActivity.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.instantapps;
+
+import android.os.Bundle;
+import android.widget.TextView;
+
+import com.android.cts.verifier.R;
+
+/**
+ * Test for manual verification of the functionality to view/delete Instant Apps
+ * locally cached for each individual app package.
+ *
+ * The test verifies that Instant App can be viewed and deleted in
+ * Apps and Notifiction Settings.
+ */
+public class AppInfoTestActivity extends BaseTestActivity {
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setInfoResources(R.string.ia_app_info, R.string.ia_app_info_info, -1);
+ TextView extraText = (TextView) findViewById(R.id.instruction_extra_text);
+ extraText.setText(R.string.ia_app_info_instruction_label);
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/instantapps/BaseTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/instantapps/BaseTestActivity.java
new file mode 100644
index 0000000..b359327
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/instantapps/BaseTestActivity.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.verifier.instantapps;
+
+import android.app.AlertDialog;
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.net.Uri;
+import android.util.Log;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+import android.widget.ImageButton;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+/**
+ * Base {@link Activity} class for testing Instant Apps System UIs.
+ */
+public abstract class BaseTestActivity extends PassFailButtons.Activity
+ implements OnClickListener {
+
+ private static final String TAG = "InstantApps";
+ private static final String APP_URL = "https://instantapp.cts.android.com";
+ private static final String APP_PACKAGE = "com.android.cts.instantapp";
+
+ private ImageButton mPassButton;
+ private ImageButton mFailButton;
+ private Button mStartTestButton;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.instant_apps);
+ setPassFailButtonClickListeners();
+
+ mPassButton = (ImageButton) findViewById(R.id.pass_button);
+ mFailButton = (ImageButton) findViewById(R.id.fail_button);
+ mStartTestButton = (Button) findViewById(R.id.start_test_button);
+ mStartTestButton.setOnClickListener(this);
+
+ resetButtons();
+
+ mStartTestButton.setEnabled(true);
+ }
+
+ @Override
+ public void onClick(View view) {
+ if (view != mStartTestButton) return;
+
+ Log.v(TAG, "Start Test button clicked");
+ try {
+ Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(APP_URL));
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.addCategory(Intent.CATEGORY_BROWSABLE);
+ intent.setPackage(APP_PACKAGE);
+ startActivity(intent);
+
+ mStartTestButton.setEnabled(false);
+ enablePassFailButtons(true);
+ } catch (ActivityNotFoundException e) {
+ // Use ActivityNotFoundException as an indicator that Instant App is
+ // not installed.
+ Log.v(TAG, "Instant App not installed.");
+
+ // Display alert dialog with instruction for installing the Instant App.
+ new AlertDialog.Builder(
+ BaseTestActivity.this)
+ .setIcon(android.R.drawable.ic_dialog_info)
+ .setTitle(R.string.ia_install_dialog_title)
+ .setMessage(R.string.ia_install_dialog_text)
+ .setPositiveButton(android.R.string.ok, null)
+ .show();
+ }
+ }
+
+ private void resetButtons() {
+ enablePassFailButtons(false);
+ }
+
+ private void enablePassFailButtons(boolean enable) {
+ mPassButton.setEnabled(enable);
+ mFailButton.setEnabled(enable);
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/instantapps/NotificationTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/instantapps/NotificationTestActivity.java
index 7c77f67..6e2052c 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/instantapps/NotificationTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/instantapps/NotificationTestActivity.java
@@ -15,113 +15,25 @@
*/
package com.android.cts.verifier.instantapps;
-import android.app.AlertDialog;
-import android.content.ActivityNotFoundException;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.ImageFormat;
-import android.graphics.Matrix;
-import android.hardware.Camera;
import android.os.Bundle;
-import android.os.Handler;
-import android.net.Uri;
-import android.util.Log;
-import android.view.SurfaceHolder;
-import android.view.SurfaceView;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.widget.Button;
-import android.widget.ImageButton;
-import android.widget.ImageView;
-import android.widget.LinearLayout.LayoutParams;
import android.widget.TextView;
-import com.android.cts.verifier.PassFailButtons;
import com.android.cts.verifier.R;
-import com.android.cts.verifier.TestResult;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.List;
-import java.util.TreeSet;
/**
- *
* Test for manual verification of Instant Apps notification.
*
* The test verifies that an Instant App notification will be shown when
* an Instant App is at foreground.
*/
-public class NotificationTestActivity extends PassFailButtons.Activity
- implements OnClickListener {
-
- private static final String TAG = "InstantApps";
- private static final String APP_URL = "https://instantapp.cts.android.com";
- private static final String APP_PACKAGE = "com.android.cts.instantapp";
-
- private ImageButton mPassButton;
- private ImageButton mFailButton;
- private Button mStartTestButton;
+public class NotificationTestActivity extends BaseTestActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- setContentView(R.layout.instant_apps);
- setPassFailButtonClickListeners();
setInfoResources(R.string.ia_notification, R.string.ia_notification_info, -1);
-
- mPassButton = (ImageButton) findViewById(R.id.pass_button);
- mFailButton = (ImageButton) findViewById(R.id.fail_button);
- mStartTestButton = (Button) findViewById(R.id.start_test_button);
- mStartTestButton.setOnClickListener(this);
-
- resetButtons();
-
- mStartTestButton.setEnabled(true);
- }
-
- @Override
- public void onClick(View view) {
- if (view != mStartTestButton) return;
-
- Log.v(TAG, "Start Test button clicked");
- try {
- Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(APP_URL));
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- intent.addCategory(Intent.CATEGORY_BROWSABLE);
- intent.setPackage(APP_PACKAGE);
- startActivity(intent);
-
- mStartTestButton.setEnabled(false);
- enablePassFailButtons(true);
- } catch (ActivityNotFoundException e) {
- // Use ActivityNotFoundException as an indicator that Instant App is
- // not installed.
- Log.v(TAG, "Instant App not installed.");
-
- // Display alert dialog with instruction for installing the Instant App.
- new AlertDialog.Builder(
- NotificationTestActivity.this)
- .setIcon(android.R.drawable.ic_dialog_info)
- .setTitle(R.string.ia_install_dialog_title)
- .setMessage(R.string.ia_install_dialog_text)
- .setPositiveButton(android.R.string.ok, null)
- .show();
- }
- }
-
- private void resetButtons() {
- enablePassFailButtons(false);
- }
-
- private void enablePassFailButtons(boolean enable) {
- mPassButton.setEnabled(enable);
- mFailButton.setEnabled(enable);
+ TextView extraText = (TextView) findViewById(R.id.instruction_extra_text);
+ extraText.setText(R.string.ia_notification_instruction_label);
}
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/instantapps/RecentAppsTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/instantapps/RecentAppsTestActivity.java
new file mode 100644
index 0000000..617031f
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/instantapps/RecentAppsTestActivity.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.instantapps;
+
+import android.os.Bundle;
+import android.widget.TextView;
+
+import com.android.cts.verifier.R;
+
+/**
+ * Test for manual verification of Instant Apps Recents function.
+ *
+ * The test verifies that Instant App can be accessed in Recent Apps screen.
+ */
+public class RecentAppsTestActivity extends BaseTestActivity {
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setInfoResources(R.string.ia_recents, R.string.ia_recents_info, -1);
+ TextView extraText = (TextView) findViewById(R.id.instruction_extra_text);
+ extraText.setText(R.string.ia_recents_instruction_label);
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/IntentFiltersTestHelper.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/IntentFiltersTestHelper.java
index 9a33320..ccef312 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/IntentFiltersTestHelper.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/IntentFiltersTestHelper.java
@@ -251,7 +251,7 @@
}
if (pm.hasSystemFeature(PackageManager.FEATURE_INPUT_METHODS)) {
- forwardedIntentsFromManaged.addAll(Arrays.asList(
+ notForwardedIntentsFromManaged.addAll(Arrays.asList(
new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS),
new Intent(Settings.ACTION_INPUT_METHOD_SUBTYPE_SETTINGS)));
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/PolicyTransparencyTestListActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/PolicyTransparencyTestListActivity.java
index 998f8a8..d20e047 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/PolicyTransparencyTestListActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/PolicyTransparencyTestListActivity.java
@@ -70,7 +70,7 @@
final String[] settingsIntentActions = new String[] {
Settings.ACTION_DATE_SETTINGS,
Settings.ACTION_SETTINGS,
- Settings.ACTION_SECURITY_SETTINGS,
+ Settings.ACTION_DISPLAY_SETTINGS,
Settings.ACTION_DISPLAY_SETTINGS,
Settings.ACTION_SETTINGS,
Settings.ACTION_ACCESSIBILITY_SETTINGS,
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/UserRestrictions.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/UserRestrictions.java
index f004ace..53b1bf3 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/UserRestrictions.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/UserRestrictions.java
@@ -262,7 +262,8 @@
// This test should not run on watch
return !pm.hasSystemFeature(PackageManager.FEATURE_WATCH);
case UserManager.DISALLOW_OUTGOING_BEAM:
- return pm.hasSystemFeature(PackageManager.FEATURE_NFC);
+ return pm.hasSystemFeature(PackageManager.FEATURE_NFC)
+ && pm.hasSystemFeature(PackageManager.FEATURE_NFC_BEAM);
case UserManager.DISALLOW_SHARE_LOCATION:
return pm.hasSystemFeature(PackageManager.FEATURE_LOCATION);
case UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES:
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/NdefPushReceiverActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/NdefPushReceiverActivity.java
index 0697be2..377d068 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/NdefPushReceiverActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/NdefPushReceiverActivity.java
@@ -123,7 +123,7 @@
// be set in onPrepareDialog.
return new AlertDialog.Builder(this)
.setIcon(android.R.drawable.ic_dialog_info)
- .setTitle(R.string.nfc_result_failure)
+ .setTitle(R.string.result_failure)
.setMessage("")
.setPositiveButton(android.R.string.ok, null)
.show();
@@ -140,8 +140,8 @@
boolean isMatch = args.getBoolean(IS_MATCH_ARG);
AlertDialog alert = (AlertDialog) dialog;
alert.setTitle(isMatch
- ? R.string.nfc_result_success
- : R.string.nfc_result_failure);
+ ? R.string.result_success
+ : R.string.result_failure);
alert.setMessage(isMatch
? getString(R.string.nfc_ndef_push_receive_success)
: getString(R.string.nfc_ndef_push_receive_failure));
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/TagVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/TagVerifierActivity.java
index 85a9de5..d9166a5 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/TagVerifierActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/TagVerifierActivity.java
@@ -352,7 +352,7 @@
// Placeholder title and message that will be set properly in onPrepareDialog
return new AlertDialog.Builder(this)
.setIcon(android.R.drawable.ic_dialog_alert)
- .setTitle(R.string.nfc_result_failure)
+ .setTitle(R.string.result_failure)
.setMessage("")
.setPositiveButton(android.R.string.ok, null)
.create();
@@ -378,8 +378,8 @@
AlertDialog alert = (AlertDialog) dialog;
alert.setTitle(isMatch
- ? R.string.nfc_result_success
- : R.string.nfc_result_failure);
+ ? R.string.result_success
+ : R.string.result_failure);
alert.setMessage(getString(R.string.nfc_result_message, expectedContent, actualContent));
}
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 f0ec49f..d5c81fe 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/BubblesVerifierActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/BubblesVerifierActivity.java
@@ -106,6 +106,9 @@
mTests.add(new RemoveMetadataTest());
mTests.add(new AddMetadataTest());
mTests.add(new ExpandBubbleTest());
+ mTests.add(new DismissBubbleTest());
+ mTests.add(new DismissNotificationTest());
+ mTests.add(new AutoExpandBubbleTest());
setPassFailButtonClickListeners();
@@ -195,7 +198,7 @@
@Override
public void performTestAction() {
Notification.Builder builder =
- getBasicNotifBuilder("Bubble notification", "SendBubbleTest");
+ getBasicNotifBuilder("Bubble notification", "1: SendBubbleTest");
builder.setBubbleMetadata(getBasicBubbleBuilder().build());
mNotificationManager.notify(NOTIFICATION_ID, builder.build());
@@ -222,7 +225,7 @@
@Override
public void performTestAction() {
Notification.Builder builder =
- getBasicNotifBuilder("Bubble notification", "SuppressNotifTest");
+ getBasicNotifBuilder("Bubble notification", "2: SuppressNotifTest");
Notification.BubbleMetadata metadata = getBasicBubbleBuilder()
.setSuppressNotification(true)
@@ -253,7 +256,7 @@
@Override
public void performTestAction() {
Notification.Builder builder =
- getBasicNotifBuilder("Bubble notification", "AddNotifTest");
+ getBasicNotifBuilder("Bubble notification", "3: AddNotifTest");
Notification.BubbleMetadata metadata = getBasicBubbleBuilder()
.setSuppressNotification(false)
@@ -284,7 +287,7 @@
@Override
public void performTestAction() {
Notification.Builder builder =
- getBasicNotifBuilder("Bubble notification", "RemoveMetadataTest");
+ getBasicNotifBuilder("Bubble notification", "4: RemoveMetadataTest");
mNotificationManager.notify(NOTIFICATION_ID, builder.build());
}
}
@@ -309,7 +312,7 @@
@Override
public void performTestAction() {
Notification.Builder builder =
- getBasicNotifBuilder("Bubble notification", "AddMetadataTest");
+ getBasicNotifBuilder("Bubble notification", "5: AddMetadataTest");
Notification.BubbleMetadata metadata = getBasicBubbleBuilder().build();
builder.setBubbleMetadata(metadata);
@@ -330,6 +333,93 @@
}
}
+ private class DismissBubbleTest extends BubblesTestStep {
+ @Override
+ public int getButtonText() {
+ return R.string.bubbles_notification_test_button_7;
+ }
+
+ @Override
+ public int getTestTitle() {
+ return R.string.bubbles_notification_test_title_7;
+ }
+
+ @Override
+ public int getTestDescription() {
+ return R.string.bubbles_notification_test_verify_7;
+ }
+
+ @Override
+ public void performTestAction() {
+ Notification.Builder builder =
+ getBasicNotifBuilder("Bubble notification", "7: DismissBubbleTest");
+
+ Notification.BubbleMetadata metadata = getBasicBubbleBuilder().build();
+ builder.setBubbleMetadata(metadata);
+
+ mNotificationManager.notify(NOTIFICATION_ID, builder.build());
+ }
+ }
+
+ private class DismissNotificationTest extends BubblesTestStep {
+ @Override
+ public int getButtonText() {
+ return R.string.bubbles_notification_test_button_8;
+ }
+
+ @Override
+ public int getTestTitle() {
+ return R.string.bubbles_notification_test_title_8;
+ }
+
+ @Override
+ public int getTestDescription() {
+ return R.string.bubbles_notification_test_verify_8;
+ }
+
+ @Override
+ public void performTestAction() {
+ Notification.Builder builder =
+ getBasicNotifBuilder("Bubble notification",
+ "8: DismissNotificationTest: Dismiss me!!");
+
+ Notification.BubbleMetadata metadata = getBasicBubbleBuilder().build();
+ builder.setBubbleMetadata(metadata);
+
+ mNotificationManager.notify(NOTIFICATION_ID, builder.build());
+ }
+ }
+
+ private class AutoExpandBubbleTest extends BubblesTestStep {
+ @Override
+ public int getButtonText() {
+ return R.string.bubbles_notification_test_button_9;
+ }
+
+ @Override
+ public int getTestTitle() {
+ return R.string.bubbles_notification_test_title_9;
+ }
+
+ @Override
+ public int getTestDescription() {
+ return R.string.bubbles_notification_test_verify_9;
+ }
+
+ @Override
+ public void performTestAction() {
+ Notification.Builder builder =
+ getBasicNotifBuilder("Bubble notification", "9: Auto expanded bubble");
+
+ Notification.BubbleMetadata metadata =
+ getBasicBubbleBuilder().setAutoExpandBubble(true).build();
+ builder.setBubbleMetadata(metadata);
+
+ 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/p2p/ResponderTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/p2p/ResponderTestActivity.java
index 4285a1a..39f0bf8 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/p2p/ResponderTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/p2p/ResponderTestActivity.java
@@ -21,6 +21,7 @@
import android.content.IntentFilter;
import android.net.wifi.p2p.WifiP2pDevice;
import android.net.wifi.p2p.WifiP2pManager;
+import android.net.wifi.p2p.WifiP2pManager.Channel;
import android.os.Bundle;
import android.os.Handler;
import android.view.WindowManager;
@@ -58,6 +59,9 @@
*/
private TextView mMyDeviceView;
+ private WifiP2pManager mP2pMgr;
+ private Channel mChannel;
+
/*
* BroadcastReceiver to obtain own device information.
*/
@@ -92,6 +96,8 @@
// keep screen on while this activity is front view.
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+ mP2pMgr = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE);
+ mChannel = mP2pMgr.initialize(this, getMainLooper(), null);
}
@Override
@@ -99,6 +105,17 @@
super.onResume();
mTestCase.start(this);
registerReceiver(mReceiver, mIntentFilter);
+ mP2pMgr.requestDeviceInfo(mChannel, wifiP2pDevice -> {
+ if (wifiP2pDevice != null) {
+ // need to show in the GUI thread.
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mMyDeviceView.setText("Device Name: " + wifiP2pDevice.deviceName);
+ }
+ });
+ }
+ });
}
@Override
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVXCheckAnalyzer.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVXCheckAnalyzer.java
index 45439a7..c15d2ac 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVXCheckAnalyzer.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVXCheckAnalyzer.java
@@ -66,7 +66,7 @@
private static final boolean OUTPUT_DEBUG_IMAGE = false;
private static final double VALID_FRAME_THRESHOLD = 0.8;
- private static final double REPROJECTION_THRESHOLD_RATIO = 0.03;
+ private static final double REPROJECTION_THRESHOLD_RATIO = 0.01;
private static final boolean FORCE_CV_ANALYSIS = false;
private static final boolean TRACE_VIDEO_ANALYSIS = false;
private static final double DECIMATION_FPS_TARGET = 15.0;
@@ -880,13 +880,6 @@
// reproject points to for evaluation of result accuracy of solvePnP
Calib3d.projectPoints(grid, rvec, tvec, camMat, coeff, reprojCenters);
- // error is evaluated in norm2, which is real error in pixel distance / sqrt(2)
- double error = Core.norm(centers, reprojCenters, Core.NORM_L2);
-
- if (LOCAL_LOGV) {
- Log.v(TAG, "Found attitude, re-projection error = " + error);
- }
-
// Calculate the average distance between opposite corners of the pattern in pixels
Point[] centerPoints = centers.toArray();
Point bottomLeftPos = centerPoints[0];
@@ -896,10 +889,27 @@
double avgPixelDist = (getDistanceBetweenPoints(bottomLeftPos, topRightPos)
+ getDistanceBetweenPoints(bottomRightPos, topLeftPos)) / 2;
+ // Calculate the average pixel error between the circle centers from the video and the
+ // reprojected circle centers based on the estimated camera position. The error provides
+ // a way to estimate how accurate the assumed test device's position is. If the error
+ // is high, then the frame should be discarded to prevent an inaccurate test device's
+ // position from being compared against the rotation vector sample at that time.
+ Point[] reprojectedPointsArray = reprojCenters.toArray();
+ double avgCenterError = 0.0;
+ for (int curCenter = 0; curCenter < reprojectedPointsArray.length; curCenter++) {
+ avgCenterError += getDistanceBetweenPoints(
+ reprojectedPointsArray[curCenter], centerPoints[curCenter]);
+ }
+ avgCenterError /= reprojectedPointsArray.length;
+
+ if (LOCAL_LOGV) {
+ Log.v(TAG, "Found attitude, re-projection error = " + avgCenterError);
+ }
+
// if error is reasonable, add it into the results. Use a dynamic threshold based on
// the pixel distance of opposite corners of the pattern to prevent higher resolution
// video or the distance between the camera and the test pattern from impacting the test
- if (error < REPROJECTION_THRESHOLD_RATIO * avgPixelDist) {
+ if (avgCenterError < REPROJECTION_THRESHOLD_RATIO * avgPixelDist) {
double [] rv = new double[3];
double timestamp;
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVXCheckTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVXCheckTestActivity.java
index 9fbeba2..58145e2 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVXCheckTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVXCheckTestActivity.java
@@ -301,10 +301,8 @@
String message = "Test Roll Axis Accuracy";
- Assert.assertEquals("Roll RMS error", 0.0, mReport.roll_rms_error,
- Criterion.roll_rms_error);
- Assert.assertEquals("Roll max error", 0.0, mReport.roll_max_error,
- Criterion.roll_max_error);
+ assertLessThan("Roll RMS error", mReport.roll_rms_error, Criterion.roll_rms_error);
+ assertLessThan("Roll max error", mReport.roll_max_error, Criterion.roll_max_error);
return message;
}
@@ -316,10 +314,8 @@
String message = "Test Pitch Axis Accuracy";
- Assert.assertEquals("Pitch RMS error", 0.0, mReport.pitch_rms_error,
- Criterion.pitch_rms_error);
- Assert.assertEquals("Pitch max error", 0.0, mReport.pitch_max_error,
- Criterion.pitch_max_error);
+ assertLessThan("Pitch RMS error", mReport.pitch_rms_error, Criterion.pitch_rms_error);
+ assertLessThan("Pitch max error", mReport.pitch_max_error, Criterion.pitch_max_error);
return message;
}
@@ -331,13 +327,16 @@
String message = "Test Yaw Axis Accuracy";
- Assert.assertEquals("Yaw RMS error", 0.0, mReport.yaw_rms_error,
- Criterion.yaw_rms_error);
- Assert.assertEquals("Yaw max error", 0.0, mReport.yaw_max_error,
- Criterion.yaw_max_error);
+ assertLessThan("Yaw RMS error", mReport.yaw_rms_error, Criterion.yaw_rms_error);
+ assertLessThan("Yaw max error", mReport.yaw_max_error, Criterion.yaw_max_error);
return message;
}
+ private void assertLessThan(String message, double lhs, double rhs) {
+ Assert.assertTrue(String.format("%s - expected %.4f < %.4f", message, lhs, rhs),
+ lhs < rhs);
+ }
+
private void loadOpenCVSuccessfulOrSkip() throws SensorTestStateNotSupportedException {
if (!mOpenCVLoadSuccessful)
throw new SensorTestStateNotSupportedException("Skipped due to OpenCV cannot be loaded");
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/StepSensorPermissionTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/StepSensorPermissionTestActivity.java
new file mode 100644
index 0000000..18148fa
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/StepSensorPermissionTestActivity.java
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.sensors;
+
+import com.android.cts.verifier.sensors.base.SensorCtsVerifierTestActivity;
+
+import android.Manifest;
+import com.android.cts.verifier.R;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.net.Uri;
+import android.provider.Settings;
+
+import java.util.ArrayList;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Test cases to verify step sensor permissions
+ */
+public class StepSensorPermissionTestActivity extends SensorCtsVerifierTestActivity
+ implements SensorEventListener {
+ private SensorManager mSensorManager;
+
+ private boolean mHasAccelFeature = false;
+ private boolean mHasStepCounterFeature = false;
+ private boolean mHasStepDetectorFeature = false;
+ private CountDownLatch mEventReceivedLatch = null;
+ private Sensor mSensorUnderTest = null;
+ private AccelRecorder mAccelRecorder = null;
+
+ public StepSensorPermissionTestActivity() {
+ super(StepSensorPermissionTestActivity.class);
+ }
+
+ @Override
+ protected void activitySetUp() {
+ mSensorManager = (SensorManager) getApplicationContext()
+ .getSystemService(Context.SENSOR_SERVICE);
+
+ mHasAccelFeature = getApplicationContext().getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_SENSOR_ACCELEROMETER);
+ mHasStepCounterFeature = getApplicationContext().getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_SENSOR_STEP_COUNTER);
+ mHasStepDetectorFeature = getApplicationContext().getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_SENSOR_STEP_DETECTOR);
+
+ mAccelRecorder = new AccelRecorder(mSensorManager);
+ }
+
+ @Override
+ protected void activityCleanUp() {
+ mSensorManager.unregisterListener(this);
+ mAccelRecorder.stop();
+
+ // Notify the user that the test has concluded. This will play in both the pass
+ // and fail case
+ try {
+ playSound();
+ } catch (InterruptedException e) {
+ // Ignored
+ }
+ }
+
+ /**
+ * Test cases.
+ */
+ @SuppressWarnings("unused")
+ public String testStepEvents() throws Throwable {
+ if (mHasStepCounterFeature || mHasStepDetectorFeature) {
+ // Verify that sensors cannot be registered when the permission is not granted
+ runTest(false /* permissionGranted */);
+
+ // Signal to the user that the first part of the test is over
+ try {
+ playSound();
+ } catch (InterruptedException e) {
+ // Ignored
+ }
+
+ // Verify that the sensors can be registered when the permission is not granted
+ runTest(true /* permissionGranted */);
+ }
+
+ return "Pass";
+ }
+
+ private void runTest(boolean permissionGranted) throws InterruptedException {
+ if (hasPermission(Manifest.permission.ACTIVITY_RECOGNITION) != permissionGranted) {
+ requestChangePermission(permissionGranted);
+ }
+
+ waitForUser(R.string.snsr_step_permission_walk);
+ checkPermission(Manifest.permission.ACTIVITY_RECOGNITION, permissionGranted);
+
+ if (mHasStepDetectorFeature) {
+ checkSensor(Sensor.TYPE_STEP_DETECTOR, permissionGranted);
+ }
+
+ if (mHasStepCounterFeature) {
+ checkSensor(Sensor.TYPE_STEP_COUNTER, permissionGranted);
+ }
+ }
+
+ private void requestChangePermission(boolean enable) throws InterruptedException {
+ SensorTestLogger logger = getTestLogger();
+ if (enable) {
+ logger.logInstructions(R.string.snsr_step_permission_enable);
+ } else {
+ logger.logInstructions(R.string.snsr_step_permission_disable);
+ }
+ waitForUserToContinue();
+ Intent intent = new Intent();
+ intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
+ Uri uri = Uri.fromParts("package", getPackageName(), null);
+ intent.setData(uri);
+ startActivity(intent);
+ }
+
+ private boolean hasPermission(String permission) {
+ return getApplicationContext().checkSelfPermission(permission) ==
+ PackageManager.PERMISSION_GRANTED;
+ }
+
+ private void checkPermission(String permission, boolean expected) {
+ if (expected && !hasPermission(permission)) {
+ throw new AssertionError(String.format("Should not have '%s' permission", permission));
+ } else if (!expected && hasPermission(permission)) {
+ throw new AssertionError(String.format("Should have '%s' permission", permission));
+ }
+ }
+
+ private void checkSensor(int sensorType, boolean expected) throws InterruptedException {
+ if (mHasAccelFeature && !expected) {
+ // Record accel when no events are expected to ensure that the device is moving during
+ // the test.
+ mAccelRecorder.start();
+ }
+
+ mEventReceivedLatch = new CountDownLatch(1);
+ Sensor sensor = mSensorManager.getDefaultSensor(sensorType);
+ mSensorUnderTest = sensor;
+
+ String msg = String.format("Should %sbe able to register for '%s' events",
+ expected ? "" : "not ", sensor.getName());
+ assertTrue(msg, mSensorManager.registerListener(this, sensor, 0, 0) == expected);
+
+ boolean eventReceived = mEventReceivedLatch.await(5, TimeUnit.SECONDS);
+
+ // Ensure that events are only received when the application has permission
+ if (expected) {
+ assertTrue("Failed to receive event for " + sensor.getName(), eventReceived);
+ } else {
+ assertFalse("Should not have received event for " + sensor.getName(), eventReceived);
+
+ if (mHasAccelFeature) {
+ // Verify that the device was moving during the test
+ mAccelRecorder.stop();
+ assertTrue("Walking not detected", mAccelRecorder.variance() > 1.0f);
+ }
+ }
+ }
+
+ @Override
+ public void onSensorChanged(SensorEvent sensorEvent) {
+ if (sensorEvent.sensor == mSensorUnderTest) {
+ mEventReceivedLatch.countDown();
+ }
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int i) {
+ }
+
+ class AccelRecorder implements SensorEventListener {
+
+ private SensorManager mSensorManager;
+ private Sensor mAccel;
+ private ArrayList<Float> mMagnitudes = new ArrayList<>();
+
+ public AccelRecorder(SensorManager sensorManager) {
+ mSensorManager = sensorManager;
+ mAccel = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
+ }
+
+ public void start() {
+ mMagnitudes.clear();
+ mSensorManager.registerListener(
+ this, mAccel, (int)TimeUnit.MILLISECONDS.toMicros(20), 0);
+ }
+
+ public void stop() {
+ mSensorManager.unregisterListener(this);
+ }
+
+ public float variance() {
+ if (mMagnitudes.size() <= 1) {
+ return 0.0f;
+ }
+
+ float mean = 0;
+ for (float val : mMagnitudes) {
+ mean += val;
+ }
+ mean /= mMagnitudes.size();
+
+ float var = 0;
+ for (float val : mMagnitudes) {
+ var += (val - mean) * (val - mean);
+ }
+ return var / (mMagnitudes.size() - 1);
+ }
+
+ @Override
+ public void onSensorChanged(SensorEvent sensorEvent) {
+ if (sensorEvent.sensor == mAccel) {
+ float magnitude = sensorEvent.values[0] * sensorEvent.values[0] +
+ sensorEvent.values[1] * sensorEvent.values[1] +
+ sensorEvent.values[2] * sensorEvent.values[2];
+ mMagnitudes.add((float)Math.sqrt(magnitude));
+ }
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int i) {
+
+ }
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/helpers/PowerTestHostLink.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/helpers/PowerTestHostLink.java
index d53005f..ca553cf 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/helpers/PowerTestHostLink.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/helpers/PowerTestHostLink.java
@@ -77,6 +77,7 @@
*/
public final static String SOCKET_NAME = "/android/cts/powertest";
+ private Context mContext;
private volatile boolean mStopThread;
private final SensorManager mSensorManager;
private final HostToDeviceInterface mHostToDeviceExecutor;
@@ -86,6 +87,7 @@
Log.d(TAG, " +++ Begin of localSocketServer() +++ ");
mHostToDeviceExecutor = listener;
mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
+ mContext = context;
}
/**
@@ -217,7 +219,8 @@
response = RESPONSE_OK;
}
} else if (request.startsWith(REQUEST_EXTERNAL_STORAGE)){
- response = SensorCtsHelper.getSensorTestDataDirectory("power/").getAbsolutePath();
+ response = SensorCtsHelper.getSensorTestDataDirectory(mContext, "power/")
+ .getAbsolutePath();
Log.d(TAG,"External storage is " + response);
} else if (request.startsWith(REQUEST_SCREEN_OFF)) {
try {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/wifi/testcase/NetworkSuggestionTestCase.java b/apps/CtsVerifier/src/com/android/cts/verifier/wifi/testcase/NetworkSuggestionTestCase.java
index 316aac8..53bcc4a 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/wifi/testcase/NetworkSuggestionTestCase.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/wifi/testcase/NetworkSuggestionTestCase.java
@@ -41,6 +41,8 @@
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
@@ -51,9 +53,11 @@
private static final String TAG = "NetworkSuggestionTestCase";
private static final boolean DBG = true;
+ private static final int PERIODIC_SCAN_INTERVAL_MS = 10_000;
private static final int CALLBACK_TIMEOUT_MS = 40_000;
private final Object mLock = new Object();
+ private final ScheduledExecutorService mExecutorService;
private final List<WifiNetworkSuggestion> mNetworkSuggestions = new ArrayList<>();
private ConnectivityManager mConnectivityManager;
@@ -68,6 +72,7 @@
public NetworkSuggestionTestCase(Context context, boolean setBssid,
boolean setRequiresAppInteraction) {
super(context);
+ mExecutorService = Executors.newSingleThreadScheduledExecutor();
mSetBssid = setBssid;
mSetRequiresAppInteraction = setRequiresAppInteraction;
}
@@ -143,7 +148,15 @@
return false;
}
- // Step 5: Wait for connection.
+ // Step 5: Trigger scans periodically to trigger network selection quicker.
+ if (DBG) Log.v(TAG, "Triggering scan periodically");
+ mExecutorService.scheduleAtFixedRate(() -> {
+ if (!mWifiManager.startScan()) {
+ Log.w(TAG, "Failed to trigger scan");
+ }
+ }, 0, PERIODIC_SCAN_INTERVAL_MS, TimeUnit.MILLISECONDS);
+
+ // Step 6: Wait for connection.
if (DBG) Log.v(TAG, "Waiting for connection");
mListener.onTestMsgReceived(mContext.getString(
R.string.wifi_status_suggestion_wait_for_connect));
@@ -156,7 +169,7 @@
mListener.onTestMsgReceived(
mContext.getString(R.string.wifi_status_suggestion_connect));
- // Step 6: Ensure that we connected to the suggested network (optionally, the correct
+ // Step 7: Ensure that we connected to the suggested network (optionally, the correct
// BSSID).
if (!mTestUtils.isConnected("\"" + openNetwork.SSID + "\"",
// TODO: This might fail if there are other BSSID's for the same network & the
@@ -185,7 +198,7 @@
mContext.getString(R.string.wifi_status_suggestion_post_connect_bcast));
}
- // Step 7: Remove the suggestions from the app.
+ // Step 8: Remove the suggestions from the app.
if (DBG) Log.v(TAG, "Removing suggestion");
mListener.onTestMsgReceived(mContext.getString(R.string.wifi_status_suggestion_remove));
if (mWifiManager.removeNetworkSuggestions(mNetworkSuggestions)
@@ -194,7 +207,7 @@
return false;
}
- // Step 8: Ensure we don't disconnect immediately on suggestion removal.
+ // Step 9: Ensure we don't disconnect immediately on suggestion removal.
mListener.onTestMsgReceived(
mContext.getString(R.string.wifi_status_suggestion_wait_for_disconnect));
if (DBG) Log.v(TAG, "Ensuring we don't disconnect immediately");
@@ -224,6 +237,7 @@
@Override
protected void tearDown() {
+ mExecutorService.shutdownNow();
if (mBroadcastReceiver != null) {
mContext.unregisterReceiver(mBroadcastReceiver);
}
diff --git a/tests/framework/base/windowmanager/dummyTests/dummySdk25/Android.mk b/apps/ForceStopHelperApp/Android.mk
similarity index 69%
rename from tests/framework/base/windowmanager/dummyTests/dummySdk25/Android.mk
rename to apps/ForceStopHelperApp/Android.mk
index 510ef5a..7ae586a 100644
--- a/tests/framework/base/windowmanager/dummyTests/dummySdk25/Android.mk
+++ b/apps/ForceStopHelperApp/Android.mk
@@ -1,4 +1,3 @@
-#
# Copyright (C) 2019 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,22 +12,24 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-LOCAL_PATH := $(call my-dir)
+LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := tests optional
+LOCAL_PACKAGE_NAME := CtsForceStopHelper
-# TODO(b/129909356): Remove this once CtsActivityManagerDeviceSdk25TestCases is renamed to
-# CtsWindowManagerSdk25TestCases
-LOCAL_PACKAGE_NAME := CtsActivityManagerDeviceSdk25TestCases
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_SDK_VERSION := 25
+LOCAL_RESOURCE_DIR += $(LOCAL_PATH)/res
-LOCAL_STATIC_JAVA_LIBRARIES := androidx.test.rules
+LOCAL_SDK_VERSION := current
+LOCAL_MIN_SDK_VERSION := 12
+# tag this module as a cts test artifact
LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
include $(BUILD_CTS_PACKAGE)
diff --git a/apps/ForceStopHelperApp/AndroidManifest.xml b/apps/ForceStopHelperApp/AndroidManifest.xml
new file mode 100644
index 0000000..2bd9b0d
--- /dev/null
+++ b/apps/ForceStopHelperApp/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.cts.forcestophelper" >
+
+ <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
+
+ <application android:label="Force stop helper app">
+ <activity android:name=".RecentTaskActivity"
+ android:exported="true" />
+ <service android:name=".TaskRemovedListenerService"
+ android:stopWithTask="false"/>
+ <receiver android:name=".AlarmReceiver"
+ android:exported="true" />
+ </application>
+</manifest>
diff --git a/apps/ForceStopHelperApp/res/drawable-hdpi/cts_verifier.png b/apps/ForceStopHelperApp/res/drawable-hdpi/cts_verifier.png
new file mode 100644
index 0000000..98a3246
--- /dev/null
+++ b/apps/ForceStopHelperApp/res/drawable-hdpi/cts_verifier.png
Binary files differ
diff --git a/apps/ForceStopHelperApp/res/layout/main.xml b/apps/ForceStopHelperApp/res/layout/main.xml
new file mode 100644
index 0000000..ce2c29d
--- /dev/null
+++ b/apps/ForceStopHelperApp/res/layout/main.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.
+ -->
+
+<RelativeLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_margin="8dp">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/status_message"
+ android:text="@string/status_finished"
+ android:visibility="invisible" />
+
+ </LinearLayout>
+
+</RelativeLayout>
diff --git a/apps/ForceStopHelperApp/res/values/strings.xml b/apps/ForceStopHelperApp/res/values/strings.xml
new file mode 100644
index 0000000..d88c976
--- /dev/null
+++ b/apps/ForceStopHelperApp/res/values/strings.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources>
+ <string name="status_finished">Setup complete. Please return to the cts verifier</string>
+</resources>
diff --git a/apps/ForceStopHelperApp/src/com/android/cts/forcestophelper/AlarmReceiver.java b/apps/ForceStopHelperApp/src/com/android/cts/forcestophelper/AlarmReceiver.java
new file mode 100644
index 0000000..a7927aa
--- /dev/null
+++ b/apps/ForceStopHelperApp/src/com/android/cts/forcestophelper/AlarmReceiver.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 com.android.cts.forcestophelper;
+
+import static com.android.cts.forcestophelper.Constants.ACTION_ALARM;
+import static com.android.cts.forcestophelper.Constants.EXTRA_ON_ALARM;
+
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+
+public class AlarmReceiver extends BroadcastReceiver {
+
+ private static final String TAG = AlarmReceiver.class.getSimpleName();
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (ACTION_ALARM.equals(intent.getAction())) {
+ final PendingIntent onAlarm = intent.getParcelableExtra(EXTRA_ON_ALARM);
+ if (onAlarm != null) {
+ try {
+ onAlarm.send();
+ } catch (PendingIntent.CanceledException e) {
+ Log.w(TAG, "onAlarm pending intent was canceled", e);
+ }
+ } else {
+ Log.e(TAG, "Could not find pending intent extra " + EXTRA_ON_ALARM);
+ }
+ }
+ }
+
+ public static PendingIntent createAlarmPendingIntent(Context context, PendingIntent onAlarm) {
+ final Intent alarmIntent = new Intent(ACTION_ALARM)
+ .setClass(context, AlarmReceiver.class)
+ .putExtra(EXTRA_ON_ALARM, onAlarm);
+ return PendingIntent.getBroadcast(context, 0, alarmIntent,
+ PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_UPDATE_CURRENT);
+ }
+}
diff --git a/apps/ForceStopHelperApp/src/com/android/cts/forcestophelper/Constants.java b/apps/ForceStopHelperApp/src/com/android/cts/forcestophelper/Constants.java
new file mode 100644
index 0000000..a8e39d2
--- /dev/null
+++ b/apps/ForceStopHelperApp/src/com/android/cts/forcestophelper/Constants.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.forcestophelper;
+
+public interface Constants {
+ String PACKAGE_NAME = "com.android.cts.forcestophelper";
+ String ACTIVITY_CLASS_NAME = "com.android.cts.forcestophelper.RecentTaskActivity";
+ String ACTION_ALARM = PACKAGE_NAME + ".action.ACTION_ALARM";
+ String EXTRA_ON_TASK_REMOVED = PACKAGE_NAME + ".extra.ON_TASK_REMOVED";
+ String EXTRA_ON_ALARM = PACKAGE_NAME + ".extra.ON_ALARM";
+ long ALARM_DELAY = 5_000;
+}
diff --git a/apps/ForceStopHelperApp/src/com/android/cts/forcestophelper/RecentTaskActivity.java b/apps/ForceStopHelperApp/src/com/android/cts/forcestophelper/RecentTaskActivity.java
new file mode 100644
index 0000000..6f853ee
--- /dev/null
+++ b/apps/ForceStopHelperApp/src/com/android/cts/forcestophelper/RecentTaskActivity.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.forcestophelper;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.TextView;
+
+/**
+ * Test activity to show up as a task in recents.
+ */
+public class RecentTaskActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.main);
+ final Bundle extras = getIntent().getExtras();
+
+ final Intent serviceIntent = new Intent();
+ serviceIntent.setClass(this, TaskRemovedListenerService.class);
+ if (extras != null) {
+ serviceIntent.putExtras(extras);
+ }
+ startService(serviceIntent);
+
+ final TextView status = findViewById(R.id.status_message);
+ status.setVisibility(View.VISIBLE);
+ }
+}
diff --git a/apps/ForceStopHelperApp/src/com/android/cts/forcestophelper/TaskRemovedListenerService.java b/apps/ForceStopHelperApp/src/com/android/cts/forcestophelper/TaskRemovedListenerService.java
new file mode 100644
index 0000000..3955d45
--- /dev/null
+++ b/apps/ForceStopHelperApp/src/com/android/cts/forcestophelper/TaskRemovedListenerService.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.cts.forcestophelper;
+
+import static com.android.cts.forcestophelper.Constants.ALARM_DELAY;
+import static com.android.cts.forcestophelper.Constants.EXTRA_ON_ALARM;
+import static com.android.cts.forcestophelper.Constants.EXTRA_ON_TASK_REMOVED;
+import static com.android.cts.forcestophelper.AlarmReceiver.createAlarmPendingIntent;
+
+import android.app.AlarmManager;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+import android.util.Log;
+
+/**
+ * Service that listens for {@link #onTaskRemoved(Intent)} for any task in this application.
+ */
+public class TaskRemovedListenerService extends Service {
+ private static final String TAG = TaskRemovedListenerService.class.getSimpleName();
+
+ private PendingIntent mOnTaskRemoved;
+ private PendingIntent mOnAlarm;
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ mOnTaskRemoved = intent.getParcelableExtra(EXTRA_ON_TASK_REMOVED);
+ mOnAlarm = intent.getParcelableExtra(EXTRA_ON_ALARM);
+
+ if (mOnTaskRemoved != null && mOnAlarm != null) {
+ final NotificationManager nm = getSystemService(NotificationManager.class);
+ nm.createNotificationChannel(
+ new NotificationChannel(TAG, TAG, NotificationManager.IMPORTANCE_DEFAULT));
+ final Notification notification = new Notification.Builder(this, TAG)
+ .setSmallIcon(R.drawable.cts_verifier)
+ .setContentTitle("Test Service")
+ .build();
+ startForeground(1, notification);
+ } else {
+ Log.e(TAG, "Need pending intents for onAlarm and onTaskRemoved. Stopping service.");
+ stopSelf();
+ }
+ return START_NOT_STICKY;
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ if (mOnTaskRemoved != null) {
+ Log.e(TAG, "Stopping without delivering mOnTaskRemoved");
+ }
+ }
+
+ @Override
+ public void onTaskRemoved(Intent rootIntent) {
+ super.onTaskRemoved(rootIntent);
+ if (mOnTaskRemoved != null) {
+ try {
+ mOnTaskRemoved.send();
+ } catch (PendingIntent.CanceledException e) {
+ Log.w(TAG, "mOnTaskRemoved was canceled", e);
+ }
+ mOnTaskRemoved = null;
+ }
+ final PendingIntent alarmPi = createAlarmPendingIntent(this, mOnAlarm);
+ final AlarmManager am = getSystemService(AlarmManager.class);
+ am.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, ALARM_DELAY, alarmPi);
+
+ stopSelf();
+ }
+}
diff --git a/apps/NotificationBot/src/com/android/cts/robot/NotificationBot.java b/apps/NotificationBot/src/com/android/cts/robot/NotificationBot.java
index 0fb1d65..60991b2 100644
--- a/apps/NotificationBot/src/com/android/cts/robot/NotificationBot.java
+++ b/apps/NotificationBot/src/com/android/cts/robot/NotificationBot.java
@@ -36,7 +36,7 @@
public class NotificationBot extends BroadcastReceiver {
private static final String TAG = "NotificationBot";
- private static final String NOTIFICATION_CHANNEL_ID = TAG;
+ private static final String NOTIFICATION_CHANNEL_ID = TAG + "_high";
private static final String EXTRA_ID = "ID";
private static final String EXTRA_NOTIFICATION = "NOTIFICATION";
private static final String ACTION_POST = "com.android.cts.robot.ACTION_POST";
@@ -139,7 +139,7 @@
context.getSystemService(NotificationManager.class);
notificationManager.createNotificationChannel(new NotificationChannel(
NOTIFICATION_CHANNEL_ID, NOTIFICATION_CHANNEL_ID,
- NotificationManager.IMPORTANCE_DEFAULT));
+ NotificationManager.IMPORTANCE_HIGH));
final Notification.Builder nb = new Notification.Builder(context, NOTIFICATION_CHANNEL_ID)
.setContentTitle(intent.getStringExtra(EXTRA_NOTIFICATION_TITLE))
.setSmallIcon(android.R.drawable.ic_popup_sync)
diff --git a/build/device_info_package.mk b/build/device_info_package.mk
index 1973ef9..09e062e 100644
--- a/build/device_info_package.mk
+++ b/build/device_info_package.mk
@@ -24,6 +24,7 @@
android.permission.WRITE_EXTERNAL_STORAGE
DEVICE_INFO_ACTIVITIES += \
$(DEVICE_INFO_PACKAGE).AppStandbyDeviceInfo \
+ $(DEVICE_INFO_PACKAGE).ClientIdDeviceInfo \
$(DEVICE_INFO_PACKAGE).ConfigurationDeviceInfo \
$(DEVICE_INFO_PACKAGE).CpuDeviceInfo \
$(DEVICE_INFO_PACKAGE).FeatureDeviceInfo \
diff --git a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/ClientIdDeviceInfo.java b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/ClientIdDeviceInfo.java
new file mode 100644
index 0000000..001dbb2
--- /dev/null
+++ b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/ClientIdDeviceInfo.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.compatibility.common.deviceinfo;
+
+import android.util.Log;
+
+import com.android.compatibility.common.util.DeviceInfoStore;
+import com.android.compatibility.common.util.PropertyUtil;
+
+import java.io.IOException;
+import java.util.Map;
+
+/**
+ * Client ID info collector.
+ */
+public final class ClientIdDeviceInfo extends DeviceInfo {
+
+ private static final String LOG_TAG = "ClientIdDeviceInfo";
+
+ @Override
+ protected void collectDeviceInfo(DeviceInfoStore store) throws Exception {
+ try {
+ collectClientIds(store);
+ } catch (IOException e) {
+ Log.w(LOG_TAG, "Failed to collect client IDs", e);
+ }
+ }
+
+ private void collectClientIds(DeviceInfoStore store) throws IOException {
+ store.startArray("client_id");
+ Map<String, String> clientIds = PropertyUtil.getClientIds();
+ for (String name : clientIds.keySet()) {
+ store.startGroup();
+ store.addResult("name", name);
+ store.addResult("value", clientIds.get(name));
+ store.endGroup();
+ }
+ store.endArray();
+ }
+}
diff --git a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/MediaDeviceInfo.java b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/MediaDeviceInfo.java
index 91cc58d..ba1cefe 100644
--- a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/MediaDeviceInfo.java
+++ b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/MediaDeviceInfo.java
@@ -46,11 +46,15 @@
store.startGroup();
store.addResult("name", info.getName());
+ store.addResult("canonical", info.getCanonicalName());
store.addResult("encoder", info.isEncoder());
+ store.addResult("alias", info.isAlias());
+ store.addResult("software", info.isSoftwareOnly());
+ store.addResult("hardware", info.isHardwareAccelerated());
+ store.addResult("vendor", info.isVendor());
store.startArray("supported_type");
for (String type : info.getSupportedTypes()) {
-
store.startGroup();
store.addResult("type", type);
CodecCapabilities codecCapabilities = info.getCapabilitiesForType(type);
@@ -64,6 +68,13 @@
}
store.endArray(); // codec_profile_level
}
+ if (codecCapabilities.colorFormats.length > 0) {
+ store.startArray("codec_color_format");
+ for (int format : codecCapabilities.colorFormats) {
+ store.addResult("format", format);
+ }
+ store.endArray(); // codec_color_format
+ }
store.addResult("supported_secure_playback", codecCapabilities.isFeatureSupported(
CodecCapabilities.FEATURE_SecurePlayback));
VideoCapabilities videoCapabilities = codecCapabilities.getVideoCapabilities();
diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/DeviceReportLog.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/DeviceReportLog.java
index d170263..c680fc3 100644
--- a/common/device-side/util-axt/src/com/android/compatibility/common/util/DeviceReportLog.java
+++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/DeviceReportLog.java
@@ -25,6 +25,8 @@
import java.io.IOException;
import java.util.List;
+import androidx.test.InstrumentationRegistry;
+
/**
* Handles adding results to the report for device side tests.
*
@@ -40,8 +42,10 @@
private ReportLogDeviceInfoStore store;
public DeviceReportLog(String reportLogName, String streamName) {
- this(reportLogName, streamName,
- new File(Environment.getExternalStorageDirectory(), "report-log-files"));
+ this(reportLogName, streamName, new File(InstrumentationRegistry
+ .getInstrumentation().getTargetContext()
+ .getExternalFilesDir(null).getPath(),
+ "report-log-files"));
}
public DeviceReportLog(String reportLogName, String streamName, File logDirectory) {
diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/MediaUtils.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/MediaUtils.java
index 16c42b2..772009d 100644
--- a/common/device-side/util-axt/src/com/android/compatibility/common/util/MediaUtils.java
+++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/MediaUtils.java
@@ -205,9 +205,12 @@
// 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()) {
+ for (MediaCodecInfo info : sMCL.getCodecInfos()) {
+ if (info.isAlias()) {
+ // don't consider aliases here
+ continue;
+ }
if (info.isEncoder() != isEncoder) {
continue;
}
diff --git a/common/device-side/util/Android.bp b/common/device-side/util/Android.bp
index 134b4f7..e1552b6 100644
--- a/common/device-side/util/Android.bp
+++ b/common/device-side/util/Android.bp
@@ -22,6 +22,7 @@
],
static_libs: [
+ "androidx.test.rules",
"compatibility-common-util-devicesidelib",
"android-support-test",
"ub-uiautomator",
@@ -36,4 +37,4 @@
],
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/DeviceReportLog.java b/common/device-side/util/src/com/android/compatibility/common/util/DeviceReportLog.java
index d170263..12c9f1a 100644
--- 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
@@ -21,6 +21,8 @@
import android.os.Environment;
import android.util.Log;
+import androidx.test.InstrumentationRegistry;
+
import java.io.File;
import java.io.IOException;
import java.util.List;
@@ -40,8 +42,10 @@
private ReportLogDeviceInfoStore store;
public DeviceReportLog(String reportLogName, String streamName) {
- this(reportLogName, streamName,
- new File(Environment.getExternalStorageDirectory(), "report-log-files"));
+ this(reportLogName, streamName, new File(InstrumentationRegistry
+ .getInstrumentation().getTargetContext()
+ .getExternalFilesDir(null).getPath(),
+ "report-log-files"));
}
public DeviceReportLog(String reportLogName, String streamName, File logDirectory) {
diff --git a/hostsidetests/angle/src/android/angle/cts/CtsAngleCommon.java b/hostsidetests/angle/src/android/angle/cts/CtsAngleCommon.java
index c3e9c11..3a45a3c 100644
--- a/hostsidetests/angle/src/android/angle/cts/CtsAngleCommon.java
+++ b/hostsidetests/angle/src/android/angle/cts/CtsAngleCommon.java
@@ -105,18 +105,9 @@
String propDisablePreloading = device.getProperty(PROPERTY_DISABLE_OPENGL_PRELOADING);
String propGfxDriver = device.getProperty(PROPERTY_GFX_DRIVER);
- // Make sure ANGLE exists on the device
- if((angleSupported == null) || (angleSupported.equals("false"))) {
- return false;
- }
-
- // This logic is attempting to mimic ZygoteInit.java::ZygoteInit#preloadOpenGL()
- if (((propDisablePreloading != null) && propDisablePreloading.equals("false")) &&
- ((propGfxDriver == null) || propGfxDriver.isEmpty())) {
- return false;
- }
-
- return true;
+ return (angleSupported != null) && (angleSupported.equals("true")) &&
+ (((propDisablePreloading != null) && propDisablePreloading.equals("true")) ||
+ ((propGfxDriver != null) && !propGfxDriver.isEmpty()));
}
static void startActivity(ITestDevice device, String action) throws Exception {
diff --git a/hostsidetests/angle/src/android/angle/cts/CtsAngleDeveloperOptionHostTest.java b/hostsidetests/angle/src/android/angle/cts/CtsAngleDeveloperOptionHostTest.java
index 3b76b5d7..5373a3b 100644
--- a/hostsidetests/angle/src/android/angle/cts/CtsAngleDeveloperOptionHostTest.java
+++ b/hostsidetests/angle/src/android/angle/cts/CtsAngleDeveloperOptionHostTest.java
@@ -92,6 +92,9 @@
public void testEnableAngleForAll() throws Exception {
Assume.assumeTrue(isAngleLoadable(getDevice()));
+ installPackage(ANGLE_DRIVER_TEST_APP);
+ installPackage(ANGLE_DRIVER_TEST_SEC_APP);
+
setAndValidateAngleDevOptionPkgDriver(ANGLE_DRIVER_TEST_PKG,
sDriverGlobalSettingMap.get(OpenGlDriverChoice.DEFAULT));
setAndValidateAngleDevOptionPkgDriver(ANGLE_DRIVER_TEST_SEC_PKG,
@@ -99,9 +102,6 @@
setGlobalSetting(getDevice(), SETTINGS_GLOBAL_ALL_USE_ANGLE, "1");
- installPackage(ANGLE_DRIVER_TEST_APP);
- installPackage(ANGLE_DRIVER_TEST_SEC_APP);
-
waitThenRunDeviceTests(this, ANGLE_DRIVER_TEST_PKG,
ANGLE_DRIVER_TEST_PKG + "." + ANGLE_DRIVER_TEST_CLASS,
ANGLE_DRIVER_TEST_ANGLE_METHOD);
@@ -117,11 +117,11 @@
public void testUseDefaultDriver() throws Exception {
Assume.assumeTrue(isAngleLoadable(getDevice()));
+ installPackage(ANGLE_DRIVER_TEST_APP);
+
setAndValidateAngleDevOptionPkgDriver(ANGLE_DRIVER_TEST_PKG,
sDriverGlobalSettingMap.get(OpenGlDriverChoice.DEFAULT));
- installPackage(ANGLE_DRIVER_TEST_APP);
-
waitThenRunDeviceTests(this, ANGLE_DRIVER_TEST_PKG,
ANGLE_DRIVER_TEST_PKG + "." + ANGLE_DRIVER_TEST_CLASS,
ANGLE_DRIVER_TEST_DEFAULT_METHOD);
@@ -134,11 +134,11 @@
public void testUseAngleDriver() throws Exception {
Assume.assumeTrue(isAngleLoadable(getDevice()));
+ installPackage(ANGLE_DRIVER_TEST_APP);
+
setAndValidateAngleDevOptionPkgDriver(ANGLE_DRIVER_TEST_PKG,
sDriverGlobalSettingMap.get(OpenGlDriverChoice.ANGLE));
- installPackage(ANGLE_DRIVER_TEST_APP);
-
waitThenRunDeviceTests(this, ANGLE_DRIVER_TEST_PKG,
ANGLE_DRIVER_TEST_PKG + "." + ANGLE_DRIVER_TEST_CLASS,
ANGLE_DRIVER_TEST_ANGLE_METHOD);
@@ -151,11 +151,11 @@
public void testUseNativeDriver() throws Exception {
Assume.assumeTrue(isAngleLoadable(getDevice()));
+ installPackage(ANGLE_DRIVER_TEST_APP);
+
setAndValidateAngleDevOptionPkgDriver(ANGLE_DRIVER_TEST_PKG,
sDriverGlobalSettingMap.get(OpenGlDriverChoice.NATIVE));
- installPackage(ANGLE_DRIVER_TEST_APP);
-
waitThenRunDeviceTests(this, ANGLE_DRIVER_TEST_PKG,
ANGLE_DRIVER_TEST_PKG + "." + ANGLE_DRIVER_TEST_CLASS,
ANGLE_DRIVER_TEST_NATIVE_METHOD);
@@ -168,13 +168,13 @@
public void testSettingsLengthMismatch() throws Exception {
Assume.assumeTrue(isAngleLoadable(getDevice()));
+ installPackage(ANGLE_DRIVER_TEST_APP);
+ installPackage(ANGLE_DRIVER_TEST_SEC_APP);
+
setAndValidateAngleDevOptionPkgDriver(ANGLE_DRIVER_TEST_PKG + "," +
ANGLE_DRIVER_TEST_SEC_PKG,
sDriverGlobalSettingMap.get(OpenGlDriverChoice.ANGLE));
- installPackage(ANGLE_DRIVER_TEST_APP);
- installPackage(ANGLE_DRIVER_TEST_SEC_APP);
-
waitThenRunDeviceTests(this, ANGLE_DRIVER_TEST_PKG,
ANGLE_DRIVER_TEST_PKG + "." + ANGLE_DRIVER_TEST_CLASS,
ANGLE_DRIVER_TEST_DEFAULT_METHOD);
@@ -191,10 +191,10 @@
public void testUseInvalidDriver() throws Exception {
Assume.assumeTrue(isAngleLoadable(getDevice()));
- setAndValidateAngleDevOptionPkgDriver(ANGLE_DRIVER_TEST_PKG, "timtim");
-
installPackage(ANGLE_DRIVER_TEST_APP);
+ setAndValidateAngleDevOptionPkgDriver(ANGLE_DRIVER_TEST_PKG, "timtim");
+
waitThenRunDeviceTests(this, ANGLE_DRIVER_TEST_PKG,
ANGLE_DRIVER_TEST_PKG + "." + ANGLE_DRIVER_TEST_CLASS,
ANGLE_DRIVER_TEST_DEFAULT_METHOD);
@@ -229,14 +229,14 @@
public void testMultipleDevOptionsAngleNative() throws Exception {
Assume.assumeTrue(isAngleLoadable(getDevice()));
+ installPackage(ANGLE_DRIVER_TEST_APP);
+ installPackage(ANGLE_DRIVER_TEST_SEC_APP);
+
setAndValidateAngleDevOptionPkgDriver(ANGLE_DRIVER_TEST_PKG + "," +
ANGLE_DRIVER_TEST_SEC_PKG,
sDriverGlobalSettingMap.get(OpenGlDriverChoice.ANGLE) + "," +
sDriverGlobalSettingMap.get(OpenGlDriverChoice.NATIVE));
- installPackage(ANGLE_DRIVER_TEST_APP);
- installPackage(ANGLE_DRIVER_TEST_SEC_APP);
-
waitThenRunDeviceTests(this, ANGLE_DRIVER_TEST_PKG,
ANGLE_DRIVER_TEST_PKG + "." + ANGLE_DRIVER_TEST_CLASS,
ANGLE_DRIVER_TEST_ANGLE_METHOD);
@@ -315,12 +315,12 @@
public void testDefaultNotInSettings() throws Exception {
Assume.assumeTrue(isAngleLoadable(getDevice()));
- setAndValidateAngleDevOptionPkgDriver(ANGLE_DRIVER_TEST_PKG,
- sDriverGlobalSettingMap.get(OpenGlDriverChoice.DEFAULT));
-
// Install the package so the setting isn't removed because the package isn't present.
installPackage(ANGLE_DRIVER_TEST_APP);
+ setAndValidateAngleDevOptionPkgDriver(ANGLE_DRIVER_TEST_PKG,
+ sDriverGlobalSettingMap.get(OpenGlDriverChoice.DEFAULT));
+
// Run the ANGLE activity so it'll clear up any 'default' settings.
startActivity(getDevice(), ANGLE_MAIN_ACTIVTY);
@@ -376,13 +376,13 @@
public void testMultipleDevOptionsAngleDefault() throws Exception {
Assume.assumeTrue(isAngleLoadable(getDevice()));
+ installPackage(ANGLE_DRIVER_TEST_APP);
+ installPackage(ANGLE_DRIVER_TEST_SEC_APP);
+
setAndValidateAngleDevOptionPkgDriver(ANGLE_DRIVER_TEST_PKG + "," + ANGLE_DRIVER_TEST_SEC_PKG,
sDriverGlobalSettingMap.get(OpenGlDriverChoice.ANGLE) + "," +
sDriverGlobalSettingMap.get(OpenGlDriverChoice.DEFAULT));
- installPackage(ANGLE_DRIVER_TEST_APP);
- installPackage(ANGLE_DRIVER_TEST_SEC_APP);
-
// Run the ANGLE activity so it'll clear up any 'default' settings.
startActivity(getDevice(), ANGLE_MAIN_ACTIVTY);
@@ -408,12 +408,12 @@
public void testMultipleDevOptionsAngleNativeUninstall() throws Exception {
Assume.assumeTrue(isAngleLoadable(getDevice()));
+ installPackage(ANGLE_DRIVER_TEST_SEC_APP);
+
setAndValidateAngleDevOptionPkgDriver(ANGLE_DRIVER_TEST_PKG + "," + ANGLE_DRIVER_TEST_SEC_PKG,
sDriverGlobalSettingMap.get(OpenGlDriverChoice.ANGLE) + "," +
sDriverGlobalSettingMap.get(OpenGlDriverChoice.NATIVE));
- installPackage(ANGLE_DRIVER_TEST_SEC_APP);
-
// Run the ANGLE activity so it'll clear up any 'default' settings.
startActivity(getDevice(), ANGLE_MAIN_ACTIVTY);
diff --git a/hostsidetests/appsecurity/AndroidTest.xml b/hostsidetests/appsecurity/AndroidTest.xml
index f06bac4..0ff742f 100644
--- a/hostsidetests/appsecurity/AndroidTest.xml
+++ b/hostsidetests/appsecurity/AndroidTest.xml
@@ -16,7 +16,7 @@
<configuration description="Config for the CTS App Security host tests">
<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_instant_app" />
<target_preparer class="android.appsecurity.cts.AppSecurityPreparer" />
<test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
<option name="jar" value="CtsAppSecurityHostTestCases.jar" />
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 d240864..be68d85 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
@@ -86,7 +86,7 @@
}
private void doSandboxed(boolean sandboxed) throws Exception {
- assertEquals(sandboxed, Environment.isExternalStorageSandboxed());
+ assertEquals(!sandboxed, Environment.isExternalStorageLegacy());
// We can always see mounted state
assertEquals(Environment.MEDIA_MOUNTED, Environment.getExternalStorageState());
@@ -256,7 +256,14 @@
}
try (ParcelFileDescriptor pfd = mContentResolver.openFileDescriptor(blue, "r")) {
}
- try (ParcelFileDescriptor pfd = mContentResolver.openFileDescriptor(blue, "w")) {
+ if (Environment.isExternalStorageLegacy()) {
+ try (ParcelFileDescriptor pfd = mContentResolver.openFileDescriptor(blue, "w")) {
+ }
+ } else {
+ try (ParcelFileDescriptor pfd = mContentResolver.openFileDescriptor(blue, "w")) {
+ fail("Expected write access to be blocked");
+ } catch (SecurityException | FileNotFoundException expected) {
+ }
}
}
diff --git a/tests/tests/permission2/Android.mk b/hostsidetests/appsecurity/test-apps/StorageAppA/Android.mk
old mode 100755
new mode 100644
similarity index 61%
copy from tests/tests/permission2/Android.mk
copy to hostsidetests/appsecurity/test-apps/StorageAppA/Android.mk
index a058ab8..75adae4
--- a/tests/tests/permission2/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/StorageAppA/Android.mk
@@ -1,5 +1,4 @@
-#
-# Copyright (C) 2009 The Android Open Source Project
+# 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.
@@ -12,32 +11,24 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-#
+
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := tests
-
-LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-
-# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests cts_instant
+LOCAL_SDK_VERSION := test_current
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ androidx.test.rules \
+ ub-uiautomator
LOCAL_JAVA_LIBRARIES := android.test.base.stubs
-LOCAL_STATIC_JAVA_LIBRARIES := \
- androidx.test.core \
- compatibility-device-util-axt \
- ctstestrunner-axt \
- guava
+LOCAL_SRC_FILES := $(call all-java-files-under, ../StorageApp/src/)
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_PACKAGE_NAME := CtsStorageAppA
-LOCAL_PACKAGE_NAME := CtsPermission2TestCases
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+LOCAL_DEX_PREOPT := false
-LOCAL_SDK_VERSION := test_current
-
-include $(BUILD_CTS_PACKAGE)
-
-include $(call all-makefiles-under,$(LOCAL_PATH))
+include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/hostsidetests/appsecurity/test-apps/StorageAppA/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/StorageAppA/AndroidManifest.xml
new file mode 100644
index 0000000..a9ae731
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/StorageAppA/AndroidManifest.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.cts.storageapp_a">
+
+ <application android:requestLegacyExternalStorage="true" android:appCategory="video">
+ <uses-library android:name="android.test.runner" />
+ <activity android:name="com.android.cts.storageapp.UtilsActivity" />
+ <receiver android:name="com.android.cts.storageapp.UtilsReceiver" android:exported="true" />
+ <provider
+ android:name="com.android.cts.storageapp.UtilsProvider"
+ android:authorities="com.android.cts.storageapp_a"
+ android:exported="true" />
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.cts.storageapp_a" />
+
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+
+</manifest>
diff --git a/tests/tests/permission2/Android.mk b/hostsidetests/appsecurity/test-apps/StorageAppB/Android.mk
old mode 100755
new mode 100644
similarity index 61%
copy from tests/tests/permission2/Android.mk
copy to hostsidetests/appsecurity/test-apps/StorageAppB/Android.mk
index a058ab8..bdcccaf
--- a/tests/tests/permission2/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/StorageAppB/Android.mk
@@ -1,5 +1,4 @@
-#
-# Copyright (C) 2009 The Android Open Source Project
+# 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.
@@ -12,32 +11,24 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-#
+
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := tests
-
-LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-
-# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests cts_instant
+LOCAL_SDK_VERSION := test_current
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ androidx.test.rules \
+ ub-uiautomator
LOCAL_JAVA_LIBRARIES := android.test.base.stubs
-LOCAL_STATIC_JAVA_LIBRARIES := \
- androidx.test.core \
- compatibility-device-util-axt \
- ctstestrunner-axt \
- guava
+LOCAL_SRC_FILES := $(call all-java-files-under, ../StorageApp/src/)
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_PACKAGE_NAME := CtsStorageAppB
-LOCAL_PACKAGE_NAME := CtsPermission2TestCases
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+LOCAL_DEX_PREOPT := false
-LOCAL_SDK_VERSION := test_current
-
-include $(BUILD_CTS_PACKAGE)
-
-include $(call all-makefiles-under,$(LOCAL_PATH))
+include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/hostsidetests/appsecurity/test-apps/StorageAppB/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/StorageAppB/AndroidManifest.xml
new file mode 100644
index 0000000..019815a
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/StorageAppB/AndroidManifest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.cts.storageapp_b">
+
+ <application android:requestLegacyExternalStorage="true">
+ <uses-library android:name="android.test.runner" />
+ <receiver android:name="com.android.cts.storageapp.UtilsReceiver" android:exported="true" />
+ <provider
+ android:name="com.android.cts.storageapp.UtilsProvider"
+ android:authorities="com.android.cts.storageapp_b"
+ android:exported="true" />
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.cts.storageapp_b" />
+
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+
+</manifest>
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 956baeb..2723a6d 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
@@ -34,6 +34,7 @@
import android.os.SystemClock;
import android.provider.Settings;
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;
@@ -59,6 +60,7 @@
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeoutException;
+import java.util.regex.Pattern;
@RunWith(AndroidJUnit4.class)
public abstract class BasePermissionsTest {
@@ -445,7 +447,8 @@
String confirmTitle = CaseMap.toUpper().apply(
resources.getConfiguration().getLocales().get(0),
resources.getString(confirmResId));
- getUiDevice().wait(Until.findObject(By.textStartsWith(confirmTitle)),
+ getUiDevice().wait(Until.findObject(
+ byTextStartsWithCaseInsensitive(confirmTitle)),
GLOBAL_TIMEOUT_MILLIS).click();
waitForIdle();
@@ -462,6 +465,10 @@
waitForIdle();
}
+ private BySelector byTextStartsWithCaseInsensitive(String prefix) {
+ return By.text(Pattern.compile(String.format("(?i)^%s.*$", Pattern.quote(prefix))));
+ }
+
private String getPermissionLabel(String permission) throws Exception {
String labelResName = sPermissionToLabelResNameMap.get(permission);
assertNotNull("Unknown permisison " + permission, labelResName);
diff --git a/hostsidetests/content/AndroidTest.xml b/hostsidetests/content/AndroidTest.xml
index 300d94b..e13a5d9 100644
--- a/hostsidetests/content/AndroidTest.xml
+++ b/hostsidetests/content/AndroidTest.xml
@@ -18,6 +18,7 @@
<option name="config-descriptor:metadata" key="component" value="framework" />
<!-- This is a test for account data sync and READ_SYNC_SETTINGS/WRITE_SYNC_SETTINGS are required, which instant apps don't have -->
<option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
<test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
<option name="jar" value="CtsSyncContentHostTestCases.jar" />
<option name="runtime-hint" value="2m" />
diff --git a/hostsidetests/devicepolicy/app/ContentCaptureApp/AndroidManifest.xml b/hostsidetests/devicepolicy/app/ContentCaptureApp/AndroidManifest.xml
index a0d7b30..9d0bd25 100644
--- a/hostsidetests/devicepolicy/app/ContentCaptureApp/AndroidManifest.xml
+++ b/hostsidetests/devicepolicy/app/ContentCaptureApp/AndroidManifest.xml
@@ -25,14 +25,6 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
-
- <service
- android:name=".SimpleContentCaptureService"
- android:permission="android.permission.BIND_CONTENT_CAPTURE_SERVICE">
- <intent-filter>
- <action android:name="android.service.contentcapture.ContentCaptureService" />
- </intent-filter>
- </service>
</application>
</manifest>
\ No newline at end of file
diff --git a/hostsidetests/devicepolicy/app/ContentCaptureApp/src/com/android/cts/devicepolicy/contentcaptureapp/SimpleActivity.java b/hostsidetests/devicepolicy/app/ContentCaptureApp/src/com/android/cts/devicepolicy/contentcaptureapp/SimpleActivity.java
index 5ed3bb2..5f44044 100644
--- a/hostsidetests/devicepolicy/app/ContentCaptureApp/src/com/android/cts/devicepolicy/contentcaptureapp/SimpleActivity.java
+++ b/hostsidetests/devicepolicy/app/ContentCaptureApp/src/com/android/cts/devicepolicy/contentcaptureapp/SimpleActivity.java
@@ -17,25 +17,55 @@
package com.android.cts.devicepolicy.contentcaptureapp;
import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
import android.util.Log;
import android.view.contentcapture.ContentCaptureManager;
public class SimpleActivity extends Activity {
- public static final String TAG = SimpleActivity.class.getSimpleName();
+ public static final String TAG = SimpleActivity.class.getSimpleName() + "contentcapture";
+
+ private SimpleActivityReceiver mReceiver;
+
+ private ContentCaptureManager mManager;
+
+ @Override
+ protected void onCreate(Bundle saveInstanceState) {
+ super.onCreate(saveInstanceState);
+ mReceiver = new SimpleActivityReceiver();
+ registerReceiver(mReceiver, new IntentFilter(SimpleActivityReceiver.ACTION));
+ }
@Override
public void onStart() {
Log.d(TAG, "onStart(): userId=" + android.os.Process.myUserHandle().getIdentifier());
super.onStart();
- final ContentCaptureManager mgr = getSystemService(ContentCaptureManager.class);
- if (mgr == null) {
+ mManager = getSystemService(ContentCaptureManager.class);
+ if (mManager == null) {
Log.e(TAG, "no manager");
- return;
+ finish();
}
- final boolean enabled = mgr.isContentCaptureEnabled();
- Log.v(TAG, "enabled: " + enabled);
- setResult(enabled ? 1 : 0);
- finish();
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ unregisterReceiver(mReceiver);
+ }
+
+ private class SimpleActivityReceiver extends BroadcastReceiver {
+ private static final String ACTION = "SimpleActivityReceiver";
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final Intent broadcastIntent = new Intent()
+ .setAction("ContentCaptureActivityReceiver")
+ .putExtra("enabled", mManager.isContentCaptureEnabled());
+ sendBroadcast(broadcastIntent);
+ }
}
}
diff --git a/hostsidetests/devicepolicy/app/ContentCaptureService/Android.bp b/hostsidetests/devicepolicy/app/ContentCaptureService/Android.bp
new file mode 100644
index 0000000..5c98da2
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/ContentCaptureService/Android.bp
@@ -0,0 +1,27 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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: "CtsDevicePolicyContentCaptureService",
+ defaults: ["cts_defaults"],
+ 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/hostsidetests/devicepolicy/app/ContentCaptureService/AndroidManifest.xml b/hostsidetests/devicepolicy/app/ContentCaptureService/AndroidManifest.xml
new file mode 100644
index 0000000..0710df78d
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/ContentCaptureService/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.cts.devicepolicy.contentcaptureservice" >
+
+ <application>
+ <service
+ android:name=".SimpleContentCaptureService"
+ android:permission="android.permission.BIND_CONTENT_CAPTURE_SERVICE">
+ <intent-filter>
+ <action android:name="android.service.contentcapture.ContentCaptureService" />
+ </intent-filter>
+ </service>
+ </application>
+
+</manifest>
\ No newline at end of file
diff --git a/hostsidetests/devicepolicy/app/ContentCaptureApp/src/com/android/cts/devicepolicy/contentcaptureapp/SimpleContentCaptureService.java b/hostsidetests/devicepolicy/app/ContentCaptureService/src/com/android/cts/devicepolicy/contentcaptureservice/SimpleContentCaptureService.java
similarity index 75%
rename from hostsidetests/devicepolicy/app/ContentCaptureApp/src/com/android/cts/devicepolicy/contentcaptureapp/SimpleContentCaptureService.java
rename to hostsidetests/devicepolicy/app/ContentCaptureService/src/com/android/cts/devicepolicy/contentcaptureservice/SimpleContentCaptureService.java
index 8d11d6a..1133a7f 100644
--- a/hostsidetests/devicepolicy/app/ContentCaptureApp/src/com/android/cts/devicepolicy/contentcaptureapp/SimpleContentCaptureService.java
+++ b/hostsidetests/devicepolicy/app/ContentCaptureService/src/com/android/cts/devicepolicy/contentcaptureservice/SimpleContentCaptureService.java
@@ -14,9 +14,11 @@
* limitations under the License.
*/
-package com.android.cts.devicepolicy.contentcaptureapp;
+package com.android.cts.devicepolicy.contentcaptureservice;
+import android.content.ComponentName;
import android.service.contentcapture.ContentCaptureService;
+import android.util.ArraySet;
import android.util.Log;
import android.view.contentcapture.ContentCaptureContext;
import android.view.contentcapture.ContentCaptureSessionId;
@@ -28,6 +30,12 @@
@Override
public void onConnected() {
Log.d(TAG, "onConnected()");
+ final ArraySet<String> packages = new ArraySet<>();
+ packages.add("com.android.cts.devicepolicy.contentcaptureapp");
+ packages.add("com.android.cts.devicepolicy.contentcaptureservice");
+ packages.add("com.android.cts.deviceandprofileowner");
+
+ setContentCaptureWhitelist(packages, null);
}
@Override
diff --git a/hostsidetests/devicepolicy/app/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/CrossProfileAppsTargetUserTest.java b/hostsidetests/devicepolicy/app/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/CrossProfileAppsTargetUserTest.java
index 2aa1ae8..ffbafda 100644
--- a/hostsidetests/devicepolicy/app/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/CrossProfileAppsTargetUserTest.java
+++ b/hostsidetests/devicepolicy/app/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/CrossProfileAppsTargetUserTest.java
@@ -143,5 +143,18 @@
public void testGetProfileSwitchingIconDrawable() throws Exception {
assertNotNull(mCrossProfileApps.getProfileSwitchingIconDrawable(mTargetUser));
}
+
+ // Designed to be called by host-side tests; not a real test.
+ @Test
+ public void testStartMainActivity_noAsserts() {
+ mCrossProfileApps.startMainActivity(
+ MainActivity.getComponentName(mContext), mTargetUser);
+ }
+
+ // Designed to be called by host-side tests; not a real test.
+ @Test
+ public void testGetTargetUserProfiles_noAsserts() {
+ mCrossProfileApps.getTargetUserProfiles();
+ }
}
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 6a01ea6..fe2621e 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
@@ -23,6 +23,8 @@
* Tests for {@link DevicePolicyManager#resetPassword} for complex cases.
*
* This needs to be run as device owner, because in NYC DA can't clear or change the password.
+ * @deprecated New tests related to password quality and reset password API should be added to
+ * {@code com.android.cts.deviceandprofileowner.ResetPasswordWithTokenTest}
*/
public class DeviceOwnerPasswordTest extends BaseDeviceAdminTest {
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/latest/AndroidManifest.xml b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/latest/AndroidManifest.xml
index 6d77e7b..440d2f8 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/latest/AndroidManifest.xml
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/latest/AndroidManifest.xml
@@ -18,6 +18,7 @@
package="com.android.cts.deviceandprofileowner">
<uses-sdk android:minSdkVersion="23"/>
+ <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ContentCaptureActivity.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ContentCaptureActivity.java
index c2f27b7..e166095 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ContentCaptureActivity.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ContentCaptureActivity.java
@@ -17,9 +17,13 @@
package com.android.cts.deviceandprofileowner;
import android.app.Activity;
+import android.content.BroadcastReceiver;
import android.content.ComponentName;
+import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.os.Bundle;
+import android.util.Log;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -28,18 +32,23 @@
* Wrapper class used to call the activity in the non-test APK and wait for its result.
*/
public class ContentCaptureActivity extends Activity {
+ private static final String TAG = "ContentCaptureActivity";
public static final String CONTENT_CAPTURE_PACKAGE_NAME =
"com.android.cts.devicepolicy.contentcaptureapp";
public static final String CONTENT_CAPTURE_ACTIVITY_NAME = CONTENT_CAPTURE_PACKAGE_NAME
+ ".SimpleActivity";
- private final CountDownLatch mLatch = new CountDownLatch(1);
- private boolean mEnabled;
+ private CountDownLatch mEnabledLatch = new CountDownLatch(1);
+ private CountDownLatch mDisabledLatch = new CountDownLatch(1);
+
+ private ContentCaptureActivityReceiver mReceiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ mReceiver = new ContentCaptureActivityReceiver();
+ registerReceiver(mReceiver, new IntentFilter(ContentCaptureActivityReceiver.ACTION));
final Intent launchIntent = new Intent();
launchIntent.setComponent(
@@ -47,19 +56,42 @@
startActivityForResult(launchIntent, 42);
}
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- mEnabled = resultCode == 1;
- mLatch.countDown();
+ public void waitContentCaptureEnabled(boolean enabled) throws InterruptedException {
+ final Intent broadcastIntent = new Intent().setAction("SimpleActivityReceiver");
+ sendBroadcast(broadcastIntent);
+
+ final CountDownLatch latch = enabled ? mEnabledLatch : mDisabledLatch;
+ final boolean called = latch.await(2, TimeUnit.SECONDS);
+
+ if (!called) {
+ Log.e(TAG, CONTENT_CAPTURE_PACKAGE_NAME
+ + " didn't get " + (enabled ? "enabled" : "disabled") + " state in 2 seconds");
+ }
}
- public boolean isContentCaptureEnabled() throws InterruptedException {
- final boolean called = mLatch.await(2, TimeUnit.SECONDS);
- if (!called) {
- throw new IllegalStateException(CONTENT_CAPTURE_PACKAGE_NAME
- + " didn't finish in 2 seconds");
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ unregisterReceiver(mReceiver);
+ }
+
+ private class ContentCaptureActivityReceiver extends BroadcastReceiver {
+ private static final String ACTION = "ContentCaptureActivityReceiver";
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ boolean enabled = intent.getBooleanExtra("enabled", false);
+ if (enabled) {
+ if (mEnabledLatch.getCount() == 0) {
+ Log.e(TAG, "already received enabled broadcast");
+ }
+ mEnabledLatch.countDown();
+ } else {
+ if (mDisabledLatch.getCount() == 0) {
+ Log.e(TAG, "already received disabled broadcast");
+ }
+ mDisabledLatch.countDown();
+ }
}
- finish();
- return mEnabled;
}
}
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ContentCaptureRestrictionsTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ContentCaptureRestrictionsTest.java
index 95b953b..5995cfa 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ContentCaptureRestrictionsTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ContentCaptureRestrictionsTest.java
@@ -27,12 +27,12 @@
public class ContentCaptureRestrictionsTest extends BaseDeviceAdminTest {
// TODO(b/123540602): use @TestAPI to get max duration constant from ContentCaptureManager
- private static final int MAX_TIME_TEMPORARY_SERVICE_CAN_BE_SET= 12000;
+ private static final int MAX_TIME_TEMPORARY_SERVICE_CAN_BE_SET= 120000;
private static final int SLEEP_TIME_WAITING_FOR_SERVICE_CONNECTION_MS = 100;
private static final String SERVICE_NAME =
- "com.android.cts.devicepolicy.contentcapture/.SimpleContentCaptureService";
+ "com.android.cts.devicepolicy.contentcaptureservice/.SimpleContentCaptureService";
int mUserId;
@@ -57,32 +57,23 @@
public void testDisallowContentCapture_allowed() throws Exception {
enableService();
- final boolean enabledBefore = launchActivityAndGetEnabled();
- assertTrue(enabledBefore);
-
- mDevicePolicyManager.addUserRestriction(ADMIN_RECEIVER_COMPONENT, DISALLOW_CONTENT_CAPTURE);
-
- // Must try a couple times because it will be disabled asynchronously.
- for (int i = 1; i <= 5; i++) {
- final boolean disabledAfter = !launchActivityAndGetEnabled();
- if (disabledAfter) {
- return;
- }
- Thread.sleep(100);
- }
- fail("Not disabled after 2.5s");
- }
-
- private boolean launchActivityAndGetEnabled() throws Exception {
final Intent launchIntent = new Intent();
launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
launchIntent.setClassName(CONTENT_CAPTURE_PACKAGE_NAME, CONTENT_CAPTURE_ACTIVITY_NAME);
final ContentCaptureActivity activity = launchActivity(
"com.android.cts.deviceandprofileowner", ContentCaptureActivity.class, null);
- return activity.isContentCaptureEnabled();
+
+ activity.waitContentCaptureEnabled(true);
+
+ mDevicePolicyManager.addUserRestriction(ADMIN_RECEIVER_COMPONENT, DISALLOW_CONTENT_CAPTURE);
+
+ activity.waitContentCaptureEnabled(false);
}
private void enableService() throws Exception {
+ runShellCommand("settings put secure --user %d %s %d default",
+ mUserId, USER_SETUP_COMPLETE, 1);
+
runShellCommand("cmd content_capture set temporary-service %d %s %d", mUserId,
SERVICE_NAME, MAX_TIME_TEMPORARY_SERVICE_CAN_BE_SET);
// TODO: ideally it should wait until the service's onConnected() is called, but that
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ResetPasswordWithTokenTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ResetPasswordWithTokenTest.java
index 6cab658..72f01aa 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ResetPasswordWithTokenTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ResetPasswordWithTokenTest.java
@@ -15,6 +15,13 @@
*/
package com.android.cts.deviceandprofileowner;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
+
import android.app.KeyguardManager;
import android.app.admin.DevicePolicyManager;
@@ -45,6 +52,9 @@
if (mShouldRun) {
cleanUpResetPasswordToken();
}
+ resetComplexPasswordRestrictions();
+ mDevicePolicyManager.setPasswordQuality(ADMIN_RECEIVER_COMPONENT,
+ PASSWORD_QUALITY_UNSPECIFIED);
super.tearDown();
}
@@ -66,8 +76,7 @@
SHORT_PASSWORD, TOKEN0, 0));
// Set a strong password constraint and expect the sufficiency check to fail
- mDevicePolicyManager.setPasswordQuality(ADMIN_RECEIVER_COMPONENT,
- DevicePolicyManager.PASSWORD_QUALITY_NUMERIC);
+ mDevicePolicyManager.setPasswordQuality(ADMIN_RECEIVER_COMPONENT, PASSWORD_QUALITY_NUMERIC);
mDevicePolicyManager.setPasswordMinimumLength(ADMIN_RECEIVER_COMPONENT, 6);
assertPasswordSufficiency(false);
@@ -81,8 +90,7 @@
if (!mShouldRun) {
return;
}
- mDevicePolicyManager.setPasswordQuality(ADMIN_RECEIVER_COMPONENT,
- DevicePolicyManager.PASSWORD_QUALITY_NUMERIC);
+ mDevicePolicyManager.setPasswordQuality(ADMIN_RECEIVER_COMPONENT, PASSWORD_QUALITY_NUMERIC);
mDevicePolicyManager.setPasswordMinimumLength(ADMIN_RECEIVER_COMPONENT, 6);
assertFalse(mDevicePolicyManager.resetPasswordWithToken(ADMIN_RECEIVER_COMPONENT,
@@ -97,7 +105,7 @@
return;
}
mDevicePolicyManager.setPasswordQuality(ADMIN_RECEIVER_COMPONENT,
- DevicePolicyManager.PASSWORD_QUALITY_COMPLEX);
+ PASSWORD_QUALITY_COMPLEX);
mDevicePolicyManager.setPasswordMinimumNumeric(ADMIN_RECEIVER_COMPONENT, 1);
mDevicePolicyManager.setPasswordMinimumLetters(ADMIN_RECEIVER_COMPONENT, 1);
mDevicePolicyManager.setPasswordMinimumSymbols(ADMIN_RECEIVER_COMPONENT, 0);
@@ -131,6 +139,369 @@
assertFalse(km.isDeviceSecure());
}
+ public void testPasswordQuality_something() {
+ if (!mShouldRun) {
+ return;
+ }
+
+ mDevicePolicyManager.setPasswordQuality(ADMIN_RECEIVER_COMPONENT,
+ PASSWORD_QUALITY_SOMETHING);
+ assertEquals(PASSWORD_QUALITY_SOMETHING,
+ mDevicePolicyManager.getPasswordQuality(ADMIN_RECEIVER_COMPONENT));
+ assertPasswordSufficiency(false);
+
+ String caseDescription = "initial";
+ assertPasswordSucceeds("1234", caseDescription);
+ assertPasswordSucceeds("abcd", caseDescription); // can't change.
+ assertPasswordSucceeds("abcd1234", caseDescription);
+
+ mDevicePolicyManager.setPasswordMinimumLength(ADMIN_RECEIVER_COMPONENT, 10);
+ caseDescription = "minimum password length = 10";
+ assertEquals(10, mDevicePolicyManager.getPasswordMinimumLength(ADMIN_RECEIVER_COMPONENT));
+ assertPasswordSufficiency(true); // length not checked for this quality
+
+ // TODO(ascull): fix resetPassword() logic so these succeed
+ assertPasswordFails("1234", caseDescription);
+ assertPasswordFails("abcd", caseDescription);
+ assertPasswordFails("abcd1234", caseDescription);
+
+ mDevicePolicyManager.setPasswordMinimumLength(ADMIN_RECEIVER_COMPONENT, 4);
+ caseDescription = "minimum password length = 4";
+ assertEquals(4, mDevicePolicyManager.getPasswordMinimumLength(
+ ADMIN_RECEIVER_COMPONENT));
+ assertPasswordSufficiency(true);
+
+ assertPasswordSucceeds("1234", caseDescription);
+ assertPasswordSucceeds("abcd", caseDescription);
+ assertPasswordSucceeds("abcd1234", caseDescription);
+ }
+
+ public void testPasswordQuality_numeric() {
+ if (!mShouldRun) {
+ return;
+ }
+
+ mDevicePolicyManager.setPasswordQuality(ADMIN_RECEIVER_COMPONENT, PASSWORD_QUALITY_NUMERIC);
+ assertEquals(PASSWORD_QUALITY_NUMERIC,
+ mDevicePolicyManager.getPasswordQuality(ADMIN_RECEIVER_COMPONENT));
+ assertPasswordSufficiency(false); // failure
+
+ String caseDescription = "initial";
+ assertPasswordSucceeds("1234", caseDescription);
+ assertPasswordSucceeds("abcd", caseDescription);
+ assertPasswordSucceeds("abcd1234", caseDescription);
+
+ mDevicePolicyManager.setPasswordMinimumLength(ADMIN_RECEIVER_COMPONENT, 10);
+ caseDescription = "minimum password length = 10";
+ assertEquals(10, mDevicePolicyManager.getPasswordMinimumLength(ADMIN_RECEIVER_COMPONENT));
+ assertPasswordSufficiency(false);
+
+ assertPasswordFails("1234", caseDescription);
+ assertPasswordFails("abcd", caseDescription);
+ assertPasswordFails("abcd1234", caseDescription);
+
+ mDevicePolicyManager.setPasswordMinimumLength(ADMIN_RECEIVER_COMPONENT, 4);
+ caseDescription = "minimum password length = 4";
+ assertEquals(4, mDevicePolicyManager.getPasswordMinimumLength(
+ ADMIN_RECEIVER_COMPONENT));
+ assertPasswordSufficiency(true);
+
+ assertPasswordSucceeds("1234", caseDescription);
+ assertPasswordSucceeds("abcd", caseDescription);
+ assertPasswordSucceeds("abcd1234", caseDescription);
+ }
+
+ public void testPasswordQuality_alphabetic() {
+ if (!mShouldRun) {
+ return;
+ }
+
+ mDevicePolicyManager.setPasswordQuality(ADMIN_RECEIVER_COMPONENT,
+ PASSWORD_QUALITY_ALPHABETIC);
+ assertEquals(PASSWORD_QUALITY_ALPHABETIC,
+ mDevicePolicyManager.getPasswordQuality(ADMIN_RECEIVER_COMPONENT));
+ assertPasswordSufficiency(false);
+
+ String caseDescription = "initial";
+ assertPasswordFails("1234", caseDescription); // can't change
+ assertPasswordSucceeds("abcd", caseDescription);
+ assertPasswordSucceeds("abcd1234", caseDescription);
+
+ mDevicePolicyManager.setPasswordMinimumLength(ADMIN_RECEIVER_COMPONENT, 10);
+ caseDescription = "minimum password length = 10";
+ assertEquals(10, mDevicePolicyManager.getPasswordMinimumLength(ADMIN_RECEIVER_COMPONENT));
+ assertPasswordSufficiency(false);
+
+ assertPasswordFails("1234", caseDescription);
+ assertPasswordFails("abcd", caseDescription);
+ assertPasswordFails("abcd1234", caseDescription);
+
+ mDevicePolicyManager.setPasswordMinimumLength(ADMIN_RECEIVER_COMPONENT, 4);
+ caseDescription = "minimum password length = 4";
+ assertEquals(4, mDevicePolicyManager.getPasswordMinimumLength(
+ ADMIN_RECEIVER_COMPONENT));
+ assertPasswordSufficiency(true);
+
+ assertPasswordFails("1234", caseDescription);
+ assertPasswordSucceeds("abcd", caseDescription);
+ assertPasswordSucceeds("abcd1234", caseDescription);
+ }
+
+ public void testPasswordQuality_alphanumeric() {
+ if (!mShouldRun) {
+ return;
+ }
+
+ mDevicePolicyManager.setPasswordQuality(ADMIN_RECEIVER_COMPONENT,
+ PASSWORD_QUALITY_ALPHANUMERIC);
+ assertEquals(PASSWORD_QUALITY_ALPHANUMERIC,
+ mDevicePolicyManager.getPasswordQuality(ADMIN_RECEIVER_COMPONENT));
+ assertPasswordSufficiency(false);
+
+ String caseDescription = "initial";
+ assertPasswordFails("1234", caseDescription);
+ assertPasswordFails("abcd", caseDescription);
+ assertPasswordSucceeds("abcd1234", caseDescription);
+
+ mDevicePolicyManager.setPasswordMinimumLength(ADMIN_RECEIVER_COMPONENT, 10);
+ caseDescription = "minimum password length = 10";
+ assertEquals(10, mDevicePolicyManager.getPasswordMinimumLength(ADMIN_RECEIVER_COMPONENT));
+ assertPasswordSufficiency(false);
+
+ assertPasswordFails("1234", caseDescription);
+ assertPasswordFails("abcd", caseDescription);
+ assertPasswordFails("abcd1234", caseDescription);
+
+ mDevicePolicyManager.setPasswordMinimumLength(ADMIN_RECEIVER_COMPONENT, 4);
+ caseDescription = "minimum password length = 4";
+ assertEquals(4, mDevicePolicyManager.getPasswordMinimumLength(
+ ADMIN_RECEIVER_COMPONENT));
+ assertPasswordSufficiency(true);
+
+ assertPasswordFails("1234", caseDescription);
+ assertPasswordFails("abcd", caseDescription);
+ assertPasswordSucceeds("abcd1234", caseDescription);
+ }
+
+ public void testPasswordQuality_complexUpperCase() {
+ if (!mShouldRun) {
+ return;
+ }
+
+ mDevicePolicyManager.setPasswordQuality(ADMIN_RECEIVER_COMPONENT, PASSWORD_QUALITY_COMPLEX);
+ assertEquals(PASSWORD_QUALITY_COMPLEX,
+ mDevicePolicyManager.getPasswordQuality(ADMIN_RECEIVER_COMPONENT));
+ resetComplexPasswordRestrictions();
+
+ String caseDescription = "minimum UpperCase=0";
+ assertPasswordSucceeds("abc1", caseDescription);
+ assertPasswordSucceeds("aBc1", caseDescription);
+ assertPasswordSucceeds("ABC1", caseDescription);
+ assertPasswordSucceeds("ABCD", caseDescription);
+ assertPasswordFails("123", caseDescription); // too short
+
+ mDevicePolicyManager.setPasswordMinimumUpperCase(ADMIN_RECEIVER_COMPONENT, 1);
+ assertEquals(1, mDevicePolicyManager.getPasswordMinimumUpperCase(ADMIN_RECEIVER_COMPONENT));
+ caseDescription = "minimum UpperCase=1";
+ assertPasswordFails("abc1", caseDescription);
+ assertPasswordSucceeds("aBc1", caseDescription);
+ assertPasswordSucceeds("ABC1", caseDescription);
+ assertPasswordSucceeds("ABCD", caseDescription);
+ assertPasswordFails("123", caseDescription); // too short
+
+ mDevicePolicyManager.setPasswordMinimumUpperCase(ADMIN_RECEIVER_COMPONENT, 3);
+ assertEquals(3, mDevicePolicyManager.getPasswordMinimumUpperCase(ADMIN_RECEIVER_COMPONENT));
+ caseDescription = "minimum UpperCase=3";
+ assertPasswordFails("abc1", caseDescription);
+ assertPasswordFails("aBC1", caseDescription);
+ assertPasswordSucceeds("ABC1", caseDescription);
+ assertPasswordSucceeds("ABCD", caseDescription);
+ assertPasswordFails("123", caseDescription); // too short
+ }
+
+ public void testPasswordQuality_complexLowerCase() {
+ if (!mShouldRun) {
+ return;
+ }
+
+ mDevicePolicyManager.setPasswordQuality(ADMIN_RECEIVER_COMPONENT, PASSWORD_QUALITY_COMPLEX);
+ assertEquals(PASSWORD_QUALITY_COMPLEX,
+ mDevicePolicyManager.getPasswordQuality(ADMIN_RECEIVER_COMPONENT));
+ resetComplexPasswordRestrictions();
+
+ String caseDescription = "minimum LowerCase=0";
+ assertPasswordSucceeds("ABCD", caseDescription);
+ assertPasswordSucceeds("aBC1", caseDescription);
+ assertPasswordSucceeds("abc1", caseDescription);
+ assertPasswordSucceeds("abcd", caseDescription);
+ assertPasswordFails("123", caseDescription); // too short
+
+ mDevicePolicyManager.setPasswordMinimumLowerCase(ADMIN_RECEIVER_COMPONENT, 1);
+ assertEquals(1, mDevicePolicyManager.getPasswordMinimumLowerCase(ADMIN_RECEIVER_COMPONENT));
+ caseDescription = "minimum LowerCase=1";
+ assertPasswordFails("ABCD", caseDescription);
+ assertPasswordSucceeds("aBC1", caseDescription);
+ assertPasswordSucceeds("abc1", caseDescription);
+ assertPasswordSucceeds("abcd", caseDescription);
+ assertPasswordFails("123", caseDescription); // too short
+
+ mDevicePolicyManager.setPasswordMinimumLowerCase(ADMIN_RECEIVER_COMPONENT, 3);
+ assertEquals(3, mDevicePolicyManager.getPasswordMinimumLowerCase(ADMIN_RECEIVER_COMPONENT));
+ caseDescription = "minimum LowerCase=3";
+ assertPasswordFails("ABCD", caseDescription);
+ assertPasswordFails("aBC1", caseDescription);
+ assertPasswordSucceeds("abc1", caseDescription);
+ assertPasswordSucceeds("abcd", caseDescription);
+ assertPasswordFails("123", caseDescription); // too short
+ }
+
+ public void testPasswordQuality_complexLetters() {
+ if (!mShouldRun) {
+ return;
+ }
+
+ mDevicePolicyManager.setPasswordQuality(ADMIN_RECEIVER_COMPONENT, PASSWORD_QUALITY_COMPLEX);
+ assertEquals(PASSWORD_QUALITY_COMPLEX,
+ mDevicePolicyManager.getPasswordQuality(ADMIN_RECEIVER_COMPONENT));
+ resetComplexPasswordRestrictions();
+
+ String caseDescription = "minimum Letters=0";
+ assertPasswordSucceeds("1234", caseDescription);
+ assertPasswordSucceeds("a123", caseDescription);
+ assertPasswordSucceeds("abc1", caseDescription);
+ assertPasswordSucceeds("abcd", caseDescription);
+ assertPasswordFails("123", caseDescription); // too short
+
+ mDevicePolicyManager.setPasswordMinimumLetters(ADMIN_RECEIVER_COMPONENT, 1);
+ assertEquals(1, mDevicePolicyManager.getPasswordMinimumLetters(ADMIN_RECEIVER_COMPONENT));
+ caseDescription = "minimum Letters=1";
+ assertPasswordFails("1234", caseDescription);
+ assertPasswordSucceeds("a123", caseDescription);
+ assertPasswordSucceeds("abc1", caseDescription);
+ assertPasswordSucceeds("abcd", caseDescription);
+ assertPasswordFails("123", caseDescription); // too short
+
+ mDevicePolicyManager.setPasswordMinimumLetters(ADMIN_RECEIVER_COMPONENT, 3);
+ assertEquals(3, mDevicePolicyManager.getPasswordMinimumLetters(ADMIN_RECEIVER_COMPONENT));
+ caseDescription = "minimum Letters=3";
+ assertPasswordFails("1234", caseDescription);
+ assertPasswordFails("a123", caseDescription);
+ assertPasswordSucceeds("abc1", caseDescription);
+ assertPasswordSucceeds("abcd", caseDescription);
+ assertPasswordFails("123", caseDescription); // too short
+ }
+
+ public void testPasswordQuality_complexNumeric() {
+ if (!mShouldRun) {
+ return;
+ }
+
+ mDevicePolicyManager.setPasswordQuality(ADMIN_RECEIVER_COMPONENT, PASSWORD_QUALITY_COMPLEX);
+ assertEquals(PASSWORD_QUALITY_COMPLEX,
+ mDevicePolicyManager.getPasswordQuality(ADMIN_RECEIVER_COMPONENT));
+ resetComplexPasswordRestrictions();
+
+ String caseDescription = "minimum Numeric=0";
+ assertPasswordSucceeds("abcd", caseDescription);
+ assertPasswordSucceeds("1abc", caseDescription);
+ assertPasswordSucceeds("123a", caseDescription);
+ assertPasswordSucceeds("1234", caseDescription);
+ assertPasswordFails("123", caseDescription); // too short
+
+ mDevicePolicyManager.setPasswordMinimumNumeric(ADMIN_RECEIVER_COMPONENT, 1);
+ assertEquals(1, mDevicePolicyManager.getPasswordMinimumNumeric(ADMIN_RECEIVER_COMPONENT));
+ caseDescription = "minimum Numeric=1";
+ assertPasswordFails("abcd", caseDescription);
+ assertPasswordSucceeds("1abc", caseDescription);
+ assertPasswordSucceeds("123a", caseDescription);
+ assertPasswordSucceeds("1234", caseDescription);
+ assertPasswordFails("123", caseDescription); // too short
+
+ mDevicePolicyManager.setPasswordMinimumNumeric(ADMIN_RECEIVER_COMPONENT, 3);
+ assertEquals(3, mDevicePolicyManager.getPasswordMinimumNumeric(ADMIN_RECEIVER_COMPONENT));
+ caseDescription = "minimum Numeric=3";
+ assertPasswordFails("abcd", caseDescription);
+ assertPasswordFails("1abc", caseDescription);
+ assertPasswordSucceeds("123a", caseDescription);
+ assertPasswordSucceeds("1234", caseDescription);
+ assertPasswordFails("123", caseDescription); // too short
+ }
+
+ public void testPasswordQuality_complexSymbols() {
+ if (!mShouldRun) {
+ return;
+ }
+
+ mDevicePolicyManager.setPasswordQuality(ADMIN_RECEIVER_COMPONENT, PASSWORD_QUALITY_COMPLEX);
+ assertEquals(PASSWORD_QUALITY_COMPLEX,
+ mDevicePolicyManager.getPasswordQuality(ADMIN_RECEIVER_COMPONENT));
+ resetComplexPasswordRestrictions();
+
+ String caseDescription = "minimum Symbols=0";
+ assertPasswordSucceeds("abcd", caseDescription);
+ assertPasswordSucceeds("_bc1", caseDescription);
+ assertPasswordSucceeds("@#!1", caseDescription);
+ assertPasswordSucceeds("_@#!", caseDescription);
+ assertPasswordFails("123", caseDescription); // too short
+
+ mDevicePolicyManager.setPasswordMinimumSymbols(ADMIN_RECEIVER_COMPONENT, 1);
+ assertEquals(1, mDevicePolicyManager.getPasswordMinimumSymbols(ADMIN_RECEIVER_COMPONENT));
+ caseDescription = "minimum Symbols=1";
+ assertPasswordFails("abcd", caseDescription);
+ assertPasswordSucceeds("_bc1", caseDescription);
+ assertPasswordSucceeds("@#!1", caseDescription);
+ assertPasswordSucceeds("_@#!", caseDescription);
+ assertPasswordFails("123", caseDescription); // too short
+
+ mDevicePolicyManager.setPasswordMinimumSymbols(ADMIN_RECEIVER_COMPONENT, 3);
+ assertEquals(3, mDevicePolicyManager.getPasswordMinimumSymbols(ADMIN_RECEIVER_COMPONENT));
+ caseDescription = "minimum Symbols=3";
+ assertPasswordFails("abcd", caseDescription);
+ assertPasswordFails("_bc1", caseDescription);
+ assertPasswordSucceeds("@#!1", caseDescription);
+ assertPasswordSucceeds("_@#!", caseDescription);
+ assertPasswordFails("123", caseDescription); // too short
+ }
+
+ public void testPasswordQuality_complexNonLetter() {
+ if (!mShouldRun) {
+ return;
+ }
+
+ mDevicePolicyManager.setPasswordQuality(ADMIN_RECEIVER_COMPONENT, PASSWORD_QUALITY_COMPLEX);
+ assertEquals(PASSWORD_QUALITY_COMPLEX,
+ mDevicePolicyManager.getPasswordQuality(ADMIN_RECEIVER_COMPONENT));
+ resetComplexPasswordRestrictions();
+
+ String caseDescription = "minimum NonLetter=0";
+ assertPasswordSucceeds("Abcd", caseDescription);
+ assertPasswordSucceeds("_bcd", caseDescription);
+ assertPasswordSucceeds("3bcd", caseDescription);
+ assertPasswordSucceeds("_@3c", caseDescription);
+ assertPasswordSucceeds("_25!", caseDescription);
+ assertPasswordFails("123", caseDescription); // too short
+
+ mDevicePolicyManager.setPasswordMinimumNonLetter(ADMIN_RECEIVER_COMPONENT, 1);
+ assertEquals(1, mDevicePolicyManager.getPasswordMinimumNonLetter(ADMIN_RECEIVER_COMPONENT));
+ caseDescription = "minimum NonLetter=1";
+ assertPasswordFails("Abcd", caseDescription);
+ assertPasswordSucceeds("_bcd", caseDescription);
+ assertPasswordSucceeds("3bcd", caseDescription);
+ assertPasswordSucceeds("_@3c", caseDescription);
+ assertPasswordSucceeds("_25!", caseDescription);
+ assertPasswordFails("123", caseDescription); // too short
+
+ mDevicePolicyManager.setPasswordMinimumNonLetter(ADMIN_RECEIVER_COMPONENT, 3);
+ assertEquals(3, mDevicePolicyManager.getPasswordMinimumNonLetter(ADMIN_RECEIVER_COMPONENT));
+ caseDescription = "minimum NonLetter=3";
+ assertPasswordFails("Abcd", caseDescription);
+ assertPasswordFails("_bcd", caseDescription);
+ assertPasswordFails("3bcd", caseDescription);
+ assertPasswordSucceeds("_@3c", caseDescription);
+ assertPasswordSucceeds("_25!", caseDescription);
+ assertPasswordFails("123", caseDescription); // too short
+ }
+
private boolean setUpResetPasswordToken(boolean acceptFailure) {
// set up a token
assertFalse(mDevicePolicyManager.isResetPasswordTokenActive(ADMIN_RECEIVER_COMPONENT));
@@ -166,4 +537,32 @@
assertFalse(mDevicePolicyManager.resetPasswordWithToken(ADMIN_RECEIVER_COMPONENT,
SHORT_PASSWORD, TOKEN0, 0));
}
+
+ private void assertPasswordFails(String password, String restriction) {
+ try {
+ boolean passwordResetResult = mDevicePolicyManager.resetPasswordWithToken(
+ ADMIN_RECEIVER_COMPONENT, password, TOKEN0, /* flags= */0);
+ assertFalse("Password '" + password + "' should have failed on " + restriction,
+ passwordResetResult);
+ } catch (IllegalArgumentException e) {
+ // yesss, we have failed!
+ }
+ }
+
+ private void assertPasswordSucceeds(String password, String restriction) {
+ boolean passwordResetResult = mDevicePolicyManager.resetPasswordWithToken(
+ ADMIN_RECEIVER_COMPONENT, password, TOKEN0, /* flags= */0);
+ assertTrue("Password '" + password + "' failed on " + restriction, passwordResetResult);
+ assertPasswordSufficiency(true);
+ }
+
+ private void resetComplexPasswordRestrictions() {
+ mDevicePolicyManager.setPasswordMinimumLength(ADMIN_RECEIVER_COMPONENT, 0);
+ mDevicePolicyManager.setPasswordMinimumUpperCase(ADMIN_RECEIVER_COMPONENT, 0);
+ mDevicePolicyManager.setPasswordMinimumLowerCase(ADMIN_RECEIVER_COMPONENT, 0);
+ mDevicePolicyManager.setPasswordMinimumLetters(ADMIN_RECEIVER_COMPONENT, 0);
+ mDevicePolicyManager.setPasswordMinimumNumeric(ADMIN_RECEIVER_COMPONENT, 0);
+ mDevicePolicyManager.setPasswordMinimumSymbols(ADMIN_RECEIVER_COMPONENT, 0);
+ mDevicePolicyManager.setPasswordMinimumNonLetter(ADMIN_RECEIVER_COMPONENT, 0);
+ }
}
\ No newline at end of file
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/AndroidManifest.xml b/hostsidetests/devicepolicy/app/ManagedProfile/AndroidManifest.xml
index 41fb7c0..822cf9b 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/AndroidManifest.xml
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/AndroidManifest.xml
@@ -18,6 +18,7 @@
package="com.android.cts.managedprofile">
<uses-sdk android:minSdkVersion="27"/>
+ <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ResetPasswordWithTokenTest.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ResetPasswordWithTokenTest.java
index 4ea8f3b..54246a2 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ResetPasswordWithTokenTest.java
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ResetPasswordWithTokenTest.java
@@ -21,6 +21,8 @@
import android.content.Intent;
import android.os.UserManager;
+import com.android.compatibility.common.util.BlockingBroadcastReceiver;
+
public class ResetPasswordWithTokenTest extends BaseManagedProfileTest {
private static final String PASSWORD0 = "1234";
@@ -49,10 +51,6 @@
// Reset password on the work profile will enable separate work challenge for it.
assertTrue(mDevicePolicyManager.resetPasswordWithToken(ADMIN_RECEIVER_COMPONENT, PASSWORD0,
token, 0));
-
- mDevicePolicyManager.setPasswordQuality(ADMIN_RECEIVER_COMPONENT,
- DevicePolicyManager.PASSWORD_QUALITY_NUMERIC);
- mDevicePolicyManager.setPasswordMinimumLength(ADMIN_RECEIVER_COMPONENT, 6);
}
public void testResetPasswordBeforeUnlock() {
@@ -61,12 +59,37 @@
assertTrue(mDevicePolicyManager.isResetPasswordTokenActive(ADMIN_RECEIVER_COMPONENT));
assertTrue(mDevicePolicyManager.resetPasswordWithToken(ADMIN_RECEIVER_COMPONENT, PASSWORD1,
token, 0));
+
+ mDevicePolicyManager.setPasswordQuality(ADMIN_RECEIVER_COMPONENT,
+ DevicePolicyManager.PASSWORD_QUALITY_NUMERIC);
+ mDevicePolicyManager.setPasswordMinimumLength(ADMIN_RECEIVER_COMPONENT, 6);
try {
mDevicePolicyManager.isActivePasswordSufficient();
fail("Did not throw expected exception.");
} catch (IllegalStateException expected) {}
}
+ public void testClearPasswordBeforeUnlock() {
+ UserManager um = mContext.getSystemService(UserManager.class);
+ assertFalse(um.isUserUnlocked());
+ assertTrue(mDevicePolicyManager.isResetPasswordTokenActive(ADMIN_RECEIVER_COMPONENT));
+ assertTrue(mDevicePolicyManager.resetPasswordWithToken(ADMIN_RECEIVER_COMPONENT, null,
+ token, 0));
+
+ // When password is cleared, the system should automatically unlock the user.
+ final BlockingBroadcastReceiver receiver = new BlockingBroadcastReceiver(mContext,
+ Intent.ACTION_USER_UNLOCKED);
+ receiver.register();
+ try {
+ // Give the broadcast long enough time, as unlocking user could be slow.
+ assertNotNull(receiver.awaitForBroadcast(90_000));
+ } finally {
+ receiver.unregisterQuietly();
+ }
+ assertTrue(um.isUserUnlocked());
+ assertTrue(mDevicePolicyManager.isActivePasswordSufficient());
+ }
+
public void testSetResetPasswordToken() {
assertTrue(mDevicePolicyManager.setResetPasswordToken(ADMIN_RECEIVER_COMPONENT, token));
assertTrue(mDevicePolicyManager.isResetPasswordTokenActive(ADMIN_RECEIVER_COMPONENT));
diff --git a/hostsidetests/devicepolicy/app/NoLaunchableActivityApp/Android.bp b/hostsidetests/devicepolicy/app/NoLaunchableActivityApp/Android.bp
index 9456a6a..2e8d9f4 100644
--- a/hostsidetests/devicepolicy/app/NoLaunchableActivityApp/Android.bp
+++ b/hostsidetests/devicepolicy/app/NoLaunchableActivityApp/Android.bp
@@ -32,3 +32,45 @@
],
sdk_version: "current",
}
+
+// Build for no component app
+android_test_helper_app {
+ name: "CtsNoComponentApp",
+ dex_preopt: {
+ enabled: false,
+ },
+ optimize: {
+ enabled: false,
+ },
+ srcs: ["src/**/*.java"],
+ // tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "vts",
+ "general-tests",
+ "cts_instant",
+ ],
+ manifest: "no_component_AndroidManifest.xml",
+ sdk_version: "current",
+}
+
+// Build for no permission app
+android_test_helper_app {
+ name: "CtsNoPermissionApp",
+ dex_preopt: {
+ enabled: false,
+ },
+ optimize: {
+ enabled: false,
+ },
+ srcs: ["src/**/*.java"],
+ // tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "vts",
+ "general-tests",
+ "cts_instant",
+ ],
+ manifest: "no_permission_AndroidManifest.xml",
+ sdk_version: "current",
+}
diff --git a/hostsidetests/devicepolicy/app/WifiConfigCreator/AndroidManifest.xml b/hostsidetests/devicepolicy/app/WifiConfigCreator/AndroidManifest.xml
index ebd9c6c..7f0973d 100644
--- a/hostsidetests/devicepolicy/app/WifiConfigCreator/AndroidManifest.xml
+++ b/hostsidetests/devicepolicy/app/WifiConfigCreator/AndroidManifest.xml
@@ -19,6 +19,7 @@
package="com.android.cts.deviceowner.wificonfigcreator">
<uses-sdk android:targetSdkVersion="28"/>
+ <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/CrossProfileAppsHostSideTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/CrossProfileAppsHostSideTest.java
index 8a6f4da..f86e010 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/CrossProfileAppsHostSideTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/CrossProfileAppsHostSideTest.java
@@ -1,10 +1,16 @@
package com.android.cts.devicepolicy;
+import static com.android.cts.devicepolicy.metrics.DevicePolicyEventLogVerifier.assertMetricsLogged;
+
+import android.stats.devicepolicy.EventId;
+
+import com.android.cts.devicepolicy.metrics.DevicePolicyEventWrapper;
import com.android.tradefed.device.DeviceNotAvailableException;
import java.io.FileNotFoundException;
import java.util.Collections;
import java.util.Map;
+import javax.annotation.Nullable;
/**
* In the test, managed profile and secondary user are created. We then verify
@@ -22,6 +28,7 @@
private int mProfileId;
private int mSecondaryUserId;
private boolean mHasManagedUserFeature;
+ private boolean mCanTestMultiUser;
@Override
protected void setUp() throws Exception {
@@ -34,9 +41,10 @@
createAndStartManagedProfile();
installRequiredApps(mProfileId);
}
- if (mSupportsMultiUser) {
+ if (canCreateAdditionalUsers(1)) {
mSecondaryUserId = createUser();
installRequiredApps(mSecondaryUserId);
+ mCanTestMultiUser = true;
}
}
@@ -65,14 +73,14 @@
}
public void testPrimaryUserToSecondaryUser() throws Exception {
- if (!mSupportsMultiUser) {
+ if (!mCanTestMultiUser) {
return;
}
verifyCrossProfileAppsApi(mPrimaryUserId, mSecondaryUserId, NON_TARGET_USER_TEST_CLASS);
}
public void testSecondaryUserToManagedProfile() throws Exception {
- if (!mSupportsMultiUser || !mHasManagedUserFeature) {
+ if (!mCanTestMultiUser || !mHasManagedUserFeature) {
return;
}
verifyCrossProfileAppsApi(mSecondaryUserId, mProfileId, NON_TARGET_USER_TEST_CLASS);
@@ -80,18 +88,56 @@
}
public void testManagedProfileToSecondaryUser() throws Exception {
- if (!mSupportsMultiUser || !mHasManagedUserFeature) {
+ if (!mCanTestMultiUser || !mHasManagedUserFeature) {
return;
}
verifyCrossProfileAppsApi(mProfileId, mSecondaryUserId, NON_TARGET_USER_TEST_CLASS);
}
+ public void testStartMainActivity_logged() throws Exception {
+ assertMetricsLogged(
+ getDevice(),
+ () -> {
+ runDeviceTest(
+ mProfileId,
+ mPrimaryUserId,
+ TARGET_USER_TEST_CLASS,
+ "testStartMainActivity_noAsserts");
+ },
+ new DevicePolicyEventWrapper
+ .Builder(EventId.CROSS_PROFILE_APPS_START_ACTIVITY_AS_USER_VALUE)
+ .setStrings(new String[] {"com.android.cts.crossprofileappstest"})
+ .build());
+ }
+
+ public void testGetTargetUserProfiles_logged() throws Exception {
+ assertMetricsLogged(
+ getDevice(),
+ () -> {
+ runDeviceTest(
+ mProfileId,
+ mPrimaryUserId,
+ TARGET_USER_TEST_CLASS,
+ "testGetTargetUserProfiles_noAsserts");
+ },
+ new DevicePolicyEventWrapper
+ .Builder(EventId.CROSS_PROFILE_APPS_GET_TARGET_USER_PROFILES_VALUE)
+ .setStrings(new String[] {"com.android.cts.crossprofileappstest"})
+ .build());
+ }
+
private void verifyCrossProfileAppsApi(int fromUserId, int targetUserId, String testClass)
throws Exception {
+ runDeviceTest(fromUserId, targetUserId, testClass, /* testMethod= */ null);
+ }
+
+ private void runDeviceTest(
+ int fromUserId, int targetUserId, String testClass, @Nullable String testMethod)
+ throws Exception {
runDeviceTestsAsUser(
TEST_PACKAGE,
testClass,
- null,
+ testMethod,
fromUserId,
createTargetUserParam(targetUserId));
}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
index d9e855d..a811793 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
@@ -25,6 +25,7 @@
import com.android.cts.devicepolicy.metrics.DevicePolicyEventWrapper;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.log.LogUtil.CLog;
+
import com.google.common.collect.ImmutableMap;
import java.io.File;
@@ -115,8 +116,12 @@
private static final String AUTOFILL_APP_PKG = "com.android.cts.devicepolicy.autofillapp";
private static final String AUTOFILL_APP_APK = "CtsDevicePolicyAutofillApp.apk";
+ private static final String CONTENT_CAPTURE_APP_PKG = "com.android.cts.devicepolicy.contentcaptureapp";
private static final String CONTENT_CAPTURE_APP_APK = "CtsDevicePolicyContentCaptureApp.apk";
+ private static final String CONTENT_CAPTURE_SERVICE_PKG = "com.android.cts.devicepolicy.contentcaptureservice";
+ private static final String CONTENT_CAPTURE_SERVICE_APK = "CtsDevicePolicyContentCaptureService.apk";
+
protected static final String ASSIST_APP_PKG = "com.android.cts.devicepolicy.assistapp";
protected static final String ASSIST_APP_APK = "CtsDevicePolicyAssistApp.apk";
@@ -183,6 +188,8 @@
getDevice().uninstallPackage(INTENT_SENDER_PKG);
getDevice().uninstallPackage(CUSTOMIZATION_APP_PKG);
getDevice().uninstallPackage(AUTOFILL_APP_PKG);
+ getDevice().uninstallPackage(CONTENT_CAPTURE_SERVICE_PKG);
+ getDevice().uninstallPackage(CONTENT_CAPTURE_APP_PKG);
getDevice().uninstallPackage(PRINTING_APP_PKG);
getDevice().uninstallPackage(METERED_DATA_APP_PKG);
getDevice().uninstallPackage(TEST_APP_PKG);
@@ -870,9 +877,7 @@
"testDisallowAutofill_allowed");
}
- // TODO(b/124127364): currently disabled becase ContentCaptureManager.isContentCaptureEnabled()
- // doesn't return right value when the service is disabled after the activity already launched
- public void disabledtestDisallowContentCapture_allowed() throws Exception {
+ public void testDisallowContentCapture_allowed() throws Exception {
if (!mHasFeature) {
return;
}
@@ -881,6 +886,7 @@
if (!hasContentCapture) {
return;
}
+ installAppAsUser(CONTENT_CAPTURE_SERVICE_APK, mUserId);
installAppAsUser(CONTENT_CAPTURE_APP_APK, mUserId);
setDefaultContentCaptureServiceEnabled(false);
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
index 3100832..a0c2ce0 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
@@ -1146,8 +1146,7 @@
if (!shouldRunTelecomTest()) {
return;
}
- getDevice().setSetting(
- mProfileUserId, "secure", "dialer_default_application", MANAGED_PROFILE_PKG);
+ getDevice().executeShellCommand("telecom set-default-dialer " + MANAGED_PROFILE_PKG);
// Place a outgoing call through work phone account using TelecomManager and verify the
// call is inserted properly.
@@ -1325,6 +1324,20 @@
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
diff --git a/hostsidetests/incident/AndroidTest.xml b/hostsidetests/incident/AndroidTest.xml
index 18a73fa..65a347b 100644
--- a/hostsidetests/incident/AndroidTest.xml
+++ b/hostsidetests/incident/AndroidTest.xml
@@ -16,6 +16,8 @@
<configuration description="Config for CTS Incident host test cases">
<option name="test-suite-tag" value="cts" />
<option name="config-descriptor:metadata" key="component" value="metrics" />
+ <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
<target_preparer class="com.android.tradefed.targetprep.SwitchUserTargetPreparer">
<option name="user-type" value="system" />
</target_preparer>
diff --git a/hostsidetests/incident/apps/errorsapp/AndroidManifest.xml b/hostsidetests/incident/apps/errorsapp/AndroidManifest.xml
index 140deac..38c173e 100644
--- a/hostsidetests/incident/apps/errorsapp/AndroidManifest.xml
+++ b/hostsidetests/incident/apps/errorsapp/AndroidManifest.xml
@@ -24,10 +24,10 @@
<application>
<uses-library android:name="android.test.runner" />
-
- <activity android:name=".ANRActivity" android:label="ANR Test Activity" android:process=":TestProcess" />
- <activity android:name=".ExceptionActivity" android:label="Exception Test Activity" android:process=":TestProcess" />
- <activity android:name=".NativeActivity" android:label="Native Crash Test Activity" android:process=":TestProcess" />
+ <activity android:name=".ANRActivity" android:process=":TestProcess" />
+ <activity android:name=".ExceptionActivity" android:process=":TestProcess" />
+ <activity android:name=".NativeActivity" android:process=":TestProcess" />
+ <receiver android:name=".Receiver" android:process=":TestProcess" />
</application>
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
diff --git a/hostsidetests/incident/apps/errorsapp/src/com/android/server/cts/errors/ANRActivity.java b/hostsidetests/incident/apps/errorsapp/src/com/android/server/cts/errors/ANRActivity.java
index 10442d3..4c38aad 100644
--- a/hostsidetests/incident/apps/errorsapp/src/com/android/server/cts/errors/ANRActivity.java
+++ b/hostsidetests/incident/apps/errorsapp/src/com/android/server/cts/errors/ANRActivity.java
@@ -22,6 +22,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
+import android.util.Log;
import android.view.WindowManager;
/**
@@ -33,17 +34,7 @@
@Override
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
-
- getBaseContext().registerReceiver(new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- while (true) {}
- }
- }, new IntentFilter(Intent.ACTION_SCREEN_ON));
-
- 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);
+ Log.d(TAG, "gonna do while (true) {}");
+ while (true) {}
}
}
diff --git a/hostsidetests/incident/apps/errorsapp/src/com/android/server/cts/errors/ErrorsTests.java b/hostsidetests/incident/apps/errorsapp/src/com/android/server/cts/errors/ErrorsTests.java
index 493e639..9cf56ac 100644
--- a/hostsidetests/incident/apps/errorsapp/src/com/android/server/cts/errors/ErrorsTests.java
+++ b/hostsidetests/incident/apps/errorsapp/src/com/android/server/cts/errors/ErrorsTests.java
@@ -82,14 +82,19 @@
public void testANR() throws Exception {
Log.i(TAG, "testANR");
+ // Require that we get an ANR entry that shows that the activity is blocked in onCreate.
registerReceiver(mContext, mResultsReceivedSignal, ANR_TAG,
mContext.getPackageName() + ":TestProcess",
- "Subject: Broadcast of Intent { act=android.intent.action.SCREEN_ON");
+ "com.android.server.cts.errors.ANRActivity.onCreate");
Intent intent = new Intent();
intent.setClass(mContext, ANRActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(intent);
+ final Intent receiver = new Intent(mContext, Receiver.class);
+ mContext.sendBroadcast(receiver);
+ Log.i(TAG, "testANR -- sent broadcast to " + receiver);
+
assertTrue(mResultsReceivedSignal.await(TIMEOUT_SECS, TimeUnit.SECONDS));
}
@@ -114,17 +119,24 @@
public void onReceive(Context context, Intent intent) {
// DropBox might receive other entries while we're waiting for the error
// entry, so we need to check the tag and stack trace before continuing.
- DropBoxManager.Entry entry = mDropbox.getNextEntry(wantTag, mStartMs);
- if (entry != null) {
- String stackTrace = entry.getText(10000); // Only need to check a few lines.
+ while (true) {
+ final DropBoxManager.Entry entry = mDropbox.getNextEntry(wantTag, mStartMs);
+ if (entry == null) {
+ break;
+ }
+ Log.d(TAG, "ErrorsTest got message from drobpox: " + entry.getTag());
+ mStartMs = entry.getTimeMillis();
+ String stackTrace = entry.getText(64 * 1024);
boolean allMatches = true;
for (String line : wantInStackTrace) {
- allMatches &= stackTrace.contains(line);
+ boolean matched = stackTrace.contains(line);
+ Log.d(TAG, " matched=" + matched + " line: " + line);
+ allMatches &= matched;
}
- entry.close();
if (allMatches) {
onReceiveLatch.countDown();
}
+ entry.close();
}
}
}, new IntentFilter(DropBoxManager.ACTION_DROPBOX_ENTRY_ADDED));
diff --git a/hostsidetests/incident/apps/errorsapp/src/com/android/server/cts/errors/Receiver.java b/hostsidetests/incident/apps/errorsapp/src/com/android/server/cts/errors/Receiver.java
new file mode 100644
index 0000000..0c78c7a
--- /dev/null
+++ b/hostsidetests/incident/apps/errorsapp/src/com/android/server/cts/errors/Receiver.java
@@ -0,0 +1,32 @@
+/*
+ * 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.server.cts.errors;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+
+public class Receiver extends BroadcastReceiver {
+ private static final String TAG = "ErrorsTests.Receiver";
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Log.d(TAG, "Receiver.onReceive");
+ throw new RuntimeException("If this broadcast was received, then the app didn't ANR");
+ }
+}
diff --git a/hostsidetests/net/AndroidTest.xml b/hostsidetests/net/AndroidTest.xml
index 0656cae..c7cab7b 100644
--- a/hostsidetests/net/AndroidTest.xml
+++ b/hostsidetests/net/AndroidTest.xml
@@ -16,6 +16,9 @@
<configuration description="Config for CTS net host test cases">
<option name="test-suite-tag" value="cts" />
<option name="config-descriptor:metadata" key="component" value="networking" />
+ <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
+
<target_preparer class="com.android.cts.net.NetPolicyTestsPreparer" />
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
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 4e4d1f6..6dfa2f3 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
@@ -153,7 +153,14 @@
Log.i(TAG, "Apps status on " + getName() + ":\n"
+ "\ttest app: uid=" + mMyUid + ", state=" + getProcessStateByUid(mMyUid) + "\n"
+ "\tapp2: uid=" + mUid + ", state=" + getProcessStateByUid(mUid));
- executeShellCommand("settings get global app_idle_constants");
+
+ // app_idle_constants set in NetPolicyTestsPreparer.setUp() is not always sucessful (suspect
+ // timing issue), here we set it again to make sure.
+ final String appIdleConstants = "parole_duration=0,stable_charging_threshold=0";
+ executeShellCommand("settings put global app_idle_constants " + appIdleConstants);
+ final String currentConstants =
+ executeShellCommand("settings get global app_idle_constants");
+ assertEquals(appIdleConstants, currentConstants);
}
@Override
@@ -204,7 +211,7 @@
do {
attempts++;
count = getNumberBroadcastsReceived(receiverName, ACTION_RESTRICT_BACKGROUND_CHANGED);
- if (count == expectedCount) {
+ if (count >= expectedCount) {
break;
}
Log.d(TAG, "Expecting count " + expectedCount + " but actual is " + count + " after "
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 72563d4..cfe6a73 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
@@ -22,6 +22,8 @@
import android.util.Log;
+import com.android.compatibility.common.util.CddTest;
+
public class DataSaverModeTest extends AbstractRestrictBackgroundNetworkTestCase {
private static final String[] REQUIRED_WHITELISTED_PACKAGES = {
@@ -35,7 +37,6 @@
super.setUp();
mIsDataSaverSupported = isDataSaverSupported();
- if (!isSupported()) return;
// Set initial state.
setRestrictBackground(false);
@@ -201,6 +202,20 @@
}
}
+ @CddTest(requirement="7.4.7/C-2-2")
+ public void testBroadcastNotSentOnUnsupportedDevices() throws Exception {
+ if (isSupported()) return;
+
+ setRestrictBackground(true);
+ assertRestrictBackgroundChangedReceived(0);
+
+ setRestrictBackground(false);
+ assertRestrictBackgroundChangedReceived(0);
+
+ setRestrictBackground(true);
+ assertRestrictBackgroundChangedReceived(0);
+ }
+
private void assertDataSaverStatusOnBackground(int expectedStatus) throws Exception {
assertRestrictBackgroundStatus(expectedStatus);
assertBackgroundNetworkAccess(expectedStatus != RESTRICT_BACKGROUND_STATUS_ENABLED);
diff --git a/hostsidetests/net/src/com/android/cts/net/HostsideRestrictBackgroundNetworkTests.java b/hostsidetests/net/src/com/android/cts/net/HostsideRestrictBackgroundNetworkTests.java
index 5f5ea43..4598c393 100644
--- a/hostsidetests/net/src/com/android/cts/net/HostsideRestrictBackgroundNetworkTests.java
+++ b/hostsidetests/net/src/com/android/cts/net/HostsideRestrictBackgroundNetworkTests.java
@@ -81,6 +81,11 @@
"testGetRestrictBackgroundStatus_requiredWhitelistedPackages");
}
+ public void testDataSaverMode_broadcastNotSentOnUnsupportedDevices() throws Exception {
+ runDeviceTests(TEST_PKG, TEST_PKG + ".DataSaverModeTest",
+ "testBroadcastNotSentOnUnsupportedDevices");
+ }
+
/*****************************
* Battery Saver Mode tests. *
*****************************/
diff --git a/hostsidetests/net/src/com/android/cts/net/NetPolicyTestsPreparer.java b/hostsidetests/net/src/com/android/cts/net/NetPolicyTestsPreparer.java
index ca14c27..bc2ee2c 100644
--- a/hostsidetests/net/src/com/android/cts/net/NetPolicyTestsPreparer.java
+++ b/hostsidetests/net/src/com/android/cts/net/NetPolicyTestsPreparer.java
@@ -48,7 +48,7 @@
}
private void setAppIdleConstants(String appIdleConstants) throws DeviceNotAvailableException {
- executeCmd("settings put global app_idle_constants " + appIdleConstants);
+ executeCmd("settings put global app_idle_constants \"" + appIdleConstants + "\"");
}
private String getAppIdleConstants() throws DeviceNotAvailableException {
diff --git a/hostsidetests/numberblocking/AndroidTest.xml b/hostsidetests/numberblocking/AndroidTest.xml
index 6ebc522..e337060 100644
--- a/hostsidetests/numberblocking/AndroidTest.xml
+++ b/hostsidetests/numberblocking/AndroidTest.xml
@@ -16,6 +16,8 @@
<configuration description="Config for CTS number blocking test cases">
<option name="test-suite-tag" value="cts" />
<option name="config-descriptor:metadata" key="component" value="abuse" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="CtsHostsideNumberBlockingAppTest.apk" />
diff --git a/hostsidetests/security/AndroidTest.xml b/hostsidetests/security/AndroidTest.xml
index 53b7e48..ac79c0e 100755
--- a/hostsidetests/security/AndroidTest.xml
+++ b/hostsidetests/security/AndroidTest.xml
@@ -17,6 +17,7 @@
<option name="test-suite-tag" value="cts" />
<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" />
<test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
<option name="jar" value="CtsSecurityHostTestCases.jar" />
diff --git a/hostsidetests/security/src/android/cts/security/KernelConfigTest.java b/hostsidetests/security/src/android/cts/security/KernelConfigTest.java
index 525a3b4..e81383b 100644
--- a/hostsidetests/security/src/android/cts/security/KernelConfigTest.java
+++ b/hostsidetests/security/src/android/cts/security/KernelConfigTest.java
@@ -178,20 +178,51 @@
}
}
+ private static final String QUALCOMM_SOC_FILE = "/sys/devices/soc0/chip_name";
+
+ private String getHardware() throws Exception {
+ String hardware = "DEFAULT";
+ /* lookup for Qualcomm devices */
+ if (doesFileExist(QUALCOMM_SOC_FILE)) {
+ hardware = mDevice.pullFileContents(QUALCOMM_SOC_FILE).trim();
+ }
+ /* TODO lookup other hardware as we get exemption requests. */
+ return hardwareMitigations.containsKey(hardware) ? hardware : "DEFAULT";
+ }
+
+ private boolean doesFileExist(String filePath) throws Exception {
+ String lsGrep = mDevice.executeShellCommand(String.format("ls \"%s\"", filePath));
+ return lsGrep.trim().equals(filePath);
+ }
+
+ private Map<String, String[]> hardwareMitigations = new HashMap<String, String[]>() {
+ {
+ put("SM8150", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
+ put("DEFAULT", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y",
+ "CONFIG_UNMAP_KERNEL_AT_EL0=y"});
+ }};
+
+ private String[] lookupMitigations() throws Exception {
+ return hardwareMitigations.get(getHardware());
+ }
+
/**
- * Test that the kernel has KTPI enabled for architectures and kernel versions that support it.
+ * Test that the kernel has Spectre/Meltdown mitigations for architectures and kernel versions
+ * that support it. Exempt platforms which are known to not be vulnerable.
*
* @throws Exception
*/
@CddTest(requirement="9.7")
- public void testConfigKTPI() throws Exception {
+ public void testConfigHardwareMitigations() throws Exception {
if (PropertyUtil.getFirstApiLevel(mDevice) < 28) {
return;
}
if (CpuFeatures.isArm64(mDevice) && !CpuFeatures.kernelVersionLessThan(mDevice, 4, 4)) {
- assertTrue("Linux kernel must have KPTI enabled: CONFIG_UNMAP_KERNEL_AT_EL0=y",
- configSet.contains("CONFIG_UNMAP_KERNEL_AT_EL0=y"));
+ for (String mitigation : lookupMitigations()) {
+ assertTrue("Linux kernel must have " + mitigation + " enabled.",
+ configSet.contains(mitigation));
+ }
} else if (CpuFeatures.isX86(mDevice)) {
assertTrue("Linux kernel must have KPTI enabled: CONFIG_PAGE_TABLE_ISOLATION=y",
configSet.contains("CONFIG_PAGE_TABLE_ISOLATION=y"));
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/SecurityTestCase.java b/hostsidetests/securitybulletin/src/android/security/cts/SecurityTestCase.java
index b1bd053..68f06d5 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/SecurityTestCase.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/SecurityTestCase.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 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.
@@ -24,8 +24,6 @@
import java.util.regex.Pattern;
import java.util.regex.Matcher;
-import java.util.Map;
-import java.util.HashMap;
import com.android.ddmlib.Log;
import java.util.concurrent.Callable;
import java.math.BigInteger;
@@ -46,51 +44,16 @@
public void setUp() throws Exception {
super.setUp();
- String uptime = getDevice().executeShellCommand("cat /proc/uptime");
- kernelStartTime = System.currentTimeMillis()/1000 -
- Integer.parseInt(uptime.substring(0, uptime.indexOf('.')));
- //TODO:(badash@): Watch for other things to track.
+ getDevice().waitForDeviceAvailable();
+ getDevice().disableAdbRoot();
+ updateKernelStartTime();
+ // TODO:(badash@): Watch for other things to track.
// Specifically time when app framework starts
oomCatcher.start();
}
/**
- * Allows a CTS test to pass if called after a planned reboot.
- */
- public void updateKernelStartTime() throws Exception {
- String uptime = getDevice().executeShellCommand("cat /proc/uptime");
- kernelStartTime = System.currentTimeMillis()/1000 -
- Integer.parseInt(uptime.substring(0, uptime.indexOf('.')));
- }
-
- /**
- * Use {@link NativeDevice#enableAdbRoot()} internally.
- *
- * The test methods calling this function should run even if enableAdbRoot fails, which is why
- * the return value is ignored. However, we may want to act on that data point in the future.
- */
- public boolean enableAdbRoot(ITestDevice mDevice) throws DeviceNotAvailableException {
- if(mDevice.enableAdbRoot()) {
- return true;
- } else {
- CLog.w("\"enable-root\" set to false! Root is required to check if device is vulnerable.");
- return false;
- }
- }
-
- /**
- * Check if a driver is present on a machine
- */
- public boolean containsDriver(ITestDevice mDevice, String driver) throws Exception {
- String result = mDevice.executeShellCommand("ls -Zl " + driver);
- if(result.contains("No such file or directory")) {
- return false;
- }
- return true;
- }
-
- /**
* Makes sure the phone is online, and the ensure the current boottime is within 2 seconds
* (due to rounding) of the previous boottime to check if The phone has crashed.
*/
@@ -98,7 +61,13 @@
public void tearDown() throws Exception {
oomCatcher.stop(getDevice().getSerialNumber());
- getDevice().waitForDeviceAvailable(120 * 1000);
+ try {
+ getDevice().waitForDeviceAvailable(90 * 1000);
+ } catch (DeviceNotAvailableException e) {
+ // Force a disconnection of all existing sessions to see if that unsticks adbd.
+ getDevice().executeAdbCommand("reconnect");
+ getDevice().waitForDeviceAvailable(30 * 1000);
+ }
if (oomCatcher.isOomDetected()) {
// we don't need to check kernel start time if we intentionally rebooted because oom
@@ -115,13 +84,11 @@
break;
}
} else {
- String uptime = getDevice().executeShellCommand("cat /proc/uptime");
- assertTrue("Phone has had a hard reset",
- (System.currentTimeMillis()/1000 -
- Integer.parseInt(uptime.substring(0, uptime.indexOf('.')))
- - kernelStartTime < 2));
- //TODO(badash@): add ability to catch runtime restart
- getDevice().disableAdbRoot();
+ long deviceTime = getDeviceUptime() + kernelStartTime;
+ long hostTime = System.currentTimeMillis() / 1000;
+ assertTrue("Phone has had a hard reset", (hostTime - deviceTime) < 2);
+
+ // TODO(badash@): add ability to catch runtime restart
}
}
@@ -206,4 +173,34 @@
return false;
}
+
+ /**
+ * Check if a driver is present on a machine.
+ * deprecated: use AdbUtils.stat() instead!
+ */
+ @Deprecated
+ protected boolean containsDriver(ITestDevice mDevice, String driver) throws Exception {
+ String result = mDevice.executeShellCommand("ls -Zl " + driver);
+ if(result.contains("No such file or directory")) {
+ return false;
+ }
+ return true;
+ }
+
+ private long getDeviceUptime() throws DeviceNotAvailableException {
+ String uptime = getDevice().executeShellCommand("cat /proc/uptime");
+ return Long.parseLong(uptime.substring(0, uptime.indexOf('.')));
+ }
+
+ /**
+ * Allows a test to pass if called after a planned reboot.
+ */
+ public void updateKernelStartTime() throws DeviceNotAvailableException {
+ long uptime = getDeviceUptime();
+ kernelStartTime = (System.currentTimeMillis() / 1000) - uptime;
+ }
+
+ public HostsideOomCatcher getOomCatcher() {
+ return oomCatcher;
+ }
}
diff --git a/hostsidetests/shortcuts/deviceside/backup/launcher1/Android.mk b/hostsidetests/shortcuts/deviceside/backup/launcher1/Android.mk
index e3feb66..2c647d3 100644
--- a/hostsidetests/shortcuts/deviceside/backup/launcher1/Android.mk
+++ b/hostsidetests/shortcuts/deviceside/backup/launcher1/Android.mk
@@ -39,6 +39,6 @@
LOCAL_JAVA_LIBRARIES := android.test.base.stubs
-LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := system_current
include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/shortcuts/deviceside/backup/launcher2/Android.mk b/hostsidetests/shortcuts/deviceside/backup/launcher2/Android.mk
index c9850ef..4279ea6 100644
--- a/hostsidetests/shortcuts/deviceside/backup/launcher2/Android.mk
+++ b/hostsidetests/shortcuts/deviceside/backup/launcher2/Android.mk
@@ -39,6 +39,6 @@
LOCAL_JAVA_LIBRARIES := android.test.base.stubs
-LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := system_current
include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/shortcuts/deviceside/backup/launcher3/Android.mk b/hostsidetests/shortcuts/deviceside/backup/launcher3/Android.mk
index 6edadb8e..f4f3cdd 100644
--- a/hostsidetests/shortcuts/deviceside/backup/launcher3/Android.mk
+++ b/hostsidetests/shortcuts/deviceside/backup/launcher3/Android.mk
@@ -39,6 +39,6 @@
LOCAL_JAVA_LIBRARIES := android.test.base.stubs
-LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := system_current
include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/shortcuts/deviceside/backup/launcher4new/Android.mk b/hostsidetests/shortcuts/deviceside/backup/launcher4new/Android.mk
index fedee32..8509b88 100644
--- a/hostsidetests/shortcuts/deviceside/backup/launcher4new/Android.mk
+++ b/hostsidetests/shortcuts/deviceside/backup/launcher4new/Android.mk
@@ -39,6 +39,6 @@
LOCAL_JAVA_LIBRARIES := android.test.base.stubs
-LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := system_current
include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/shortcuts/deviceside/backup/launcher4old/Android.mk b/hostsidetests/shortcuts/deviceside/backup/launcher4old/Android.mk
index 297e405..2ac1739 100644
--- a/hostsidetests/shortcuts/deviceside/backup/launcher4old/Android.mk
+++ b/hostsidetests/shortcuts/deviceside/backup/launcher4old/Android.mk
@@ -39,6 +39,6 @@
LOCAL_JAVA_LIBRARIES := android.test.base.stubs
-LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := system_current
include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/shortcuts/deviceside/backup/publisher1/Android.mk b/hostsidetests/shortcuts/deviceside/backup/publisher1/Android.mk
index 8bf3edf..16e42c2 100644
--- a/hostsidetests/shortcuts/deviceside/backup/publisher1/Android.mk
+++ b/hostsidetests/shortcuts/deviceside/backup/publisher1/Android.mk
@@ -39,6 +39,6 @@
LOCAL_JAVA_LIBRARIES := android.test.base.stubs
-LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := system_current
include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/shortcuts/deviceside/backup/publisher2/Android.mk b/hostsidetests/shortcuts/deviceside/backup/publisher2/Android.mk
index e211529..bd54e76 100644
--- a/hostsidetests/shortcuts/deviceside/backup/publisher2/Android.mk
+++ b/hostsidetests/shortcuts/deviceside/backup/publisher2/Android.mk
@@ -39,6 +39,6 @@
LOCAL_JAVA_LIBRARIES := android.test.base.stubs
-LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := system_current
include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/shortcuts/deviceside/backup/publisher3/Android.mk b/hostsidetests/shortcuts/deviceside/backup/publisher3/Android.mk
index c8085bc..ec0d201 100644
--- a/hostsidetests/shortcuts/deviceside/backup/publisher3/Android.mk
+++ b/hostsidetests/shortcuts/deviceside/backup/publisher3/Android.mk
@@ -39,6 +39,6 @@
LOCAL_JAVA_LIBRARIES := android.test.base.stubs
-LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := system_current
include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/shortcuts/deviceside/backup/publisher4new/Android.mk b/hostsidetests/shortcuts/deviceside/backup/publisher4new/Android.mk
index f37f7f9..8f4993a 100644
--- a/hostsidetests/shortcuts/deviceside/backup/publisher4new/Android.mk
+++ b/hostsidetests/shortcuts/deviceside/backup/publisher4new/Android.mk
@@ -41,6 +41,6 @@
LOCAL_JAVA_LIBRARIES := android.test.base.stubs
-LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := system_current
include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/shortcuts/deviceside/backup/publisher4new_nobackup/Android.mk b/hostsidetests/shortcuts/deviceside/backup/publisher4new_nobackup/Android.mk
index 177b114..456da8f 100644
--- a/hostsidetests/shortcuts/deviceside/backup/publisher4new_nobackup/Android.mk
+++ b/hostsidetests/shortcuts/deviceside/backup/publisher4new_nobackup/Android.mk
@@ -41,6 +41,6 @@
LOCAL_JAVA_LIBRARIES := android.test.base.stubs
-LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := system_current
include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/shortcuts/deviceside/backup/publisher4new_nomanifest/Android.mk b/hostsidetests/shortcuts/deviceside/backup/publisher4new_nomanifest/Android.mk
index 730b497..6b7e3af 100644
--- a/hostsidetests/shortcuts/deviceside/backup/publisher4new_nomanifest/Android.mk
+++ b/hostsidetests/shortcuts/deviceside/backup/publisher4new_nomanifest/Android.mk
@@ -41,6 +41,6 @@
LOCAL_JAVA_LIBRARIES := android.test.base.stubs
-LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := system_current
include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/shortcuts/deviceside/backup/publisher4new_wrongkey/Android.mk b/hostsidetests/shortcuts/deviceside/backup/publisher4new_wrongkey/Android.mk
index 033b547..1f46743 100644
--- a/hostsidetests/shortcuts/deviceside/backup/publisher4new_wrongkey/Android.mk
+++ b/hostsidetests/shortcuts/deviceside/backup/publisher4new_wrongkey/Android.mk
@@ -43,6 +43,6 @@
LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/cts-testkey1
-LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := system_current
include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/shortcuts/deviceside/backup/publisher4old/Android.mk b/hostsidetests/shortcuts/deviceside/backup/publisher4old/Android.mk
index 8a9462d..0e84c87 100644
--- a/hostsidetests/shortcuts/deviceside/backup/publisher4old/Android.mk
+++ b/hostsidetests/shortcuts/deviceside/backup/publisher4old/Android.mk
@@ -39,6 +39,6 @@
LOCAL_JAVA_LIBRARIES := android.test.base.stubs
-LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := system_current
include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/shortcuts/deviceside/backup/publisher4old_nomanifest/Android.mk b/hostsidetests/shortcuts/deviceside/backup/publisher4old_nomanifest/Android.mk
index 082ef4c..1f772a9 100644
--- a/hostsidetests/shortcuts/deviceside/backup/publisher4old_nomanifest/Android.mk
+++ b/hostsidetests/shortcuts/deviceside/backup/publisher4old_nomanifest/Android.mk
@@ -41,6 +41,6 @@
LOCAL_JAVA_LIBRARIES := android.test.base.stubs
-LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := system_current
include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/shortcuts/deviceside/common/src/android/content/pm/cts/shortcut/device/common/ShortcutManagerDeviceTestBase.java b/hostsidetests/shortcuts/deviceside/common/src/android/content/pm/cts/shortcut/device/common/ShortcutManagerDeviceTestBase.java
index f3fd238..da91a71 100644
--- a/hostsidetests/shortcuts/deviceside/common/src/android/content/pm/cts/shortcut/device/common/ShortcutManagerDeviceTestBase.java
+++ b/hostsidetests/shortcuts/deviceside/common/src/android/content/pm/cts/shortcut/device/common/ShortcutManagerDeviceTestBase.java
@@ -28,9 +28,12 @@
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.os.UserHandle;
+import android.os.UserManager;
import android.test.InstrumentationTestCase;
import android.text.TextUtils;
+import com.android.compatibility.common.util.ShellIdentityUtils;
+
import java.util.List;
/**
@@ -38,10 +41,13 @@
*/
public abstract class ShortcutManagerDeviceTestBase extends InstrumentationTestCase {
private ShortcutManager mManager;
+ private UserManager mUserManager;
private LauncherApps mLauncherApps;
private String mOriginalLauncher;
+ protected boolean mIsManagedUser;
+
protected Context getContext() {
return getInstrumentation().getTargetContext();
}
@@ -53,12 +59,16 @@
mOriginalLauncher = getDefaultLauncher(getInstrumentation());
mManager = getContext().getSystemService(ShortcutManager.class);
+ mUserManager = getContext().getSystemService(UserManager.class);
mLauncherApps = getContext().getSystemService(LauncherApps.class);
+
+ mIsManagedUser = ShellIdentityUtils.invokeMethodWithShellPermissions(
+ mUserManager, UserManager::isManagedProfile);
}
@Override
protected void tearDown() throws Exception {
- if (!TextUtils.isEmpty(mOriginalLauncher)) {
+ if (!mIsManagedUser && !TextUtils.isEmpty(mOriginalLauncher)) {
setDefaultLauncher(getInstrumentation(), mOriginalLauncher);
}
diff --git a/hostsidetests/shortcuts/deviceside/multiuser/Android.mk b/hostsidetests/shortcuts/deviceside/multiuser/Android.mk
index 48b57c8..255b569 100644
--- a/hostsidetests/shortcuts/deviceside/multiuser/Android.mk
+++ b/hostsidetests/shortcuts/deviceside/multiuser/Android.mk
@@ -41,6 +41,6 @@
LOCAL_JAVA_LIBRARIES := android.test.base.stubs
-LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := system_current
include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/shortcuts/deviceside/upgrade/Android.mk b/hostsidetests/shortcuts/deviceside/upgrade/Android.mk
index 0de206f..0397afc 100644
--- a/hostsidetests/shortcuts/deviceside/upgrade/Android.mk
+++ b/hostsidetests/shortcuts/deviceside/upgrade/Android.mk
@@ -45,7 +45,7 @@
LOCAL_JAVA_LIBRARIES := android.test.base.stubs
-LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := system_current
include $(BUILD_CTS_PACKAGE)
@@ -78,6 +78,6 @@
LOCAL_JAVA_LIBRARIES := android.test.base.stubs
-LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := system_current
include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/stagedinstall/Android.bp b/hostsidetests/stagedinstall/Android.bp
index bf291cd..01bb8be 100644
--- a/hostsidetests/stagedinstall/Android.bp
+++ b/hostsidetests/stagedinstall/Android.bp
@@ -44,10 +44,18 @@
manifest : "app/AndroidManifest.xml",
java_resources: [
+ ":StagedInstallTestApexV1_NotPreInstalled",
":StagedInstallTestApexV2",
+ ":StagedInstallTestApexV2_AdditionalFile",
+ ":StagedInstallTestApexV2_AdditionalFolder",
+ ":StagedInstallTestApexV2_WithPostInstallHook",
+ ":StagedInstallTestApexV2_WithPreInstallHook",
+ ":StagedInstallTestApexV2_WrongSha",
+ ":StagedInstallTestApexV3",
":StagedInstallTestAppAv1",
":StagedInstallTestAppAv2",
":StagedInstallTestAppBv1",
+ ":StagedInstallTestAppSamePackageNameAsApex",
],
static_libs: [
"androidx.test.runner",
@@ -83,9 +91,66 @@
manifest: "testdata/apk/Bv1.xml",
}
+android_test_helper_app {
+ name: "StagedInstallTestAppSamePackageNameAsApex",
+
+ srcs: ["testdata/apk/src/**/*java"],
+
+ manifest: "testdata/apk/StagedInstallTestAppSamePackageNameAsApex.xml",
+}
+
prebuilt_apex {
name: "StagedInstallTestApexV2",
src: "testdata/apex/com.android.apex.cts.shim.v2.apex",
filename: "com.android.apex.cts.shim.v2.apex",
installable: false,
}
+
+prebuilt_apex {
+ name: "StagedInstallTestApexV3",
+ src: "testdata/apex/com.android.apex.cts.shim.v3.apex",
+ filename: "com.android.apex.cts.shim.v3.apex",
+ installable: false,
+}
+
+prebuilt_apex {
+ name: "StagedInstallTestApexV2_AdditionalFile",
+ src: "testdata/apex/com.android.apex.cts.shim.v2_additional_file.apex",
+ filename: "com.android.apex.cts.shim.v2_additional_file.apex",
+ installable: false,
+}
+
+prebuilt_apex {
+ name: "StagedInstallTestApexV2_AdditionalFolder",
+ src: "testdata/apex/com.android.apex.cts.shim.v2_additional_folder.apex",
+ filename: "com.android.apex.cts.shim.v2_additional_folder.apex",
+ installable: false,
+}
+
+prebuilt_apex {
+ name: "StagedInstallTestApexV2_WithPostInstallHook",
+ src: "testdata/apex/com.android.apex.cts.shim.v2_with_post_install_hook.apex",
+ filename: "com.android.apex.cts.shim.v2_with_post_install_hook.apex",
+ installable: false,
+}
+
+prebuilt_apex {
+ name: "StagedInstallTestApexV2_WithPreInstallHook",
+ src: "testdata/apex/com.android.apex.cts.shim.v2_with_pre_install_hook.apex",
+ filename: "com.android.apex.cts.shim.v2_with_pre_install_hook.apex",
+ installable: false,
+}
+
+prebuilt_apex {
+ name: "StagedInstallTestApexV2_WrongSha",
+ src: "testdata/apex/com.android.apex.cts.shim.v2_wrong_sha.apex",
+ filename: "com.android.apex.cts.shim.v2_wrong_sha.apex",
+ installable: false,
+}
+
+prebuilt_apex {
+ name: "StagedInstallTestApexV1_NotPreInstalled",
+ src: "testdata/apex/com.android.apex.cts.shim_not_pre_installed.apex",
+ filename: "com.android.apex.cts.shim_not_pre_installed.apex",
+ installable: false,
+}
diff --git a/hostsidetests/stagedinstall/AndroidTest.xml b/hostsidetests/stagedinstall/AndroidTest.xml
index ab3764c..ad944b0 100644
--- a/hostsidetests/stagedinstall/AndroidTest.xml
+++ b/hostsidetests/stagedinstall/AndroidTest.xml
@@ -31,5 +31,6 @@
</target_preparer>
<test class="com.android.tradefed.testtype.HostTest" >
<option name="class" value="com.android.tests.stagedinstall.host.StagedInstallTest" />
+ <option name="class" value="com.android.tests.stagedinstall.host.ApexShimValidationTest" />
</test>
</configuration>
diff --git a/hostsidetests/stagedinstall/app/src/com/android/tests/stagedinstall/ApexShimValidationTest.java b/hostsidetests/stagedinstall/app/src/com/android/tests/stagedinstall/ApexShimValidationTest.java
new file mode 100644
index 0000000..f9c4ba8
--- /dev/null
+++ b/hostsidetests/stagedinstall/app/src/com/android/tests/stagedinstall/ApexShimValidationTest.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.stagedinstall;
+
+import static com.android.tests.stagedinstall.PackageInstallerSessionInfoSubject.assertThat;
+
+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 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;
+
+/**
+ * These tests use a similar structure to {@link StagedInstallTest}. See
+ * {@link StagedInstallTest} documentation for reference.
+ *
+ * @see StagedInstallTest
+ */
+@RunWith(JUnit4.class)
+public class ApexShimValidationTest {
+
+ private static final String SHIM_APEX_PACKAGE_NAME = "com.android.apex.cts.shim";
+
+ @Before
+ public void adoptShellPermissions() {
+ InstrumentationRegistry
+ .getInstrumentation()
+ .getUiAutomation()
+ .adoptShellPermissionIdentity(
+ Manifest.permission.INSTALL_PACKAGES,
+ Manifest.permission.DELETE_PACKAGES);
+ }
+
+ @After
+ public void dropShellPermissions() {
+ InstrumentationRegistry
+ .getInstrumentation()
+ .getUiAutomation()
+ .dropShellPermissionIdentity();
+ }
+
+ @Before
+ public void clearBroadcastReceiver() {
+ SessionUpdateBroadcastReceiver.sessionBroadcasts.clear();
+ }
+
+ @Test
+ public void testRejectsApexWithAdditionalFile_Commit() throws Exception {
+ int sessionId = stageApex("com.android.apex.cts.shim.v2_additional_file.apex");
+ PackageInstaller.SessionInfo info =
+ SessionUpdateBroadcastReceiver.sessionBroadcasts.poll(60, TimeUnit.SECONDS);
+ assertThat(info.getSessionId()).isEqualTo(sessionId);
+ assertThat(info).isStagedSessionFailed();
+ }
+
+ @Test
+ public void testRejectsApexWithAdditionalFolder_Commit() throws Exception {
+ int sessionId = stageApex("com.android.apex.cts.shim.v2_additional_folder.apex");
+ PackageInstaller.SessionInfo info =
+ SessionUpdateBroadcastReceiver.sessionBroadcasts.poll(60, TimeUnit.SECONDS);
+ assertThat(info.getSessionId()).isEqualTo(sessionId);
+ assertThat(info).isStagedSessionFailed();
+ }
+
+ @Test
+ public void testRejectsApexWithPostInstallHook_Commit() throws Exception {
+ int sessionId = stageApex("com.android.apex.cts.shim.v2_with_post_install_hook.apex");
+ PackageInstaller.SessionInfo info =
+ SessionUpdateBroadcastReceiver.sessionBroadcasts.poll(60, TimeUnit.SECONDS);
+ assertThat(info.getSessionId()).isEqualTo(sessionId);
+ assertThat(info).isStagedSessionFailed();
+ }
+
+ @Test
+ public void testRejectsApexWithPreInstallHook_Commit() throws Exception {
+ int sessionId = stageApex("com.android.apex.cts.shim.v2_with_pre_install_hook.apex");
+ PackageInstaller.SessionInfo info =
+ SessionUpdateBroadcastReceiver.sessionBroadcasts.poll(60, TimeUnit.SECONDS);
+ assertThat(info.getSessionId()).isEqualTo(sessionId);
+ assertThat(info).isStagedSessionFailed();
+ }
+
+ @Test
+ public void testRejectsApexWrongSHA_Commit() throws Exception {
+ int sessionId = stageApex("com.android.apex.cts.shim.v2_wrong_sha.apex");
+ PackageInstaller.SessionInfo info =
+ SessionUpdateBroadcastReceiver.sessionBroadcasts.poll(60, TimeUnit.SECONDS);
+ assertThat(info.getSessionId()).isEqualTo(sessionId);
+ assertThat(info).isStagedSessionFailed();
+ }
+
+ @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;
+ }
+ }
+
+ 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);
+ session.commit(LocalIntentSender.getIntentSender());
+ Intent result = LocalIntentSender.getIntentSenderResult();
+ 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 0c2d6e0d..af2fd64 100644
--- a/hostsidetests/stagedinstall/app/src/com/android/tests/stagedinstall/StagedInstallTest.java
+++ b/hostsidetests/stagedinstall/app/src/com/android/tests/stagedinstall/StagedInstallTest.java
@@ -81,6 +81,8 @@
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(),
@@ -404,8 +406,144 @@
}
}
+ @Test
+ public void testInstallStagedNonPreInstalledApex_UserBuild_Fails() throws Exception {
+ assertThat(getInstalledVersion(NOT_PRE_INSTALLED_SHIM_APEX_PACKAGE_NAME)).isEqualTo(-1);
+ int sessionId = stageSingleApk(
+ "com.android.apex.cts.shim_not_pre_installed.apex")
+ .assertSuccessful().getSessionId();
+ PackageInstaller.SessionInfo sessionInfo = waitForBroadcast(sessionId);
+ assertThat(sessionInfo).isStagedSessionFailed();
+ }
+
+ @Test
+ public void testStageApkWithSameNameAsApexShouldFail_Commit() throws Exception {
+ assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(1);
+ int sessionId = stageSingleApk(
+ "StagedInstallTestAppSamePackageNameAsApex.apk").assertSuccessful().getSessionId();
+ waitForIsReadyBroadcast(sessionId);
+ assertSessionReady(sessionId);
+ storeSessionId(sessionId);
+ }
+
+ @Test
+ public void testStageApkWithSameNameAsApexShouldFail_VerifyPostReboot() throws Exception {
+ int sessionId = retrieveLastSessionId();
+ assertSessionFailed(sessionId);
+ assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).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);
+ }
+
+ @Test
+ public void testInstallV3Apex_Commit() throws Exception {
+ int sessionId = stageSingleApk(
+ "com.android.apex.cts.shim.v3.apex").assertSuccessful().getSessionId();
+ waitForIsReadyBroadcast(sessionId);
+ assertSessionReady(sessionId);
+ storeSessionId(sessionId);
+ }
+
+ @Test
+ public void testInstallV3Apex_VerifyPostReboot() throws Exception {
+ int sessionId = retrieveLastSessionId();
+ assertSessionApplied(sessionId);
+ assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).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();
+ PackageInstaller.SessionInfo sessionInfo = waitForBroadcast(sessionId);
+ assertThat(sessionInfo).isStagedSessionFailed();
+ // Also verify that correct session info is reported by PackageManager.
+ assertSessionFailed(sessionId);
+ storeSessionId(sessionId);
+ }
+
+ @Test
+ public void testStagedInstallDowngradeApex_DowngradeNotRequested_Fails_VerifyPostReboot()
+ throws Exception {
+ // TODO: uncomment after we start to persist finalized staged sessions.
+ // 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);
+ }
+
+ @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();
+ waitForIsReadyBroadcast(sessionId);
+ assertSessionReady(sessionId);
+ storeSessionId(sessionId);
+ }
+
+ @Test
+ public void testStagedInstallDowngradeApex_DowngradeRequested_DebugBuild_VerifyPostReboot()
+ throws Exception {
+ int sessionId = retrieveLastSessionId();
+ assertSessionApplied(sessionId);
+ // Apex should be downgraded.
+ assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).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();
+ PackageInstaller.SessionInfo sessionInfo = waitForBroadcast(sessionId);
+ assertThat(sessionInfo).isStagedSessionFailed();
+ // Also verify that correct session info is reported by PackageManager.
+ assertSessionFailed(sessionId);
+ storeSessionId(sessionId);
+ }
+
+ @Test
+ public void testStagedInstallDowngradeApex_DowngradeRequested_UserBuild_Fails_VerifyPostReboot()
+ throws Exception {
+ // TODO: uncomment after we start to persist finalized staged sessions.
+ // int sessionId = retrieveLastSessionId();
+ // assertSessionFailed(sessionId);
+ // Apex shouldn't be downgraded.
+ assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(3);
+ }
+
+ @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");
+ }
+ }
+
private static PackageInstaller getPackageInstaller() {
- return InstrumentationRegistry.getInstrumentation().getContext().getPackageManager().getPackageInstaller();
+ return InstrumentationRegistry.getInstrumentation().getContext().getPackageManager()
+ .getPackageInstaller();
}
private static long getInstalledVersion(String packageName) {
@@ -576,15 +714,7 @@
}
public String getErrorMessage() {
- int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
- PackageInstaller.STATUS_FAILURE);
- if (status == -1) {
- throw new AssertionError("PENDING USER ACTION");
- }
- if (status == 0) {
- throw new AssertionError("Result was successful");
- }
- return result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE);
+ return extractErrorMessage(result);
}
public StageSessionResult assertSuccessful() {
@@ -593,6 +723,18 @@
}
}
+ private static String extractErrorMessage(Intent result) {
+ int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
+ PackageInstaller.STATUS_FAILURE);
+ if (status == -1) {
+ throw new AssertionError("PENDING USER ACTION");
+ }
+ if (status == 0) {
+ throw new AssertionError("Result was successful");
+ }
+ return result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE);
+ }
+
private static void abandonSession(int sessionId) {
getPackageInstaller().abandonSession(sessionId);
}
@@ -641,16 +783,21 @@
private void waitForIsReadyBroadcast(int sessionId) {
Log.i(TAG, "Waiting for session " + sessionId + " to be ready");
try {
- PackageInstaller.SessionInfo info =
- SessionUpdateBroadcastReceiver.sessionBroadcasts.poll(60, TimeUnit.SECONDS);
- assertThat(info).isNotNull();
- assertThat(info.getSessionId()).isEqualTo(sessionId);
+ PackageInstaller.SessionInfo info = waitForBroadcast(sessionId);
assertThat(info.isStagedSessionReady()).isTrue();
assertThat(info.isStagedSessionApplied()).isFalse();
assertWithMessage(info.getStagedSessionErrorMessage())
.that(info.isStagedSessionFailed()).isFalse();
- } catch (InterruptedException e) {
+ } catch (Exception e) {
throw new AssertionError(e);
}
}
+
+ private PackageInstaller.SessionInfo waitForBroadcast(int sessionId) throws Exception {
+ PackageInstaller.SessionInfo info =
+ SessionUpdateBroadcastReceiver.sessionBroadcasts.poll(60, TimeUnit.SECONDS);
+ assertThat(info).isNotNull();
+ assertThat(info.getSessionId()).isEqualTo(sessionId);
+ return info;
+ }
}
diff --git a/hostsidetests/stagedinstall/src/com/android/tests/stagedinstall/host/ApexShimValidationTest.java b/hostsidetests/stagedinstall/src/com/android/tests/stagedinstall/host/ApexShimValidationTest.java
new file mode 100644
index 0000000..9182ee9
--- /dev/null
+++ b/hostsidetests/stagedinstall/src/com/android/tests/stagedinstall/host/ApexShimValidationTest.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.stagedinstall.host;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.junit.Assume.assumeThat;
+
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+
+import org.hamcrest.CoreMatchers;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests to validate that only what is considered a correct shim apex can be installed.
+ *
+ * <p>Shim apex is considered correct iff:
+ * <ul>
+ * <li>It doesn't have any pre or post install hooks.</li>
+ * <li>It's {@code apex_payload.img} contains only a regular text file called
+ * {@code hash.txt}.</li>
+ * <li>It's {@code sha512} hash is whitelisted in the {@code hash.txt} of pre-installed on the
+ * {@code /system} partition shim apex.</li>
+ * </ul>
+ */
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class ApexShimValidationTest extends BaseHostJUnit4Test {
+
+ private static final String SHIM_APEX_PACKAGE_NAME = "com.android.apex.cts.shim";
+
+ /**
+ * Runs the given phase of a test by calling into the device.
+ * Throws an exception if the test phase fails.
+ * <p>
+ * For example, <code>runPhase("testInstallStagedApkCommit");</code>
+ */
+ private void runPhase(String phase) throws Exception {
+ assertThat(runDeviceTests("com.android.tests.stagedinstall",
+ "com.android.tests.stagedinstall.ApexShimValidationTest",
+ phase)).isTrue();
+ }
+
+ private void cleanUp() throws Exception {
+ assertThat(runDeviceTests("com.android.tests.stagedinstall",
+ "com.android.tests.stagedinstall.StagedInstallTest",
+ "cleanUp")).isTrue();
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ final String updatable = getDevice().getProperty("ro.apex.updatable");
+ assumeThat("Device doesn't support updating APEX", updatable, CoreMatchers.equalTo("true"));
+ cleanUp();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ cleanUp();
+ }
+
+ @Test
+ public void testShimApexIsPreInstalled() throws Exception {
+ boolean isShimApexPreInstalled =
+ getDevice().getActiveApexes().stream().anyMatch(
+ apex -> apex.name.equals(SHIM_APEX_PACKAGE_NAME));
+ assertWithMessage("Shim APEX is not pre-installed").that(
+ isShimApexPreInstalled).isTrue();
+ }
+
+ @Test
+ public void testRejectsApexWithAdditionalFile() throws Exception {
+ runPhase("testRejectsApexWithAdditionalFile_Commit");
+ getDevice().reboot();
+ runPhase("testInstallRejected_VerifyPostReboot");
+ }
+
+ @Test
+ public void testRejectsApexWithAdditionalFolder() throws Exception {
+ runPhase("testRejectsApexWithAdditionalFolder_Commit");
+ getDevice().reboot();
+ runPhase("testInstallRejected_VerifyPostReboot");
+ }
+
+ @Test
+ public void testRejectsApexWithPostInstallHook() throws Exception {
+ runPhase("testRejectsApexWithPostInstallHook_Commit");
+ getDevice().reboot();
+ runPhase("testInstallRejected_VerifyPostReboot");
+ }
+
+ @Test
+ public void testRejectsApexWithPreInstallHook() throws Exception {
+ runPhase("testRejectsApexWithPreInstallHook_Commit");
+ getDevice().reboot();
+ runPhase("testInstallRejected_VerifyPostReboot");
+ }
+
+ @Test
+ public void testRejectsApexWrongSHA() throws Exception {
+ runPhase("testRejectsApexWrongSHA_Commit");
+ getDevice().reboot();
+ runPhase("testInstallRejected_VerifyPostReboot");
+ }
+}
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 c7514c0..aee50e5 100644
--- a/hostsidetests/stagedinstall/src/com/android/tests/stagedinstall/host/StagedInstallTest.java
+++ b/hostsidetests/stagedinstall/src/com/android/tests/stagedinstall/host/StagedInstallTest.java
@@ -20,6 +20,7 @@
import static org.hamcrest.CoreMatchers.endsWith;
import static org.hamcrest.CoreMatchers.not;
+import static org.junit.Assume.assumeFalse;
import static org.junit.Assume.assumeThat;
import static org.junit.Assume.assumeTrue;
@@ -145,6 +146,7 @@
@Test
public void testStagedInstallDowngrade_DowngradeRequested_DebugBuild() throws Exception {
assumeThat(getDevice().getBuildFlavor(), not(endsWith("-user")));
+
runPhase("testStagedInstallDowngrade_DowngradeRequested_Commit");
getDevice().reboot();
runPhase("testStagedInstallDowngrade_DowngradeRequested_DebugBuild_VerifyPostReboot");
@@ -153,6 +155,7 @@
@Test
public void testStagedInstallDowngrade_DowngradeRequested_UserBuild() throws Exception {
assumeThat(getDevice().getBuildFlavor(), endsWith("-user"));
+
runPhase("testStagedInstallDowngrade_DowngradeRequested_Commit");
getDevice().reboot();
runPhase("testStagedInstallDowngrade_DowngradeRequested_UserBuild_VerifyPostReboot");
@@ -161,6 +164,7 @@
@Test
public void testShimApexShouldPreInstalledIfUpdatingApexIsSupported() throws Exception {
assumeTrue("Device does not support updating APEX", isUpdatingApexSupported());
+
final ITestDevice.ApexInfo shimApex = getShimApex();
assertThat(shimApex.versionCode).isEqualTo(1);
}
@@ -168,6 +172,7 @@
@Test
public void testInstallStagedApex() throws Exception {
assumeTrue("Device does not support updating APEX", isUpdatingApexSupported());
+
runPhase("testInstallStagedApex_Commit");
getDevice().reboot();
runPhase("testInstallStagedApex_VerifyPostReboot");
@@ -176,6 +181,7 @@
@Test
public void testInstallStagedApexAndApk() throws Exception {
assumeTrue("Device does not support updating APEX", isUpdatingApexSupported());
+
runPhase("testInstallStagedApexAndApk_Commit");
getDevice().reboot();
runPhase("testInstallStagedApexAndApk_VerifyPostReboot");
@@ -184,9 +190,88 @@
@Test
public void testsFailsNonStagedApexInstall() throws Exception {
assumeTrue("Device does not support updating APEX", isUpdatingApexSupported());
+
runPhase("testsFailsNonStagedApexInstall");
}
+ @Test
+ public void testInstallStagedNonPreInstalledApex_UserBuild_Fails() throws Exception {
+ assumeThat(getDevice().getBuildFlavor(), endsWith("-user"));
+ assumeTrue("Device does not support updating APEX", isUpdatingApexSupported());
+
+ runPhase("testInstallStagedNonPreInstalledApex_UserBuild_Fails");
+ }
+
+ @Test
+ public void testStageApkWithSameNameAsApexShouldFail() throws Exception {
+ assumeTrue("Device does not support updating APEX", isUpdatingApexSupported());
+
+ runPhase("testStageApkWithSameNameAsApexShouldFail_Commit");
+ getDevice().reboot();
+ runPhase("testStageApkWithSameNameAsApexShouldFail_VerifyPostReboot");
+ }
+
+ @Test
+ public void testNonStagedInstallApkWithSameNameAsApexShouldFail() throws Exception {
+ assumeTrue("Device does not support updating APEX", isUpdatingApexSupported());
+ runPhase("testNonStagedInstallApkWithSameNameAsApexShouldFail");
+ }
+
+ @Test
+ public void testStagedInstallDowngradeApex_DowngradeNotRequested_Fails() throws Exception {
+ assumeTrue("Device does not support updating APEX", isUpdatingApexSupported());
+
+ installV3Apex();
+ runPhase("testStagedInstallDowngradeApex_DowngradeNotRequested_Fails_Commit");
+ getDevice().reboot();
+ runPhase("testStagedInstallDowngradeApex_DowngradeNotRequested_Fails_VerifyPostReboot");
+ }
+
+ @Test
+ public void testStagedInstallDowngradeApex_DowngradeRequested_DebugBuild() throws Exception {
+ assumeThat(getDevice().getBuildFlavor(), not(endsWith("-user")));
+ assumeTrue("Device does not support updating APEX", isUpdatingApexSupported());
+
+ installV3Apex();
+ runPhase("testStagedInstallDowngradeApex_DowngradeRequested_DebugBuild_Commit");
+ getDevice().reboot();
+ runPhase("testStagedInstallDowngradeApex_DowngradeRequested_DebugBuild_VerifyPostReboot");
+ }
+
+ @Test
+ public void testStagedInstallDowngradeApex_DowngradeRequested_UserBuild_Fails()
+ throws Exception {
+ assumeThat(getDevice().getBuildFlavor(), endsWith("-user"));
+ assumeTrue("Device does not support updating APEX", isUpdatingApexSupported());
+
+ installV3Apex();
+ runPhase("testStagedInstallDowngradeApex_DowngradeRequested_UserBuild_Fails_Commit");
+ getDevice().reboot();
+ runPhase("testStagedInstallDowngradeApex_DowngradeRequested_UserBuild_Fails_"
+ + "VerifyPostReboot");
+ }
+
+ @Test
+ public void testInstallStagedApex_SameGrade() throws Exception {
+ assumeTrue("Device does not support updating APEX", isUpdatingApexSupported());
+
+ installV3Apex();
+ installV3Apex();
+ }
+
+ @Test
+ public void testInstallApex_DeviceDoesNotSupportApex_Fails() throws Exception {
+ assumeFalse("Device supports updating APEX", isUpdatingApexSupported());
+
+ runPhase("testInstallApex_DeviceDoesNotSupportApex_Fails");
+ }
+
+ private void installV3Apex()throws Exception {
+ runPhase("testInstallV3Apex_Commit");
+ getDevice().reboot();
+ runPhase("testInstallV3Apex_VerifyPostReboot");
+ }
+
private boolean isUpdatingApexSupported() throws Exception {
final String updatable = getDevice().getProperty("ro.apex.updatable");
return updatable != null && updatable.equals("true");
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 19fb348..6b950a4 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
new file mode 100644
index 0000000..ff7f550
--- /dev/null
+++ 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
new file mode 100644
index 0000000..1597fa4
--- /dev/null
+++ 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_with_post_install_hook.apex b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_with_post_install_hook.apex
new file mode 100644
index 0000000..be3a0b7
--- /dev/null
+++ 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
new file mode 100644
index 0000000..7caa3c0
--- /dev/null
+++ 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
new file mode 100644
index 0000000..8db2afd
--- /dev/null
+++ 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
new file mode 100644
index 0000000..8b3e214
--- /dev/null
+++ 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_not_pre_installed.apex b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim_not_pre_installed.apex
new file mode 100644
index 0000000..2036ce5
--- /dev/null
+++ b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim_not_pre_installed.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apk/StagedInstallTestAppSamePackageNameAsApex.xml b/hostsidetests/stagedinstall/testdata/apk/StagedInstallTestAppSamePackageNameAsApex.xml
new file mode 100644
index 0000000..2954538
--- /dev/null
+++ b/hostsidetests/stagedinstall/testdata/apk/StagedInstallTestAppSamePackageNameAsApex.xml
@@ -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
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.apex.cts.shim"
+ android:versionCode="2"
+ android:versionName="2.0" >
+
+
+ <uses-sdk android:minSdkVersion="19" />
+
+ <application android:label="StagedInstall Test App With Same Package Name As Apex">
+ <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/AndroidTest.xml b/hostsidetests/statsd/AndroidTest.xml
index a1beb7c..764e80f 100644
--- a/hostsidetests/statsd/AndroidTest.xml
+++ b/hostsidetests/statsd/AndroidTest.xml
@@ -16,6 +16,7 @@
<configuration description="Config for CTS Statsd host test cases">
<option name="test-suite-tag" value="cts" />
<option name="config-descriptor:metadata" key="component" value="statsd" />
+ <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
<option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
<test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
<option name="jar" value="CtsStatsdHostTestCases.jar" />
diff --git a/hostsidetests/statsd/apps/statsdapp/Android.mk b/hostsidetests/statsd/apps/statsdapp/Android.mk
index 582d6d1..fed11e5 100644
--- a/hostsidetests/statsd/apps/statsdapp/Android.mk
+++ b/hostsidetests/statsd/apps/statsdapp/Android.mk
@@ -15,6 +15,16 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
+LOCAL_MODULE := liblmkhelper
+LOCAL_SRC_FILES := \
+ jni/alloc_stress_activity.cpp
+LOCAL_CFLAGS += -Wall -Werror
+LOCAL_SHARED_LIBRARIES := liblog
+LOCAL_NDK_STL_VARIANT := c++_static
+LOCAL_SDK_VERSION := current
+include $(BUILD_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
LOCAL_PACKAGE_NAME := CtsStatsdApp
LOCAL_PRIVATE_PLATFORM_APIS := true
@@ -37,7 +47,12 @@
androidx.legacy_legacy-support-v4 \
androidx.test.rules
+LOCAL_JNI_SHARED_LIBRARIES := \
+ liblmkhelper
+
# tag this module as a cts test artifact
LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+LOCAL_MULTILIB := both
+
include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/statsd/apps/statsdapp/AndroidManifest.xml b/hostsidetests/statsd/apps/statsdapp/AndroidManifest.xml
index 62415d7..225e12b 100644
--- a/hostsidetests/statsd/apps/statsdapp/AndroidManifest.xml
+++ b/hostsidetests/statsd/apps/statsdapp/AndroidManifest.xml
@@ -16,6 +16,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.server.cts.device.statsd" >
+ <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_NETWORK_STATE"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
@@ -55,6 +56,12 @@
</intent-filter>
</activity>
+ <activity
+ android:name=".LmkActivity"
+ android:label="Lmk Test Activity"
+ android:launchMode="singleInstance"
+ android:process=":LmkProcess"
+ />
<activity android:name=".DaveyActivity" android:exported="true" />
<activity android:name=".HiddenApiUsedActivity" android:exported="true" />
<activity
diff --git a/hostsidetests/statsd/apps/statsdapp/jni/alloc_stress_activity.cpp b/hostsidetests/statsd/apps/statsdapp/jni/alloc_stress_activity.cpp
new file mode 100644
index 0000000..463efe6
--- /dev/null
+++ b/hostsidetests/statsd/apps/statsdapp/jni/alloc_stress_activity.cpp
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ *
+ */
+
+#include <jni.h>
+#include <iostream>
+#include <tuple>
+#include <algorithm>
+#include <numeric>
+#include <string>
+#include <sstream>
+#include <cstring>
+#include <fstream>
+
+#include <android/log.h>
+#define LOG(...) __android_log_write(ANDROID_LOG_INFO, "ALLOC-STRESS", __VA_ARGS__)
+
+using namespace std;
+
+size_t s = 4 * (1 << 20); // 4 MB
+extern "C"
+JNIEXPORT void JNICALL
+Java_com_android_server_cts_device_statsd_LmkActivity_cmain(JNIEnv* , jobject /* this */)
+{
+ long long allocCount = 0;
+ while (1) {
+ char *ptr = (char *) malloc(s);
+ memset(ptr, (int) allocCount >> 10, s);
+ std::stringstream ss;
+ ss << "total alloc: " << (allocCount >> 20) << endl;
+ LOG(ss.str().c_str());
+ allocCount += s;
+ }
+}
diff --git a/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/LmkActivity.java b/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/LmkActivity.java
new file mode 100644
index 0000000..c3600a1
--- /dev/null
+++ b/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/LmkActivity.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 com.android.server.cts.device.statsd;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class LmkActivity extends Activity {
+
+ static {
+ System.loadLibrary("lmkhelper");
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ new Thread(new Runnable() {
+ public void run() {
+ cmain();
+ }
+ }).start();
+
+ }
+
+ /**
+ * Keep allocating memory until the process is killed by LMKD.
+ **/
+ public native void cmain();
+
+}
diff --git a/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java b/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
index 56c9039..a7e74bb 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
@@ -40,6 +40,7 @@
import com.android.os.AtomsProto.GpsScanStateChanged;
import com.android.os.AtomsProto.HiddenApiUsed;
import com.android.os.AtomsProto.LooperStats;
+import com.android.os.AtomsProto.LmkKillOccurred;
import com.android.os.AtomsProto.MediaCodecStateChanged;
import com.android.os.AtomsProto.NativeProcessMemoryState;
import com.android.os.AtomsProto.OverlayStateChanged;
@@ -82,6 +83,32 @@
super.tearDown();
}
+ public void testLmkKillOccurred() throws Exception {
+ if (statsdDisabled()) {
+ return;
+ }
+
+ StatsdConfig.Builder conf = createConfigBuilder()
+ .addAllowedLogSource("AID_LMKD");
+ final int atomTag = Atom.LMK_KILL_OCCURRED_FIELD_NUMBER;
+ addAtomEvent(conf, atomTag, false);
+ uploadConfig(conf);
+
+ Thread.sleep(WAIT_TIME_SHORT);
+
+ runActivity("LmkActivity", null, null, 5000);
+
+ // Sorted list of events in order in which they occurred.
+ List<EventMetricData> data = getEventMetricDataList();
+
+ assertEquals(1, data.size());
+ assertTrue(data.get(0).getAtom().hasLmkKillOccurred());
+ LmkKillOccurred atom = data.get(0).getAtom().getLmkKillOccurred();
+ assertEquals(getUid(), atom.getUid());
+ assertEquals(DEVICE_SIDE_TEST_PACKAGE + ":LmkProcess", atom.getProcessName());
+ assertEquals(0, atom.getOomAdjScore());
+ }
+
public void testAppCrashOccurred() throws Exception {
if (statsdDisabled()) {
return;
diff --git a/hostsidetests/statsd/src/android/cts/statsd/metric/GaugeMetricsTests.java b/hostsidetests/statsd/src/android/cts/statsd/metric/GaugeMetricsTests.java
index e2c5a9b..4435c26 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/metric/GaugeMetricsTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/metric/GaugeMetricsTests.java
@@ -18,10 +18,13 @@
import android.cts.statsd.atom.DeviceAtomTestCase;
import com.android.internal.os.StatsdConfigProto;
+import com.android.internal.os.StatsdConfigProto.ActivationType;
import com.android.internal.os.StatsdConfigProto.AtomMatcher;
+import com.android.internal.os.StatsdConfigProto.EventActivation;
import com.android.internal.os.StatsdConfigProto.FieldFilter;
import com.android.internal.os.StatsdConfigProto.FieldMatcher;
import com.android.internal.os.StatsdConfigProto.FieldValueMatcher;
+import com.android.internal.os.StatsdConfigProto.MetricActivation;
import com.android.internal.os.StatsdConfigProto.Predicate;
import com.android.internal.os.StatsdConfigProto.SimpleAtomMatcher;
import com.android.internal.os.StatsdConfigProto.SimplePredicate;
@@ -38,104 +41,159 @@
private static final int APP_BREADCRUMB_REPORTED_B_MATCH_START_ID = 2;
public void testGaugeMetric() throws Exception {
- if (statsdDisabled()) {
- return;
- }
- // Add AtomMatcher's.
- AtomMatcher startAtomMatcher =
- MetricsUtils.startAtomMatcher(APP_BREADCRUMB_REPORTED_A_MATCH_START_ID);
- AtomMatcher stopAtomMatcher =
- MetricsUtils.stopAtomMatcher(APP_BREADCRUMB_REPORTED_A_MATCH_STOP_ID);
- AtomMatcher atomMatcher =
- MetricsUtils.simpleAtomMatcher(APP_BREADCRUMB_REPORTED_B_MATCH_START_ID);
+ if (statsdDisabled()) {
+ return;
+ }
+ // Add AtomMatcher's.
+ AtomMatcher startAtomMatcher =
+ MetricsUtils.startAtomMatcher(APP_BREADCRUMB_REPORTED_A_MATCH_START_ID);
+ AtomMatcher stopAtomMatcher =
+ MetricsUtils.stopAtomMatcher(APP_BREADCRUMB_REPORTED_A_MATCH_STOP_ID);
+ AtomMatcher atomMatcher =
+ MetricsUtils.simpleAtomMatcher(APP_BREADCRUMB_REPORTED_B_MATCH_START_ID);
- StatsdConfigProto.StatsdConfig.Builder builder = createConfigBuilder();
- builder.addAtomMatcher(startAtomMatcher);
- builder.addAtomMatcher(stopAtomMatcher);
- builder.addAtomMatcher(atomMatcher);
+ StatsdConfigProto.StatsdConfig.Builder builder = createConfigBuilder();
+ builder.addAtomMatcher(startAtomMatcher);
+ builder.addAtomMatcher(stopAtomMatcher);
+ builder.addAtomMatcher(atomMatcher);
- // Add Predicate's.
- SimplePredicate simplePredicate = SimplePredicate.newBuilder()
- .setStart(APP_BREADCRUMB_REPORTED_A_MATCH_START_ID)
- .setStop(APP_BREADCRUMB_REPORTED_A_MATCH_STOP_ID)
- .build();
- Predicate predicate = Predicate.newBuilder()
- .setId(MetricsUtils.StringToId("Predicate"))
- .setSimplePredicate(simplePredicate)
- .build();
- builder.addPredicate(predicate);
+ // Add Predicate's.
+ SimplePredicate simplePredicate = SimplePredicate.newBuilder()
+ .setStart(APP_BREADCRUMB_REPORTED_A_MATCH_START_ID)
+ .setStop(APP_BREADCRUMB_REPORTED_A_MATCH_STOP_ID)
+ .build();
+ Predicate predicate = Predicate.newBuilder()
+ .setId(MetricsUtils.StringToId("Predicate"))
+ .setSimplePredicate(simplePredicate)
+ .build();
+ builder.addPredicate(predicate);
- // Add GaugeMetric.
- FieldMatcher fieldMatcher =
- FieldMatcher.newBuilder().setField(APP_BREADCRUMB_REPORTED_B_MATCH_START_ID).build();
- builder.addGaugeMetric(
- StatsdConfigProto.GaugeMetric.newBuilder()
- .setId(MetricsUtils.GAUGE_METRIC_ID)
- .setWhat(APP_BREADCRUMB_REPORTED_B_MATCH_START_ID)
- .setCondition(predicate.getId())
- .setGaugeFieldsFilter(
- FieldFilter.newBuilder().setIncludeAll(false).setFields(fieldMatcher).build())
- .setDimensionsInWhat(
- FieldMatcher.newBuilder()
- .setField(APP_BREADCRUMB_REPORTED_B_MATCH_START_ID)
- .addChild(FieldMatcher.newBuilder()
- .setField(AppBreadcrumbReported.STATE_FIELD_NUMBER)
- .build())
- .build())
- .setBucket(StatsdConfigProto.TimeUnit.CTS)
- .build());
+ // Add GaugeMetric.
+ FieldMatcher fieldMatcher =
+ FieldMatcher.newBuilder().setField(APP_BREADCRUMB_REPORTED_B_MATCH_START_ID).build();
+ builder.addGaugeMetric(
+ StatsdConfigProto.GaugeMetric.newBuilder()
+ .setId(MetricsUtils.GAUGE_METRIC_ID)
+ .setWhat(APP_BREADCRUMB_REPORTED_B_MATCH_START_ID)
+ .setCondition(predicate.getId())
+ .setGaugeFieldsFilter(
+ FieldFilter.newBuilder().setIncludeAll(false).setFields(fieldMatcher).build())
+ .setDimensionsInWhat(
+ FieldMatcher.newBuilder()
+ .setField(APP_BREADCRUMB_REPORTED_B_MATCH_START_ID)
+ .addChild(FieldMatcher.newBuilder()
+ .setField(AppBreadcrumbReported.STATE_FIELD_NUMBER)
+ .build())
+ .build())
+ .setBucket(StatsdConfigProto.TimeUnit.CTS)
+ .build());
- // Upload config.
- uploadConfig(builder);
+ // Upload config.
+ uploadConfig(builder);
- // Create AppBreadcrumbReported Start/Stop events.
- doAppBreadcrumbReportedStart(0);
- Thread.sleep(10);
- doAppBreadcrumbReportedStart(1);
- Thread.sleep(10);
- doAppBreadcrumbReportedStart(2);
- Thread.sleep(2000);
- doAppBreadcrumbReportedStop(2);
- Thread.sleep(10);
- doAppBreadcrumbReportedStop(0);
- Thread.sleep(10);
- doAppBreadcrumbReportedStop(1);
- doAppBreadcrumbReportedStart(2);
- Thread.sleep(10);
- doAppBreadcrumbReportedStart(1);
- Thread.sleep(2000);
- doAppBreadcrumbReportedStop(2);
- Thread.sleep(10);
- doAppBreadcrumbReportedStop(1);
+ // Create AppBreadcrumbReported Start/Stop events.
+ doAppBreadcrumbReportedStart(0);
+ Thread.sleep(10);
+ doAppBreadcrumbReportedStart(1);
+ Thread.sleep(10);
+ doAppBreadcrumbReportedStart(2);
+ Thread.sleep(2000);
+ doAppBreadcrumbReportedStop(2);
+ Thread.sleep(10);
+ doAppBreadcrumbReportedStop(0);
+ Thread.sleep(10);
+ doAppBreadcrumbReportedStop(1);
+ doAppBreadcrumbReportedStart(2);
+ Thread.sleep(10);
+ doAppBreadcrumbReportedStart(1);
+ Thread.sleep(2000);
+ doAppBreadcrumbReportedStop(2);
+ Thread.sleep(10);
+ doAppBreadcrumbReportedStop(1);
- // Wait for the metrics to propagate to statsd.
- Thread.sleep(2000);
+ // Wait for the metrics to propagate to statsd.
+ Thread.sleep(2000);
- StatsLogReport metricReport = getStatsLogReport();
- LogUtil.CLog.d("Got the following gauge metric data: " + metricReport.toString());
- assertEquals(MetricsUtils.GAUGE_METRIC_ID, metricReport.getMetricId());
- assertTrue(metricReport.hasGaugeMetrics());
- StatsLogReport.GaugeMetricDataWrapper gaugeData = metricReport.getGaugeMetrics();
- assertEquals(gaugeData.getDataCount(), 1);
+ StatsLogReport metricReport = getStatsLogReport();
+ LogUtil.CLog.d("Got the following gauge metric data: " + metricReport.toString());
+ assertEquals(MetricsUtils.GAUGE_METRIC_ID, metricReport.getMetricId());
+ assertTrue(metricReport.hasGaugeMetrics());
+ StatsLogReport.GaugeMetricDataWrapper gaugeData = metricReport.getGaugeMetrics();
+ assertEquals(gaugeData.getDataCount(), 1);
- int bucketCount = gaugeData.getData(0).getBucketInfoCount();
- GaugeMetricData data = gaugeData.getData(0);
- assertTrue(bucketCount > 2);
- MetricsUtils.assertBucketTimePresent(data.getBucketInfo(0));
- assertEquals(data.getBucketInfo(0).getAtomCount(), 1);
- assertEquals(data.getBucketInfo(0).getAtom(0).getAppBreadcrumbReported().getLabel(), 0);
- assertEquals(data.getBucketInfo(0).getAtom(0).getAppBreadcrumbReported().getState(),
- AppBreadcrumbReported.State.START);
+ int bucketCount = gaugeData.getData(0).getBucketInfoCount();
+ GaugeMetricData data = gaugeData.getData(0);
+ assertTrue(bucketCount > 2);
+ MetricsUtils.assertBucketTimePresent(data.getBucketInfo(0));
+ assertEquals(data.getBucketInfo(0).getAtomCount(), 1);
+ assertEquals(data.getBucketInfo(0).getAtom(0).getAppBreadcrumbReported().getLabel(), 0);
+ assertEquals(data.getBucketInfo(0).getAtom(0).getAppBreadcrumbReported().getState(),
+ AppBreadcrumbReported.State.START);
- MetricsUtils.assertBucketTimePresent(data.getBucketInfo(1));
- assertEquals(data.getBucketInfo(1).getAtomCount(), 1);
+ MetricsUtils.assertBucketTimePresent(data.getBucketInfo(1));
+ assertEquals(data.getBucketInfo(1).getAtomCount(), 1);
- MetricsUtils.assertBucketTimePresent(data.getBucketInfo(bucketCount-1));
- assertEquals(data.getBucketInfo(bucketCount - 1).getAtomCount(), 1);
- assertEquals(
- data.getBucketInfo(bucketCount - 1).getAtom(0).getAppBreadcrumbReported().getLabel(), 2);
- assertEquals(
- data.getBucketInfo(bucketCount - 1).getAtom(0).getAppBreadcrumbReported().getState(),
- AppBreadcrumbReported.State.STOP);
+ MetricsUtils.assertBucketTimePresent(data.getBucketInfo(bucketCount-1));
+ assertEquals(data.getBucketInfo(bucketCount - 1).getAtomCount(), 1);
+ assertEquals(
+ data.getBucketInfo(bucketCount - 1).getAtom(0).getAppBreadcrumbReported().getLabel(), 2);
+ assertEquals(
+ data.getBucketInfo(bucketCount - 1).getAtom(0).getAppBreadcrumbReported().getState(),
+ AppBreadcrumbReported.State.STOP);
}
+
+ public void testPulledGaugeMetricWithActivation() throws Exception {
+ if (statsdDisabled()) {
+ return;
+ }
+ // Add AtomMatcher's.
+ int activationAtomMatcherId = 1;
+ int activationAtomMatcherLabel = 1;
+
+ int systemUptimeMatcherId = 2;
+ AtomMatcher activationAtomMatcher =
+ MetricsUtils.appBreadcrumbMatcherWithLabel(
+ activationAtomMatcherId, activationAtomMatcherLabel);
+ AtomMatcher systemUptimeMatcher =
+ AtomMatcher.newBuilder()
+ .setId(systemUptimeMatcherId)
+ .setSimpleAtomMatcher(
+ SimpleAtomMatcher.newBuilder().setAtomId(Atom.SYSTEM_UPTIME_FIELD_NUMBER))
+ .build();
+
+ StatsdConfigProto.StatsdConfig.Builder builder = createConfigBuilder();
+ builder.addAtomMatcher(activationAtomMatcher);
+ builder.addAtomMatcher(systemUptimeMatcher);
+
+ // Add GaugeMetric.
+ builder.addGaugeMetric(
+ StatsdConfigProto.GaugeMetric.newBuilder()
+ .setId(MetricsUtils.GAUGE_METRIC_ID)
+ .setWhat(systemUptimeMatcherId)
+ .setGaugeFieldsFilter(
+ FieldFilter.newBuilder().setIncludeAll(true).build())
+ .setBucket(StatsdConfigProto.TimeUnit.CTS)
+ .build());
+
+ // Add activation.
+ builder.addMetricActivation(MetricActivation.newBuilder()
+ .setMetricId(MetricsUtils.GAUGE_METRIC_ID)
+ .setActivationType(ActivationType.ACTIVATE_IMMEDIATELY)
+ .addEventActivation(EventActivation.newBuilder()
+ .setAtomMatcherId(activationAtomMatcherId)
+ .setTtlSeconds(5)));
+
+ // Upload config.
+ uploadConfig(builder);
+
+ // Plenty of time to pull, but we should not keep the data since we are not active.
+ Thread.sleep(20_000);
+
+ StatsLogReport metricReport = getStatsLogReport();
+ LogUtil.CLog.d("Got the following gauge metric data: " + metricReport.toString());
+ assertEquals(MetricsUtils.GAUGE_METRIC_ID, metricReport.getMetricId());
+ assertFalse(metricReport.hasGaugeMetrics());
+ }
+
+
}
diff --git a/hostsidetests/statsd/src/android/cts/statsd/metric/MetricsUtils.java b/hostsidetests/statsd/src/android/cts/statsd/metric/MetricsUtils.java
index f65eac9..f400dfe 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/metric/MetricsUtils.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/metric/MetricsUtils.java
@@ -83,6 +83,17 @@
.build();
}
+ public static AtomMatcher appBreadcrumbMatcherWithLabel(int id, int label) {
+ return AtomMatcher.newBuilder()
+ .setId(id)
+ .setSimpleAtomMatcher(SimpleAtomMatcher.newBuilder()
+ .setAtomId(Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER)
+ .addFieldValueMatcher(FieldValueMatcher.newBuilder()
+ .setField(AppBreadcrumbReported.LABEL_FIELD_NUMBER)
+ .setEqInt(label)))
+ .build();
+ }
+
public static long StringToId(String str) {
return str.hashCode();
}
diff --git a/hostsidetests/statsd/src/android/cts/statsd/metric/ValueMetricsTests.java b/hostsidetests/statsd/src/android/cts/statsd/metric/ValueMetricsTests.java
index a54215b..6c70b9ed 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/metric/ValueMetricsTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/metric/ValueMetricsTests.java
@@ -17,37 +17,27 @@
import android.cts.statsd.atom.DeviceAtomTestCase;
-import com.android.internal.os.StatsdConfigProto;
+import com.android.internal.os.StatsdConfigProto.ActivationType;
import com.android.internal.os.StatsdConfigProto.AtomMatcher;
-import com.android.internal.os.StatsdConfigProto.FieldMatcher;
-import com.android.internal.os.StatsdConfigProto.ValueMetric;
-import com.android.os.AtomsProto.AppBreadcrumbReported;
-import com.android.os.AtomsProto.SystemElapsedRealtime;
-
-import com.android.os.AtomsProto.Atom;
-import com.android.os.StatsLog.ValueBucketInfo;
-import com.android.os.StatsLog.ValueMetricData;
-import com.android.os.StatsLog.StatsLogReport;
-import com.android.annotations.Nullable;
-import com.android.internal.os.StatsdConfigProto.AtomMatcher;
-import com.android.internal.os.StatsdConfigProto.EventMetric;
+import com.android.internal.os.StatsdConfigProto.EventActivation;
import com.android.internal.os.StatsdConfigProto.FieldFilter;
import com.android.internal.os.StatsdConfigProto.FieldMatcher;
import com.android.internal.os.StatsdConfigProto.FieldValueMatcher;
-import com.android.internal.os.StatsdConfigProto.GaugeMetric;
+import com.android.internal.os.StatsdConfigProto.MetricActivation;
import com.android.internal.os.StatsdConfigProto.Predicate;
import com.android.internal.os.StatsdConfigProto.SimpleAtomMatcher;
import com.android.internal.os.StatsdConfigProto.SimplePredicate;
import com.android.internal.os.StatsdConfigProto.StatsdConfig;
import com.android.internal.os.StatsdConfigProto.TimeUnit;
-import com.android.os.AtomsProto.Atom;
+import com.android.internal.os.StatsdConfigProto.ValueMetric;
+
import com.android.os.AtomsProto.AppBreadcrumbReported;
-import com.android.os.AtomsProto.ScreenStateChanged;
-import com.android.os.StatsLog.ConfigMetricsReport;
-import com.android.os.StatsLog.ConfigMetricsReportList;
-import com.android.os.StatsLog.EventMetricData;
-import com.android.os.StatsLog.GaugeMetricData;
+import com.android.os.AtomsProto.Atom;
+import com.android.os.AtomsProto.SystemElapsedRealtime;
import com.android.os.StatsLog.StatsLogReport;
+import com.android.os.StatsLog.ValueBucketInfo;
+import com.android.os.StatsLog.ValueMetricData;
+
import com.android.tradefed.log.LogUtil;
public class ValueMetricsTests extends DeviceAtomTestCase {
@@ -67,7 +57,7 @@
AtomMatcher atomMatcher =
MetricsUtils.simpleAtomMatcher(APP_BREADCRUMB_REPORTED_B_MATCH_START_ID);
- StatsdConfigProto.StatsdConfig.Builder builder = createConfigBuilder();
+ StatsdConfig.Builder builder = createConfigBuilder();
builder.addAtomMatcher(startAtomMatcher);
builder.addAtomMatcher(stopAtomMatcher);
builder.addAtomMatcher(atomMatcher);
@@ -77,7 +67,7 @@
ValueMetric.newBuilder()
.setId(MetricsUtils.VALUE_METRIC_ID)
.setWhat(APP_BREADCRUMB_REPORTED_B_MATCH_START_ID)
- .setBucket(StatsdConfigProto.TimeUnit.CTS)
+ .setBucket(TimeUnit.CTS)
.setValueField(FieldMatcher.newBuilder()
.setField(Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER)
.addChild(FieldMatcher.newBuilder().setField(
@@ -135,7 +125,7 @@
AtomMatcher stopAtomMatcher =
MetricsUtils.stopAtomMatcher(predicateFalseName.hashCode());
- StatsdConfigProto.StatsdConfig.Builder builder = createConfigBuilder();
+ StatsdConfig.Builder builder = createConfigBuilder();
builder.addAtomMatcher(startAtomMatcher);
builder.addAtomMatcher(stopAtomMatcher);
builder.addPredicate(Predicate.newBuilder()
@@ -158,7 +148,7 @@
ValueMetric.newBuilder()
.setId(MetricsUtils.VALUE_METRIC_ID)
.setWhat(atomName.hashCode())
- .setBucket(StatsdConfigProto.TimeUnit.ONE_MINUTE)
+ .setBucket(TimeUnit.ONE_MINUTE)
.setValueField(FieldMatcher.newBuilder()
.setField(Atom.SYSTEM_ELAPSED_REALTIME_FIELD_NUMBER)
.addChild(FieldMatcher.newBuilder().setField(
@@ -215,7 +205,7 @@
AtomMatcher stopAtomMatcher =
MetricsUtils.stopAtomMatcher(predicateFalseName.hashCode());
- StatsdConfigProto.StatsdConfig.Builder builder = createConfigBuilder();
+ StatsdConfig.Builder builder = createConfigBuilder();
builder.addAtomMatcher(startAtomMatcher);
builder.addAtomMatcher(stopAtomMatcher);
builder.addPredicate(Predicate.newBuilder()
@@ -238,7 +228,7 @@
ValueMetric.newBuilder()
.setId(MetricsUtils.VALUE_METRIC_ID)
.setWhat(atomName.hashCode())
- .setBucket(StatsdConfigProto.TimeUnit.ONE_MINUTE)
+ .setBucket(TimeUnit.ONE_MINUTE)
.setValueField(FieldMatcher.newBuilder()
.setField(Atom.SYSTEM_ELAPSED_REALTIME_FIELD_NUMBER)
.addChild(FieldMatcher.newBuilder().setField(
@@ -283,4 +273,62 @@
// At most we lose one full min bucket
assertTrue(totalValue > (GAP_INTERVAL*NUM_EVENTS - 60_000));
}
+
+ // Test value metric with pulled atoms and across multiple buckets
+ public void testPullerAcrossBucketsWithActivation() throws Exception {
+ if (statsdDisabled()) {
+ return;
+ }
+
+ StatsdConfig.Builder builder = createConfigBuilder();
+
+ // Add AtomMatcher's.
+ int activationAtomMatcherId = 1;
+ int activationAtomMatcherLabel = 1;
+ AtomMatcher activationAtomMatcher =
+ MetricsUtils.appBreadcrumbMatcherWithLabel(
+ activationAtomMatcherId, activationAtomMatcherLabel);
+ final String atomName = "SYSTEM_ELAPSED_REALTIME";
+ SimpleAtomMatcher.Builder sam = SimpleAtomMatcher.newBuilder()
+ .setAtomId(Atom.SYSTEM_ELAPSED_REALTIME_FIELD_NUMBER);
+ builder.addAtomMatcher(activationAtomMatcher)
+ .addAtomMatcher(AtomMatcher.newBuilder()
+ .setId(atomName.hashCode())
+ .setSimpleAtomMatcher(sam));
+
+ // Add ValueMetric.
+ builder.addValueMetric(
+ ValueMetric.newBuilder()
+ .setId(MetricsUtils.VALUE_METRIC_ID)
+ .setWhat(atomName.hashCode())
+ .setBucket(TimeUnit.ONE_MINUTE)
+ .setValueField(FieldMatcher.newBuilder()
+ .setField(Atom.SYSTEM_ELAPSED_REALTIME_FIELD_NUMBER)
+ .addChild(FieldMatcher.newBuilder().setField(
+ SystemElapsedRealtime.TIME_MILLIS_FIELD_NUMBER)))
+ .build());
+ // Add activation.
+ builder.addMetricActivation(MetricActivation.newBuilder()
+ .setMetricId(MetricsUtils.VALUE_METRIC_ID)
+ .setActivationType(ActivationType.ACTIVATE_IMMEDIATELY)
+ .addEventActivation(EventActivation.newBuilder()
+ .setAtomMatcherId(activationAtomMatcherId)
+ .setTtlSeconds(5)));
+
+
+ // Upload config.
+ uploadConfig(builder);
+
+ // Wait for 1 min and 10 sec to capture at least 1 bucket
+ Thread.sleep(60_000 + 10_000);
+
+ // Wait for the metrics to propagate to statsd.
+ Thread.sleep(1_000);
+
+ StatsLogReport metricReport = getStatsLogReport();
+ LogUtil.CLog.d("Got the following value metric data: " + metricReport.toString());
+ assertEquals(MetricsUtils.VALUE_METRIC_ID, metricReport.getMetricId());
+ assertFalse(metricReport.hasValueMetrics());
+ }
+
}
diff --git a/hostsidetests/sustainedperf/AndroidTest.xml b/hostsidetests/sustainedperf/AndroidTest.xml
index dd49674..2377fa4 100644
--- a/hostsidetests/sustainedperf/AndroidTest.xml
+++ b/hostsidetests/sustainedperf/AndroidTest.xml
@@ -16,6 +16,8 @@
<configuration description="Config for CTS Sustained Performance host test cases">
<option name="test-suite-tag" value="cts" />
<option name="config-descriptor:metadata" key="component" value="systems" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="CtsSustainedPerformanceTestCases.apk" />
diff --git a/hostsidetests/systemui/AndroidTest.xml b/hostsidetests/systemui/AndroidTest.xml
index e1ae6e9..baeb90d 100644
--- a/hostsidetests/systemui/AndroidTest.xml
+++ b/hostsidetests/systemui/AndroidTest.xml
@@ -16,6 +16,8 @@
<configuration description="Config for CTS System UI host test cases">
<option name="test-suite-tag" value="cts" />
<option name="config-descriptor:metadata" key="component" value="sysui" />
+ <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="CtsSystemUiDeviceApp.apk" />
diff --git a/tests/accessibility/Android.bp b/tests/accessibility/Android.bp
index c72a2ba..fba6008 100644
--- a/tests/accessibility/Android.bp
+++ b/tests/accessibility/Android.bp
@@ -12,14 +12,23 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+java_library_static {
+ name: "CtsAccessibilityCommon",
+ sdk_version: "test_current",
+ static_libs: [
+ "compatibility-device-util-axt",
+ ],
+ srcs: ["common/src/**/*.java"],
+}
+
android_test {
name: "CtsAccessibilityTestCases",
defaults: ["cts_defaults"],
srcs: ["src/**/*.java"],
static_libs: [
- "compatibility-device-util-axt",
"ctstestrunner-axt",
"hamcrest-library",
+ "CtsAccessibilityCommon",
],
libs: ["android.test.base.stubs"],
// Tag this module as a cts test artifact
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/InstrumentedAccessibilityService.java b/tests/accessibility/common/src/android/accessibility/cts/common/InstrumentedAccessibilityService.java
similarity index 85%
rename from tests/accessibilityservice/src/android/accessibilityservice/cts/InstrumentedAccessibilityService.java
rename to tests/accessibility/common/src/android/accessibility/cts/common/InstrumentedAccessibilityService.java
index 0ff067f..3f9a344 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/InstrumentedAccessibilityService.java
+++ b/tests/accessibility/common/src/android/accessibility/cts/common/InstrumentedAccessibilityService.java
@@ -14,40 +14,44 @@
* limitations under the License.
*/
-package android.accessibilityservice.cts;
+package android.accessibility.cts.common;
+
+import static com.android.compatibility.common.util.TestUtils.waitOn;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
import android.accessibilityservice.AccessibilityService;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.app.Instrumentation;
+import android.app.UiAutomation;
import android.content.Context;
+import android.content.Intent;
import android.os.Handler;
import android.os.SystemClock;
import android.provider.Settings;
-import androidx.annotation.CallSuper;
-import android.test.InstrumentationTestCase;
+import android.util.Log;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
+import androidx.annotation.CallSuper;
+
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
-
public class InstrumentedAccessibilityService extends AccessibilityService {
+ private static final String LOG_TAG = "InstrumentedA11yService";
private static final boolean DEBUG = false;
// Match com.android.server.accessibility.AccessibilityManagerService#COMPONENT_NAME_SEPARATOR
private static final String COMPONENT_NAME_SEPARATOR = ":";
-
- // Timeout disabled in #DEBUG mode to prevent breakpoint-related failures
- private static final int TIMEOUT_SERVICE_ENABLE = DEBUG ? Integer.MAX_VALUE : 10000;
private static final int TIMEOUT_SERVICE_PERFORM_SYNC = DEBUG ? Integer.MAX_VALUE : 5000;
private static final HashMap<Class, WeakReference<InstrumentedAccessibilityService>>
@@ -55,8 +59,11 @@
private final Handler mHandler = new Handler();
final Object mInterruptWaitObject = new Object();
+
public boolean mOnInterruptCalled;
+ // Timeout disabled in #DEBUG mode to prevent breakpoint-related failures
+ public static final int TIMEOUT_SERVICE_ENABLE = DEBUG ? Integer.MAX_VALUE : 10000;
@Override
@CallSuper
@@ -65,6 +72,13 @@
sInstances.put(getClass(), new WeakReference<>(this));
sInstances.notifyAll();
}
+ Log.v(LOG_TAG, "onServiceConnected [" + this + "]");
+ }
+
+ @Override
+ public boolean onUnbind(Intent intent) {
+ Log.v(LOG_TAG, "onUnbind [" + this + "]");
+ return false;
}
@Override
@@ -72,6 +86,7 @@
synchronized (sInstances) {
sInstances.remove(getClass());
}
+ Log.v(LOG_TAG, "onDestroy [" + this + "]");
}
@Override
@@ -151,7 +166,7 @@
}
}
- protected static <T extends InstrumentedAccessibilityService> T enableService(
+ public static <T extends InstrumentedAccessibilityService> T enableService(
Instrumentation instrumentation, Class<T> clazz) {
final String serviceName = clazz.getSimpleName();
final Context context = instrumentation.getContext();
@@ -189,7 +204,7 @@
throw new RuntimeException("Accessibility service " + serviceName + " not found");
}
- private static <T extends InstrumentedAccessibilityService> T getInstanceForClass(Class clazz,
+ public static <T extends InstrumentedAccessibilityService> T getInstanceForClass(Class clazz,
long timeoutMillis) {
final long timeoutTimeMillis = SystemClock.uptimeMillis() + timeoutMillis;
while (SystemClock.uptimeMillis() < timeoutTimeMillis) {
@@ -218,32 +233,23 @@
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);
}
});
-
- ShellCommandBuilder.create(instrumentation)
+ final UiAutomation uiAutomation = instrumentation.getUiAutomation(
+ UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES);
+ ShellCommandBuilder.create(uiAutomation)
.deleteSecureSetting(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES)
.deleteSecureSetting(Settings.Secure.ACCESSIBILITY_ENABLED)
.run();
+ uiAutomation.destroy();
- final long timeoutTimeMillis = SystemClock.uptimeMillis() + TIMEOUT_SERVICE_ENABLE;
- while (SystemClock.uptimeMillis() < timeoutTimeMillis) {
- synchronized (waitLockForA11yOff) {
- if (manager.getEnabledAccessibilityServiceList(
- AccessibilityServiceInfo.FEEDBACK_ALL_MASK).isEmpty()) {
- return;
- }
- try {
- waitLockForA11yOff.wait(timeoutTimeMillis - SystemClock.uptimeMillis());
- } catch (InterruptedException e) {
- // Ignored; loop again
- }
- }
- }
- throw new RuntimeException("Disabling all accessibility services took longer than "
- + TIMEOUT_SERVICE_ENABLE + "ms");
+ waitOn(waitLockForA11yOff, () -> !accessibilityEnabled.get(), TIMEOUT_SERVICE_ENABLE,
+ "Accessibility turns off");
}
}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/ShellCommandBuilder.java b/tests/accessibility/common/src/android/accessibility/cts/common/ShellCommandBuilder.java
similarity index 77%
rename from tests/accessibilityservice/src/android/accessibilityservice/cts/ShellCommandBuilder.java
rename to tests/accessibility/common/src/android/accessibility/cts/common/ShellCommandBuilder.java
index 109c3f8..c305ceb 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/ShellCommandBuilder.java
+++ b/tests/accessibility/common/src/android/accessibility/cts/common/ShellCommandBuilder.java
@@ -14,11 +14,12 @@
* limitations under the License.
*/
-package android.accessibilityservice.cts;
+package android.accessibility.cts.common;
import android.app.Instrumentation;
import android.app.UiAutomation;
import android.os.ParcelFileDescriptor;
+import android.util.Log;
import java.io.BufferedReader;
import java.io.FileInputStream;
@@ -28,20 +29,33 @@
import java.nio.charset.StandardCharsets;
import java.util.LinkedList;
+/**
+ * A helper class to execute batch of shell commands.
+ */
public class ShellCommandBuilder {
- private final LinkedList<Runnable> mCommands = new LinkedList<>();
+ private static final String LOG_TAG = "ShellCommandBuilder";
- private final Instrumentation mInstrumentation;
+ private final LinkedList<Runnable> mCommands = new LinkedList<>();
private final UiAutomation mUiAutomation;
+ /**
+ * Returns a {@link ShellCommandBuilder} with an {@link UiAutomation} which doesn't suppress
+ * accessibility service.
+ */
public static ShellCommandBuilder create(Instrumentation instrumentation) {
- return new ShellCommandBuilder(instrumentation);
+ return new ShellCommandBuilder(instrumentation.getUiAutomation(
+ UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES));
}
- private ShellCommandBuilder(Instrumentation instrumentation) {
- mInstrumentation = instrumentation;
- mUiAutomation = mInstrumentation.getUiAutomation(
- UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES);
+ /**
+ * Returns a {@link ShellCommandBuilder} with {@code uiAutomation}.
+ */
+ public static ShellCommandBuilder create(UiAutomation uiAutomation) {
+ return new ShellCommandBuilder(uiAutomation);
+ }
+
+ private ShellCommandBuilder(UiAutomation uiAutomation) {
+ mUiAutomation = uiAutomation;
}
public void run() {
@@ -75,6 +89,7 @@
}
public static void execShellCommand(UiAutomation automation, String command) {
+ Log.i(LOG_TAG, "command [" + command + "]");
try (ParcelFileDescriptor fd = automation.executeShellCommand(command)) {
try (InputStream inputStream = new FileInputStream(fd.getFileDescriptor())) {
try (BufferedReader reader = new BufferedReader(
@@ -85,7 +100,7 @@
}
}
} catch (IOException e) {
- throw new RuntimeException("Failed to exec shell command", e);
+ throw new RuntimeException("Failed to exec shell command [" + command + "]", e);
}
}
}
diff --git a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityManagerTest.java b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityManagerTest.java
index d42e52b..a681baf 100644
--- a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityManagerTest.java
+++ b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityManagerTest.java
@@ -16,12 +16,11 @@
package android.view.accessibility.cts;
-import static android.view.accessibility.cts.ServiceControlUtils.TIMEOUT_FOR_SERVICE_ENABLE;
+import static android.accessibility.cts.common.InstrumentedAccessibilityService.TIMEOUT_SERVICE_ENABLE;
import static android.view.accessibility.cts.ServiceControlUtils.getEnabledServices;
import static android.view.accessibility.cts.ServiceControlUtils.waitForConditionWithServiceStateChange;
import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
-import static com.android.compatibility.common.util.TestUtils.waitOn;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -29,6 +28,7 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import android.accessibility.cts.common.InstrumentedAccessibilityService;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.app.Instrumentation;
import android.app.Service;
@@ -97,12 +97,12 @@
mHandler = new Handler(mTargetContext.getMainLooper());
// In case the test runner started a UiAutomation, destroy it to start with a clean slate.
sInstrumentation.getUiAutomation().destroy();
- ServiceControlUtils.turnAccessibilityOff(sInstrumentation);
+ InstrumentedAccessibilityService.disableAllServices(sInstrumentation);
}
@After
public void tearDown() throws Exception {
- ServiceControlUtils.turnAccessibilityOff(sInstrumentation);
+ InstrumentedAccessibilityService.disableAllServices(sInstrumentation);
}
@Test
@@ -127,7 +127,8 @@
@Test
public void testIsTouchExplorationEnabled() throws Exception {
- ServiceControlUtils.enableSpeakingAndVibratingServices(sInstrumentation);
+ SpeakingAccessibilityService.enableSelf(sInstrumentation);
+ VibratingAccessibilityService.enableSelf(sInstrumentation);
new PollingCheck() {
@Override
protected boolean check() {
@@ -162,7 +163,8 @@
@Test
public void testGetEnabledAccessibilityServiceList() throws Exception {
- ServiceControlUtils.enableSpeakingAndVibratingServices(sInstrumentation);
+ SpeakingAccessibilityService.enableSelf(sInstrumentation);
+ VibratingAccessibilityService.enableSelf(sInstrumentation);
List<AccessibilityServiceInfo> enabledServices =
mAccessibilityManager.getEnabledAccessibilityServiceList(
AccessibilityServiceInfo.FEEDBACK_ALL_MASK);
@@ -187,7 +189,8 @@
@Test
public void testGetEnabledAccessibilityServiceListForType() throws Exception {
- ServiceControlUtils.enableSpeakingAndVibratingServices(sInstrumentation);
+ SpeakingAccessibilityService.enableSelf(sInstrumentation);
+ VibratingAccessibilityService.enableSelf(sInstrumentation);
List<AccessibilityServiceInfo> enabledServices =
mAccessibilityManager.getEnabledAccessibilityServiceList(
AccessibilityServiceInfo.FEEDBACK_SPOKEN);
@@ -206,9 +209,10 @@
@Test
public void testGetEnabledAccessibilityServiceListForTypes() throws Exception {
- ServiceControlUtils.enableSpeakingAndVibratingServices(sInstrumentation);
+ SpeakingAccessibilityService.enableSelf(sInstrumentation);
+ VibratingAccessibilityService.enableSelf(sInstrumentation);
// For this test, also enable a service with multiple feedback types
- ServiceControlUtils.enableMultipleFeedbackTypesService(sInstrumentation);
+ SpeakingAndVibratingAccessibilityService.enableSelf(sInstrumentation);
List<AccessibilityServiceInfo> enabledServices =
mAccessibilityManager.getEnabledAccessibilityServiceList(
@@ -268,7 +272,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.
- ServiceControlUtils.enableSpeakingAndVibratingServices(sInstrumentation);
+ SpeakingAccessibilityService.enableSelf(sInstrumentation);
+ VibratingAccessibilityService.enableSelf(sInstrumentation);
waitForAccessibilityEnabled();
mAccessibilityManager.interrupt();
}
@@ -277,7 +282,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.
- ServiceControlUtils.enableSpeakingAndVibratingServices(sInstrumentation);
+ SpeakingAccessibilityService.enableSelf(sInstrumentation);
+ VibratingAccessibilityService.enableSelf(sInstrumentation);
waitForAccessibilityEnabled();
mAccessibilityManager.sendAccessibilityEvent(AccessibilityEvent.obtain(
AccessibilityEvent.TYPE_VIEW_CLICKED));
@@ -295,12 +301,13 @@
}
};
mAccessibilityManager.addTouchExplorationStateChangeListener(listener);
- ServiceControlUtils.enableSpeakingAndVibratingServices(sInstrumentation);
+ SpeakingAccessibilityService.enableSelf(sInstrumentation);
+ VibratingAccessibilityService.enableSelf(sInstrumentation);
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());
- ServiceControlUtils.turnAccessibilityOff(sInstrumentation);
+ InstrumentedAccessibilityService.disableAllServices(sInstrumentation);
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",
@@ -320,12 +327,13 @@
}
};
mAccessibilityManager.addTouchExplorationStateChangeListener(listener, mHandler);
- ServiceControlUtils.enableSpeakingAndVibratingServices(sInstrumentation);
+ SpeakingAccessibilityService.enableSelf(sInstrumentation);
+ VibratingAccessibilityService.enableSelf(sInstrumentation);
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());
- ServiceControlUtils.turnAccessibilityOff(sInstrumentation);
+ InstrumentedAccessibilityService.disableAllServices(sInstrumentation);
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",
@@ -345,12 +353,12 @@
}
};
mAccessibilityManager.addAccessibilityStateChangeListener(listener);
- ServiceControlUtils.enableMultipleFeedbackTypesService(sInstrumentation);
+ SpeakingAndVibratingAccessibilityService.enableSelf(sInstrumentation);
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());
- ServiceControlUtils.turnAccessibilityOff(sInstrumentation);
+ InstrumentedAccessibilityService.disableAllServices(sInstrumentation);
assertAtomicBooleanBecomes(atomicBoolean, false, waitObject,
"Accessibility state listener not called when services disabled");
assertFalse("Listener told that accessibility is disabled, but manager says enabled",
@@ -370,12 +378,12 @@
}
};
mAccessibilityManager.addAccessibilityStateChangeListener(listener, mHandler);
- ServiceControlUtils.enableMultipleFeedbackTypesService(sInstrumentation);
+ SpeakingAndVibratingAccessibilityService.enableSelf(sInstrumentation);
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());
- ServiceControlUtils.turnAccessibilityOff(sInstrumentation);
+ InstrumentedAccessibilityService.disableAllServices(sInstrumentation);
assertAtomicBooleanBecomes(atomicBoolean, false, waitObject,
"Accessibility state listener not called when services disabled");
assertFalse("Listener told that accessibility is disabled, but manager says enabled",
@@ -385,7 +393,8 @@
@Test
public void testGetRecommendedTimeoutMillis() throws Exception {
- ServiceControlUtils.enableSpeakingAndVibratingServices(sInstrumentation);
+ SpeakingAccessibilityService.enableSelf(sInstrumentation);
+ VibratingAccessibilityService.enableSelf(sInstrumentation);
waitForAccessibilityEnabled();
UiAutomation automan = sInstrumentation.getUiAutomation(
UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES);
@@ -445,9 +454,10 @@
runWithShellPermissionIdentity(uiAutomation,
() -> mAccessibilityManager.performAccessibilityShortcut());
// Make sure the service starts up
- waitOn(SpeakingAccessibilityService.sWaitObjectForConnecting,
- () -> SpeakingAccessibilityService.sConnectedInstance != null,
- TIMEOUT_FOR_SERVICE_ENABLE, "Speaking accessibility service starts up");
+ final SpeakingAccessibilityService service =
+ SpeakingAccessibilityService.getInstanceForClass(
+ SpeakingAccessibilityService.class, TIMEOUT_SERVICE_ENABLE);
+ assertTrue("Speaking accessibility service starts up", service != null);
} finally {
configureShortcut(uiAutomation, originalShortcut);
uiAutomation.destroy();
@@ -464,7 +474,7 @@
waitForConditionWithServiceStateChange(mTargetContext, () -> TextUtils.equals(
mAccessibilityManager.getAccessibilityShortcutService(),
shortcutService),
- TIMEOUT_FOR_SERVICE_ENABLE,
+ TIMEOUT_SERVICE_ENABLE,
"accessibility shortcut set to test service"));
}
return currentService;
@@ -474,7 +484,7 @@
boolean expectedValue, Object waitObject, String message)
throws Exception {
long timeoutTime =
- System.currentTimeMillis() + TIMEOUT_FOR_SERVICE_ENABLE;
+ System.currentTimeMillis() + TIMEOUT_SERVICE_ENABLE;
synchronized (waitObject) {
while ((atomicBoolean.get() != expectedValue)
&& (System.currentTimeMillis() < timeoutTime)) {
@@ -494,7 +504,7 @@
};
mAccessibilityManager.addAccessibilityStateChangeListener(listener);
long timeoutTime =
- System.currentTimeMillis() + TIMEOUT_FOR_SERVICE_ENABLE;
+ System.currentTimeMillis() + TIMEOUT_SERVICE_ENABLE;
synchronized (waitObject) {
while (!mAccessibilityManager.isEnabled()
&& (System.currentTimeMillis() < timeoutTime)) {
diff --git a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityServiceInfoTest.java b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityServiceInfoTest.java
index cf3fb15..0029b53 100644
--- a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityServiceInfoTest.java
+++ b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityServiceInfoTest.java
@@ -16,6 +16,7 @@
package android.view.accessibility.cts;
+import android.accessibility.cts.common.InstrumentedAccessibilityService;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.app.Service;
import android.test.InstrumentationTestCase;
@@ -36,12 +37,13 @@
@Override
public void setUp() throws Exception {
- ServiceControlUtils.enableSpeakingAndVibratingServices(getInstrumentation());
+ SpeakingAccessibilityService.enableSelf(getInstrumentation());
+ VibratingAccessibilityService.enableSelf(getInstrumentation());
}
@Override
public void tearDown() {
- ServiceControlUtils.turnAccessibilityOff(getInstrumentation());
+ InstrumentedAccessibilityService.disableAllServices(getInstrumentation());
}
/**
diff --git a/tests/accessibility/src/android/view/accessibility/cts/ServiceControlUtils.java b/tests/accessibility/src/android/view/accessibility/cts/ServiceControlUtils.java
index 437c35e..9b1fae1 100644
--- a/tests/accessibility/src/android/view/accessibility/cts/ServiceControlUtils.java
+++ b/tests/accessibility/src/android/view/accessibility/cts/ServiceControlUtils.java
@@ -18,134 +18,17 @@
import static com.android.compatibility.common.util.TestUtils.waitOn;
-import android.app.Instrumentation;
-import android.app.UiAutomation;
import android.content.ContentResolver;
import android.content.Context;
-import android.os.ParcelFileDescriptor;
import android.provider.Settings;
import android.view.accessibility.AccessibilityManager;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.BooleanSupplier;
/**
* Utility methods for enabling and disabling the services used in this package
*/
public class ServiceControlUtils {
- public static final int TIMEOUT_FOR_SERVICE_ENABLE = 10000; // millis; 10s
-
- private static final String SETTING_ENABLE_SPEAKING_AND_VIBRATING_SERVICES =
- "android.view.accessibility.cts/.SpeakingAccessibilityService:"
- + "android.view.accessibility.cts/.VibratingAccessibilityService";
-
- private static final String SETTING_ENABLE_MULTIPLE_FEEDBACK_TYPES_SERVICE =
- "android.view.accessibility.cts/.SpeakingAndVibratingAccessibilityService";
-
- /**
- * Enable {@code SpeakingAccessibilityService} and {@code VibratingAccessibilityService}
- *
- * @param instrumentation A valid instrumentation
- */
- public static void enableSpeakingAndVibratingServices(Instrumentation instrumentation)
- throws IOException {
- Context context = instrumentation.getContext();
-
- // Get permission to enable accessibility
- UiAutomation uiAutomation = instrumentation.getUiAutomation();
-
- // Change the settings to enable the two services
- String alreadyEnabledServices = getEnabledServices(context.getContentResolver());
- ParcelFileDescriptor fd = uiAutomation.executeShellCommand("settings --user cur put secure "
- + Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES + " "
- + alreadyEnabledServices + ":"
- + SETTING_ENABLE_SPEAKING_AND_VIBRATING_SERVICES);
- InputStream in = new FileInputStream(fd.getFileDescriptor());
- byte[] buffer = new byte[4096];
- while (in.read(buffer) > 0);
- uiAutomation.destroy();
-
- // Wait for speaking service to be connected
- waitOn(SpeakingAccessibilityService.sWaitObjectForConnecting,
- () -> SpeakingAccessibilityService.sConnectedInstance != null,
- TIMEOUT_FOR_SERVICE_ENABLE, "Speaking accessibility service starts up");
-
- // Wait for vibrating service to be connected
- waitOn(VibratingAccessibilityService.sWaitObjectForConnecting,
- () -> VibratingAccessibilityService.sConnectedInstance != null,
- TIMEOUT_FOR_SERVICE_ENABLE, "Vibrating accessibility service starts up");
- }
-
- /**
- * Enable {@link SpeakingAndVibratingAccessibilityService} for tests requiring a service with
- * multiple feedback types
- *
- * @param instrumentation A valid instrumentation
- */
- public static void enableMultipleFeedbackTypesService(Instrumentation instrumentation)
- throws IOException {
- Context context = instrumentation.getContext();
-
- // Get permission to enable accessibility
- UiAutomation uiAutomation = instrumentation.getUiAutomation();
-
- // Change the settings to enable the services
- String alreadyEnabledServices = getEnabledServices(context.getContentResolver());
- ParcelFileDescriptor fd = uiAutomation.executeShellCommand("settings --user cur put secure "
- + Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES + " "
- + alreadyEnabledServices + ":"
- + SETTING_ENABLE_MULTIPLE_FEEDBACK_TYPES_SERVICE);
- InputStream in = new FileInputStream(fd.getFileDescriptor());
- byte[] buffer = new byte[4096];
- while (in.read(buffer) > 0);
- uiAutomation.destroy();
-
- // Wait for the service to be connected
- waitOn(SpeakingAndVibratingAccessibilityService.sWaitObjectForConnecting,
- () -> SpeakingAndVibratingAccessibilityService.sConnectedInstance != null,
- TIMEOUT_FOR_SERVICE_ENABLE,
- "Multiple feedback types accessibility service starts up");
- }
-
- /**
- * Turn off all accessibility services. Assumes permissions to write settings are already
- * set, which they are in
- * {@link ServiceControlUtils#enableSpeakingAndVibratingServices(Instrumentation)}.
- *
- * @param instrumentation A valid instrumentation
- */
- public static void turnAccessibilityOff(Instrumentation instrumentation) {
- if (SpeakingAccessibilityService.sConnectedInstance != null) {
- SpeakingAccessibilityService.sConnectedInstance.disableSelf();
- SpeakingAccessibilityService.sConnectedInstance = null;
- }
- if (VibratingAccessibilityService.sConnectedInstance != null) {
- VibratingAccessibilityService.sConnectedInstance.disableSelf();
- VibratingAccessibilityService.sConnectedInstance = null;
- }
- if (SpeakingAndVibratingAccessibilityService.sConnectedInstance != null) {
- SpeakingAndVibratingAccessibilityService.sConnectedInstance.disableSelf();
- SpeakingAndVibratingAccessibilityService.sConnectedInstance = null;
- }
-
- final Object waitLockForA11yOff = new Object();
- AccessibilityManager manager = (AccessibilityManager) instrumentation
- .getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
- // Updates to manager.isEnabled() aren't synchronized
- AtomicBoolean accessibilityEnabled = new AtomicBoolean(manager.isEnabled());
- AccessibilityManager.AccessibilityStateChangeListener listener = (boolean b) -> {
- synchronized (waitLockForA11yOff) {
- waitLockForA11yOff.notifyAll();
- accessibilityEnabled.set(b);
- }
- };
- manager.addAccessibilityStateChangeListener(listener);
- waitOn(waitLockForA11yOff, () -> !accessibilityEnabled.get(), TIMEOUT_FOR_SERVICE_ENABLE,
- "Accessibility turns off");
- }
public static String getEnabledServices(ContentResolver cr) {
return Settings.Secure.getString(cr, Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
diff --git a/tests/accessibility/src/android/view/accessibility/cts/SpeakingAccessibilityService.java b/tests/accessibility/src/android/view/accessibility/cts/SpeakingAccessibilityService.java
index 19daf4b..d05232a 100644
--- a/tests/accessibility/src/android/view/accessibility/cts/SpeakingAccessibilityService.java
+++ b/tests/accessibility/src/android/view/accessibility/cts/SpeakingAccessibilityService.java
@@ -16,46 +16,20 @@
package android.view.accessibility.cts;
-import android.accessibilityservice.AccessibilityService;
-import android.accessibilityservice.AccessibilityServiceInfo;
+import android.accessibility.cts.common.InstrumentedAccessibilityService;
+import android.app.Instrumentation;
import android.content.ComponentName;
-import android.view.accessibility.AccessibilityEvent;
/**
* Stub accessibility service that reports itself as providing spoken feedback.
*/
-public class SpeakingAccessibilityService extends AccessibilityService {
+public class SpeakingAccessibilityService extends InstrumentedAccessibilityService {
public static final ComponentName COMPONENT_NAME = new ComponentName(
"android.view.accessibility.cts",
"android.view.accessibility.cts.SpeakingAccessibilityService");
- public static Object sWaitObjectForConnecting = new Object();
- public static SpeakingAccessibilityService sConnectedInstance;
-
- @Override
- public void onDestroy() {
- sConnectedInstance = null;
- }
-
- @Override
- protected void onServiceConnected() {
- final AccessibilityServiceInfo info = getServiceInfo();
- info.flags |= AccessibilityServiceInfo.FLAG_REQUEST_TOUCH_EXPLORATION_MODE;
- setServiceInfo(info);
-
- synchronized (sWaitObjectForConnecting) {
- sConnectedInstance = this;
- sWaitObjectForConnecting.notifyAll();
- }
- }
-
- @Override
- public void onAccessibilityEvent(AccessibilityEvent event) {
- /* do nothing */
- }
-
- @Override
- public void onInterrupt() {
- /* do nothing */
+ 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 4a591f4..cb8126d 100644
--- a/tests/accessibility/src/android/view/accessibility/cts/SpeakingAndVibratingAccessibilityService.java
+++ b/tests/accessibility/src/android/view/accessibility/cts/SpeakingAndVibratingAccessibilityService.java
@@ -16,37 +16,16 @@
package android.view.accessibility.cts;
-import android.accessibilityservice.AccessibilityService;
-import android.accessibilityservice.AccessibilityServiceInfo;
-import android.view.accessibility.AccessibilityEvent;
+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 AccessibilityService {
- public static Object sWaitObjectForConnecting = new Object();
-
- public static SpeakingAndVibratingAccessibilityService sConnectedInstance;
-
- @Override
- public void onDestroy() {
- sConnectedInstance = null;
- }
-
-
- @Override
- protected void onServiceConnected() {
- synchronized (sWaitObjectForConnecting) {
- sConnectedInstance = this;
- sWaitObjectForConnecting.notifyAll();
- }
- }
-
- @Override
- public void onAccessibilityEvent(AccessibilityEvent event) {
- }
-
- @Override
- public void onInterrupt() {
+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 41edf9f..6b866aa 100644
--- a/tests/accessibility/src/android/view/accessibility/cts/VibratingAccessibilityService.java
+++ b/tests/accessibility/src/android/view/accessibility/cts/VibratingAccessibilityService.java
@@ -16,42 +16,15 @@
package android.view.accessibility.cts;
-import android.accessibilityservice.AccessibilityService;
-import android.accessibilityservice.AccessibilityServiceInfo;
-import android.view.accessibility.AccessibilityEvent;
+import android.accessibility.cts.common.InstrumentedAccessibilityService;
+import android.app.Instrumentation;
/**
* Stub accessibility service that reports itself as providing haptic feedback.
*/
-public class VibratingAccessibilityService extends AccessibilityService {
- public static Object sWaitObjectForConnecting = new Object();
-
- public static VibratingAccessibilityService sConnectedInstance;
-
- @Override
- public void onDestroy() {
- sConnectedInstance = null;
- }
-
- @Override
- protected void onServiceConnected() {
- final AccessibilityServiceInfo info = getServiceInfo();
- info.flags |= AccessibilityServiceInfo.FLAG_REQUEST_TOUCH_EXPLORATION_MODE;
- setServiceInfo(info);
-
- synchronized (sWaitObjectForConnecting) {
- sConnectedInstance = this;
- sWaitObjectForConnecting.notifyAll();
- }
- }
-
- @Override
- public void onAccessibilityEvent(AccessibilityEvent event) {
- /* do nothing */
- }
-
- @Override
- public void onInterrupt() {
- /* do nothing */
+public class VibratingAccessibilityService extends InstrumentedAccessibilityService {
+ public static VibratingAccessibilityService enableSelf(Instrumentation instrumentation) {
+ return InstrumentedAccessibilityService.enableService(instrumentation,
+ VibratingAccessibilityService.class);
}
}
diff --git a/tests/accessibilityservice/Android.mk b/tests/accessibilityservice/Android.mk
index 2bf06d9..c4480d7 100644
--- a/tests/accessibilityservice/Android.mk
+++ b/tests/accessibilityservice/Android.mk
@@ -22,8 +22,8 @@
ctstestrunner-axt \
hamcrest-library \
mockito-target-minus-junit4 \
- compatibility-device-util-axt \
- platform-test-annotations
+ platform-test-annotations \
+ CtsAccessibilityCommon
LOCAL_JAVA_LIBRARIES := android.test.runner.stubs android.test.base.stubs
diff --git a/tests/accessibilityservice/AndroidManifest.xml b/tests/accessibilityservice/AndroidManifest.xml
index e7e313e..fc0b2e6 100644
--- a/tests/accessibilityservice/AndroidManifest.xml
+++ b/tests/accessibilityservice/AndroidManifest.xml
@@ -93,7 +93,7 @@
</service>
<service
- android:name=".InstrumentedAccessibilityService"
+ android:name="android.accessibility.cts.common.InstrumentedAccessibilityService"
android:label="@string/title_soft_keyboard_modes_accessibility_service"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
<intent-filter>
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java
index 586a9d7..3b70810 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java
@@ -45,6 +45,8 @@
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify;
+import android.accessibility.cts.common.InstrumentedAccessibilityService;
+import android.accessibility.cts.common.ShellCommandBuilder;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.accessibilityservice.cts.activities.AccessibilityEndToEndActivity;
import android.app.Activity;
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityMagnificationTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityMagnificationTest.java
index bffdf96..2774457 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityMagnificationTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityMagnificationTest.java
@@ -25,6 +25,8 @@
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify;
+import android.accessibility.cts.common.InstrumentedAccessibilityService;
+import android.accessibility.cts.common.ShellCommandBuilder;
import android.accessibilityservice.AccessibilityService.MagnificationController;
import android.accessibilityservice.AccessibilityService.MagnificationController.OnMagnificationChangedListener;
import android.accessibilityservice.AccessibilityServiceInfo;
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityOverlayTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityOverlayTest.java
index bd053f3..e461bdf 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityOverlayTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityOverlayTest.java
@@ -20,6 +20,7 @@
import static org.junit.Assert.assertTrue;
+import android.accessibility.cts.common.InstrumentedAccessibilityService;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.accessibilityservice.cts.utils.AsyncUtils;
import android.app.Instrumentation;
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilitySoftKeyboardModesTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilitySoftKeyboardModesTest.java
index c683a8d..8bb1bfb 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilitySoftKeyboardModesTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilitySoftKeyboardModesTest.java
@@ -21,6 +21,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import android.accessibility.cts.common.InstrumentedAccessibilityService;
import android.accessibilityservice.AccessibilityService.SoftKeyboardController;
import android.accessibilityservice.AccessibilityService.SoftKeyboardController.OnShowModeChangedListener;
import android.accessibilityservice.cts.activities.AccessibilityTestActivity;
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityVolumeTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityVolumeTest.java
index e8c529d..16e0b68 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityVolumeTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityVolumeTest.java
@@ -17,6 +17,7 @@
import static org.junit.Assert.assertEquals;
+import android.accessibility.cts.common.InstrumentedAccessibilityService;
import android.app.Instrumentation;
import android.content.pm.PackageManager;
import android.media.AudioManager;
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/GestureDetectionStubAccessibilityService.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/GestureDetectionStubAccessibilityService.java
index 63601cd..042904b 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/GestureDetectionStubAccessibilityService.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/GestureDetectionStubAccessibilityService.java
@@ -14,6 +14,8 @@
package android.accessibilityservice.cts;
+import android.accessibility.cts.common.InstrumentedAccessibilityService;
+
import android.app.Instrumentation;
import android.view.accessibility.AccessibilityEvent;
import java.util.ArrayList;
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/MagnificationGestureHandlerTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/MagnificationGestureHandlerTest.java
index df17442..72bbd97 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/MagnificationGestureHandlerTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/MagnificationGestureHandlerTest.java
@@ -43,6 +43,9 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static java.util.concurrent.TimeUnit.SECONDS;
+
+import android.accessibility.cts.common.InstrumentedAccessibilityService;
import android.accessibilityservice.GestureDescription;
import android.accessibilityservice.GestureDescription.StrokeDescription;
import android.accessibilityservice.cts.AccessibilityGestureDispatchTest.GestureDispatchActivity;
@@ -66,8 +69,6 @@
import org.junit.Test;
import org.junit.runner.RunWith;
-import static java.util.concurrent.TimeUnit.SECONDS;
-
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/StubAccessibilityButtonService.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/StubAccessibilityButtonService.java
index ad3050a..c0ce5b6 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/StubAccessibilityButtonService.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/StubAccessibilityButtonService.java
@@ -14,7 +14,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
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/StubFingerprintGestureService.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/StubFingerprintGestureService.java
index 10776c8..87539f8 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/StubFingerprintGestureService.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/StubFingerprintGestureService.java
@@ -14,6 +14,7 @@
package android.accessibilityservice.cts;
+import android.accessibility.cts.common.InstrumentedAccessibilityService;
import android.app.Instrumentation;
/**
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/StubGestureAccessibilityService.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/StubGestureAccessibilityService.java
index e1699e8..68c4e7b 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/StubGestureAccessibilityService.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/StubGestureAccessibilityService.java
@@ -14,6 +14,7 @@
package android.accessibilityservice.cts;
+import android.accessibility.cts.common.InstrumentedAccessibilityService;
import android.accessibilityservice.GestureDescription;
import android.app.Instrumentation;
import android.os.Handler;
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/StubMagnificationAccessibilityService.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/StubMagnificationAccessibilityService.java
index 669ebda..195ce89 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/StubMagnificationAccessibilityService.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/StubMagnificationAccessibilityService.java
@@ -14,6 +14,7 @@
package android.accessibilityservice.cts;
+import android.accessibility.cts.common.InstrumentedAccessibilityService;
import android.app.Instrumentation;
/**
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/GestureUtils.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/GestureUtils.java
index 076a6da..99fa727 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/GestureUtils.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/GestureUtils.java
@@ -16,10 +16,10 @@
package android.accessibilityservice.cts.utils;
+import android.accessibility.cts.common.InstrumentedAccessibilityService;
import android.accessibilityservice.AccessibilityService.GestureResultCallback;
import android.accessibilityservice.GestureDescription;
import android.accessibilityservice.GestureDescription.StrokeDescription;
-import android.accessibilityservice.cts.InstrumentedAccessibilityService;
import android.graphics.Path;
import android.graphics.PointF;
import android.view.ViewConfiguration;
diff --git a/tests/app/DownloadManagerApi28Test/Android.mk b/tests/app/DownloadManagerApi28Test/Android.mk
new file mode 100644
index 0000000..51f4dde
--- /dev/null
+++ b/tests/app/DownloadManagerApi28Test/Android.mk
@@ -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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+# don't include this package in any target
+LOCAL_MODULE_TAGS := optional
+# and when built explicitly put it in the data partition
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_JAVA_LIBRARIES := \
+ android.test.runner.stubs \
+ org.apache.http.legacy \
+ android.test.base.stubs
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ compatibility-device-util-axt \
+ ctstestrunner-axt \
+ ctstestserver \
+ mockito-target-minus-junit4 \
+ androidx.test.rules \
+ platform-test-annotations \
+ androidx.test.rules \
+
+LOCAL_SRC_FILES := \
+ $(call all-java-files-under, src) \
+ ../src/android/app/cts/DownloadManagerTestBase.java
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/../app/res
+
+LOCAL_ASSET_DIR := $(LOCAL_PATH)/../app/assets
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+LOCAL_PACKAGE_NAME := CtsDownloadManagerApi28
+
+LOCAL_SDK_VERSION := test_current
+LOCAL_MIN_SDK_VERSION := 11
+
+include $(BUILD_CTS_PACKAGE)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
\ No newline at end of file
diff --git a/tests/app/DownloadManagerApi28Test/AndroidManifest.xml b/tests/app/DownloadManagerApi28Test/AndroidManifest.xml
new file mode 100644
index 0000000..f6eb1c4
--- /dev/null
+++ b/tests/app/DownloadManagerApi28Test/AndroidManifest.xml
@@ -0,0 +1,35 @@
+<?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.cts.downloads.api28">
+
+ <uses-sdk android:minSdkVersion="11" android:targetSdkVersion="28" />
+
+ <uses-permission android:name="android.permission.INTERNET" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+
+ <application android:usesCleartextTraffic="true"
+ android:networkSecurityConfig="@xml/network_security_config">
+ <uses-library android:name="android.test.runner" />
+ <uses-library android:name="org.apache.http.legacy" android:required="false" />
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.app.cts.downloads.api28" />
+
+</manifest>
\ No newline at end of file
diff --git a/tests/framework/base/windowmanager/dummyTests/dummySdk28/AndroidTest.xml b/tests/app/DownloadManagerApi28Test/AndroidTest.xml
similarity index 64%
copy from tests/framework/base/windowmanager/dummyTests/dummySdk28/AndroidTest.xml
copy to tests/app/DownloadManagerApi28Test/AndroidTest.xml
index 0c3cc4a..8d93448 100644
--- a/tests/framework/base/windowmanager/dummyTests/dummySdk28/AndroidTest.xml
+++ b/tests/app/DownloadManagerApi28Test/AndroidTest.xml
@@ -13,22 +13,18 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
-<!-- TODO(b/129909356): Remove this once CtsActivityManagerDeviceSdk28TestCases is renamed to
- CtsWindowManagerSdk28TestCases -->
-<configuration description="Config for CTS WindowManager dummy SDK 28 compatibility test cases">
+<configuration description="Config for DownloadManagerApi28Test">
<option name="test-suite-tag" value="cts" />
<option name="config-descriptor:metadata" key="component" value="framework" />
- <!-- These tests require targeting API 28 which does not support instant apps -->
- <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
<option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
- <target_preparer class="com.android.compatibility.common.tradefed.targetprep.LocationCheck" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
- <option name="test-file-name" value="CtsActivityManagerDeviceSdk28TestCases.apk" />
+ <option name="test-file-name" value="CtsDownloadManagerApi28.apk" />
</target_preparer>
- <test class="com.android.tradefed.testtype.AndroidJUnitTest">
- <option name="package" value="android.server.wm.cts.dummy28" />
- <option name="runtime-hint" value="1m" />
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+ <option name="package" value="android.app.cts.downloads.api28" />
</test>
-</configuration>
+
+</configuration>
\ No newline at end of file
diff --git a/tests/app/DownloadManagerApi28Test/src/android/app/cts/DownloadManagerApi28Test.java b/tests/app/DownloadManagerApi28Test/src/android/app/cts/DownloadManagerApi28Test.java
new file mode 100644
index 0000000..fd4cea7
--- /dev/null
+++ b/tests/app/DownloadManagerApi28Test/src/android/app/cts/DownloadManagerApi28Test.java
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.app.cts;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.app.DownloadManager;
+import android.content.ContentResolver;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Environment;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.io.FileInputStream;
+
+@RunWith(AndroidJUnit4.class)
+public class DownloadManagerApi28Test extends DownloadManagerTestBase {
+
+ @Test
+ public void testSetDestinationUri_publicDir() throws Exception {
+ File publicLocation = new File(
+ Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS),
+ "publicFile.bin");
+ if (publicLocation.exists()) {
+ assertTrue(publicLocation.delete());
+ }
+
+ final DownloadCompleteReceiver receiver = new DownloadCompleteReceiver();
+ try {
+ IntentFilter intentFilter = new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
+ mContext.registerReceiver(receiver, intentFilter);
+
+ DownloadManager.Request requestPublic = new DownloadManager.Request(getGoodUrl());
+ requestPublic.setDestinationUri(Uri.fromFile(publicLocation));
+ long id = mDownloadManager.enqueue(requestPublic);
+
+ int allDownloads = getTotalNumberDownloads();
+ assertEquals(1, allDownloads);
+
+ receiver.waitForDownloadComplete(SHORT_TIMEOUT, id);
+ assertSuccessfulDownload(id, publicLocation);
+
+ assertRemoveDownload(id, 0);
+ } finally {
+ mContext.unregisterReceiver(receiver);
+ }
+ }
+
+ @Test
+ public void testSetDestinationUri_sdcardPath() throws Exception {
+ File path = new File("/sdcard/publicFile.bin");
+ if (path.exists()) {
+ assertTrue(path.delete());
+ }
+
+ final DownloadCompleteReceiver receiver = new DownloadCompleteReceiver();
+ try {
+ IntentFilter intentFilter = new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
+ mContext.registerReceiver(receiver, intentFilter);
+
+ DownloadManager.Request requestPublic = new DownloadManager.Request(getGoodUrl());
+ requestPublic.setDestinationUri(Uri.fromFile(path));
+ long id = mDownloadManager.enqueue(requestPublic);
+
+ int allDownloads = getTotalNumberDownloads();
+ assertEquals(1, allDownloads);
+
+ receiver.waitForDownloadComplete(SHORT_TIMEOUT, id);
+ assertSuccessfulDownload(id, path);
+
+ assertRemoveDownload(id, 0);
+ } finally {
+ mContext.unregisterReceiver(receiver);
+ }
+ }
+
+ @Test
+ public void testDestinationInExternalPublicDir() throws Exception {
+ File publicLocation = new File(
+ Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS),
+ "publicFile.bin");
+ if (publicLocation.exists()) {
+ assertTrue(publicLocation.delete());
+ }
+
+ final DownloadCompleteReceiver receiver = new DownloadCompleteReceiver();
+ try {
+ IntentFilter intentFilter = new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
+ mContext.registerReceiver(receiver, intentFilter);
+
+ DownloadManager.Request requestPublic = new DownloadManager.Request(getGoodUrl());
+ requestPublic.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOCUMENTS,
+ "publicFile.bin");
+ long id = mDownloadManager.enqueue(requestPublic);
+
+ int allDownloads = getTotalNumberDownloads();
+ assertEquals(1, allDownloads);
+
+ receiver.waitForDownloadComplete(SHORT_TIMEOUT, id);
+ assertSuccessfulDownload(id, publicLocation);
+
+ assertRemoveDownload(id, 0);
+ } finally {
+ mContext.unregisterReceiver(receiver);
+ }
+ }
+
+ @Test
+ public void testAddCompletedDownload_publicDirs() throws Exception {
+ final String[] filePaths = new String[] {
+ createFile(Environment.getExternalStoragePublicDirectory(
+ Environment.DIRECTORY_DOWNLOADS), "file1.txt").getPath(),
+ createFile(Environment.getExternalStoragePublicDirectory(
+ Environment.DIRECTORY_DOCUMENTS), "file2.txt").getPath(),
+ };
+
+ for (String path : filePaths) {
+ final String fileContents = path + "_" + System.nanoTime();
+
+ writeToFile(new File(path), fileContents);
+
+ final long id = mDownloadManager.addCompletedDownload("Test title", "Test desc", true,
+ "text/plain", path, fileContents.getBytes().length, true);
+ final String actualContents = readFromFile(mDownloadManager.openDownloadedFile(id));
+ assertEquals(fileContents, actualContents);
+
+ final Uri downloadUri = mDownloadManager.getUriForDownloadedFile(id);
+ mContext.grantUriPermission("com.android.shell", downloadUri,
+ Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ final String rawFilePath = getRawFilePath(downloadUri);
+ final String rawFileContents = readFromRawFile(rawFilePath);
+ assertEquals(fileContents, rawFileContents);
+ assertRemoveDownload(id, 0);
+ }
+ }
+
+
+ @Test
+ public void testDownloadManager_mediaStoreEntry() throws Exception {
+ final DownloadCompleteReceiver receiver = new DownloadCompleteReceiver();
+ try {
+ mContext.registerReceiver(receiver,
+ new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
+
+ final String fileName = "noiseandchirps.mp3";
+ final DownloadManager.Request request
+ = new DownloadManager.Request(getAssetUrl(fileName));
+ final Uri[] downloadPathUris = new Uri[] {
+ Uri.fromFile(new File(Environment.getExternalStoragePublicDirectory(
+ Environment.DIRECTORY_DOWNLOADS), "testfile1.mp3")),
+ Uri.parse("file:///sdcard/testfile2.mp3"),
+ };
+ for (Uri downloadLocation : downloadPathUris) {
+ request.setDestinationUri(downloadLocation);
+ final long downloadId = mDownloadManager.enqueue(request);
+ receiver.waitForDownloadComplete(SHORT_TIMEOUT, downloadId);
+ assertSuccessfulDownload(downloadId, new File(downloadLocation.getPath()));
+ final Uri downloadUri = mDownloadManager.getUriForDownloadedFile(downloadId);
+ mContext.grantUriPermission("com.android.shell", downloadUri,
+ Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ final Uri mediaStoreUri = getMediaStoreUri(downloadUri);
+ final ContentResolver contentResolver = mContext.getContentResolver();
+ assertArrayEquals(hash(contentResolver.openInputStream(downloadUri)),
+ hash(contentResolver.openInputStream(mediaStoreUri)));
+
+ // Delete entry in DownloadProvider and verify it's deleted from MediaProvider
+ // as well.
+ assertRemoveDownload(downloadId, 0);
+ try (Cursor cursor = mContext.getContentResolver().query(
+ mediaStoreUri, null, null, null)) {
+ assertEquals(0, cursor.getCount());
+ }
+ }
+ } finally {
+ mContext.unregisterReceiver(receiver);
+ }
+ }
+
+ /**
+ * Add a file using DownloadManager.addCompleted and verify that the file has been added
+ * to MediaStore as well.
+ */
+ @Test
+ public void testAddCompletedDownload_mediaStoreEntry() throws Exception {
+ final String[] downloadPath = new String[] {
+ new File(Environment.getExternalStoragePublicDirectory(
+ Environment.DIRECTORY_DOWNLOADS), "file1.mp3").getPath(),
+ new File(Environment.getExternalStoragePublicDirectory(
+ Environment.DIRECTORY_MUSIC), "file2.mp3").getPath(),
+ "/sdcard/file3.mp3",
+ };
+ for (String downloadLocation : downloadPath) {
+ final String fileContents = downloadLocation + "_" + System.nanoTime();
+ final File file = new File(Uri.parse(downloadLocation).getPath());
+ writeToFile(file, fileContents);
+
+ final long downloadId = mDownloadManager.addCompletedDownload("Test title", "Test desc",
+ true, "text/plain", downloadLocation, fileContents.getBytes().length, true);
+ assertTrue(downloadId >= 0);
+ final Uri downloadUri = mDownloadManager.getUriForDownloadedFile(downloadId);
+ mContext.grantUriPermission("com.android.shell", downloadUri,
+ Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ final Uri mediaStoreUri = getMediaStoreUri(downloadUri);
+ assertArrayEquals(hash(new FileInputStream(file)),
+ hash(mContext.getContentResolver().openInputStream(mediaStoreUri)));
+
+ // Delete entry in DownloadProvider and verify it's deleted from MediaProvider as well.
+ assertRemoveDownload(downloadId, 0);
+ try (Cursor cursor = mContext.getContentResolver().query(
+ mediaStoreUri, null, null, null)) {
+ assertEquals(0, cursor.getCount());
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/app/DownloadManagerLegacyTest/Android.mk b/tests/app/DownloadManagerLegacyTest/Android.mk
new file mode 100644
index 0000000..8f1bcba
--- /dev/null
+++ b/tests/app/DownloadManagerLegacyTest/Android.mk
@@ -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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+# don't include this package in any target
+LOCAL_MODULE_TAGS := optional
+# and when built explicitly put it in the data partition
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_JAVA_LIBRARIES := \
+ android.test.runner.stubs \
+ org.apache.http.legacy \
+ android.test.base.stubs
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ compatibility-device-util-axt \
+ ctstestrunner-axt \
+ ctstestserver \
+ mockito-target-minus-junit4 \
+ androidx.test.rules \
+ platform-test-annotations \
+ androidx.test.rules \
+
+LOCAL_SRC_FILES := \
+ $(call all-java-files-under, src) \
+ ../src/android/app/cts/DownloadManagerTestBase.java
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/../app/res
+
+LOCAL_ASSET_DIR := $(LOCAL_PATH)/../app/assets
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+LOCAL_PACKAGE_NAME := CtsDownloadManagerLegacy
+
+LOCAL_SDK_VERSION := test_current
+LOCAL_MIN_SDK_VERSION := 11
+
+include $(BUILD_CTS_PACKAGE)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
\ No newline at end of file
diff --git a/tests/app/DownloadManagerLegacyTest/AndroidManifest.xml b/tests/app/DownloadManagerLegacyTest/AndroidManifest.xml
new file mode 100644
index 0000000..eae3b6a
--- /dev/null
+++ b/tests/app/DownloadManagerLegacyTest/AndroidManifest.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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.app.cts.downloads.legacy">
+
+ <uses-sdk android:minSdkVersion="11" />
+
+ <uses-permission android:name="android.permission.INTERNET" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+
+ <application android:usesCleartextTraffic="true"
+ android:networkSecurityConfig="@xml/network_security_config"
+ android:requestLegacyExternalStorage="true">
+ <uses-library android:name="android.test.runner" />
+ <uses-library android:name="org.apache.http.legacy" android:required="false" />
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.app.cts.downloads.legacy" />
+
+</manifest>
diff --git a/tests/framework/base/windowmanager/dummyTests/dummySdk28/AndroidTest.xml b/tests/app/DownloadManagerLegacyTest/AndroidTest.xml
similarity index 64%
rename from tests/framework/base/windowmanager/dummyTests/dummySdk28/AndroidTest.xml
rename to tests/app/DownloadManagerLegacyTest/AndroidTest.xml
index 0c3cc4a..d225952 100644
--- a/tests/framework/base/windowmanager/dummyTests/dummySdk28/AndroidTest.xml
+++ b/tests/app/DownloadManagerLegacyTest/AndroidTest.xml
@@ -13,22 +13,20 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
-<!-- TODO(b/129909356): Remove this once CtsActivityManagerDeviceSdk28TestCases is renamed to
- CtsWindowManagerSdk28TestCases -->
-<configuration description="Config for CTS WindowManager dummy SDK 28 compatibility test cases">
+<configuration description="Config for DownloadManagerLegacyTest">
<option name="test-suite-tag" value="cts" />
<option name="config-descriptor:metadata" key="component" value="framework" />
- <!-- These tests require targeting API 28 which does not support instant apps -->
- <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
<option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
- <target_preparer class="com.android.compatibility.common.tradefed.targetprep.LocationCheck" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
- <option name="test-file-name" value="CtsActivityManagerDeviceSdk28TestCases.apk" />
+ <option name="test-file-name" value="CtsDownloadManagerLegacy.apk" />
</target_preparer>
- <test class="com.android.tradefed.testtype.AndroidJUnitTest">
- <option name="package" value="android.server.wm.cts.dummy28" />
- <option name="runtime-hint" value="1m" />
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+ <option name="package" value="android.app.cts.downloads.legacy" />
+ <!--TODO: Remove-->
+ <!--<option name="isolated-storage" value="false" />-->
</test>
+
</configuration>
diff --git a/tests/app/DownloadManagerLegacyTest/src/android/app/cts/DownloadManagerLegacyTest.java b/tests/app/DownloadManagerLegacyTest/src/android/app/cts/DownloadManagerLegacyTest.java
new file mode 100644
index 0000000..43efb20
--- /dev/null
+++ b/tests/app/DownloadManagerLegacyTest/src/android/app/cts/DownloadManagerLegacyTest.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.app.cts;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Environment;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.io.FileInputStream;
+
+@RunWith(AndroidJUnit4.class)
+public class DownloadManagerLegacyTest extends DownloadManagerTestBase {
+ @Test
+ public void testAddCompletedDownload() throws Exception {
+ final String[] filePaths = new String[] {
+ createFile(Environment.getExternalStoragePublicDirectory(
+ Environment.DIRECTORY_DOWNLOADS), "file1.txt").getPath(),
+ "/sdcard/Download/file2.txt",
+ };
+
+ for (String path : filePaths) {
+ final String fileContents = path + "_" + System.nanoTime();
+
+ writeToFile(new File(path), fileContents);
+
+ final long id = mDownloadManager.addCompletedDownload("Test title", "Test desc", true,
+ "text/plain", path, fileContents.getBytes().length, true);
+ final String actualContents = readFromFile(mDownloadManager.openDownloadedFile(id));
+ assertEquals(fileContents, actualContents);
+
+ final Uri downloadUri = mDownloadManager.getUriForDownloadedFile(id);
+ mContext.grantUriPermission("com.android.shell", downloadUri,
+ Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ final String rawFilePath = getRawFilePath(downloadUri);
+ final String rawFileContents = readFromRawFile(rawFilePath);
+ assertEquals(fileContents, rawFileContents);
+ assertRemoveDownload(id, 0);
+ }
+ }
+
+ @Test
+ public void testAddCompletedDownload_invalidPublicDir() throws Exception {
+ final File file = new File(
+ Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS),
+ "colors.txt");
+ final String fileContents = "RED;GREEN;BLUE";
+ writeToFileFromShell(file, fileContents);
+ try {
+ mDownloadManager.addCompletedDownload("Test title", "Test desc", true,
+ "text/plain", file.getPath(), fileContents.getBytes().length, true);
+ fail(file + " is not valid for addCompletedDownload()");
+ } catch (Exception e) {
+ // expected
+ }
+ }
+
+ /**
+ * Add a file using DownloadManager.addCompleteDownload
+ * and verify that the file has been added to MediaStore as well.
+ */
+ @Test
+ public void testAddCompletedDownload_mediaStoreEntry() throws Exception {
+ final String[] downloadPath = new String[] {
+ new File(Environment.getExternalStoragePublicDirectory(
+ Environment.DIRECTORY_DOWNLOADS), "file1.mp3").getPath(),
+ "/sdcard/Download/file2.mp3",
+ };
+ for (String downloadLocation : downloadPath) {
+ final String fileContents = downloadLocation + "_" + System.nanoTime();
+ final File file = new File(downloadLocation);
+ writeToFile(file, fileContents);
+
+ final long downloadId = mDownloadManager.addCompletedDownload("Test title", "Test desc",
+ true, "text/plain", downloadLocation, fileContents.getBytes().length, true);
+ assertTrue(downloadId >= 0);
+ final Uri downloadUri = mDownloadManager.getUriForDownloadedFile(downloadId);
+ mContext.grantUriPermission("com.android.shell", downloadUri,
+ Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ final Uri mediaStoreUri = getMediaStoreUri(downloadUri);
+ assertArrayEquals(hash(new FileInputStream(file)),
+ hash(mContext.getContentResolver().openInputStream(mediaStoreUri)));
+
+ // Delete entry in DownloadProvider and verify it's deleted from MediaProvider as well.
+ assertRemoveDownload(downloadId, 0);
+ try (Cursor cursor = mContext.getContentResolver().query(
+ mediaStoreUri, null, null, null)) {
+ assertEquals(0, cursor.getCount());
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/app/TEST_MAPPING b/tests/app/TEST_MAPPING
index 94f8345..60d71d3 100644
--- a/tests/app/TEST_MAPPING
+++ b/tests/app/TEST_MAPPING
@@ -4,6 +4,9 @@
"name": "CtsAppTestCases",
"options": [
{
+ "exclude-annotation": "androidx.test.filters.LargeTest"
+ },
+ {
"exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
diff --git a/tests/app/src/android/app/cts/AlarmManagerTest.java b/tests/app/src/android/app/cts/AlarmManagerTest.java
index cf3662c..559bb8a 100644
--- a/tests/app/src/android/app/cts/AlarmManagerTest.java
+++ b/tests/app/src/android/app/cts/AlarmManagerTest.java
@@ -31,7 +31,7 @@
import android.test.MoreAsserts;
import android.util.Log;
-import androidx.test.filters.FlakyTest;
+import androidx.test.filters.LargeTest;
import com.android.compatibility.common.util.PollingCheck;
@@ -228,7 +228,7 @@
}
}
- @FlakyTest
+ @LargeTest
public void testSetRepeating() throws Exception {
mMockAlarmReceiver.setAlarmedFalse();
mWakeupTime = System.currentTimeMillis() + TEST_ALARM_FUTURITY;
diff --git a/tests/app/src/android/app/cts/AlertDialog_BuilderTest.java b/tests/app/src/android/app/cts/AlertDialog_BuilderTest.java
index f0ff28d..fceae14 100644
--- a/tests/app/src/android/app/cts/AlertDialog_BuilderTest.java
+++ b/tests/app/src/android/app/cts/AlertDialog_BuilderTest.java
@@ -16,6 +16,15 @@
package android.app.cts;
+import static androidx.test.internal.runner.junit4.statement.UiThreadStatement.runOnUiThread;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import static org.mockito.Mockito.*;
+
+import android.app.Activity;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.app.Instrumentation;
@@ -30,8 +39,7 @@
import android.content.DialogInterface.OnMultiChoiceClickListener;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
-import android.test.ActivityInstrumentationTestCase2;
-import android.test.suitebuilder.annotation.SmallTest;
+import android.os.SystemClock;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
@@ -40,16 +48,24 @@
import android.widget.Button;
import android.widget.ListAdapter;
import android.widget.ListView;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
import org.mockito.ArgumentCaptor;
import com.android.compatibility.common.util.PollingCheck;
-import static org.mockito.Mockito.*;
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.rule.ActivityTestRule;
@SmallTest
-public class AlertDialog_BuilderTest extends ActivityInstrumentationTestCase2<DialogStubActivity> {
+@RunWith(JUnit4.class)
+public class AlertDialog_BuilderTest {
private Builder mBuilder;
- private Context mContext;
private Instrumentation mInstrumentation;
private final CharSequence mTitle = "title";
private Drawable mDrawable;
@@ -70,33 +86,29 @@
private OnItemSelectedListener mOnItemSelectedListener = mock(OnItemSelectedListener.class);
+ @Rule
+ public ActivityTestRule<DialogStubActivity> mActivityRule =
+ new ActivityTestRule<>(DialogStubActivity.class);
+
+ private Context mContext;
+
private OnMultiChoiceClickListener mOnMultiChoiceClickListener =
mock(OnMultiChoiceClickListener.class);
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mBuilder = null;
- mInstrumentation = getInstrumentation();
- mContext = getActivity();
-
- PollingCheck.waitFor(() -> getActivity().hasWindowFocus());
-
- mButton = null;
- mView = null;
- mListView = null;
- mDialog = null;
- mSelectedItem = null;
+ @Before
+ public void setUp() throws Exception {
+ mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ Activity activity = mActivityRule.getActivity();
+ mContext = activity;
+ PollingCheck.waitFor(activity::hasWindowFocus);
}
- public AlertDialog_BuilderTest() {
- super("android.app.stubs", DialogStubActivity.class);
- }
-
+ @Test
public void testConstructor() {
new AlertDialog.Builder(mContext);
}
+ @Test
public void testConstructorWithThemeId() {
mBuilder = new AlertDialog.Builder(mContext, R.style.DialogTheme_Test);
@@ -109,8 +121,9 @@
assertEquals(20, ta.getInt(0, 0));
}
+ @Test
public void testSetIconWithParamInt() throws Throwable {
- runTestOnUiThread(new Runnable() {
+ runOnUiThread(new Runnable() {
public void run() {
mDrawable = mContext.getResources().getDrawable(android.R.drawable.btn_default);
mBuilder = new AlertDialog.Builder(mContext);
@@ -121,8 +134,9 @@
mInstrumentation.waitForIdleSync();
}
+ @Test
public void testSetIconWithParamDrawable() throws Throwable {
- runTestOnUiThread(new Runnable() {
+ runOnUiThread(new Runnable() {
public void run() {
mDrawable = mContext.getResources().getDrawable(android.R.drawable.btn_default);
mBuilder = new AlertDialog.Builder(mContext);
@@ -133,8 +147,9 @@
mInstrumentation.waitForIdleSync();
}
+ @Test
public void testSetIconAttribute() throws Throwable {
- runTestOnUiThread(new Runnable() {
+ runOnUiThread(new Runnable() {
public void run() {
mDrawable = mContext.getResources().getDrawable(android.R.drawable.btn_default);
mBuilder = new AlertDialog.Builder(mContext);
@@ -145,8 +160,9 @@
mInstrumentation.waitForIdleSync();
}
+ @Test
public void testSetPositiveButtonWithParamInt() throws Throwable {
- runTestOnUiThread(new Runnable() {
+ runOnUiThread(new Runnable() {
public void run() {
mBuilder = new AlertDialog.Builder(mContext);
mBuilder.setPositiveButton(android.R.string.yes, mOnClickListener);
@@ -166,8 +182,9 @@
verifyNoMoreInteractions(mOnDismissListener);
}
+ @Test
public void testSetPositiveButtonWithParamCharSequence() throws Throwable {
- runTestOnUiThread(new Runnable() {
+ runOnUiThread(new Runnable() {
public void run() {
mBuilder = new AlertDialog.Builder(mContext);
mBuilder.setPositiveButton(android.R.string.yes, mOnClickListener);
@@ -186,8 +203,9 @@
verifyNoMoreInteractions(mOnDismissListener);
}
+ @Test
public void testSetNegativeButtonWithParamCharSequence() throws Throwable {
- runTestOnUiThread(new Runnable() {
+ runOnUiThread(new Runnable() {
public void run() {
mBuilder = new AlertDialog.Builder(mContext);
mBuilder.setNegativeButton(mTitle, mOnClickListener);
@@ -206,8 +224,9 @@
verifyNoMoreInteractions(mOnDismissListener);
}
+ @Test
public void testSetNegativeButtonWithParamInt() throws Throwable {
- runTestOnUiThread(new Runnable() {
+ runOnUiThread(new Runnable() {
public void run() {
mBuilder = new AlertDialog.Builder(mContext);
mBuilder.setNegativeButton(R.string.notify, mOnClickListener);
@@ -226,8 +245,9 @@
verifyNoMoreInteractions(mOnDismissListener);
}
+ @Test
public void testSetNeutralButtonWithParamInt() throws Throwable {
- runTestOnUiThread(new Runnable() {
+ runOnUiThread(new Runnable() {
public void run() {
mBuilder = new AlertDialog.Builder(mContext);
mBuilder.setNeutralButton(R.string.notify, mOnClickListener);
@@ -246,8 +266,9 @@
verifyNoMoreInteractions(mOnDismissListener);
}
+ @Test
public void testSetNeutralButtonWithParamCharSequence() throws Throwable {
- runTestOnUiThread(new Runnable() {
+ runOnUiThread(new Runnable() {
public void run() {
mBuilder = new AlertDialog.Builder(mContext);
mBuilder.setNeutralButton(mTitle, mOnClickListener);
@@ -267,7 +288,7 @@
}
private void testCancelable(final boolean cancelable) throws Throwable {
- runTestOnUiThread(new Runnable() {
+ runOnUiThread(new Runnable() {
public void run() {
mBuilder = new AlertDialog.Builder(mContext);
mBuilder.setCancelable(cancelable);
@@ -275,13 +296,8 @@
}
});
mInstrumentation.waitForIdleSync();
- new PollingCheck() {
- @Override
- protected boolean check() {
- return mDialog.isShowing();
- }
- }.run();
- mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_BACK);
+ PollingCheck.waitFor(mDialog::isShowing);
+ sendKeySync(KeyEvent.KEYCODE_BACK);
mInstrumentation.waitForIdleSync();
new PollingCheck() {
@Override
@@ -300,16 +316,19 @@
}.run();
}
+ @Test
public void testSetCancelable() throws Throwable {
testCancelable(true);
}
+ @Test
public void testDisableCancelable() throws Throwable {
testCancelable(false);
}
+ @Test
public void testSetOnCancelListener() throws Throwable {
- runTestOnUiThread(new Runnable() {
+ runOnUiThread(new Runnable() {
public void run() {
mBuilder = new AlertDialog.Builder(mContext);
mBuilder.setOnCancelListener(mOnCancelListener);
@@ -322,8 +341,9 @@
verifyNoMoreInteractions(mOnCancelListener);
}
+ @Test
public void testSetOnDismissListener() throws Throwable {
- runTestOnUiThread(new Runnable() {
+ runOnUiThread(new Runnable() {
public void run() {
mBuilder = new AlertDialog.Builder(mContext);
mBuilder.setOnDismissListener(mOnDismissListener);
@@ -336,8 +356,9 @@
verifyNoMoreInteractions(mOnDismissListener);
}
+ @Test
public void testSetOnKeyListener() throws Throwable {
- runTestOnUiThread(new Runnable() {
+ runOnUiThread(new Runnable() {
public void run() {
mBuilder = new AlertDialog.Builder(mContext);
mBuilder.setOnKeyListener(mOnKeyListener);
@@ -345,7 +366,9 @@
}
});
mInstrumentation.waitForIdleSync();
- sendKeys(KeyEvent.KEYCODE_0, KeyEvent.KEYCODE_1);
+ sendKeySync(KeyEvent.KEYCODE_0);
+ sendKeySync(KeyEvent.KEYCODE_1);
+ mInstrumentation.waitForIdleSync();
// Use Mockito captures so that we can verify that each "sent" key code resulted
// in one DOWN event and one UP event.
ArgumentCaptor<KeyEvent> keyEvent0Captor = ArgumentCaptor.forClass(KeyEvent.class);
@@ -361,8 +384,9 @@
assertEquals(KeyEvent.ACTION_UP, keyEvent1Captor.getAllValues().get(1).getAction());
}
+ @Test
public void testSetItemsWithParamInt() throws Throwable {
- runTestOnUiThread(new Runnable() {
+ runOnUiThread(new Runnable() {
public void run() {
mBuilder = new AlertDialog.Builder(mContext);
mBuilder.setItems(R.array.difficultyLevel, mOnClickListener);
@@ -377,11 +401,12 @@
assertEquals(levels[0], mListView.getItemAtPosition(0));
}
+ @Test
public void testSetItemsWithParamCharSequence() throws Throwable {
final CharSequence[] expect = mContext.getResources().getTextArray(
R.array.difficultyLevel);
- runTestOnUiThread(new Runnable() {
+ runOnUiThread(new Runnable() {
public void run() {
mBuilder = new AlertDialog.Builder(mContext);
mBuilder.setItems(expect, mOnClickListener);
@@ -393,9 +418,10 @@
assertEquals(expect[0], mListView.getItemAtPosition(0));
}
+ @Test
public void testSetAdapter() throws Throwable {
final ListAdapter adapter = new AdapterTest();
- runTestOnUiThread(new Runnable() {
+ runOnUiThread(new Runnable() {
public void run() {
mBuilder = new AlertDialog.Builder(mContext);
mBuilder.setAdapter(adapter, mOnClickListener);
@@ -407,12 +433,13 @@
assertEquals(adapter, mListView.getAdapter());
}
+ @Test
public void testSetMultiChoiceItemsWithParamInt() throws Throwable {
final CharSequence[] items = mContext.getResources().getTextArray(
R.array.difficultyLevel);
- runTestOnUiThread(new Runnable() {
+ runOnUiThread(new Runnable() {
public void run() {
mBuilder = new AlertDialog.Builder(mContext);
mBuilder.setMultiChoiceItems(R.array.difficultyLevel, null,
@@ -432,11 +459,12 @@
assertEquals(items[0], mListView.getItemAtPosition(0));
}
+ @Test
public void testSetMultiChoiceItemsWithParamCharSequence() throws Throwable {
final CharSequence[] items = mContext.getResources().getTextArray(
R.array.difficultyLevel);
- runTestOnUiThread(new Runnable() {
+ runOnUiThread(new Runnable() {
public void run() {
mBuilder = new AlertDialog.Builder(mContext);
mBuilder.setMultiChoiceItems(items, null, mOnMultiChoiceClickListener);
@@ -455,11 +483,12 @@
assertEquals(items[0], mListView.getItemAtPosition(0));
}
+ @Test
public void testSetSingleChoiceItemsWithParamInt() throws Throwable {
final CharSequence[] items = mContext.getResources().getTextArray(
R.array.difficultyLevel);
- runTestOnUiThread(new Runnable() {
+ runOnUiThread(new Runnable() {
public void run() {
mBuilder = new AlertDialog.Builder(mContext);
mBuilder.setSingleChoiceItems(R.array.difficultyLevel, 0,
@@ -477,11 +506,12 @@
verifyNoMoreInteractions(mOnClickListener);
}
+ @Test
public void testSetSingleChoiceItemsWithParamCharSequence() throws Throwable {
final CharSequence[] items = mContext.getResources().getTextArray(
R.array.difficultyLevel);
- runTestOnUiThread(new Runnable() {
+ runOnUiThread(new Runnable() {
public void run() {
mBuilder = new AlertDialog.Builder(mContext);
mBuilder.setSingleChoiceItems(items, 0, mOnClickListener);
@@ -498,11 +528,12 @@
verifyNoMoreInteractions(mOnClickListener);
}
+ @Test
public void testSetSingleChoiceItems() throws Throwable {
final CharSequence[] items = mContext.getResources().getTextArray(
R.array.difficultyLevel);
- runTestOnUiThread(new Runnable() {
+ runOnUiThread(new Runnable() {
public void run() {
mBuilder = new AlertDialog.Builder(mContext);
mBuilder.setSingleChoiceItems(new ArrayAdapter<CharSequence>(mContext,
@@ -521,8 +552,9 @@
verifyNoMoreInteractions(mOnClickListener);
}
+ @Test
public void testSetOnItemSelectedListener() throws Throwable {
- runTestOnUiThread(new Runnable() {
+ runOnUiThread(new Runnable() {
public void run() {
mBuilder = new AlertDialog.Builder(mContext);
mBuilder.setOnItemSelectedListener(mOnItemSelectedListener);
@@ -538,10 +570,11 @@
verifyNoMoreInteractions(mOnItemSelectedListener);
}
+ @Test
public void testSetView() throws Throwable {
final View view = new View(mContext);
view.setId(100);
- runTestOnUiThread(new Runnable() {
+ runOnUiThread(new Runnable() {
public void run() {
mBuilder = new AlertDialog.Builder(mContext);
mBuilder.setView(view);
@@ -553,8 +586,9 @@
assertEquals(view, mView);
}
+ @Test
public void testSetViewFromInflater() throws Throwable {
- runTestOnUiThread(new Runnable() {
+ runOnUiThread(new Runnable() {
public void run() {
mBuilder = new AlertDialog.Builder(mContext);
mBuilder.setView(LayoutInflater.from(mBuilder.getContext()).inflate(
@@ -569,8 +603,9 @@
assertNotNull(mView.findViewById(R.id.username_edit));
}
+ @Test
public void testSetViewById() throws Throwable {
- runTestOnUiThread(new Runnable() {
+ runOnUiThread(new Runnable() {
public void run() {
mBuilder = new AlertDialog.Builder(mContext);
mBuilder.setView(R.layout.alert_dialog_text_entry_2);
@@ -584,8 +619,9 @@
assertNotNull(mView.findViewById(R.id.username_edit));
}
+ @Test
public void testSetCustomTitle() throws Throwable {
- runTestOnUiThread(new Runnable() {
+ runOnUiThread(new Runnable() {
public void run() {
mBuilder = new AlertDialog.Builder(mContext);
mBuilder.setCustomTitle(LayoutInflater.from(mBuilder.getContext()).inflate(
@@ -596,8 +632,9 @@
mInstrumentation.waitForIdleSync();
}
+ @Test
public void testSetInverseBackgroundForced() throws Throwable {
- runTestOnUiThread(new Runnable() {
+ runOnUiThread(new Runnable() {
public void run() {
mBuilder = new AlertDialog.Builder(mContext);
mBuilder.setInverseBackgroundForced(true);
@@ -608,8 +645,9 @@
mInstrumentation.waitForIdleSync();
}
+ @Test
public void testCreate() throws Throwable {
- runTestOnUiThread(new Runnable() {
+ runOnUiThread(new Runnable() {
public void run() {
mBuilder = new AlertDialog.Builder(mContext);
mDialog = mBuilder.create();
@@ -621,8 +659,9 @@
assertTrue(mDialog.isShowing());
}
+ @Test
public void testShow() throws Throwable {
- runTestOnUiThread(new Runnable() {
+ runOnUiThread(new Runnable() {
public void run() {
mBuilder = new AlertDialog.Builder(mContext);
mDialog = mBuilder.show();
@@ -632,6 +671,17 @@
assertTrue(mDialog.isShowing());
}
+ private void sendKeySync(int keyCode) {
+ final long downTime = SystemClock.uptimeMillis();
+ final KeyEvent downEvent =
+ new KeyEvent(downTime, downTime, KeyEvent.ACTION_DOWN, keyCode, 0);
+ mInstrumentation.getUiAutomation().injectInputEvent(downEvent, true /*sync*/);
+
+ final KeyEvent upEvent =
+ new KeyEvent(downTime, SystemClock.uptimeMillis(), KeyEvent.ACTION_UP, keyCode, 0);
+ mInstrumentation.getUiAutomation().injectInputEvent(upEvent, true /*sync*/);
+ }
+
private static class AdapterTest implements android.widget.ListAdapter {
public boolean areAllItemsEnabled() {
return true;
diff --git a/tests/app/src/android/app/cts/DownloadManagerTest.java b/tests/app/src/android/app/cts/DownloadManagerTest.java
index eaf3876..ced5ed3 100644
--- a/tests/app/src/android/app/cts/DownloadManagerTest.java
+++ b/tests/app/src/android/app/cts/DownloadManagerTest.java
@@ -15,8 +15,6 @@
*/
package android.app.cts;
-import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
-
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -27,10 +25,7 @@
import android.app.DownloadManager;
import android.app.DownloadManager.Query;
import android.app.DownloadManager.Request;
-import android.content.BroadcastReceiver;
import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
@@ -40,72 +35,23 @@
import android.database.Cursor;
import android.net.Uri;
import android.os.Environment;
-import android.os.FileUtils;
import android.os.ParcelFileDescriptor;
-import android.os.SystemClock;
-import android.provider.MediaStore;
-import android.text.TextUtils;
-import android.text.format.DateUtils;
import android.util.Log;
-import android.webkit.cts.CtsTestServer;
import androidx.test.filters.FlakyTest;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import com.android.compatibility.common.util.CddTest;
-import com.android.compatibility.common.util.PollingCheck;
-import org.junit.After;
-import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStream;
-import java.io.PrintWriter;
-import java.nio.charset.StandardCharsets;
-import java.security.DigestInputStream;
-import java.security.MessageDigest;
-import java.util.Arrays;
-import java.util.HashSet;
@RunWith(AndroidJUnit4.class)
-public class DownloadManagerTest {
- private static final String TAG = "DownloadManagerTest";
-
- /**
- * According to the CDD Section 7.6.1, the DownloadManager implementation must be able to
- * download individual files of 100 MB.
- */
- private static final int MINIMUM_DOWNLOAD_BYTES = 100 * 1024 * 1024;
-
- private static final long SHORT_TIMEOUT = 5 * DateUtils.SECOND_IN_MILLIS;
- private static final long LONG_TIMEOUT = 3 * DateUtils.MINUTE_IN_MILLIS;
-
- private Context mContext;
- private DownloadManager mDownloadManager;
-
- private CtsTestServer mWebServer;
-
- @Before
- public void setUp() throws Exception {
- mContext = InstrumentationRegistry.getTargetContext();
- mDownloadManager = (DownloadManager) mContext.getSystemService(Context.DOWNLOAD_SERVICE);
- mWebServer = new CtsTestServer(mContext);
- clearDownloads();
- }
-
- @After
- public void tearDown() throws Exception {
- mWebServer.shutdown();
- }
+public class DownloadManagerTest extends DownloadManagerTestBase {
@FlakyTest
@Test
@@ -368,6 +314,129 @@
}
}
+ @Test
+ public void testSetDestinationUri() throws Exception {
+ final File documentsFile = new File(
+ Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS),
+ "uriFile.bin");
+ if (documentsFile.exists()) {
+ assertTrue(documentsFile.delete());
+ }
+
+ final Request badRequest = new Request(getGoodUrl());
+ badRequest.setDestinationUri(Uri.fromFile(documentsFile));
+ try {
+ mDownloadManager.enqueue(badRequest);
+ fail(documentsFile + " is not valid for setDestinationUri()");
+ } catch (Exception e) {
+ // Expected
+ }
+
+ final Uri badUri = Uri.parse("file:///sdcard/Android/data/"
+ + mContext.getPackageName() + "/../uriFile.bin");
+ final Request badRequest2 = new Request(getGoodUrl());
+ badRequest2.setDestinationUri(badUri);
+ try {
+ mDownloadManager.enqueue(badRequest2);
+ fail(badUri + " is not valid for setDestinationUri()");
+ } catch (Exception e) {
+ // Expected
+ }
+
+ final File sdcardPath = new File("/sdcard/uriFile.bin");
+ final Request badRequest3 = new Request(getGoodUrl());
+ badRequest3.setDestinationUri(Uri.fromFile(sdcardPath));
+ try {
+ mDownloadManager.enqueue(badRequest2);
+ fail(sdcardPath + " is not valid for setDestinationUri()");
+ } catch (Exception e) {
+ // Expected
+ }
+
+ File downloadsFile = new File(
+ Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),
+ "uriFile.bin");
+ if (downloadsFile.exists()) {
+ assertTrue(downloadsFile.delete());
+ }
+
+ final DownloadCompleteReceiver receiver = new DownloadCompleteReceiver();
+ try {
+ IntentFilter intentFilter = new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
+ mContext.registerReceiver(receiver, intentFilter);
+
+ DownloadManager.Request request = new DownloadManager.Request(getGoodUrl());
+ request.setDestinationUri(Uri.fromFile(downloadsFile));
+ long id = mDownloadManager.enqueue(request);
+
+ int allDownloads = getTotalNumberDownloads();
+ assertEquals(1, allDownloads);
+
+ receiver.waitForDownloadComplete(SHORT_TIMEOUT, id);
+ assertSuccessfulDownload(id, downloadsFile);
+
+ assertRemoveDownload(id, 0);
+ } finally {
+ mContext.unregisterReceiver(receiver);
+ }
+ }
+
+ @Test
+ public void testSetDestinationInExternalPublicDir() throws Exception {
+ File publicLocation = new File(
+ Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),
+ "testing/publicFile.bin");
+ if (publicLocation.exists()) {
+ assertTrue(publicLocation.delete());
+ }
+
+ final DownloadCompleteReceiver receiver = new DownloadCompleteReceiver();
+ try {
+ IntentFilter intentFilter = new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
+ mContext.registerReceiver(receiver, intentFilter);
+
+ DownloadManager.Request requestPublic = new DownloadManager.Request(getGoodUrl());
+ requestPublic.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS,
+ "testing/publicFile.bin");
+ long id = mDownloadManager.enqueue(requestPublic);
+
+ int allDownloads = getTotalNumberDownloads();
+ assertEquals(1, allDownloads);
+
+ receiver.waitForDownloadComplete(SHORT_TIMEOUT, id);
+ assertSuccessfulDownload(id, publicLocation);
+
+ assertRemoveDownload(id, 0);
+ } finally {
+ mContext.unregisterReceiver(receiver);
+ }
+ }
+
+ @Test
+ public void testSetDestinationInExternalPublicDir_invalidRequests() {
+ final Request badRequest = new Request(getGoodUrl());
+ try {
+ badRequest.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOCUMENTS,
+ "uriFile.bin");
+ mDownloadManager.enqueue(badRequest);
+ fail(new File(Environment.getExternalStoragePublicDirectory(
+ Environment.DIRECTORY_DOCUMENTS), "uriFile.bin")
+ + " is not valid for setDestinationInExternalPublicDir()");
+ } catch (Exception e) {
+ // Expected
+ }
+
+ final Request badRequest2 = new Request(getGoodUrl());
+ try {
+ badRequest2.setDestinationInExternalPublicDir("TestDir", "uriFile.bin");
+ mDownloadManager.enqueue(badRequest2);
+ fail("/sdcard/TestDir/uriFile.bin"
+ + " is not valid for setDestinationInExternalPublicDir()");
+ } catch (Exception e) {
+ // Expected
+ }
+ }
+
private String cannonicalizeProcessName(ApplicationInfo ai) {
return cannonicalizeProcessName(ai.processName, ai);
}
@@ -442,8 +511,8 @@
@Test
public void testAddCompletedDownload() throws Exception {
final String fileContents = "RED;GREEN;BLUE";
- final File file = createFile(Environment.getExternalStoragePublicDirectory(
- Environment.DIRECTORY_DOCUMENTS), "colors.txt");
+ final File file = createFile(mContext.getExternalFilesDir(null), "colors.txt");
+
writeToFile(file, fileContents);
final long id = mDownloadManager.addCompletedDownload("Test title", "Test desc", true,
@@ -463,10 +532,10 @@
@Test
public void testAddCompletedDownload_invalidPaths() throws Exception {
final String fileContents = "RED;GREEN;BLUE";
- final File file = createFile(mContext.getFilesDir(), "colors.txt");
- writeToFile(file, fileContents);
// Try adding internal path
+ File file = createFile(mContext.getFilesDir(), "colors.txt");
+ writeToFile(file, fileContents);
try {
mDownloadManager.addCompletedDownload("Test title", "Test desc", true,
"text/plain", file.getPath(), fileContents.getBytes().length, true);
@@ -475,12 +544,47 @@
// expected
}
- // Try adding non-existent path
+ // Try adding path in top-level download dir
+ file = new File(
+ Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),
+ "colors.txt");
+ writeToFileFromShell(file, fileContents);
try {
mDownloadManager.addCompletedDownload("Test title", "Test desc", true,
- "text/plain", new File(mContext.getFilesDir(), "test_file.mp4").getPath(),
+ "text/plain", file.getPath(), fileContents.getBytes().length, true);
+ fail("addCompletedDownload should have failed for top-level download dir");
+ } catch (Exception e) {
+ // expected
+ }
+
+ // Try adding top-level sdcard path
+ final String path = "/sdcard/test-download.txt";
+ writeToFileFromShell(new File(path), fileContents);
+ try {
+ mDownloadManager.addCompletedDownload("Test title", "Test desc", true,
+ "text/plain", path, fileContents.getBytes().length, true);
+ fail("addCompletedDownload should have failed for top-level sdcard path");
+ } catch (Exception e) {
+ // expected
+ }
+
+
+ final String path2 = "/sdcard/Android/data/" + mContext.getPackageName()
+ + "/../uriFile.bin";
+ try {
+ mDownloadManager.addCompletedDownload("Test title", "Test desc", true,
+ "text/plain", path2, fileContents.getBytes().length, true);
+ fail(path2 + " is not valid for addCompleteDownload()");
+ } catch (Exception e) {
+ // Expected
+ }
+
+ // Try adding non-existent path
+ try {
+ mDownloadManager.addCompletedDownload("Test title", "Test desc", true, "text/plain",
+ new File(mContext.getExternalFilesDir(null), "test_file.mp4").getPath(),
fileContents.getBytes().length, true);
- fail("addCompletedDownload should have failed for adding internal path");
+ fail("addCompletedDownload should have failed for adding non-existent path");
} catch (Exception e) {
// expected
}
@@ -495,27 +599,6 @@
}
}
- @FlakyTest
- @Test
- public void testAddCompletedDownload_sdcardPath() throws Exception {
- final String fileContents = "RED;GREEN;BLUE";
- final File file = new File("/sdcard", "colors.txt");
- writeToFile(file, fileContents);
-
- final long id = mDownloadManager.addCompletedDownload("Test title", "Test desc", true,
- "text/plain", file.getPath(), fileContents.getBytes().length, true);
- final String actualContents = readFromFile(mDownloadManager.openDownloadedFile(id));
- assertEquals(fileContents, actualContents);
-
- final Uri downloadUri = mDownloadManager.getUriForDownloadedFile(id);
- mContext.grantUriPermission("com.android.shell", downloadUri,
- Intent.FLAG_GRANT_READ_URI_PERMISSION);
- final String rawFilePath = getRawFilePath(downloadUri);
- final String rawFileContents = readFromRawFile(rawFilePath);
- assertEquals(fileContents, rawFileContents);
- assertRemoveDownload(id, 0);
- }
-
/**
* Download a file using DownloadManager and verify that the file has been added
* to MediaStore as well.
@@ -532,7 +615,7 @@
final Request request = new Request(getAssetUrl(fileName));
final File[] downloadLocations = new File[] {
new File(mContext.getExternalFilesDir(null), "file1.mp3"),
- new File("/sdcard", "file2.mp3"),
+ new File(mContext.getExternalCacheDir(), "file2.mp3"),
};
for (File downloadLocation : downloadLocations) {
request.setDestinationUri(Uri.fromFile(downloadLocation));
@@ -547,7 +630,12 @@
assertArrayEquals(hash(contentResolver.openInputStream(downloadUri)),
hash(contentResolver.openInputStream(mediaStoreUri)));
+ // Delete entry in DownloadProvider and verify it's deleted from MediaProvider as well.
assertRemoveDownload(downloadId, 0);
+ try (Cursor cursor = mContext.getContentResolver().query(
+ mediaStoreUri, null, null, null)) {
+ assertEquals(0, cursor.getCount());
+ }
}
} finally {
mContext.unregisterReceiver(receiver);
@@ -561,18 +649,18 @@
@FlakyTest
@Test
public void testAddCompletedDownload_mediaStoreEntry() throws Exception {
- final File[] files = new File[] {
- createFile(Environment.getExternalStoragePublicDirectory(
- Environment.DIRECTORY_DOCUMENTS), "file1.txt"),
- createFile(new File("/sdcard"), "file2.txt"),
+ final String[] filePaths = new String[] {
+ new File(mContext.getExternalFilesDir(null), "file1.txt").getPath(),
+ "/sdcard/Android/data/" + mContext.getPackageName() + "/file2.txt",
};
- for (File file : files) {
- final String fileContents = file.getName() + "_" + System.nanoTime();
+ for (String path : filePaths) {
+ final String fileContents = path + "_" + System.nanoTime();
+ final File file = new File(path);
writeToFile(file, fileContents);
final long downloadId = mDownloadManager.addCompletedDownload("Test title", "Test desc",
- true, "text/plain", file.getPath(), fileContents.getBytes().length, true);
+ true, "text/plain", path, fileContents.getBytes().length, true);
assertTrue(downloadId >= 0);
final Uri downloadUri = mDownloadManager.getUriForDownloadedFile(downloadId);
mContext.grantUriPermission("com.android.shell", downloadUri,
@@ -580,341 +668,13 @@
final Uri mediaStoreUri = getMediaStoreUri(downloadUri);
assertArrayEquals(hash(new FileInputStream(file)),
hash(mContext.getContentResolver().openInputStream(mediaStoreUri)));
+
+ // Delete entry in DownloadProvider and verify it's deleted from MediaProvider as well.
assertRemoveDownload(downloadId, 0);
- }
- }
-
- /**
- * Add a file to DownloadProvider using DownloadManager.addCompletedDownload and verify
- * updates to this entry in DownlaodProvider are reflected in MediaProvider as well.
- */
- @FlakyTest
- @Test
- public void testDownloadManagerUpdates() throws Exception {
- final File dataDir = mContext.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS);
- dataDir.mkdir();
- final File downloadFile = new File(dataDir, "colors.txt");
- downloadFile.createNewFile();
- final String fileContents = "RED;GREEN;BLUE";
- try (final PrintWriter pw = new PrintWriter(downloadFile)) {
- pw.print(fileContents);
- }
-
- // Insert into DownloadProvider and verify it's added to MediaProvider as well
- final String testTitle = "Test title";
- final long downloadId = mDownloadManager.addCompletedDownload(testTitle, "Test desc", true,
- "text/plain", downloadFile.getPath(), fileContents.getBytes().length, true);
- assertTrue(downloadId >= 0);
- final Uri downloadUri = mDownloadManager.getUriForDownloadedFile(downloadId);
- mContext.grantUriPermission("com.android.shell", downloadUri,
- Intent.FLAG_GRANT_READ_URI_PERMISSION);
- final Uri mediaStoreUri = getMediaStoreUri(downloadUri);
- assertArrayEquals(hash(new FileInputStream(downloadFile)),
- hash(mContext.getContentResolver().openInputStream(mediaStoreUri)));
- try (Cursor cursor = mContext.getContentResolver().query(mediaStoreUri,
- new String[] { MediaStore.DownloadColumns.DISPLAY_NAME }, null, null)) {
- cursor.moveToNext();
- assertEquals(testTitle, cursor.getString(0));
- }
-
- // Update title in DownloadProvider and verify the change took effect in MediaProvider
- // as well.
- final String newTitle = "New title";
- final ContentValues updateValues = new ContentValues();
- updateValues.put(DownloadManager.COLUMN_TITLE, newTitle);
- assertEquals(1, mContext.getContentResolver().update(
- downloadUri, updateValues, null, null));
- try (Cursor cursor = mContext.getContentResolver().query(mediaStoreUri,
- new String[] { MediaStore.DownloadColumns.DISPLAY_NAME }, null, null)) {
- cursor.moveToNext();
- assertEquals(newTitle, cursor.getString(0));
- }
-
- // Delete entry in DownloadProvider and verify it's deleted from MediaProvider as well.
- assertRemoveDownload(downloadId, 0);
- try (Cursor cursor = mContext.getContentResolver().query(
- mediaStoreUri, null, null, null)) {
- assertEquals(0, cursor.getCount());
- }
- }
-
- @FlakyTest
- @Test
- public void testDownloadManager_mediaStoreUpdates() throws Exception {
- final File dataDir = mContext.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS);
- dataDir.mkdir();
- final File downloadFile = new File(dataDir, "colors.txt");
- downloadFile.createNewFile();
- final String fileContents = "RED;GREEN;BLUE";
- try (final PrintWriter pw = new PrintWriter(downloadFile)) {
- pw.print(fileContents);
- }
-
- // Insert into DownloadProvider and verify it's added to MediaProvider as well
- final String testTitle = "Test_title";
- final long downloadId = mDownloadManager.addCompletedDownload(testTitle, "Test desc", true,
- "text/plain", downloadFile.getPath(), fileContents.getBytes().length, true);
- assertTrue(downloadId >= 0);
- final Uri downloadUri = mDownloadManager.getUriForDownloadedFile(downloadId);
- try (Cursor cursor = mContext.getContentResolver().query(downloadUri,
- new String[] { DownloadManager.COLUMN_TITLE }, null, null)) {
- cursor.moveToNext();
- assertEquals(testTitle, cursor.getString(0));
- }
-
- mContext.grantUriPermission("com.android.shell", downloadUri,
- Intent.FLAG_GRANT_READ_URI_PERMISSION);
- final Uri mediaStoreUri = getMediaStoreUri(downloadUri);
- final String newTitle = "New_title";
- updateUri(mediaStoreUri, "_display_name", newTitle);
- try (Cursor cursor = mContext.getContentResolver().query(downloadUri,
- new String[] { DownloadManager.COLUMN_TITLE }, null, null)) {
- cursor.moveToNext();
- assertEquals(newTitle, cursor.getString(0));
- }
-
- assertRemoveDownload(downloadId, 0);
- }
-
- private void updateUri(Uri uri, String column, String value) throws Exception {
- final String cmd = String.format("content update --uri %s --bind %s:s:%s",
- uri, column, value);
- final String res = runShellCommand(cmd).trim();
- assertTrue(res, TextUtils.isEmpty(res));
- }
-
- private static byte[] hash(InputStream in) throws Exception {
- try (DigestInputStream digestIn = new DigestInputStream(in,
- MessageDigest.getInstance("SHA-1"));
- OutputStream out = new FileOutputStream(new File("/dev/null"))) {
- FileUtils.copy(digestIn, out);
- return digestIn.getMessageDigest().digest();
- } finally {
- FileUtils.closeQuietly(in);
- }
- }
-
- private Uri getMediaStoreUri(Uri downloadUri) throws Exception {
- final String cmd = String.format("content query --uri %s --projection %s",
- downloadUri, DownloadManager.COLUMN_MEDIASTORE_URI);
- final String res = runShellCommand(cmd).trim();
- final String str = DownloadManager.COLUMN_MEDIASTORE_URI + "=";
- final int i = res.indexOf(str);
- if (i >= 0) {
- return Uri.parse(res.substring(i + str.length()));
- } else {
- throw new FileNotFoundException("Failed to find "
- + DownloadManager.COLUMN_MEDIASTORE_URI + " for "
- + downloadUri + "; found " + res);
- }
- }
-
- private static String getRawFilePath(Uri uri) throws Exception {
- final String res = runShellCommand("content query --uri " + uri + " --projection _data");
- final int i = res.indexOf("_data=");
- if (i >= 0) {
- return res.substring(i + 6);
- } else {
- throw new FileNotFoundException("Failed to find _data for " + uri + "; found " + res);
- }
- }
-
- private static String readFromRawFile(String filePath) throws Exception {
- Log.d(TAG, "Reading form file: " + filePath);
- return runShellCommand("cat " + filePath);
- }
-
- private static String readFromFile(ParcelFileDescriptor pfd) throws Exception {
- BufferedReader br = null;
- try (final InputStream in = new FileInputStream(pfd.getFileDescriptor())) {
- br = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8));
- String str;
- StringBuilder out = new StringBuilder();
- while ((str = br.readLine()) != null) {
- out.append(str);
- }
- return out.toString();
- } finally {
- if (br != null) {
- br.close();
+ try (Cursor cursor = mContext.getContentResolver().query(
+ mediaStoreUri, null, null, null)) {
+ assertEquals(0, cursor.getCount());
}
}
}
-
- private static File createFile(File baseDir, String fileName) {
- if (!baseDir.exists()) {
- baseDir.mkdirs();
- }
- return new File(baseDir, fileName);
- }
-
- private static void writeToFile(File file, String contents) throws Exception {
- try (final PrintWriter out = new PrintWriter(file)) {
- out.print(contents);
- }
- }
-
- private class DownloadCompleteReceiver extends BroadcastReceiver {
- private HashSet<Long> mCompleteIds = new HashSet<>();
-
- public DownloadCompleteReceiver() {
- }
-
- @Override
- public void onReceive(Context context, Intent intent) {
- synchronized (mCompleteIds) {
- mCompleteIds.add(intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1));
- mCompleteIds.notifyAll();
- }
- }
-
- private boolean isCompleteLocked(long... ids) {
- for (long id : ids) {
- if (!mCompleteIds.contains(id)) {
- return false;
- }
- }
- return true;
- }
-
- public void waitForDownloadComplete(long timeoutMillis, long... waitForIds)
- throws InterruptedException {
- if (waitForIds.length == 0) {
- throw new IllegalArgumentException("Missing IDs to wait for");
- }
-
- final long startTime = SystemClock.elapsedRealtime();
- do {
- synchronized (mCompleteIds) {
- mCompleteIds.wait(timeoutMillis);
- if (isCompleteLocked(waitForIds)) return;
- }
- } while ((SystemClock.elapsedRealtime() - startTime) < timeoutMillis);
-
- throw new InterruptedException("Timeout waiting for IDs " + Arrays.toString(waitForIds)
- + "; received " + mCompleteIds.toString()
- + ". Make sure you have WiFi or some other connectivity for this test.");
- }
- }
-
- private void clearDownloads() {
- if (getTotalNumberDownloads() > 0) {
- Cursor cursor = null;
- try {
- Query query = new Query();
- cursor = mDownloadManager.query(query);
- int columnIndex = cursor.getColumnIndex(DownloadManager.COLUMN_ID);
- long[] removeIds = new long[cursor.getCount()];
- for (int i = 0; cursor.moveToNext(); i++) {
- removeIds[i] = cursor.getLong(columnIndex);
- }
- assertEquals(removeIds.length, mDownloadManager.remove(removeIds));
- assertEquals(0, getTotalNumberDownloads());
- } finally {
- if (cursor != null) {
- cursor.close();
- }
- }
- }
- }
-
- private Uri getGoodUrl() {
- return Uri.parse(mWebServer.getTestDownloadUrl("cts-good-download", 0));
- }
-
- private Uri getBadUrl() {
- return Uri.parse(mWebServer.getBaseUri() + "/nosuchurl");
- }
-
- private Uri getMinimumDownloadUrl() {
- return Uri.parse(mWebServer.getTestDownloadUrl("cts-minimum-download",
- MINIMUM_DOWNLOAD_BYTES));
- }
-
- private Uri getAssetUrl(String asset) {
- return Uri.parse(mWebServer.getAssetUrl(asset));
- }
-
- private int getTotalNumberDownloads() {
- Cursor cursor = null;
- try {
- Query query = new Query();
- cursor = mDownloadManager.query(query);
- return cursor.getCount();
- } finally {
- if (cursor != null) {
- cursor.close();
- }
- }
- }
-
- private void assertDownloadQueryableById(long downloadId) {
- Cursor cursor = null;
- try {
- Query query = new Query().setFilterById(downloadId);
- cursor = mDownloadManager.query(query);
- assertEquals(1, cursor.getCount());
- } finally {
- if (cursor != null) {
- cursor.close();
- }
- }
- }
-
- private void assertDownloadQueryableByStatus(final int status) {
- new PollingCheck() {
- @Override
- protected boolean check() {
- Cursor cursor= null;
- try {
- Query query = new Query().setFilterByStatus(status);
- cursor = mDownloadManager.query(query);
- return 1 == cursor.getCount();
- } finally {
- if (cursor != null) {
- cursor.close();
- }
- }
- }
- }.run();
- }
-
- private void assertSuccessfulDownload(long id, File location) throws Exception {
- Cursor cursor = null;
- try {
- final File expectedLocation = location.getCanonicalFile();
- cursor = mDownloadManager.query(new Query().setFilterById(id));
- assertTrue(cursor.moveToNext());
- assertEquals(DownloadManager.STATUS_SUCCESSFUL, cursor.getInt(
- cursor.getColumnIndex(DownloadManager.COLUMN_STATUS)));
- assertEquals(Uri.fromFile(expectedLocation).toString(),
- cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI)));
- assertTrue(expectedLocation.exists());
- } finally {
- if (cursor != null) {
- cursor.close();
- }
- }
- }
-
- private void assertRemoveDownload(long removeId, int expectedNumDownloads) {
- Cursor cursor = null;
- try {
- assertEquals(1, mDownloadManager.remove(removeId));
- Query query = new Query();
- cursor = mDownloadManager.query(query);
- assertEquals(expectedNumDownloads, cursor.getCount());
- } finally {
- if (cursor != null) {
- cursor.close();
- }
- }
- }
-
- private boolean hasInternetConnection() {
- final PackageManager pm = mContext.getPackageManager();
- return pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)
- || pm.hasSystemFeature(PackageManager.FEATURE_WIFI)
- || pm.hasSystemFeature(PackageManager.FEATURE_ETHERNET);
- }
}
diff --git a/tests/app/src/android/app/cts/DownloadManagerTestBase.java b/tests/app/src/android/app/cts/DownloadManagerTestBase.java
new file mode 100644
index 0000000..71eb4e9a
--- /dev/null
+++ b/tests/app/src/android/app/cts/DownloadManagerTestBase.java
@@ -0,0 +1,335 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.app.cts;
+
+import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.app.DownloadManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.FileUtils;
+import android.os.ParcelFileDescriptor;
+import android.os.SystemClock;
+import android.text.TextUtils;
+import android.text.format.DateUtils;
+import android.util.Log;
+import android.webkit.cts.CtsTestServer;
+
+import androidx.test.InstrumentationRegistry;
+
+import com.android.compatibility.common.util.PollingCheck;
+
+import org.junit.After;
+import org.junit.Before;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.nio.charset.StandardCharsets;
+import java.security.DigestInputStream;
+import java.security.MessageDigest;
+import java.util.Arrays;
+import java.util.HashSet;
+
+public class DownloadManagerTestBase {
+ protected static final String TAG = "DownloadManagerTest";
+
+ /**
+ * According to the CDD Section 7.6.1, the DownloadManager implementation must be able to
+ * download individual files of 100 MB.
+ */
+ protected static final int MINIMUM_DOWNLOAD_BYTES = 100 * 1024 * 1024;
+
+ protected static final long SHORT_TIMEOUT = 5 * DateUtils.SECOND_IN_MILLIS;
+ protected static final long LONG_TIMEOUT = 3 * DateUtils.MINUTE_IN_MILLIS;
+
+ protected Context mContext;
+ protected DownloadManager mDownloadManager;
+
+ private CtsTestServer mWebServer;
+
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getTargetContext();
+ mDownloadManager = (DownloadManager) mContext.getSystemService(Context.DOWNLOAD_SERVICE);
+ mWebServer = new CtsTestServer(mContext);
+ clearDownloads();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mWebServer.shutdown();
+ clearDownloads();
+ }
+
+ protected void updateUri(Uri uri, String column, String value) throws Exception {
+ final String cmd = String.format("content update --uri %s --bind %s:s:%s",
+ uri, column, value);
+ final String res = runShellCommand(cmd).trim();
+ assertTrue(res, TextUtils.isEmpty(res));
+ }
+
+ protected static byte[] hash(InputStream in) throws Exception {
+ try (DigestInputStream digestIn = new DigestInputStream(in,
+ MessageDigest.getInstance("SHA-1"));
+ OutputStream out = new FileOutputStream(new File("/dev/null"))) {
+ FileUtils.copy(digestIn, out);
+ return digestIn.getMessageDigest().digest();
+ } finally {
+ FileUtils.closeQuietly(in);
+ }
+ }
+
+ protected Uri getMediaStoreUri(Uri downloadUri) throws Exception {
+ final String cmd = String.format("content query --uri %s --projection %s",
+ downloadUri, DownloadManager.COLUMN_MEDIASTORE_URI);
+ final String res = runShellCommand(cmd).trim();
+ final String str = DownloadManager.COLUMN_MEDIASTORE_URI + "=";
+ final int i = res.indexOf(str);
+ if (i >= 0) {
+ return Uri.parse(res.substring(i + str.length()));
+ } else {
+ throw new FileNotFoundException("Failed to find "
+ + DownloadManager.COLUMN_MEDIASTORE_URI + " for "
+ + downloadUri + "; found " + res);
+ }
+ }
+
+ protected static String getRawFilePath(Uri uri) throws Exception {
+ final String res = runShellCommand("content query --uri " + uri + " --projection _data");
+ final int i = res.indexOf("_data=");
+ if (i >= 0) {
+ return res.substring(i + 6);
+ } else {
+ throw new FileNotFoundException("Failed to find _data for " + uri + "; found " + res);
+ }
+ }
+
+ protected static String readFromRawFile(String filePath) throws Exception {
+ Log.d(TAG, "Reading form file: " + filePath);
+ return runShellCommand("cat " + filePath);
+ }
+
+ protected static String readFromFile(ParcelFileDescriptor pfd) throws Exception {
+ BufferedReader br = null;
+ try (final InputStream in = new FileInputStream(pfd.getFileDescriptor())) {
+ br = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8));
+ String str;
+ StringBuilder out = new StringBuilder();
+ while ((str = br.readLine()) != null) {
+ out.append(str);
+ }
+ return out.toString();
+ } finally {
+ if (br != null) {
+ br.close();
+ }
+ }
+ }
+
+ protected static File createFile(File baseDir, String fileName) {
+ if (!baseDir.exists()) {
+ baseDir.mkdirs();
+ }
+ return new File(baseDir, fileName);
+ }
+
+ protected static void writeToFile(File file, String contents) throws Exception {
+ try (final PrintWriter out = new PrintWriter(file)) {
+ out.print(contents);
+ }
+ }
+
+ protected static void writeToFileFromShell(File file, String contents) throws Exception {
+ final String cmd = "echo \"" + contents + "\" > " + file;
+ final String res = runShellCommand(cmd);
+ Log.d(TAG, "Output of '" + cmd + "': '" + res + "'");
+ }
+
+ protected void clearDownloads() {
+ if (getTotalNumberDownloads() > 0) {
+ Cursor cursor = null;
+ try {
+ DownloadManager.Query query = new DownloadManager.Query();
+ cursor = mDownloadManager.query(query);
+ int columnIndex = cursor.getColumnIndex(DownloadManager.COLUMN_ID);
+ long[] removeIds = new long[cursor.getCount()];
+ for (int i = 0; cursor.moveToNext(); i++) {
+ removeIds[i] = cursor.getLong(columnIndex);
+ }
+ assertEquals(removeIds.length, mDownloadManager.remove(removeIds));
+ assertEquals(0, getTotalNumberDownloads());
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+ }
+ }
+
+ protected Uri getGoodUrl() {
+ return Uri.parse(mWebServer.getTestDownloadUrl("cts-good-download", 0));
+ }
+
+ protected Uri getBadUrl() {
+ return Uri.parse(mWebServer.getBaseUri() + "/nosuchurl");
+ }
+
+ protected Uri getMinimumDownloadUrl() {
+ return Uri.parse(mWebServer.getTestDownloadUrl("cts-minimum-download",
+ MINIMUM_DOWNLOAD_BYTES));
+ }
+
+ protected Uri getAssetUrl(String asset) {
+ return Uri.parse(mWebServer.getAssetUrl(asset));
+ }
+
+ protected int getTotalNumberDownloads() {
+ Cursor cursor = null;
+ try {
+ DownloadManager.Query query = new DownloadManager.Query();
+ cursor = mDownloadManager.query(query);
+ return cursor.getCount();
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+ }
+
+ protected void assertDownloadQueryableById(long downloadId) {
+ Cursor cursor = null;
+ try {
+ DownloadManager.Query query = new DownloadManager.Query().setFilterById(downloadId);
+ cursor = mDownloadManager.query(query);
+ assertEquals(1, cursor.getCount());
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+ }
+
+ protected void assertDownloadQueryableByStatus(final int status) {
+ new PollingCheck() {
+ @Override
+ protected boolean check() {
+ Cursor cursor= null;
+ try {
+ DownloadManager.Query query = new DownloadManager.Query().setFilterByStatus(status);
+ cursor = mDownloadManager.query(query);
+ return 1 == cursor.getCount();
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+ }
+ }.run();
+ }
+
+ protected void assertSuccessfulDownload(long id, File location) throws Exception {
+ Cursor cursor = null;
+ try {
+ final File expectedLocation = location.getCanonicalFile();
+ cursor = mDownloadManager.query(new DownloadManager.Query().setFilterById(id));
+ assertTrue(cursor.moveToNext());
+ assertEquals(DownloadManager.STATUS_SUCCESSFUL, cursor.getInt(
+ cursor.getColumnIndex(DownloadManager.COLUMN_STATUS)));
+ assertEquals(Uri.fromFile(expectedLocation).toString(),
+ cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI)));
+ assertTrue(expectedLocation.exists());
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+ }
+
+ protected void assertRemoveDownload(long removeId, int expectedNumDownloads) {
+ Cursor cursor = null;
+ try {
+ assertEquals(1, mDownloadManager.remove(removeId));
+ DownloadManager.Query query = new DownloadManager.Query();
+ cursor = mDownloadManager.query(query);
+ assertEquals(expectedNumDownloads, cursor.getCount());
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+ }
+
+ protected boolean hasInternetConnection() {
+ final PackageManager pm = mContext.getPackageManager();
+ return pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)
+ || pm.hasSystemFeature(PackageManager.FEATURE_WIFI)
+ || pm.hasSystemFeature(PackageManager.FEATURE_ETHERNET);
+ }
+
+ public static class DownloadCompleteReceiver extends BroadcastReceiver {
+ private HashSet<Long> mCompleteIds = new HashSet<>();
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ synchronized (mCompleteIds) {
+ mCompleteIds.add(intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1));
+ mCompleteIds.notifyAll();
+ }
+ }
+
+ private boolean isCompleteLocked(long... ids) {
+ for (long id : ids) {
+ if (!mCompleteIds.contains(id)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public void waitForDownloadComplete(long timeoutMillis, long... waitForIds)
+ throws InterruptedException {
+ if (waitForIds.length == 0) {
+ throw new IllegalArgumentException("Missing IDs to wait for");
+ }
+
+ final long startTime = SystemClock.elapsedRealtime();
+ do {
+ synchronized (mCompleteIds) {
+ mCompleteIds.wait(timeoutMillis);
+ if (isCompleteLocked(waitForIds)) return;
+ }
+ } while ((SystemClock.elapsedRealtime() - startTime) < timeoutMillis);
+
+ throw new InterruptedException("Timeout waiting for IDs " + Arrays.toString(waitForIds)
+ + "; received " + mCompleteIds.toString()
+ + ". Make sure you have WiFi or some other connectivity for this test.");
+ }
+ }
+}
diff --git a/tests/app/src/android/app/cts/NotificationManagerTest.java b/tests/app/src/android/app/cts/NotificationManagerTest.java
index 8b94334..253d77f 100644
--- a/tests/app/src/android/app/cts/NotificationManagerTest.java
+++ b/tests/app/src/android/app/cts/NotificationManagerTest.java
@@ -47,6 +47,7 @@
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Person;
+import android.app.RemoteInput;
import android.app.UiAutomation;
import android.app.stubs.AutomaticZenRuleActivity;
import android.app.stubs.BubblesTestActivity;
@@ -2390,7 +2391,7 @@
badNumberString);
}
- public void testNotificationManagerBubblePolicy_flagForMessage() {
+ public void testNotificationManagerBubblePolicy_flagForMessage_failsNoRemoteInput() {
Person person = new Person.Builder()
.setName("bubblebot")
.build();
@@ -2404,7 +2405,35 @@
SystemClock.currentThreadTimeMillis(), person)
)
.setSmallIcon(android.R.drawable.sym_def_app_icon);
- sendAndVerifyBubble(1, nb, null /* use default metadata */, true /* shouldBeBubble */);
+ sendAndVerifyBubble(1, nb, null /* use default metadata */, false);
+ }
+
+ public void testNotificationManagerBubblePolicy_flagForMessage_succeeds() {
+ Person person = new Person.Builder()
+ .setName("bubblebot")
+ .build();
+
+ RemoteInput remoteInput = new RemoteInput.Builder("reply_key").setLabel("reply").build();
+ PendingIntent inputIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
+ Icon icon = Icon.createWithResource(mContext, android.R.drawable.sym_def_app_icon);
+ Notification.Action replyAction = new Notification.Action.Builder(icon, "Reply",
+ inputIntent).addRemoteInput(remoteInput)
+ .build();
+
+ Notification.Builder nb = new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
+ .setContentTitle("foo")
+ .setStyle(new Notification.MessagingStyle(person)
+ .setConversationTitle("Bubble Chat")
+ .addMessage("Hello?",
+ SystemClock.currentThreadTimeMillis() - 300000, person)
+ .addMessage("Is it me you're looking for?",
+ SystemClock.currentThreadTimeMillis(), person)
+ )
+ .setActions(replyAction)
+ .setSmallIcon(android.R.drawable.sym_def_app_icon);
+
+ boolean shouldBeBubble = !mActivityManager.isLowRamDevice();
+ sendAndVerifyBubble(1, nb, null /* use default metadata */, shouldBeBubble);
}
public void testNotificationManagerBubblePolicy_flagForPhonecall() {
@@ -2412,8 +2441,9 @@
serviceIntent.putExtra(EXTRA_TEST_CASE, TEST_SUCCESS);
mContext.startService(serviceIntent);
+ boolean shouldBeBubble = !mActivityManager.isLowRamDevice();
if (!checkNotificationExistence(BUBBLE_NOTIF_ID,
- true /* shouldExist */, true /* shouldBeBubble */)) {
+ true /* shouldExist */, shouldBeBubble)) {
fail("couldn't find posted notification bubble with id=" + BUBBLE_NOTIF_ID);
}
}
@@ -2515,8 +2545,10 @@
// The notif should be allowed to update as a bubble
a.sendBubble(2);
+ boolean shouldBeBubble = !mActivityManager.isLowRamDevice();
+
if (!checkNotificationExistence(BUBBLE_NOTIF_ID,
- true /* shouldExist */, true /* shouldBeBubble */)) {
+ true /* shouldExist */, shouldBeBubble)) {
fail("couldn't find posted notification bubble with id=" + BUBBLE_NOTIF_ID);
}
diff --git a/tests/app/src/android/app/cts/ServiceTest.java b/tests/app/src/android/app/cts/ServiceTest.java
index 6bea944..6649346 100644
--- a/tests/app/src/android/app/cts/ServiceTest.java
+++ b/tests/app/src/android/app/cts/ServiceTest.java
@@ -1160,6 +1160,31 @@
}
/**
+ * Verify that certain characters are prohibited in instanceName.
+ */
+ public void testFailBindIsoaltedServiceWithInvalidInstanceName() throws Exception {
+ String[] badNames = {
+ "t\rest",
+ "test\n",
+ "test-three",
+ "test four",
+ "escape\u00a9seq",
+ "\u0164est",
+ };
+ for (String instanceName : badNames) {
+ EmptyConnection conn = new EmptyConnection();
+ try {
+ mContext.bindIsolatedService(mIsolatedService, Context.BIND_AUTO_CREATE,
+ instanceName, mContextMainExecutor, conn);
+ mContext.unbindService(conn);
+ fail("Didn't get IllegalArgumentException: " + instanceName);
+ } catch (IllegalArgumentException e) {
+ // This is expected.
+ }
+ }
+ }
+
+ /**
* Verify that bindIsolatedService() correctly makes different instances when given
* different instance names.
*/
@@ -1534,6 +1559,8 @@
* Test that the system properly orders processes bound by an activity within the
* LRU list.
*/
+ // TODO(b/131059432): Re-enable the test after that bug is fixed.
+ @FlakyTest
@MediumTest
public void testActivityServiceBindingLru() throws Exception {
// Bring up the activity we will hang services off of.
diff --git a/tests/app/src/android/app/cts/SystemFeaturesTest.java b/tests/app/src/android/app/cts/SystemFeaturesTest.java
index afc179e..2257689 100644
--- a/tests/app/src/android/app/cts/SystemFeaturesTest.java
+++ b/tests/app/src/android/app/cts/SystemFeaturesTest.java
@@ -50,6 +50,7 @@
import androidx.test.filters.FlakyTest;
import androidx.test.platform.app.InstrumentationRegistry;
+import com.android.compatibility.common.util.CddTest;
import com.android.compatibility.common.util.PropertyUtil;
import com.android.compatibility.common.util.SystemUtil;
@@ -160,6 +161,7 @@
}
}
+ @CddTest(requirement="7.5.4/C-0-8")
private void checkCamera2Features() throws Exception {
String[] cameraIds = mCameraManager.getCameraIdList();
boolean fullCamera = false;
@@ -167,6 +169,7 @@
boolean manualPostProcessing = false;
boolean motionTracking = false;
boolean raw = false;
+ boolean hasFlash = false;
CameraCharacteristics[] cameraChars = new CameraCharacteristics[cameraIds.length];
for (String cameraId : cameraIds) {
CameraCharacteristics chars = mCameraManager.getCameraCharacteristics(cameraId);
@@ -195,6 +198,11 @@
break;
}
}
+
+ Boolean flashAvailable = chars.get(CameraCharacteristics.FLASH_INFO_AVAILABLE);
+ if (flashAvailable) {
+ hasFlash = true;
+ }
}
assertFeature(fullCamera, PackageManager.FEATURE_CAMERA_LEVEL_FULL);
assertFeature(manualSensor, PackageManager.FEATURE_CAMERA_CAPABILITY_MANUAL_SENSOR);
@@ -215,6 +223,7 @@
// available
assertNotAvailable(PackageManager.FEATURE_CAMERA_AR);
}
+ assertFeature(hasFlash, PackageManager.FEATURE_CAMERA_FLASH);
}
private void checkFrontCamera() {
diff --git a/tests/apppredictionservice/AndroidManifest.xml b/tests/apppredictionservice/AndroidManifest.xml
index fa470fd..8ee464a 100644
--- a/tests/apppredictionservice/AndroidManifest.xml
+++ b/tests/apppredictionservice/AndroidManifest.xml
@@ -18,6 +18,8 @@
package="android.apppredictionservice.cts"
android:targetSandboxVersion="2">
+ <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
+
<application>
<uses-library android:name="android.test.runner" />
diff --git a/tests/apppredictionservice/AndroidTest.xml b/tests/apppredictionservice/AndroidTest.xml
index 660955c..8166a4c 100644
--- a/tests/apppredictionservice/AndroidTest.xml
+++ b/tests/apppredictionservice/AndroidTest.xml
@@ -16,7 +16,8 @@
<configuration description="Config for App Prediction CTS tests.">
<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" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="CtsAppPredictionServiceTestCases.apk" />
diff --git a/tests/apppredictionservice/src/android/apppredictionservice/cts/AppPredictionServiceTest.java b/tests/apppredictionservice/src/android/apppredictionservice/cts/AppPredictionServiceTest.java
index 890cbd4..eae6016 100644
--- a/tests/apppredictionservice/src/android/apppredictionservice/cts/AppPredictionServiceTest.java
+++ b/tests/apppredictionservice/src/android/apppredictionservice/cts/AppPredictionServiceTest.java
@@ -216,6 +216,25 @@
client.destroy();
}
+ @Test
+ public void testFailureWithoutPermission() {
+ setService(null);
+ InstrumentationRegistry.getInstrumentation().getUiAutomation().revokeRuntimePermission(
+ InstrumentationRegistry.getTargetContext().getPackageName(),
+ android.Manifest.permission.PACKAGE_USAGE_STATS);
+ assertFails(() -> createTestPredictor(createTestPredictionContext()));
+ }
+
+ @Test
+ public void testSuccessWithPermission() {
+ setService(null);
+ InstrumentationRegistry.getInstrumentation().getUiAutomation().grantRuntimePermission(
+ InstrumentationRegistry.getTargetContext().getPackageName(),
+ android.Manifest.permission.PACKAGE_USAGE_STATS);
+ AppPredictor predictor = createTestPredictor(createTestPredictionContext());
+ predictor.destroy();
+ }
+
private void assertFails(Runnable r) {
try {
r.run();
diff --git a/tests/tests/permission2/Android.mk b/tests/attentionservice/Android.mk
old mode 100755
new mode 100644
similarity index 64%
rename from tests/tests/permission2/Android.mk
rename to tests/attentionservice/Android.mk
index a058ab8..0d77c35
--- a/tests/tests/permission2/Android.mk
+++ b/tests/attentionservice/Android.mk
@@ -1,5 +1,4 @@
-#
-# Copyright (C) 2009 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,32 +11,33 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-#
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
+# Don't include this package in any target
LOCAL_MODULE_TAGS := tests
+# When built, explicitly put it in the data partition.
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests cts_instant
+LOCAL_DEX_PREOPT := false
-LOCAL_JAVA_LIBRARIES := android.test.base.stubs
+LOCAL_PROGUARD_ENABLED := disabled
LOCAL_STATIC_JAVA_LIBRARIES := \
- androidx.test.core \
- compatibility-device-util-axt \
- ctstestrunner-axt \
- guava
+ androidx.test.rules \
+ androidx.test.ext.junit \
+ compatibility-device-util-axt \
+ platform-test-annotations
LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_PACKAGE_NAME := CtsPermission2TestCases
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts
-LOCAL_SDK_VERSION := test_current
+LOCAL_PACKAGE_NAME := CtsAttentionServiceDeviceTestCases
-include $(BUILD_CTS_PACKAGE)
+LOCAL_SDK_VERSION := system_current
-include $(call all-makefiles-under,$(LOCAL_PATH))
+include $(BUILD_CTS_PACKAGE)
\ No newline at end of file
diff --git a/tests/attentionservice/AndroidManifest.xml b/tests/attentionservice/AndroidManifest.xml
new file mode 100644
index 0000000..22ab937
--- /dev/null
+++ b/tests/attentionservice/AndroidManifest.xml
@@ -0,0 +1,45 @@
+<?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.attentionservice.cts">
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ <service
+ android:name=".CtsTestAttentionService"
+ android:label="CtsTestAttentionService"
+ android:permission="android.permission.BIND_ATTENTION_SERVICE">
+ <intent-filter>
+ <action android:name="android.service.attention.AttentionService"/>
+ </intent-filter>
+ </service>
+ <activity android:name="CtsAttentionServiceDeviceActivity" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+
+ <!-- self-instrumenting test package. -->
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:label="CTS tests for the Attention Service APIs."
+ android:targetPackage="android.attentionservice.cts" >
+ </instrumentation>
+</manifest>
diff --git a/tests/attentionservice/AndroidTest.xml b/tests/attentionservice/AndroidTest.xml
new file mode 100644
index 0000000..e48a590
--- /dev/null
+++ b/tests/attentionservice/AndroidTest.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
+ -->
+
+<configuration description="Config for CtsAttentionServiceDeviceTestCases">
+ <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" />
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="CtsAttentionServiceDeviceTestCases.apk" />
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="android.attentionservice.cts" />
+ </test>
+</configuration>
diff --git a/tests/attentionservice/src/android/attentionservice/cts/CtsAttentionServiceDeviceTest.java b/tests/attentionservice/src/android/attentionservice/cts/CtsAttentionServiceDeviceTest.java
new file mode 100644
index 0000000..5e41cf4
--- /dev/null
+++ b/tests/attentionservice/src/android/attentionservice/cts/CtsAttentionServiceDeviceTest.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.attentionservice.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 android.platform.test.annotations.AppModeFull;
+import android.provider.DeviceConfig;
+import android.service.attention.AttentionService;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.compatibility.common.util.DeviceConfigStateChangerRule;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * This suite of test ensures that AttentionManagerService behaves correctly when properly bound
+ * to an AttentionService implementation
+ */
+@RunWith(AndroidJUnit4.class)
+@AppModeFull(reason = "PM will not recognize CtsTestAttentionService in instantMode.")
+public class CtsAttentionServiceDeviceTest {
+ private static final String SERVICE_ENABLED = "service_enabled";
+ private static final String FAKE_SERVICE_PACKAGE =
+ CtsTestAttentionService.class.getPackage().getName();
+
+ @Rule
+ public final DeviceConfigStateChangerRule mLookAllTheseRules =
+ new DeviceConfigStateChangerRule(getInstrumentation().getTargetContext(),
+ DeviceConfig.NAMESPACE_ATTENTION_MANAGER_SERVICE,
+ SERVICE_ENABLED,
+ "true");
+
+ @Before
+ public void setUp() {
+ clearTestableAttentionService();
+ CtsTestAttentionService.reset();
+ bindToTestService();
+ }
+
+ @After
+ public void tearDown() {
+ clearTestableAttentionService();
+ }
+
+ @Test
+ public void testAttentionService_OnSuccess() {
+ /** From manager, call CheckAttention() on test service */
+ assertThat(CtsTestAttentionService.hasPendingChecks()).isFalse();
+ callCheckAttention();
+
+ /** From test service, verify that onCheckAttention was called */
+ assertThat(CtsTestAttentionService.hasPendingChecks()).isTrue();
+
+ /** From test service, respond with onSuccess */
+ CtsTestAttentionService.respondSuccess(
+ AttentionService.ATTENTION_SUCCESS_PRESENT);
+
+ /** From manager, verify onSuccess callback was received*/
+ assertThat(getLastTestCallbackCode()).isEqualTo(AttentionService.ATTENTION_SUCCESS_PRESENT);
+ }
+
+ @Test
+ public void testAttentionService_OnCancelledFromManager() {
+ /** From manager, call CheckAttention() on test service */
+ assertThat(CtsTestAttentionService.hasPendingChecks()).isFalse();
+ callCheckAttention();
+
+ /** From test service, verify that onCheckAttention was called */
+ assertThat(CtsTestAttentionService.hasPendingChecks()).isTrue();
+
+ /** From manager, cancel the check */
+ callCancelAttention();
+
+ /** From test service, verify that the check was cancelled */
+ assertThat(CtsTestAttentionService.hasPendingChecks()).isFalse();
+
+ /** From manager, verify that the onFailure callback was called with
+ * ATTENTION_FAILURE_CANCELLED */
+ assertThat(getLastTestCallbackCode()).isEqualTo(
+ AttentionService.ATTENTION_FAILURE_CANCELLED);
+ }
+
+ @Test
+ public void testAttentionService_OnCancelledFromService() {
+ /** From manager, call CheckAttention() on test service */
+ assertThat(CtsTestAttentionService.hasPendingChecks()).isFalse();
+ callCheckAttention();
+
+ /** From test service, verify that onCheckAttention was called */
+ assertThat(CtsTestAttentionService.hasPendingChecks()).isTrue();
+
+ /** From test service, cancel the check and respond with ATTENTION_FAILURE_CANCELLED */
+ CtsTestAttentionService.respondFailure(AttentionService.ATTENTION_FAILURE_CANCELLED);
+
+ /** From test service, verify that the check was cancelled */
+ assertThat(CtsTestAttentionService.hasPendingChecks()).isFalse();
+
+ /** From manager, verify that the onFailure callback was called with
+ * ATTENTION_FAILURE_CANCELLED */
+ assertThat(getLastTestCallbackCode()).isEqualTo(
+ AttentionService.ATTENTION_FAILURE_CANCELLED);
+ }
+
+ private void bindToTestService() {
+ /** On Manager, bind to test service */
+ assertThat(getAttentionServiceComponent()).isNotEqualTo(FAKE_SERVICE_PACKAGE);
+ assertThat(setTestableAttentionService(FAKE_SERVICE_PACKAGE)).isTrue();
+ assertThat(getAttentionServiceComponent()).contains(FAKE_SERVICE_PACKAGE);
+ }
+
+ private String getAttentionServiceComponent() {
+ return runShellCommand("cmd attention getAttentionServiceComponent");
+ }
+
+ private int getLastTestCallbackCode() {
+ return Integer.parseInt(runShellCommand("cmd attention getLastTestCallbackCode"));
+ }
+
+ /**
+ * This call is asynchronous (manager spawns + binds to service and then asynchronously makes a
+ * check attention call).
+ * As such, we need to ensure consistent testing results, by waiting until we receive a response
+ * in our test service w/ CountDownLatch(s).
+ */
+ private void callCheckAttention() {
+ wakeUpScreen();
+ Boolean isSuccess = runShellCommand("cmd attention call checkAttention")
+ .equals("true");
+ assertThat(isSuccess).isTrue();
+ CtsTestAttentionService.onReceivedResponse();
+ }
+
+ private void callCancelAttention() {
+ runShellCommand("cmd attention call cancelCheckAttention");
+ CtsTestAttentionService.onReceivedResponse();
+ }
+
+ private boolean setTestableAttentionService(String service) {
+ return runShellCommand("cmd attention setTestableAttentionService " + service)
+ .equals("true");
+ }
+
+ private void clearTestableAttentionService() {
+ runShellCommand("cmd attention clearTestableAttentionService");
+ }
+
+ private void wakeUpScreen() {
+ runShellCommand("input keyevent KEYCODE_WAKEUP");
+ }
+}
diff --git a/tests/attentionservice/src/android/attentionservice/cts/CtsTestAttentionService.java b/tests/attentionservice/src/android/attentionservice/cts/CtsTestAttentionService.java
new file mode 100644
index 0000000..232f2ff
--- /dev/null
+++ b/tests/attentionservice/src/android/attentionservice/cts/CtsTestAttentionService.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.attentionservice.cts;
+
+import android.service.attention.AttentionService;
+import android.util.Log;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+public class CtsTestAttentionService extends AttentionService {
+ private static final String TAG = "CtsTestAttentionService";
+ private static AttentionCallback sCurrentCallback;
+ private static CountDownLatch sRespondLatch = new CountDownLatch(1);
+
+ @Override
+ public void onCheckAttention(AttentionCallback callback) {
+ sCurrentCallback = callback;
+ sRespondLatch.countDown();
+ }
+
+ @Override
+ public void onCancelAttentionCheck(AttentionCallback callback) {
+ callback.onFailure(ATTENTION_FAILURE_CANCELLED);
+ reset();
+ sRespondLatch.countDown();
+ }
+
+ public static void reset() {
+ sCurrentCallback = null;
+ }
+
+ public static void respondSuccess(int code) {
+ if (sCurrentCallback != null) {
+ sCurrentCallback.onSuccess(code, 0);
+ }
+ reset();
+ }
+
+ public static void respondFailure(int code) {
+ if (sCurrentCallback != null) {
+ sCurrentCallback.onFailure(code);
+ }
+ reset();
+ }
+
+ public static boolean hasPendingChecks() {
+ return sCurrentCallback != null;
+ }
+
+ public static void onReceivedResponse() {
+ try {
+ if (!sRespondLatch.await(500, TimeUnit.MILLISECONDS)) {
+ throw new AssertionError("CtsTestAttentionService timed out while expecting a call.");
+ }
+ //reset for next
+ sRespondLatch = new CountDownLatch(1);
+ } catch (InterruptedException e) {
+ Log.e(TAG, e.getMessage());
+ Thread.currentThread().interrupt();
+ throw new AssertionError("Got InterruptedException while waiting for response.");
+ }
+ }
+}
diff --git a/tests/autofillservice/Android.mk b/tests/autofillservice/Android.mk
index 43f16e1..cab840a 100644
--- a/tests/autofillservice/Android.mk
+++ b/tests/autofillservice/Android.mk
@@ -24,6 +24,7 @@
LOCAL_STATIC_JAVA_LIBRARIES := \
androidx.annotation_annotation \
+ androidx.test.ext.junit \
compatibility-device-util-axt \
ctsdeviceutillegacy-axt \
ctstestrunner-axt \
diff --git a/tests/autofillservice/AndroidManifest.xml b/tests/autofillservice/AndroidManifest.xml
index 9f3f6b7..fe21919 100644
--- a/tests/autofillservice/AndroidManifest.xml
+++ b/tests/autofillservice/AndroidManifest.xml
@@ -40,8 +40,10 @@
android:theme="@style/MyAutofilledHighlight"/>
<activity android:name=".LoginWithStringsActivity" />
<activity android:name=".LoginNotImportantForAutofillActivity" />
+ <activity android:name=".LoginNotImportantForAutofillWrappedActivityContextActivity" />
+ <activity android:name=".LoginNotImportantForAutofillWrappedApplicationContextActivity" />
<activity android:name=".WelcomeActivity" android:taskAffinity=".WelcomeActivity"/>
- <activity android:name=".ViewAttributesTestActivity" android:screenOrientation="portrait"/>
+ <activity android:name=".ViewAttributesTestActivity" />
<activity android:name=".AuthenticationActivity" />
<activity android:name=".ManualAuthenticationActivity" />
<activity android:name=".CheckoutActivity" android:taskAffinity=".CheckoutActivity"/>
diff --git a/tests/autofillservice/src/android/autofillservice/cts/AbstractAutoFillActivity.java b/tests/autofillservice/src/android/autofillservice/cts/AbstractAutoFillActivity.java
index 63df568..a955587 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/AbstractAutoFillActivity.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/AbstractAutoFillActivity.java
@@ -42,6 +42,7 @@
public abstract class AbstractAutoFillActivity extends Activity {
private final CountDownLatch mDestroyedLatch = new CountDownLatch(1);
+ protected final String mTag = getClass().getSimpleName();
private MyAutofillCallback mCallback;
/**
@@ -109,7 +110,7 @@
* <p>Note: caller doesn't need to call {@link #unregisterCallback()}, it will be automatically
* unregistered on {@link #finish()}.
*/
- protected MyAutofillCallback registerCallback() {
+ public MyAutofillCallback registerCallback() {
assertWithMessage("already registered").that(mCallback).isNull();
mCallback = new MyAutofillCallback();
getAutofillManager().registerCallback(mCallback);
diff --git a/tests/autofillservice/src/android/autofillservice/cts/AuthenticationActivity.java b/tests/autofillservice/src/android/autofillservice/cts/AuthenticationActivity.java
index 9e2c8f9..b247ec2 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/AuthenticationActivity.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/AuthenticationActivity.java
@@ -223,8 +223,8 @@
if (response.getResponseType() == NULL) {
result = null;
} else {
- result = response
- .asFillResponse((id) -> Helper.findNodeByResourceId(structure, id));
+ result = response.asFillResponse(/* contexts= */ null,
+ (id) -> Helper.findNodeByResourceId(structure, id));
}
} else if (dataset != null) {
result = dataset.asDataset((id) -> Helper.findNodeByResourceId(structure, id));
diff --git a/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java b/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java
index a078191..03388510 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java
@@ -20,6 +20,8 @@
import static android.autofillservice.cts.InstrumentedAutoFillService.SERVICE_NAME;
import static android.content.Context.CLIPBOARD_SERVICE;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
import static com.android.compatibility.common.util.ShellUtils.runShellCommand;
import android.autofillservice.cts.InstrumentedAutoFillService.Replier;
@@ -34,8 +36,7 @@
import android.widget.RemoteViews;
import androidx.annotation.NonNull;
-import androidx.test.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.compatibility.common.util.DeviceConfigStateChangerRule;
import com.android.compatibility.common.util.RequiredFeatureRule;
@@ -176,7 +177,7 @@
protected static final Replier sReplier = InstrumentedAutoFillService.getReplier();
- protected static final Context sContext = InstrumentationRegistry.getTargetContext();
+ protected static final Context sContext = getInstrumentation().getTargetContext();
// Hack because JUnit requires that @ClassRule instance belong to a public class.
protected static final SettingsStateKeeperRule sTheRealServiceSettingsKeeper =
diff --git a/tests/autofillservice/src/android/autofillservice/cts/BatchUpdatesTest.java b/tests/autofillservice/src/android/autofillservice/cts/BatchUpdatesTest.java
index 444fbe3..b063342 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/BatchUpdatesTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/BatchUpdatesTest.java
@@ -27,7 +27,7 @@
import android.service.autofill.Transformation;
import android.widget.RemoteViews;
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/tests/autofillservice/src/android/autofillservice/cts/CannedFillResponse.java b/tests/autofillservice/src/android/autofillservice/cts/CannedFillResponse.java
index 09853e9..477d6d8 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/CannedFillResponse.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/CannedFillResponse.java
@@ -23,19 +23,21 @@
import android.app.assist.AssistStructure.ViewNode;
import android.content.IntentSender;
import android.os.Bundle;
-import android.service.autofill.CustomDescription;
import android.service.autofill.Dataset;
import android.service.autofill.FillCallback;
+import android.service.autofill.FillContext;
import android.service.autofill.FillResponse;
-import android.service.autofill.Sanitizer;
import android.service.autofill.SaveInfo;
import android.service.autofill.UserData;
-import android.service.autofill.Validator;
+import android.util.Log;
import android.util.Pair;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillValue;
import android.widget.RemoteViews;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -61,17 +63,16 @@
*/
public final class CannedFillResponse {
+ private static final String TAG = CannedFillResponse.class.getSimpleName();
+
private final ResponseType mResponseType;
private final List<CannedDataset> mDatasets;
- private final ArrayList<Pair<Sanitizer, AutofillId[]>> mSanitizers;
private final String mFailureMessage;
private final int mSaveType;
- private final Validator mValidator;
private final String[] mRequiredSavableIds;
private final String[] mOptionalSavableIds;
private final AutofillId[] mRequiredSavableAutofillIds;
private final String mSaveDescription;
- private final CustomDescription mCustomDescription;
private final Bundle mExtras;
private final RemoteViews mPresentation;
private final RemoteViews mHeader;
@@ -85,21 +86,21 @@
private final int mFillResponseFlags;
private final AutofillId mSaveTriggerId;
private final long mDisableDuration;
- private final AutofillId[] mFieldClassificationIds;
+ private final String[] mFieldClassificationIds;
private final boolean mFieldClassificationIdsOverflow;
private final SaveInfoDecorator mSaveInfoDecorator;
private final UserData mUserData;
+ private final DoubleVisitor<List<FillContext>, FillResponse.Builder> mVisitor;
+ private DoubleVisitor<List<FillContext>, SaveInfo.Builder> mSaveInfoVisitor;
private CannedFillResponse(Builder builder) {
mResponseType = builder.mResponseType;
mDatasets = builder.mDatasets;
mFailureMessage = builder.mFailureMessage;
- mValidator = builder.mValidator;
mRequiredSavableIds = builder.mRequiredSavableIds;
mRequiredSavableAutofillIds = builder.mRequiredSavableAutofillIds;
mOptionalSavableIds = builder.mOptionalSavableIds;
mSaveDescription = builder.mSaveDescription;
- mCustomDescription = builder.mCustomDescription;
mSaveType = builder.mSaveType;
mExtras = builder.mExtras;
mPresentation = builder.mPresentation;
@@ -110,7 +111,6 @@
mIgnoredIds = builder.mIgnoredIds;
mNegativeActionStyle = builder.mNegativeActionStyle;
mNegativeActionListener = builder.mNegativeActionListener;
- mSanitizers = builder.mSanitizers;
mSaveInfoFlags = builder.mSaveInfoFlags;
mFillResponseFlags = builder.mFillResponseFlags;
mSaveTriggerId = builder.mSaveTriggerId;
@@ -119,6 +119,8 @@
mFieldClassificationIdsOverflow = builder.mFieldClassificationIdsOverflow;
mSaveInfoDecorator = builder.mSaveInfoDecorator;
mUserData = builder.mUserData;
+ mVisitor = builder.mVisitor;
+ mSaveInfoVisitor = builder.mSaveInfoVisitor;
}
/**
@@ -140,7 +142,6 @@
public static final CannedFillResponse DO_NOT_REPLY_RESPONSE =
new Builder(ResponseType.TIMEOUT).build();
-
/**
* Constant used to call {@link FillCallback#onFailure(CharSequence)} method.
*/
@@ -159,7 +160,8 @@
* Creates a new response, replacing the dataset field ids by the real ids from the assist
* structure.
*/
- public FillResponse asFillResponse(Function<String, ViewNode> nodeResolver) {
+ public FillResponse asFillResponse(@Nullable List<FillContext> contexts,
+ @NonNull Function<String, ViewNode> nodeResolver) {
final FillResponse.Builder builder = new FillResponse.Builder()
.setFlags(mFillResponseFlags);
if (mDatasets != null) {
@@ -169,54 +171,49 @@
builder.addDataset(dataset);
}
}
- final SaveInfo.Builder saveInfo;
+ final SaveInfo.Builder saveInfoBuilder;
if (mRequiredSavableIds != null || mOptionalSavableIds != null
|| mRequiredSavableAutofillIds != null || mSaveInfoDecorator != null) {
if (mRequiredSavableAutofillIds != null) {
- saveInfo = new SaveInfo.Builder(mSaveType, mRequiredSavableAutofillIds);
+ saveInfoBuilder = new SaveInfo.Builder(mSaveType, mRequiredSavableAutofillIds);
} else {
- saveInfo = mRequiredSavableIds == null || mRequiredSavableIds.length == 0
+ saveInfoBuilder = mRequiredSavableIds == null || mRequiredSavableIds.length == 0
? new SaveInfo.Builder(mSaveType)
: new SaveInfo.Builder(mSaveType,
getAutofillIds(nodeResolver, mRequiredSavableIds));
}
- saveInfo.setFlags(mSaveInfoFlags);
+ saveInfoBuilder.setFlags(mSaveInfoFlags);
- if (mValidator != null) {
- saveInfo.setValidator(mValidator);
- }
if (mOptionalSavableIds != null) {
- saveInfo.setOptionalIds(getAutofillIds(nodeResolver, mOptionalSavableIds));
+ saveInfoBuilder.setOptionalIds(getAutofillIds(nodeResolver, mOptionalSavableIds));
}
if (mSaveDescription != null) {
- saveInfo.setDescription(mSaveDescription);
+ saveInfoBuilder.setDescription(mSaveDescription);
}
if (mNegativeActionListener != null) {
- saveInfo.setNegativeAction(mNegativeActionStyle, mNegativeActionListener);
+ saveInfoBuilder.setNegativeAction(mNegativeActionStyle, mNegativeActionListener);
}
-
- if (mCustomDescription != null) {
- saveInfo.setCustomDescription(mCustomDescription);
- }
-
- for (Pair<Sanitizer, AutofillId[]> sanitizer : mSanitizers) {
- saveInfo.addSanitizer(sanitizer.first, sanitizer.second);
- }
-
if (mSaveTriggerId != null) {
- saveInfo.setTriggerId(mSaveTriggerId);
+ saveInfoBuilder.setTriggerId(mSaveTriggerId);
}
} else if (mSaveInfoFlags != 0) {
- saveInfo = new SaveInfo.Builder(mSaveType).setFlags(mSaveInfoFlags);
+ saveInfoBuilder = new SaveInfo.Builder(mSaveType).setFlags(mSaveInfoFlags);
} else {
- saveInfo = null;
+ saveInfoBuilder = null;
}
- if (saveInfo != null) {
+ if (saveInfoBuilder != null) {
+ // TODO: merge decorator and visitor
if (mSaveInfoDecorator != null) {
- mSaveInfoDecorator.decorate(saveInfo, nodeResolver);
+ mSaveInfoDecorator.decorate(saveInfoBuilder, nodeResolver);
}
- builder.setSaveInfo(saveInfo.build());
+ if (mSaveInfoVisitor != null) {
+ Log.d(TAG, "Visiting saveInfo " + saveInfoBuilder);
+ mSaveInfoVisitor.visit(contexts, saveInfoBuilder);
+ }
+ final SaveInfo saveInfo = saveInfoBuilder.build();
+ Log.d(TAG, "saveInfo:" + saveInfo);
+ builder.setSaveInfo(saveInfo);
}
if (mIgnoredIds != null) {
builder.setIgnoredIds(getAutofillIds(nodeResolver, mIgnoredIds));
@@ -236,7 +233,8 @@
}
builder.setFieldClassificationIds(fieldIds);
} else if (mFieldClassificationIds != null) {
- builder.setFieldClassificationIds(mFieldClassificationIds);
+ builder.setFieldClassificationIds(
+ getAutofillIds(nodeResolver, mFieldClassificationIds));
}
if (mExtras != null) {
builder.setClientState(mExtras);
@@ -250,7 +248,14 @@
if (mUserData != null) {
builder.setUserData(mUserData);
}
- return builder.build();
+ if (mVisitor != null) {
+ Log.d(TAG, "Visiting " + builder);
+ mVisitor.visit(contexts, builder);
+ }
+
+ final FillResponse response = builder.build();
+ Log.v(TAG, "Response: " + response);
+ return response;
}
@Override
@@ -264,20 +269,20 @@
+ ", fillResponseFlags=" + mFillResponseFlags
+ ", failureMessage=" + mFailureMessage
+ ", saveDescription=" + mSaveDescription
- + ", mCustomDescription=" + mCustomDescription
+ ", hasPresentation=" + (mPresentation != null)
+ ", hasHeader=" + (mHeader != null)
+ ", hasFooter=" + (mFooter != null)
+ ", hasAuthentication=" + (mAuthentication != null)
+ ", authenticationIds=" + Arrays.toString(mAuthenticationIds)
+ ", ignoredIds=" + Arrays.toString(mIgnoredIds)
- + ", sanitizers =" + mSanitizers
+ ", saveTriggerId=" + mSaveTriggerId
+ ", disableDuration=" + mDisableDuration
+ ", fieldClassificationIds=" + Arrays.toString(mFieldClassificationIds)
+ ", fieldClassificationIdsOverflow=" + mFieldClassificationIdsOverflow
+ ", saveInfoDecorator=" + mSaveInfoDecorator
+ ", userData=" + mUserData
+ + ", visitor=" + mVisitor
+ + ", saveInfoVisitor=" + mSaveInfoVisitor
+ "]";
}
@@ -291,15 +296,12 @@
public static final class Builder {
private final List<CannedDataset> mDatasets = new ArrayList<>();
- private final ArrayList<Pair<Sanitizer, AutofillId[]>> mSanitizers = new ArrayList<>();
private final ResponseType mResponseType;
private String mFailureMessage;
- private Validator mValidator;
private String[] mRequiredSavableIds;
private String[] mOptionalSavableIds;
private AutofillId[] mRequiredSavableAutofillIds;
private String mSaveDescription;
- public CustomDescription mCustomDescription;
public int mSaveType = -1;
private Bundle mExtras;
private RemoteViews mPresentation;
@@ -314,10 +316,12 @@
private int mFillResponseFlags;
private AutofillId mSaveTriggerId;
private long mDisableDuration;
- private AutofillId[] mFieldClassificationIds;
+ private String[] mFieldClassificationIds;
private boolean mFieldClassificationIdsOverflow;
private SaveInfoDecorator mSaveInfoDecorator;
private UserData mUserData;
+ private DoubleVisitor<List<FillContext>, FillResponse.Builder> mVisitor;
+ private DoubleVisitor<List<FillContext>, SaveInfo.Builder> mSaveInfoVisitor;
public Builder(ResponseType type) {
mResponseType = type;
@@ -334,14 +338,6 @@
}
/**
- * Sets the validator for this request
- */
- public Builder setValidator(Validator validator) {
- mValidator = validator;
- return this;
- }
-
- /**
* Sets the required savable ids based on their {@code resourceId}.
*/
public Builder setRequiredSavableIds(int type, String... ids) {
@@ -350,15 +346,6 @@
return this;
}
- /**
- * Sets the required savable ids based on their {@code autofillId}.
- */
- public Builder setRequiredSavableAutofillIds(int type, AutofillId... ids) {
- mSaveType = type;
- mRequiredSavableAutofillIds = ids;
- return this;
- }
-
public Builder setSaveInfoFlags(int flags) {
mSaveInfoFlags = flags;
return this;
@@ -386,14 +373,6 @@
}
/**
- * Sets the description passed to the {@link SaveInfo}.
- */
- public Builder setCustomDescription(CustomDescription description) {
- mCustomDescription = description;
- return this;
- }
-
- /**
* Sets the extra passed to {@link
* android.service.autofill.FillResponse.Builder#setClientState(Bundle)}.
*/
@@ -436,14 +415,6 @@
return this;
}
- /**
- * Adds a save sanitizer.
- */
- public Builder addSanitizer(Sanitizer sanitizer, AutofillId... ids) {
- mSanitizers.add(new Pair<>(sanitizer, ids));
- return this;
- }
-
public CannedFillResponse build() {
return new CannedFillResponse(this);
}
@@ -475,7 +446,7 @@
/**
* Sets the ids used for field classification.
*/
- public Builder setFieldClassificationIds(AutofillId... ids) {
+ public Builder setFieldClassificationIds(String... ids) {
assertWithMessage("already set").that(mFieldClassificationIds).isNull();
mFieldClassificationIds = ids;
return this;
@@ -517,6 +488,30 @@
mUserData = userData;
return this;
}
+
+ /**
+ * Sets a generic visitor for the "real" request and response.
+ *
+ * <p>Typically used in cases where the test need to infer data from the request to build
+ * the response.
+ */
+ public Builder setVisitor(
+ @NonNull DoubleVisitor<List<FillContext>, FillResponse.Builder> visitor) {
+ mVisitor = visitor;
+ return this;
+ }
+
+ /**
+ * Sets a generic visitor for the "real" request and save info.
+ *
+ * <p>Typically used in cases where the test need to infer data from the request to build
+ * the response.
+ */
+ public Builder setSaveInfoVisitor(
+ @NonNull DoubleVisitor<List<FillContext>, SaveInfo.Builder> visitor) {
+ mSaveInfoVisitor = visitor;
+ return this;
+ }
}
/**
@@ -536,8 +531,6 @@
*/
public static class CannedDataset {
private final Map<String, AutofillValue> mFieldValues;
- private final Map<AutofillId, AutofillValue> mFieldValuesById;
- private final Map<AutofillId, RemoteViews> mFieldPresentationsById;
private final Map<String, RemoteViews> mFieldPresentations;
private final Map<String, Pair<Boolean, Pattern>> mFieldFilters;
private final RemoteViews mPresentation;
@@ -546,8 +539,6 @@
private CannedDataset(Builder builder) {
mFieldValues = builder.mFieldValues;
- mFieldValuesById = builder.mFieldValuesById;
- mFieldPresentationsById = builder.mFieldPresentationsById;
mFieldPresentations = builder.mFieldPresentations;
mFieldFilters = builder.mFieldFilters;
mPresentation = builder.mPresentation;
@@ -589,20 +580,6 @@
}
}
}
- if (mFieldValuesById != null) {
- // NOTE: filter is not yet supported when calling methods that explicitly pass
- // autofill id
- for (Map.Entry<AutofillId, AutofillValue> entry : mFieldValuesById.entrySet()) {
- final AutofillId autofillId = entry.getKey();
- final AutofillValue value = entry.getValue();
- final RemoteViews presentation = mFieldPresentationsById.get(autofillId);
- if (presentation != null) {
- builder.setValue(autofillId, value, presentation);
- } else {
- builder.setValue(autofillId, value);
- }
- }
- }
builder.setId(mId).setAuthentication(mAuthentication);
return builder.build();
}
@@ -611,18 +588,14 @@
public String toString() {
return "CannedDataset " + mId + " : [hasPresentation=" + (mPresentation != null)
+ ", fieldPresentations=" + (mFieldPresentations)
- + ", fieldPresentationsById=" + (mFieldPresentationsById)
+ ", hasAuthentication=" + (mAuthentication != null)
+ ", fieldValues=" + mFieldValues
- + ", fieldValuesById=" + mFieldValuesById
+ ", fieldFilters=" + mFieldFilters + "]";
}
public static class Builder {
private final Map<String, AutofillValue> mFieldValues = new HashMap<>();
- private final Map<AutofillId, AutofillValue> mFieldValuesById = new HashMap<>();
private final Map<String, RemoteViews> mFieldPresentations = new HashMap<>();
- private final Map<AutofillId, RemoteViews> mFieldPresentationsById = new HashMap<>();
private final Map<String, Pair<Boolean, Pattern>> mFieldFilters = new HashMap<>();
private RemoteViews mPresentation;
@@ -709,21 +682,6 @@
}
/**
- * Sets the canned value of a date field based on its {@code autofillId}.
- */
- public Builder setField(AutofillId autofillId, AutofillValue value) {
- mFieldValuesById.put(autofillId, value);
- return this;
- }
-
- /**
- * Sets the canned value of a date field based on its {@code autofillId}.
- */
- public Builder setField(AutofillId autofillId, String text) {
- return setField(autofillId, AutofillValue.forText(text));
- }
-
- /**
* Sets the canned value of a date field based on its {@code id}.
*
* <p>The meaning of the id is defined by the object using the canned dataset.
@@ -751,15 +709,6 @@
}
/**
- * Sets the canned value of a date field based on its {@code autofillId}.
- */
- public Builder setField(AutofillId autofillId, String text, RemoteViews presentation) {
- setField(autofillId, AutofillValue.forText(text));
- mFieldPresentationsById.put(autofillId, presentation);
- return this;
- }
-
- /**
* Sets the canned value of a field based on its {@code id}.
*
* <p>The meaning of the id is defined by the object using the canned dataset.
diff --git a/tests/autofillservice/src/android/autofillservice/cts/CharSequenceTransformationTest.java b/tests/autofillservice/src/android/autofillservice/cts/CharSequenceTransformationTest.java
index 946e665..73fc9d4 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/CharSequenceTransformationTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/CharSequenceTransformationTest.java
@@ -31,7 +31,7 @@
import android.view.autofill.AutofillId;
import android.widget.RemoteViews;
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/tests/autofillservice/src/android/autofillservice/cts/CheckoutActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/CheckoutActivityTest.java
index dead535..1f411df 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/CheckoutActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/CheckoutActivityTest.java
@@ -30,6 +30,7 @@
import static android.autofillservice.cts.Helper.assertTextIsSanitized;
import static android.autofillservice.cts.Helper.assertToggleIsSanitized;
import static android.autofillservice.cts.Helper.assertToggleValue;
+import static android.autofillservice.cts.Helper.findAutofillIdByResourceId;
import static android.autofillservice.cts.Helper.findNodeByResourceId;
import static android.autofillservice.cts.Helper.getContext;
import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_CREDIT_CARD;
@@ -45,9 +46,11 @@
import android.platform.test.annotations.AppModeFull;
import android.service.autofill.CharSequenceTransformation;
import android.service.autofill.CustomDescription;
+import android.service.autofill.FillContext;
import android.service.autofill.ImageTransformation;
import android.support.test.uiautomator.By;
import android.support.test.uiautomator.UiObject2;
+import android.view.autofill.AutofillId;
import android.widget.ArrayAdapter;
import android.widget.RemoteViews;
import android.widget.Spinner;
@@ -269,31 +272,38 @@
// Set expectations.
final String packageName = getContext().getPackageName();
- final RemoteViews presentation = new RemoteViews(packageName,
- R.layout.two_horizontal_text_fields);
- final CharSequenceTransformation trans1 = new CharSequenceTransformation
- .Builder(mActivity.getCcNumber().getAutofillId(), Pattern.compile("(.*)"), "$1")
- .build();
- final CharSequenceTransformation trans2 = new CharSequenceTransformation
- .Builder(mActivity.getCcExpiration().getAutofillId(), Pattern.compile("(.*)"), "$1")
- .build();
- final ImageTransformation trans3 = (withContentDescription
- ? new ImageTransformation.Builder(mActivity.getCcNumber().getAutofillId(),
- Pattern.compile("(.*)"), R.drawable.android,
- "One image is worth thousand words")
- : new ImageTransformation.Builder(mActivity.getCcNumber().getAutofillId(),
- Pattern.compile("(.*)"), R.drawable.android))
- .build();
-
- final CustomDescription customDescription = new CustomDescription.Builder(presentation)
- .addChild(R.id.first, trans1)
- .addChild(R.id.second, trans2)
- .addChild(R.id.img, trans3)
- .build();
-
sReplier.addResponse(new CannedFillResponse.Builder()
.setRequiredSavableIds(SAVE_DATA_TYPE_CREDIT_CARD, ID_CC_NUMBER, ID_CC_EXPIRATION)
- .setCustomDescription(customDescription)
+ .setSaveInfoVisitor((contexts, builder) -> {
+ final RemoteViews presentation = new RemoteViews(packageName,
+ R.layout.two_horizontal_text_fields);
+ final FillContext context = contexts.get(0);
+ final AutofillId ccNumberId = findAutofillIdByResourceId(context,
+ ID_CC_NUMBER);
+ final AutofillId ccExpirationId = findAutofillIdByResourceId(context,
+ ID_CC_EXPIRATION);
+ final CharSequenceTransformation trans1 = new CharSequenceTransformation
+ .Builder(ccNumberId, Pattern.compile("(.*)"), "$1")
+ .build();
+ final CharSequenceTransformation trans2 = new CharSequenceTransformation
+ .Builder(ccExpirationId, Pattern.compile("(.*)"), "$1")
+ .build();
+ final ImageTransformation trans3 = (withContentDescription
+ ? new ImageTransformation.Builder(ccNumberId,
+ Pattern.compile("(.*)"), R.drawable.android,
+ "One image is worth thousand words")
+ : new ImageTransformation.Builder(ccNumberId,
+ Pattern.compile("(.*)"), R.drawable.android))
+ .build();
+
+ final CustomDescription customDescription =
+ new CustomDescription.Builder(presentation)
+ .addChild(R.id.first, trans1)
+ .addChild(R.id.second, trans2)
+ .addChild(R.id.img, trans3)
+ .build();
+ builder.setCustomDescription(customDescription);
+ })
.build());
// Dynamically change view contents
@@ -349,22 +359,29 @@
// Set expectations.
final String packageName = getContext().getPackageName();
- final RemoteViews presentation = new RemoteViews(packageName,
- R.layout.two_horizontal_text_fields);
- final CharSequenceTransformation trans1 = new CharSequenceTransformation
- .Builder(mActivity.getCcNumber().getAutofillId(), Pattern.compile("(.*)"), "$1")
- .build();
- final CharSequenceTransformation trans2 = new CharSequenceTransformation
- .Builder(mActivity.getCcExpiration().getAutofillId(), Pattern.compile("(.*)"), "$1")
- .build();
- final CustomDescription customDescription = new CustomDescription.Builder(presentation)
- .addChild(R.id.first, trans1)
- .addChild(R.id.second, trans2)
- .build();
-
sReplier.addResponse(new CannedFillResponse.Builder()
.setRequiredSavableIds(SAVE_DATA_TYPE_CREDIT_CARD, ID_CC_NUMBER, ID_CC_EXPIRATION)
- .setCustomDescription(customDescription)
+ .setSaveInfoVisitor((contexts, builder) -> {
+ final FillContext context = contexts.get(0);
+ final AutofillId ccNumberId = findAutofillIdByResourceId(context,
+ ID_CC_NUMBER);
+ final AutofillId ccExpirationId = findAutofillIdByResourceId(context,
+ ID_CC_EXPIRATION);
+ final RemoteViews presentation = new RemoteViews(packageName,
+ R.layout.two_horizontal_text_fields);
+ final CharSequenceTransformation trans1 = new CharSequenceTransformation
+ .Builder(ccNumberId, Pattern.compile("(.*)"), "$1")
+ .build();
+ final CharSequenceTransformation trans2 = new CharSequenceTransformation
+ .Builder(ccExpirationId, Pattern.compile("(.*)"), "$1")
+ .build();
+ final CustomDescription customDescription =
+ new CustomDescription.Builder(presentation)
+ .addChild(R.id.first, trans1)
+ .addChild(R.id.second, trans2)
+ .build();
+ builder.setCustomDescription(customDescription);
+ })
.build());
// Dynamically change view contents
diff --git a/tests/autofillservice/src/android/autofillservice/cts/CustomDescriptionDateTest.java b/tests/autofillservice/src/android/autofillservice/cts/CustomDescriptionDateTest.java
index 13896aa..ad89227 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/CustomDescriptionDateTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/CustomDescriptionDateTest.java
@@ -17,6 +17,7 @@
import static android.autofillservice.cts.AbstractDatePickerActivity.ID_DATE_PICKER;
import static android.autofillservice.cts.AbstractDatePickerActivity.ID_OUTPUT;
+import static android.autofillservice.cts.Helper.findAutofillIdByResourceId;
import static android.autofillservice.cts.Helper.getContext;
import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_GENERIC;
@@ -59,18 +60,20 @@
// Set service.
enableService();
- final AutofillId id = mActivity.getDatePicker().getAutofillId();
-
// Set expectations.
sReplier.addResponse(new CannedFillResponse.Builder()
- .setCustomDescription(new CustomDescription
- .Builder(newTemplate(R.layout.two_horizontal_text_fields))
- .addChild(R.id.first,
- new DateTransformation(id, new SimpleDateFormat("MM/yyyy")))
- .addChild(R.id.second,
- new DateTransformation(id, new SimpleDateFormat("MM-yy")))
- .build())
.setRequiredSavableIds(SAVE_DATA_TYPE_GENERIC, ID_OUTPUT, ID_DATE_PICKER)
+ .setSaveInfoVisitor((contexts, builder) -> {
+ final AutofillId id = findAutofillIdByResourceId(contexts.get(0),
+ ID_DATE_PICKER);
+ builder.setCustomDescription(new CustomDescription
+ .Builder(newTemplate(R.layout.two_horizontal_text_fields))
+ .addChild(R.id.first,
+ new DateTransformation(id, new SimpleDateFormat("MM/yyyy")))
+ .addChild(R.id.second,
+ new DateTransformation(id, new SimpleDateFormat("MM-yy")))
+ .build());
+ })
.build());
// Trigger auto-fill.
@@ -111,8 +114,6 @@
// Set service.
enableService();
- final AutofillId id = mActivity.getDatePicker().getAutofillId();
-
// Set expectations.
final Calendar cal = Calendar.getInstance();
cal.clear();
@@ -134,7 +135,10 @@
.setRequiredSavableIds(SAVE_DATA_TYPE_GENERIC, ID_OUTPUT, ID_DATE_PICKER);
if (withSanitization) {
- response.addSanitizer(new DateValueSanitizer(new SimpleDateFormat("MM/yyyy")), id);
+ response.setSaveInfoVisitor((contexts, builder) -> {
+ final AutofillId id = findAutofillIdByResourceId(contexts.get(0), ID_DATE_PICKER);
+ builder.addSanitizer(new DateValueSanitizer(new SimpleDateFormat("MM/yyyy")), id);
+ });
}
sReplier.addResponse(response.build());
diff --git a/tests/autofillservice/src/android/autofillservice/cts/CustomDescriptionHelper.java b/tests/autofillservice/src/android/autofillservice/cts/CustomDescriptionHelper.java
index 2e913b5..5fc33e7 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/CustomDescriptionHelper.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/CustomDescriptionHelper.java
@@ -15,11 +15,11 @@
*/
package android.autofillservice.cts;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
import android.service.autofill.CustomDescription;
import android.widget.RemoteViews;
-import androidx.test.InstrumentationRegistry;
-
public final class CustomDescriptionHelper {
public static final String ID_SHOW = "show";
@@ -30,7 +30,7 @@
public static final String ID_PASSWORD_MASKED = "password_masked";
private static final String sPackageName =
- InstrumentationRegistry.getTargetContext().getPackageName();
+ getInstrumentation().getTargetContext().getPackageName();
public static CustomDescription.Builder newCustomDescriptionWithUsernameAndPassword() {
diff --git a/tests/autofillservice/src/android/autofillservice/cts/CustomDescriptionTest.java b/tests/autofillservice/src/android/autofillservice/cts/CustomDescriptionTest.java
index 2abde4f..0c6792d 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/CustomDescriptionTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/CustomDescriptionTest.java
@@ -18,6 +18,7 @@
import static android.autofillservice.cts.Helper.ID_PASSWORD;
import static android.autofillservice.cts.Helper.ID_USERNAME;
+import static android.autofillservice.cts.Helper.findAutofillIdByResourceId;
import static android.autofillservice.cts.Helper.getContext;
import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_GENERIC;
@@ -28,6 +29,7 @@
import android.service.autofill.BatchUpdates;
import android.service.autofill.CharSequenceTransformation;
import android.service.autofill.CustomDescription;
+import android.service.autofill.FillContext;
import android.service.autofill.ImageTransformation;
import android.service.autofill.RegexValidator;
import android.service.autofill.TextValueSanitizer;
@@ -60,13 +62,15 @@
@Nullable Runnable uiVerifier) throws Exception {
enableService();
- final AutofillId usernameId = mActivity.getUsername().getAutofillId();
- final AutofillId passwordId = mActivity.getPassword().getAutofillId();
-
// Set response with custom description
sReplier.addResponse(new CannedFillResponse.Builder()
.setRequiredSavableIds(SAVE_DATA_TYPE_GENERIC, ID_USERNAME, ID_PASSWORD)
- .setCustomDescription(descriptionBuilder.apply(usernameId, passwordId))
+ .setSaveInfoVisitor((contexts, builder) -> {
+ final FillContext context = contexts.get(0);
+ final AutofillId usernameId = findAutofillIdByResourceId(context, ID_USERNAME);
+ final AutofillId passwordId = findAutofillIdByResourceId(context, ID_PASSWORD);
+ builder.setCustomDescription(descriptionBuilder.apply(usernameId, passwordId));
+ })
.build());
// Trigger autofill with custom description
@@ -92,27 +96,34 @@
public void testSanitizationBeforeBatchUpdates() throws Exception {
enableService();
- final RemoteViews presentation = newTemplate(R.layout.two_horizontal_text_fields);
-
- final AutofillId usernameId = mActivity.getUsername().getAutofillId();
-
- // Validator for sanitization
- final Validator validCondition = new RegexValidator(usernameId, Pattern.compile("user"));
-
- final RemoteViews update = newTemplate(-666); // layout id not really used
- update.setTextViewText(R.id.first, "batch updated");
-
- final CustomDescription customDescription = new CustomDescription.Builder(presentation)
- .batchUpdate(validCondition,
- new BatchUpdates.Builder().updateTemplate(update).build())
- .build();
-
// Set response with custom description
sReplier.addResponse(new CannedFillResponse.Builder()
.setRequiredSavableIds(SAVE_DATA_TYPE_GENERIC, ID_USERNAME)
- .addSanitizer(new TextValueSanitizer(Pattern.compile("USERNAME"), "user"),
- usernameId)
- .setCustomDescription(customDescription)
+ .setSaveInfoVisitor((contexts, builder) -> {
+ final RemoteViews presentation =
+ newTemplate(R.layout.two_horizontal_text_fields);
+
+ final AutofillId usernameId =
+ findAutofillIdByResourceId(contexts.get(0), ID_USERNAME);
+
+ // Validator for sanitization
+ final Validator validCondition =
+ new RegexValidator(usernameId, Pattern.compile("user"));
+
+ final RemoteViews update = newTemplate(-666); // layout id not really used
+ update.setTextViewText(R.id.first, "batch updated");
+
+ final CustomDescription customDescription = new CustomDescription
+ .Builder(presentation)
+ .batchUpdate(validCondition,
+ new BatchUpdates.Builder().updateTemplate(update).build())
+ .build();
+ builder
+ .addSanitizer(new TextValueSanitizer(Pattern.compile("USERNAME"), "user"),
+ usernameId)
+ .setCustomDescription(customDescription);
+
+ })
.build());
// Trigger autofill with custom description
@@ -133,25 +144,31 @@
public void testSanitizationBeforeTransformations() throws Exception {
enableService();
- final RemoteViews presentation = newTemplate(R.layout.two_horizontal_text_fields);
-
- final AutofillId usernameId = mActivity.getUsername().getAutofillId();
-
- // Transformation
- final CharSequenceTransformation trans = new CharSequenceTransformation
- .Builder(usernameId, Pattern.compile("user"), "transformed")
- .build();
-
- final CustomDescription customDescription = new CustomDescription.Builder(presentation)
- .addChild(R.id.first, trans)
- .build();
-
// Set response with custom description
sReplier.addResponse(new CannedFillResponse.Builder()
.setRequiredSavableIds(SAVE_DATA_TYPE_GENERIC, ID_USERNAME)
- .addSanitizer(new TextValueSanitizer(Pattern.compile("USERNAME"), "user"),
- usernameId)
- .setCustomDescription(customDescription)
+ .setSaveInfoVisitor((contexts, builder) -> {
+ final RemoteViews presentation =
+ newTemplate(R.layout.two_horizontal_text_fields);
+
+ final AutofillId usernameId =
+ findAutofillIdByResourceId(contexts.get(0), ID_USERNAME);
+
+ // Transformation
+ final CharSequenceTransformation trans = new CharSequenceTransformation
+ .Builder(usernameId, Pattern.compile("user"), "transformed")
+ .build();
+
+ final CustomDescription customDescription = new CustomDescription
+ .Builder(presentation)
+ .addChild(R.id.first, trans)
+ .build();
+ builder
+ .addSanitizer(new TextValueSanitizer(Pattern.compile("USERNAME"), "user"),
+ usernameId)
+ .setCustomDescription(customDescription);
+
+ })
.build());
// Trigger autofill with custom description
@@ -508,22 +525,27 @@
private void multipleTransformationsForSameFieldTest(boolean matchFirst) throws Exception {
enableService();
- // Set response with custom description
- final AutofillId usernameId = mActivity.getUsername().getAutofillId();
- final CharSequenceTransformation firstTrans = new CharSequenceTransformation
- .Builder(usernameId, Pattern.compile("(marco)"), "polo")
- .build();
- final CharSequenceTransformation secondTrans = new CharSequenceTransformation
- .Builder(usernameId, Pattern.compile("(MARCO)"), "POLO")
- .build();
- RemoteViews presentation = newTemplate(R.layout.two_horizontal_text_fields);
- final CustomDescription customDescription = new CustomDescription.Builder(presentation)
- .addChild(R.id.first, firstTrans)
- .addChild(R.id.first, secondTrans)
- .build();
sReplier.addResponse(new CannedFillResponse.Builder()
.setRequiredSavableIds(SAVE_DATA_TYPE_GENERIC, ID_USERNAME)
- .setCustomDescription(customDescription)
+ .setSaveInfoVisitor((contexts, builder) -> {
+ // Set response with custom description
+ final AutofillId usernameId =
+ findAutofillIdByResourceId(contexts.get(0), ID_USERNAME);
+ final CharSequenceTransformation firstTrans = new CharSequenceTransformation
+ .Builder(usernameId, Pattern.compile("(marco)"), "polo")
+ .build();
+ final CharSequenceTransformation secondTrans = new CharSequenceTransformation
+ .Builder(usernameId, Pattern.compile("(MARCO)"), "POLO")
+ .build();
+ final RemoteViews presentation =
+ newTemplate(R.layout.two_horizontal_text_fields);
+ final CustomDescription customDescription =
+ new CustomDescription.Builder(presentation)
+ .addChild(R.id.first, firstTrans)
+ .addChild(R.id.first, secondTrans)
+ .build();
+ builder.setCustomDescription(customDescription);
+ })
.build());
// Trigger autofill with custom description
diff --git a/tests/autofillservice/src/android/autofillservice/cts/CustomDescriptionUnitTest.java b/tests/autofillservice/src/android/autofillservice/cts/CustomDescriptionUnitTest.java
index a0c0f9e..8432d16 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/CustomDescriptionUnitTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/CustomDescriptionUnitTest.java
@@ -33,7 +33,7 @@
import android.util.SparseArray;
import android.widget.RemoteViews;
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/tests/autofillservice/src/android/autofillservice/cts/DatasetTest.java b/tests/autofillservice/src/android/autofillservice/cts/DatasetTest.java
index 2f39e852..b83b94d 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/DatasetTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/DatasetTest.java
@@ -27,7 +27,7 @@
import android.view.autofill.AutofillValue;
import android.widget.RemoteViews;
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/tests/autofillservice/src/android/autofillservice/cts/DateValueSanitizerTest.java b/tests/autofillservice/src/android/autofillservice/cts/DateValueSanitizerTest.java
index eeda1b7..fc9f1dd 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/DateValueSanitizerTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/DateValueSanitizerTest.java
@@ -27,7 +27,7 @@
import android.util.Log;
import android.view.autofill.AutofillValue;
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/tests/autofillservice/src/android/autofillservice/cts/DoubleVisitor.java b/tests/autofillservice/src/android/autofillservice/cts/DoubleVisitor.java
new file mode 100644
index 0000000..8379307
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/DoubleVisitor.java
@@ -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.
+ */
+package android.autofillservice.cts;
+
+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
+ */
+// TODO: move to common
+public interface DoubleVisitor<V1, V2> {
+
+ /**
+ * Visit those objects.
+ */
+ void visit(@NonNull V1 visited1, @NonNull V2 visited2);
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/DuplicateIdActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/DuplicateIdActivityTest.java
index 1e16e0f..60bac5f 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/DuplicateIdActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/DuplicateIdActivityTest.java
@@ -18,6 +18,7 @@
import static android.autofillservice.cts.CannedFillResponse.NO_RESPONSE;
import static android.autofillservice.cts.DuplicateIdActivity.DUPLICATE_ID;
+import static android.autofillservice.cts.Helper.assertEqualsIgnoreSession;
import static com.android.compatibility.common.util.ShellUtils.runShellCommand;
@@ -158,8 +159,10 @@
// We just need
// - to restore as many views as we can (i.e. one)
// - make sure the autofill ids are still unique after
- final boolean view1WasRestored = (recreatedId1.equals(id1) || recreatedId1.equals(id2));
- final boolean view2WasRestored = (recreatedId2.equals(id1) || recreatedId2.equals(id2));
+ final boolean view1WasRestored = (recreatedId1.equalsIgnoreSession(id1)
+ || recreatedId1.equalsIgnoreSession(id2));
+ final boolean view2WasRestored = (recreatedId2.equalsIgnoreSession(id1)
+ || recreatedId2.equalsIgnoreSession(id2));
// One id was restored
assertThat(view1WasRestored || view2WasRestored).isTrue();
@@ -168,6 +171,6 @@
assertThat(recreatedId1).isNotEqualTo(recreatedId2);
// Assert id of new view
- assertThat(newId1).isEqualTo(child.getAutofillId());
+ assertEqualsIgnoreSession(newId1, child.getAutofillId());
}
}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/FieldsClassificationTest.java b/tests/autofillservice/src/android/autofillservice/cts/FieldsClassificationTest.java
index 20a184f..ff25596 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/FieldsClassificationTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/FieldsClassificationTest.java
@@ -15,8 +15,13 @@
*/
package android.autofillservice.cts;
+import static android.autofillservice.cts.GridActivity.ID_L1C1;
+import static android.autofillservice.cts.GridActivity.ID_L1C2;
+import static android.autofillservice.cts.GridActivity.ID_L2C1;
+import static android.autofillservice.cts.GridActivity.ID_L2C2;
import static android.autofillservice.cts.Helper.assertFillEventForContextCommitted;
import static android.autofillservice.cts.Helper.assertFillEventForFieldsClassification;
+import static android.autofillservice.cts.Helper.findAutofillIdByResourceId;
import static android.provider.Settings.Secure.AUTOFILL_FEATURE_FIELD_CLASSIFICATION;
import static android.provider.Settings.Secure.AUTOFILL_USER_DATA_MAX_CATEGORY_COUNT;
import static android.provider.Settings.Secure.AUTOFILL_USER_DATA_MAX_FIELD_CLASSIFICATION_IDS_SIZE;
@@ -31,6 +36,7 @@
import android.autofillservice.cts.Helper.FieldClassificationResult;
import android.os.Bundle;
import android.platform.test.annotations.AppModeFull;
+import android.service.autofill.FillContext;
import android.service.autofill.FillEventHistory.Event;
import android.service.autofill.UserData;
import android.view.autofill.AutofillId;
@@ -43,6 +49,7 @@
import org.junit.Test;
import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
@AppModeFull(reason = "Service-specific test")
public class FieldsClassificationTest extends AbstractGridActivityTestCase {
@@ -176,9 +183,8 @@
.build());
final MyAutofillCallback callback = mActivity.registerCallback();
final EditText field = mActivity.getCell(1, 1);
- final AutofillId fieldId = field.getAutofillId();
sReplier.addResponse(new CannedFillResponse.Builder()
- .setFieldClassificationIds(fieldId)
+ .setFieldClassificationIds(ID_L1C1)
.build());
// Trigger autofill
@@ -211,9 +217,11 @@
.build());
final MyAutofillCallback callback = mActivity.registerCallback();
final EditText field = mActivity.getCell(1, 1);
- final AutofillId fieldId = field.getAutofillId();
+ final AtomicReference<AutofillId> fieldId = new AtomicReference<>();
sReplier.addResponse(new CannedFillResponse.Builder()
- .setFieldClassificationIds(fieldId)
+ .setFieldClassificationIds(ID_L1C1)
+ .setVisitor((contexts, builder) -> fieldId
+ .set(findAutofillIdByResourceId(contexts.get(0), ID_L1C1)))
.build());
// Trigger autofill
@@ -231,7 +239,7 @@
// Assert results
final List<Event> events = InstrumentedAutoFillService.getFillEvents(1);
- assertFillEventForFieldsClassification(events.get(0), fieldId, "cat", 1);
+ assertFillEventForFieldsClassification(events.get(0), fieldId.get(), "cat", 1);
}
@Test
@@ -247,9 +255,11 @@
.build());
final MyAutofillCallback callback = mActivity.registerCallback();
final EditText field = mActivity.getCell(1, 1);
- final AutofillId fieldId = field.getAutofillId();
+ final AtomicReference<AutofillId> fieldId = new AtomicReference<>();
sReplier.addResponse(new CannedFillResponse.Builder()
- .setFieldClassificationIds(fieldId)
+ .setFieldClassificationIds(ID_L1C1)
+ .setVisitor((contexts, builder) -> fieldId
+ .set(findAutofillIdByResourceId(contexts.get(0), ID_L1C1)))
.build());
// Trigger autofill
@@ -267,7 +277,7 @@
// Assert results
final List<Event> events = InstrumentedAutoFillService.getFillEvents(1);
- assertFillEventForFieldsClassification(events.get(0), fieldId, "cat", 1);
+ assertFillEventForFieldsClassification(events.get(0), fieldId.get(), "cat", 1);
}
private void simpleHitTest(boolean setAlgorithm, String algorithm) throws Exception {
@@ -282,9 +292,11 @@
mAfm.setUserData(userData.build());
final MyAutofillCallback callback = mActivity.registerCallback();
final EditText field = mActivity.getCell(1, 1);
- final AutofillId fieldId = field.getAutofillId();
+ final AtomicReference<AutofillId> fieldId = new AtomicReference<>();
sReplier.addResponse(new CannedFillResponse.Builder()
- .setFieldClassificationIds(fieldId)
+ .setFieldClassificationIds(ID_L1C1)
+ .setVisitor((contexts, builder) -> fieldId
+ .set(findAutofillIdByResourceId(contexts.get(0), ID_L1C1)))
.build());
// Trigger autofill
@@ -302,7 +314,7 @@
// Assert results
final List<Event> events = InstrumentedAutoFillService.getFillEvents(1);
- assertFillEventForFieldsClassification(events.get(0), fieldId, "myId", 1);
+ assertFillEventForFieldsClassification(events.get(0), fieldId.get(), "myId", 1);
}
@Test
@@ -317,9 +329,11 @@
.build());
final MyAutofillCallback callback = mActivity.registerCallback();
final EditText field = mActivity.getCell(1, 1);
- final AutofillId fieldId = field.getAutofillId();
+ final AtomicReference<AutofillId> fieldId = new AtomicReference<>();
sReplier.addResponse(new CannedFillResponse.Builder()
- .setFieldClassificationIds(fieldId)
+ .setFieldClassificationIds(ID_L1C1)
+ .setVisitor((contexts, builder) -> fieldId
+ .set(findAutofillIdByResourceId(contexts.get(0), ID_L1C1)))
.build());
// Trigger autofill
@@ -339,7 +353,7 @@
final List<Event> events = InstrumentedAutoFillService.getFillEvents(1);
assertFillEventForFieldsClassification(events.get(0),
new FieldClassificationResult[] {
- new FieldClassificationResult(fieldId,
+ new FieldClassificationResult(fieldId.get(),
new String[] { "cat1", "cat2"},
new float[] {1, 1})
});
@@ -364,9 +378,11 @@
.add("Iam2ND", "2ndId").build());
final MyAutofillCallback callback = mActivity.registerCallback();
final EditText field = mActivity.getCell(1, 1);
- final AutofillId fieldId = field.getAutofillId();
+ final AtomicReference<AutofillId> fieldId = new AtomicReference<>();
sReplier.addResponse(new CannedFillResponse.Builder()
- .setFieldClassificationIds(fieldId)
+ .setFieldClassificationIds(ID_L1C1)
+ .setVisitor((contexts, builder) -> fieldId
+ .set(findAutofillIdByResourceId(contexts.get(0), ID_L1C1)))
.build());
// Trigger autofill
@@ -387,11 +403,11 @@
// Best match is 0.66 (4 of 6), worst is 0.5 (3 of 6)
if (firstMatch) {
assertFillEventForFieldsClassification(events.get(0), new FieldClassificationResult[] {
- new FieldClassificationResult(fieldId, new String[] { "1stId", "2ndId" },
+ new FieldClassificationResult(fieldId.get(), new String[] { "1stId", "2ndId" },
new float[] { 0.66F, 0.5F })});
} else {
assertFillEventForFieldsClassification(events.get(0), new FieldClassificationResult[] {
- new FieldClassificationResult(fieldId, new String[] { "2ndId", "1stId" },
+ new FieldClassificationResult(fieldId.get(), new String[] { "2ndId", "1stId" },
new float[] { 0.66F, 0.5F }) });
}
}
@@ -405,11 +421,15 @@
mAfm.setUserData(new UserData.Builder("id", "FULLY", "myId").build());
final MyAutofillCallback callback = mActivity.registerCallback();
final EditText field1 = mActivity.getCell(1, 1);
- final AutofillId fieldId1 = field1.getAutofillId();
- final EditText field2 = mActivity.getCell(1, 2);
- final AutofillId fieldId2 = field2.getAutofillId();
+ final AtomicReference<AutofillId> fieldId1 = new AtomicReference<>();
+ final AtomicReference<AutofillId> fieldId2 = new AtomicReference<>();
sReplier.addResponse(new CannedFillResponse.Builder()
- .setFieldClassificationIds(fieldId1, fieldId2)
+ .setFieldClassificationIds(ID_L1C1, ID_L1C2)
+ .setVisitor((contexts, builder) -> {
+ final FillContext context = contexts.get(0);
+ fieldId1.set(findAutofillIdByResourceId(context, ID_L1C1));
+ fieldId2.set(findAutofillIdByResourceId(context, ID_L1C2));
+ })
.build());
// Trigger autofill
@@ -430,8 +450,8 @@
final List<Event> events = InstrumentedAutoFillService.getFillEvents(1);
assertFillEventForFieldsClassification(events.get(0),
new FieldClassificationResult[] {
- new FieldClassificationResult(fieldId1, "myId", 1.0F),
- new FieldClassificationResult(fieldId2, "myId", 0.6F),
+ new FieldClassificationResult(fieldId1.get(), "myId", 1.0F),
+ new FieldClassificationResult(fieldId2.get(), "myId", 0.6F),
});
}
@@ -447,15 +467,19 @@
.build());
final MyAutofillCallback callback = mActivity.registerCallback();
final EditText field1 = mActivity.getCell(1, 1);
- final AutofillId fieldId1 = field1.getAutofillId();
- final EditText field2 = mActivity.getCell(1, 2);
- final AutofillId fieldId2 = field2.getAutofillId();
- final EditText field3 = mActivity.getCell(2, 1);
- final AutofillId fieldId3 = field3.getAutofillId();
- final EditText field4 = mActivity.getCell(2, 2);
- final AutofillId fieldId4 = field4.getAutofillId();
+ final AtomicReference<AutofillId> fieldId1 = new AtomicReference<>();
+ final AtomicReference<AutofillId> fieldId2 = new AtomicReference<>();
+ final AtomicReference<AutofillId> fieldId3 = new AtomicReference<>();
+ final AtomicReference<AutofillId> fieldId4 = new AtomicReference<>();
sReplier.addResponse(new CannedFillResponse.Builder()
- .setFieldClassificationIds(fieldId1, fieldId2)
+ .setFieldClassificationIds(ID_L1C1, ID_L1C2)
+ .setVisitor((contexts, builder) -> {
+ final FillContext context = contexts.get(0);
+ fieldId1.set(findAutofillIdByResourceId(context, ID_L1C1));
+ fieldId2.set(findAutofillIdByResourceId(context, ID_L1C2));
+ fieldId3.set(findAutofillIdByResourceId(context, ID_L2C1));
+ fieldId4.set(findAutofillIdByResourceId(context, ID_L2C2));
+ })
.build());
// Trigger autofill
@@ -478,14 +502,14 @@
final List<Event> events = InstrumentedAutoFillService.getFillEvents(1);
assertFillEventForFieldsClassification(events.get(0),
new FieldClassificationResult[] {
- new FieldClassificationResult(fieldId1, new String[] { "myId", "otherId" },
- new float[] { 1.0F, 0.2F }),
- new FieldClassificationResult(fieldId2, new String[] { "otherId", "myId" },
- new float[] { 1.0F, 0.2F }),
- new FieldClassificationResult(fieldId3, new String[] { "myId", "otherId" },
- new float[] { 0.6F, 0.2F }),
- new FieldClassificationResult(fieldId4, new String[] { "otherId", "myId"},
- new float[] { 0.80F, 0.2F })});
+ new FieldClassificationResult(fieldId1.get(),
+ new String[] { "myId", "otherId" }, new float[] { 1.0F, 0.2F }),
+ new FieldClassificationResult(fieldId2.get(),
+ new String[] { "otherId", "myId" }, new float[] { 1.0F, 0.2F }),
+ new FieldClassificationResult(fieldId3.get(),
+ new String[] { "myId", "otherId" }, new float[] { 0.6F, 0.2F }),
+ new FieldClassificationResult(fieldId4.get(),
+ new String[] { "otherId", "myId"}, new float[] { 0.80F, 0.2F })});
}
@Test
@@ -505,13 +529,17 @@
.build());
final MyAutofillCallback callback = mActivity.registerCallback();
final EditText field1 = mActivity.getCell(1, 1);
- final AutofillId fieldId1 = field1.getAutofillId();
- final EditText field2 = mActivity.getCell(1, 2);
- final AutofillId fieldId2 = field2.getAutofillId();
- final EditText field3 = mActivity.getCell(2, 1);
- final AutofillId fieldId3 = field3.getAutofillId();
+ final AtomicReference<AutofillId> fieldId1 = new AtomicReference<>();
+ final AtomicReference<AutofillId> fieldId2 = new AtomicReference<>();
+ final AtomicReference<AutofillId> fieldId3 = new AtomicReference<>();
sReplier.addResponse(new CannedFillResponse.Builder()
- .setFieldClassificationIds(fieldId1, fieldId2)
+ .setFieldClassificationIds(ID_L1C1, ID_L1C2)
+ .setVisitor((contexts, builder) -> {
+ final FillContext context = contexts.get(0);
+ fieldId1.set(findAutofillIdByResourceId(context, ID_L1C1));
+ fieldId2.set(findAutofillIdByResourceId(context, ID_L1C2));
+ fieldId3.set(findAutofillIdByResourceId(context, ID_L2C1));
+ })
.build());
// Trigger autofill
@@ -533,12 +561,12 @@
final List<Event> events = InstrumentedAutoFillService.getFillEvents(1);
assertFillEventForFieldsClassification(events.get(0),
new FieldClassificationResult[] {
- new FieldClassificationResult(fieldId1, new String[] { "myId", "otherId" },
- new float[] { 1.0F, 0.2F }),
- new FieldClassificationResult(fieldId2, new String[] { "otherId" },
- new float[] { 1.0F }),
- new FieldClassificationResult(fieldId3, new String[] { "otherId" },
- new float[] { 0.2F })});
+ new FieldClassificationResult(fieldId1.get(),
+ new String[] { "myId", "otherId" }, new float[] { 1.0F, 0.2F }),
+ new FieldClassificationResult(fieldId2.get(),
+ new String[] { "otherId" }, new float[] { 1.0F }),
+ new FieldClassificationResult(fieldId3.get(),
+ new String[] { "otherId" }, new float[] { 0.2F })});
}
@Test
@@ -555,15 +583,19 @@
.build());
final MyAutofillCallback callback = mActivity.registerCallback();
final EditText field1 = mActivity.getCell(1, 1);
- final AutofillId fieldId1 = field1.getAutofillId();
- final EditText field2 = mActivity.getCell(1, 2);
- final AutofillId fieldId2 = field2.getAutofillId();
- final EditText field3 = mActivity.getCell(2, 1);
- final AutofillId fieldId3 = field3.getAutofillId();
- final EditText field4 = mActivity.getCell(2, 2);
- final AutofillId fieldId4 = field4.getAutofillId();
+ final AtomicReference<AutofillId> fieldId1 = new AtomicReference<>();
+ final AtomicReference<AutofillId> fieldId2 = new AtomicReference<>();
+ final AtomicReference<AutofillId> fieldId3 = new AtomicReference<>();
+ final AtomicReference<AutofillId> fieldId4 = new AtomicReference<>();
sReplier.addResponse(new CannedFillResponse.Builder()
- .setFieldClassificationIds(fieldId1, fieldId2)
+ .setFieldClassificationIds(ID_L1C1, ID_L1C2)
+ .setVisitor((contexts, builder) -> {
+ final FillContext context = contexts.get(0);
+ fieldId1.set(findAutofillIdByResourceId(context, ID_L1C1));
+ fieldId2.set(findAutofillIdByResourceId(context, ID_L1C2));
+ fieldId3.set(findAutofillIdByResourceId(context, ID_L2C1));
+ fieldId4.set(findAutofillIdByResourceId(context, ID_L2C2));
+ })
.build());
// Trigger autofill
@@ -586,14 +618,14 @@
final List<Event> events = InstrumentedAutoFillService.getFillEvents(1);
assertFillEventForFieldsClassification(events.get(0),
new FieldClassificationResult[] {
- new FieldClassificationResult(fieldId1, new String[] { "myId", "otherId" },
- new float[] { 1.0F, 0.2F }),
- new FieldClassificationResult(fieldId2, new String[] { "otherId", "myId" },
- new float[] { 1.0F, 0.2F }),
- new FieldClassificationResult(fieldId3, new String[] { "myId", "otherId" },
- new float[] { 0.6F, 0.2F }),
- new FieldClassificationResult(fieldId4, new String[] { "otherId", "myId"},
- new float[] { 0.80F, 0.2F })});
+ new FieldClassificationResult(fieldId1.get(),
+ new String[] { "myId", "otherId" }, new float[] { 1.0F, 0.2F }),
+ new FieldClassificationResult(fieldId2.get(),
+ new String[] { "otherId", "myId" }, new float[] { 1.0F, 0.2F }),
+ new FieldClassificationResult(fieldId3.get(),
+ new String[] { "myId", "otherId" }, new float[] { 0.6F, 0.2F }),
+ new FieldClassificationResult(fieldId4.get(),
+ new String[] { "otherId", "myId"}, new float[] { 0.80F, 0.2F })});
}
@Test
@@ -605,9 +637,8 @@
mAfm.setUserData(new UserData.Builder("id", "ABCDEF", "myId").build());
final MyAutofillCallback callback = mActivity.registerCallback();
final EditText field = mActivity.getCell(1, 1);
- final AutofillId fieldId = field.getAutofillId();
sReplier.addResponse(new CannedFillResponse.Builder()
- .setFieldClassificationIds(fieldId)
+ .setFieldClassificationIds(ID_L1C1)
.build());
// Trigger autofill
@@ -637,9 +668,8 @@
mAfm.setUserData(new UserData.Builder("id", "FULLY", "myId").build());
final MyAutofillCallback callback = mActivity.registerCallback();
final EditText field = mActivity.getCell(1, 1);
- final AutofillId fieldId = field.getAutofillId();
sReplier.addResponse(new CannedFillResponse.Builder()
- .setFieldClassificationIds(fieldId)
+ .setFieldClassificationIds(ID_L1C1)
.build());
// Trigger autofill
@@ -669,15 +699,14 @@
final MyAutofillCallback callback = mActivity.registerCallback();
final EditText field = mActivity.getCell(1, 1);
- final AutofillId fieldId = field.getAutofillId();
+ final AtomicReference<AutofillId> fieldId1 = new AtomicReference<>();
sReplier.addResponse(new CannedFillResponse.Builder()
- .setFieldClassificationIds(fieldId)
+ .setFieldClassificationIds(ID_L1C1)
+ .setVisitor((contexts, builder) -> fieldId1
+ .set(findAutofillIdByResourceId(contexts.get(0), ID_L1C1)))
.setUserData(new UserData.Builder("id2", "TEST2", "cat")
.setFieldClassificationAlgorithm(null, null)
.build())
- .build())
- .addResponse(new CannedFillResponse.Builder()
- .setFieldClassificationIds(fieldId)
.build());
// Trigger autofill
@@ -694,7 +723,14 @@
mAfm.commit();
final Event packageUserDataEvent = InstrumentedAutoFillService.getFillEvents(1).get(0);
- assertFillEventForFieldsClassification(packageUserDataEvent, fieldId, "cat", 0.8F);
+ assertFillEventForFieldsClassification(packageUserDataEvent, fieldId1.get(), "cat", 0.8F);
+
+ final AtomicReference<AutofillId> fieldId2 = new AtomicReference<>();
+ sReplier.addResponse(new CannedFillResponse.Builder()
+ .setVisitor((contexts, builder) -> fieldId2
+ .set(findAutofillIdByResourceId(contexts.get(0), ID_L1C1)))
+ .setFieldClassificationIds(ID_L1C1)
+ .build());
// Need to switch focus first
mActivity.focusCell(1, 2);
@@ -711,7 +747,7 @@
// Assert results
final Event defaultUserDataEvent = InstrumentedAutoFillService.getFillEvents(1).get(0);
- assertFillEventForFieldsClassification(defaultUserDataEvent, fieldId, "cat", 1.0F);
+ assertFillEventForFieldsClassification(defaultUserDataEvent, fieldId2.get(), "cat", 1.0F);
}
@Test
@@ -723,11 +759,15 @@
mAfm.setUserData(new UserData.Builder("id", "FULLY", "myId").build());
final MyAutofillCallback callback = mActivity.registerCallback();
final EditText field1 = mActivity.getCell(1, 1);
- final AutofillId fieldId1 = field1.getAutofillId();
- final EditText field2 = mActivity.getCell(1, 2);
- final AutofillId fieldId2 = field2.getAutofillId();
+ final AtomicReference<AutofillId> fieldId1 = new AtomicReference<>();
+ final AtomicReference<AutofillId> fieldId2 = new AtomicReference<>();
sReplier.addResponse(new CannedFillResponse.Builder()
- .setFieldClassificationIds(fieldId1, fieldId2)
+ .setFieldClassificationIds(ID_L1C1, ID_L1C2)
+ .setVisitor((contexts, builder) -> {
+ final FillContext context = contexts.get(0);
+ fieldId1.set(findAutofillIdByResourceId(context, ID_L1C1));
+ fieldId2.set(findAutofillIdByResourceId(context, ID_L1C2));
+ })
.setUserData(new UserData.Builder("id2", "FOOLY", "otherId")
.add("EMPTY", "myId")
.build())
@@ -751,10 +791,10 @@
final List<Event> events = InstrumentedAutoFillService.getFillEvents(1);
assertFillEventForFieldsClassification(events.get(0),
new FieldClassificationResult[] {
- new FieldClassificationResult(fieldId1, new String[] { "otherId", "myId" },
- new float[] { 0.6F, 0.2F }),
- new FieldClassificationResult(fieldId2, new String[] { "myId", "otherId" },
- new float[] { 1.0F, 0.2F }),
+ new FieldClassificationResult(fieldId1.get(),
+ new String[] { "otherId", "myId" }, new float[] { 0.6F, 0.2F }),
+ new FieldClassificationResult(fieldId2.get(),
+ new String[] { "myId", "otherId" }, new float[] { 1.0F, 0.2F }),
});
}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/FillEventHistoryTest.java b/tests/autofillservice/src/android/autofillservice/cts/FillEventHistoryTest.java
index 2c6bfe5..6e0914d 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/FillEventHistoryTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/FillEventHistoryTest.java
@@ -28,6 +28,7 @@
import static android.autofillservice.cts.Helper.assertFillEventForDatasetSelected;
import static android.autofillservice.cts.Helper.assertFillEventForSaveShown;
import static android.autofillservice.cts.Helper.assertNoDeprecatedClientState;
+import static android.autofillservice.cts.Helper.findAutofillIdByResourceId;
import static android.autofillservice.cts.InstrumentedAutoFillService.waitUntilConnected;
import static android.autofillservice.cts.InstrumentedAutoFillService.waitUntilDisconnected;
import static android.autofillservice.cts.LoginActivity.BACKDOOR_USERNAME;
@@ -40,10 +41,12 @@
import static com.google.common.truth.Truth.assertWithMessage;
import android.autofillservice.cts.CannedFillResponse.CannedDataset;
+import android.autofillservice.cts.InstrumentedAutoFillService.FillRequest;
import android.content.Intent;
import android.content.IntentSender;
import android.os.Bundle;
import android.platform.test.annotations.AppModeFull;
+import android.service.autofill.FillContext;
import android.service.autofill.FillEventHistory;
import android.service.autofill.FillEventHistory.Event;
import android.service.autofill.FillResponse;
@@ -605,7 +608,7 @@
// Trigger autofill on username
mActivity.onUsername(View::requestFocus);
- sReplier.getNextFillRequest();
+ final FillRequest request = sReplier.getNextFillRequest();
final UiObject2 datasetPicker = mUiBot.assertDatasets("dataset1", "dataset2");
mUiBot.selectDataset(datasetPicker, "dataset1");
@@ -636,7 +639,8 @@
assertThat(event2.getClientState()).isNull();
assertThat(event2.getSelectedDatasetIds()).isEmpty();
assertThat(event2.getIgnoredDatasetIds()).containsExactly("id2");
- final AutofillId passwordId = mActivity.getPassword().getAutofillId();
+ final AutofillId passwordId = findAutofillIdByResourceId(request.contexts.get(0),
+ ID_PASSWORD);
final Map<AutofillId, String> changedFields = event2.getChangedFields();
assertThat(changedFields).containsExactly(passwordId, "id2");
assertThat(event2.getManuallyEnteredField()).isEmpty();
@@ -666,7 +670,7 @@
// Trigger autofill on username
mActivity.onUsername(View::requestFocus);
- sReplier.getNextFillRequest();
+ final FillRequest request = sReplier.getNextFillRequest();
final UiObject2 datasetPicker = mUiBot.assertDatasets("dataset1", "dataset2");
mUiBot.selectDataset(datasetPicker, "dataset2");
@@ -697,7 +701,8 @@
assertThat(event2.getClientState()).isNull();
assertThat(event2.getSelectedDatasetIds()).containsExactly("id2");
assertThat(event2.getIgnoredDatasetIds()).isEmpty();
- final AutofillId passwordId = mActivity.getPassword().getAutofillId();
+ final AutofillId passwordId = findAutofillIdByResourceId(request.contexts.get(0),
+ ID_PASSWORD);
final Map<AutofillId, String> changedFields = event2.getChangedFields();
assertThat(changedFields).containsExactly(passwordId, "id2");
assertThat(event2.getManuallyEnteredField()).isEmpty();
@@ -785,7 +790,7 @@
// Trigger autofill on username
mActivity.onUsername(View::requestFocus);
- sReplier.getNextFillRequest();
+ final FillRequest request = sReplier.getNextFillRequest();
final UiObject2 datasetPicker = mUiBot.assertDatasets("dataset1", "dataset2");
mUiBot.selectDataset(datasetPicker, "dataset1");
@@ -818,8 +823,10 @@
assertThat(event2.getSelectedDatasetIds()).containsExactly("id1");
assertThat(event2.getIgnoredDatasetIds()).containsExactly("id2");
final Map<AutofillId, String> changedFields = event2.getChangedFields();
- final AutofillId usernameId = mActivity.getUsername().getAutofillId();
- final AutofillId passwordId = mActivity.getPassword().getAutofillId();
+ final FillContext context = request.contexts.get(0);
+ final AutofillId usernameId = findAutofillIdByResourceId(context, ID_USERNAME);
+ final AutofillId passwordId = findAutofillIdByResourceId(context, ID_PASSWORD);
+
assertThat(changedFields).containsExactlyEntriesIn(
ImmutableMap.of(usernameId, "id1", passwordId, "id1"));
assertThat(event2.getManuallyEnteredField()).isEmpty();
@@ -851,7 +858,7 @@
// Trigger autofill
mActivity.onUsername(View::requestFocus);
- sReplier.getNextFillRequest();
+ final FillRequest request = sReplier.getNextFillRequest();
// Autofill username
mUiBot.selectDataset("dataset1");
@@ -899,7 +906,8 @@
assertThat(event3.getSelectedDatasetIds()).containsExactly("id1", "id2");
assertThat(event3.getIgnoredDatasetIds()).isEmpty();
final Map<AutofillId, String> changedFields = event3.getChangedFields();
- final AutofillId passwordId = mActivity.getPassword().getAutofillId();
+ final AutofillId passwordId = findAutofillIdByResourceId(request.contexts.get(0),
+ ID_PASSWORD);
assertThat(changedFields).containsExactly(passwordId, "id2");
assertThat(event3.getManuallyEnteredField()).isEmpty();
}
@@ -1069,7 +1077,7 @@
.build());
// Trigger autofill on username
mActivity.onUsername(View::requestFocus);
- sReplier.getNextFillRequest();
+ final FillRequest request = sReplier.getNextFillRequest();
mUiBot.assertDatasets("dataset1", "dataset2");
// Verify history
@@ -1095,8 +1103,9 @@
assertThat(event.getSelectedDatasetIds()).isEmpty();
assertThat(event.getIgnoredDatasetIds()).containsExactly("id1", "id2");
assertThat(event.getChangedFields()).isEmpty();
- final AutofillId usernameId = mActivity.getUsername().getAutofillId();
- final AutofillId passwordId = mActivity.getPassword().getAutofillId();
+ final FillContext context = request.contexts.get(0);
+ final AutofillId usernameId = findAutofillIdByResourceId(context, ID_USERNAME);
+ final AutofillId passwordId = findAutofillIdByResourceId(context, ID_PASSWORD);
final Map<AutofillId, Set<String>> manuallyEnteredFields =
event.getManuallyEnteredField();
@@ -1140,7 +1149,7 @@
.build());
// Trigger autofill on username
mActivity.onUsername(View::requestFocus);
- sReplier.getNextFillRequest();
+ final FillRequest request = sReplier.getNextFillRequest();
mUiBot.assertDatasets("dataset1", "dataset2", "dataset3");
// Verify history
@@ -1167,8 +1176,9 @@
assertThat(event.getSelectedDatasetIds()).isEmpty();
assertThat(event.getIgnoredDatasetIds()).containsExactly("id1", "id2", "id3");
assertThat(event.getChangedFields()).isEmpty();
- final AutofillId usernameId = mActivity.getUsername().getAutofillId();
- final AutofillId passwordId = mActivity.getPassword().getAutofillId();
+ final FillContext context = request.contexts.get(0);
+ final AutofillId usernameId = findAutofillIdByResourceId(context, ID_USERNAME);
+ final AutofillId passwordId = findAutofillIdByResourceId(context, ID_PASSWORD);
final Map<AutofillId, Set<String>> manuallyEnteredFields =
event.getManuallyEnteredField();
diff --git a/tests/autofillservice/src/android/autofillservice/cts/Helper.java b/tests/autofillservice/src/android/autofillservice/cts/Helper.java
index b550bee..3413422 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/Helper.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/Helper.java
@@ -60,7 +60,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import androidx.test.InstrumentationRegistry;
+import androidx.test.platform.app.InstrumentationRegistry;
import com.android.compatibility.common.util.BitmapUtils;
import com.android.compatibility.common.util.OneTimeSettingsListener;
@@ -96,6 +96,7 @@
public static final String ID_LOGIN = "login";
public static final String ID_OUTPUT = "output";
public static final String ID_STATIC_TEXT = "static_text";
+ public static final String ID_EMPTY = "empty";
public static final String NULL_DATASET_ID = null;
@@ -138,10 +139,6 @@
return id.equals(node.getIdEntry());
};
- private static final NodeFilter<ViewNode> AUTOFILL_ID_FILTER = (node, id) -> {
- return id.equals(node.getAutofillId());
- };
-
private static final NodeFilter<ViewNode> HTML_NAME_FILTER = (node, id) -> {
return id.equals(getHtmlName(node));
};
@@ -404,6 +401,18 @@
}
/**
+ * Gets a node given its Android resource id.
+ */
+ @NonNull
+ public static AutofillId findAutofillIdByResourceId(@NonNull FillContext context,
+ @NonNull String resourceId) {
+ final ViewNode node = findNodeByFilter(context.getStructure(), resourceId,
+ RESOURCE_ID_FILTER);
+ assertWithMessage("No node for resourceId %s", resourceId).that(node).isNotNull();
+ return node.getAutofillId();
+ }
+
+ /**
* Gets the {@code name} attribute of a node representing an HTML input tag.
*/
@Nullable
@@ -465,14 +474,6 @@
}
/**
- * Gets a view (or a descendant of it) that has the given {@code id}, or {@code null} if
- * not found.
- */
- public static ViewNode findNodeByAutofillId(AssistStructure structure, AutofillId id) {
- return findNodeByFilter(structure, id, AUTOFILL_ID_FILTER);
- }
-
- /**
* Asserts a text-based node is sanitized.
*/
public static void assertTextIsSanitized(ViewNode node) {
@@ -1403,6 +1404,16 @@
}
/**
+ * Asserts these autofill ids are the same, except for the session.
+ */
+ public static void assertEqualsIgnoreSession(@NonNull AutofillId id1, @NonNull AutofillId id2) {
+ assertWithMessage("id1 is null").that(id1).isNotNull();
+ assertWithMessage("id2 is null").that(id2).isNotNull();
+ assertWithMessage("%s is not equal to %s", id1, id2).that(id1.equalsIgnoreSession(id2))
+ .isTrue();
+ }
+
+ /**
* Allows the test to draw overlaid windows.
*
* <p>Should call {@link #disallowOverlays()} afterwards.
diff --git a/tests/autofillservice/src/android/autofillservice/cts/ImageTransformationTest.java b/tests/autofillservice/src/android/autofillservice/cts/ImageTransformationTest.java
index 0b69e83..06bb218 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/ImageTransformationTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/ImageTransformationTest.java
@@ -30,7 +30,7 @@
import android.view.autofill.AutofillId;
import android.widget.RemoteViews;
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/tests/autofillservice/src/android/autofillservice/cts/InstrumentedAutoFillService.java b/tests/autofillservice/src/android/autofillservice/cts/InstrumentedAutoFillService.java
index 067621d..8fa55d0 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/InstrumentedAutoFillService.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/InstrumentedAutoFillService.java
@@ -356,12 +356,12 @@
* CancellationSignal, FillCallback)} that can be asserted at the end of a test case.
*/
public static final class FillRequest {
- final AssistStructure structure;
- final List<FillContext> contexts;
- final Bundle data;
- final CancellationSignal cancellationSignal;
- final FillCallback callback;
- final int flags;
+ public final AssistStructure structure;
+ public final List<FillContext> contexts;
+ public final Bundle data;
+ public final CancellationSignal cancellationSignal;
+ public final FillCallback callback;
+ public final int flags;
private FillRequest(List<FillContext> contexts, Bundle data,
CancellationSignal cancellationSignal, FillCallback callback, int flags) {
@@ -655,15 +655,15 @@
switch (mIdMode) {
case RESOURCE_ID:
- fillResponse = response.asFillResponse(
+ fillResponse = response.asFillResponse(contexts,
(id) -> Helper.findNodeByResourceId(contexts, id));
break;
case HTML_NAME:
- fillResponse = response.asFillResponse(
+ fillResponse = response.asFillResponse(contexts,
(name) -> Helper.findNodeByHtmlName(contexts, name));
break;
case HTML_NAME_OR_RESOURCE_ID:
- fillResponse = response.asFillResponse(
+ fillResponse = response.asFillResponse(contexts,
(id) -> Helper.findNodeByHtmlNameOrResourceId(contexts, id));
break;
default:
diff --git a/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java
index 3cc6b31..e4d77bc 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java
@@ -20,6 +20,7 @@
import static android.autofillservice.cts.CannedFillResponse.FAIL;
import static android.autofillservice.cts.CannedFillResponse.NO_MOAR_RESPONSES;
import static android.autofillservice.cts.CannedFillResponse.NO_RESPONSE;
+import static android.autofillservice.cts.Helper.ID_EMPTY;
import static android.autofillservice.cts.Helper.ID_PASSWORD;
import static android.autofillservice.cts.Helper.ID_PASSWORD_LABEL;
import static android.autofillservice.cts.Helper.ID_USERNAME;
@@ -33,7 +34,7 @@
import static android.autofillservice.cts.Helper.assertValue;
import static android.autofillservice.cts.Helper.disallowOverlays;
import static android.autofillservice.cts.Helper.dumpStructure;
-import static android.autofillservice.cts.Helper.findNodeByAutofillId;
+import static android.autofillservice.cts.Helper.findAutofillIdByResourceId;
import static android.autofillservice.cts.Helper.findNodeByResourceId;
import static android.autofillservice.cts.Helper.isAutofillWindowFullScreen;
import static android.autofillservice.cts.Helper.setUserComplete;
@@ -84,6 +85,7 @@
import android.os.Bundle;
import android.os.SystemClock;
import android.platform.test.annotations.AppModeFull;
+import android.service.autofill.FillContext;
import android.service.autofill.SaveInfo;
import android.support.test.uiautomator.UiObject2;
import android.util.Log;
@@ -146,7 +148,7 @@
.addResponse(NO_RESPONSE)
.addResponse(NO_MOAR_RESPONSES);
- // Trigger a
+ // Trigger autofill
mActivity.onUsername(View::requestFocus);
sReplier.getNextFillRequest();
mUiBot.assertNoDatasetsEver();
@@ -182,6 +184,7 @@
// Try again, in a field that was added after the first request
final EditText child = new EditText(mActivity);
+ child.setId(R.id.empty);
mActivity.addChild(child);
final OneTimeTextWatcher watcher = new OneTimeTextWatcher("child", child,
"new view on the block");
@@ -189,7 +192,7 @@
sReplier.addResponse(new CannedDataset.Builder()
.setField(ID_USERNAME, "dude")
.setField(ID_PASSWORD, "sweet")
- .setField(child.getAutofillId(), "new view on the block")
+ .setField(ID_EMPTY, "new view on the block")
.setPresentation(createPresentation("The Dude"))
.build());
mActivity.syncRunOnUiThread(() -> child.requestFocus());
@@ -284,12 +287,13 @@
// Try again, in a field that was added after the first request
final EditText child = new EditText(mActivity);
+ child.setId(R.id.empty);
mActivity.addChild(child);
sReplier.addResponse(new CannedFillResponse.Builder()
- .setRequiredSavableAutofillIds(SAVE_DATA_TYPE_PASSWORD,
- mActivity.getUsername().getAutofillId(),
- mActivity.getPassword().getAutofillId(),
- child.getAutofillId())
+ .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD,
+ ID_USERNAME,
+ ID_PASSWORD,
+ ID_EMPTY)
.build());
mActivity.syncRunOnUiThread(() -> child.requestFocus());
@@ -322,8 +326,7 @@
assertTextAndValue(username, "malkovich");
final ViewNode password = findNodeByResourceId(saveRequest.structure, ID_PASSWORD);
assertTextAndValue(password, "malkovich");
- final ViewNode childNode = findNodeByAutofillId(saveRequest.structure,
- child.getAutofillId());
+ final ViewNode childNode = findNodeByResourceId(saveRequest.structure, ID_EMPTY);
assertTextAndValue(childNode, "NOT MR.M");
}
@@ -467,8 +470,9 @@
final FillRequest request = sReplier.getNextFillRequest();
assertWithMessage("CancelationSignal is null").that(request.cancellationSignal).isNotNull();
assertTextIsSanitized(request.structure, ID_PASSWORD);
- assertThat(request.contexts.get(request.contexts.size() - 1).getFocusedId())
- .isEqualTo(mActivity.getUsername().getAutofillId());
+ final FillContext fillContext = request.contexts.get(request.contexts.size() - 1);
+ assertThat(fillContext.getFocusedId())
+ .isEqualTo(findAutofillIdByResourceId(fillContext, ID_USERNAME));
// Make sure initial focus was properly set.
assertWithMessage("Username node is not focused").that(
diff --git a/tests/autofillservice/src/android/autofillservice/cts/LoginNotImportantForAutofillWrappedActivityContextActivity.java b/tests/autofillservice/src/android/autofillservice/cts/LoginNotImportantForAutofillWrappedActivityContextActivity.java
new file mode 100644
index 0000000..035cea6
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/LoginNotImportantForAutofillWrappedActivityContextActivity.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.autofillservice.cts;
+
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.util.Log;
+
+/**
+ * Same as {@link LoginNotImportantForAutofillActivity}, but using a context wrapper of itself
+ * as the base context.
+ */
+public class LoginNotImportantForAutofillWrappedActivityContextActivity
+ extends LoginNotImportantForAutofillActivity {
+
+ private Context mMyBaseContext;
+
+ @Override
+ public Context getBaseContext() {
+ if (mMyBaseContext == null) {
+ mMyBaseContext = new ContextWrapper(super.getBaseContext());
+ Log.d(mTag, "getBaseContext(): set to " + mMyBaseContext + " (instead of "
+ + super.getBaseContext() + ")");
+ }
+ return mMyBaseContext;
+ }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/LoginNotImportantForAutofillWrappedApplicationContextActivity.java b/tests/autofillservice/src/android/autofillservice/cts/LoginNotImportantForAutofillWrappedApplicationContextActivity.java
new file mode 100644
index 0000000..b47cfc6
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/LoginNotImportantForAutofillWrappedApplicationContextActivity.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.autofillservice.cts;
+
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.util.Log;
+
+/**
+ * Same as {@link LoginNotImportantForAutofillActivity}, but using a context wrapper of itself
+ * as the base context.
+ */
+public class LoginNotImportantForAutofillWrappedApplicationContextActivity
+ extends LoginNotImportantForAutofillActivity {
+
+ private Context mMyBaseContext;
+
+ @Override
+ public Context getBaseContext() {
+ if (mMyBaseContext == null) {
+ mMyBaseContext = new ContextWrapper(getApplicationContext());
+ Log.d(mTag, "getBaseContext(): set to " + mMyBaseContext + " (instead of "
+ + super.getBaseContext() + ")");
+ }
+ return mMyBaseContext;
+ }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/LuhnChecksumValidatorTest.java b/tests/autofillservice/src/android/autofillservice/cts/LuhnChecksumValidatorTest.java
index 8f71ca0..6eb8b8e 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/LuhnChecksumValidatorTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/LuhnChecksumValidatorTest.java
@@ -27,7 +27,7 @@
import android.service.autofill.ValueFinder;
import android.view.autofill.AutofillId;
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/tests/autofillservice/src/android/autofillservice/cts/ManualAuthenticationActivity.java b/tests/autofillservice/src/android/autofillservice/cts/ManualAuthenticationActivity.java
index 14cb3dd..b1983d1 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/ManualAuthenticationActivity.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/ManualAuthenticationActivity.java
@@ -52,7 +52,7 @@
if (structure != null) {
Parcelable result;
if (sResponse != null) {
- result = sResponse.asFillResponse(
+ result = sResponse.asFillResponse(/* contexts= */ null,
(id) -> Helper.findNodeByResourceId(structure, id));
} else if (sDataset != null) {
result = sDataset.asDataset(
diff --git a/tests/autofillservice/src/android/autofillservice/cts/MultiScreenDifferentActivitiesTest.java b/tests/autofillservice/src/android/autofillservice/cts/MultiScreenDifferentActivitiesTest.java
new file mode 100644
index 0000000..122cfc5
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/MultiScreenDifferentActivitiesTest.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.autofillservice.cts;
+
+import static android.autofillservice.cts.Helper.assertTextAndValue;
+import static android.autofillservice.cts.Helper.findNodeByResourceId;
+import static android.autofillservice.cts.PreSimpleSaveActivity.ID_PRE_INPUT;
+import static android.autofillservice.cts.SimpleSaveActivity.ID_INPUT;
+import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_PASSWORD;
+import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_USERNAME;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.app.assist.AssistStructure;
+import android.autofillservice.cts.InstrumentedAutoFillService.SaveRequest;
+import android.content.ComponentName;
+import android.platform.test.annotations.AppModeFull;
+import android.service.autofill.SaveInfo;
+import android.support.test.uiautomator.UiObject2;
+
+import org.junit.Test;
+
+@AppModeFull(reason = "Service-specific test")
+public class MultiScreenDifferentActivitiesTest
+ extends AutoFillServiceTestCase.ManualActivityLaunch {
+
+ @Test
+ public void testActivityNotDelayedIsNotMerged() throws Exception {
+ // Set service.
+ enableService();
+
+ // Trigger autofill on 1st activity, without using FLAG_DELAY_SAVE
+ final PreSimpleSaveActivity activity1 = startPreSimpleSaveActivity();
+ // Set expectations.
+ sReplier.addResponse(new CannedFillResponse.Builder()
+ .setRequiredSavableIds(SAVE_DATA_TYPE_USERNAME, ID_PRE_INPUT)
+ .build());
+
+ activity1.syncRunOnUiThread(() -> activity1.mPreInput.requestFocus());
+ sReplier.getNextFillRequest();
+
+ // Trigger autofill on 2nd activity
+ final SimpleSaveActivity activity2 = startSimpleSaveActivity();
+ sReplier.addResponse(new CannedFillResponse.Builder()
+ .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_INPUT)
+ .build());
+ activity2.syncRunOnUiThread(() -> activity2.mInput.requestFocus());
+ sReplier.getNextFillRequest();
+
+ // Trigger save
+ activity2.syncRunOnUiThread(() -> {
+ activity2.mInput.setText("ID");
+ activity2.mCommit.performClick();
+ });
+ final UiObject2 saveUi = mUiBot.assertSaveShowing(SAVE_DATA_TYPE_PASSWORD);
+
+ // Save it...
+ mUiBot.saveForAutofill(saveUi, true);
+
+ // ... and assert results
+ final SaveRequest saveRequest = sReplier.getNextSaveRequest();
+
+ // Make sure only second request is available
+ assertThat(saveRequest.contexts).hasSize(1);
+
+ assertTextAndValue(findNodeByResourceId(saveRequest.structure, ID_INPUT), "ID");
+ }
+
+ @Test
+ public void testDelayedActivityIsMerged() throws Exception {
+ // Set service.
+ enableService();
+
+ // Trigger autofill on 1st activity, usingFLAG_DELAY_SAVE
+ final PreSimpleSaveActivity activity1 = startPreSimpleSaveActivity();
+ // Set expectations.
+ sReplier.addResponse(new CannedFillResponse.Builder()
+ .setSaveInfoFlags(SaveInfo.FLAG_DELAY_SAVE)
+ .setRequiredSavableIds(SAVE_DATA_TYPE_USERNAME, ID_PRE_INPUT)
+ .build());
+
+ activity1.syncRunOnUiThread(() -> activity1.mPreInput.requestFocus());
+ sReplier.getNextFillRequest();
+
+ // Fill field but don't finish session yet
+ activity1.syncRunOnUiThread(() -> {
+ activity1.mPreInput.setText("PRE");
+ });
+
+ // Trigger autofill on 2nd activity
+ final SimpleSaveActivity activity2 = startSimpleSaveActivity();
+ sReplier.addResponse(new CannedFillResponse.Builder()
+ .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_INPUT)
+ .build());
+ activity2.syncRunOnUiThread(() -> activity2.mInput.requestFocus());
+ sReplier.getNextFillRequest();
+
+ // Trigger save
+ activity2.syncRunOnUiThread(() -> {
+ activity2.mInput.setText("ID");
+ activity2.mCommit.performClick();
+ });
+ final UiObject2 saveUi = mUiBot.assertSaveShowing(SAVE_DATA_TYPE_PASSWORD);
+
+ // Save it...
+ mUiBot.saveForAutofill(saveUi, true);
+
+ // ... and assert results
+ final SaveRequest saveRequest = sReplier.getNextSaveRequest();
+
+ // Make sure both requests are available
+ assertThat(saveRequest.contexts).hasSize(2);
+
+ // Assert 1st request
+ final AssistStructure structure1 = saveRequest.contexts.get(0).getStructure();
+ assertWithMessage("no structure for 1st activity").that(structure1).isNotNull();
+ assertTextAndValue(findNodeByResourceId(structure1, ID_PRE_INPUT), "PRE");
+ assertThat(findNodeByResourceId(structure1, ID_INPUT)).isNull();
+ final ComponentName component1 = structure1.getActivityComponent();
+ assertThat(component1).isEqualTo(activity1.getComponentName());
+
+ // Assert 2nd request
+ final AssistStructure structure2 = saveRequest.contexts.get(1).getStructure();
+ assertWithMessage("no structure for 2nd activity").that(structure2).isNotNull();
+ assertThat(findNodeByResourceId(structure2, ID_PRE_INPUT)).isNull();
+ assertTextAndValue(findNodeByResourceId(structure2, ID_INPUT), "ID");
+ final ComponentName component2 = structure2.getActivityComponent();
+ assertThat(component2).isEqualTo(activity2.getComponentName());
+ }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/MultiScreenLoginTest.java b/tests/autofillservice/src/android/autofillservice/cts/MultiScreenLoginTest.java
index eaf60ba..ca0a2d2 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/MultiScreenLoginTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/MultiScreenLoginTest.java
@@ -21,6 +21,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.findAutofillIdByResourceId;
import static android.autofillservice.cts.Helper.findNodeByResourceId;
import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_PASSWORD;
import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_USERNAME;
@@ -40,7 +41,6 @@
import android.util.Log;
import android.view.autofill.AutofillId;
-import org.junit.Ignore;
import org.junit.Test;
import java.util.regex.Pattern;
@@ -321,7 +321,6 @@
saveBothFieldsCustomDescription(false);
}
- @Ignore("TODO(b/113593220): need new API to set context id")
@Test
public void testSaveBothFieldsCustomDescription_sameIds() throws Exception {
saveBothFieldsCustomDescription(true);
@@ -332,11 +331,11 @@
enableService();
// Set ids
- final AutofillId usernameId = mActivity.getUsernameAutofillId();
- final AutofillId passwordId = sameAutofillId ? usernameId
+ final AutofillId appUsernameId = mActivity.getUsernameAutofillId();
+ final AutofillId appPasswordId = sameAutofillId ? appUsernameId
: mActivity.getAutofillManager().getNextAutofillId();
- mActivity.setPasswordAutofillId(passwordId);
- Log.d(TAG, "usernameId: " + usernameId + ", passwordId: " + passwordId);
+ mActivity.setPasswordAutofillId(appPasswordId);
+ Log.d(TAG, "App: usernameId=" + appUsernameId + ", passwordId=" + appPasswordId);
// First handle username...
@@ -364,19 +363,32 @@
final PasswordOnlyActivity passwordActivity = AutofillTestWatcher
.getActivity(PasswordOnlyActivity.class);
+ // Must get AutofillIds from FillRequest, as they contain the proper session ids
+ final AutofillId svcUsernameId = findAutofillIdByResourceId(fillRequest1.contexts.get(0),
+ ID_USERNAME);
+ Log.d(TAG, "Service: usernameId=" + svcUsernameId);
// Set expectations.
- final CharSequenceTransformation usernameTrans =
- new CharSequenceTransformation.Builder(usernameId, MATCH_ALL, "$1").build();
- final CharSequenceTransformation passwordTrans =
- new CharSequenceTransformation.Builder(passwordId, MATCH_ALL, "$1").build();
sReplier.addResponse(new CannedFillResponse.Builder()
- .setRequiredSavableAutofillIds(SAVE_DATA_TYPE_USERNAME | SAVE_DATA_TYPE_PASSWORD,
- passwordId)
- .setCustomDescription(newCustomDescriptionWithUsernameAndPassword()
- .addChild(R.id.username, usernameTrans)
- .addChild(R.id.password, passwordTrans)
- .build())
+ .setVisitor((contexts, builder) -> {
+ final AutofillId svcPasswordId =
+ findAutofillIdByResourceId(contexts.get(1), ID_PASSWORD);
+ Log.d(TAG, "Service: passwordId=" + svcPasswordId);
+ final CharSequenceTransformation usernameTrans =
+ new CharSequenceTransformation.Builder(svcUsernameId, MATCH_ALL, "$1")
+ .build();
+ final CharSequenceTransformation passwordTrans =
+ new CharSequenceTransformation.Builder(svcPasswordId, MATCH_ALL, "$1")
+ .build();
+ builder.setSaveInfo(new SaveInfo.Builder(
+ SAVE_DATA_TYPE_USERNAME | SAVE_DATA_TYPE_PASSWORD,
+ new AutofillId[] {svcPasswordId})
+ .setCustomDescription(newCustomDescriptionWithUsernameAndPassword()
+ .addChild(R.id.username, usernameTrans)
+ .addChild(R.id.password, passwordTrans)
+ .build())
+ .build());
+ })
.build());
// Trigger autofill
diff --git a/tests/autofillservice/src/android/autofillservice/cts/MutableAutofillIdTest.java b/tests/autofillservice/src/android/autofillservice/cts/MutableAutofillIdTest.java
index f9b57f1..8d63fcd 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/MutableAutofillIdTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/MutableAutofillIdTest.java
@@ -18,8 +18,9 @@
import static android.autofillservice.cts.GridActivity.ID_L1C1;
import static android.autofillservice.cts.GridActivity.ID_L1C2;
+import static android.autofillservice.cts.Helper.assertEqualsIgnoreSession;
import static android.autofillservice.cts.Helper.assertTextIsSanitized;
-import static android.autofillservice.cts.Helper.findNodeByAutofillId;
+import static android.autofillservice.cts.Helper.findNodeByResourceId;
import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_GENERIC;
import static com.google.common.truth.Truth.assertThat;
@@ -55,14 +56,12 @@
final EditText field1 = mActivity.getCell(1, 1);
final AutofillId oldIdField1 = field1.getAutofillId();
- final EditText field2 = mActivity.getCell(1, 2);
- final AutofillId idField2 = field2.getAutofillId();
// Prepare response
final CannedFillResponse response1 = new CannedFillResponse.Builder()
.addDataset(new CannedDataset.Builder()
- .setField(oldIdField1, "l1c1", createPresentation("l1c1"))
- .setField(idField2, "l1c2", createPresentation("l1c2"))
+ .setField(ID_L1C1, "l1c1", createPresentation("l1c1"))
+ .setField(ID_L1C2, "l1c2", createPresentation("l1c2"))
.build())
.build();
sReplier.addResponse(response1);
@@ -71,7 +70,7 @@
focusCell(1, 1);
final FillRequest fillRequest1 = sReplier.getNextFillRequest();
final ViewNode node1Request1 = assertTextIsSanitized(fillRequest1.structure, ID_L1C1);
- assertThat(node1Request1.getAutofillId()).isEqualTo(oldIdField1);
+ assertEqualsIgnoreSession(node1Request1.getAutofillId(), oldIdField1);
mUiBot.assertDatasets("l1c1");
// Make sure 2nd field shows picker
@@ -101,7 +100,7 @@
// Trigger another request because 1st view has a different id. Service will ignore it now.
final CannedFillResponse response2 = new CannedFillResponse.Builder()
.addDataset(new CannedDataset.Builder()
- .setField(idField2, "l1c2", createPresentation("l1c2"))
+ .setField(ID_L1C2, "l1c2", createPresentation("l1c2"))
.build())
.build();
sReplier.addResponse(response2);
@@ -113,7 +112,7 @@
final FillRequest fillRequest2 = sReplier.getNextFillRequest();
final ViewNode node1Request2 = assertTextIsSanitized(fillRequest2.structure, ID_L1C1);
// Make sure node has new id.
- assertThat(node1Request2.getAutofillId()).isEqualTo(newIdField1);
+ assertEqualsIgnoreSession(node1Request2.getAutofillId(), newIdField1);
mUiBot.assertNoDatasets();
// Make sure 2nd field shows picker
@@ -133,14 +132,12 @@
final EditText field1 = mActivity.getCell(1, 1);
final AutofillId oldIdField1 = field1.getAutofillId();
- final EditText field2 = mActivity.getCell(1, 2);
- final AutofillId idField2 = field2.getAutofillId();
// Prepare response
final CannedFillResponse response1 = new CannedFillResponse.Builder()
.addDataset(new CannedDataset.Builder()
- .setField(oldIdField1, "l1c1", createPresentation("l1c1"))
- .setField(idField2, "l1c2", createPresentation("l1c2"))
+ .setField(ID_L1C1, "l1c1", createPresentation("l1c1"))
+ .setField(ID_L1C2, "l1c2", createPresentation("l1c2"))
.build())
.build();
sReplier.addResponse(response1);
@@ -149,7 +146,7 @@
focusCell(1, 1);
final FillRequest fillRequest1 = sReplier.getNextFillRequest();
final ViewNode node1Request1 = assertTextIsSanitized(fillRequest1.structure, ID_L1C1);
- assertThat(node1Request1.getAutofillId()).isEqualTo(oldIdField1);
+ assertEqualsIgnoreSession(node1Request1.getAutofillId(), oldIdField1);
mUiBot.assertDatasets("l1c1");
// Make sure 2nd field shows picker
@@ -162,7 +159,7 @@
// Change id
mActivity.removeCell(1, 1);
field1.setAutofillId(newIdField1);
- assertThat(field1.getAutofillId()).isEqualTo(newIdField1);
+ assertEqualsIgnoreSession(field1.getAutofillId(), newIdField1);
Log.d(TAG, "Changed id of " + ID_L1C1 + " from " + oldIdField1 + " to " + newIdField1);
// Autofill it - just 2nd field should be autofilled
@@ -199,12 +196,10 @@
final EditText field1 = mActivity.getCell(1, 1);
final AutofillId oldIdField1 = field1.getAutofillId();
- final EditText field2 = mActivity.getCell(1, 2);
- final AutofillId idField2 = field2.getAutofillId();
// Prepare response
final CannedFillResponse response1 = new CannedFillResponse.Builder()
- .setRequiredSavableAutofillIds(SAVE_DATA_TYPE_GENERIC, oldIdField1, idField2)
+ .setRequiredSavableIds(SAVE_DATA_TYPE_GENERIC, ID_L1C1, ID_L1C2)
.build();
sReplier.addResponse(response1);
@@ -212,7 +207,7 @@
mActivity.focusCell(1, 1); // No window change because it's not showing dataset picker.
final FillRequest fillRequest1 = sReplier.getNextFillRequest();
final ViewNode node1Request1 = assertTextIsSanitized(fillRequest1.structure, ID_L1C1);
- assertThat(node1Request1.getAutofillId()).isEqualTo(oldIdField1);
+ assertEqualsIgnoreSession(node1Request1.getAutofillId(), oldIdField1);
mUiBot.assertNoDatasetsEver();
// Make sure 2nd field doesn't trigger a new request
@@ -232,10 +227,10 @@
final CannedFillResponse.Builder response2 = new CannedFillResponse.Builder();
if (serviceIgnoresNewId) {
// ... and service will ignore it now.
- response2.setRequiredSavableAutofillIds(SAVE_DATA_TYPE_GENERIC, idField2);
+ response2.setRequiredSavableIds(SAVE_DATA_TYPE_GENERIC, ID_L1C2);
} else {
// ..but service is still expecting the old id.
- response2.setRequiredSavableAutofillIds(SAVE_DATA_TYPE_GENERIC, oldIdField1, idField2);
+ response2.setRequiredSavableIds(SAVE_DATA_TYPE_GENERIC, ID_L1C1, ID_L1C2);
}
sReplier.addResponse(response2.build());
@@ -244,7 +239,7 @@
final FillRequest fillRequest2 = sReplier.getNextFillRequest();
final ViewNode node1Request2 = assertTextIsSanitized(fillRequest2.structure, ID_L1C1);
// Make sure node has new id.
- assertThat(node1Request2.getAutofillId()).isEqualTo(newIdField1);
+ assertEqualsIgnoreSession(node1Request2.getAutofillId(), newIdField1);
mUiBot.assertNoDatasetsEver();
// Now triggers save
@@ -260,33 +255,23 @@
// Assert 1st context
final AssistStructure structure1 = contexts.get(0).getStructure();
- final ViewNode oldNode1Context1 = findNodeByAutofillId(structure1, oldIdField1);
- assertThat(oldNode1Context1).isNotNull();
- assertThat(oldNode1Context1.getIdEntry()).isEqualTo(ID_L1C1);
- assertThat(oldNode1Context1.getText().toString()).isEqualTo("OLD");
+ final ViewNode newNode1Context1 = findNodeByResourceId(structure1, ID_L1C1);
+ assertThat(newNode1Context1).isNotNull();
+ assertThat(newNode1Context1.getText().toString()).isEqualTo("OLD");
- final ViewNode newNode1Context1 = findNodeByAutofillId(structure1, newIdField1);
- assertThat(newNode1Context1).isNull();
-
- final ViewNode node2Context1 = findNodeByAutofillId(structure1, idField2);
+ final ViewNode node2Context1 = findNodeByResourceId(structure1, ID_L1C2);
assertThat(node2Context1).isNotNull();
- assertThat(node2Context1.getIdEntry()).isEqualTo(ID_L1C2);
assertThat(node2Context1.getText().toString()).isEqualTo("NOD2");
// Assert 2nd context
final AssistStructure structure2 = contexts.get(1).getStructure();
- final ViewNode oldNode1Context2 = findNodeByAutofillId(structure2, oldIdField1);
- assertThat(oldNode1Context2).isNull();
-
- final ViewNode newNode1Context2 = findNodeByAutofillId(structure2, newIdField1);
+ final ViewNode newNode1Context2 = findNodeByResourceId(structure2, ID_L1C1);
assertThat(newNode1Context2).isNotNull();
- assertThat(newNode1Context2.getIdEntry()).isEqualTo(ID_L1C1);
assertThat(newNode1Context2.getText().toString()).isEqualTo("NEW");
- final ViewNode node2Context2 = findNodeByAutofillId(structure2, idField2);
+ final ViewNode node2Context2 = findNodeByResourceId(structure2, ID_L1C2);
assertThat(node2Context2).isNotNull();
- assertThat(node2Context2.getIdEntry()).isEqualTo(ID_L1C2);
assertThat(node2Context2.getText().toString()).isEqualTo("NOD2");
}
}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/MyAutofillCallback.java b/tests/autofillservice/src/android/autofillservice/cts/MyAutofillCallback.java
index 65ce0e3..7a09dbf 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/MyAutofillCallback.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/MyAutofillCallback.java
@@ -38,7 +38,7 @@
/**
* Custom {@link AutofillCallback} used to recover events during tests.
*/
-final class MyAutofillCallback extends AutofillCallback {
+public final class MyAutofillCallback extends AutofillCallback {
private static final String TAG = "MyAutofillCallback";
private final BlockingQueue<MyEvent> mEvents = new LinkedBlockingQueue<>();
@@ -77,7 +77,7 @@
/**
* Gets the next available event or fail if it times out.
*/
- MyEvent getEvent() throws InterruptedException {
+ public MyEvent getEvent() throws InterruptedException {
final MyEvent event = mEvents.poll(CONNECTION_TIMEOUT.ms(), TimeUnit.MILLISECONDS);
if (event == null) {
throw new RetryableException(CONNECTION_TIMEOUT, "no event");
@@ -88,7 +88,7 @@
/**
* Assert no more events were received.
*/
- void assertNotCalled() throws InterruptedException {
+ public void assertNotCalled() throws InterruptedException {
final MyEvent event = mEvents.poll(CALLBACK_NOT_CALLED_TIMEOUT_MS, TimeUnit.MILLISECONDS);
if (event != null) {
// Not retryable.
@@ -99,7 +99,7 @@
/**
* Used to assert there is no event left behind.
*/
- void assertNumberUnhandledEvents(int expected) {
+ public void assertNumberUnhandledEvents(int expected) {
assertWithMessage("Invalid number of events left: %s", mEvents).that(mEvents.size())
.isEqualTo(expected);
}
@@ -107,7 +107,7 @@
/**
* Convenience method to assert an UI shown event for the given view was received.
*/
- MyEvent assertUiShownEvent(View expectedView) throws InterruptedException {
+ public MyEvent assertUiShownEvent(View expectedView) throws InterruptedException {
final MyEvent event = getEvent();
assertWithMessage("Invalid type on event %s", event).that(event.event)
.isEqualTo(EVENT_INPUT_SHOWN);
@@ -119,7 +119,8 @@
/**
* Convenience method to assert an UI shown event for the given virtual view was received.
*/
- void assertUiShownEvent(View expectedView, int expectedChildId) throws InterruptedException {
+ public void assertUiShownEvent(View expectedView, int expectedChildId)
+ throws InterruptedException {
final MyEvent event = assertUiShownEvent(expectedView);
assertWithMessage("Invalid child on event %s", event).that(event.childId)
.isEqualTo(expectedChildId);
@@ -130,7 +131,7 @@
*
* @return virtual child id
*/
- int assertUiShownEventForVirtualChild(View expectedView) throws InterruptedException {
+ public int assertUiShownEventForVirtualChild(View expectedView) throws InterruptedException {
final MyEvent event = assertUiShownEvent(expectedView);
return event.childId;
}
@@ -138,7 +139,7 @@
/**
* Convenience method to assert an UI hidden event for the given view was received.
*/
- MyEvent assertUiHiddenEvent(View expectedView) throws InterruptedException {
+ public MyEvent assertUiHiddenEvent(View expectedView) throws InterruptedException {
final MyEvent event = getEvent();
assertWithMessage("Invalid type on event %s", event).that(event.event)
.isEqualTo(EVENT_INPUT_HIDDEN);
@@ -150,7 +151,8 @@
/**
* Convenience method to assert an UI hidden event for the given view was received.
*/
- void assertUiHiddenEvent(View expectedView, int expectedChildId) throws InterruptedException {
+ public void assertUiHiddenEvent(View expectedView, int expectedChildId)
+ throws InterruptedException {
final MyEvent event = assertUiHiddenEvent(expectedView);
assertWithMessage("Invalid child on event %s", event).that(event.childId)
.isEqualTo(expectedChildId);
@@ -159,7 +161,7 @@
/**
* Convenience method to assert an UI unavailable event for the given view was received.
*/
- MyEvent assertUiUnavailableEvent(View expectedView) throws InterruptedException {
+ public MyEvent assertUiUnavailableEvent(View expectedView) throws InterruptedException {
final MyEvent event = getEvent();
assertWithMessage("Invalid type on event %s", event).that(event.event)
.isEqualTo(EVENT_INPUT_UNAVAILABLE);
@@ -171,7 +173,7 @@
/**
* Convenience method to assert an UI unavailable event for the given view was received.
*/
- void assertUiUnavailableEvent(View expectedView, int expectedChildId)
+ public void assertUiUnavailableEvent(View expectedView, int expectedChildId)
throws InterruptedException {
final MyEvent event = assertUiUnavailableEvent(expectedView);
assertWithMessage("Invalid child on event %s", event).that(event.childId)
diff --git a/tests/autofillservice/src/android/autofillservice/cts/OnClickActionTest.java b/tests/autofillservice/src/android/autofillservice/cts/OnClickActionTest.java
index 0ebd0a4..c65f78c5 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/OnClickActionTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/OnClickActionTest.java
@@ -25,6 +25,7 @@
import static android.autofillservice.cts.Helper.ID_PASSWORD_LABEL;
import static android.autofillservice.cts.Helper.ID_USERNAME_LABEL;
import static android.autofillservice.cts.Helper.assertTextAndValue;
+import static android.autofillservice.cts.Helper.findAutofillIdByResourceId;
import static android.autofillservice.cts.Helper.findNodeByResourceId;
import static android.autofillservice.cts.SimpleSaveActivity.ID_INPUT;
import static android.autofillservice.cts.SimpleSaveActivity.ID_PASSWORD;
@@ -33,6 +34,7 @@
import android.autofillservice.cts.InstrumentedAutoFillService.SaveRequest;
import android.platform.test.annotations.AppModeFull;
import android.service.autofill.CharSequenceTransformation;
+import android.service.autofill.FillContext;
import android.service.autofill.OnClickAction;
import android.service.autofill.VisibilitySetterAction;
import android.support.test.uiautomator.UiObject2;
@@ -70,36 +72,38 @@
enableService();
// Set expectations.
- final AutofillId usernameId = mActivity.mInput.getAutofillId();
- final AutofillId passwordId = mActivity.mPassword.getAutofillId();
-
- final CharSequenceTransformation usernameTrans =
- new CharSequenceTransformation.Builder(usernameId, MATCH_ALL, "$1").build();
- final CharSequenceTransformation passwordTrans =
- new CharSequenceTransformation.Builder(passwordId, MATCH_ALL, "$1").build();
-
sReplier.addResponse(new CannedFillResponse.Builder()
.setRequiredSavableIds(SAVE_DATA_TYPE_GENERIC, ID_INPUT, ID_PASSWORD)
- .setCustomDescription(newCustomDescriptionWithHiddenFields()
- .addChild(R.id.username_plain, usernameTrans)
- .addChild(R.id.password_plain, passwordTrans)
- .addOnClickAction(R.id.show, new VisibilitySetterAction
- .Builder(R.id.hide, View.VISIBLE)
- .setVisibility(R.id.show, View.GONE)
- .setVisibility(R.id.username_plain, View.VISIBLE)
- .setVisibility(R.id.password_plain, View.VISIBLE)
- .setVisibility(R.id.username_masked, View.GONE)
- .setVisibility(R.id.password_masked, View.GONE)
- .build())
- .addOnClickAction(R.id.hide, new VisibilitySetterAction
- .Builder(R.id.show, View.VISIBLE)
- .setVisibility(R.id.hide, View.GONE)
- .setVisibility(R.id.username_masked, View.VISIBLE)
- .setVisibility(R.id.password_masked, View.VISIBLE)
- .setVisibility(R.id.username_plain, View.GONE)
- .setVisibility(R.id.password_plain, View.GONE)
- .build())
- .build())
+ .setSaveInfoVisitor((contexts, builder) -> {
+ final FillContext context = contexts.get(0);
+ final AutofillId usernameId = findAutofillIdByResourceId(context, ID_INPUT);
+ final AutofillId passwordId = findAutofillIdByResourceId(context, ID_PASSWORD);
+
+ final CharSequenceTransformation usernameTrans = new CharSequenceTransformation
+ .Builder(usernameId, MATCH_ALL, "$1").build();
+ final CharSequenceTransformation passwordTrans = new CharSequenceTransformation
+ .Builder(passwordId, MATCH_ALL, "$1").build();
+ builder.setCustomDescription(newCustomDescriptionWithHiddenFields()
+ .addChild(R.id.username_plain, usernameTrans)
+ .addChild(R.id.password_plain, passwordTrans)
+ .addOnClickAction(R.id.show, new VisibilitySetterAction
+ .Builder(R.id.hide, View.VISIBLE)
+ .setVisibility(R.id.show, View.GONE)
+ .setVisibility(R.id.username_plain, View.VISIBLE)
+ .setVisibility(R.id.password_plain, View.VISIBLE)
+ .setVisibility(R.id.username_masked, View.GONE)
+ .setVisibility(R.id.password_masked, View.GONE)
+ .build())
+ .addOnClickAction(R.id.hide, new VisibilitySetterAction
+ .Builder(R.id.show, View.VISIBLE)
+ .setVisibility(R.id.hide, View.GONE)
+ .setVisibility(R.id.username_masked, View.VISIBLE)
+ .setVisibility(R.id.password_masked, View.VISIBLE)
+ .setVisibility(R.id.username_plain, View.GONE)
+ .setVisibility(R.id.password_plain, View.GONE)
+ .build())
+ .build());
+ })
.build());
// Trigger autofill.
diff --git a/tests/autofillservice/src/android/autofillservice/cts/PreSimpleSaveActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/PreSimpleSaveActivityTest.java
index 244d72f..2b368bb 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/PreSimpleSaveActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/PreSimpleSaveActivityTest.java
@@ -17,6 +17,7 @@
import static android.autofillservice.cts.Helper.ID_STATIC_TEXT;
import static android.autofillservice.cts.Helper.assertTextAndValue;
+import static android.autofillservice.cts.Helper.findAutofillIdByResourceId;
import static android.autofillservice.cts.Helper.findNodeByResourceId;
import static android.autofillservice.cts.LoginActivity.ID_USERNAME_CONTAINER;
import static android.autofillservice.cts.PreSimpleSaveActivity.ID_PRE_INPUT;
@@ -35,6 +36,7 @@
import android.support.test.uiautomator.By;
import android.support.test.uiautomator.UiObject2;
import android.view.View;
+import android.view.autofill.AutofillId;
import android.widget.RemoteViews;
import java.util.regex.Pattern;
@@ -63,8 +65,9 @@
// Set expectations.
sReplier.addResponse(new CannedFillResponse.Builder()
- .setCustomDescription(newCustomDescription(WelcomeActivity.class))
.setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_PRE_INPUT)
+ .setSaveInfoVisitor((contexts, builder) -> builder
+ .setCustomDescription(newCustomDescription(WelcomeActivity.class)))
.build());
// Trigger autofill.
@@ -121,8 +124,9 @@
// Set expectations.
sReplier.addResponse(new CannedFillResponse.Builder()
- .setCustomDescription(newCustomDescription(WelcomeActivity.class))
.setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_PRE_INPUT)
+ .setSaveInfoVisitor((contexts, builder) -> builder
+ .setCustomDescription(newCustomDescription(WelcomeActivity.class)))
.build());
// Trigger autofill.
@@ -213,8 +217,9 @@
// Set expectations.
sReplier.addResponse(new CannedFillResponse.Builder()
- .setCustomDescription(newCustomDescription(WelcomeActivity.class))
.setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_PRE_INPUT)
+ .setSaveInfoVisitor((contexts, builder) -> builder
+ .setCustomDescription(newCustomDescription(WelcomeActivity.class)))
.build());
// Trigger autofill.
@@ -270,8 +275,9 @@
// Set expectations.
sReplier.addResponse(new CannedFillResponse.Builder()
- .setCustomDescription(newCustomDescription(TrampolineWelcomeActivity.class))
.setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_PRE_INPUT)
+ .setSaveInfoVisitor((contexts, builder) -> builder.setCustomDescription(
+ newCustomDescription(TrampolineWelcomeActivity.class)))
.build());
// Trigger autofill.
@@ -334,23 +340,24 @@
// Set service.
enableService();
- final CustomDescription.Builder customDescription =
- newCustomDescriptionBuilder(WelcomeActivity.class);
- final RemoteViews update = newTemplate();
- if (updateLinkView) {
- update.setCharSequence(R.id.link, "setText", "TAP ME IF YOU CAN");
- } else {
- update.setCharSequence(R.id.static_text, "setText", "ME!");
- }
- Validator validCondition = new RegexValidator(mActivity.mPreInput.getAutofillId(),
- Pattern.compile(".*"));
- customDescription.batchUpdate(validCondition,
- new BatchUpdates.Builder().updateTemplate(update).build());
-
// Set expectations.
sReplier.addResponse(new CannedFillResponse.Builder()
- .setCustomDescription(customDescription.build())
.setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_PRE_INPUT)
+ .setSaveInfoVisitor((contexts, builder) -> {
+ final CustomDescription.Builder customDescription =
+ newCustomDescriptionBuilder(WelcomeActivity.class);
+ final RemoteViews update = newTemplate();
+ if (updateLinkView) {
+ update.setCharSequence(R.id.link, "setText", "TAP ME IF YOU CAN");
+ } else {
+ update.setCharSequence(R.id.static_text, "setText", "ME!");
+ }
+ final AutofillId id = findAutofillIdByResourceId(contexts.get(0), ID_PRE_INPUT);
+ final Validator validCondition = new RegexValidator(id, Pattern.compile(".*"));
+ customDescription.batchUpdate(validCondition,
+ new BatchUpdates.Builder().updateTemplate(update).build());
+ builder.setCustomDescription(customDescription.build());
+ })
.build());
// Trigger autofill.
diff --git a/tests/autofillservice/src/android/autofillservice/cts/RegexValidatorTest.java b/tests/autofillservice/src/android/autofillservice/cts/RegexValidatorTest.java
index a000cf0..7802c56 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/RegexValidatorTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/RegexValidatorTest.java
@@ -27,7 +27,7 @@
import android.service.autofill.ValueFinder;
import android.view.autofill.AutofillId;
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/tests/autofillservice/src/android/autofillservice/cts/SaveInfoTest.java b/tests/autofillservice/src/android/autofillservice/cts/SaveInfoTest.java
index 0e6364a..43ce97a 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/SaveInfoTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/SaveInfoTest.java
@@ -31,7 +31,7 @@
import android.service.autofill.SaveInfo;
import android.view.autofill.AutofillId;
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/tests/autofillservice/src/android/autofillservice/cts/SimpleSaveActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/SimpleSaveActivityTest.java
index cec472e..14bec9f 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/SimpleSaveActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/SimpleSaveActivityTest.java
@@ -20,6 +20,7 @@
import static android.autofillservice.cts.Helper.LARGE_STRING;
import static android.autofillservice.cts.Helper.assertTextAndValue;
import static android.autofillservice.cts.Helper.assertTextValue;
+import static android.autofillservice.cts.Helper.findAutofillIdByResourceId;
import static android.autofillservice.cts.Helper.findNodeByResourceId;
import static android.autofillservice.cts.LoginActivity.ID_USERNAME_CONTAINER;
import static android.autofillservice.cts.SimpleSaveActivity.ID_COMMIT;
@@ -48,6 +49,7 @@
import android.platform.test.annotations.AppModeFull;
import android.service.autofill.BatchUpdates;
import android.service.autofill.CustomDescription;
+import android.service.autofill.FillContext;
import android.service.autofill.FillEventHistory;
import android.service.autofill.RegexValidator;
import android.service.autofill.SaveInfo;
@@ -398,8 +400,16 @@
// Set expectations.
sReplier.addResponse(new CannedFillResponse.Builder()
- .setRequiredSavableIds(SAVE_DATA_TYPE_USERNAME | SAVE_DATA_TYPE_PASSWORD,
- ID_PASSWORD)
+ // Must explicitly set visitor, otherwise setRequiredSavableIds() would get the
+ // id from the 1st context
+ .setVisitor((contexts, builder) -> {
+ final AutofillId passwordId =
+ findAutofillIdByResourceId(contexts.get(1), ID_PASSWORD);
+ builder.setSaveInfo(new SaveInfo.Builder(
+ SAVE_DATA_TYPE_USERNAME | SAVE_DATA_TYPE_PASSWORD,
+ new AutofillId[] {passwordId})
+ .build());
+ })
.build());
// Trigger autofill on second "fragment"
@@ -747,8 +757,9 @@
// Set expectations.
sReplier.addResponse(new CannedFillResponse.Builder()
- .setCustomDescription(newCustomDescription(WelcomeActivity.class))
.setRequiredSavableIds(SAVE_DATA_TYPE_GENERIC, ID_INPUT)
+ .setSaveInfoVisitor((contexts, builder) -> builder
+ .setCustomDescription(newCustomDescription(WelcomeActivity.class)))
.build());
// Trigger autofill.
@@ -815,8 +826,9 @@
// Set expectations.
sReplier.addResponse(new CannedFillResponse.Builder()
- .setCustomDescription(newCustomDescription(WelcomeActivity.class))
.setRequiredSavableIds(SAVE_DATA_TYPE_GENERIC, ID_INPUT)
+ .setSaveInfoVisitor((contexts, builder) -> builder
+ .setCustomDescription(newCustomDescription(WelcomeActivity.class)))
.build());
// Trigger autofill.
@@ -900,8 +912,9 @@
// Set expectations.
sReplier.addResponse(new CannedFillResponse.Builder()
- .setCustomDescription(newCustomDescription(WelcomeActivity.class))
.setRequiredSavableIds(SAVE_DATA_TYPE_GENERIC, ID_INPUT)
+ .setSaveInfoVisitor((contexts, builder) -> builder
+ .setCustomDescription(newCustomDescription(WelcomeActivity.class)))
.build());
// Trigger autofill.
@@ -1013,8 +1026,10 @@
// Set expectations.
sReplier.addResponse(new CannedFillResponse.Builder()
- .setCustomDescription(newCustomDescription(TrampolineWelcomeActivity.class))
.setRequiredSavableIds(SAVE_DATA_TYPE_GENERIC, ID_INPUT)
+ .setSaveInfoVisitor((contexts, builder) -> builder
+ .setCustomDescription(
+ newCustomDescription(TrampolineWelcomeActivity.class)))
.build());
// Trigger autofill.
@@ -1071,11 +1086,15 @@
enableService();
// Set expectations.
- final AutofillId inputId = mActivity.mInput.getAutofillId();
- final AutofillId passwordId = mActivity.mPassword.getAutofillId();
sReplier.addResponse(new CannedFillResponse.Builder()
.setRequiredSavableIds(SAVE_DATA_TYPE_GENERIC, ID_INPUT)
- .addSanitizer(new TextValueSanitizer(TRIMMER_PATTERN, "$1"), inputId, passwordId)
+ .setSaveInfoVisitor((contexts, builder) -> {
+ final FillContext context = contexts.get(0);
+ final AutofillId inputId = findAutofillIdByResourceId(context, ID_INPUT);
+ final AutofillId passwordId = findAutofillIdByResourceId(context, ID_PASSWORD);
+ builder.addSanitizer(new TextValueSanitizer(TRIMMER_PATTERN, "$1"), inputId,
+ passwordId);
+ })
.build());
// Trigger autofill.
@@ -1107,12 +1126,16 @@
enableService();
// Set expectations.
- final AutofillId inputId = mActivity.mInput.getAutofillId();
- final AutofillId passwordId = mActivity.mPassword.getAutofillId();
sReplier.addResponse(new CannedFillResponse.Builder()
.setRequiredSavableIds(SAVE_DATA_TYPE_GENERIC, ID_INPUT)
.setOptionalSavableIds(ID_PASSWORD)
- .addSanitizer(new TextValueSanitizer(TRIMMER_PATTERN, "$1"), inputId, passwordId)
+ .setSaveInfoVisitor((contexts, builder) -> {
+ final FillContext context = contexts.get(0);
+ final AutofillId inputId = findAutofillIdByResourceId(context, ID_INPUT);
+ final AutofillId passwordId = findAutofillIdByResourceId(context, ID_PASSWORD);
+ builder.addSanitizer(new TextValueSanitizer(TRIMMER_PATTERN, "$1"), inputId,
+ passwordId);
+ })
.build());
// Trigger autofill.
@@ -1149,11 +1172,15 @@
enableService();
// Set expectations.
- final AutofillId inputId = mActivity.mInput.getAutofillId();
- final AutofillId passwordId = mActivity.mPassword.getAutofillId();
sReplier.addResponse(new CannedFillResponse.Builder()
.setRequiredSavableIds(SAVE_DATA_TYPE_GENERIC, ID_INPUT, ID_PASSWORD)
- .addSanitizer(new TextValueSanitizer(TRIMMER_PATTERN, "$1"), inputId, passwordId)
+ .setSaveInfoVisitor((contexts, builder) -> {
+ final FillContext context = contexts.get(0);
+ final AutofillId inputId = findAutofillIdByResourceId(context, ID_INPUT);
+ final AutofillId passwordId = findAutofillIdByResourceId(context, ID_PASSWORD);
+ builder.addSanitizer(new TextValueSanitizer(TRIMMER_PATTERN, "$1"), inputId,
+ passwordId);
+ })
.addDataset(new CannedDataset.Builder()
.setField(ID_INPUT, "id")
.setField(ID_PASSWORD, "pass")
@@ -1183,11 +1210,15 @@
enableService();
// Set expectations.
- final AutofillId passwordId = mActivity.mPassword.getAutofillId();
sReplier.addResponse(new CannedFillResponse.Builder()
.setRequiredSavableIds(SAVE_DATA_TYPE_GENERIC, ID_INPUT)
.setOptionalSavableIds(ID_PASSWORD)
- .addSanitizer(new TextValueSanitizer(Pattern.compile("(pass) "), "$1"), passwordId)
+ .setSaveInfoVisitor((contexts, builder) -> {
+ final FillContext context = contexts.get(0);
+ final AutofillId passwordId = findAutofillIdByResourceId(context, ID_PASSWORD);
+ builder.addSanitizer(new TextValueSanitizer(Pattern.compile("(pass) "), "$1"),
+ passwordId);
+ })
.addDataset(new CannedDataset.Builder()
.setField(ID_INPUT, "id")
.setField(ID_PASSWORD, "pass")
@@ -1217,12 +1248,15 @@
enableService();
// Set expectations.
- final AutofillId inputId = mActivity.mInput.getAutofillId();
- final AutofillId passwordId = mActivity.mPassword.getAutofillId();
sReplier.addResponse(new CannedFillResponse.Builder()
.setRequiredSavableIds(SAVE_DATA_TYPE_GENERIC, ID_INPUT, ID_PASSWORD)
- .addSanitizer(new TextValueSanitizer(Pattern.compile("dude"), "$1"),
- inputId, passwordId)
+ .setSaveInfoVisitor((contexts, builder) -> {
+ final FillContext context = contexts.get(0);
+ final AutofillId inputId = findAutofillIdByResourceId(context, ID_INPUT);
+ final AutofillId passwordId = findAutofillIdByResourceId(context, ID_PASSWORD);
+ builder.addSanitizer(new TextValueSanitizer(Pattern.compile("dude"), "$1"),
+ inputId, passwordId);
+ })
.addDataset(new CannedDataset.Builder()
.setField(ID_INPUT, "#id#")
.setField(ID_PASSWORD, "#pass#")
@@ -1252,13 +1286,17 @@
enableService();
// Set expectations.
- final AutofillId inputId = mActivity.mInput.getAutofillId();
- final AutofillId passwordId = mActivity.mPassword.getAutofillId();
sReplier.addResponse(new CannedFillResponse.Builder()
.setRequiredSavableIds(SAVE_DATA_TYPE_GENERIC, ID_INPUT)
.setOptionalSavableIds(ID_PASSWORD)
- .addSanitizer(new TextValueSanitizer(Pattern.compile("dude"), "$1"),
- inputId, passwordId)
+ .setSaveInfoVisitor((contexts, builder) -> {
+ final FillContext context = contexts.get(0);
+ final AutofillId inputId = findAutofillIdByResourceId(context, ID_INPUT);
+ final AutofillId passwordId = findAutofillIdByResourceId(context, ID_PASSWORD);
+ builder.addSanitizer(new TextValueSanitizer(Pattern.compile("dude"), "$1"),
+ inputId, passwordId);
+
+ })
.addDataset(new CannedDataset.Builder()
.setField(ID_INPUT, "id")
.setField(ID_PASSWORD, "#pass#")
@@ -1391,21 +1429,24 @@
enableService();
// Set expectations.
- final CustomDescription.Builder customDescription =
- newCustomDescriptionBuilder(WelcomeActivity.class);
- final RemoteViews update = newTemplate();
- if (updateLinkView) {
- update.setCharSequence(R.id.link, "setText", "TAP ME IF YOU CAN");
- } else {
- update.setCharSequence(R.id.static_text, "setText", "ME!");
- }
- Validator validCondition = new RegexValidator(mActivity.mInput.getAutofillId(),
- Pattern.compile(".*"));
- customDescription.batchUpdate(validCondition,
- new BatchUpdates.Builder().updateTemplate(update).build());
-
sReplier.addResponse(new CannedFillResponse.Builder()
- .setCustomDescription(customDescription.build())
+ .setSaveInfoVisitor((contexts, builder) -> {
+ // Set response with custom description
+ final AutofillId id = findAutofillIdByResourceId(contexts.get(0), ID_INPUT);
+ final CustomDescription.Builder customDescription =
+ newCustomDescriptionBuilder(WelcomeActivity.class);
+ final RemoteViews update = newTemplate();
+ if (updateLinkView) {
+ update.setCharSequence(R.id.link, "setText", "TAP ME IF YOU CAN");
+ } else {
+ update.setCharSequence(R.id.static_text, "setText", "ME!");
+ }
+ Validator validCondition = new RegexValidator(id, Pattern.compile(".*"));
+ customDescription.batchUpdate(validCondition,
+ new BatchUpdates.Builder().updateTemplate(update).build());
+
+ builder.setCustomDescription(customDescription.build());
+ })
.setRequiredSavableIds(SAVE_DATA_TYPE_GENERIC, ID_INPUT)
.build());
diff --git a/tests/autofillservice/src/android/autofillservice/cts/TextValueSanitizerTest.java b/tests/autofillservice/src/android/autofillservice/cts/TextValueSanitizerTest.java
index a89fbde..dbe072c 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/TextValueSanitizerTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/TextValueSanitizerTest.java
@@ -24,7 +24,7 @@
import android.service.autofill.TextValueSanitizer;
import android.view.autofill.AutofillValue;
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/tests/autofillservice/src/android/autofillservice/cts/UiBot.java b/tests/autofillservice/src/android/autofillservice/cts/UiBot.java
index ad3ef46..40a9589 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/UiBot.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/UiBot.java
@@ -56,7 +56,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import androidx.test.InstrumentationRegistry;
+import androidx.test.platform.app.InstrumentationRegistry;
import com.android.compatibility.common.util.RetryableException;
import com.android.compatibility.common.util.Timeout;
@@ -1043,15 +1043,22 @@
public void assertChild(@NonNull UiObject2 parent, @NonNull String childId,
@Nullable Visitor<UiObject2> assertion) {
final UiObject2 child = parent.findObject(By.res(mPackageName, childId));
- if (assertion != null) {
- assertWithMessage("Didn't find child with id '%s'", childId).that(child).isNotNull();
- try {
- assertion.visit(child);
- } catch (Throwable t) {
- throw new AssertionError("Error on child '" + childId + "'", t);
+ try {
+ if (assertion != null) {
+ assertWithMessage("Didn't find child with id '%s'", childId).that(child)
+ .isNotNull();
+ try {
+ assertion.visit(child);
+ } catch (Throwable t) {
+ throw new AssertionError("Error on child '" + childId + "'", t);
+ }
+ } else {
+ assertWithMessage("Shouldn't find child with id '%s'", childId).that(child)
+ .isNull();
}
- } else {
- assertWithMessage("Shouldn't find child with id '%s'", childId).that(child).isNull();
+ } catch (RuntimeException | Error e) {
+ dumpScreen("assertChild(" + childId + ") failed: " + e);
+ throw e;
}
}
@@ -1068,10 +1075,4 @@
SystemClock.sleep(timeout);
}
}
-
- private boolean getBoolean(String id) {
- final Resources resources = mContext.getResources();
- final int booleanId = resources.getIdentifier(id, "bool", "android");
- return resources.getBoolean(booleanId);
- }
}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/UserDataTest.java b/tests/autofillservice/src/android/autofillservice/cts/UserDataTest.java
index cfc43cc..d93151d 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/UserDataTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/UserDataTest.java
@@ -22,6 +22,8 @@
import static android.provider.Settings.Secure.AUTOFILL_USER_DATA_MAX_VALUE_LENGTH;
import static android.provider.Settings.Secure.AUTOFILL_USER_DATA_MIN_VALUE_LENGTH;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
import static com.google.common.truth.Truth.assertThat;
import static org.testng.Assert.assertThrows;
@@ -30,8 +32,6 @@
import android.platform.test.annotations.AppModeFull;
import android.service.autofill.UserData;
-import androidx.test.InstrumentationRegistry;
-
import com.android.compatibility.common.util.SettingsStateChangerRule;
import com.google.common.base.Strings;
@@ -46,7 +46,7 @@
@AppModeFull(reason = "Unit test")
public class UserDataTest {
- private static final Context sContext = InstrumentationRegistry.getContext();
+ private static final Context sContext = getInstrumentation().getTargetContext();
@ClassRule
public static final SettingsStateChangerRule sUserDataMaxFcSizeChanger =
diff --git a/tests/autofillservice/src/android/autofillservice/cts/ValidatorTest.java b/tests/autofillservice/src/android/autofillservice/cts/ValidatorTest.java
index 522fd98..aa89fae 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/ValidatorTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/ValidatorTest.java
@@ -18,6 +18,7 @@
import static android.autofillservice.cts.Helper.ID_PASSWORD;
import static android.autofillservice.cts.Helper.ID_USERNAME;
+import static android.autofillservice.cts.Helper.findAutofillIdByResourceId;
import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_GENERIC;
import static com.google.common.truth.Truth.assertThat;
@@ -53,17 +54,19 @@
private void integrationTest(boolean willSaveBeShown) throws Exception {
enableService();
- final AutofillId usernameId = mActivity.getUsername().getAutofillId();
-
final String username = willSaveBeShown ? "7992739871-3" : "4815162342-108";
- final LuhnChecksumValidator validator = new LuhnChecksumValidator(usernameId);
- // Sanity check to make sure the validator is properly configured
- assertValidator(validator, usernameId, username, willSaveBeShown);
// Set response
sReplier.addResponse(new CannedFillResponse.Builder()
.setRequiredSavableIds(SAVE_DATA_TYPE_GENERIC, ID_USERNAME, ID_PASSWORD)
- .setValidator(validator)
+ .setSaveInfoVisitor((contexts, builder) -> {
+ final AutofillId usernameId =
+ findAutofillIdByResourceId(contexts.get(0), ID_USERNAME);
+ final LuhnChecksumValidator validator = new LuhnChecksumValidator(usernameId);
+ // Sanity check to make sure the validator is properly configured
+ assertValidator(validator, usernameId, username, willSaveBeShown);
+ builder.setValidator(validator);
+ })
.build());
// Trigger auto-fill
diff --git a/tests/autofillservice/src/android/autofillservice/cts/ViewAttributesTest.java b/tests/autofillservice/src/android/autofillservice/cts/ViewAttributesTest.java
index 7598eca..be03b84 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/ViewAttributesTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/ViewAttributesTest.java
@@ -29,7 +29,7 @@
import androidx.annotation.IdRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/tests/autofillservice/src/android/autofillservice/cts/ViewVisitor.java b/tests/autofillservice/src/android/autofillservice/cts/ViewVisitor.java
deleted file mode 100644
index 99ca5c3..0000000
--- a/tests/autofillservice/src/android/autofillservice/cts/ViewVisitor.java
+++ /dev/null
@@ -1,34 +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 android.autofillservice.cts;
-
-/**
- * Visitor for a view.
- *
- * <p>Typically used by activities under test to provide a way to run an action on the view using
- * the UI thread. Example:
- * <pre><code>
- * void onUsername(ViewVisitor<EditText> v) {
- * runOnUiThread(() -> {
- * v.visit(mUsername);
- * });
- * }
- * </code></pre>
- */
-interface ViewVisitor<T>{
-
- void visit(T view);
-}
\ No newline at end of file
diff --git a/tests/autofillservice/src/android/autofillservice/cts/VisibilitySetterActionTest.java b/tests/autofillservice/src/android/autofillservice/cts/VisibilitySetterActionTest.java
index 8df6a2d..202c5fd 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/VisibilitySetterActionTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/VisibilitySetterActionTest.java
@@ -16,6 +16,8 @@
package android.autofillservice.cts;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
import static com.google.common.truth.Truth.assertThat;
import static org.testng.Assert.assertThrows;
@@ -26,8 +28,6 @@
import android.view.View;
import android.view.ViewGroup;
-import androidx.test.InstrumentationRegistry;
-
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.junit.MockitoJUnitRunner;
@@ -36,7 +36,7 @@
@AppModeFull(reason = "Unit test")
public class VisibilitySetterActionTest {
- private static final Context sContext = InstrumentationRegistry.getContext();
+ private static final Context sContext = getInstrumentation().getTargetContext();
private final ViewGroup mRootView = new ViewGroup(sContext) {
@Override
diff --git a/tests/autofillservice/src/android/autofillservice/cts/Visitor.java b/tests/autofillservice/src/android/autofillservice/cts/Visitor.java
index 26e62e3..95bafa7 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/Visitor.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/Visitor.java
@@ -29,5 +29,5 @@
// TODO: move to common code
public interface Visitor<T> {
- void visit(T view);
+ void visit(T object);
}
\ No newline at end of file
diff --git a/tests/autofillservice/src/android/autofillservice/cts/augmented/AbstractLoginNotImportantForAutofillTestCase.java b/tests/autofillservice/src/android/autofillservice/cts/augmented/AbstractLoginNotImportantForAutofillTestCase.java
new file mode 100644
index 0000000..859b1e2
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/augmented/AbstractLoginNotImportantForAutofillTestCase.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.autofillservice.cts.augmented;
+
+import static android.autofillservice.cts.augmented.AugmentedHelper.assertBasicRequestInfo;
+import static android.autofillservice.cts.augmented.CannedAugmentedFillResponse.NO_AUGMENTED_RESPONSE;
+
+import android.autofillservice.cts.LoginNotImportantForAutofillActivity;
+import android.autofillservice.cts.augmented.CtsAugmentedAutofillService.AugmentedFillRequest;
+import android.support.test.uiautomator.UiObject2;
+import android.view.View;
+import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillValue;
+import android.widget.EditText;
+
+import org.junit.Test;
+
+abstract class AbstractLoginNotImportantForAutofillTestCase<A extends
+ LoginNotImportantForAutofillActivity> extends
+ AugmentedAutofillAutoActivityLaunchTestCase<A> {
+
+ protected A mActivity;
+
+ @Test
+ public void testAutofill_none() throws Exception {
+ // Set services
+ enableService();
+ enableAugmentedService();
+
+ // Set expectations
+ final EditText username = mActivity.getUsername();
+ final AutofillValue expectedFocusedValue = username.getAutofillValue();
+ final AutofillId expectedFocusedId = username.getAutofillId();
+ sAugmentedReplier.addResponse(NO_AUGMENTED_RESPONSE);
+
+ // Trigger autofill
+ mActivity.onUsername(View::requestFocus);
+ final AugmentedFillRequest request = sAugmentedReplier.getNextFillRequest();
+
+ // Assert request
+ assertBasicRequestInfo(request, mActivity, expectedFocusedId, expectedFocusedValue);
+
+ // Make sure standard Autofill UI is not shown.
+ mUiBot.assertNoDatasetsEver();
+
+ // Make sure Augmented Autofill UI is not shown.
+ mAugmentedUiBot.assertUiNeverShown();
+ }
+
+ @Test
+ public void testAutofill_oneField() throws Exception {
+ // Set services
+ enableService();
+ enableAugmentedService();
+
+ // Set expectations
+ final EditText username = mActivity.getUsername();
+ final AutofillId usernameId = username.getAutofillId();
+ final AutofillValue expectedFocusedValue = username.getAutofillValue();
+ sAugmentedReplier.addResponse(new CannedAugmentedFillResponse.Builder()
+ .setDataset(new CannedAugmentedFillResponse.Dataset.Builder("Augment Me")
+ .setField(usernameId, "dude")
+ .build(), usernameId)
+ .build());
+ mActivity.expectAutoFill("dude");
+
+ // Trigger autofill
+ mActivity.onUsername(View::requestFocus);
+ final AugmentedFillRequest request = sAugmentedReplier.getNextFillRequest();
+
+ // Assert request
+ assertBasicRequestInfo(request, mActivity, usernameId, expectedFocusedValue);
+
+ // Make sure standard Autofill UI is not shown.
+ mUiBot.assertNoDatasetsEver();
+
+ // Make sure Augmented Autofill UI is shown.
+ final UiObject2 ui = mAugmentedUiBot.assertUiShown(usernameId, "Augment Me");
+
+ // Autofill
+ ui.click();
+ mActivity.assertAutoFilled();
+ mAugmentedUiBot.assertUiGone();
+ }
+
+ @Test
+ public void testAutofill_twoFields() throws Exception {
+ // Set services
+ enableService();
+ enableAugmentedService();
+
+ // Set expectations
+ final EditText username = mActivity.getUsername();
+ final AutofillId usernameId = username.getAutofillId();
+ final AutofillValue expectedFocusedValue = username.getAutofillValue();
+ sAugmentedReplier.addResponse(new CannedAugmentedFillResponse.Builder()
+ .setDataset(new CannedAugmentedFillResponse.Dataset.Builder("Augment Me")
+ .setField(usernameId, "dude")
+ .setField(mActivity.getPassword().getAutofillId(), "sweet")
+ .build(), usernameId)
+ .build());
+ mActivity.expectAutoFill("dude", "sweet");
+
+ // Trigger autofill
+ mActivity.onUsername(View::requestFocus);
+ final AugmentedFillRequest request = sAugmentedReplier.getNextFillRequest();
+
+ // Assert request
+ assertBasicRequestInfo(request, mActivity, usernameId, expectedFocusedValue);
+
+ // Make sure standard Autofill UI is not shown.
+ mUiBot.assertNoDatasetsEver();
+
+ // Make sure Augmented Autofill UI is shown.
+ final UiObject2 ui = mAugmentedUiBot.assertUiShown(usernameId, "Augment Me");
+
+ // Autofill
+ ui.click();
+ mActivity.assertAutoFilled();
+ mAugmentedUiBot.assertUiGone();
+ }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/augmented/AugmentedHelper.java b/tests/autofillservice/src/android/autofillservice/cts/augmented/AugmentedHelper.java
index 110a22f..2e09705 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/augmented/AugmentedHelper.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/augmented/AugmentedHelper.java
@@ -79,15 +79,21 @@
public static void assertBasicRequestInfo(@NonNull AugmentedFillRequest request,
@NonNull Activity activity, @NonNull AutofillId expectedFocusedId,
@NonNull AutofillValue expectedFocusedValue) {
+ assertBasicRequestInfo(request, activity, expectedFocusedId,
+ expectedFocusedValue.getTextValue().toString());
+ }
+
+ public static void assertBasicRequestInfo(@NonNull AugmentedFillRequest request,
+ @NonNull Activity activity, @NonNull AutofillId expectedFocusedId,
+ @NonNull String expectedFocusedValue) {
Preconditions.checkNotNull(activity);
Preconditions.checkNotNull(expectedFocusedId);
assertWithMessage("no AugmentedFillRequest").that(request).isNotNull();
assertWithMessage("no FillRequest on %s", request).that(request.request).isNotNull();
assertWithMessage("no FillController on %s", request).that(request.controller).isNotNull();
assertWithMessage("no FillCallback on %s", request).that(request.callback).isNotNull();
- // TODO(b/122728762): re-add when set
-// assertWithMessage("no CancellationSignal on %s", request).that(request.cancellationSignal)
-// .isNotNull();
+ assertWithMessage("no CancellationSignal on %s", request).that(request.cancellationSignal)
+ .isNotNull();
// NOTE: task id can change, we might need to set it in the activity's onCreate()
assertWithMessage("wrong task id on %s", request).that(request.request.getTaskId())
.isEqualTo(activity.getTaskId());
@@ -105,16 +111,21 @@
assertAutofillValue(expectedFocusedValue, actualFocusedValue);
}
- public static void assertAutofillValue(final AutofillValue expectedValue,
- final AutofillValue actualValue) {
+ public static void assertAutofillValue(@NonNull AutofillValue expectedValue,
+ @NonNull AutofillValue actualValue) {
// It only supports text values for now...
assertWithMessage("expected value is not text: %s", expectedValue)
.that(expectedValue.isText()).isTrue();
+ assertAutofillValue(expectedValue.getTextValue().toString(), actualValue);
+ }
+
+ public static void assertAutofillValue(@NonNull String expectedValue,
+ @NonNull AutofillValue actualValue) {
assertWithMessage("actual value is not text: %s", actualValue)
.that(actualValue.isText()).isTrue();
- assertWithMessage("wrong autofill value").that(actualValue.getTextValue())
- .isEqualTo(expectedValue.getTextValue());
+ assertWithMessage("wrong autofill value").that(actualValue.getTextValue().toString())
+ .isEqualTo(expectedValue);
}
@NonNull
diff --git a/tests/autofillservice/src/android/autofillservice/cts/augmented/AugmentedLoginActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/augmented/AugmentedLoginActivityTest.java
index e620969..00ce820 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/augmented/AugmentedLoginActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/augmented/AugmentedLoginActivityTest.java
@@ -32,6 +32,7 @@
import android.autofillservice.cts.AutofillActivityTestRule;
import android.autofillservice.cts.Helper;
import android.autofillservice.cts.LoginActivity;
+import android.autofillservice.cts.MyAutofillCallback;
import android.autofillservice.cts.OneTimeCancellationSignalListener;
import android.autofillservice.cts.augmented.CtsAugmentedAutofillService.AugmentedFillRequest;
import android.content.ComponentName;
@@ -238,6 +239,7 @@
}
@Test
+ @AppModeFull(reason = "testAutoFill_mainServiceReturnedNull_augmentedAutofillOneField enough")
public void testAugmentedAutoFill_multipleRequests() throws Exception {
// Set services
enableService();
@@ -307,6 +309,81 @@
}
@Test
+ public void testAugmentedAutoFill_callback() throws Exception {
+ // Set services
+ enableService();
+ enableAugmentedService();
+
+ // Set expectations
+ final MyAutofillCallback callback = mActivity.registerCallback();
+ final EditText username = mActivity.getUsername();
+ final AutofillId usernameId = username.getAutofillId();
+ final AutofillValue usernameValue = username.getAutofillValue();
+ sReplier.addResponse(NO_RESPONSE);
+ sAugmentedReplier.addResponse(new CannedAugmentedFillResponse.Builder()
+ .setDataset(new CannedAugmentedFillResponse.Dataset.Builder("req1")
+ .build(), usernameId)
+ .build());
+
+ // Trigger autofill
+ mActivity.onUsername(View::requestFocus);
+ sReplier.getNextFillRequest();
+ final AugmentedFillRequest request1 = sAugmentedReplier.getNextFillRequest();
+
+ // Assert request
+ assertBasicRequestInfo(request1, mActivity, usernameId, usernameValue);
+
+ // Make sure standard Autofill UI is not shown.
+ mUiBot.assertNoDatasetsEver();
+
+ // Make sure Augmented Autofill UI is shown.
+ callback.assertUiShownEvent(username);
+ mAugmentedUiBot.assertUiShown(usernameId, "req1");
+
+ // Move focus away to make sure Augmented Autofill UI is gone.
+ mActivity.onLogin(View::requestFocus);
+ mAugmentedUiBot.assertUiGone();
+ callback.assertUiHiddenEvent(username);
+
+ // Tap on password field
+ final EditText password = mActivity.getPassword();
+ final AutofillId passwordId = password.getAutofillId();
+ final AutofillValue passwordValue = password.getAutofillValue();
+ sAugmentedReplier.addResponse(new CannedAugmentedFillResponse.Builder()
+ .setDataset(new CannedAugmentedFillResponse.Dataset.Builder("req2")
+ .build(), passwordId)
+ .build());
+ mActivity.onPassword(View::requestFocus);
+ mUiBot.assertNoDatasetsEver();
+ final AugmentedFillRequest request2 = sAugmentedReplier.getNextFillRequest();
+ assertBasicRequestInfo(request2, mActivity, passwordId, passwordValue);
+
+ callback.assertUiShownEvent(password);
+ mAugmentedUiBot.assertUiShown(passwordId, "req2");
+
+ // Tap on username again...
+ sAugmentedReplier.addResponse(new CannedAugmentedFillResponse.Builder()
+ .setDataset(new CannedAugmentedFillResponse.Dataset.Builder("Augment Me")
+ .setField(usernameId, "dude")
+ .setField(passwordId, "sweet")
+ .build(), usernameId)
+ .build());
+
+ mActivity.onUsername(View::requestFocus);
+ final AugmentedFillRequest request3 = sAugmentedReplier.getNextFillRequest();
+ assertBasicRequestInfo(request3, mActivity, usernameId, usernameValue);
+ final UiObject2 ui = mAugmentedUiBot.assertUiShown(usernameId, "Augment Me");
+
+ // ...and autofill this time
+ mActivity.expectAutoFill("dude", "sweet");
+ ui.click();
+ mActivity.assertAutoFilled();
+ mAugmentedUiBot.assertUiGone();
+ callback.assertUiHiddenEvent(password);
+ }
+
+ @Test
+ @AppModeFull(reason = "testAutoFill_mainServiceReturnedNull_augmentedAutofillOneField enough")
public void testAugmentedAutoFill_rotateDevice() throws Exception {
assumeTrue("Rotation is supported", Helper.isRotationSupported(mContext));
@@ -409,6 +486,7 @@
}
@Test
+ @AppModeFull(reason = "testAutoFill_mainServiceReturnedNull_augmentedAutofillOneField enough")
public void testAugmentedAutoFill_mainServiceDisabled() throws Exception {
// Set services
Helper.disableAutofillService(sContext);
@@ -445,6 +523,48 @@
}
@Test
+ @AppModeFull(reason = "testAutoFill_mainServiceReturnedNull_augmentedAutofillOneField enough")
+ public void testAugmentedAutoFill_mainServiceDisabled_valueChangedOnSecondRequest()
+ throws Exception {
+ // Set services
+ Helper.disableAutofillService(sContext);
+ enableAugmentedService();
+
+ // Set expectations
+ final EditText username = mActivity.getUsername();
+ final AutofillId usernameId = username.getAutofillId();
+ final AutofillValue initialValue = username.getAutofillValue();
+ sAugmentedReplier.addResponse(NO_AUGMENTED_RESPONSE);
+
+ // Trigger autofill
+ mActivity.onUsername(View::requestFocus);
+ final AugmentedFillRequest request1 = sAugmentedReplier.getNextFillRequest();
+
+ // Assert request
+ assertBasicRequestInfo(request1, mActivity, usernameId, initialValue);
+
+ // Make sure UIs were not shown
+ mUiBot.assertNoDatasetsEver();
+ mAugmentedUiBot.assertUiNeverShown();
+
+ // Change field value
+ mActivity.onUsername((v) -> v.setText("DOH"));
+
+ // Trigger autofill again
+ sAugmentedReplier.addResponse(NO_AUGMENTED_RESPONSE);
+ mActivity.onUsername(View::performClick);
+ final AugmentedFillRequest request2 = sAugmentedReplier.getNextFillRequest();
+
+ // Assert 2nd request
+ assertBasicRequestInfo(request2, mActivity, usernameId, "DOH");
+
+ // Make sure UIs were not shown
+ mUiBot.assertNoDatasetsEver();
+ mAugmentedUiBot.assertUiNeverShown();
+ }
+
+ @Test
+ @AppModeFull(reason = "testAutoFill_mainServiceReturnedNull_augmentedAutofillOneField enough")
public void testSetAugmentedAutofillWhitelist_noStandardServiceSet() throws Exception {
final AutofillManager mgr = mActivity.getAutofillManager();
assertThrows(SecurityException.class,
@@ -453,6 +573,7 @@
}
@Test
+ @AppModeFull(reason = "testAutoFill_mainServiceReturnedNull_augmentedAutofillOneField enough")
public void testSetAugmentedAutofillWhitelist_notAugmentedService() throws Exception {
enableService();
final AutofillManager mgr = mActivity.getAutofillManager();
@@ -462,6 +583,7 @@
}
@Test
+ @AppModeFull(reason = "testAutoFill_mainServiceReturnedNull_augmentedAutofillOneField enough")
public void testAugmentedAutofill_packageNotWhitelisted() throws Exception {
// Set services
enableService();
@@ -488,6 +610,7 @@
}
@Test
+ @AppModeFull(reason = "testAutoFill_mainServiceReturnedNull_augmentedAutofillOneField enough")
public void testAugmentedAutofill_activityNotWhitelisted() throws Exception {
// Set services
enableService();
diff --git a/tests/autofillservice/src/android/autofillservice/cts/augmented/AugmentedLoginNotImportantForAutofillActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/augmented/AugmentedLoginNotImportantForAutofillActivityTest.java
index 6d88b0b..3875561 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/augmented/AugmentedLoginNotImportantForAutofillActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/augmented/AugmentedLoginNotImportantForAutofillActivityTest.java
@@ -16,26 +16,13 @@
package android.autofillservice.cts.augmented;
-import static android.autofillservice.cts.augmented.AugmentedHelper.assertBasicRequestInfo;
-import static android.autofillservice.cts.augmented.CannedAugmentedFillResponse.NO_AUGMENTED_RESPONSE;
-
import android.autofillservice.cts.AutofillActivityTestRule;
import android.autofillservice.cts.LoginNotImportantForAutofillActivity;
-import android.autofillservice.cts.augmented.CtsAugmentedAutofillService.AugmentedFillRequest;
import android.platform.test.annotations.AppModeFull;
-import android.support.test.uiautomator.UiObject2;
-import android.view.View;
-import android.view.autofill.AutofillId;
-import android.view.autofill.AutofillValue;
-import android.widget.EditText;
-
-import org.junit.Test;
@AppModeFull(reason = "AugmentedLoginActivityTest is enough")
public class AugmentedLoginNotImportantForAutofillActivityTest
- extends AugmentedAutofillAutoActivityLaunchTestCase<LoginNotImportantForAutofillActivity> {
-
- protected LoginNotImportantForAutofillActivity mActivity;
+ extends AbstractLoginNotImportantForAutofillTestCase<LoginNotImportantForAutofillActivity> {
@Override
protected AutofillActivityTestRule<LoginNotImportantForAutofillActivity> getActivityRule() {
@@ -47,103 +34,4 @@
}
};
}
-
- @Test
- public void testAutofill_none() throws Exception {
- // Set services
- enableService();
- enableAugmentedService();
-
- // Set expectations
- final EditText username = mActivity.getUsername();
- final AutofillValue expectedFocusedValue = username.getAutofillValue();
- final AutofillId expectedFocusedId = username.getAutofillId();
- sAugmentedReplier.addResponse(NO_AUGMENTED_RESPONSE);
-
- // Trigger autofill
- mActivity.onUsername(View::requestFocus);
- final AugmentedFillRequest request = sAugmentedReplier.getNextFillRequest();
-
- // Assert request
- assertBasicRequestInfo(request, mActivity, expectedFocusedId, expectedFocusedValue);
-
- // Make sure standard Autofill UI is not shown.
- mUiBot.assertNoDatasetsEver();
-
- // Make sure Augmented Autofill UI is not shown.
- mAugmentedUiBot.assertUiNeverShown();
- }
-
- @Test
- public void testAutofill_oneField() throws Exception {
- // Set services
- enableService();
- enableAugmentedService();
-
- // Set expectations
- final EditText username = mActivity.getUsername();
- final AutofillId usernameId = username.getAutofillId();
- final AutofillValue expectedFocusedValue = username.getAutofillValue();
- sAugmentedReplier.addResponse(new CannedAugmentedFillResponse.Builder()
- .setDataset(new CannedAugmentedFillResponse.Dataset.Builder("Augment Me")
- .setField(usernameId, "dude")
- .build(), usernameId)
- .build());
- mActivity.expectAutoFill("dude");
-
- // Trigger autofill
- mActivity.onUsername(View::requestFocus);
- final AugmentedFillRequest request = sAugmentedReplier.getNextFillRequest();
-
- // Assert request
- assertBasicRequestInfo(request, mActivity, usernameId, expectedFocusedValue);
-
- // Make sure standard Autofill UI is not shown.
- mUiBot.assertNoDatasetsEver();
-
- // Make sure Augmented Autofill UI is shown.
- final UiObject2 ui = mAugmentedUiBot.assertUiShown(usernameId, "Augment Me");
-
- // Autofill
- ui.click();
- mActivity.assertAutoFilled();
- mAugmentedUiBot.assertUiGone();
- }
-
- @Test
- public void testAutofill_twoFields() throws Exception {
- // Set services
- enableService();
- enableAugmentedService();
-
- // Set expectations
- final EditText username = mActivity.getUsername();
- final AutofillId usernameId = username.getAutofillId();
- final AutofillValue expectedFocusedValue = username.getAutofillValue();
- sAugmentedReplier.addResponse(new CannedAugmentedFillResponse.Builder()
- .setDataset(new CannedAugmentedFillResponse.Dataset.Builder("Augment Me")
- .setField(usernameId, "dude")
- .setField(mActivity.getPassword().getAutofillId(), "sweet")
- .build(), usernameId)
- .build());
- mActivity.expectAutoFill("dude", "sweet");
-
- // Trigger autofill
- mActivity.onUsername(View::requestFocus);
- final AugmentedFillRequest request = sAugmentedReplier.getNextFillRequest();
-
- // Assert request
- assertBasicRequestInfo(request, mActivity, usernameId, expectedFocusedValue);
-
- // Make sure standard Autofill UI is not shown.
- mUiBot.assertNoDatasetsEver();
-
- // Make sure Augmented Autofill UI is shown.
- final UiObject2 ui = mAugmentedUiBot.assertUiShown(usernameId, "Augment Me");
-
- // Autofill
- ui.click();
- mActivity.assertAutoFilled();
- mAugmentedUiBot.assertUiGone();
- }
}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/augmented/AugmentedNotImportantForAutofillWrappedActivityContextTest.java b/tests/autofillservice/src/android/autofillservice/cts/augmented/AugmentedNotImportantForAutofillWrappedActivityContextTest.java
new file mode 100644
index 0000000..e4300ac
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/augmented/AugmentedNotImportantForAutofillWrappedActivityContextTest.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 android.autofillservice.cts.augmented;
+
+import android.autofillservice.cts.AutofillActivityTestRule;
+import android.autofillservice.cts.LoginNotImportantForAutofillWrappedActivityContextActivity;
+import android.platform.test.annotations.AppModeFull;
+
+@AppModeFull(reason = "AugmentedLoginActivityTest is enough")
+public class AugmentedNotImportantForAutofillWrappedActivityContextTest extends
+ AbstractLoginNotImportantForAutofillTestCase<
+ LoginNotImportantForAutofillWrappedActivityContextActivity> {
+ @Override
+ protected AutofillActivityTestRule<LoginNotImportantForAutofillWrappedActivityContextActivity>
+ getActivityRule() {
+ return new AutofillActivityTestRule<
+ LoginNotImportantForAutofillWrappedActivityContextActivity>(
+ LoginNotImportantForAutofillWrappedActivityContextActivity.class) {
+ @Override
+ protected void afterActivityLaunched() {
+ mActivity = getActivity();
+ }
+ };
+ }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/augmented/AugmentedNotImportantForAutofillWrappedApplicationContextTest.java b/tests/autofillservice/src/android/autofillservice/cts/augmented/AugmentedNotImportantForAutofillWrappedApplicationContextTest.java
new file mode 100644
index 0000000..efdb036
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/augmented/AugmentedNotImportantForAutofillWrappedApplicationContextTest.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 android.autofillservice.cts.augmented;
+
+import android.autofillservice.cts.AutofillActivityTestRule;
+import android.autofillservice.cts.LoginNotImportantForAutofillWrappedApplicationContextActivity;
+import android.platform.test.annotations.AppModeFull;
+
+@AppModeFull(reason = "AugmentedLoginActivityTest is enough")
+public class AugmentedNotImportantForAutofillWrappedApplicationContextTest extends
+ AbstractLoginNotImportantForAutofillTestCase<
+ LoginNotImportantForAutofillWrappedApplicationContextActivity> {
+ @Override
+ protected AutofillActivityTestRule<
+ LoginNotImportantForAutofillWrappedApplicationContextActivity> getActivityRule() {
+ return new AutofillActivityTestRule<
+ LoginNotImportantForAutofillWrappedApplicationContextActivity>(
+ LoginNotImportantForAutofillWrappedApplicationContextActivity.class) {
+ @Override
+ protected void afterActivityLaunched() {
+ mActivity = getActivity();
+ }
+ };
+ }
+}
diff --git a/tests/camera/libctscamera2jni/native-camera-jni.cpp b/tests/camera/libctscamera2jni/native-camera-jni.cpp
index 262436f..31e8070 100644
--- a/tests/camera/libctscamera2jni/native-camera-jni.cpp
+++ b/tests/camera/libctscamera2jni/native-camera-jni.cpp
@@ -29,6 +29,7 @@
#include <jni.h>
#include <stdio.h>
#include <string.h>
+#include <set>
#include <android/native_window_jni.h>
@@ -47,6 +48,11 @@
char errorString[MAX_ERROR_STRING_LEN];
}
+template <>
+struct std::default_delete<ACameraMetadata> {
+ inline void operator()(ACameraMetadata* chars) const { ACameraMetadata_free(chars); }
+};
+
class CameraServiceListener {
public:
static void onAvailable(void* obj, const char* cameraId) {
@@ -265,6 +271,7 @@
~CaptureResultListener() {
std::unique_lock<std::mutex> l(mMutex);
clearSavedRequestsLocked();
+ clearFailedFrameNumbersLocked();
}
static void onCaptureStart(void* /*obj*/, ACameraCaptureSession* /*session*/,
@@ -401,7 +408,7 @@
}
CaptureResultListener* thiz = reinterpret_cast<CaptureResultListener*>(obj);
std::lock_guard<std::mutex> lock(thiz->mMutex);
- thiz->mLastFailedFrameNumber = failure->frameNumber;
+ thiz->mFailedFrameNumbers.insert(failure->frameNumber);
thiz->mResultCondition.notify_one();
}
@@ -416,7 +423,7 @@
}
CaptureResultListener* thiz = reinterpret_cast<CaptureResultListener*>(obj);
std::lock_guard<std::mutex> lock(thiz->mMutex);
- thiz->mLastFailedFrameNumber = failure->captureFailure.frameNumber;
+ thiz->mFailedFrameNumbers.insert(failure->captureFailure.frameNumber);
thiz->mResultCondition.notify_one();
}
@@ -474,8 +481,7 @@
std::unique_lock<std::mutex> l(mMutex);
while ((mLastCompletedFrameNumber != frameNumber) &&
- (mLastLostFrameNumber != frameNumber) &&
- (mLastFailedFrameNumber != frameNumber)) {
+ (mLastLostFrameNumber != frameNumber) && !checkForFailureLocked(frameNumber)) {
auto timeout = std::chrono::system_clock::now() +
std::chrono::seconds(timeoutSec);
if (std::cv_status::timeout == mResultCondition.wait_until(l, timeout)) {
@@ -484,8 +490,7 @@
}
if ((mLastCompletedFrameNumber == frameNumber) ||
- (mLastLostFrameNumber == frameNumber) ||
- (mLastFailedFrameNumber == frameNumber)) {
+ (mLastLostFrameNumber == frameNumber) || checkForFailureLocked(frameNumber)) {
ret = true;
}
@@ -514,15 +519,20 @@
}
}
+ bool checkForFailure(int64_t frameNumber) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ return checkForFailureLocked(frameNumber);
+ }
+
void reset() {
std::lock_guard<std::mutex> lock(mMutex);
mLastSequenceIdCompleted = -1;
mLastSequenceFrameNumber = -1;
mLastCompletedFrameNumber = -1;
mLastLostFrameNumber = -1;
- mLastFailedFrameNumber = -1;
mSaveCompletedRequests = false;
clearSavedRequestsLocked();
+ clearFailedFrameNumbersLocked();
}
private:
@@ -532,7 +542,7 @@
int64_t mLastSequenceFrameNumber = -1;
int64_t mLastCompletedFrameNumber = -1;
int64_t mLastLostFrameNumber = -1;
- int64_t mLastFailedFrameNumber = -1;
+ std::set<int64_t> mFailedFrameNumbers;
bool mSaveCompletedRequests = false;
std::vector<ACaptureRequest*> mCompletedRequests;
// Registered physical camera Ids that are being requested upon.
@@ -544,6 +554,14 @@
}
mCompletedRequests.clear();
}
+
+ void clearFailedFrameNumbersLocked() {
+ mFailedFrameNumbers.clear();
+ }
+
+ bool checkForFailureLocked(int64_t frameNumber) {
+ return mFailedFrameNumbers.find(frameNumber) != mFailedFrameNumbers.end();
+ }
};
class ImageReaderListener {
@@ -1370,6 +1388,18 @@
return ret;
}
+ camera_status_t startRepeatingRequest(int *sequenceId, ACaptureRequest *request,
+ ACameraCaptureSession_captureCallbacks *resultCb) {
+ if (mSession == nullptr || request == nullptr || resultCb == nullptr) {
+ ALOGE("Testcase cannot start repeating request: session %p, request %p resultCb %p",
+ mSession, request, resultCb);
+ return ACAMERA_ERROR_UNKNOWN;
+ }
+
+ return ACameraCaptureSession_setRepeatingRequest(mSession, resultCb, 1, &request,
+ sequenceId);
+ }
+
camera_status_t stopPreview() {
if (mSession == nullptr) {
ALOGE("Testcase cannot stop preview: session %p", mSession);
@@ -3319,6 +3349,180 @@
return pass;
}
+// Test the camera NDK capture failure path by acquiring the maximum amount of ImageReader
+// buffers available. Since there is no circulation of camera images, the registered output
+// surface will eventually run out of free buffers and start reporting capture errors.
+extern "C" jboolean
+Java_android_hardware_camera2_cts_NativeCameraDeviceTest_\
+testCameraDeviceCaptureFailureNative(JNIEnv* env) {
+ const size_t NUM_TEST_IMAGES = 10;
+ const size_t NUM_FAILED_FRAMES = 3; // Wait for at least 3 consecutive failed capture requests
+ const int64_t NUM_TOTAL_FRAMES = 60; // Avoid waiting for more than 60 frames
+ const size_t TEST_WIDTH = 640;
+ const size_t TEST_HEIGHT = 480;
+ media_status_t mediaRet = AMEDIA_ERROR_UNKNOWN;
+ int numCameras = 0;
+ bool pass = false;
+ PreviewTestCase testCase;
+ uint32_t timeoutSec = 10; // It is important to keep this timeout bigger than the framework
+ // timeout
+
+ camera_status_t ret = testCase.initWithErrorLog();
+ if (ret != ACAMERA_OK) {
+ // Don't log error here. testcase did it
+ goto exit;
+ }
+
+ numCameras = testCase.getNumCameras();
+ if (numCameras < 0) {
+ LOG_ERROR(errorString, "Testcase returned negative number of cameras: %d", numCameras);
+ goto exit;
+ }
+
+ for (int i = 0; i < numCameras; i++) {
+ const char* cameraId = testCase.getCameraId(i);
+ if (cameraId == nullptr) {
+ LOG_ERROR(errorString, "Testcase returned null camera id for camera %d", i);
+ goto exit;
+ }
+
+ std::unique_ptr<ACameraMetadata> chars(testCase.getCameraChars(i));
+ if (chars.get() == nullptr) {
+ LOG_ERROR(errorString, "Get camera %s characteristics failure", cameraId);
+ goto exit;
+ }
+ StaticInfo staticInfo(chars.get());
+
+ if (!staticInfo.isColorOutputSupported()) {
+ continue;
+ }
+
+ ret = testCase.openCamera(cameraId);
+ if (ret != ACAMERA_OK) {
+ LOG_ERROR(errorString, "Open camera device %s failure. ret %d", cameraId, ret);
+ goto exit;
+ }
+
+ if (testCase.isCameraAvailable(cameraId)) {
+ LOG_ERROR(errorString, "Camera %s should be unavailable now", cameraId);
+ goto exit;
+ }
+
+ ImageReaderListener readerListener;
+ AImageReader_ImageListener readerCb =
+ { &readerListener, ImageReaderListener::acquireImageCb };
+ mediaRet = testCase.initImageReaderWithErrorLog(TEST_WIDTH, TEST_HEIGHT,
+ AIMAGE_FORMAT_YUV_420_888, NUM_TEST_IMAGES, &readerCb);
+ if (mediaRet != AMEDIA_OK) {
+ // Don't log error here. testcase did it
+ goto exit;
+ }
+
+ ret = testCase.createCaptureSessionWithLog();
+ if (ret != ACAMERA_OK) {
+ // Don't log error here. testcase did it
+ goto exit;
+ }
+
+ ret = testCase.createRequestsWithErrorLog();
+ if (ret != ACAMERA_OK) {
+ // Don't log error here. testcase did it
+ goto exit;
+ }
+
+ CaptureResultListener resultListener;
+ ACameraCaptureSession_captureCallbacks resultCb {
+ &resultListener,
+ CaptureResultListener::onCaptureStart,
+ CaptureResultListener::onCaptureProgressed,
+ CaptureResultListener::onCaptureCompleted,
+ CaptureResultListener::onCaptureFailed,
+ CaptureResultListener::onCaptureSequenceCompleted,
+ CaptureResultListener::onCaptureSequenceAborted,
+ CaptureResultListener::onCaptureBufferLost
+ };
+ ACaptureRequest* requestTemplate = nullptr;
+ ret = testCase.getStillRequest(&requestTemplate);
+ if (ret != ACAMERA_OK || requestTemplate == nullptr) {
+ // Don't log error here. testcase did it
+ goto exit;
+ }
+
+ int seqId;
+ ret = testCase.startRepeatingRequest(&seqId, requestTemplate, &resultCb);
+ if (ret != ACAMERA_OK) {
+ // Don't log error here. testcase did it
+ goto exit;
+ }
+
+ size_t failedRequestCount;
+ int64_t lastFrameNumber;
+ int64_t lastFailedRequestNumber = -1;
+ failedRequestCount = lastFrameNumber = 0;
+ while ((failedRequestCount < NUM_FAILED_FRAMES) && (lastFrameNumber < NUM_TOTAL_FRAMES)) {
+ auto frameArrived = resultListener.waitForFrameNumber(lastFrameNumber, timeoutSec);
+ if (!frameArrived) {
+ LOG_ERROR(errorString, "Camera %s timed out waiting on last frame number!",
+ cameraId);
+ goto exit;
+ }
+ auto failedFrameNumber = resultListener.checkForFailure(lastFrameNumber) ?
+ lastFrameNumber : -1;
+ if (lastFailedRequestNumber != failedFrameNumber) {
+ if ((lastFailedRequestNumber + 1) == failedFrameNumber) {
+ failedRequestCount++;
+ } else {
+ failedRequestCount = 1;
+ }
+ lastFailedRequestNumber = failedFrameNumber;
+ }
+ lastFrameNumber++;
+ }
+
+ ret = testCase.stopPreview();
+ if (ret != ACAMERA_OK) {
+ LOG_ERROR(errorString, "stopPreview failed!");
+ goto exit;
+ }
+
+ ret = testCase.resetWithErrorLog();
+ if (ret != ACAMERA_OK) {
+ // Don't log error here. testcase did it
+ goto exit;
+ }
+
+ usleep(100000); // sleep to give some time for callbacks to happen
+
+ if (!testCase.isCameraAvailable(cameraId)) {
+ LOG_ERROR(errorString, "Camera %s should be available now", cameraId);
+ goto exit;
+ }
+
+ if (failedRequestCount < NUM_FAILED_FRAMES) {
+ LOG_ERROR(errorString, "Unable to receive %zu consecutive capture failures within"
+ " %" PRId64 " capture requests", NUM_FAILED_FRAMES, NUM_TOTAL_FRAMES);
+ goto exit;
+ }
+ }
+
+ ret = testCase.deInit();
+ if (ret != ACAMERA_OK) {
+ LOG_ERROR(errorString, "Testcase deInit failed: ret %d", ret);
+ goto exit;
+ }
+
+ pass = true;
+
+exit:
+
+ ALOGI("%s %s", __FUNCTION__, pass ? "pass" : "failed");
+ if (!pass) {
+ throwAssertionError(env, errorString);
+ }
+
+ return pass;
+}
+
extern "C" jboolean
Java_android_hardware_camera2_cts_NativeImageReaderTest_\
testJpegNative(
diff --git a/tests/camera/src/android/hardware/camera2/cts/BurstCaptureTest.java b/tests/camera/src/android/hardware/camera2/cts/BurstCaptureTest.java
index cb14e7e..03dbdeb5 100644
--- a/tests/camera/src/android/hardware/camera2/cts/BurstCaptureTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/BurstCaptureTest.java
@@ -47,10 +47,27 @@
*/
@Test
public void testYuvBurst() throws Exception {
+ final int YUV_BURST_SIZE = 100;
+ testBurst(ImageFormat.YUV_420_888, YUV_BURST_SIZE, true/*checkFrameRate*/);
+ }
+
+ /**
+ * Test JPEG burst capture with full-AUTO control.
+ *
+ * Also verifies sensor settings operation if READ_SENSOR_SETTINGS is available.
+ * Compared to testYuvBurst, this test uses STILL_CAPTURE intent, and exercises path where
+ * enableZsl is enabled.
+ */
+ @Test
+ public void testJpegBurst() throws Exception {
+ final int JPEG_BURST_SIZE = 10;
+ testBurst(ImageFormat.JPEG, JPEG_BURST_SIZE, false/*checkFrameRate*/);
+ }
+
+ private void testBurst(int fmt, int burstSize, boolean checkFrameRate) throws Exception {
for (int i = 0; i < mCameraIds.length; i++) {
try {
String id = mCameraIds[i];
- Log.i(TAG, "Testing YUV Burst for camera " + id);
StaticMetadata staticInfo = mAllStaticInfo.get(id);
if (!staticInfo.isColorOutputSupported()) {
@@ -69,7 +86,7 @@
}
openDevice(id);
- yuvBurstTestByCamera(id);
+ burstTestByCamera(id, fmt, burstSize, checkFrameRate);
} finally {
closeDevice();
closeImageReader();
@@ -77,19 +94,19 @@
}
}
- private void yuvBurstTestByCamera(String cameraId) throws Exception {
+ private void burstTestByCamera(String cameraId, int fmt, int burstSize,
+ boolean checkFrameRate) throws Exception {
// Parameters
final int MAX_CONVERGENCE_FRAMES = 150; // 5 sec at 30fps
final long MAX_PREVIEW_RESULT_TIMEOUT_MS = 2000;
- final int BURST_SIZE = 100;
final float FRAME_DURATION_MARGIN_FRACTION = 0.1f;
// Find a good preview size (bound to 1080p)
final Size previewSize = mOrderedPreviewSizes.get(0);
- // Get maximum YUV_420_888 size
+ // Get maximum size for fmt
final Size stillSize = getSortedSizesForFormat(
- cameraId, mCameraManager, ImageFormat.YUV_420_888, /*bound*/null).get(0);
+ cameraId, mCameraManager, fmt, /*bound*/null).get(0);
// Find max pipeline depth and sync latency
final int maxPipelineDepth = mStaticInfo.getCharacteristics().get(
@@ -97,11 +114,11 @@
final int maxSyncLatency = mStaticInfo.getCharacteristics().get(
CameraCharacteristics.SYNC_MAX_LATENCY);
- // Find minimum frame duration for full-res YUV_420_888
+ // Find minimum frame duration for full-res resolution
StreamConfigurationMap config = mStaticInfo.getCharacteristics().get(
CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
final long minStillFrameDuration =
- config.getOutputMinFrameDuration(ImageFormat.YUV_420_888, stillSize);
+ config.getOutputMinFrameDuration(fmt, stillSize);
Range<Integer> targetRange = getSuitableFpsRangeForDuration(cameraId, minStillFrameDuration);
@@ -117,8 +134,12 @@
CaptureRequest.Builder previewBuilder =
mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
- CaptureRequest.Builder burstBuilder =
- mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+ int burstTemplate = (fmt == ImageFormat.JPEG) ?
+ CameraDevice.TEMPLATE_STILL_CAPTURE : CameraDevice.TEMPLATE_PREVIEW;
+ CaptureRequest.Builder burstBuilder = mCamera.createCaptureRequest(burstTemplate);
+ Boolean enableZsl = burstBuilder.get(CaptureRequest.CONTROL_ENABLE_ZSL);
+ boolean zslStillEnabled = enableZsl != null && enableZsl &&
+ burstTemplate == CameraDevice.TEMPLATE_STILL_CAPTURE;
previewBuilder.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE,
targetRange);
@@ -136,13 +157,13 @@
prepareCaptureAndStartPreview(
previewBuilder, burstBuilder,
previewSize, stillSize,
- ImageFormat.YUV_420_888, resultListener,
+ fmt, resultListener,
/*maxNumImages*/ 3, imageDropper);
// Create burst
List<CaptureRequest> burst = new ArrayList<>();
- for (int i = 0; i < BURST_SIZE; i++) {
+ for (int i = 0; i < burstSize; i++) {
burst.add(burstBuilder.build());
}
@@ -313,7 +334,7 @@
// Get next result
burstIndex++;
- if (burstIndex == BURST_SIZE) {
+ if (burstIndex == burstSize) {
burstEndTimeStamp = burstResult.get(CaptureResult.SENSOR_TIMESTAMP);
break;
}
@@ -321,11 +342,12 @@
}
// Verify no preview frames interleaved in burst results
- while (true) {
+ while (true) {
CaptureResult previewResult =
resultListener.getCaptureResult(MAX_PREVIEW_RESULT_TIMEOUT_MS);
long previewTimestamp = previewResult.get(CaptureResult.SENSOR_TIMESTAMP);
- if (previewTimestamp >= burstStartTimestamp && previewTimestamp <= burstEndTimeStamp) {
+ if (!zslStillEnabled && previewTimestamp >= burstStartTimestamp
+ && previewTimestamp <= burstEndTimeStamp) {
fail("Preview frame is interleaved with burst frames! Preview timestamp:" +
previewTimestamp + ", burst [" + burstStartTimestamp + ", " +
burstEndTimeStamp + "]");
@@ -335,39 +357,40 @@
}
// Verify inter-frame durations
+ if (checkFrameRate) {
+ long meanFrameSum = 0;
+ for (Long duration : frameDurations) {
+ meanFrameSum += duration;
+ }
+ float meanFrameDuration = (float) meanFrameSum / frameDurations.size();
- long meanFrameSum = 0;
- for (Long duration : frameDurations) {
- meanFrameSum += duration;
- }
- float meanFrameDuration = (float) meanFrameSum / frameDurations.size();
+ float stddevSum = 0;
+ for (Long duration : frameDurations) {
+ stddevSum += (duration - meanFrameDuration) * (duration - meanFrameDuration);
+ }
+ float stddevFrameDuration = (float)
+ Math.sqrt(1.f / (frameDurations.size() - 1 ) * stddevSum);
- float stddevSum = 0;
- for (Long duration : frameDurations) {
- stddevSum += (duration - meanFrameDuration) * (duration - meanFrameDuration);
- }
- float stddevFrameDuration = (float)
- Math.sqrt(1.f / (frameDurations.size() - 1 ) * stddevSum);
+ Log.i(TAG, String.format("Cam %s: Burst frame duration mean: %.1f, stddev: %.1f",
+ cameraId, meanFrameDuration, stddevFrameDuration));
- Log.i(TAG, String.format("Cam %s: Burst frame duration mean: %.1f, stddev: %.1f", cameraId,
- meanFrameDuration, stddevFrameDuration));
+ assertTrue(
+ String.format("Cam %s: Burst frame duration mean %.1f ns is larger than " +
+ "acceptable, expecting below %d ns, allowing below %d", cameraId,
+ meanFrameDuration, minStillFrameDuration, frameDurationBound),
+ meanFrameDuration <= frameDurationBound);
- assertTrue(
- String.format("Cam %s: Burst frame duration mean %.1f ns is larger than acceptable, " +
- "expecting below %d ns, allowing below %d", cameraId,
- meanFrameDuration, minStillFrameDuration, frameDurationBound),
- meanFrameDuration <= frameDurationBound);
+ // Calculate upper 97.5% bound (assuming durations are normally distributed...)
+ float limit95FrameDuration = meanFrameDuration + 2 * stddevFrameDuration;
- // Calculate upper 97.5% bound (assuming durations are normally distributed...)
- float limit95FrameDuration = meanFrameDuration + 2 * stddevFrameDuration;
-
- // Don't enforce this yet, but warn
- if (limit95FrameDuration > frameDurationBound) {
- Log.w(TAG,
- String.format("Cam %s: Standard deviation is too large compared to limit: " +
- "mean: %.1f ms, stddev: %.1f ms: 95%% bound: %f ms", cameraId,
- meanFrameDuration/1e6, stddevFrameDuration/1e6,
- limit95FrameDuration/1e6));
+ // Don't enforce this yet, but warn
+ if (limit95FrameDuration > frameDurationBound) {
+ Log.w(TAG,
+ String.format("Cam %s: Standard deviation is too large compared to limit: " +
+ "mean: %.1f ms, stddev: %.1f ms: 95%% bound: %f ms", cameraId,
+ meanFrameDuration/1e6, stddevFrameDuration/1e6,
+ limit95FrameDuration/1e6));
+ }
}
}
}
diff --git a/tests/camera/src/android/hardware/camera2/cts/CameraManagerTest.java b/tests/camera/src/android/hardware/camera2/cts/CameraManagerTest.java
index 170cc3f..2b3e8a2 100644
--- a/tests/camera/src/android/hardware/camera2/cts/CameraManagerTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/CameraManagerTest.java
@@ -30,11 +30,13 @@
import android.hardware.camera2.cts.CameraTestUtils.HandlerExecutor;
import android.hardware.camera2.cts.CameraTestUtils.MockStateCallback;
import android.hardware.camera2.cts.helpers.CameraErrorCollector;
+import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import android.test.AndroidTestCase;
import android.util.Log;
+import com.android.compatibility.common.util.PropertyUtil;
import com.android.ex.camera2.blocking.BlockingStateCallback;
import org.mockito.ArgumentCaptor;
@@ -671,4 +673,24 @@
} // testCameraManagerListenerCallbacks
+ // Verify no LEGACY-level devices appear on devices first launched in the Q release or newer
+ public void testNoLegacyOnQ() throws Exception {
+ if(PropertyUtil.getFirstApiLevel() < Build.VERSION_CODES.Q){
+ // LEGACY still allowed for devices upgrading to Q
+ return;
+ }
+ String[] ids = mCameraManager.getCameraIdList();
+ for (int i = 0; i < ids.length; i++) {
+ CameraCharacteristics props = mCameraManager.getCameraCharacteristics(ids[i]);
+ assertNotNull(
+ String.format("Can't get camera characteristics from: ID %s", ids[i]), props);
+ Integer hardwareLevel = props.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
+ assertNotNull(
+ String.format("Can't get hardware level from: ID %s", ids[i]), hardwareLevel);
+ assertTrue(String.format(
+ "Camera device %s cannot be LEGACY level for devices launching on Q",
+ ids[i]),
+ hardwareLevel != CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY);
+ }
+ }
}
diff --git a/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java b/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
index 2df70b0..c3a3a19 100644
--- a/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
@@ -35,15 +35,19 @@
import android.media.CamcorderProfile;
import android.media.ImageReader;
import android.os.Build;
+import android.util.DisplayMetrics;
import android.util.Log;
import android.util.Rational;
import android.util.Range;
import android.util.Size;
import android.util.Pair;
import android.util.Patterns;
+import android.view.Display;
import android.view.Surface;
import android.view.WindowManager;
+import com.android.compatibility.common.util.CddTest;
+
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -1184,8 +1188,12 @@
Size maxFastYuvSize = maxYuvSize;
Size[] slowYuvSizes = config.getHighResolutionOutputSizes(ImageFormat.YUV_420_888);
+ Size maxSlowYuvSizeLessThan24M = null;
if (haveBurstCapability && slowYuvSizes != null && slowYuvSizes.length > 0) {
Size maxSlowYuvSize = CameraTestUtils.getMaxSize(slowYuvSizes);
+ final int SIZE_24MP_BOUND = 24000000;
+ maxSlowYuvSizeLessThan24M =
+ CameraTestUtils.getMaxSizeWithBound(slowYuvSizes, SIZE_24MP_BOUND);
maxYuvSize = CameraTestUtils.getMaxSize(new Size[]{maxYuvSize, maxSlowYuvSize});
}
@@ -1205,13 +1213,6 @@
boolean haveAwbLock = CameraTestUtils.getValueNotNull(
c, CameraCharacteristics.CONTROL_AWB_LOCK_AVAILABLE);
- // Ensure that max YUV output is fast enough - needs to be at least 10 fps
-
- long maxYuvRate =
- config.getOutputMinFrameDuration(ImageFormat.YUV_420_888, maxYuvSize);
- final long MIN_MAXSIZE_DURATION_BOUND_NS = 100000000; // 100 ms, 10 fps
- boolean haveMaxYuvRate = maxYuvRate <= MIN_MAXSIZE_DURATION_BOUND_NS;
-
// Ensure that some >=8MP YUV output is fast enough - needs to be at least 20 fps
long maxFastYuvRate =
@@ -1223,6 +1224,16 @@
boolean havefast8MPYuv = (maxFastYuvSize.getWidth() * maxFastYuvSize.getHeight()) >
SIZE_8MP_BOUND;
+ // Ensure that max YUV output smaller than 24MP is fast enough
+ // - needs to be at least 10 fps
+ final long MIN_MAXSIZE_DURATION_BOUND_NS = 100000000; // 100 ms, 10 fps
+ long maxYuvRate = maxFastYuvRate;
+ if (maxSlowYuvSizeLessThan24M != null) {
+ maxYuvRate = config.getOutputMinFrameDuration(
+ ImageFormat.YUV_420_888, maxSlowYuvSizeLessThan24M);
+ }
+ boolean haveMaxYuvRate = maxYuvRate <= MIN_MAXSIZE_DURATION_BOUND_NS;
+
// Ensure that there's an FPS range that's fast enough to capture at above
// minFrameDuration, for full-auto bursts at the fast resolutions
Range[] fpsRanges = CameraTestUtils.getValueNotNull(
@@ -1616,13 +1627,13 @@
"All necessary depth fields defined, but DEPTH_OUTPUT capability is not listed",
!hasFields);
- boolean reportCalibration = poseTranslation != null ||
- poseRotation != null || cameraIntrinsics !=null;
+ boolean reportCalibration = poseTranslation != null ||
+ poseRotation != null || cameraIntrinsics !=null;
// Verify calibration keys are co-existing
if (reportCalibration) {
mCollector.expectTrue(
"Calibration keys must be co-existing",
- poseTranslation != null && poseRotation != null &&
+ poseTranslation != null && poseRotation != null &&
cameraIntrinsics !=null);
}
@@ -1630,7 +1641,7 @@
if (reportDistortion) {
mCollector.expectTrue(
"Calibration keys must present where distortion is reported",
- reportCalibration);
+ reportCalibration);
}
}
counter++;
@@ -2136,15 +2147,21 @@
// Verify that if multiple focal lengths or apertures are supported, they are in
// ascending order.
- float[] focalLengths = c.get(CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS);
- for (int i = 0; i < focalLengths.length-1; i++) {
- mCollector.expectTrue("Camera's available focal lengths must be ascending!",
- focalLengths[i] < focalLengths[i+1]);
- }
- float[] apertures = c.get(CameraCharacteristics.LENS_INFO_AVAILABLE_APERTURES);
- for (int i = 0; i < apertures.length-1; i++) {
- mCollector.expectTrue("Camera's available apertures must be ascending!",
- apertures[i] < apertures[i+1]);
+ Integer hwLevel = c.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
+ boolean isExternalCamera = (hwLevel ==
+ CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL);
+ if (!isExternalCamera) {
+ float[] focalLengths = c.get(
+ CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS);
+ for (int i = 0; i < focalLengths.length-1; i++) {
+ mCollector.expectTrue("Camera's available focal lengths must be ascending!",
+ focalLengths[i] < focalLengths[i+1]);
+ }
+ float[] apertures = c.get(CameraCharacteristics.LENS_INFO_AVAILABLE_APERTURES);
+ for (int i = 0; i < apertures.length-1; i++) {
+ mCollector.expectTrue("Camera's available apertures must be ascending!",
+ apertures[i] < apertures[i+1]);
+ }
}
counter++;
}
@@ -2230,6 +2247,61 @@
}
/**
+ * Check camera orientation against device orientation
+ */
+ @CddTest(requirement="7.5.5/C-1-1")
+ public void testCameraOrientationAlignedWithDevice() {
+ WindowManager windowManager =
+ (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
+ Display display = windowManager.getDefaultDisplay();
+ DisplayMetrics metrics = new DisplayMetrics();
+ display.getMetrics(metrics);
+
+ // For square screen, test is guaranteed to pass
+ if (metrics.widthPixels == metrics.heightPixels) {
+ return;
+ }
+
+ // Handle display rotation
+ int displayRotation = display.getRotation();
+ if (displayRotation == Surface.ROTATION_90 || displayRotation == Surface.ROTATION_270) {
+ int tmp = metrics.widthPixels;
+ metrics.widthPixels = metrics.heightPixels;
+ metrics.heightPixels = tmp;
+ }
+ boolean isDevicePortrait = metrics.widthPixels < metrics.heightPixels;
+
+ int counter = 0;
+ for (CameraCharacteristics c : mCharacteristics) {
+ // Camera size
+ Size pixelArraySize = c.get(CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE);
+ // Camera orientation
+ int sensorOrientation = c.get(CameraCharacteristics.SENSOR_ORIENTATION);
+
+ // For square sensor, test is guaranteed to pass
+ if (pixelArraySize.getWidth() == pixelArraySize.getHeight()) {
+ counter++;
+ continue;
+ }
+
+ // Camera size adjusted for device native orientation.
+ Size adjustedSensorSize;
+ if (sensorOrientation == 90 || sensorOrientation == 270) {
+ adjustedSensorSize = new Size(
+ pixelArraySize.getHeight(), pixelArraySize.getWidth());
+ } else {
+ adjustedSensorSize = pixelArraySize;
+ }
+
+ boolean isCameraPortrait =
+ adjustedSensorSize.getWidth() < adjustedSensorSize.getHeight();
+ assertFalse("Camera " + mAllCameraIds[counter] + "'s long dimension must "
+ + "align with screen's long dimension", isDevicePortrait^isCameraPortrait);
+ counter++;
+ }
+ }
+
+ /**
* Get lens distortion coefficients, as a list of 6 floats; returns null if no valid
* distortion field is available
*/
diff --git a/tests/camera/src/android/hardware/camera2/cts/HeifWriterTest.java b/tests/camera/src/android/hardware/camera2/cts/HeifWriterTest.java
index 131bf79..060c022 100644
--- a/tests/camera/src/android/hardware/camera2/cts/HeifWriterTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/HeifWriterTest.java
@@ -60,11 +60,13 @@
private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ private String mFilePath;
private static final String OUTPUT_FILENAME = "output.heic";
@Override
public void setUp() throws Exception {
super.setUp();
+ mFilePath = mContext.getExternalFilesDir(null).getPath();
}
@Override
@@ -116,8 +118,7 @@
String filename = "Cam" + id + "_" + width + "x" + height +
"_" + cap + ".heic";
builder.setOutputPath(
- new File(Environment.getExternalStorageDirectory(),
- filename).getAbsolutePath());
+ new File(mFilePath, filename).getAbsolutePath());
TestConfig config = builder.build();
try {
@@ -132,6 +133,9 @@
.build();
} catch (IOException e) {
// Continue in case the size is not supported
+ sessionFailure = true;
+ Log.i(TAG, "Skip due to heifWriter creation failure: "
+ + e.getMessage());
continue;
}
diff --git a/tests/camera/src/android/hardware/camera2/cts/LogicalCameraDeviceTest.java b/tests/camera/src/android/hardware/camera2/cts/LogicalCameraDeviceTest.java
index 016ad3b..c1793fe 100644
--- a/tests/camera/src/android/hardware/camera2/cts/LogicalCameraDeviceTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/LogicalCameraDeviceTest.java
@@ -116,7 +116,17 @@
List<OutputConfiguration> outputs = new ArrayList<>();
OutputConfiguration outputConfig = new OutputConfiguration(
imageReader.getSurface());
+ OutputConfiguration outputConfigCopy =
+ new OutputConfiguration(imageReader.getSurface());
+ assertTrue("OutputConfiguration must be equal to its copy",
+ outputConfig.equals(outputConfigCopy));
outputConfig.setPhysicalCameraId(id);
+ assertFalse("OutputConfigurations with different physical Ids must be different",
+ outputConfig.equals(outputConfigCopy));
+ String idCopy = new String(id);
+ outputConfigCopy.setPhysicalCameraId(idCopy);
+ assertTrue("OutputConfigurations with same physical Ids must be equal",
+ outputConfig.equals(outputConfigCopy));
// Regardless of logical camera or non-logical camera, create a session of an
// output configuration with invalid physical camera id, verify that the
diff --git a/tests/camera/src/android/hardware/camera2/cts/NativeCameraDeviceTest.java b/tests/camera/src/android/hardware/camera2/cts/NativeCameraDeviceTest.java
index 3c3a167..2c452d2 100644
--- a/tests/camera/src/android/hardware/camera2/cts/NativeCameraDeviceTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/NativeCameraDeviceTest.java
@@ -113,6 +113,12 @@
testCameraDeviceLogicalPhysicalSettingsNative(mPreviewSurface));
}
+ @Test
+ public void testCameraDeviceCaptureFailure() {
+ assertTrue("testCameraDeviceCaptureFailure fail, see log for details",
+ testCameraDeviceCaptureFailureNative());
+ }
+
private static native boolean testCameraDeviceOpenAndCloseNative();
private static native boolean testCameraDeviceCreateCaptureRequestNative();
private static native boolean testCameraDeviceSessionOpenAndCloseNative(Surface preview);
@@ -122,5 +128,6 @@
private static native boolean testCameraDeviceSharedOutputUpdate(Surface src, Surface dst);
private static native boolean testCameraDeviceLogicalPhysicalStreamingNative(Surface preview);
private static native boolean testCameraDeviceLogicalPhysicalSettingsNative(Surface preview);
+ private static native boolean testCameraDeviceCaptureFailureNative();
}
diff --git a/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java b/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java
index 6f2d488..bb78760 100644
--- a/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java
+++ b/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java
@@ -1430,6 +1430,38 @@
}
/**
+ * Get the largest size by area within (less than) bound
+ *
+ * @param sizes an array of sizes, must have at least 1 element
+ *
+ * @return Largest Size. Null if no such size exists within bound.
+ *
+ * @throws IllegalArgumentException if sizes was null or had 0 elements, or bound is invalid.
+ */
+ public static Size getMaxSizeWithBound(Size[] sizes, int bound) {
+ if (sizes == null || sizes.length == 0) {
+ throw new IllegalArgumentException("sizes was empty");
+ }
+ if (bound <= 0) {
+ throw new IllegalArgumentException("bound is invalid");
+ }
+
+ Size sz = null;
+ for (Size size : sizes) {
+ if (size.getWidth() * size.getHeight() >= bound) {
+ continue;
+ }
+
+ if (sz == null ||
+ size.getWidth() * size.getHeight() > sz.getWidth() * sz.getHeight()) {
+ sz = size;
+ }
+ }
+
+ return sz;
+ }
+
+ /**
* Returns true if the given {@code array} contains the given element.
*
* @param array {@code array} to check for {@code elem}
diff --git a/tests/contentcaptureservice/Android.mk b/tests/contentcaptureservice/Android.mk
index da511248..bca3c1f 100644
--- a/tests/contentcaptureservice/Android.mk
+++ b/tests/contentcaptureservice/Android.mk
@@ -24,6 +24,7 @@
LOCAL_STATIC_JAVA_LIBRARIES := \
androidx.annotation_annotation \
+ androidx.test.ext.junit \
compatibility-device-util-axt \
ctstestrunner-axt \
truth-prebuilt \
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/AbstractContentCaptureIntegrationTest.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/AbstractContentCaptureIntegrationTest.java
index cf941ed..9a9c743 100644
--- a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/AbstractContentCaptureIntegrationTest.java
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/AbstractContentCaptureIntegrationTest.java
@@ -35,7 +35,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.compatibility.common.util.DeviceConfigStateChangerRule;
import com.android.compatibility.common.util.DeviceConfigStateManager;
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CustomView.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CustomView.java
index cdc9b27..18e4cbb 100644
--- a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CustomView.java
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CustomView.java
@@ -39,13 +39,18 @@
super(context, attrs);
}
- public void onProvideContentCaptureStructure(ViewStructure structure) {
+ public void onProvideContentCaptureStructure(@NonNull ViewStructure structure) {
+ Log.v(TAG, "onProvideContentCaptureStructure(): delegate=" + mDelegate);
if (mDelegate != null) {
Log.d(TAG, "onProvideContentCaptureStructure(): delegating");
structure.setClassName(getAccessibilityClassName().toString());
mDelegate.visit(structure);
Log.d(TAG, "onProvideContentCaptureStructure(): delegated");
}
+ else {
+ Log.d(TAG, "onProvideContentCaptureStructure(): explicitly setting class name");
+ structure.setClassName(getAccessibilityClassName().toString());
+ }
}
@Override
@@ -60,7 +65,9 @@
@Override
public CharSequence getAccessibilityClassName() {
- return CustomView.class.getName();
+ final String name = CustomView.class.getName();
+ Log.d(TAG, "getAccessibilityClassName(): " + name);
+ return name;
}
void setContentCaptureDelegate(@NonNull Visitor<ViewStructure> delegate) {
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CustomViewActivity.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CustomViewActivity.java
index 7c38342..69e2236 100644
--- a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CustomViewActivity.java
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CustomViewActivity.java
@@ -25,6 +25,7 @@
import android.contentcaptureservice.cts.CtsContentCaptureService.Session;
import android.contentcaptureservice.cts.common.DoubleVisitor;
+import android.contentcaptureservice.cts.common.Visitor;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
@@ -40,6 +41,9 @@
private static final String TAG = CustomViewActivity.class.getSimpleName();
private static DoubleVisitor<CustomView, ViewStructure> sCustomViewDelegate;
+ private static Visitor<CustomViewActivity> sRootViewVisitor;
+
+ private static boolean sFlagSecure;
/**
* Mininum number of events generated when the activity starts.
@@ -59,6 +63,14 @@
sCustomViewDelegate = delegate;
}
+ static void onRootView(@NonNull Visitor<CustomViewActivity> visitor) {
+ sRootViewVisitor = visitor;
+ }
+
+ static void setFlagSecure(boolean hasFlagSecure) {
+ sFlagSecure = hasFlagSecure;
+ }
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -70,6 +82,13 @@
mCustomView.setContentCaptureDelegate(
(structure) -> sCustomViewDelegate.visit(mCustomView, structure));
}
+ if (sRootViewVisitor != null) {
+ try {
+ sRootViewVisitor.visit(this);
+ } finally {
+ sRootViewVisitor = null;
+ }
+ }
}
@Override
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CustomViewActivityTest.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CustomViewActivityTest.java
index d725850..828df19 100644
--- a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CustomViewActivityTest.java
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CustomViewActivityTest.java
@@ -26,6 +26,7 @@
import static android.contentcaptureservice.cts.Helper.MY_PACKAGE;
import static android.contentcaptureservice.cts.Helper.OTHER_PACKAGE;
import static android.contentcaptureservice.cts.Helper.await;
+import static android.contentcaptureservice.cts.Helper.eventually;
import static android.contentcaptureservice.cts.common.ActivitiesWatcher.ActivityLifecycle.DESTROYED;
import static android.contentcaptureservice.cts.common.ActivitiesWatcher.ActivityLifecycle.RESUMED;
import static android.view.contentcapture.ContentCaptureCondition.FLAG_IS_REGEX;
@@ -43,11 +44,14 @@
import android.util.Log;
import android.view.View;
import android.view.ViewStructure;
+import android.view.WindowManager;
import android.view.autofill.AutofillId;
import android.view.contentcapture.ContentCaptureCondition;
+import android.view.contentcapture.ContentCaptureContext;
import android.view.contentcapture.ContentCaptureEvent;
import android.view.contentcapture.ContentCaptureManager;
import android.view.contentcapture.ContentCaptureSession;
+import android.view.contentcapture.ContentCaptureSessionId;
import androidx.annotation.NonNull;
import androidx.test.rule.ActivityTestRule;
@@ -454,6 +458,61 @@
assertThat(actualConditions).isNull();
}
+ @Test
+ public void testContentCaptureEnabled_dynamicFlagSecure() throws Exception {
+ final CtsContentCaptureService service = enableService();
+ final ActivityWatcher watcher = startWatcher();
+
+ final CustomViewActivity activity = launchActivity();
+ watcher.waitFor(RESUMED);
+
+ final ContentCaptureManager mgr = activity.getContentCaptureManager();
+ assertThat(mgr.isContentCaptureEnabled()).isTrue();
+
+ activity.syncRunOnUiThread(() -> {
+ activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);
+ });
+
+ eventually("Waiting for flag secure to be disabled",
+ () -> mgr.isContentCaptureEnabled() ? null : "not_used");
+
+ activity.syncRunOnUiThread(() -> {
+ activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_SECURE);
+ });
+
+ eventually("Waiting for flag secure to be re-enabled",
+ () -> mgr.isContentCaptureEnabled() ? "not_used" : null);
+ }
+
+ @Test
+ public void testDisabledByFlagSecure() throws Exception {
+ final CtsContentCaptureService service = enableService();
+ final ActivityWatcher watcher = startWatcher();
+
+ CustomViewActivity.onRootView((activity) -> {
+ activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);
+ });
+
+ final CustomViewActivity activity = launchActivity();
+ watcher.waitFor(RESUMED);
+
+ final ContentCaptureManager mgr = activity.getContentCaptureManager();
+ assertThat(mgr.isContentCaptureEnabled()).isFalse();
+
+ activity.finish();
+ watcher.waitFor(DESTROYED);
+
+ final Session session = service.getOnlyFinishedSession();
+ assertThat((session.context.getFlags()
+ & ContentCaptureContext.FLAG_DISABLED_BY_FLAG_SECURE) != 0).isTrue();
+
+ final ContentCaptureSessionId sessionId = session.id;
+ assertRightActivity(session, sessionId, activity);
+
+ final List<ContentCaptureEvent> events = session.getEvents();
+ assertThat(events).isEmpty();
+ }
+
/**
* Sets a delegate that will generate the events asynchronously,
* after {@code onProvideContentCaptureStructure()} returns.
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/Helper.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/Helper.java
index a9596b6..88f7e41 100644
--- a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/Helper.java
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/Helper.java
@@ -15,6 +15,8 @@
*/
package android.contentcaptureservice.cts;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
import static com.android.compatibility.common.util.ShellUtils.runShellCommand;
import android.content.ComponentName;
@@ -28,7 +30,6 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import androidx.test.InstrumentationRegistry;
import com.android.compatibility.common.util.Timeout;
@@ -58,7 +59,7 @@
public static final String RESOURCE_STRING_SERVICE_NAME = "config_defaultContentCaptureService";
- public static final Context sContext = InstrumentationRegistry.getTargetContext();
+ public static final Context sContext = getInstrumentation().getTargetContext();
private static final Timeout MY_TIMEOUT = new Timeout("MY_TIMEOUT", GENERIC_TIMEOUT_MS, 2F,
GENERIC_TIMEOUT_MS);
diff --git a/tests/framework/base/windowmanager/AndroidManifest.xml b/tests/framework/base/windowmanager/AndroidManifest.xml
index caae73f..fd4cf03 100644
--- a/tests/framework/base/windowmanager/AndroidManifest.xml
+++ b/tests/framework/base/windowmanager/AndroidManifest.xml
@@ -156,6 +156,12 @@
<activity android:name="android.server.wm.MultiDisplaySystemDecorationTests$ImeTestActivity2" />
<activity android:name="android.server.wm.MultiDisplaySystemDecorationTests$ImeTestActivityWithBrokenContextWrapper" />
+ <activity android:name="android.server.wm.MultiDisplayClientTests$ClientTestActivity" />
+ <activity android:name="android.server.wm.MultiDisplayClientTests$NoRelaunchActivity"
+ android:resizeableActivity="true"
+ android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density|touchscreen"
+ />
+
<activity android:name="android.server.wm.KeyguardLockedTests$ShowWhenLockedImeActivity" />
<activity android:name="android.server.wm.lifecycle.ActivityStarterTests$StandardActivity"
@@ -189,6 +195,7 @@
android:exported="true"/>
<activity android:name="android.server.wm.ActivityViewTest$ActivityViewTestActivity"
+ android:configChanges="keyboardHidden"
android:exported="true"/>
<provider
@@ -277,7 +284,8 @@
<activity android:name="android.server.wm.LocationOnScreenTests$TestActivity"
android:theme="@style/no_starting_window" />
<activity android:name="android.server.wm.LocationInWindowTests$TestActivity" />
- <activity android:name="android.server.wm.EnsureBarContrastTest$TestActivity" />
+ <activity android:name="android.server.wm.EnsureBarContrastTest$TestActivity"
+ android:theme="@style/no_starting_window" />
<activity android:name="android.server.wm.WindowFocusTests$PrimaryActivity" />
<activity android:name="android.server.wm.WindowFocusTests$SecondaryActivity"
android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density" />
diff --git a/tests/framework/base/windowmanager/AndroidTest.xml b/tests/framework/base/windowmanager/AndroidTest.xml
index af1764c..fed61d7 100644
--- a/tests/framework/base/windowmanager/AndroidTest.xml
+++ b/tests/framework/base/windowmanager/AndroidTest.xml
@@ -16,7 +16,7 @@
<configuration description="Config for CTS WindowManager 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="instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
<option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.LocationCheck"/>
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
diff --git a/tests/framework/base/windowmanager/app/AndroidManifest.xml b/tests/framework/base/windowmanager/app/AndroidManifest.xml
index 9ab7e8e..851943e 100755
--- a/tests/framework/base/windowmanager/app/AndroidManifest.xml
+++ b/tests/framework/base/windowmanager/app/AndroidManifest.xml
@@ -61,7 +61,7 @@
android:resizeableActivity="true"
android:allowEmbedded="true"
android:exported="true"
- android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density"
+ android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density|touchscreen"
/>
<activity android:name=".NonResizeableActivity"
android:resizeableActivity="false"
@@ -84,7 +84,7 @@
/>
<activity android:name=".NoRelaunchActivity"
android:resizeableActivity="true"
- android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|fontScale"
+ android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|fontScale|colorMode|density|touchscreen"
android:exported="true"
android:taskAffinity="nobody.but.NoRelaunchActivity"
/>
@@ -289,6 +289,8 @@
android:exported="true"
android:theme="@style/WallpaperTheme"
/>
+ <activity android:name=".InputMethodTestActivity"
+ android:exported="true" />
<activity android:name=".KeyguardLockActivity"
android:exported="true"
/>
diff --git a/tests/framework/base/windowmanager/app/src/android/server/wm/app/Components.java b/tests/framework/base/windowmanager/app/src/android/server/wm/app/Components.java
index 7300411..94b397f 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
@@ -185,6 +185,16 @@
public static final ComponentName SINGLE_TASK_INSTANCE_DISPLAY_ACTIVITY3 =
component("SingleTaskInstanceDisplayActivity3");
+ public static final ComponentName INPUT_METHOD_TEST_ACTIVITY =
+ component("InputMethodTestActivity");
+
+ /**
+ * Action and extra key constants for {@link #INPUT_METHOD_TEST_ACTIVITY}.
+ */
+ public static class InputMethodTestActivity {
+ public static final String EXTRA_PRIVATE_IME_OPTIONS = "private_ime_options";
+ public static final String EXTRA_TEST_CURSOR_ANCHOR_INFO = "cursor_anchor_info";
+ }
/**
* The keys are used for {@link TestJournalProvider} when testing wallpaper
@@ -363,6 +373,8 @@
public static final String EXTRA_START_ACTIVITY = "start_activity";
// Adds a click listener to finish this activity when it is clicked
public static final String EXTRA_TAP_TO_FINISH = "tap_to_finish";
+ // Dismiss keyguard when activity show.
+ public static final String EXTRA_DISMISS_KEYGUARD = "dismiss_keyguard";
}
/**
diff --git a/tests/framework/base/windowmanager/app/src/android/server/wm/app/InputMethodTestActivity.java b/tests/framework/base/windowmanager/app/src/android/server/wm/app/InputMethodTestActivity.java
new file mode 100644
index 0000000..e690390
--- /dev/null
+++ b/tests/framework/base/windowmanager/app/src/android/server/wm/app/InputMethodTestActivity.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.app;
+
+import static android.server.wm.app.Components.InputMethodTestActivity.EXTRA_PRIVATE_IME_OPTIONS;
+import static android.server.wm.app.Components.InputMethodTestActivity.EXTRA_TEST_CURSOR_ANCHOR_INFO;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+import android.view.inputmethod.CursorAnchorInfo;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputConnection;
+import android.view.inputmethod.InputConnectionWrapper;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.LinearLayout;
+
+import com.android.compatibility.common.util.ImeAwareEditText;
+
+public final class InputMethodTestActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ final Intent intent = getIntent();
+ final CursorAnchorInfo info = intent.getParcelableExtra(EXTRA_TEST_CURSOR_ANCHOR_INFO);
+
+ final LinearLayout layout = new LinearLayout(this);
+ layout.setOrientation(LinearLayout.VERTICAL);
+
+ final ImeAwareEditText editText = new ImeAwareEditText(this) {
+ @Override
+ public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
+ final InputConnection original = super.onCreateInputConnection(outAttrs);
+ final View view = this;
+ return new InputConnectionWrapper(original, false) {
+ @Override
+ public boolean requestCursorUpdates(int cursorUpdateMode) {
+ if (info != null) {
+ // If EXTRA_TEST_CURSOR_ANCHOR_INFO is provided, then call
+ // IMM#updateCursorAnchorInfo() with that object.
+ view.post(() -> view.getContext()
+ .getSystemService(InputMethodManager.class)
+ .updateCursorAnchorInfo(view, info));
+ return true;
+ }
+ return super.requestCursorUpdates(cursorUpdateMode);
+ }
+ };
+ }
+ };
+
+ final String privateImeOption = intent.getStringExtra(EXTRA_PRIVATE_IME_OPTIONS);
+ if (privateImeOption != null) {
+ editText.setPrivateImeOptions(privateImeOption);
+ }
+ layout.addView(editText);
+ editText.scheduleShowSoftInput();
+ editText.requestFocus();
+
+ setContentView(layout);
+ }
+}
diff --git a/tests/framework/base/windowmanager/app/src/android/server/wm/app/PipActivity.java b/tests/framework/base/windowmanager/app/src/android/server/wm/app/PipActivity.java
index c9800fa..5f9dcc1 100644
--- a/tests/framework/base/windowmanager/app/src/android/server/wm/app/PipActivity.java
+++ b/tests/framework/base/windowmanager/app/src/android/server/wm/app/PipActivity.java
@@ -25,6 +25,7 @@
import static android.server.wm.app.Components.PipActivity.ACTION_MOVE_TO_BACK;
import static android.server.wm.app.Components.PipActivity.ACTION_SET_REQUESTED_ORIENTATION;
import static android.server.wm.app.Components.PipActivity.EXTRA_ASSERT_NO_ON_STOP_BEFORE_PIP;
+import static android.server.wm.app.Components.PipActivity.EXTRA_DISMISS_KEYGUARD;
import static android.server.wm.app.Components.PipActivity.EXTRA_ENTER_PIP;
import static android.server.wm.app.Components.PipActivity.EXTRA_ENTER_PIP_ASPECT_RATIO_DENOMINATOR;
import static android.server.wm.app.Components.PipActivity.EXTRA_ENTER_PIP_ASPECT_RATIO_NUMERATOR;
@@ -39,6 +40,7 @@
import static android.server.wm.app.Components.PipActivity.EXTRA_SHOW_OVER_KEYGUARD;
import static android.server.wm.app.Components.PipActivity.EXTRA_START_ACTIVITY;
import static android.server.wm.app.Components.PipActivity.EXTRA_TAP_TO_FINISH;
+import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD;
import android.app.Activity;
import android.app.ActivityOptions;
@@ -119,6 +121,11 @@
setShowWhenLocked(true);
}
+ // Set the window flag to dismiss the keyguard
+ if (getIntent().hasExtra(EXTRA_DISMISS_KEYGUARD)) {
+ getWindow().addFlags(FLAG_DISMISS_KEYGUARD);
+ }
+
boolean enteringPip = false;
// Enter picture in picture with the given aspect ratio if provided
if (getIntent().hasExtra(EXTRA_ENTER_PIP)) {
diff --git a/tests/framework/base/windowmanager/backgroundactivity/AndroidTest.xml b/tests/framework/base/windowmanager/backgroundactivity/AndroidTest.xml
index 3e9a842..75a53e3 100644
--- a/tests/framework/base/windowmanager/backgroundactivity/AndroidTest.xml
+++ b/tests/framework/base/windowmanager/backgroundactivity/AndroidTest.xml
@@ -18,14 +18,18 @@
<configuration description="Config for CTS starting background activity 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="instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
<option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.LocationCheck" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
- <!-- TODO(b/129909356): Consolidate this to CtsWindowManagerDeviceTestCases.apk -->
- <option name="test-file-name" value="CtsActivityManagerBackgroundActivityTestCases.apk" />
+ <option name="install-arg" value="-t" />
<option name="test-file-name" value="CtsBackgroundActivityAppA.apk" />
+ </target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <!-- TODO(b/129909356): Rename to CtsWindowManagerDeviceTestCases.apk -->
+ <option name="test-file-name" value="CtsActivityManagerBackgroundActivityTestCases.apk" />
<option name="test-file-name" value="CtsBackgroundActivityAppB.apk" />
</target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest">
diff --git a/tests/framework/base/windowmanager/backgroundactivity/AppA/AndroidManifest.xml b/tests/framework/base/windowmanager/backgroundactivity/AppA/AndroidManifest.xml
index 26ca0b2..9e98b64 100755
--- a/tests/framework/base/windowmanager/backgroundactivity/AppA/AndroidManifest.xml
+++ b/tests/framework/base/windowmanager/backgroundactivity/AppA/AndroidManifest.xml
@@ -18,18 +18,32 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android.server.wm.backgroundactivity.appa">
- <application>
+ <application android:testOnly="true">
<receiver
android:name=".StartBackgroundActivityReceiver"
android:exported="true"/>
<receiver
android:name=".SendPendingIntentReceiver"
android:exported="true"/>
+ <receiver
+ android:name=".SimpleAdminReceiver"
+ android:permission="android.permission.BIND_DEVICE_ADMIN">
+ <meta-data android:name="android.app.device_admin"
+ android:resource="@xml/device_admin" />
+ <intent-filter>
+ <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
+ </intent-filter>
+ </receiver>
<activity
android:name=".ForegroundActivity"
+ android:taskAffinity=".am_cts_bg_task_a"
android:exported="true" />
<activity
android:name=".BackgroundActivity"
+ android:taskAffinity=".am_cts_bg_task_b"
+ android:exported="true" />
+ <activity
+ android:name=".SecondBackgroundActivity"
android:exported="true" />
</application>
</manifest>
diff --git a/tests/framework/base/windowmanager/backgroundactivity/AppA/res/xml/device_admin.xml b/tests/framework/base/windowmanager/backgroundactivity/AppA/res/xml/device_admin.xml
new file mode 100644
index 0000000..48177ee
--- /dev/null
+++ b/tests/framework/base/windowmanager/backgroundactivity/AppA/res/xml/device_admin.xml
@@ -0,0 +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.
+ -->
+
+<device-admin xmlns:android="http://schemas.android.com/apk/res/android" android:visible="false">
+ <uses-policies />
+</device-admin>
\ No newline at end of file
diff --git a/tests/framework/base/windowmanager/backgroundactivity/AppA/src/android/server/wm/backgroundactivity/appa/BackgroundActivity.java b/tests/framework/base/windowmanager/backgroundactivity/AppA/src/android/server/wm/backgroundactivity/appa/BackgroundActivity.java
index 7be9161..f913bef 100644
--- a/tests/framework/base/windowmanager/backgroundactivity/AppA/src/android/server/wm/backgroundactivity/appa/BackgroundActivity.java
+++ b/tests/framework/base/windowmanager/backgroundactivity/AppA/src/android/server/wm/backgroundactivity/appa/BackgroundActivity.java
@@ -17,6 +17,8 @@
package android.server.wm.backgroundactivity.appa;
import android.app.Activity;
+import android.os.Bundle;
+import android.util.Log;
/**
* A background activity that will be launched, for testing if app is able to start background
@@ -24,4 +26,23 @@
*/
public class BackgroundActivity extends Activity {
+ public static final String TAG = "BackgroundActivity";
+
+ @Override
+ protected void onCreate(Bundle bundle) {
+ super.onCreate(bundle);
+ Log.i(TAG, "onCreate");
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ Log.i(TAG, "onResume");
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ Log.i(TAG, "onPause");
+ }
}
diff --git a/tests/framework/base/windowmanager/backgroundactivity/AppA/src/android/server/wm/backgroundactivity/appa/Components.java b/tests/framework/base/windowmanager/backgroundactivity/AppA/src/android/server/wm/backgroundactivity/appa/Components.java
index fcd7508..56aa319 100644
--- a/tests/framework/base/windowmanager/backgroundactivity/AppA/src/android/server/wm/backgroundactivity/appa/Components.java
+++ b/tests/framework/base/windowmanager/backgroundactivity/AppA/src/android/server/wm/backgroundactivity/appa/Components.java
@@ -23,18 +23,40 @@
public static final ComponentName APP_A_BACKGROUND_ACTIVITY =
component(Components.class, "BackgroundActivity");
+ public static final ComponentName APP_A_SECOND_BACKGROUND_ACTIVITY =
+ component(Components.class, "SecondBackgroundActivity");
public static final ComponentName APP_A_FOREGROUND_ACTIVITY =
component(Components.class, "ForegroundActivity");
public static final ComponentName APP_A_SEND_PENDING_INTENT_RECEIVER =
component(Components.class, "SendPendingIntentReceiver");
public static final ComponentName APP_A_START_ACTIVITY_RECEIVER =
component(Components.class, "StartBackgroundActivityReceiver");
+ public static final ComponentName APP_A_SIMPLE_ADMIN_RECEIVER =
+ component(Components.class, "SimpleAdminReceiver");
/** Extra key constants for {@link #APP_A_FOREGROUND_ACTIVITY}. */
public static class ForegroundActivity {
public static final String LAUNCH_BACKGROUND_ACTIVITY_EXTRA =
"LAUNCH_BACKGROUND_ACTIVITY_EXTRA";
+ public static final String LAUNCH_SECOND_BACKGROUND_ACTIVITY_EXTRA =
+ "LAUNCH_SECOND_BACKGROUND_ACTIVITY_EXTRA";
public static final String RELAUNCH_FOREGROUND_ACTIVITY_EXTRA =
"RELAUNCH_FOREGROUND_ACTIVITY_EXTRA";
+ public static final String START_ACTIVITY_FROM_FG_ACTIVITY_DELAY_MS_EXTRA =
+ "START_ACTIVITY_FROM_FG_ACTIVITY_DELAY_MS_EXTRA";
+ public static final String START_ACTIVITY_FROM_FG_ACTIVITY_NEW_TASK_EXTRA =
+ "START_ACTIVITY_FROM_FG_ACTIVITY_NEW_TASK_EXTRA";
}
+
+ /** Extra key constants for {@link #APP_A_SEND_PENDING_INTENT_RECEIVER} */
+ public static class SendPendingIntentReceiver {
+ public static final String IS_BROADCAST_EXTRA = "IS_BROADCAST_EXTRA";
+ }
+
+ /** Extra key constants for {@link #APP_A_START_ACTIVITY_RECEIVER} */
+ public static class StartBackgroundActivityReceiver {
+ public static final String START_ACTIVITY_DELAY_MS_EXTRA =
+ "START_ACTIVITY_FROM_FG_ACTIVITY_DELAY_MS_EXTRA";
+ }
+
}
diff --git a/tests/framework/base/windowmanager/backgroundactivity/AppA/src/android/server/wm/backgroundactivity/appa/ForegroundActivity.java b/tests/framework/base/windowmanager/backgroundactivity/AppA/src/android/server/wm/backgroundactivity/appa/ForegroundActivity.java
index a0d943f..377cabf 100644
--- a/tests/framework/base/windowmanager/backgroundactivity/AppA/src/android/server/wm/backgroundactivity/appa/ForegroundActivity.java
+++ b/tests/framework/base/windowmanager/backgroundactivity/AppA/src/android/server/wm/backgroundactivity/appa/ForegroundActivity.java
@@ -17,7 +17,10 @@
package android.server.wm.backgroundactivity.appa;
import static android.server.wm.backgroundactivity.appa.Components.ForegroundActivity.LAUNCH_BACKGROUND_ACTIVITY_EXTRA;
+import static android.server.wm.backgroundactivity.appa.Components.ForegroundActivity.LAUNCH_SECOND_BACKGROUND_ACTIVITY_EXTRA;
import static android.server.wm.backgroundactivity.appa.Components.ForegroundActivity.RELAUNCH_FOREGROUND_ACTIVITY_EXTRA;
+import static android.server.wm.backgroundactivity.appa.Components.ForegroundActivity.START_ACTIVITY_FROM_FG_ACTIVITY_DELAY_MS_EXTRA;
+import static android.server.wm.backgroundactivity.appa.Components.ForegroundActivity.START_ACTIVITY_FROM_FG_ACTIVITY_NEW_TASK_EXTRA;
import android.app.Activity;
import android.content.Intent;
@@ -36,10 +39,31 @@
super.onCreate(bundle);
Intent intent = getIntent();
mRelaunch = intent.getBooleanExtra(RELAUNCH_FOREGROUND_ACTIVITY_EXTRA, false);
+
boolean launchBackground = intent.getBooleanExtra(LAUNCH_BACKGROUND_ACTIVITY_EXTRA, false);
+ final int delay = intent.getIntExtra(START_ACTIVITY_FROM_FG_ACTIVITY_DELAY_MS_EXTRA, 0);
+ boolean newTask = intent.getBooleanExtra(START_ACTIVITY_FROM_FG_ACTIVITY_NEW_TASK_EXTRA, false);
if (launchBackground) {
- Intent newIntent = new Intent();
+ final Intent newIntent = new Intent();
newIntent.setClass(this, BackgroundActivity.class);
+ if (newTask) {
+ newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ }
+ if (delay == 0) {
+ startActivity(newIntent);
+ } else {
+ new Thread(() -> {
+ SystemClock.sleep(delay);
+ startActivity(newIntent);
+ }).start();
+ }
+ }
+
+ boolean launchSecond = intent.getBooleanExtra(
+ LAUNCH_SECOND_BACKGROUND_ACTIVITY_EXTRA, false);
+ if (launchSecond) {
+ Intent newIntent = new Intent();
+ newIntent.setClass(this, SecondBackgroundActivity.class);
startActivity(newIntent);
}
}
diff --git a/tests/framework/base/windowmanager/dummyTests/dummySdk25/src/android/server/wm/DummySdk25Test.java b/tests/framework/base/windowmanager/backgroundactivity/AppA/src/android/server/wm/backgroundactivity/appa/SecondBackgroundActivity.java
similarity index 68%
rename from tests/framework/base/windowmanager/dummyTests/dummySdk25/src/android/server/wm/DummySdk25Test.java
rename to tests/framework/base/windowmanager/backgroundactivity/AppA/src/android/server/wm/backgroundactivity/appa/SecondBackgroundActivity.java
index 6d960f1..1551807 100644
--- a/tests/framework/base/windowmanager/dummyTests/dummySdk25/src/android/server/wm/DummySdk25Test.java
+++ b/tests/framework/base/windowmanager/backgroundactivity/AppA/src/android/server/wm/backgroundactivity/appa/SecondBackgroundActivity.java
@@ -14,17 +14,13 @@
* limitations under the License
*/
-package android.server.wm;
+package android.server.wm.backgroundactivity.appa;
-import org.junit.Ignore;
-import org.junit.Test;
+import android.app.Activity;
-public class DummySdk25Test {
+/**
+ * A distinct background activity for testing which of two activity starts succeeds.
+ */
+public class SecondBackgroundActivity extends Activity {
- @Test
- @Ignore
- public void dummyTest() {
- // TODO(b/129909356): Rename CtsActivityManagerDeviceSdk25TestCases to
- // CtsWindowManagerSdk25TestCases.
- }
}
diff --git a/tests/framework/base/windowmanager/backgroundactivity/AppA/src/android/server/wm/backgroundactivity/appa/SendPendingIntentReceiver.java b/tests/framework/base/windowmanager/backgroundactivity/AppA/src/android/server/wm/backgroundactivity/appa/SendPendingIntentReceiver.java
index 6053d53..3647be4 100644
--- a/tests/framework/base/windowmanager/backgroundactivity/AppA/src/android/server/wm/backgroundactivity/appa/SendPendingIntentReceiver.java
+++ b/tests/framework/base/windowmanager/backgroundactivity/AppA/src/android/server/wm/backgroundactivity/appa/SendPendingIntentReceiver.java
@@ -16,7 +16,11 @@
package android.server.wm.backgroundactivity.appa;
+
import static android.server.wm.backgroundactivity.appa.Components.APP_A_BACKGROUND_ACTIVITY;
+import static android.server.wm.backgroundactivity.appa.Components.APP_A_START_ACTIVITY_RECEIVER;
+import static android.server.wm.backgroundactivity.appa.Components.SendPendingIntentReceiver.IS_BROADCAST_EXTRA;
+import static android.server.wm.backgroundactivity.appa.Components.StartBackgroundActivityReceiver.START_ACTIVITY_DELAY_MS_EXTRA;
import static android.server.wm.backgroundactivity.appb.Components.APP_B_START_PENDING_INTENT_RECEIVER;
import static android.server.wm.backgroundactivity.appb.Components.StartPendingIntentReceiver.PENDING_INTENT_EXTRA;
@@ -31,15 +35,28 @@
public class SendPendingIntentReceiver extends BroadcastReceiver {
@Override
- public void onReceive(Context context, Intent notUsed) {
+ public void onReceive(Context context, Intent receivedIntent) {
+ boolean isBroadcast = receivedIntent.getBooleanExtra(IS_BROADCAST_EXTRA, false);
+ int startActivityDelayMs = receivedIntent.getIntExtra(START_ACTIVITY_DELAY_MS_EXTRA, 0);
- // Create a pendingIntent to launch appA's BackgroundActivity
- Intent newIntent = new Intent();
- newIntent.setComponent(APP_A_BACKGROUND_ACTIVITY);
- newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ final PendingIntent pendingIntent;
+ if (isBroadcast) {
+ // Create a pendingIntent to launch send broadcast to appA and appA will start
+ // background activity.
+ Intent newIntent = new Intent();
+ newIntent.setComponent(APP_A_START_ACTIVITY_RECEIVER);
+ newIntent.putExtra(START_ACTIVITY_DELAY_MS_EXTRA, startActivityDelayMs);
+ pendingIntent = PendingIntent.getBroadcast(context, 0,
+ newIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+ } else {
+ // Create a pendingIntent to launch appA's BackgroundActivity
+ Intent newIntent = new Intent();
+ newIntent.setComponent(APP_A_BACKGROUND_ACTIVITY);
+ newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- PendingIntent pendingIntent = PendingIntent.getActivity(context, 0,
- newIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+ pendingIntent = PendingIntent.getActivity(context, 0,
+ newIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+ }
// Send the pendingIntent to appB
Intent intent = new Intent();
diff --git a/tests/framework/base/windowmanager/dummyTests/dummySdk28/src/android/server/wm/DummySdk28Test.java b/tests/framework/base/windowmanager/backgroundactivity/AppA/src/android/server/wm/backgroundactivity/appa/SimpleAdminReceiver.java
similarity index 67%
copy from tests/framework/base/windowmanager/dummyTests/dummySdk28/src/android/server/wm/DummySdk28Test.java
copy to tests/framework/base/windowmanager/backgroundactivity/AppA/src/android/server/wm/backgroundactivity/appa/SimpleAdminReceiver.java
index 991d082..5cb49d5 100644
--- a/tests/framework/base/windowmanager/dummyTests/dummySdk28/src/android/server/wm/DummySdk28Test.java
+++ b/tests/framework/base/windowmanager/backgroundactivity/AppA/src/android/server/wm/backgroundactivity/appa/SimpleAdminReceiver.java
@@ -14,17 +14,9 @@
* limitations under the License.
*/
-package android.server.wm;
+package android.server.wm.backgroundactivity.appa;
-import org.junit.Ignore;
-import org.junit.Test;
+import android.app.admin.DeviceAdminReceiver;
-public class DummySdk28Test {
-
- @Test
- @Ignore
- public void dummyTest() {
- // TODO(b/129909356): Rename CtsActivityManagerDeviceSdk28TestCases to
- // CtsWindowManagerSdk28TestCases.
- }
-}
+public class SimpleAdminReceiver extends DeviceAdminReceiver {
+}
\ No newline at end of file
diff --git a/tests/framework/base/windowmanager/backgroundactivity/AppA/src/android/server/wm/backgroundactivity/appa/StartBackgroundActivityReceiver.java b/tests/framework/base/windowmanager/backgroundactivity/AppA/src/android/server/wm/backgroundactivity/appa/StartBackgroundActivityReceiver.java
index fd7ec8f..844dd4f 100644
--- a/tests/framework/base/windowmanager/backgroundactivity/AppA/src/android/server/wm/backgroundactivity/appa/StartBackgroundActivityReceiver.java
+++ b/tests/framework/base/windowmanager/backgroundactivity/AppA/src/android/server/wm/backgroundactivity/appa/StartBackgroundActivityReceiver.java
@@ -16,9 +16,12 @@
package android.server.wm.backgroundactivity.appa;
+import static android.server.wm.backgroundactivity.appa.Components.StartBackgroundActivityReceiver.START_ACTIVITY_DELAY_MS_EXTRA;
+
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
+import android.os.SystemClock;
/**
* A class to help test case to start background activity.
@@ -26,7 +29,19 @@
public class StartBackgroundActivityReceiver extends BroadcastReceiver {
@Override
- public void onReceive(Context context, Intent notUsed) {
+ public void onReceive(Context context, Intent intent) {
+ if (!intent.hasExtra(START_ACTIVITY_DELAY_MS_EXTRA)) {
+ startActivityNow(context);
+ return;
+ }
+ final int startActivityDelayMs = intent.getIntExtra(START_ACTIVITY_DELAY_MS_EXTRA, 0);
+ new Thread(() -> {
+ SystemClock.sleep(startActivityDelayMs);
+ startActivityNow(context);
+ }).start();
+ }
+
+ private void startActivityNow(Context context) {
Intent newIntent = new Intent(context, BackgroundActivity.class);
newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(newIntent);
diff --git a/tests/framework/base/windowmanager/backgroundactivity/src/android/server/wm/BackgroundActivityLaunchTest.java b/tests/framework/base/windowmanager/backgroundactivity/src/android/server/wm/BackgroundActivityLaunchTest.java
index dfb4676..3ed4cfe 100644
--- a/tests/framework/base/windowmanager/backgroundactivity/src/android/server/wm/BackgroundActivityLaunchTest.java
+++ b/tests/framework/base/windowmanager/backgroundactivity/src/android/server/wm/BackgroundActivityLaunchTest.java
@@ -16,6 +16,7 @@
package android.server.wm;
+import static android.server.wm.ActivityManagerState.STATE_INITIALIZING;
import static android.server.wm.ActivityManagerState.STATE_RESUMED;
import static android.server.wm.ComponentNameUtils.getActivityName;
import static android.server.wm.UiDeviceUtils.pressHomeButton;
@@ -23,15 +24,27 @@
import static android.server.wm.UiDeviceUtils.pressWakeupButton;
import static android.server.wm.backgroundactivity.appa.Components.APP_A_BACKGROUND_ACTIVITY;
import static android.server.wm.backgroundactivity.appa.Components.APP_A_FOREGROUND_ACTIVITY;
+import static android.server.wm.backgroundactivity.appa.Components.APP_A_SECOND_BACKGROUND_ACTIVITY;
import static android.server.wm.backgroundactivity.appa.Components.APP_A_SEND_PENDING_INTENT_RECEIVER;
+import static android.server.wm.backgroundactivity.appa.Components.APP_A_SIMPLE_ADMIN_RECEIVER;
import static android.server.wm.backgroundactivity.appa.Components.APP_A_START_ACTIVITY_RECEIVER;
import static android.server.wm.backgroundactivity.appa.Components.ForegroundActivity.LAUNCH_BACKGROUND_ACTIVITY_EXTRA;
+import static android.server.wm.backgroundactivity.appa.Components.ForegroundActivity.LAUNCH_SECOND_BACKGROUND_ACTIVITY_EXTRA;
import static android.server.wm.backgroundactivity.appa.Components.ForegroundActivity.RELAUNCH_FOREGROUND_ACTIVITY_EXTRA;
+import static android.server.wm.backgroundactivity.appa.Components.ForegroundActivity.START_ACTIVITY_FROM_FG_ACTIVITY_DELAY_MS_EXTRA;
+import static android.server.wm.backgroundactivity.appa.Components.ForegroundActivity.START_ACTIVITY_FROM_FG_ACTIVITY_NEW_TASK_EXTRA;
+import static android.server.wm.backgroundactivity.appa.Components.SendPendingIntentReceiver.IS_BROADCAST_EXTRA;
+import static android.server.wm.backgroundactivity.appa.Components.StartBackgroundActivityReceiver.START_ACTIVITY_DELAY_MS_EXTRA;
import static android.server.wm.backgroundactivity.appb.Components.APP_B_FOREGROUND_ACTIVITY;
import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
+import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import android.app.ActivityManager;
@@ -53,6 +66,8 @@
import org.junit.rules.TestRule;
import org.junit.runners.model.Statement;
+import java.util.List;
+
/**
* This class covers all test cases for starting/blocking background activities.
* As instrumentation tests started by shell are whitelisted to allow starting background activity,
@@ -64,6 +79,8 @@
@Presubmit
public class BackgroundActivityLaunchTest extends ActivityManagerTestBase {
+ private static final int ACTIVITY_FOCUS_TIMEOUT_MS = 3000;
+
/** Copied from {@link Settings.Global#BACKGROUND_ACTIVITY_STARTS_ENABLED}. */
private static final String BACKGROUND_ACTIVITY_STARTS_ENABLED =
"background_activity_starts_enabled";
@@ -92,6 +109,9 @@
pressWakeupButton();
pressUnlockButton();
removeStacksWithActivityTypes(ALL_ACTIVITY_TYPE_BUT_HOME);
+ assertNull(mAmWmState.getAmState().getTaskByActivity(APP_A_BACKGROUND_ACTIVITY));
+ assertNull(mAmWmState.getAmState().getTaskByActivity(APP_A_FOREGROUND_ACTIVITY));
+ assertNull(mAmWmState.getAmState().getTaskByActivity(APP_B_FOREGROUND_ACTIVITY));
runShellCommand("cmd deviceidle tempwhitelist -d 100000 "
+ APP_A_FOREGROUND_ACTIVITY.getPackageName());
@@ -103,6 +123,12 @@
public void tearDown() throws Exception {
pressHomeButton();
mAmWmState.waitForHomeActivityVisible();
+ runWithShellPermissionIdentity(() -> {
+ runShellCommand("dpm remove-active-admin --user current "
+ + APP_A_SIMPLE_ADMIN_RECEIVER.flattenToString());
+ });
+ // TODO(b/130169434): Remove it when app switch protection bug is fixed
+ SystemClock.sleep(5000);
}
@Test
@@ -111,8 +137,9 @@
Intent intent = new Intent();
intent.setComponent(APP_A_START_ACTIVITY_RECEIVER);
mContext.sendBroadcast(intent);
- boolean result = waitForActivity(APP_A_BACKGROUND_ACTIVITY);
+ boolean result = waitForActivityFocused(APP_A_BACKGROUND_ACTIVITY);
assertFalse("Should not able to launch background activity", result);
+ assertTaskStack(null, APP_A_BACKGROUND_ACTIVITY);
}
@Test
@@ -122,19 +149,22 @@
intent.setComponent(APP_A_FOREGROUND_ACTIVITY);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(intent);
- boolean result = waitForActivity(APP_A_FOREGROUND_ACTIVITY);
+ boolean result = waitForActivityFocused(APP_A_FOREGROUND_ACTIVITY);
assertTrue("Not able to start foreground activity", result);
+ assertTaskStack(new ComponentName[]{APP_A_FOREGROUND_ACTIVITY}, APP_A_FOREGROUND_ACTIVITY);
// Start AppA background activity successfully as there's a foreground activity
intent = new Intent();
intent.setComponent(APP_A_START_ACTIVITY_RECEIVER);
mContext.sendBroadcast(intent);
- result = waitForActivity(APP_A_BACKGROUND_ACTIVITY);
+ result = waitForActivityFocused(APP_A_BACKGROUND_ACTIVITY);
assertTrue("Not able to launch background activity", result);
+ assertTaskStack(new ComponentName[]{APP_A_FOREGROUND_ACTIVITY}, APP_A_FOREGROUND_ACTIVITY);
+ assertTaskStack(new ComponentName[]{APP_A_BACKGROUND_ACTIVITY}, APP_A_BACKGROUND_ACTIVITY);
}
@Test
- public void testActivityNotBlockedwhenForegroundActivityLaunch() throws Exception {
+ public void testActivityNotBlockedWhenForegroundActivityLaunch() throws Exception {
// Start foreground activity, and foreground activity able to launch background activity
// successfully
Intent intent = new Intent();
@@ -142,8 +172,72 @@
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(LAUNCH_BACKGROUND_ACTIVITY_EXTRA, true);
mContext.startActivity(intent);
- boolean result = waitForActivity(APP_A_BACKGROUND_ACTIVITY);
+ boolean result = waitForActivityFocused(APP_A_BACKGROUND_ACTIVITY);
assertTrue("Not able to launch background activity", result);
+ assertTaskStack(new ComponentName[]{APP_A_BACKGROUND_ACTIVITY, APP_A_FOREGROUND_ACTIVITY},
+ APP_A_FOREGROUND_ACTIVITY);
+ }
+
+ @Test
+ public void testActivityNotBlockedwhenForegroundActivityLaunchInSameTask() throws Exception {
+ // Start foreground activity, and foreground activity able to launch background activity
+ // successfully
+ Intent intent = new Intent();
+ intent.setComponent(APP_A_FOREGROUND_ACTIVITY);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.putExtra(LAUNCH_BACKGROUND_ACTIVITY_EXTRA, true);
+ intent.putExtra(START_ACTIVITY_FROM_FG_ACTIVITY_DELAY_MS_EXTRA, 7000);
+ mContext.startActivity(intent);
+ boolean result = waitForActivityFocused(ACTIVITY_FOCUS_TIMEOUT_MS,
+ APP_A_FOREGROUND_ACTIVITY);
+ assertTrue("Not able to launch background activity", result);
+ assertTaskStack(new ComponentName[]{APP_A_FOREGROUND_ACTIVITY}, APP_A_FOREGROUND_ACTIVITY);
+
+ // The foreground activity will be paused but will attempt to restart itself in onPause()
+ pressHomeButton();
+ mAmWmState.waitForHomeActivityVisible();
+
+ // Any activity launch will be blocked for 5s because of app switching protection.
+ SystemClock.sleep(7000);
+
+ result = waitForActivityFocused(APP_A_FOREGROUND_ACTIVITY);
+ assertFalse("Previously foreground Activity should not be able to relaunch itself", result);
+ result = waitForActivityFocused(APP_A_BACKGROUND_ACTIVITY);
+ assertFalse("Previously foreground Activity should not be able to relaunch itself", result);
+ assertTaskStack(new ComponentName[]{APP_A_BACKGROUND_ACTIVITY, APP_A_FOREGROUND_ACTIVITY},
+ APP_A_FOREGROUND_ACTIVITY);
+ }
+
+ @Test
+ public void testActivityNotBlockedWhenForegroundActivityLaunchInDifferentTask()
+ throws Exception {
+ // Start foreground activity, and foreground activity able to launch background activity
+ // successfully
+ Intent intent = new Intent();
+ intent.setComponent(APP_A_FOREGROUND_ACTIVITY);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.putExtra(LAUNCH_BACKGROUND_ACTIVITY_EXTRA, true);
+ intent.putExtra(START_ACTIVITY_FROM_FG_ACTIVITY_DELAY_MS_EXTRA, 7000);
+ intent.putExtra(START_ACTIVITY_FROM_FG_ACTIVITY_NEW_TASK_EXTRA, true);
+ mContext.startActivity(intent);
+ boolean result = waitForActivityFocused(ACTIVITY_FOCUS_TIMEOUT_MS,
+ APP_A_FOREGROUND_ACTIVITY);
+ assertTrue("Not able to launch background activity", result);
+ assertTaskStack(new ComponentName[]{APP_A_FOREGROUND_ACTIVITY}, APP_A_FOREGROUND_ACTIVITY);
+
+ // The foreground activity will be paused but will attempt to restart itself in onPause()
+ pressHomeButton();
+ mAmWmState.waitForHomeActivityVisible();
+
+ // Any activity launch will be blocked for 5s because of app switching protection.
+ SystemClock.sleep(7000);
+
+ result = waitForActivityFocused(APP_A_FOREGROUND_ACTIVITY);
+ assertFalse("Previously foreground Activity should not be able to relaunch itself", result);
+ result = waitForActivityFocused(APP_A_BACKGROUND_ACTIVITY);
+ assertFalse("Previously foreground Activity should not be able to relaunch itself", result);
+ assertTaskStack(new ComponentName[]{APP_A_FOREGROUND_ACTIVITY}, APP_A_FOREGROUND_ACTIVITY);
+ assertTaskStack(null, APP_A_BACKGROUND_ACTIVITY);
}
@Test
@@ -155,8 +249,9 @@
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(RELAUNCH_FOREGROUND_ACTIVITY_EXTRA, true);
mContext.startActivity(intent);
- boolean result = waitForActivity(APP_A_FOREGROUND_ACTIVITY);
+ boolean result = waitForActivityFocused(APP_A_FOREGROUND_ACTIVITY);
assertTrue("Not able to start foreground activity", result);
+ assertTaskStack(new ComponentName[]{APP_A_FOREGROUND_ACTIVITY}, APP_A_FOREGROUND_ACTIVITY);
// The foreground activity will be paused but will attempt to restart itself in onPause()
pressHomeButton();
@@ -165,18 +260,39 @@
// Any activity launch will be blocked for 5s because of app switching protection.
SystemClock.sleep(5000);
- result = waitForActivity(APP_A_FOREGROUND_ACTIVITY);
+ result = waitForActivityFocused(APP_A_FOREGROUND_ACTIVITY);
assertFalse("Previously foreground Activity should not be able to relaunch itself", result);
+ assertTaskStack(new ComponentName[]{APP_A_FOREGROUND_ACTIVITY}, APP_A_FOREGROUND_ACTIVITY);
+ }
+
+ @Test
+ public void testSecondActivityNotBlockedWhenForegroundActivityLaunch() throws Exception {
+ // Start AppA foreground activity, which will immediately launch one activity
+ // and then the second.
+ Intent intent = new Intent();
+ intent.setComponent(APP_A_FOREGROUND_ACTIVITY);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.putExtra(LAUNCH_BACKGROUND_ACTIVITY_EXTRA, true);
+ intent.putExtra(LAUNCH_SECOND_BACKGROUND_ACTIVITY_EXTRA, true);
+ mContext.startActivity(intent);
+
+ boolean result = waitForActivityFocused(APP_A_SECOND_BACKGROUND_ACTIVITY);
+ assertTrue("Not able to launch second background activity", result);
+
+ waitAndAssertActivityState(APP_A_BACKGROUND_ACTIVITY, STATE_INITIALIZING,
+ "First activity should have been created");
+ assertTaskStack(
+ new ComponentName[]{APP_A_SECOND_BACKGROUND_ACTIVITY, APP_A_BACKGROUND_ACTIVITY,
+ APP_A_FOREGROUND_ACTIVITY}, APP_A_FOREGROUND_ACTIVITY);
}
@Test
public void testPendingIntentActivityBlocked() throws Exception {
// Cannot start activity by pending intent, as both appA and appB are in background
- Intent intent = new Intent();
- intent.setComponent(APP_A_SEND_PENDING_INTENT_RECEIVER);
- mContext.sendBroadcast(intent);
- boolean result = waitForActivity(APP_A_BACKGROUND_ACTIVITY);
+ sendPendingIntentActivity();
+ boolean result = waitForActivityFocused(APP_A_BACKGROUND_ACTIVITY);
assertFalse("Should not able to launch background activity", result);
+ assertTaskStack(null, APP_A_BACKGROUND_ACTIVITY);
}
@Test
@@ -187,16 +303,17 @@
intent.setComponent(APP_A_FOREGROUND_ACTIVITY);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(intent);
- boolean result = waitForActivity(APP_A_FOREGROUND_ACTIVITY);
+ boolean result = waitForActivityFocused(APP_A_FOREGROUND_ACTIVITY);
assertTrue("Not able to start foreground Activity", result);
+ assertTaskStack(new ComponentName[]{APP_A_FOREGROUND_ACTIVITY}, APP_A_FOREGROUND_ACTIVITY);
// Send pendingIntent from AppA to AppB, and the AppB launch the pending intent to start
// activity in App A
- intent = new Intent();
- intent.setComponent(APP_A_SEND_PENDING_INTENT_RECEIVER);
- mContext.sendBroadcast(intent);
- result = waitForActivity(APP_A_BACKGROUND_ACTIVITY);
+ sendPendingIntentActivity();
+ result = waitForActivityFocused(APP_A_BACKGROUND_ACTIVITY);
assertTrue("Not able to launch background activity", result);
+ assertTaskStack(new ComponentName[]{APP_A_FOREGROUND_ACTIVITY}, APP_A_FOREGROUND_ACTIVITY);
+ assertTaskStack(new ComponentName[]{APP_A_BACKGROUND_ACTIVITY}, APP_A_BACKGROUND_ACTIVITY);
}
@Test
@@ -206,21 +323,135 @@
intent.setComponent(APP_B_FOREGROUND_ACTIVITY);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(intent);
- boolean result = waitForActivity(APP_B_FOREGROUND_ACTIVITY);
+ boolean result = waitForActivityFocused(APP_B_FOREGROUND_ACTIVITY);
assertTrue("Not able to start foreground Activity", result);
+ assertTaskStack(new ComponentName[]{APP_B_FOREGROUND_ACTIVITY}, APP_B_FOREGROUND_ACTIVITY);
// Send pendingIntent from AppA to AppB, and the AppB launch the pending intent to start
// activity in App A
- intent = new Intent();
- intent.setComponent(APP_A_SEND_PENDING_INTENT_RECEIVER);
- mContext.sendBroadcast(intent);
- result = waitForActivity(APP_A_BACKGROUND_ACTIVITY);
+ sendPendingIntentActivity();
+ result = waitForActivityFocused(APP_A_BACKGROUND_ACTIVITY);
assertTrue("Not able to launch background activity", result);
+ assertTaskStack(new ComponentName[]{APP_A_BACKGROUND_ACTIVITY}, APP_A_BACKGROUND_ACTIVITY);
+ assertTaskStack(new ComponentName[]{APP_B_FOREGROUND_ACTIVITY}, APP_B_FOREGROUND_ACTIVITY);
}
- // Return true if the activity is shown within a reasonable time.
- private boolean waitForActivity(ComponentName componentName) {
- mAmWmState.waitForActivityState(componentName, STATE_RESUMED);
+ @Test
+ public void testPendingIntentBroadcastTimeout_noDelay() throws Exception {
+ assertPendingIntentBroadcastTimeoutTest(0, true);
+ }
+
+ @Test
+ public void testPendingIntentBroadcastTimeout_delay1s() throws Exception {
+ assertPendingIntentBroadcastTimeoutTest(1000, true);
+ }
+
+ @Test
+ public void testPendingIntentBroadcastTimeout_delay12s() throws Exception {
+ assertPendingIntentBroadcastTimeoutTest(12000, false);
+ }
+
+ @Test
+ public void testPendingIntentBroadcast_appBIsBackground() throws Exception {
+ // Send pendingIntent from AppA to AppB, and the AppB launch the pending intent to start
+ // activity in App A
+ sendPendingIntentBroadcast(0);
+ boolean result = waitForActivityFocused(APP_A_BACKGROUND_ACTIVITY);
+ assertFalse("Should not able to launch background activity", result);
+ assertTaskStack(null, APP_A_BACKGROUND_ACTIVITY);
+ }
+
+ @Test
+ public void testDeviceOwner() throws Exception {
+ // Send pendingIntent from AppA to AppB, and the AppB launch the pending intent to start
+ // activity in App A
+ String cmdResult = runShellCommand("dpm set-device-owner --user cur "
+ + APP_A_SIMPLE_ADMIN_RECEIVER.flattenToString());
+ assertThat(cmdResult).contains("Success");
+ Intent intent = new Intent();
+ intent.setComponent(APP_A_START_ACTIVITY_RECEIVER);
+ mContext.sendBroadcast(intent);
+ boolean result = waitForActivityFocused(APP_A_BACKGROUND_ACTIVITY);
+ assertTrue("Not able to launch background activity", result);
+ assertTaskStack(new ComponentName[]{APP_A_BACKGROUND_ACTIVITY}, APP_A_BACKGROUND_ACTIVITY);
+ }
+
+ private void assertTaskStack(ComponentName[] expectedComponents,
+ ComponentName sourceComponent) {
+ if (expectedComponents == null) {
+ assertNull(mAmWmState.getAmState().getTaskByActivity(sourceComponent));
+ return;
+ }
+ List<ActivityManagerState.Activity> actual = mAmWmState.getAmState().getTaskByActivity(
+ sourceComponent).mActivities;
+ assertEquals(expectedComponents.length, actual.size());
+ int size = expectedComponents.length;
+ for (int i = 0; i < size; i++) {
+ assertEquals(expectedComponents[i].flattenToShortString(), actual.get(i).getName());
+ }
+ }
+
+ private void assertPendingIntentBroadcastTimeoutTest(int delayMs, boolean expectedResult) {
+ // Start AppB foreground activity
+ Intent intent = new Intent();
+ intent.setComponent(APP_B_FOREGROUND_ACTIVITY);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ mContext.startActivity(intent);
+ boolean result = waitForActivityFocused(APP_B_FOREGROUND_ACTIVITY);
+ assertTrue("Not able to start foreground Activity", result);
+ assertTaskStack(new ComponentName[]{APP_B_FOREGROUND_ACTIVITY}, APP_B_FOREGROUND_ACTIVITY);
+
+ // Send pendingIntent from AppA to AppB, and the AppB launch the pending intent to start
+ // activity in App A
+ sendPendingIntentBroadcast(delayMs);
+ result = waitForActivityFocused(ACTIVITY_FOCUS_TIMEOUT_MS + delayMs,
+ APP_A_BACKGROUND_ACTIVITY);
+ assertEquals(expectedResult, result);
+ if (expectedResult) {
+ assertTaskStack(new ComponentName[]{APP_A_BACKGROUND_ACTIVITY},
+ APP_A_BACKGROUND_ACTIVITY);
+ } else {
+ assertTaskStack(null, APP_A_BACKGROUND_ACTIVITY);
+ }
+ }
+
+ private boolean waitForActivityFocused(ComponentName componentName) {
+ return waitForActivityFocused(ACTIVITY_FOCUS_TIMEOUT_MS, componentName);
+ }
+
+ // Return true if the activity is shown before timeout
+ private boolean waitForActivityFocused(int timeoutMs, ComponentName componentName) {
+ long endTime = System.currentTimeMillis() + timeoutMs;
+ while (endTime > System.currentTimeMillis()) {
+ mAmWmState.getAmState().computeState();
+ mAmWmState.getWmState().computeState();
+ if (mAmWmState.getAmState().hasActivityState(componentName, STATE_RESUMED)) {
+ SystemClock.sleep(200);
+ mAmWmState.getAmState().computeState();
+ mAmWmState.getWmState().computeState();
+ break;
+ }
+ SystemClock.sleep(200);
+ mAmWmState.getAmState().computeState();
+ mAmWmState.getWmState().computeState();
+ }
return getActivityName(componentName).equals(mAmWmState.getAmState().getFocusedActivity());
}
+
+ private void sendPendingIntentActivity() {
+ Intent intent = new Intent();
+ intent.setComponent(APP_A_SEND_PENDING_INTENT_RECEIVER);
+ intent.putExtra(IS_BROADCAST_EXTRA, false);
+ mContext.sendBroadcast(intent);
+ }
+
+ private void sendPendingIntentBroadcast(int delayMs) {
+ Intent intent = new Intent();
+ intent.setComponent(APP_A_SEND_PENDING_INTENT_RECEIVER);
+ intent.putExtra(IS_BROADCAST_EXTRA, true);
+ if (delayMs > 0) {
+ intent.putExtra(START_ACTIVITY_DELAY_MS_EXTRA, delayMs);
+ }
+ mContext.sendBroadcast(intent);
+ }
}
diff --git a/tests/framework/base/windowmanager/dummyTests/Android.mk b/tests/framework/base/windowmanager/dummyTests/Android.mk
deleted file mode 100644
index 16b36e7..0000000
--- a/tests/framework/base/windowmanager/dummyTests/Android.mk
+++ /dev/null
@@ -1,35 +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.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests optional
-
-# TODO(b/130526034): Cleanup CtsActivityManagerDeviceTestCases, once no one refer the test APK.
-LOCAL_PACKAGE_NAME := CtsActivityManagerDeviceTestCases
-LOCAL_SDK_VERSION := test_current
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_STATIC_JAVA_LIBRARIES := androidx.test.rules
-
-# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-
-include $(BUILD_CTS_PACKAGE)
-
-# Build the test APKs using their own makefiles
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/framework/base/windowmanager/dummyTests/AndroidManifest.xml b/tests/framework/base/windowmanager/dummyTests/AndroidManifest.xml
deleted file mode 100644
index 0ae7842..0000000
--- a/tests/framework/base/windowmanager/dummyTests/AndroidManifest.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * 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.
- -->
-
-<manifest
- xmlns:android="http://schemas.android.com/apk/res/android"
- package="android.server.am.cts">
-
- <!--
- TODO(b/130526034): Cleanup CtsActivityManagerDeviceTestCases, once no one refer the test APK.
- -->
- <application android:label="CtsActivityManagerDeviceTestCases">
- <uses-library android:name="android.test.runner" />
- </application>
-
- <instrumentation
- android:name="androidx.test.runner.AndroidJUnitRunner"
- android:label="CTS dummy tests of ActivityManager"
- android:targetPackage="android.server.am.cts" />
-
-</manifest>
diff --git a/tests/framework/base/windowmanager/dummyTests/AndroidTest.xml b/tests/framework/base/windowmanager/dummyTests/AndroidTest.xml
deleted file mode 100644
index e7efca8..0000000
--- a/tests/framework/base/windowmanager/dummyTests/AndroidTest.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<configuration description="Config for dummy CTS ActivityManager test cases">
- <option name="test-suite-tag" value="cts" />
- <option name="config-descriptor:metadata" key="component" value="framework" />
- <!-- This module needs a permission not available to instant apps -->
- <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
- <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
- <target_preparer class="com.android.compatibility.common.tradefed.targetprep.LocationCheck"/>
- <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
- <option name="cleanup-apks" value="true" />
- <!--
- TODO(b/130526034): Cleanup CtsActivityManagerDeviceTestCases, once no one refer the
- test APK.
- -->
- <option name="test-file-name" value="CtsActivityManagerDeviceTestCases.apk" />
- </target_preparer>
- <test class="com.android.tradefed.testtype.AndroidJUnitTest">
- <option name="package" value="android.server.am.cts"/>
- <option name="runtime-hint" value="1m"/>
- </test>
-</configuration>
diff --git a/tests/framework/base/windowmanager/dummyTests/dummySdk25/AndroidManifest.xml b/tests/framework/base/windowmanager/dummyTests/dummySdk25/AndroidManifest.xml
deleted file mode 100644
index 3ca9d97..0000000
--- a/tests/framework/base/windowmanager/dummyTests/dummySdk25/AndroidManifest.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<?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.server.wm.cts.dummy25">
-
- <uses-sdk android:targetSdkVersion="25" />
-
- <!-- TODO(b/129909356): Remove this once CtsActivityManagerDeviceSdk25TestCases is renamed to
- CtsWindowManagerSdk25TestCases -->
- <application android:label="CtsActivityManagerDeviceSdk25TestCases">
- <uses-library android:name="android.test.runner" />
- </application>
-
- <instrumentation
- android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="android.server.wm.cts.dummy25" />
-
-</manifest>
diff --git a/tests/framework/base/windowmanager/dummyTests/dummySdk25/AndroidTest.xml b/tests/framework/base/windowmanager/dummyTests/dummySdk25/AndroidTest.xml
deleted file mode 100644
index 2d9e0fe..0000000
--- a/tests/framework/base/windowmanager/dummyTests/dummySdk25/AndroidTest.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<?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.
--->
-
-<!-- TODO(b/129909356): Remove this once CtsActivityManagerDeviceSdk25TestCases is renamed to
- CtsWindowManagerSdk25TestCases -->
-<configuration description="Config for CTS WindowManager dummy SDK 25 compatibility test cases">
- <option name="test-suite-tag" value="cts" />
- <option name="config-descriptor:metadata" key="component" value="framework" />
- <!-- These tests require targeting API 25 which does not support instant apps -->
- <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
- <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
- <target_preparer class="com.android.compatibility.common.tradefed.targetprep.LocationCheck" />
- <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
- <option name="cleanup-apks" value="true" />
- <option name="test-file-name" value="CtsActivityManagerDeviceSdk25TestCases.apk" />
- </target_preparer>
- <test class="com.android.tradefed.testtype.AndroidJUnitTest">
- <option name="package" value="android.server.wm.cts.dummy25" />
- <option name="runtime-hint" value="1m" />
- </test>
-</configuration>
diff --git a/tests/framework/base/windowmanager/dummyTests/dummySdk28/Android.mk b/tests/framework/base/windowmanager/dummyTests/dummySdk28/Android.mk
deleted file mode 100644
index 5042de3..0000000
--- a/tests/framework/base/windowmanager/dummyTests/dummySdk28/Android.mk
+++ /dev/null
@@ -1,33 +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.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests optional
-
-# TODO(b/129909356): Remove this once CtsActivityManagerDeviceSdk28TestCases is renamed to
-# CtsWindowManagerSdk28TestCases
-LOCAL_PACKAGE_NAME := CtsActivityManagerDeviceSdk28TestCases
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_SDK_VERSION := 28
-
-LOCAL_STATIC_JAVA_LIBRARIES := androidx.test.rules
-
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-
-include $(BUILD_CTS_PACKAGE)
diff --git a/tests/framework/base/windowmanager/dummyTests/dummySdk28/AndroidManifest.xml b/tests/framework/base/windowmanager/dummyTests/dummySdk28/AndroidManifest.xml
deleted file mode 100644
index 7579028..0000000
--- a/tests/framework/base/windowmanager/dummyTests/dummySdk28/AndroidManifest.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<?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.server.wm.cts.dummy28">
-
- <uses-sdk android:targetSdkVersion="28" />
-
- <!-- TODO(b/129909356): Remove this once CtsActivityManagerDeviceSdk28TestCases is renamed to
- CtsWindowManagerSdk28TestCases -->
- <application android:label="CtsActivityManagerDeviceSdk28TestCases">
- <uses-library android:name="android.test.runner" />
- </application>
-
- <instrumentation
- android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="android.server.wm.cts.dummy28" />
-
-</manifest>
diff --git a/tests/framework/base/windowmanager/dummyTests/src/android/server/am/DummyTest.java b/tests/framework/base/windowmanager/dummyTests/src/android/server/am/DummyTest.java
deleted file mode 100644
index 73f138c..0000000
--- a/tests/framework/base/windowmanager/dummyTests/src/android/server/am/DummyTest.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package android.server.am;
-
-import org.junit.Ignore;
-import org.junit.Test;
-
-public class DummyTest {
-
- @Test
- @Ignore
- public void dummyTest() {
- // TODO(b/130526034): Cleanup CtsActivityManagerDeviceTestCases, once no one refer the
- // test APK.
- }
-}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/ActivityManagerGetConfigTests.java b/tests/framework/base/windowmanager/src/android/server/wm/ActivityManagerGetConfigTests.java
index 15c0f7e..fe30e1a 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/ActivityManagerGetConfigTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/ActivityManagerGetConfigTests.java
@@ -383,7 +383,7 @@
@Test
public void testDeviceConfigWithSecondaryDisplay() throws Exception {
VirtualDisplayHelper vd = new VirtualDisplayHelper();
- final int displayId = vd.createAndWaitForPublicDisplay(false);
+ final int displayId = vd.setPublicDisplay(true).createAndWaitForDisplay();
DisplayManager dm = mContext.getSystemService(DisplayManager.class);
Display display = dm.getDisplay(displayId);
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 714bc84..c2deb24 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/ActivityViewTest.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/ActivityViewTest.java
@@ -18,11 +18,20 @@
import static android.server.wm.ActivityManagerState.STATE_RESUMED;
import static android.server.wm.ActivityManagerState.STATE_STOPPED;
+import static android.server.wm.app.Components.INPUT_METHOD_TEST_ACTIVITY;
+import static android.server.wm.app.Components.InputMethodTestActivity.EXTRA_PRIVATE_IME_OPTIONS;
+import static android.server.wm.app.Components.InputMethodTestActivity.EXTRA_TEST_CURSOR_ANCHOR_INFO;
import static android.server.wm.app.Components.TEST_ACTIVITY;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static androidx.test.InstrumentationRegistry.getInstrumentation;
+import static com.android.cts.mockime.ImeEventStreamTestUtils.editorMatcher;
+import static com.android.cts.mockime.ImeEventStreamTestUtils.expectCommand;
+import static com.android.cts.mockime.ImeEventStreamTestUtils.expectEvent;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
@@ -31,38 +40,55 @@
import android.app.Instrumentation;
import android.content.ComponentName;
import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.graphics.Matrix;
+import android.graphics.Point;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.SystemClock;
import android.platform.test.annotations.Presubmit;
import android.view.View;
import android.view.ViewGroup;
+import android.view.inputmethod.CursorAnchorInfo;
+import android.view.inputmethod.InputConnection;
import androidx.test.annotation.UiThreadTest;
import androidx.test.rule.ActivityTestRule;
import com.android.compatibility.common.util.SystemUtil;
+import com.android.cts.mockime.ImeCommand;
+import com.android.cts.mockime.ImeEvent;
+import com.android.cts.mockime.ImeEventStream;
+import com.android.cts.mockime.ImeSettings;
+import com.android.cts.mockime.MockImeSession;
+import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
/**
* Build/Install/Run:
* atest CtsWindowManagerDeviceTestCases:ActivityViewTest
*/
@Presubmit
public class ActivityViewTest extends ActivityManagerTestBase {
+ private static final long IME_EVENT_TIMEOUT = TimeUnit.SECONDS.toMillis(10);
private Instrumentation mInstrumentation;
private ActivityView mActivityView;
@Rule
- public ActivityTestRule<ActivityViewTestActivity> mActivityRule =
- new ActivityTestRule<>(ActivityViewTestActivity.class);
+ public final ActivityTestRule<ActivityViewTestActivity> mActivityRule =
+ new ActivityTestRule<>(ActivityViewTestActivity.class, true /* initialTouchMode */,
+ false /* launchActivity */);
@Before
- public void setup() {
+ public void setUp() throws Exception {
+ super.setUp();
assumeTrue(supportsMultiDisplay());
mInstrumentation = getInstrumentation();
SystemUtil.runWithShellPermissionIdentity(() -> {
@@ -72,6 +98,14 @@
separateTestJournal();
}
+ @After
+ public void tearDown() throws Exception {
+ super.tearDown();
+ if (mActivityView != null) {
+ SystemUtil.runWithShellPermissionIdentity(() -> mActivityView.release());
+ }
+ }
+
@Test
public void testStartActivity() {
launchActivityInActivityView(TEST_ACTIVITY);
@@ -173,10 +207,75 @@
assertLifecycleCounts(TEST_ACTIVITY, 0, 1, 1, 0, 0, 0, CountSpec.DONT_CARE);
}
+ @Test
+ public void testInputMethod() throws Exception {
+ assumeTrue("MockIme cannot be used for devices that do not support installable IMEs",
+ mInstrumentation.getContext().getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_INPUT_METHODS));
+
+ final String uniqueKey =
+ ActivityViewTest.class.getSimpleName() + "/" + SystemClock.elapsedRealtimeNanos();
+
+ final String privateImeOptions = uniqueKey + "/privateImeOptions";
+
+ final CursorAnchorInfo mockResult = new CursorAnchorInfo.Builder()
+ .setMatrix(new Matrix())
+ .setInsertionMarkerLocation(3.0f, 4.0f, 5.0f, 6.0f, 0)
+ .setSelectionRange(7, 8)
+ .build();
+
+ final Bundle extras = new Bundle();
+ extras.putString(EXTRA_PRIVATE_IME_OPTIONS, privateImeOptions);
+ extras.putParcelable(EXTRA_TEST_CURSOR_ANCHOR_INFO, mockResult);
+
+ try (final MockImeSession imeSession = MockImeSession.create(mContext,
+ mInstrumentation.getUiAutomation(), new ImeSettings.Builder())) {
+ final ImeEventStream stream = imeSession.openEventStream();
+ launchActivityInActivityView(INPUT_METHOD_TEST_ACTIVITY, extras);
+
+ // IME's seeing uniqueStringValue means that a valid connection is successfully
+ // established from INPUT_METHOD_TEST_ACTIVITY the MockIme
+ expectEvent(stream, editorMatcher("onStartInput", privateImeOptions),
+ IME_EVENT_TIMEOUT);
+
+ // Make sure that InputConnection#requestCursorUpdates() works.
+ final ImeCommand cursorUpdatesCommand = imeSession.callRequestCursorUpdates(
+ InputConnection.CURSOR_UPDATE_IMMEDIATE);
+ final ImeEvent cursorUpdatesEvent = expectCommand(
+ stream, cursorUpdatesCommand, IME_EVENT_TIMEOUT);
+ assertTrue(cursorUpdatesEvent.getReturnBooleanValue());
+
+ // Make sure that MockIme received the object sent above.
+ final CursorAnchorInfo receivedInfo = expectEvent(stream,
+ event -> "onUpdateCursorAnchorInfo".equals(event.getEventName()),
+ IME_EVENT_TIMEOUT).getArguments().getParcelable("cursorAnchorInfo");
+ assertNotNull(receivedInfo);
+
+ // Get the location of ActivityView in the default display's screen coordinates.
+ final AtomicReference<Point> offsetRef = new AtomicReference<>();
+ mInstrumentation.runOnMainSync(() -> {
+ final int[] xy = new int[2];
+ mActivityView.getLocationOnScreen(xy);
+ offsetRef.set(new Point(xy[0], xy[1]));
+ });
+ final Point offset = offsetRef.get();
+
+ // Make sure that the received CursorAnchorInfo has an adjusted Matrix.
+ final Matrix expectedMatrix = mockResult.getMatrix();
+ expectedMatrix.postTranslate(offset.x, offset.y);
+ assertEquals(expectedMatrix, receivedInfo.getMatrix());
+ }
+ }
+
private void launchActivityInActivityView(ComponentName activity) {
+ launchActivityInActivityView(activity, new Bundle());
+ }
+
+ private void launchActivityInActivityView(ComponentName activity, Bundle extras) {
Intent intent = new Intent();
intent.setComponent(activity);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
+ intent.putExtras(extras);
SystemUtil.runWithShellPermissionIdentity(() -> mActivityView.startActivity(intent));
mAmWmState.waitForValidState(activity);
}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/DisplayCutoutTests.java b/tests/framework/base/windowmanager/src/android/server/wm/DisplayCutoutTests.java
index 612db24..f919ef3 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/DisplayCutoutTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/DisplayCutoutTests.java
@@ -162,12 +162,13 @@
if (displayCutout != null) {
commonAsserts(activity, insets, displayCutout);
- assertCutoutsAreConsistentWithInsets(insets, displayCutout);
+ assertCutoutsAreConsistentWithInsets(displayCutout);
}
test.run(activity, insets, displayCutout, ROOT);
if (dispatchedDisplayCutout != null) {
commonAsserts(activity, dispatchedInsets, dispatchedDisplayCutout);
+ assertCutoutsAreConsistentWithInsets(dispatchedDisplayCutout);
}
test.run(activity, dispatchedInsets, dispatchedDisplayCutout, DISPATCHED);
}
@@ -194,16 +195,13 @@
}
}
- public void assertCutoutsAreConsistentWithInsets(WindowInsets insets, DisplayCutout cutout) {
- final Rect systemWindowInsets = systemWindowInsets(insets);
- assertCutoutIsConsistentWithInset("top", systemWindowInsets.top,
- cutout.getBoundingRectTop());
- assertCutoutIsConsistentWithInset("bottom", systemWindowInsets.bottom,
+ public void assertCutoutsAreConsistentWithInsets(DisplayCutout cutout) {
+ final Rect safeInsets = safeInsets(cutout);
+ assertCutoutIsConsistentWithInset("top", safeInsets.top, cutout.getBoundingRectTop());
+ assertCutoutIsConsistentWithInset("bottom", safeInsets.bottom,
cutout.getBoundingRectBottom());
- assertCutoutIsConsistentWithInset("left", systemWindowInsets.left,
- cutout.getBoundingRectLeft());
- assertCutoutIsConsistentWithInset("right", systemWindowInsets.right,
- cutout.getBoundingRectRight());
+ assertCutoutIsConsistentWithInset("left", safeInsets.left, cutout.getBoundingRectLeft());
+ assertCutoutIsConsistentWithInset("right", safeInsets.right, cutout.getBoundingRectRight());
}
private void assertSafeInsetsValid(DisplayCutout displayCutout) {
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/DisplayTests.java b/tests/framework/base/windowmanager/src/android/server/wm/DisplayTests.java
index a5599e1..2fc2f1e 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/DisplayTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/DisplayTests.java
@@ -20,12 +20,19 @@
import static android.server.wm.app.Components.TEST_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.Assume.assumeFalse;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.hardware.display.DisplayManager;
import android.platform.test.annotations.Presubmit;
import android.server.wm.ActivityManagerState.ActivityDisplay;
import android.util.Size;
+import android.view.Display;
+import androidx.test.filters.FlakyTest;
import org.junit.Test;
@@ -65,6 +72,25 @@
}
}
+ @Test
+ @FlakyTest(bugId = 129521230)
+ public void testNonDefaultDisplayResourcesConfiguration() throws Exception {
+ final int smallDisplaySize = 1000;
+ final int longDisplaySize = 1920;
+
+ // Get land-sized display's resources configuration.
+ final int smallestScreenWidthForLandDisplay = getDisplayResourcesConfiguration(
+ longDisplaySize, smallDisplaySize).smallestScreenWidthDp;
+
+ // Get port-sized display's resources configuration.
+ final int smallestScreenWidthForPortDisplay = getDisplayResourcesConfiguration(
+ smallDisplaySize, longDisplaySize).smallestScreenWidthDp;
+
+ // Check whether smallestScreenWidthDp configuration is proper.
+ assertEquals("smallestScreenWidthDp configuration should be set to smallest possible size.",
+ smallestScreenWidthForLandDisplay, smallestScreenWidthForPortDisplay);
+ }
+
/**
* Tests that launch on secondary display is not permitted if device has the feature disabled.
* Activities requested to be launched on a secondary display in this case should land on the
@@ -151,6 +177,24 @@
}
}
+ private Configuration getDisplayResourcesConfiguration(int displayWidth, int displayHeight)
+ throws Exception {
+ final Context context = getInstrumentation().getContext();
+ final DisplayManager displayManager =
+ (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
+
+ try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+ final ActivityDisplay activityDisplay = virtualDisplaySession
+ .setSimulateDisplay(true)
+ .setSimulationDisplaySize(displayWidth, displayHeight)
+ .createDisplay();
+ final Display display = displayManager.getDisplay(activityDisplay.mId);
+ Configuration config = context.createDisplayContext(display)
+ .getResources().getConfiguration();
+ return config;
+ }
+ }
+
private static class DisplayMetricsSession implements AutoCloseable {
private final ReportedDisplayMetrics mInitialDisplayMetrics;
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/FreeformWindowingModeTests.java b/tests/framework/base/windowmanager/src/android/server/wm/FreeformWindowingModeTests.java
index 0dad1c2..b4eb07f 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/FreeformWindowingModeTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/FreeformWindowingModeTests.java
@@ -35,7 +35,7 @@
/**
* Build/Install/Run:
- * atest CtsActivityManagerDeviceTestCases:FreeformWindowingModeTests
+ * atest CtsWindowManagerDeviceTestCases:FreeformWindowingModeTests
*/
@Presubmit
public class FreeformWindowingModeTests extends MultiDisplayTestBase {
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/KeyguardLockedTests.java b/tests/framework/base/windowmanager/src/android/server/wm/KeyguardLockedTests.java
index e4408f5..f0e0b9e 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/KeyguardLockedTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/KeyguardLockedTests.java
@@ -23,6 +23,7 @@
import static android.server.wm.app.Components.DISMISS_KEYGUARD_METHOD_ACTIVITY;
import static android.server.wm.app.Components.PIP_ACTIVITY;
import static android.server.wm.app.Components.PipActivity.ACTION_ENTER_PIP;
+import static android.server.wm.app.Components.PipActivity.EXTRA_DISMISS_KEYGUARD;
import static android.server.wm.app.Components.PipActivity.EXTRA_ENTER_PIP;
import static android.server.wm.app.Components.PipActivity.EXTRA_SHOW_OVER_KEYGUARD;
import static android.server.wm.app.Components.SHOW_WHEN_LOCKED_ACTIVITY;
@@ -30,11 +31,8 @@
import static android.server.wm.app.Components.TURN_SCREEN_ON_ATTR_DISMISS_KEYGUARD_ACTIVITY;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE;
-
import static androidx.test.InstrumentationRegistry.getInstrumentation;
-
import static com.android.cts.mockime.ImeEventStreamTestUtils.expectEvent;
-
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
@@ -47,16 +45,13 @@
import android.platform.test.annotations.Presubmit;
import android.widget.EditText;
import android.widget.LinearLayout;
-
import com.android.cts.mockime.ImeEventStream;
import com.android.cts.mockime.ImeSettings;
import com.android.cts.mockime.MockImeSession;
-
+import java.util.concurrent.TimeUnit;
import org.junit.Before;
import org.junit.Test;
-import java.util.concurrent.TimeUnit;
-
/**
* Build/Install/Run:
* atest CtsWindowManagerDeviceTestCases:KeyguardLockedTests
@@ -295,6 +290,27 @@
}
@Test
+ public void testDismissKeyguardPipActivity() throws Exception {
+ assumeTrue(supportsPip());
+
+ try (final LockScreenSession lockScreenSession = new LockScreenSession()) {
+ // Show an activity in PIP
+ launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true", EXTRA_DISMISS_KEYGUARD, "true");
+ waitForEnterPip(PIP_ACTIVITY);
+ mAmWmState.assertContainsStack("Must contain pinned stack.", WINDOWING_MODE_PINNED,
+ ACTIVITY_TYPE_STANDARD);
+ mAmWmState.assertVisibility(PIP_ACTIVITY, true);
+
+ // Lock the screen and ensure the PiP activity is not visible on the lockscreen even
+ // though it's marked as dismiss keyguard.
+ lockScreenSession.gotoKeyguard();
+ mAmWmState.computeState(true);
+ mAmWmState.assertKeyguardShowingAndNotOccluded();
+ mAmWmState.assertVisibility(PIP_ACTIVITY, false);
+ }
+ }
+
+ @Test
public void testShowWhenLockedAttrImeActivityAndShowSoftInput() throws Exception {
try (final LockScreenSession lockScreenSession = new LockScreenSession();
// Leverage MockImeSession to ensure at least an IME exists as default.
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 d9ad754..67f1db8 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayActivityLaunchTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayActivityLaunchTests.java
@@ -66,7 +66,7 @@
/**
* Build/Install/Run:
- * atest CtsActivityManagerDeviceTestCases:MultiDisplayActivityLaunchTests
+ * atest CtsWindowManagerDeviceTestCases:MultiDisplayActivityLaunchTests
*
* Tests activity launching behavior on multi-display environment.
*/
@@ -202,8 +202,8 @@
assertEquals("Unexpected resumed activity",
0, mAmWmState.getAmState().getResumedActivitiesCount());
- final ActivityDisplay newDisplay =
- externalDisplaySession.createVirtualDisplay(true /* showContentWhenLocked */);
+ final ActivityDisplay newDisplay = externalDisplaySession
+ .setCanShowWithInsecureKeyguard(true).createVirtualDisplay();
launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId);
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayClientTests.java b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayClientTests.java
new file mode 100644
index 0000000..fd8deb2
--- /dev/null
+++ b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayClientTests.java
@@ -0,0 +1,252 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.server.wm.CommandSession.ActivityCallback.ON_CONFIGURATION_CHANGED;
+import static android.server.wm.CommandSession.ActivityCallback.ON_RESUME;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static com.android.cts.mockime.ImeEventStreamTestUtils.expectCommand;
+import static com.android.cts.mockime.ImeEventStreamTestUtils.expectEvent;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assume.assumeTrue;
+
+import android.app.Activity;
+import android.app.ActivityOptions;
+import android.content.ComponentName;
+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.view.Display;
+import android.view.WindowManager;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+
+import androidx.test.filters.FlakyTest;
+import androidx.test.rule.ActivityTestRule;
+
+import com.android.cts.mockime.ImeEventStream;
+import com.android.cts.mockime.MockImeSession;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Build/Install/Run:
+ * atest CtsActivityManagerDeviceTestCases:MultiDisplayClientTests
+ */
+@Presubmit
+public class MultiDisplayClientTests extends MultiDisplayTestBase {
+
+ private static final long TIMEOUT = TimeUnit.SECONDS.toMillis(5); // 5 seconds
+ private static final String EXTRA_SHOW_IME = "show_ime";
+
+ @Before
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ assumeTrue(supportsMultiDisplay());
+ }
+
+ @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 */);
+ }
+
+ private void testDisplayIdUpdateOnMove(Class<? extends Activity> activityClass,
+ boolean handlesConfigChange) throws Exception {
+ final ActivityTestRule activityTestRule = new ActivityTestRule(
+ activityClass, true /* initialTouchMode */, false /* launchActivity */);
+
+ // Launch activity display.
+ separateTestJournal();
+ Activity activity = activityTestRule.launchActivity(new Intent());
+ final ComponentName activityName = activity.getComponentName();
+ waitAndAssertResume(activityName);
+
+ try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+ // Create new simulated display
+ final ActivityManagerState.ActivityDisplay newDisplay =
+ virtualDisplaySession.setSimulateDisplay(true).createDisplay();
+
+ // Move the activity to the new secondary display.
+ separateTestJournal();
+ final ActivityOptions launchOptions = ActivityOptions.makeBasic();
+ launchOptions.setLaunchDisplayId(newDisplay.mId);
+ final Intent newDisplayIntent = new Intent(mContext, activityClass);
+ newDisplayIntent.setFlags(FLAG_ACTIVITY_NEW_TASK);
+ getInstrumentation().getTargetContext().startActivity(newDisplayIntent,
+ launchOptions.toBundle());
+ waitAndAssertTopResumedActivity(activityName, newDisplay.mId,
+ "Activity moved to secondary display must be focused");
+
+ if (handlesConfigChange) {
+ // Wait for activity to receive the configuration change after move
+ waitAndAssertConfigurationChange(activityName);
+ } else {
+ // Activity will be re-created, wait for resumed state
+ waitAndAssertResume(activityName);
+ activity = activityTestRule.getActivity();
+ }
+ final String message = "Display id must be updated";
+ assertEquals(message, newDisplay.mId, activity.getDisplayId());
+ assertEquals(message, newDisplay.mId, activity.getDisplay().getDisplayId());
+ final WindowManager wm = activity.getWindowManager();
+ assertEquals(message, newDisplay.mId, wm.getDefaultDisplay().getDisplayId());
+ }
+ }
+
+ private void waitAndAssertConfigurationChange(ComponentName activityName) {
+ mAmWmState.waitForWithAmState((state) ->
+ getCallbackCount(activityName, ON_CONFIGURATION_CHANGED) == 1,
+ "waitForConfigurationChange");
+ assertEquals("Must receive a single configuration change", 1,
+ getCallbackCount(activityName, ON_CONFIGURATION_CHANGED));
+ }
+
+ private void waitAndAssertResume(ComponentName activityName) {
+ mAmWmState.waitForWithAmState((state) ->
+ getCallbackCount(activityName, ON_RESUME) == 1, "waitForResume");
+ assertEquals("Must be resumed once", 1, getCallbackCount(activityName, ON_RESUME));
+ }
+
+ private int getCallbackCount(ComponentName activityName,
+ CommandSession.ActivityCallback callback) {
+ final ActivityLifecycleCounts lifecycles = new ActivityLifecycleCounts(activityName);
+ return lifecycles.getCount(callback);
+ }
+
+ @Test
+ @FlakyTest(bugId = 130379901, detail = "Promote to presubmit once proved stable")
+ public void testDisplayIdUpdateWhenImeMove_RelaunchActivity() throws Exception {
+ try (final TestActivitySession<ClientTestActivity> session = new TestActivitySession<>()) {
+ testDisplayIdUpdateWhenImeMove(ClientTestActivity.class);
+ }
+ }
+
+ @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);
+ }
+ }
+
+ private void testDisplayIdUpdateWhenImeMove(Class<? extends ImeTestActivity> activityClass)
+ throws Exception {
+ try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession();
+ final MockImeSession mockImeSession = MockImeSession.create(mContext)) {
+
+ assertImeShownAndMatchesDisplayId(
+ activityClass, mockImeSession, DEFAULT_DISPLAY);
+
+ final ActivityManagerState.ActivityDisplay newDisplay = virtualDisplaySession
+ .setSimulateDisplay(true).setShowSystemDecorations(true).createDisplay();
+
+ // Launch activity on the secondary display and make IME show.
+ assertImeShownAndMatchesDisplayId(
+ activityClass, mockImeSession, newDisplay.mId);
+ }
+ }
+
+ private void assertImeShownAndMatchesDisplayId(Class<? extends ImeTestActivity> activityClass,
+ MockImeSession imeSession, int targetDisplayId) throws Exception {
+ final ImeEventStream stream = imeSession.openEventStream();
+
+ final Intent intent = new Intent(mContext, activityClass)
+ .putExtra(EXTRA_SHOW_IME, true).setFlags(FLAG_ACTIVITY_NEW_TASK);
+ separateTestJournal();
+ final ActivityOptions launchOptions = ActivityOptions.makeBasic();
+ launchOptions.setLaunchDisplayId(targetDisplayId);
+ getInstrumentation().getTargetContext().startActivity(intent, launchOptions.toBundle());
+
+
+ // Verify if IME is showed on the target display.
+ expectEvent(stream, event -> "showSoftInput".equals(event.getEventName()), TIMEOUT);
+ mAmWmState.waitAndAssertImeWindowShownOnDisplay(targetDisplayId);
+
+ final int displayId = expectCommand(stream, imeSession.callGetDisplayId(), TIMEOUT)
+ .getReturnIntegerValue();
+ assertEquals("Display ID must match", targetDisplayId, displayId);
+ }
+
+ @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.
+ final ActivityManagerState.ActivityDisplay newDisplay = virtualDisplaySession
+ .setSimulateDisplay(true).createDisplay();
+
+ final Display display = mContext.getSystemService(DisplayManager.class)
+ .getDisplay(newDisplay.mId);
+ final Context newDisplayContext = mContext.createDisplayContext(display);
+ final InputMethodManager imm =
+ newDisplayContext.getSystemService(InputMethodManager.class);
+
+ assertEquals(newDisplay.mId, imm.getDisplayId());
+ }
+ }
+
+ public static class ClientTestActivity extends ImeTestActivity { }
+
+ public static class NoRelaunchActivity extends ImeTestActivity { }
+
+ public static class ImeTestActivity extends CommandSession.BasicTestActivity {
+ private EditText mEditText;
+ private boolean mShouldShowIme;
+
+ @Override
+ protected void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ mShouldShowIme = getIntent().hasExtra(EXTRA_SHOW_IME);
+ if (mShouldShowIme) {
+ mEditText = new EditText(this);
+ final LinearLayout layout = new LinearLayout(this);
+ layout.setOrientation(LinearLayout.VERTICAL);
+ layout.addView(mEditText);
+ setContentView(layout);
+ }
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ if (mShouldShowIme) {
+ getWindow().setSoftInputMode(SOFT_INPUT_STATE_ALWAYS_VISIBLE);
+ mEditText.requestFocus();
+ }
+ }
+ }
+}
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 80dd440..ee0d12c 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayPolicyTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayPolicyTests.java
@@ -57,6 +57,8 @@
import android.server.wm.CommandSession.SizeInfo;
import android.util.SparseArray;
+import androidx.test.filters.FlakyTest;
+
import org.junit.Before;
import org.junit.Test;
@@ -64,7 +66,7 @@
/**
* Build/Install/Run:
- * atest CtsActivityManagerDeviceTestCases:MultiDisplayPolicyTests
+ * atest CtsWindowManagerDeviceTestCases:MultiDisplayPolicyTests
*
* Tests each expected policy on multi-display environment.
*/
@@ -263,8 +265,8 @@
try (final ExternalDisplaySession externalDisplaySession = new ExternalDisplaySession();
final PrimaryDisplayStateSession displayStateSession =
new PrimaryDisplayStateSession()) {
- final ActivityDisplay newDisplay =
- externalDisplaySession.createVirtualDisplay(true /* showContentWhenLocked */);
+ final ActivityDisplay newDisplay = externalDisplaySession
+ .setCanShowWithInsecureKeyguard(true).createVirtualDisplay();
launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId);
@@ -309,8 +311,7 @@
@Test
public void testExternalDisplayToggleState() throws Exception {
try (final ExternalDisplaySession externalDisplaySession = new ExternalDisplaySession()) {
- final ActivityDisplay newDisplay =
- externalDisplaySession.createVirtualDisplay(false /* showContentWhenLocked */);
+ final ActivityDisplay newDisplay = externalDisplaySession.createVirtualDisplay();
launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId);
@@ -349,8 +350,7 @@
mAmWmState.getAmState().computeState();
final int displayCount = mAmWmState.getAmState().getDisplayCount();
try (final ExternalDisplaySession externalDisplaySession = new ExternalDisplaySession()) {
- final ActivityDisplay newDisplay =
- externalDisplaySession.createVirtualDisplay(false /* showContentWhenLocked */);
+ final ActivityDisplay newDisplay = externalDisplaySession.createVirtualDisplay();
launchActivityOnDisplay(VIRTUAL_DISPLAY_ACTIVITY, newDisplay.mId);
waitAndAssertTopResumedActivity(VIRTUAL_DISPLAY_ACTIVITY, newDisplay.mId,
"Virtual activity should be Top Resumed Activity.");
@@ -645,8 +645,8 @@
assertEquals("Unexpected resumed activity",
0, mAmWmState.getAmState().getResumedActivitiesCount());
- final ActivityDisplay newDisplay =
- externalDisplaySession.createVirtualDisplay(true /* showContentWhenLocked */);
+ final ActivityDisplay newDisplay = externalDisplaySession
+ .setCanShowWithInsecureKeyguard(true).createVirtualDisplay();
launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId);
@@ -695,8 +695,7 @@
launchActivity(TEST_ACTIVITY);
- final ActivityDisplay newDisplay =
- externalDisplaySession.createVirtualDisplay(false /* showContentWhenLocked */);
+ final ActivityDisplay newDisplay = externalDisplaySession.createVirtualDisplay();
launchActivityOnDisplay(SHOW_WHEN_LOCKED_ATTR_ACTIVITY, newDisplay.mId);
lockScreenSession.gotoKeyguard();
@@ -717,8 +716,7 @@
launchActivity(TEST_ACTIVITY);
mAmWmState.waitForActivityState(TEST_ACTIVITY, STATE_RESUMED);
- final ActivityDisplay newDisplay =
- externalDisplaySession.createVirtualDisplay(false /* showContentWhenLocked */);
+ final ActivityDisplay newDisplay = externalDisplaySession.createVirtualDisplay();
launchActivityOnDisplay(VIRTUAL_DISPLAY_ACTIVITY, newDisplay.mId);
waitAndAssertTopResumedActivity(VIRTUAL_DISPLAY_ACTIVITY, newDisplay.mId,
"Virtual activity should be Top Resumed Activity.");
@@ -748,6 +746,7 @@
* 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 =
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 882e20e..4d9d5e3 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplaySecurityTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplaySecurityTests.java
@@ -72,7 +72,7 @@
/**
* Build/Install/Run:
- * atest CtsActivityManagerDeviceTestCases:MultiDisplaySecurityTests
+ * atest CtsWindowManagerDeviceTestCases:MultiDisplaySecurityTests
*
* Tests if be allowed to launch an activity on multi-display environment.
*/
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 d988c903..9194577 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplaySystemDecorationTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplaySystemDecorationTests.java
@@ -17,6 +17,7 @@
package android.server.wm;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.server.wm.StateLogger.logAlways;
import static android.server.wm.app.Components.HOME_ACTIVITY;
import static android.server.wm.app.Components.SECONDARY_HOME_ACTIVITY;
import static android.server.wm.app.Components.SINGLE_HOME_ACTIVITY;
@@ -53,17 +54,22 @@
import android.platform.test.annotations.Presubmit;
import android.server.wm.ActivityManagerState.ActivityDisplay;
import android.server.wm.TestJournalProvider.TestJournalContainer;
+import android.server.wm.WindowManagerState.Display;
import android.server.wm.WindowManagerState.WindowState;
import android.text.TextUtils;
import android.view.WindowManager;
import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
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;
+import com.android.cts.mockime.ImeCommand;
import com.android.cts.mockime.ImeEvent;
import com.android.cts.mockime.ImeEventStream;
import com.android.cts.mockime.ImeSettings;
@@ -109,9 +115,8 @@
TestJournalContainer.start();
- final ActivityDisplay newDisplay = virtualDisplaySession.setPublicDisplay(true)
- .setShowSystemDecorations(true)
- .createDisplay();
+ final ActivityDisplay newDisplay = virtualDisplaySession
+ .setSimulateDisplay(true).setShowSystemDecorations(true).createDisplay();
wallpaperSession.setWallpaperComponent(TEST_LIVE_WALLPAPER_SERVICE);
final String TARGET_ENGINE_DISPLAY_ID = ENGINE_DISPLAY_ID + newDisplay.mId;
@@ -125,27 +130,30 @@
* 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();
final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+ final ActivityDisplay untrustedDisplay = externalDisplaySession
+ .setPublicDisplay(true).setShowSystemDecorations(true).createVirtualDisplay();
+
+ final ActivityDisplay decoredSystemDisplay = virtualDisplaySession
+ .setSimulateDisplay(true).setShowSystemDecorations(true).createDisplay();
+
final Bitmap tmpWallpaper = wallpaperSession.getTestBitmap();
wallpaperSession.setImageWallpaper(tmpWallpaper);
- final ActivityDisplay noDecorDisplay = virtualDisplaySession.setPublicDisplay(true)
- .setShowSystemDecorations(false).createDisplay();
- // Tests when the system decor flag is included in that display, the wallpaper must
- // be displayed on the secondary display. And at the same time we do not need to wait
- // for the wallpaper which should not to be displayed.
- final ActivityDisplay decorDisplay = virtualDisplaySession.setPublicDisplay(true)
- .setShowSystemDecorations(true).createDisplay();
- mAmWmState.waitForWithWmState((state) -> isWallpaperOnDisplay(state, decorDisplay.mId),
+ mAmWmState.waitForWithWmState(
+ (state) -> isWallpaperOnDisplay(state, decoredSystemDisplay.mId),
"Waiting for wallpaper window to show");
- assertTrue("Wallpaper must be displayed on secondary display with system decor flag",
- isWallpaperOnDisplay(mAmWmState.getWmState(), decorDisplay.mId));
- assertFalse("Wallpaper must not be displayed on the display without system decor flag",
- isWallpaperOnDisplay(mAmWmState.getWmState(), noDecorDisplay.mId));
+ assertTrue("Wallpaper must be displayed on system owned display with system decor flag",
+ isWallpaperOnDisplay(mAmWmState.getWmState(), decoredSystemDisplay.mId));
+
+ assertFalse("Wallpaper must not be displayed on the untrusted display",
+ isWallpaperOnDisplay(mAmWmState.getWmState(), untrustedDisplay.mId));
}
}
@@ -197,9 +205,9 @@
*/
@Test
public void testNavBarShowingOnDisplayWithDecor() throws Exception {
- try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
- final ActivityDisplay newDisplay = virtualDisplaySession
- .setPublicDisplay(true).setShowSystemDecorations(true).createDisplay();
+ try (final ExternalDisplaySession externalDisplaySession = new ExternalDisplaySession()) {
+ final ActivityDisplay newDisplay = externalDisplaySession
+ .setPublicDisplay(true).setShowSystemDecorations(true).createVirtualDisplay();
mAmWmState.waitAndAssertNavBarShownOnDisplay(newDisplay.mId);
}
@@ -210,12 +218,14 @@
*/
@Test
public void testNavBarNotShowingOnDisplayWithoutDecor() throws Exception {
- try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
- virtualDisplaySession.setPublicDisplay(true)
- .setShowSystemDecorations(false).createDisplay();
-
+ try (final ExternalDisplaySession externalDisplaySession = new ExternalDisplaySession()) {
+ // Wait navigation bar show on default display and record the states.
+ mAmWmState.waitAndAssertNavBarShownOnDisplay(DEFAULT_DISPLAY);
final List<WindowState> expected = mAmWmState.getWmState().getAllNavigationBarStates();
+ externalDisplaySession.setPublicDisplay(true)
+ .setShowSystemDecorations(false).createVirtualDisplay();
+
waitAndAssertNavBarStatesAreTheSame(expected);
}
}
@@ -225,13 +235,16 @@
* supports system decoration.
*/
@Test
+ @FlakyTest(bugId = 131005232)
public void testNavBarNotShowingOnPrivateDisplay() throws Exception {
- try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
- virtualDisplaySession.setPublicDisplay(false)
- .setShowSystemDecorations(true).createDisplay();
-
+ try (final ExternalDisplaySession externalDisplaySession = new ExternalDisplaySession()) {
+ // Wait navigation bar show on default display and record the states.
+ mAmWmState.waitAndAssertNavBarShownOnDisplay(DEFAULT_DISPLAY);
final List<WindowState> expected = mAmWmState.getWmState().getAllNavigationBarStates();
+ externalDisplaySession.setPublicDisplay(false)
+ .setShowSystemDecorations(true).createVirtualDisplay();
+
waitAndAssertNavBarStatesAreTheSame(expected);
}
}
@@ -245,13 +258,14 @@
// display, go back to verify whether the nav bar states are unchanged to verify that no nav
// bars were added to a display that was added before executing this method that shouldn't
// have nav bars (i.e. private or without system ui decor).
- try (final VirtualDisplaySession secondDisplaySession = new VirtualDisplaySession()) {
+ try (final ExternalDisplaySession secondDisplaySession = new ExternalDisplaySession()) {
final ActivityDisplay supportsSysDecorDisplay = secondDisplaySession
- .setPublicDisplay(true).setShowSystemDecorations(true).createDisplay();
+ .setPublicDisplay(true).setShowSystemDecorations(true).createVirtualDisplay();
mAmWmState.waitAndAssertNavBarShownOnDisplay(supportsSysDecorDisplay.mId);
// This display has finished his task. Just close it.
}
+ mAmWmState.computeState(true);
final List<WindowState> result = mAmWmState.getWmState().getAllNavigationBarStates();
assertEquals("The number of nav bars should be the same", expected.size(), result.size());
@@ -271,9 +285,9 @@
public void testLaunchHomeActivityOnSecondaryDisplayWithoutDecorations() throws Exception {
try (final HomeActivitySession homeSession =
new HomeActivitySession(SECONDARY_HOME_ACTIVITY);
- final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+ final ExternalDisplaySession externalDisplaySession = new ExternalDisplaySession()) {
// Create new virtual display without system decoration support.
- final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+ final ActivityDisplay newDisplay = externalDisplaySession.createVirtualDisplay();
// Secondary home activity can't be launched on the display without system decoration
// support.
@@ -289,10 +303,10 @@
@Test
public void testLaunchSingleHomeActivityOnDisplayWithDecorations() throws Exception {
try (final HomeActivitySession homeSession = new HomeActivitySession(SINGLE_HOME_ACTIVITY);
- final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+ final ExternalDisplaySession externalDisplaySession = new ExternalDisplaySession()) {
// Create new virtual display with system decoration support.
final ActivityDisplay newDisplay
- = virtualDisplaySession.setShowSystemDecorations(true).createDisplay();
+ = externalDisplaySession.setShowSystemDecorations(true).createVirtualDisplay();
// If default home doesn't support multi-instance, default secondary home activity
// should be automatically launched on the new display.
@@ -311,10 +325,10 @@
public void testLaunchSingleSecondaryHomeActivityOnDisplayWithDecorations() throws Exception {
try (final HomeActivitySession homeSession =
new HomeActivitySession(SINGLE_SECONDARY_HOME_ACTIVITY);
- final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+ final ExternalDisplaySession externalDisplaySession = new ExternalDisplaySession()) {
// Create new virtual display with system decoration support.
final ActivityDisplay newDisplay
- = virtualDisplaySession.setShowSystemDecorations(true).createDisplay();
+ = externalDisplaySession.setShowSystemDecorations(true).createVirtualDisplay();
// If provided secondary home doesn't support multi-instance, default secondary home
// activity should be automatically launched on the new display.
@@ -332,10 +346,10 @@
@Test
public void testLaunchHomeActivityOnDisplayWithDecorations() throws Exception {
try (final HomeActivitySession homeSession = new HomeActivitySession(HOME_ACTIVITY);
- final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+ final ExternalDisplaySession externalDisplaySession = new ExternalDisplaySession()) {
// Create new virtual display with system decoration support.
final ActivityDisplay newDisplay
- = virtualDisplaySession.setShowSystemDecorations(true).createDisplay();
+ = externalDisplaySession.setShowSystemDecorations(true).createVirtualDisplay();
// If default home doesn't have SECONDARY_HOME category, default secondary home
// activity should be automatically launched on the new display.
@@ -354,10 +368,10 @@
public void testLaunchSecondaryHomeActivityOnDisplayWithDecorations() throws Exception {
try (final HomeActivitySession homeSession =
new HomeActivitySession(SECONDARY_HOME_ACTIVITY);
- final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+ final ExternalDisplaySession externalDisplaySession = new ExternalDisplaySession()) {
// Create new virtual display with system decoration support.
final ActivityDisplay newDisplay
- = virtualDisplaySession.setShowSystemDecorations(true).createDisplay();
+ = externalDisplaySession.setShowSystemDecorations(true).createVirtualDisplay();
// Provided secondary home activity should be automatically launched on the new
// display.
@@ -370,6 +384,7 @@
// IME related tests
@Test
+ @FlakyTest(bugId = 131005232)
public void testImeWindowCanSwitchToDifferentDisplays() throws Exception {
try (final TestActivitySession<ImeTestActivity> imeTestActivitySession = new
TestActivitySession<>();
@@ -382,10 +397,10 @@
mContext, getInstrumentation().getUiAutomation(), new ImeSettings.Builder())) {
// Create a virtual display and launch an activity on it.
- final ActivityDisplay newDisplay = virtualDisplaySession.setPublicDisplay(true)
- .setShowSystemDecorations(true).createDisplay();
- imeTestActivitySession.launchTestActivityOnDisplaySync(
- ImeTestActivity.class, newDisplay.mId);
+ final ActivityDisplay newDisplay = virtualDisplaySession.setShowSystemDecorations(true)
+ .setSimulateDisplay(true).createDisplay();
+ imeTestActivitySession.launchTestActivityOnDisplaySync(ImeTestActivity.class,
+ newDisplay.mId);
// Make the activity to show soft input.
final ImeEventStream stream = mockImeSession.openEventStream();
@@ -420,6 +435,7 @@
}
@Test
+ @FlakyTest(bugId = 131005232)
public void testImeApiForBug118341760() throws Exception {
final long TIMEOUT_START_INPUT = TimeUnit.SECONDS.toMillis(5);
@@ -432,8 +448,8 @@
mContext, getInstrumentation().getUiAutomation(), new ImeSettings.Builder())) {
// Create a virtual display and launch an activity on it.
- final ActivityDisplay newDisplay = virtualDisplaySession.setPublicDisplay(true)
- .setShowSystemDecorations(true).createDisplay();
+ final ActivityDisplay newDisplay = virtualDisplaySession.setShowSystemDecorations(true)
+ .setSimulateDisplay(true).createDisplay();
imeTestActivitySession.launchTestActivityOnDisplaySync(
ImeTestActivityWithBrokenContextWrapper.class, newDisplay.mId);
@@ -459,6 +475,7 @@
}
@Test
+ @FlakyTest(bugId = 131005232)
public void testImeWindowCanSwitchWhenTopFocusedDisplayChange() throws Exception {
try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession();
final TestActivitySession<ImeTestActivity> imeTestActivitySession = new
@@ -469,46 +486,42 @@
final MockImeSession mockImeSession1 = MockImeSession.create(
mContext, getInstrumentation().getUiAutomation(), new ImeSettings.Builder())) {
- // Create 2 virtual displays and launch an activity on each display.
- final List<ActivityDisplay> newDisplays = virtualDisplaySession.setPublicDisplay(true)
- .setShowSystemDecorations(true).createDisplays(2);
- final ActivityDisplay display1 = newDisplays.get(0);
- final ActivityDisplay display2 = newDisplays.get(1);
+ // Create a virtual display and launch an activity on virtual & default display.
+ final ActivityDisplay newDisplay = virtualDisplaySession.setShowSystemDecorations(true)
+ .setSimulateDisplay(true).createDisplay();
+ imeTestActivitySession.launchTestActivityOnDisplaySync(ImeTestActivity.class,
+ DEFAULT_DISPLAY);
+ imeTestActivitySession2.launchTestActivityOnDisplaySync(ImeTestActivity2.class,
+ newDisplay.mId);
- imeTestActivitySession.launchTestActivityOnDisplaySync(
- ImeTestActivity.class,
- display1.mId);
- imeTestActivitySession2.launchTestActivityOnDisplaySync(
- ImeTestActivity2.class,
- display2.mId);
+ final Display defDisplay = mAmWmState.getWmState().getDisplay(DEFAULT_DISPLAY);
final ImeEventStream stream = mockImeSession1.openEventStream();
- // Tap display1 as top focused display & request focus on EditText to show soft input.
- tapOnDisplay(display1.mOverrideConfiguration.screenWidthDp / 2,
- display1.mOverrideConfiguration.screenHeightDp / 2, display1.mId);
+ // Tap default display as top focused display & request focus on EditText to show
+ // soft input.
+ tapOnDisplayCenter(defDisplay.getDisplayId());
imeTestActivitySession.runOnMainSyncAndWait(
imeTestActivitySession.getActivity()::showSoftInput);
- waitOrderedImeEventsThenAssertImeShown(stream, display1.mId,
+ waitOrderedImeEventsThenAssertImeShown(stream, defDisplay.getDisplayId(),
editorMatcher("onStartInput",
imeTestActivitySession.getActivity().mEditText.getPrivateImeOptions()),
event -> "showSoftInput".equals(event.getEventName()));
- // Tap display2 as top focused display & request focus on EditText to show soft input.
- tapOnDisplay(display2.mOverrideConfiguration.screenWidthDp / 2,
- display2.mOverrideConfiguration.screenHeightDp / 2, display2.mId);
+ // Tap virtual display as top focused display & request focus on EditText to show
+ // soft input.
+ tapOnDisplayCenter(newDisplay.mId);
imeTestActivitySession2.runOnMainSyncAndWait(
imeTestActivitySession2.getActivity()::showSoftInput);
- waitOrderedImeEventsThenAssertImeShown(stream, display2.mId,
+ waitOrderedImeEventsThenAssertImeShown(stream, newDisplay.mId,
editorMatcher("onStartInput",
imeTestActivitySession2.getActivity().mEditText.getPrivateImeOptions()),
event -> "showSoftInput".equals(event.getEventName()));
- // Tap display1 again to make sure the IME window will come back.
- tapOnDisplay(display1.mOverrideConfiguration.screenWidthDp / 2,
- display1.mOverrideConfiguration.screenHeightDp / 2, display1.mId);
+ // Tap default display again to make sure the IME window will come back.
+ tapOnDisplayCenter(defDisplay.getDisplayId());
imeTestActivitySession.runOnMainSyncAndWait(
imeTestActivitySession.getActivity()::showSoftInput);
- waitOrderedImeEventsThenAssertImeShown(stream, display1.mId,
+ waitOrderedImeEventsThenAssertImeShown(stream, defDisplay.getDisplayId(),
editorMatcher("onStartInput",
imeTestActivitySession.getActivity().mEditText.getPrivateImeOptions()),
event -> "showSoftInput".equals(event.getEventName()));
@@ -516,11 +529,12 @@
}
/**
- * Test that the IME should be shown in default display and verify committed texts can deliver
- * to target display which does not support system decoration.
+ * Test that the IME can be shown in a different display (actually the default display) than
+ * the display on which the target IME application is shown. Then test several basic operations
+ * in {@link InputConnection}.
*/
@Test
- public void testImeShowAndCommitTextsInDefaultDisplayWhenNoSysDecor() throws Exception {
+ public void testCrossDisplayBasicImeOperations() throws Exception {
final long TIMEOUT = TimeUnit.SECONDS.toMillis(5);
try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession();
@@ -530,20 +544,19 @@
final MockImeSession mockImeSession = MockImeSession.create(
mContext, getInstrumentation().getUiAutomation(), new ImeSettings.Builder())) {
- // Create a virtual display and pretend display does not support system decoration.
+ // Create a virtual display by app and assume the display should not show IME window.
final ActivityDisplay newDisplay = virtualDisplaySession.setPublicDisplay(true)
- .setShowSystemDecorations(false).createDisplay();
- // Verify the virtual display should not support system decoration.
+ .createDisplay();
SystemUtil.runWithShellPermissionIdentity(
- () -> assertFalse("Display should not support system decoration",
+ () -> assertFalse("Display should not support showing IME window",
mTargetContext.getSystemService(WindowManager.class)
- .shouldShowSystemDecors(newDisplay.mId)));
+ .shouldShowIme(newDisplay.mId)));
// Launch Ime test activity in virtual display.
- imeTestActivitySession.launchTestActivityOnDisplaySync(
- ImeTestActivity.class,
+ imeTestActivitySession.launchTestActivityOnDisplaySync(ImeTestActivity.class,
newDisplay.mId);
- // Make the activity to show soft input on the default display.
+
+ // Verify the activity to show soft input on the default display.
final ImeEventStream stream = mockImeSession.openEventStream();
final EditText editText = imeTestActivitySession.getActivity().mEditText;
imeTestActivitySession.runOnMainSyncAndWait(
@@ -559,6 +572,13 @@
imeTestActivitySession.runOnMainAndAssertWithTimeout(
() -> TextUtils.equals(commitText, editText.getText()), TIMEOUT,
"The input text should be delivered");
+
+ // Since the IME and the IME target app are running in different displays,
+ // InputConnection#requestCursorUpdates() is not supported and it should return false.
+ // See InputMethodServiceTest#testOnUpdateCursorAnchorInfo() for the normal scenario.
+ final ImeCommand callCursorUpdates = mockImeSession.callRequestCursorUpdates(
+ InputConnection.CURSOR_UPDATE_IMMEDIATE);
+ assertFalse(expectCommand(stream, callCursorUpdates, TIMEOUT).getReturnBooleanValue());
}
}
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 acfc048..626e270 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayTestBase.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayTestBase.java
@@ -17,6 +17,7 @@
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;
@@ -58,10 +59,14 @@
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;
@@ -236,8 +241,20 @@
protected void tapOnDisplayCenter(int displayId) {
final Rect bounds = mAmWmState.getWmState().getDisplay(displayId).getDisplayRect();
tapOnDisplay(bounds.centerX(), bounds.centerY(), 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();
}
+ /**
+ * This class should only be used when you need to test virtual display created by a
+ * non-privileged app.
+ * Or when you need to test on simulated display.
+ *
+ * If you need to test virtual display created by a privileged app, please use
+ * {@link ExternalDisplaySession} instead.
+ */
public class VirtualDisplaySession implements AutoCloseable {
private int mDensityDpi = CUSTOM_DENSITY_DPI;
private boolean mLaunchInSplitScreen = false;
@@ -248,12 +265,14 @@
private boolean mPresentationDisplay = false;
private ComponentName mLaunchActivity = null;
private boolean mSimulateDisplay = false;
+ // Used to restore the supportSystemDecors state of the simulate displays.
+ private final SparseBooleanArray mSimulateSystemDecors = new SparseBooleanArray();
private boolean mMustBeCreated = true;
private Size mSimulationDisplaySize = new Size(1024 /* width */, 768 /* height */);
private boolean mVirtualDisplayCreated = false;
private final OverlayDisplayDevicesSession mOverlayDisplayDeviceSession =
- new OverlayDisplayDevicesSession();
+ new OverlayDisplayDevicesSession(mContext);
VirtualDisplaySession setDensityDpi(int densityDpi) {
mDensityDpi = densityDpi;
@@ -331,6 +350,15 @@
@Override
public void close() throws Exception {
+ if (mSimulateSystemDecors.size() > 0) {
+ final WindowManager wm = mContext.getSystemService(WindowManager.class);
+ for (int index = mSimulateSystemDecors.size() - 1; index >= 0; index--) {
+ final int displayId = mSimulateSystemDecors.keyAt(index);
+ final boolean shouldShow = mSimulateSystemDecors.valueAt(index);
+ SystemUtil.runWithShellPermissionIdentity(() ->
+ wm.setShouldShowSystemDecors(displayId, shouldShow));
+ }
+ }
mOverlayDisplayDeviceSession.close();
if (mVirtualDisplayCreated) {
destroyVirtualDisplays();
@@ -350,8 +378,15 @@
// Create virtual display with custom density dpi and specified size.
mOverlayDisplayDeviceSession.set(mSimulationDisplaySize + "/" + mDensityDpi);
+ final List<ActivityDisplay> newDisplays = assertAndGetNewDisplays(1, originalDs);
- return assertAndGetNewDisplays(1, originalDs);
+ if (mShowSystemDecorations) {
+ for (ActivityDisplay display : newDisplays) {
+ mOverlayDisplayDeviceSession.addAndConfigDisplayState(display,
+ true /* requestShowSysDecors */, true /* requestShowIme */);
+ }
+ }
+ return newDisplays;
}
/**
@@ -518,10 +553,60 @@
/** Helper class to save, set, and restore overlay_display_devices preference. */
private static class OverlayDisplayDevicesSession extends SettingsSession<String> {
- OverlayDisplayDevicesSession() {
+ private final List<OverlayDisplayState> mDisplayStates = new ArrayList<>();
+ private final WindowManager mWm;
+
+ OverlayDisplayDevicesSession(Context context) {
super(Settings.Global.getUriFor(Settings.Global.OVERLAY_DISPLAY_DEVICES),
Settings.Global::getString,
Settings.Global::putString);
+ mWm = context.getSystemService(WindowManager.class);
+ }
+
+ void addAndConfigDisplayState(ActivityDisplay display, boolean requestShowSysDecors,
+ boolean requestShowIme) {
+ SystemUtil.runWithShellPermissionIdentity(() -> {
+ final boolean showSystemDecors = mWm.shouldShowSystemDecors(display.mId);
+ final boolean showIme = mWm.shouldShowIme(display.mId);
+ mDisplayStates.add(new OverlayDisplayState(display.mId, showSystemDecors, showIme));
+ if (requestShowSysDecors != showSystemDecors) {
+ mWm.setShouldShowSystemDecors(display.mId, requestShowSysDecors);
+ TestUtils.waitUntil("Waiting for display show system decors",
+ 5 /* timeoutSecond */,
+ () -> mWm.shouldShowSystemDecors(display.mId));
+ }
+ if (requestShowIme != showIme) {
+ mWm.setShouldShowIme(display.mId, requestShowIme);
+ TestUtils.waitUntil("Waiting for display show Ime",
+ 5 /* timeoutSecond */,
+ () -> mWm.shouldShowIme(display.mId));
+ }
+ });
+ }
+
+ private void restoreDisplayStates() {
+ mDisplayStates.forEach(state -> SystemUtil.runWithShellPermissionIdentity(() -> {
+ mWm.setShouldShowSystemDecors(state.mId, state.mShouldShowSystemDecors);
+ mWm.setShouldShowIme(state.mId, state.mShouldShowIme);
+ }));
+ }
+
+ @Override
+ public void close() throws Exception {
+ super.close();
+ restoreDisplayStates();
+ }
+
+ private class OverlayDisplayState {
+ int mId;
+ boolean mShouldShowSystemDecors;
+ boolean mShouldShowIme;
+
+ OverlayDisplayState(int displayId, boolean showSysDecors, boolean showIme) {
+ mId = displayId;
+ mShouldShowSystemDecors = showSysDecors;
+ mShouldShowIme = showIme;
+ }
}
}
@@ -560,22 +645,49 @@
return hasDeviceFeature(FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS);
}
- // TODO(b/121444086): Update ExternalDisplaySession/VirtualDisplaySession usages
+ /**
+ * This class is used when you need to test virtual display created by a privileged app.
+ *
+ * If you need to test virtual display created by a non-privileged app or when you need to test
+ * on simulated display, please use {@link VirtualDisplaySession} instead.
+ */
public class ExternalDisplaySession implements AutoCloseable {
+ private boolean mCanShowWithInsecureKeyguard = false;
+ private boolean mPublicDisplay = false;
+ private boolean mShowSystemDecorations = false;
+
@Nullable
private VirtualDisplayHelper mExternalDisplayHelper;
+ ExternalDisplaySession setCanShowWithInsecureKeyguard(boolean canShowWithInsecureKeyguard) {
+ mCanShowWithInsecureKeyguard = canShowWithInsecureKeyguard;
+ return this;
+ }
+
+ ExternalDisplaySession setPublicDisplay(boolean publicDisplay) {
+ mPublicDisplay = publicDisplay;
+ return this;
+ }
+
+ ExternalDisplaySession setShowSystemDecorations(boolean showSystemDecorations) {
+ mShowSystemDecorations = showSystemDecorations;
+ return this;
+ }
+
/**
* Creates a private virtual display with insecure keyguard flags set.
*/
- ActivityDisplay createVirtualDisplay(boolean showContentWhenLocked)
- throws Exception {
+ ActivityDisplay createVirtualDisplay() throws Exception {
final List<ActivityDisplay> originalDS = getDisplaysStates();
final int originalDisplayCount = originalDS.size();
mExternalDisplayHelper = new VirtualDisplayHelper();
- mExternalDisplayHelper.createAndWaitForDisplay(showContentWhenLocked);
+ mExternalDisplayHelper
+ .setPublicDisplay(mPublicDisplay)
+ .setCanShowWithInsecureKeyguard(mCanShowWithInsecureKeyguard)
+ .setShowSystemDecorations(mShowSystemDecorations)
+ .createAndWaitForDisplay();
// Wait for the virtual display to be created and get configurations.
final List<ActivityDisplay> ds = getDisplayStateAfterChange(originalDisplayCount + 1);
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 fc9a73b..4531584 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/SplitScreenTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/SplitScreenTests.java
@@ -118,6 +118,7 @@
}
@Test
+ @FlakyTest(bugId = 131005232)
public void testNonResizeableNotDocked() throws Exception {
launchActivityInSplitScreenWithRecents(NON_RESIZEABLE_ACTIVITY);
@@ -431,6 +432,7 @@
}
@Test
+ @FlakyTest(bugId = 131005232)
public void testRotationWhileDockMinimized() throws Exception {
launchActivityInDockStackAndMinimize(TEST_ACTIVITY);
@@ -459,6 +461,7 @@
}
@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
@@ -574,6 +577,7 @@
}
@Test
+ @FlakyTest(bugId = 131005232)
public void testDifferentProcessActivityResumedPreQ() {
launchActivitiesInSplitScreen(
getLaunchActivityBuilder().setTargetActivity(SDK_27_TEST_ACTIVITY),
@@ -585,6 +589,7 @@
}
@Test
+ @FlakyTest(bugId = 131005232)
public void testActivityLifeCycleOnResizeDockedStack() throws Exception {
launchActivity(TEST_ACTIVITY);
mAmWmState.computeState(TEST_ACTIVITY);
@@ -646,6 +651,7 @@
}
@Test
+ @FlakyTest(bugId = 131005232)
public void testStackListOrderOnSplitScreenDismissed() throws Exception {
launchActivitiesInSplitScreen(
getLaunchActivityBuilder().setTargetActivity(DOCKED_ACTIVITY),
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 5a0e22c..6923afb 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/StartActivityTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/StartActivityTests.java
@@ -25,6 +25,7 @@
import android.platform.test.annotations.Presubmit;
import androidx.test.rule.ActivityTestRule;
+import androidx.test.filters.FlakyTest;
import org.junit.Rule;
import org.junit.Test;
@@ -90,6 +91,7 @@
* @throws Exception
*/
@Test
+ @FlakyTest(bugId = 131005232)
public void testLegacyStartActivityFromNonActivityContext() throws Exception {
getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY)
.setLaunchingActivity(SDK_27_LAUNCHING_ACTIVITY)
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/SurfaceControlTest.java b/tests/framework/base/windowmanager/src/android/server/wm/SurfaceControlTest.java
index a22302c..b225b16 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/SurfaceControlTest.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/SurfaceControlTest.java
@@ -17,9 +17,13 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static android.server.wm.UiDeviceUtils.pressHomeButton;
+import static android.server.wm.UiDeviceUtils.pressUnlockButton;
+import static android.server.wm.UiDeviceUtils.pressWakeupButton;
import android.graphics.Canvas;
import android.graphics.Color;
+import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
import android.support.test.uiautomator.UiObjectNotFoundException;
import android.view.Surface;
@@ -27,6 +31,7 @@
import android.view.cts.surfacevalidator.CapturedActivity;
import android.view.cts.surfacevalidator.PixelChecker;
import android.view.cts.surfacevalidator.PixelColor;
+import android.view.cts.surfacevalidator.RectChecker;
import android.view.cts.surfacevalidator.SurfaceControlTestCase;
import androidx.test.filters.LargeTest;
@@ -41,10 +46,11 @@
@LargeTest
@Presubmit
public class SurfaceControlTest {
- private static final int DEFAULT_LAYOUT_WIDTH = 100;
- private static final int DEFAULT_LAYOUT_HEIGHT = 100;
+ private static final int DEFAULT_LAYOUT_WIDTH = 640;
+ private static final int DEFAULT_LAYOUT_HEIGHT = 480;
private static final int DEFAULT_BUFFER_WIDTH = 640;
private static final int DEFAULT_BUFFER_HEIGHT = 480;
+ private static final int DEFAULT_SURFACE_SIZE = 100;
@Rule
public ActivityTestRule<CapturedActivity> mActivityRule =
@@ -64,6 +70,9 @@
@Before
public void setup() {
+ pressWakeupButton();
+ pressUnlockButton();
+
mActivity = mActivityRule.getActivity();
mActivity.dismissPermissionDialog();
}
@@ -90,7 +99,7 @@
private SurfaceControl buildDefaultSurface(SurfaceControl parent) {
return new SurfaceControl.Builder()
- .setBufferSize(DEFAULT_BUFFER_WIDTH, DEFAULT_BUFFER_HEIGHT)
+ .setBufferSize(DEFAULT_SURFACE_SIZE, DEFAULT_SURFACE_SIZE)
.setName("CTS surface")
.setParent(parent)
.build();
@@ -105,12 +114,16 @@
s.unlockCanvasAndPost(c);
}
- private SurfaceControl buildDefaultRedSurface(SurfaceControl parent) {
+ private SurfaceControl buildDefaultSurface(SurfaceControl parent, int color) {
final SurfaceControl sc = buildDefaultSurface(parent);
- fillWithColor(sc, Color.RED);
+ fillWithColor(sc, color);
return sc;
}
+ private SurfaceControl buildDefaultRedSurface(SurfaceControl parent) {
+ return buildDefaultSurface(parent, Color.RED);
+ }
+
/**
* Verify that showing a 100x100 surface filled with RED produces roughly 10,000 red pixels.
*/
@@ -127,12 +140,7 @@
sc.release();
}
},
- new PixelChecker(PixelColor.RED) { //10000
- @Override
- public boolean checkPixels(int pixelCount, int width, int height) {
- return pixelCount > 9000 && pixelCount < 11000;
- }
- });
+ new RectChecker(new Rect(0, 0, 100, 100), PixelColor.RED));
}
/**
@@ -151,19 +159,33 @@
sc.release();
}
},
- new PixelChecker(PixelColor.BLACK) { //10000
+ new RectChecker(new Rect(0, 0, 100, 100), PixelColor.WHITE));
+ }
+
+ /**
+ * Like testHide but we reparent the surface off-screen instead.
+ */
+ @Test
+ public void testReparentOff() throws Throwable {
+ verifyTest(
+ new SurfaceControlTestCase.ParentSurfaceConsumer () {
@Override
- public boolean checkPixels(int pixelCount, int width, int height) {
- return pixelCount == 0;
+ public void addChildren(SurfaceControl parent) {
+ final SurfaceControl sc = buildDefaultRedSurface(parent);
+
+ new SurfaceControl.Transaction().reparent(sc, null).apply();
+
+ sc.release();
}
- });
+ },
+ new RectChecker(new Rect(0, 0, 100, 100), PixelColor.WHITE));
}
/**
* Here we use the same red-surface set up but construct it off-screen and then re-parent it.
*/
@Test
- public void testReparent() throws Throwable {
+ public void testReparentOn() throws Throwable {
verifyTest(
new SurfaceControlTestCase.ParentSurfaceConsumer () {
@Override
@@ -177,11 +199,101 @@
sc.release();
}
},
- new PixelChecker(PixelColor.RED) { //10000
+ new RectChecker(new Rect(0, 0, 100, 100), PixelColor.RED));
+ }
+
+ /**
+ * Test that a surface with Layer "2" appears over a surface with Layer "1".
+ */
+ @Test
+ public void testSetLayer() throws Throwable {
+ verifyTest(
+ new SurfaceControlTestCase.ParentSurfaceConsumer () {
@Override
- public boolean checkPixels(int pixelCount, int width, int height) {
- return pixelCount > 9000 && pixelCount < 11000;
+ public void addChildren(SurfaceControl parent) {
+ final SurfaceControl sc = buildDefaultRedSurface(parent);
+ final SurfaceControl sc2 = buildDefaultSurface(parent, Color.GREEN);
+
+ new SurfaceControl.Transaction().setVisibility(sc, true)
+ .setVisibility(sc2, true)
+ .setLayer(sc, 1)
+ .setLayer(sc2, 2)
+ .apply();
+
+ sc.release();
}
- });
+ },
+ new RectChecker(new Rect(0, 0, 100, 100), PixelColor.GREEN));
+ }
+
+ /**
+ * Try setting the position of a surface with the top-left corner off-screen.
+ */
+ @Test
+ public void testSetGeometry_dstBoundsOffScreen() throws Throwable {
+ verifyTest(
+ new SurfaceControlTestCase.ParentSurfaceConsumer () {
+ @Override
+ public void addChildren(SurfaceControl parent) {
+ final SurfaceControl sc = buildDefaultRedSurface(parent);
+ new SurfaceControl.Transaction().setVisibility(sc, true)
+ .setGeometry(sc, null, new Rect(-50, -50, 50, 50), Surface.ROTATION_0)
+ .apply();
+
+ sc.release();
+ }
+ },
+
+ // The rect should be offset by -50 pixels
+ new RectChecker(
+ new RectChecker.Target(new Rect(0, 0, 50, 50), PixelColor.RED),
+ new RectChecker.Target(new Rect(50, 50, 150, 150), PixelColor.WHITE)));
+ }
+
+ /**
+ * Try setting the position of a surface with the top-left corner on-screen.
+ */
+ @Test
+ public void testSetGeometry_dstBoundsOnScreen() throws Throwable {
+ verifyTest(
+ new SurfaceControlTestCase.ParentSurfaceConsumer () {
+ @Override
+ public void addChildren(SurfaceControl parent) {
+ final SurfaceControl sc = buildDefaultRedSurface(parent);
+ new SurfaceControl.Transaction().setVisibility(sc, true)
+ .setGeometry(sc, null, new Rect(50, 50, 150, 150), Surface.ROTATION_0)
+ .apply();
+
+ sc.release();
+ }
+ },
+
+ // The rect should be offset by 50 pixels
+ new RectChecker(
+ new RectChecker.Target(new Rect(50, 50, 150, 150), PixelColor.RED)));
+ }
+
+ /**
+ * Try scaling a surface
+ */
+ @Test
+ public void testSetGeometry_dstBoundsScaled() throws Throwable {
+ verifyTest(
+ new SurfaceControlTestCase.ParentSurfaceConsumer () {
+ @Override
+ public void addChildren(SurfaceControl parent) {
+ final SurfaceControl sc = buildDefaultRedSurface(parent);
+ new SurfaceControl.Transaction().setVisibility(sc, true)
+ .setGeometry(sc, new Rect(0, 0, DEFAULT_SURFACE_SIZE, DEFAULT_SURFACE_SIZE),
+ new Rect(0, 0, DEFAULT_SURFACE_SIZE * 2, DEFAULT_SURFACE_SIZE*2),
+ Surface.ROTATION_0)
+ .apply();
+
+ sc.release();
+ }
+ },
+
+ new RectChecker(
+ new RectChecker.Target(new Rect(0, 0, 200, 200), PixelColor.RED)));
}
}
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 3c0adbf..cbd6816 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/VirtualDisplayHelper.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/VirtualDisplayHelper.java
@@ -42,9 +42,15 @@
*/
class VirtualDisplayHelper {
+ private boolean mPublicDisplay = false;
+ private boolean mCanShowWithInsecureKeyguard = false;
+ private boolean mShowSystemDecorations = false;
+
private static final String VIRTUAL_DISPLAY_NAME = "CtsVirtualDisplay";
/** See {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD}. */
private static final int VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD = 1 << 5;
+ /** See {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS}. */
+ private static final int VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS = 1 << 9;
private static final int DENSITY = 160;
static final int HEIGHT = 480;
@@ -54,20 +60,28 @@
private VirtualDisplay mVirtualDisplay;
private boolean mCreated;
- void createAndWaitForDisplay(boolean requestShowWhenLocked) {
+ VirtualDisplayHelper setPublicDisplay(boolean publicDisplay) {
+ mPublicDisplay = publicDisplay;
+ return this;
+ }
+
+ VirtualDisplayHelper setCanShowWithInsecureKeyguard(boolean canShowWithInsecureKeyguard) {
+ mCanShowWithInsecureKeyguard = canShowWithInsecureKeyguard;
+ return this;
+ }
+
+ VirtualDisplayHelper setShowSystemDecorations(boolean showSystemDecorations) {
+ mShowSystemDecorations = showSystemDecorations;
+ return this;
+ }
+
+ int createAndWaitForDisplay() {
SystemUtil.runWithShellPermissionIdentity(() -> {
- createVirtualDisplay(requestShowWhenLocked, 0 /* virtualDisplayFlags */);
+ createVirtualDisplay();
waitForDisplayState(mVirtualDisplay.getDisplay().getDisplayId() /* default */,
true /* on */);
mCreated = true;
});
- }
-
- int createAndWaitForPublicDisplay(boolean requestShowWhenLocked) {
- createVirtualDisplay(requestShowWhenLocked, VIRTUAL_DISPLAY_FLAG_PUBLIC);
- waitForDisplayState(mVirtualDisplay.getDisplay().getDisplayId() /* default */,
- true /* on */);
- mCreated = true;
return mVirtualDisplay.getDisplay().getDisplayId();
}
@@ -100,18 +114,29 @@
});
}
- private void createVirtualDisplay(boolean requestShowWhenLocked, int virtualDisplayFlags) {
+ private void createVirtualDisplay() {
mReader = ImageReader.newInstance(WIDTH, HEIGHT, PixelFormat.RGBA_8888, 2);
final DisplayManager displayManager = getInstrumentation()
.getContext().getSystemService(DisplayManager.class);
- int flags = VIRTUAL_DISPLAY_FLAG_PRESENTATION | VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY
- | virtualDisplayFlags;
+ int flags = VIRTUAL_DISPLAY_FLAG_PRESENTATION | VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
- if (requestShowWhenLocked) {
+ if (mPublicDisplay) {
+ flags |= VIRTUAL_DISPLAY_FLAG_PUBLIC;
+ }
+ if (mCanShowWithInsecureKeyguard) {
flags |= VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD;
}
+ if (mShowSystemDecorations) {
+ flags |= VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS;
+ }
+
+ logAlways("createVirtualDisplay: " + WIDTH + "x" + HEIGHT + ", dpi: " + DENSITY
+ + ", publicDisplay=" + mPublicDisplay
+ + ", canShowWithInsecureKeyguard=" + mCanShowWithInsecureKeyguard
+ + ", showSystemDecorations=" + mShowSystemDecorations);
+
mVirtualDisplay = displayManager.createVirtualDisplay(
VIRTUAL_DISPLAY_NAME, WIDTH, HEIGHT, DENSITY, mReader.getSurface(), flags);
}
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 d3f2079..615e572 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/WindowFocusTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/WindowFocusTests.java
@@ -133,6 +133,7 @@
* - 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);
@@ -188,6 +189,7 @@
* 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());
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 bd17ce7..5ea5526 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,6 +31,7 @@
import android.content.Intent;
import android.platform.test.annotations.Presubmit;
+import androidx.test.filters.FlakyTest;
import androidx.test.filters.MediumTest;
import org.junit.Test;
@@ -46,6 +47,7 @@
public class ActivityLifecycleKeyguardTests extends ActivityLifecycleClientTestBase {
@Test
+ @FlakyTest(bugId = 131005232)
public void testSingleLaunch() throws Exception {
assumeTrue(supportsSecureLock());
try (final LockScreenSession lockScreenSession = new LockScreenSession()) {
@@ -59,6 +61,7 @@
}
@Test
+ @FlakyTest(bugId = 131005232)
public void testKeyguardShowHide() throws Exception {
assumeTrue(supportsSecureLock());
@@ -81,6 +84,7 @@
}
@Test
+ @FlakyTest(bugId = 131005232)
public void testKeyguardShowHideOverSplitScreen() throws Exception {
assumeTrue(supportsSecureLock());
assumeTrue(supportsSplitScreenMultiWindow());
@@ -120,6 +124,7 @@
}
@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/ActivityLifecycleSplitScreenTests.java b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleSplitScreenTests.java
index 082d209..908dc09 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
@@ -46,6 +46,7 @@
import android.content.Intent;
import android.platform.test.annotations.Presubmit;
+import androidx.test.filters.FlakyTest;
import androidx.test.filters.MediumTest;
import org.junit.Before;
@@ -70,6 +71,7 @@
}
@Test
+ @FlakyTest(bugId = 131005232)
public void testResumedWhenRecreatedFromInNonFocusedStack() throws Exception {
// Launch first activity
final Activity firstActivity =
@@ -142,9 +144,7 @@
waitAndAssertActivityStates(state(secondActivity, ON_RESUME),
state(firstActivity, ON_STOP));
- LifecycleVerifier.assertSequenceMatchesOneOf(ThirdActivity.class, getLifecycleLog(),
- Arrays.asList(new ArrayList<>(), LifecycleVerifier.getRelaunchSequence(ON_RESUME)),
- "moveToSide");
+ LifecycleVerifier.assertEmptySequence(ThirdActivity.class, getLifecycleLog(), "moveToSide");
LifecycleVerifier.assertRestartAndResumeSequence(SecondActivity.class, getLifecycleLog());
LifecycleVerifier.assertSequence(FirstActivity.class, getLifecycleLog(),
Arrays.asList(ON_PAUSE, ON_STOP), "moveToSide");
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 6d34c54..b047127 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
@@ -367,14 +367,8 @@
state(secondActivity, ON_TOP_POSITION_GAINED));
LifecycleVerifier.assertSequence(CallbackTrackingActivity.class, getLifecycleLog(),
Arrays.asList(ON_TOP_POSITION_LOST), "switchTop");
- List<ActivityCallback> expectedNewIntentSequence = Arrays.asList(
- ON_PAUSE, ON_NEW_INTENT, ON_RESUME, ON_TOP_POSITION_GAINED);
- List<ActivityCallback> extraPositionTransitionNewIntentSequence =
- Arrays.asList(
- ON_TOP_POSITION_GAINED, ON_TOP_POSITION_LOST, ON_PAUSE, ON_NEW_INTENT,
- ON_RESUME, ON_TOP_POSITION_GAINED);
- LifecycleVerifier.assertSequenceMatchesOneOf(SingleTopActivity.class, getLifecycleLog(),
- Arrays.asList(expectedNewIntentSequence, extraPositionTransitionNewIntentSequence),
+ LifecycleVerifier.assertSequence(SingleTopActivity.class, getLifecycleLog(),
+ Arrays.asList(ON_PAUSE, ON_NEW_INTENT, ON_RESUME, ON_TOP_POSITION_GAINED),
"switchTop");
}
diff --git a/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityManagerState.java b/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityManagerState.java
index df518f1..d7733dc 100644
--- a/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityManagerState.java
+++ b/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityManagerState.java
@@ -59,6 +59,7 @@
public static final int DUMP_MODE_ACTIVITIES = 0;
+ public static final String STATE_INITIALIZING = "INITIALIZING";
public static final String STATE_RESUMED = "RESUMED";
public static final String STATE_PAUSED = "PAUSED";
public static final String STATE_STOPPED = "STOPPED";
@@ -681,7 +682,7 @@
}
return false;
}
-
+
public ArrayList<ActivityStack> getStacks() {
return mStacks;
}
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 54229de..d6a31dc 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
@@ -361,13 +361,16 @@
private static final int WAIT_SLICE = 50;
void launchTestActivityOnDisplaySync(Class<T> activityClass, int displayId) {
+ launchTestActivityOnDisplaySync(new Intent(mContext, activityClass), displayId);
+ }
+
+ void launchTestActivityOnDisplaySync(Intent intent, int displayId) {
SystemUtil.runWithShellPermissionIdentity(() -> {
final Bundle bundle = ActivityOptions.makeBasic()
.setLaunchDisplayId(displayId).toBundle();
final ActivityMonitor monitor = getInstrumentation()
.addMonitor((String) null, null, false);
- mContext.startActivity(new Intent(mContext, activityClass)
- .addFlags(FLAG_ACTIVITY_NEW_TASK), bundle);
+ mContext.startActivity(intent.addFlags(FLAG_ACTIVITY_NEW_TASK), bundle);
// Wait for activity launch with timeout.
mTestActivity = (T) monitor.waitForActivityWithTimeout(ACTIVITY_LAUNCH_TIMEOUT);
assertNotNull(mTestActivity);
diff --git a/tests/inputmethod/mockime/src/com/android/cts/mockime/MockIme.java b/tests/inputmethod/mockime/src/com/android/cts/mockime/MockIme.java
index 878890d..590ce86 100644
--- a/tests/inputmethod/mockime/src/com/android/cts/mockime/MockIme.java
+++ b/tests/inputmethod/mockime/src/com/android/cts/mockime/MockIme.java
@@ -42,8 +42,10 @@
import android.view.View;
import android.view.Window;
import android.view.WindowInsets;
+import android.view.WindowManager;
import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.CorrectionInfo;
+import android.view.inputmethod.CursorAnchorInfo;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.InputBinding;
@@ -266,6 +268,9 @@
sendDownUpKeyEvents(keyEventCode);
return ImeEvent.RETURN_VALUE_UNAVAILABLE;
}
+ case "getDisplayId":
+ return getSystemService(WindowManager.class)
+ .getDefaultDisplay().getDisplayId();
}
}
return ImeEvent.RETURN_VALUE_UNAVAILABLE;
@@ -518,6 +523,12 @@
return getTracer().onKeyDown(keyCode, event, () -> super.onKeyDown(keyCode, event));
}
+ @Override
+ public void onUpdateCursorAnchorInfo(CursorAnchorInfo cursorAnchorInfo) {
+ getTracer().onUpdateCursorAnchorInfo(cursorAnchorInfo,
+ () -> super.onUpdateCursorAnchorInfo(cursorAnchorInfo));
+ }
+
@CallSuper
public boolean onEvaluateInputViewShown() {
return getTracer().onEvaluateInputViewShown(() -> {
@@ -735,6 +746,13 @@
return recordEventInternal("onKeyDown", supplier::getAsBoolean, arguments);
}
+ public void onUpdateCursorAnchorInfo(CursorAnchorInfo cursorAnchorInfo,
+ @NonNull Runnable runnable) {
+ final Bundle arguments = new Bundle();
+ arguments.putParcelable("cursorAnchorInfo", cursorAnchorInfo);
+ recordEventInternal("onUpdateCursorAnchorInfo", runnable, arguments);
+ }
+
public boolean onShowInputRequested(int flags, boolean configChange,
@NonNull BooleanSupplier supplier) {
final Bundle arguments = new Bundle();
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 70538a5..2608be2 100644
--- a/tests/inputmethod/mockime/src/com/android/cts/mockime/MockImeSession.java
+++ b/tests/inputmethod/mockime/src/com/android/cts/mockime/MockImeSession.java
@@ -16,6 +16,8 @@
package com.android.cts.mockime;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
import android.app.UiAutomation;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -210,6 +212,12 @@
() -> getMockImeId().equals(getCurrentInputMethodId()));
}
+ /** @see #create(Context, UiAutomation, ImeSettings.Builder) */
+ @NonNull
+ public static MockImeSession create(@NonNull Context context) throws Exception {
+ return create(context, getInstrumentation().getUiAutomation(), new ImeSettings.Builder());
+ }
+
/**
* Creates a new Mock IME session. During this session, you can receive various events from
* {@link MockIme}.
@@ -945,4 +953,10 @@
params.putInt("keyEventCode", keyEventCode);
return callCommandInternal("sendDownUpKeyEvents", params);
}
+
+ @NonNull
+ public ImeCommand callGetDisplayId() {
+ final Bundle params = new Bundle();
+ return callCommandInternal("getDisplayId", params);
+ }
}
diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/CursorAnchorInfoTest.java b/tests/inputmethod/src/android/view/inputmethod/cts/CursorAnchorInfoTest.java
new file mode 100644
index 0000000..466a7cd
--- /dev/null
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/CursorAnchorInfoTest.java
@@ -0,0 +1,468 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.inputmethod.cts;
+
+import static android.view.inputmethod.CursorAnchorInfo.FLAG_HAS_INVISIBLE_REGION;
+import static android.view.inputmethod.CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION;
+import static android.view.inputmethod.CursorAnchorInfo.FLAG_IS_RTL;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.graphics.Matrix;
+import android.graphics.RectF;
+import android.os.Parcel;
+import android.text.TextUtils;
+import android.view.inputmethod.CursorAnchorInfo;
+import android.view.inputmethod.CursorAnchorInfo.Builder;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class CursorAnchorInfoTest {
+ private static final float EPSILON = 0.0000001f;
+
+ private static final RectF[] MANY_BOUNDS = new RectF[] {
+ new RectF(101.0f, 201.0f, 301.0f, 401.0f),
+ new RectF(102.0f, 202.0f, 302.0f, 402.0f),
+ new RectF(103.0f, 203.0f, 303.0f, 403.0f),
+ new RectF(104.0f, 204.0f, 304.0f, 404.0f),
+ new RectF(105.0f, 205.0f, 305.0f, 405.0f),
+ new RectF(106.0f, 206.0f, 306.0f, 406.0f),
+ new RectF(107.0f, 207.0f, 307.0f, 407.0f),
+ new RectF(108.0f, 208.0f, 308.0f, 408.0f),
+ new RectF(109.0f, 209.0f, 309.0f, 409.0f),
+ new RectF(110.0f, 210.0f, 310.0f, 410.0f),
+ new RectF(111.0f, 211.0f, 311.0f, 411.0f),
+ new RectF(112.0f, 212.0f, 312.0f, 412.0f),
+ new RectF(113.0f, 213.0f, 313.0f, 413.0f),
+ new RectF(114.0f, 214.0f, 314.0f, 414.0f),
+ new RectF(115.0f, 215.0f, 315.0f, 415.0f),
+ new RectF(116.0f, 216.0f, 316.0f, 416.0f),
+ new RectF(117.0f, 217.0f, 317.0f, 417.0f),
+ new RectF(118.0f, 218.0f, 318.0f, 418.0f),
+ new RectF(119.0f, 219.0f, 319.0f, 419.0f),
+ };
+ private static final int[] MANY_FLAGS_ARRAY = new int[] {
+ FLAG_HAS_INVISIBLE_REGION,
+ FLAG_HAS_INVISIBLE_REGION | FLAG_HAS_VISIBLE_REGION,
+ FLAG_HAS_VISIBLE_REGION,
+ FLAG_HAS_VISIBLE_REGION,
+ FLAG_HAS_VISIBLE_REGION,
+ FLAG_HAS_VISIBLE_REGION,
+ FLAG_HAS_VISIBLE_REGION | FLAG_IS_RTL,
+ FLAG_HAS_INVISIBLE_REGION | FLAG_HAS_VISIBLE_REGION | FLAG_IS_RTL,
+ FLAG_HAS_INVISIBLE_REGION | FLAG_IS_RTL,
+ FLAG_HAS_VISIBLE_REGION | FLAG_IS_RTL,
+ FLAG_HAS_VISIBLE_REGION,
+ FLAG_HAS_VISIBLE_REGION | FLAG_IS_RTL,
+ FLAG_HAS_VISIBLE_REGION,
+ FLAG_HAS_VISIBLE_REGION | FLAG_IS_RTL,
+ FLAG_HAS_VISIBLE_REGION,
+ FLAG_HAS_VISIBLE_REGION | FLAG_IS_RTL,
+ FLAG_HAS_VISIBLE_REGION,
+ FLAG_HAS_INVISIBLE_REGION,
+ FLAG_HAS_INVISIBLE_REGION | FLAG_IS_RTL,
+ };
+
+ @Test
+ public void testBuilder() {
+ final int selectionStart = 30;
+ final int selectionEnd = 40;
+ final int composingTextStart = 32;
+ final String composingText = "test";
+ final int insertionMarkerFlags =
+ FLAG_HAS_VISIBLE_REGION | FLAG_HAS_INVISIBLE_REGION | FLAG_IS_RTL;
+ final float insertionMarkerHorizontal = 10.5f;
+ final float insertionMarkerTop = 100.1f;
+ final float insertionMarkerBaseline = 110.4f;
+ final float insertionMarkerBottom = 111.0f;
+
+ Matrix transformMatrix = new Matrix();
+ transformMatrix.setScale(10.0f, 20.0f);
+
+ final Builder builder = new Builder();
+ builder.setSelectionRange(selectionStart, selectionEnd)
+ .setComposingText(composingTextStart, composingText)
+ .setInsertionMarkerLocation(insertionMarkerHorizontal, insertionMarkerTop,
+ insertionMarkerBaseline, insertionMarkerBottom, insertionMarkerFlags)
+ .setMatrix(transformMatrix);
+ for (int i = 0; i < MANY_BOUNDS.length; i++) {
+ final RectF bounds = MANY_BOUNDS[i];
+ final int flags = MANY_FLAGS_ARRAY[i];
+ builder.addCharacterBounds(i, bounds.left, bounds.top, bounds.right, bounds.bottom,
+ flags);
+ }
+
+ final CursorAnchorInfo info = builder.build();
+ assertEquals(selectionStart, info.getSelectionStart());
+ assertEquals(selectionEnd, info.getSelectionEnd());
+ assertEquals(composingTextStart, info.getComposingTextStart());
+ assertTrue(TextUtils.equals(composingText, info.getComposingText()));
+ assertEquals(insertionMarkerFlags, info.getInsertionMarkerFlags());
+ assertEquals(insertionMarkerHorizontal, info.getInsertionMarkerHorizontal(), EPSILON);
+ assertEquals(insertionMarkerTop, info.getInsertionMarkerTop(), EPSILON);
+ assertEquals(insertionMarkerBaseline, info.getInsertionMarkerBaseline(), EPSILON);
+ assertEquals(insertionMarkerBottom, info.getInsertionMarkerBottom(), EPSILON);
+ assertEquals(transformMatrix, info.getMatrix());
+ for (int i = 0; i < MANY_BOUNDS.length; i++) {
+ final RectF expectedBounds = MANY_BOUNDS[i];
+ assertEquals(expectedBounds, info.getCharacterBounds(i));
+ }
+ assertNull(info.getCharacterBounds(-1));
+ assertNull(info.getCharacterBounds(MANY_BOUNDS.length + 1));
+ for (int i = 0; i < MANY_FLAGS_ARRAY.length; i++) {
+ final int expectedFlags = MANY_FLAGS_ARRAY[i];
+ assertEquals(expectedFlags, info.getCharacterBoundsFlags(i));
+ }
+ assertEquals(0, info.getCharacterBoundsFlags(-1));
+ assertEquals(0, info.getCharacterBoundsFlags(MANY_BOUNDS.length + 1));
+
+ // Make sure that the builder can reproduce the same object.
+ final CursorAnchorInfo info2 = builder.build();
+ assertEquals(selectionStart, info2.getSelectionStart());
+ assertEquals(selectionEnd, info2.getSelectionEnd());
+ assertEquals(composingTextStart, info2.getComposingTextStart());
+ assertTrue(TextUtils.equals(composingText, info2.getComposingText()));
+ assertEquals(insertionMarkerFlags, info2.getInsertionMarkerFlags());
+ assertEquals(insertionMarkerHorizontal, info2.getInsertionMarkerHorizontal(), EPSILON);
+ assertEquals(insertionMarkerTop, info2.getInsertionMarkerTop(), EPSILON);
+ assertEquals(insertionMarkerBaseline, info2.getInsertionMarkerBaseline(), EPSILON);
+ assertEquals(insertionMarkerBottom, info2.getInsertionMarkerBottom(), EPSILON);
+ assertEquals(transformMatrix, info2.getMatrix());
+ for (int i = 0; i < MANY_BOUNDS.length; i++) {
+ final RectF expectedBounds = MANY_BOUNDS[i];
+ assertEquals(expectedBounds, info2.getCharacterBounds(i));
+ }
+ assertNull(info2.getCharacterBounds(-1));
+ assertNull(info2.getCharacterBounds(MANY_BOUNDS.length + 1));
+ for (int i = 0; i < MANY_FLAGS_ARRAY.length; i++) {
+ final int expectedFlags = MANY_FLAGS_ARRAY[i];
+ assertEquals(expectedFlags, info2.getCharacterBoundsFlags(i));
+ }
+ assertEquals(0, info2.getCharacterBoundsFlags(-1));
+ assertEquals(0, info2.getCharacterBoundsFlags(MANY_BOUNDS.length + 1));
+ assertEquals(info, info2);
+ assertEquals(info.hashCode(), info2.hashCode());
+
+ // Make sure that object can be marshaled via Parcel.
+ final CursorAnchorInfo info3 = cloneViaParcel(info2);
+ assertEquals(selectionStart, info3.getSelectionStart());
+ assertEquals(selectionEnd, info3.getSelectionEnd());
+ assertEquals(composingTextStart, info3.getComposingTextStart());
+ assertTrue(TextUtils.equals(composingText, info3.getComposingText()));
+ assertEquals(insertionMarkerFlags, info3.getInsertionMarkerFlags());
+ assertEquals(insertionMarkerHorizontal, info3.getInsertionMarkerHorizontal(), EPSILON);
+ assertEquals(insertionMarkerTop, info3.getInsertionMarkerTop(), EPSILON);
+ assertEquals(insertionMarkerBaseline, info3.getInsertionMarkerBaseline(), EPSILON);
+ assertEquals(insertionMarkerBottom, info3.getInsertionMarkerBottom(), EPSILON);
+ assertEquals(transformMatrix, info3.getMatrix());
+ for (int i = 0; i < MANY_BOUNDS.length; i++) {
+ final RectF expectedBounds = MANY_BOUNDS[i];
+ assertEquals(expectedBounds, info3.getCharacterBounds(i));
+ }
+ assertNull(info3.getCharacterBounds(-1));
+ assertNull(info3.getCharacterBounds(MANY_BOUNDS.length + 1));
+ for (int i = 0; i < MANY_FLAGS_ARRAY.length; i++) {
+ final int expectedFlags = MANY_FLAGS_ARRAY[i];
+ assertEquals(expectedFlags, info3.getCharacterBoundsFlags(i));
+ }
+ assertEquals(0, info3.getCharacterBoundsFlags(-1));
+ assertEquals(0, info3.getCharacterBoundsFlags(MANY_BOUNDS.length + 1));
+ assertEquals(info.hashCode(), info3.hashCode());
+
+ builder.reset();
+ final CursorAnchorInfo uninitializedInfo = builder.build();
+ assertEquals(-1, uninitializedInfo.getSelectionStart());
+ assertEquals(-1, uninitializedInfo.getSelectionEnd());
+ assertEquals(-1, uninitializedInfo.getComposingTextStart());
+ assertNull(uninitializedInfo.getComposingText());
+ assertEquals(0, uninitializedInfo.getInsertionMarkerFlags());
+ assertEquals(Float.NaN, uninitializedInfo.getInsertionMarkerHorizontal(), EPSILON);
+ assertEquals(Float.NaN, uninitializedInfo.getInsertionMarkerTop(), EPSILON);
+ assertEquals(Float.NaN, uninitializedInfo.getInsertionMarkerBaseline(), EPSILON);
+ assertEquals(Float.NaN, uninitializedInfo.getInsertionMarkerBottom(), EPSILON);
+ assertEquals(new Matrix(), uninitializedInfo.getMatrix());
+ }
+
+ @Test
+ public void testEquality() {
+ final Matrix matrix1 = new Matrix();
+ matrix1.setTranslate(10.0f, 20.0f);
+ final Matrix matrix2 = new Matrix();
+ matrix2.setTranslate(110.0f, 120.0f);
+ final Matrix nanMatrix = new Matrix();
+ nanMatrix.setValues(new float[]{
+ Float.NaN, Float.NaN, Float.NaN,
+ Float.NaN, Float.NaN, Float.NaN,
+ Float.NaN, Float.NaN, Float.NaN});
+ final int selectionStart1 = 2;
+ final int selectionEnd1 = 7;
+ final String composingText1 = "0123456789";
+ final int composingTextStart1 = 0;
+ final int insertionMarkerFlags1 = FLAG_HAS_VISIBLE_REGION;
+ final float insertionMarkerHorizontal1 = 10.5f;
+ final float insertionMarkerTop1 = 100.1f;
+ final float insertionMarkerBaseline1 = 110.4f;
+ final float insertionMarkerBottom1 = 111.0f;
+ final int selectionStart2 = 4;
+ final int selectionEnd2 = 8;
+ final String composingText2 = "9876543210";
+ final int composingTextStart2 = 3;
+ final int insertionMarkerFlags2 =
+ FLAG_HAS_VISIBLE_REGION | FLAG_HAS_INVISIBLE_REGION | FLAG_IS_RTL;
+ final float insertionMarkerHorizontal2 = 14.5f;
+ final float insertionMarkerTop2 = 200.1f;
+ final float insertionMarkerBaseline2 = 210.4f;
+ final float insertionMarkerBottom2 = 211.0f;
+
+ // Default instance should be equal.
+ assertEquals(new Builder().build(), new Builder().build());
+
+ assertEquals(
+ new Builder().setSelectionRange(selectionStart1, selectionEnd1).build(),
+ new Builder().setSelectionRange(selectionStart1, selectionEnd1).build());
+ assertNotEquals(
+ new Builder().setSelectionRange(selectionStart1, selectionEnd1).build(),
+ new Builder().setSelectionRange(selectionStart1, selectionEnd2).build());
+ assertNotEquals(
+ new Builder().setSelectionRange(selectionStart1, selectionEnd1).build(),
+ new Builder().setSelectionRange(selectionStart2, selectionEnd1).build());
+ assertNotEquals(
+ new Builder().setSelectionRange(selectionStart1, selectionEnd1).build(),
+ new Builder().setSelectionRange(selectionStart2, selectionEnd2).build());
+ assertEquals(
+ new Builder().setComposingText(composingTextStart1, composingText1).build(),
+ new Builder().setComposingText(composingTextStart1, composingText1).build());
+ assertNotEquals(
+ new Builder().setComposingText(composingTextStart1, composingText1).build(),
+ new Builder().setComposingText(composingTextStart2, composingText1).build());
+ assertNotEquals(
+ new Builder().setComposingText(composingTextStart1, composingText1).build(),
+ new Builder().setComposingText(composingTextStart1, composingText2).build());
+ assertNotEquals(
+ new Builder().setComposingText(composingTextStart1, composingText1).build(),
+ new Builder().setComposingText(composingTextStart2, composingText2).build());
+
+ // For insertion marker locations, Float#NaN is treated as if it was a number.
+ assertEquals(
+ new Builder().setMatrix(matrix1).setInsertionMarkerLocation(
+ Float.NaN, Float.NaN, Float.NaN, Float.NaN,
+ insertionMarkerFlags1).build(),
+ new Builder().setMatrix(matrix1).setInsertionMarkerLocation(
+ Float.NaN, Float.NaN, Float.NaN, Float.NaN,
+ insertionMarkerFlags1).build());
+
+ // Check Matrix.
+ assertEquals(
+ new Builder().setMatrix(matrix1).build(),
+ new Builder().setMatrix(matrix1).build());
+ assertNotEquals(
+ new Builder().setMatrix(matrix1).build(),
+ new Builder().setMatrix(matrix2).build());
+ assertNotEquals(
+ new Builder().setMatrix(matrix1).build(),
+ new Builder().setMatrix(nanMatrix).build());
+ // Unlike insertion marker locations, Float#NaN in the matrix is treated as just a NaN as
+ // usual (NaN == NaN -> false).
+ assertNotEquals(
+ new Builder().setMatrix(nanMatrix).build(),
+ new Builder().setMatrix(nanMatrix).build());
+
+ assertEquals(
+ new Builder().setMatrix(matrix1).setInsertionMarkerLocation(
+ insertionMarkerHorizontal1, insertionMarkerTop1,
+ insertionMarkerBaseline1, insertionMarkerBottom1,
+ insertionMarkerFlags1).build(),
+ new Builder().setMatrix(matrix1).setInsertionMarkerLocation(
+ insertionMarkerHorizontal1, insertionMarkerTop1,
+ insertionMarkerBaseline1, insertionMarkerBottom1,
+ insertionMarkerFlags1).build());
+ assertNotEquals(
+ new Builder().setMatrix(matrix1).setInsertionMarkerLocation(
+ Float.NaN, insertionMarkerTop1,
+ insertionMarkerBaseline1, insertionMarkerBottom1,
+ insertionMarkerFlags1).build(),
+ new Builder().setMatrix(matrix1).setInsertionMarkerLocation(
+ insertionMarkerHorizontal1, insertionMarkerTop1,
+ insertionMarkerBaseline1, insertionMarkerBottom1,
+ insertionMarkerFlags1).build());
+ assertNotEquals(
+ new Builder().setMatrix(matrix1).setInsertionMarkerLocation(
+ insertionMarkerHorizontal1, insertionMarkerTop1,
+ insertionMarkerBaseline1, insertionMarkerBottom1,
+ insertionMarkerFlags1).build(),
+ new Builder().setMatrix(matrix1).setInsertionMarkerLocation(
+ insertionMarkerHorizontal2, insertionMarkerTop1,
+ insertionMarkerBaseline1, insertionMarkerBottom1,
+ insertionMarkerFlags1).build());
+ assertNotEquals(
+ new Builder().setMatrix(matrix1).setInsertionMarkerLocation(
+ insertionMarkerHorizontal1, insertionMarkerTop1,
+ insertionMarkerBaseline1, insertionMarkerBottom1,
+ insertionMarkerFlags1).build(),
+ new Builder().setMatrix(matrix1).setInsertionMarkerLocation(
+ insertionMarkerHorizontal1, insertionMarkerTop2,
+ insertionMarkerBaseline1, insertionMarkerBottom1,
+ insertionMarkerFlags1).build());
+ assertNotEquals(
+ new Builder().setMatrix(matrix1).setInsertionMarkerLocation(
+ insertionMarkerHorizontal1, insertionMarkerTop1,
+ insertionMarkerBaseline1, insertionMarkerBottom1,
+ insertionMarkerFlags1).build(),
+ new Builder().setMatrix(matrix1).setInsertionMarkerLocation(
+ insertionMarkerHorizontal1, insertionMarkerTop1,
+ insertionMarkerBaseline2, insertionMarkerBottom1,
+ insertionMarkerFlags1).build());
+ assertNotEquals(
+ new Builder().setMatrix(matrix1).setInsertionMarkerLocation(
+ insertionMarkerHorizontal1, insertionMarkerTop1,
+ insertionMarkerBaseline1, insertionMarkerBottom1,
+ insertionMarkerFlags1).build(),
+ new Builder().setMatrix(matrix1).setInsertionMarkerLocation(
+ insertionMarkerHorizontal2, insertionMarkerTop1,
+ insertionMarkerBaseline1, insertionMarkerBottom1,
+ insertionMarkerFlags1).build());
+ assertNotEquals(
+ new Builder().setMatrix(matrix1).setInsertionMarkerLocation(
+ insertionMarkerHorizontal1, insertionMarkerTop1,
+ insertionMarkerBaseline1, insertionMarkerBottom1,
+ insertionMarkerFlags1).build(),
+ new Builder().setMatrix(matrix1).setInsertionMarkerLocation(
+ insertionMarkerHorizontal1, insertionMarkerTop1,
+ insertionMarkerBaseline1, insertionMarkerBottom2,
+ insertionMarkerFlags1).build());
+ assertNotEquals(
+ new Builder().setMatrix(matrix1).setInsertionMarkerLocation(
+ insertionMarkerHorizontal1, insertionMarkerTop1,
+ insertionMarkerBaseline1, insertionMarkerBottom1,
+ insertionMarkerFlags1).build(),
+ new Builder().setMatrix(matrix1).setInsertionMarkerLocation(
+ insertionMarkerHorizontal1, insertionMarkerTop1,
+ insertionMarkerBaseline1, insertionMarkerBottom1,
+ insertionMarkerFlags2).build());
+ }
+
+ @Test
+ public void testMatrixIsCopied() {
+ final Matrix matrix1 = new Matrix();
+ matrix1.setTranslate(10.0f, 20.0f);
+ final Matrix matrix2 = new Matrix();
+ matrix2.setTranslate(110.0f, 120.0f);
+ final Matrix matrix3 = new Matrix();
+ matrix3.setTranslate(210.0f, 220.0f);
+ final Matrix matrix = new Matrix();
+ final Builder builder = new Builder();
+
+ matrix.set(matrix1);
+ builder.setMatrix(matrix);
+ matrix.postRotate(90.0f);
+
+ final CursorAnchorInfo firstInstance = builder.build();
+ assertEquals(matrix1, firstInstance.getMatrix());
+ matrix.set(matrix2);
+ builder.setMatrix(matrix);
+ final CursorAnchorInfo secondInstance = builder.build();
+ assertEquals(matrix1, firstInstance.getMatrix());
+ assertEquals(matrix2, secondInstance.getMatrix());
+
+ matrix.set(matrix3);
+ assertEquals(matrix1, firstInstance.getMatrix());
+ assertEquals(matrix2, secondInstance.getMatrix());
+ }
+
+ @Test
+ public void testMatrixIsRequired() {
+ final int selectionStart = 30;
+ final int selectionEnd = 40;
+ final int composingTextStart = 32;
+ final String composingText = "test";
+ final int insertionMarkerFlags = FLAG_HAS_VISIBLE_REGION;
+ final float insertionMarkerHorizontal = 10.5f;
+ final float insertionMarkerTop = 100.1f;
+ final float insertionMarkerBaseline = 110.4f;
+ final float insertionMarkerBottom = 111.0f;
+ Matrix transformMatrix = new Matrix();
+ transformMatrix.setScale(10.0f, 20.0f);
+
+ final Builder builder = new Builder();
+ // Check twice to make sure if Builder#reset() works as expected.
+ for (int repeatCount = 0; repeatCount < 2; ++repeatCount) {
+ builder.setSelectionRange(selectionStart, selectionEnd)
+ .setComposingText(composingTextStart, composingText);
+ try {
+ // Should succeed as coordinate transformation matrix is not required if no
+ // positional information is specified.
+ builder.build();
+ } catch (IllegalArgumentException ex) {
+ fail();
+ }
+
+ builder.setInsertionMarkerLocation(insertionMarkerHorizontal, insertionMarkerTop,
+ insertionMarkerBaseline, insertionMarkerBottom, insertionMarkerFlags);
+ try {
+ // Coordinate transformation matrix is required if no positional information is
+ // specified.
+ builder.build();
+ fail();
+ } catch (IllegalArgumentException ex) {
+ }
+
+ builder.setMatrix(transformMatrix);
+ try {
+ // Should succeed as coordinate transformation matrix is required.
+ builder.build();
+ } catch (IllegalArgumentException ex) {
+ fail();
+ }
+
+ builder.reset();
+ }
+ }
+
+ @Test
+ public void testBuilderAddCharacterBounds() {
+ // A negative index should be rejected.
+ try {
+ new Builder().addCharacterBounds(-1, 0.0f, 0.0f, 0.0f, 0.0f, FLAG_HAS_VISIBLE_REGION);
+ fail();
+ } catch (IllegalArgumentException ex) {
+ }
+ }
+
+ private static CursorAnchorInfo cloneViaParcel(CursorAnchorInfo src) {
+ Parcel parcel = null;
+ try {
+ parcel = Parcel.obtain();
+ src.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ return new CursorAnchorInfo(parcel);
+ } finally {
+ if (parcel != null) {
+ parcel.recycle();
+ }
+ }
+ }
+}
diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/InputMethodServiceTest.java b/tests/inputmethod/src/android/view/inputmethod/cts/InputMethodServiceTest.java
index b9103ef..0b20b85 100644
--- a/tests/inputmethod/src/android/view/inputmethod/cts/InputMethodServiceTest.java
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/InputMethodServiceTest.java
@@ -19,6 +19,7 @@
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE;
import static android.view.inputmethod.cts.util.TestUtils.getOnMainSync;
+import static android.view.inputmethod.cts.util.TestUtils.runOnMainSync;
import static android.view.inputmethod.cts.util.TestUtils.waitOnMainUntil;
import static com.android.cts.mockime.ImeEventStreamTestUtils.EventFilterMode.CHECK_EXIT_EVENT_ONLY;
@@ -33,14 +34,17 @@
import static org.junit.Assert.fail;
import android.app.Instrumentation;
+import android.graphics.Matrix;
import android.inputmethodservice.InputMethodService;
import android.os.SystemClock;
import android.text.TextUtils;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
+import android.view.inputmethod.CursorAnchorInfo;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputConnectionWrapper;
+import android.view.inputmethod.InputMethodManager;
import android.view.inputmethod.cts.util.EndToEndImeTestBase;
import android.view.inputmethod.cts.util.TestActivity;
import android.view.inputmethod.cts.util.TestUtils;
@@ -64,6 +68,7 @@
import java.util.ArrayList;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Predicate;
@@ -287,4 +292,76 @@
expectedKeyCode, uptimeStart, uptimeEnd);
}
}
+
+ /**
+ * Ensure that {@link InputConnection#requestCursorUpdates(int)} works for the built-in
+ * {@link EditText} and {@link InputMethodService#onUpdateCursorAnchorInfo(CursorAnchorInfo)}
+ * will be called back.
+ */
+ @Test
+ public void testOnUpdateCursorAnchorInfo() throws Exception {
+ try (MockImeSession imeSession = MockImeSession.create(
+ InstrumentationRegistry.getContext(),
+ InstrumentationRegistry.getInstrumentation().getUiAutomation(),
+ new ImeSettings.Builder())) {
+ final String marker =
+ "testOnUpdateCursorAnchorInfo()/" + SystemClock.elapsedRealtimeNanos();
+
+ final AtomicReference<EditText> editTextRef = new AtomicReference<>();
+ final AtomicInteger requestCursorUpdatesCallCount = new AtomicInteger();
+ TestActivity.startSync(activity -> {
+ final LinearLayout layout = new LinearLayout(activity);
+ layout.setOrientation(LinearLayout.VERTICAL);
+
+ final EditText editText = new EditText(activity) {
+ @Override
+ public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
+ final InputConnection original = super.onCreateInputConnection(outAttrs);
+ return new InputConnectionWrapper(original, false) {
+ @Override
+ public boolean requestCursorUpdates(int cursorUpdateMode) {
+ if (cursorUpdateMode == InputConnection.CURSOR_UPDATE_IMMEDIATE) {
+ requestCursorUpdatesCallCount.incrementAndGet();
+ return true;
+ }
+ return false;
+ }
+ };
+ }
+ };
+ editTextRef.set(editText);
+ editText.setPrivateImeOptions(marker);
+ layout.addView(editText);
+ editText.requestFocus();
+ return layout;
+ });
+ final EditText editText = editTextRef.get();
+
+ final ImeEventStream stream = imeSession.openEventStream();
+ expectEvent(stream, editorMatcher("onStartInput", marker), TIMEOUT);
+
+ // Make sure that InputConnection#requestCursorUpdates() returns true.
+ assertTrue(expectCommand(stream,
+ imeSession.callRequestCursorUpdates(InputConnection.CURSOR_UPDATE_IMMEDIATE),
+ TIMEOUT).getReturnBooleanValue());
+
+ // Also make sure that requestCursorUpdates() actually gets called only once.
+ assertEquals(1, requestCursorUpdatesCallCount.get());
+
+ final CursorAnchorInfo originalCursorAnchorInfo = new CursorAnchorInfo.Builder()
+ .setMatrix(new Matrix())
+ .setInsertionMarkerLocation(3.0f, 4.0f, 5.0f, 6.0f, 0)
+ .setSelectionRange(7, 8)
+ .build();
+
+ runOnMainSync(() -> editText.getContext().getSystemService(InputMethodManager.class)
+ .updateCursorAnchorInfo(editText, originalCursorAnchorInfo));
+
+ final CursorAnchorInfo receivedCursorAnchorInfo = expectEvent(stream,
+ event -> "onUpdateCursorAnchorInfo".equals(event.getEventName()),
+ TIMEOUT).getArguments().getParcelable("cursorAnchorInfo");
+ assertNotNull(receivedCursorAnchorInfo);
+ assertEquals(receivedCursorAnchorInfo, originalCursorAnchorInfo);
+ }
+ }
}
diff --git a/tests/providerui/src/android/providerui/cts/MediaStoreUiTest.java b/tests/providerui/src/android/providerui/cts/MediaStoreUiTest.java
index 9814c2a..b5f30b6 100644
--- a/tests/providerui/src/android/providerui/cts/MediaStoreUiTest.java
+++ b/tests/providerui/src/android/providerui/cts/MediaStoreUiTest.java
@@ -255,7 +255,7 @@
grantRequisitePermissions(pkg, req, locationPermissions);
- Result result = getImageOrVideoCaptureIntentResult(intent, false, pkg);
+ Result result = getImageCaptureIntentResult(intent, pkg);
assertTrue("exists", target.exists());
assertTrue("has data", target.length() > 65536);
@@ -267,7 +267,9 @@
assertAttribute(exif, ExifInterface.TAG_MODEL);
assertAttribute(exif, ExifInterface.TAG_DATETIME);
float[] latLong = new float[2];
- assertTrue("Should not contain location information ", !exif.getLatLong(latLong));
+ Boolean hasLocation = exif.getLatLong(latLong);
+ assertTrue("Should not contain location information latitude: " + latLong[0] +
+ " longitude: " + latLong[1], !hasLocation);
revokeRequisitePermissions(pkg, req, locationPermissions);
}
@@ -292,75 +294,7 @@
}
}
- /**
- * Verify that whoever handles {@link MediaStore#ACTION_VIDEO_CAPTURE} can
- * correctly write the contents into a passed {@code content://} Uri, without location
- * information.
- */
- public void testVideoCaptureWithInadequeteLocationPermissions() throws Exception {
- Set<String> perms = new HashSet<>();
- perms.add(ACCESS_COARSE_LOCATION);
- perms.add(ACCESS_BACKGROUND_LOCATION);
- perms.add(ACCESS_MEDIA_LOCATION);
- testVideoCaptureWithoutLocation(perms);
- }
-
- /**
- * Helper method to verify that whoever handles {@link MediaStore#ACTION_VIDEO_CAPTURE} can
- * correctly write the contents into a passed {@code content://} Uri, necessarily without
- * location information when ACCESS_FINE_LOCATION permissions aren't given.
- */
-
- private void testVideoCaptureWithoutLocation(Set<String> locationPermissions)
- throws Exception {
- assertFalse("testVideoCaptureWithoutLocation should not be passed ACCESS_FINE_LOCATION",
- locationPermissions.contains(ACCESS_FINE_LOCATION));
- final Context context = getInstrumentation().getContext();
- if (!context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)) {
- Log.d(TAG, "Skipping due to lack of camera");
- return;
- }
-
- String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
-
- final File targetDir = new File(context.getFilesDir(), "debug");
- final File target = new File(targetDir, timeStamp + "video.mp4");
-
- targetDir.mkdirs();
- assertFalse(target.exists());
-
- final Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
- intent.putExtra(MediaStore.EXTRA_OUTPUT,
- FileProvider.getUriForFile(context, "android.providerui.cts.fileprovider", target));
-
- // Figure out who is going to answer the request
- final ResolveInfo ri = context.getPackageManager().resolveActivity(intent, 0);
- final String pkg = ri.activityInfo.packageName;
- Log.d(TAG, "We're probably launching " + ri);
-
- final PackageInfo pi = context.getPackageManager().getPackageInfo(pkg,
- PackageManager.GET_PERMISSIONS);
- final Set<String> req = new HashSet<>();
- req.addAll(Arrays.asList(pi.requestedPermissions));
-
- grantRequisitePermissions(pkg, req, locationPermissions);
- Result result = getImageOrVideoCaptureIntentResult(intent, true, pkg);
-
- assertTrue("exists", target.exists());
- assertTrue("has data", target.length() > 65536);
-
- // Check that the metadata retriever can at least identify video being present.
- final MediaMetadataRetriever mediaRetriever = new MediaMetadataRetriever();
- mediaRetriever.setDataSource(target.toString());
- assertNotNull(mediaRetriever.extractMetadata(METADATA_KEY_HAS_VIDEO));
- Log.d(TAG, "duration of video: " + mediaRetriever.extractMetadata(METADATA_KEY_DURATION));
- Log.d(TAG, "location of video: " + mediaRetriever.extractMetadata(METADATA_KEY_LOCATION));
- assertNull(mediaRetriever.extractMetadata(METADATA_KEY_LOCATION));
- revokeRequisitePermissions(pkg, req, locationPermissions);
- mediaRetriever.release();
- }
-
- private Result getImageOrVideoCaptureIntentResult(Intent intent, boolean isVideo, String pkg)
+ private Result getImageCaptureIntentResult(Intent intent, String pkg)
throws Exception {
mActivity.startActivityForResult(intent, REQUEST_CODE);
@@ -374,10 +308,6 @@
mDevice.pressKeyCode(KeyEvent.KEYCODE_CAMERA);
mDevice.waitForIdle();
SystemClock.sleep(5 * DateUtils.SECOND_IN_MILLIS);
- if (isVideo) {
- // Stop recording
- mDevice.pressKeyCode(KeyEvent.KEYCODE_CAMERA);
- }
// We're done.
mDevice.pressKeyCode(KeyEvent.KEYCODE_DPAD_CENTER);
mDevice.waitForIdle();
@@ -406,9 +336,6 @@
maybeClick(By.pkg(pkg).descContains("Capture"));
mDevice.waitForIdle();
SystemClock.sleep(5 * DateUtils.SECOND_IN_MILLIS);
- if (isVideo) {
- maybeClick(By.pkg(pkg).descContains("Stop"));
- }
maybeClick(By.pkg(pkg).descContains("Done"));
mDevice.waitForIdle();
diff --git a/tests/sensor/AndroidManifest.xml b/tests/sensor/AndroidManifest.xml
index 48afe9c..8dabdb1 100644
--- a/tests/sensor/AndroidManifest.xml
+++ b/tests/sensor/AndroidManifest.xml
@@ -18,9 +18,10 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android.hardware.sensor.cts">
+ <uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />
+ <uses-permission android:name="android.permission.BODY_SENSORS" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
- <uses-permission android:name="android.permission.BODY_SENSORS" />
<application>
<uses-library android:name="android.test.runner" />
diff --git a/tests/sensor/AndroidTest.xml b/tests/sensor/AndroidTest.xml
index 643a7ac..8edee41 100644
--- a/tests/sensor/AndroidTest.xml
+++ b/tests/sensor/AndroidTest.xml
@@ -19,10 +19,6 @@
<option name="config-descriptor:metadata" key="parameter" value="instant_app" />
<option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.LocationCheck" />
- <!-- Switch to run test in user 0 -->
- <target_preparer class="com.android.tradefed.targetprep.SwitchUserTargetPreparer">
- <option name="user-type" value="system" />
- </target_preparer>
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="CtsSensorTestCases.apk" />
diff --git a/tests/sensor/src/android/hardware/cts/SensorBatchingTests.java b/tests/sensor/src/android/hardware/cts/SensorBatchingTests.java
index 1b2b25c..17f5d07 100644
--- a/tests/sensor/src/android/hardware/cts/SensorBatchingTests.java
+++ b/tests/sensor/src/android/hardware/cts/SensorBatchingTests.java
@@ -342,7 +342,7 @@
sensorRate,
batching,
flush);
- stats.logToFile(fileName);
+ stats.logToFile(environment.getContext(), fileName);
}
}
}
diff --git a/tests/sensor/src/android/hardware/cts/SensorIntegrationTests.java b/tests/sensor/src/android/hardware/cts/SensorIntegrationTests.java
index d53034c..a125af3 100644
--- a/tests/sensor/src/android/hardware/cts/SensorIntegrationTests.java
+++ b/tests/sensor/src/android/hardware/cts/SensorIntegrationTests.java
@@ -260,7 +260,10 @@
// Create a second operation that will run in parallel and request the fastest rate after
// an initial delay. The delay is to ensure that the first operation has enabled the sensor.
- // The sensor should begin reporting at the newly requested rate.
+ // The sensor should begin reporting at the newly requested rate. Execute a flush prior to
+ // the reconfiguration to ensure that the lower frequency events are not received after the
+ // reconfiguration of the sensor.
+ SequentialSensorOperation sequentialSensorOperation = new SequentialSensorOperation();
environment = new TestSensorEnvironment(
context,
sensor,
@@ -268,15 +271,22 @@
true, /* isIntegrationTest */
sensor.getMinDelay(),
0 /* max reporting latency */);
+
+ // Create the flush operation with a delay to ensure the low frequency configuration was
+ // handled and executed.
+ TestSensorOperation flushOperation = TestSensorOperation.createFlushOperation(
+ environment, DELAY_BEFORE_CHANGING_RATE_SEC, TimeUnit.SECONDS);
+ sequentialSensorOperation.add(flushOperation);
+
+ // Create the reconfiguration request and add it after the flush
TestSensorOperation sensorOperationFast = TestSensorOperation.createOperation(
environment, EVENTS_FOR_VERIFICATION);
sensorOperationFast.addVerification(FrequencyVerification.getDefault(environment));
- DelaySensorOperation delaySensorOperation = new DelaySensorOperation(
- sensorOperationFast,
- DELAY_BEFORE_CHANGING_RATE_SEC,
- TimeUnit.SECONDS);
- operation.add(delaySensorOperation);
+ sequentialSensorOperation.add(sensorOperationFast);
+ // Add the sequential operation containing the flush and high frequency request to the
+ // existing parallel operation that already contains the low frequency request.
+ operation.add(sequentialSensorOperation);
operation.execute(getCurrentTestNode());
operation.getStats().log(TAG);
}
diff --git a/tests/sensor/src/android/hardware/cts/SensorTest.java b/tests/sensor/src/android/hardware/cts/SensorTest.java
index 4c70cd9..debf8ce 100644
--- a/tests/sensor/src/android/hardware/cts/SensorTest.java
+++ b/tests/sensor/src/android/hardware/cts/SensorTest.java
@@ -181,6 +181,17 @@
assertNull(sensor);
}
+ sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_PRESSURE);
+ boolean hasPressure = getContext().getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_SENSOR_BAROMETER);
+ // pressure sensor is optional
+ if (hasPressure) {
+ assertEquals(Sensor.TYPE_PRESSURE, sensor.getType());
+ assertSensorValues(sensor);
+ } else {
+ assertNull(sensor);
+ }
+
sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION);
// Note: orientation sensor is deprecated.
if (sensor != null) {
@@ -527,7 +538,9 @@
sensor.getName(), sensor.getPower() >= 0);
assertTrue("Max resolution must be positive. Resolution=" + sensor.getResolution() +
" " + sensor.getName(), sensor.getResolution() >= 0);
- if (SensorCtsHelper.hasResolutionRequirement(sensor)) {
+ boolean hasHifiSensors = getContext().getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_HIFI_SENSORS);
+ if (SensorCtsHelper.hasResolutionRequirement(sensor, hasHifiSensors)) {
float requiredResolution = SensorCtsHelper.getRequiredResolutionForSensor(sensor);
assertTrue("Resolution must be <= " + requiredResolution + ". Resolution=" +
sensor.getResolution() + " " + sensor.getName(),
diff --git a/tests/sensor/src/android/hardware/cts/SingleSensorTests.java b/tests/sensor/src/android/hardware/cts/SingleSensorTests.java
index a626957..1f40188 100644
--- a/tests/sensor/src/android/hardware/cts/SingleSensorTests.java
+++ b/tests/sensor/src/android/hardware/cts/SingleSensorTests.java
@@ -599,7 +599,7 @@
"single_%s_%s.txt",
SensorStats.getSanitizedSensorName(environment.getSensor()),
environment.getFrequencyString());
- stats.logToFile(fileName);
+ stats.logToFile(environment.getContext(), fileName);
}
}
}
diff --git a/tests/sensor/src/android/hardware/cts/helpers/SensorCtsHelper.java b/tests/sensor/src/android/hardware/cts/helpers/SensorCtsHelper.java
index 5ad0c43..4e18d57 100644
--- a/tests/sensor/src/android/hardware/cts/helpers/SensorCtsHelper.java
+++ b/tests/sensor/src/android/hardware/cts/helpers/SensorCtsHelper.java
@@ -15,6 +15,7 @@
*/
package android.hardware.cts.helpers;
+import android.content.Context;
import android.hardware.Sensor;
import android.os.Environment;
import android.util.Log;
@@ -255,8 +256,8 @@
/**
* @return A {@link File} representing a root directory to store sensor tests data.
*/
- public static File getSensorTestDataDirectory() throws IOException {
- File dataDirectory = new File(Environment.getExternalStorageDirectory(), "sensorTests/");
+ public static File getSensorTestDataDirectory(Context context) throws IOException {
+ File dataDirectory = context.getExternalFilesDir("sensorTests");
return createDirectoryStructure(dataDirectory);
}
@@ -265,8 +266,9 @@
*
* @param subdirectory The sub-directory's name.
*/
- public static File getSensorTestDataDirectory(String subdirectory) throws IOException {
- File subdirectoryFile = new File(getSensorTestDataDirectory(), subdirectory);
+ public static File getSensorTestDataDirectory(Context context, String subdirectory)
+ throws IOException {
+ File subdirectoryFile = new File(getSensorTestDataDirectory(context), subdirectory);
return createDirectoryStructure(subdirectoryFile);
}
@@ -328,7 +330,7 @@
return "";
}
- public static boolean hasResolutionRequirement(Sensor sensor) {
+ public static boolean hasResolutionRequirement(Sensor sensor, boolean hasHifiSensors) {
switch (sensor.getType()) {
case Sensor.TYPE_ACCELEROMETER:
case Sensor.TYPE_ACCELEROMETER_UNCALIBRATED:
@@ -337,6 +339,10 @@
case Sensor.TYPE_MAGNETIC_FIELD:
case Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED:
return true;
+
+ case Sensor.TYPE_PRESSURE:
+ // Pressure sensor only has a resolution requirement when there are HiFi sensors
+ return hasHifiSensors;
}
return false;
}
@@ -359,6 +365,11 @@
// Magnetometer must have a resolution equal to or denser
// than 0.6 uT
return 0.6f;
+ case Sensor.TYPE_PRESSURE:
+ // Pressure sensor must have at least 80 LSB / hPa which is
+ // equivalent to 0.0125 hPa / LSB. Allow for a small margin of
+ // error due to rounding errors.
+ return 1.01f * (1.0f / 80.0f);
}
return 0.0f;
}
diff --git a/tests/sensor/src/android/hardware/cts/helpers/SensorStats.java b/tests/sensor/src/android/hardware/cts/helpers/SensorStats.java
index 4387f9d..2d9f528 100644
--- a/tests/sensor/src/android/hardware/cts/helpers/SensorStats.java
+++ b/tests/sensor/src/android/hardware/cts/helpers/SensorStats.java
@@ -16,6 +16,7 @@
package android.hardware.cts.helpers;
+import android.content.Context;
import android.hardware.Sensor;
import android.hardware.cts.helpers.sensoroperations.SensorOperation;
import android.os.Environment;
@@ -148,12 +149,12 @@
/**
* Utility method to log the stats to a file. Will overwrite the file if it already exists.
*/
- public void logToFile(String fileName) throws IOException {
+ public void logToFile(Context context, String fileName) throws IOException {
// Check that external storage is mounted before attempting to write the recorded sensor
// data to file. This is necessary since Instant Apps do not have access to external
// storage.
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
- File statsDirectory = SensorCtsHelper.getSensorTestDataDirectory("stats/");
+ File statsDirectory = SensorCtsHelper.getSensorTestDataDirectory(context, "stats/");
File logFile = new File(statsDirectory, fileName);
final Map<String, Object> flattened = flatten();
FileWriter fileWriter = new FileWriter(logFile, false /* append */);
@@ -168,7 +169,7 @@
/**
* Provides a sanitized sensor name, that can be used in file names.
- * See {@link #logToFile(String)}.
+ * See {@link #logToFile(Context, String)}.
*/
public static String getSanitizedSensorName(Sensor sensor) throws SensorTestPlatformException {
return SensorCtsHelper.sanitizeStringForFileName(sensor.getStringType());
diff --git a/tests/sensor/src/android/hardware/cts/helpers/TestSensorEventListener.java b/tests/sensor/src/android/hardware/cts/helpers/TestSensorEventListener.java
index b096987..6ea353fb 100644
--- a/tests/sensor/src/android/hardware/cts/helpers/TestSensorEventListener.java
+++ b/tests/sensor/src/android/hardware/cts/helpers/TestSensorEventListener.java
@@ -259,7 +259,8 @@
}
}
- File eventsDirectory = SensorCtsHelper.getSensorTestDataDirectory("events/");
+ File eventsDirectory = SensorCtsHelper.getSensorTestDataDirectory(
+ mEnvironment.getContext(), "events/");
File logFile = new File(eventsDirectory, fileName);
FileWriter fileWriter = new FileWriter(logFile, false /* append */);
try (BufferedWriter writer = new BufferedWriter(fileWriter)) {
diff --git a/tests/signature/api-check/android-test-base-current-api/AndroidTest.xml b/tests/signature/api-check/android-test-base-current-api/AndroidTest.xml
index 2760a6c..49ac0a7 100644
--- a/tests/signature/api-check/android-test-base-current-api/AndroidTest.xml
+++ b/tests/signature/api-check/android-test-base-current-api/AndroidTest.xml
@@ -16,7 +16,7 @@
<configuration description="Config for CTS Android Test Base Current API Signature test cases">
<option name="test-suite-tag" value="cts" />
<option name="config-descriptor:metadata" key="component" value="systems" />
- <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
<option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
<option name="run-command" value="mkdir -p /data/local/tmp/signature-test" />
diff --git a/tests/signature/api-check/android-test-mock-current-api/AndroidTest.xml b/tests/signature/api-check/android-test-mock-current-api/AndroidTest.xml
index f5e841860..e90984d 100644
--- a/tests/signature/api-check/android-test-mock-current-api/AndroidTest.xml
+++ b/tests/signature/api-check/android-test-mock-current-api/AndroidTest.xml
@@ -16,7 +16,7 @@
<configuration description="Config for CTS Android Test Mock Current API Signature test cases">
<option name="test-suite-tag" value="cts" />
<option name="config-descriptor:metadata" key="component" value="systems" />
- <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
<option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
<option name="run-command" value="mkdir -p /data/local/tmp/signature-test" />
diff --git a/tests/signature/api-check/android-test-runner-current-api/AndroidTest.xml b/tests/signature/api-check/android-test-runner-current-api/AndroidTest.xml
index 5280d2f..e2cb9e0 100644
--- a/tests/signature/api-check/android-test-runner-current-api/AndroidTest.xml
+++ b/tests/signature/api-check/android-test-runner-current-api/AndroidTest.xml
@@ -16,7 +16,7 @@
<configuration description="Config for CTS Android Test Runner Current API Signature test cases">
<option name="test-suite-tag" value="cts" />
<option name="config-descriptor:metadata" key="component" value="systems" />
- <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
<option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
<option name="run-command" value="mkdir -p /data/local/tmp/signature-test" />
diff --git a/tests/signature/api-check/apache-http-legacy-uses-library-api/AndroidTest.xml b/tests/signature/api-check/apache-http-legacy-uses-library-api/AndroidTest.xml
index c0f39d7..8674d5b 100644
--- a/tests/signature/api-check/apache-http-legacy-uses-library-api/AndroidTest.xml
+++ b/tests/signature/api-check/apache-http-legacy-uses-library-api/AndroidTest.xml
@@ -16,7 +16,7 @@
<configuration description="Config for CTS Apache Http Legacy UsesLibrary API Signature test cases">
<option name="test-suite-tag" value="cts" />
<option name="config-descriptor:metadata" key="component" value="systems" />
- <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
<option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
<option name="run-command" value="mkdir -p /data/local/tmp/signature-test" />
diff --git a/tests/signature/api-check/current-api/AndroidTest.xml b/tests/signature/api-check/current-api/AndroidTest.xml
index 64dd957..dc6ded5 100644
--- a/tests/signature/api-check/current-api/AndroidTest.xml
+++ b/tests/signature/api-check/current-api/AndroidTest.xml
@@ -16,7 +16,7 @@
<configuration description="Config for CTS Current API Signature test cases">
<option name="test-suite-tag" value="cts" />
<option name="config-descriptor:metadata" key="component" value="systems" />
- <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
<option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
<option name="not-shardable" value="true" />
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
diff --git a/tests/signature/api-check/shared-libs-api/AndroidTest.xml b/tests/signature/api-check/shared-libs-api/AndroidTest.xml
index fa2f1ea..29262ca 100644
--- a/tests/signature/api-check/shared-libs-api/AndroidTest.xml
+++ b/tests/signature/api-check/shared-libs-api/AndroidTest.xml
@@ -16,7 +16,7 @@
<configuration description="Config for CTS Shared Libraries API Signature test cases">
<option name="test-suite-tag" value="cts" />
<option name="config-descriptor:metadata" key="component" value="systems" />
- <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
<option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
<option name="run-command" value="mkdir -p /data/local/tmp/signature-test" />
diff --git a/tests/signature/api-check/system-annotation/AndroidTest.xml b/tests/signature/api-check/system-annotation/AndroidTest.xml
index b9066eb..f8d1e92 100644
--- a/tests/signature/api-check/system-annotation/AndroidTest.xml
+++ b/tests/signature/api-check/system-annotation/AndroidTest.xml
@@ -16,7 +16,7 @@
<configuration description="Config for CTS System Current API Signature test cases">
<option name="test-suite-tag" value="cts" />
<option name="config-descriptor:metadata" key="component" value="systems" />
- <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
<option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
<option name="run-command" value="mkdir -p /data/local/tmp/signature-test" />
diff --git a/tests/signature/intent-check/AndroidTest.xml b/tests/signature/intent-check/AndroidTest.xml
index b0b5a6b..e76cd79 100644
--- a/tests/signature/intent-check/AndroidTest.xml
+++ b/tests/signature/intent-check/AndroidTest.xml
@@ -16,7 +16,7 @@
<configuration description="Config for CTS Intent Signature test cases">
<option name="test-suite-tag" value="cts" />
<option name="config-descriptor:metadata" key="component" value="systems" />
- <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
<option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
<option name="not-shardable" value="true" />
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher">
diff --git a/tests/tests/animation/AndroidTest.xml b/tests/tests/animation/AndroidTest.xml
index 13b2a02..7b9d17d 100644
--- a/tests/tests/animation/AndroidTest.xml
+++ b/tests/tests/animation/AndroidTest.xml
@@ -17,6 +17,7 @@
<option name="test-suite-tag" value="cts" />
<option name="config-descriptor:metadata" key="component" value="uitoolkit" />
<option name="config-descriptor:metadata" key="parameter" value="instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="CtsAnimationTestCases.apk" />
diff --git a/tests/tests/app/AndroidTest.xml b/tests/tests/app/AndroidTest.xml
index adebf03..bb95f79 100644
--- a/tests/tests/app/AndroidTest.xml
+++ b/tests/tests/app/AndroidTest.xml
@@ -17,6 +17,8 @@
<option name="test-suite-tag" value="cts" />
<option name="config-descriptor:metadata" key="component" value="misc" />
<option name="not-shardable" value="true" />
+ <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="CtsAndroidAppTestCases.apk" />
diff --git a/tests/tests/appop/Android.bp b/tests/tests/appop/Android.bp
index 112a378..e4ca806 100644
--- a/tests/tests/appop/Android.bp
+++ b/tests/tests/appop/Android.bp
@@ -23,7 +23,8 @@
"compatibility-device-util-axt",
"androidx.legacy_legacy-support-v4",
"platform-test-annotations",
- "truth-prebuilt"
+ "truth-prebuilt",
+ "androidx.test.uiautomator_uiautomator"
],
test_suites: [
diff --git a/tests/tests/appop/AndroidManifest.xml b/tests/tests/appop/AndroidManifest.xml
index a01749b..78c3816 100644
--- a/tests/tests/appop/AndroidManifest.xml
+++ b/tests/tests/appop/AndroidManifest.xml
@@ -22,6 +22,7 @@
<application>
<uses-library android:name="android.test.runner"/>
+ <activity android:name=".UidStateForceActivity" />
</application>
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
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 61c1463..2769f36 100644
--- a/tests/tests/appop/src/android/app/appops/cts/HistoricalAppopsTest.kt
+++ b/tests/tests/appop/src/android/app/appops/cts/HistoricalAppopsTest.kt
@@ -28,13 +28,15 @@
import com.google.common.truth.Truth.assertThat
import org.junit.After
import org.junit.Before
-import org.junit.Ignore
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 {
@@ -42,6 +44,17 @@
private var appOpsManager: AppOpsManager? = null
private var packageName: String? = null
+ // Start an activity to make sure this app counts as being in the foreground
+ @Rule @JvmField
+ var activityRule = ActivityTestRule(UidStateForceActivity::class.java)
+
+ @Before
+ fun wakeScreenUp() {
+ val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
+ device.wakeUp()
+ device.executeShellCommand("wm dismiss-keyguard")
+ }
+
@Before
fun setUpTest() {
appOpsManager = getContext().getSystemService(AppOpsManager::class.java)
@@ -61,31 +74,26 @@
}
@Test
- @Ignore
fun testGetHistoricalPackageOpsForegroundAccessInMemoryBucket() {
testGetHistoricalPackageOpsForegroundAtDepth(0)
}
@Test
- @Ignore
fun testGetHistoricalPackageOpsForegroundAccessFirstOnDiskBucket() {
testGetHistoricalPackageOpsForegroundAtDepth(1)
}
@Test
- @Ignore
fun testHistoricalAggregationOneLevelsDeep() {
testHistoricalAggregationSomeLevelsDeep(0)
}
@Test
- @Ignore
fun testHistoricalAggregationTwoLevelsDeep() {
testHistoricalAggregationSomeLevelsDeep(1)
}
@Test
- @Ignore
fun testHistoricalAggregationOverflow() {
// Configure historical registry behavior.
appOpsManager!!.setHistoryParameters(
@@ -123,7 +131,6 @@
}
@Test
- @Ignore
fun testHistoryTimeTravel() {
// Configure historical registry behavior.
appOpsManager!!.setHistoryParameters(
@@ -222,6 +229,8 @@
appOpsManager!!.setUidMode(AppOpsManager.OPSTR_START_FOREGROUND, 2000,
AppOpsManager.MODE_ALLOWED)
+ activityRule.activity.waitForResumed()
+
try {
val noteCount = 5
@@ -282,11 +291,11 @@
.isEqualTo(noteCount)
assertThat(op.getBackgroundAccessCount(AppOpsManager.OP_FLAGS_ALL)).isEqualTo(0)
assertThat(getAccessCount(op, AppOpsManager.UID_STATE_PERSISTENT)).isEqualTo(0)
- assertThat(getAccessCount(op, AppOpsManager.UID_STATE_TOP)).isEqualTo(0)
+ assertThat(getAccessCount(op, AppOpsManager.UID_STATE_TOP)).isEqualTo(noteCount)
assertThat(getAccessCount(op, AppOpsManager.UID_STATE_FOREGROUND_SERVICE_LOCATION))
.isEqualTo(0)
assertThat(getAccessCount(op, AppOpsManager.UID_STATE_FOREGROUND_SERVICE))
- .isEqualTo(noteCount)
+ .isEqualTo(0)
assertThat(getAccessCount(op, AppOpsManager.UID_STATE_FOREGROUND)).isEqualTo(0)
assertThat(getAccessCount(op, AppOpsManager.UID_STATE_BACKGROUND)).isEqualTo(0)
assertThat(getAccessCount(op, AppOpsManager.UID_STATE_CACHED)).isEqualTo(0)
diff --git a/tests/tests/appop/src/android/app/appops/cts/UidStateForceActivity.kt b/tests/tests/appop/src/android/app/appops/cts/UidStateForceActivity.kt
new file mode 100644
index 0000000..2bf55cb
--- /dev/null
+++ b/tests/tests/appop/src/android/app/appops/cts/UidStateForceActivity.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.appops.cts
+
+import android.app.Activity
+import java.util.concurrent.locks.ReentrantLock
+import kotlin.concurrent.withLock
+
+class UidStateForceActivity : Activity() {
+ private val lock = ReentrantLock()
+ private val condition = lock.newCondition()
+ private var isActivityResumed = false
+
+ override fun onResume() {
+ super.onResume()
+
+ lock.withLock {
+ isActivityResumed = true
+ condition.signalAll()
+ }
+ }
+
+ override fun onPause() {
+ super.onPause()
+
+ lock.withLock {
+ isActivityResumed = false
+ }
+ }
+
+ fun waitForResumed() {
+ lock.withLock {
+ while (!isActivityResumed) {
+ condition.await()
+ }
+ }
+ }
+}
diff --git a/tests/tests/assist/AndroidTest.xml b/tests/tests/assist/AndroidTest.xml
index 13b1847..60cbbe6 100644
--- a/tests/tests/assist/AndroidTest.xml
+++ b/tests/tests/assist/AndroidTest.xml
@@ -16,6 +16,8 @@
<configuration description="Config for CTS Assist 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="instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="CtsAssistService.apk" />
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/Android.bp b/tests/tests/binder_ndk/libbinder_ndk_test/Android.bp
index 761e020..a6502bf 100644
--- a/tests/tests/binder_ndk/libbinder_ndk_test/Android.bp
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/Android.bp
@@ -21,7 +21,10 @@
"test_package/Bar.aidl",
"test_package/Foo.aidl",
],
- versions: ["1"],
+ versions: [
+ "1",
+ "2",
+ ],
backend: {
java: {
sdk_version: "28",
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/api/2/test_package/Bar.aidl b/tests/tests/binder_ndk/libbinder_ndk_test/api/2/test_package/Bar.aidl
new file mode 100644
index 0000000..bf4c4fc
--- /dev/null
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/api/2/test_package/Bar.aidl
@@ -0,0 +1,8 @@
+package test_package;
+parcelable Bar {
+ String a = "BAR";
+ String b = "BAR2";
+ float c = 4.200000f;
+ int d = 100;
+ String e = "HELLO";
+}
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/api/2/test_package/Foo.aidl b/tests/tests/binder_ndk/libbinder_ndk_test/api/2/test_package/Foo.aidl
new file mode 100644
index 0000000..60e8956
--- /dev/null
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/api/2/test_package/Foo.aidl
@@ -0,0 +1,10 @@
+package test_package;
+parcelable Foo {
+ String a = "FOO";
+ int b = 42;
+ float c = 3.140000f;
+ test_package.Bar d;
+ test_package.Bar e;
+ int f = 3;
+ @nullable String[] g;
+}
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/api/2/test_package/IEmpty.aidl b/tests/tests/binder_ndk/libbinder_ndk_test/api/2/test_package/IEmpty.aidl
new file mode 100644
index 0000000..2baa52c
--- /dev/null
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/api/2/test_package/IEmpty.aidl
@@ -0,0 +1,3 @@
+package test_package;
+interface IEmpty {
+}
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/api/2/test_package/ITest.aidl b/tests/tests/binder_ndk/libbinder_ndk_test/api/2/test_package/ITest.aidl
new file mode 100644
index 0000000..b2e92fe
--- /dev/null
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/api/2/test_package/ITest.aidl
@@ -0,0 +1,56 @@
+package test_package;
+interface ITest {
+ String GetName();
+ void TestVoidReturn();
+ oneway void TestOneway();
+ int GiveMeMyCallingPid();
+ int GiveMeMyCallingUid();
+ oneway void CacheCallingInfoFromOneway();
+ int GiveMeMyCallingPidFromOneway();
+ int GiveMeMyCallingUidFromOneway();
+ int RepeatInt(int value);
+ long RepeatLong(long value);
+ float RepeatFloat(float value);
+ double RepeatDouble(double value);
+ boolean RepeatBoolean(boolean value);
+ char RepeatChar(char value);
+ byte RepeatByte(byte value);
+ IBinder RepeatBinder(IBinder value);
+ @nullable IBinder RepeatNullableBinder(@nullable IBinder value);
+ test_package.IEmpty RepeatInterface(test_package.IEmpty value);
+ @nullable test_package.IEmpty RepeatNullableInterface(@nullable test_package.IEmpty value);
+ ParcelFileDescriptor RepeatFd(in ParcelFileDescriptor fd);
+ @nullable ParcelFileDescriptor RepeatNullableFd(in @nullable ParcelFileDescriptor fd);
+ String RepeatString(String value);
+ @nullable String RepeatNullableString(@nullable String value);
+ test_package.RegularPolygon RepeatPolygon(in test_package.RegularPolygon value);
+ void RenamePolygon(inout test_package.RegularPolygon value, String newName);
+ boolean[] RepeatBooleanArray(in boolean[] input, out boolean[] repeated);
+ byte[] RepeatByteArray(in byte[] input, out byte[] repeated);
+ char[] RepeatCharArray(in char[] input, out char[] repeated);
+ int[] RepeatIntArray(in int[] input, out int[] repeated);
+ long[] RepeatLongArray(in long[] input, out long[] repeated);
+ float[] RepeatFloatArray(in float[] input, out float[] repeated);
+ double[] RepeatDoubleArray(in double[] input, out double[] repeated);
+ String[] RepeatStringArray(in String[] input, out String[] repeated);
+ test_package.RegularPolygon[] RepeatRegularPolygonArray(in test_package.RegularPolygon[] input, out test_package.RegularPolygon[] repeated);
+ @nullable boolean[] RepeatNullableBooleanArray(in @nullable boolean[] input);
+ @nullable byte[] RepeatNullableByteArray(in @nullable byte[] input);
+ @nullable char[] RepeatNullableCharArray(in @nullable char[] input);
+ @nullable int[] RepeatNullableIntArray(in @nullable int[] input);
+ @nullable long[] RepeatNullableLongArray(in @nullable long[] input);
+ @nullable float[] RepeatNullableFloatArray(in @nullable float[] input);
+ @nullable double[] RepeatNullableDoubleArray(in @nullable double[] input);
+ @nullable String[] RepeatNullableStringArray(in @nullable String[] input);
+ @nullable String[] DoubleRepeatNullableStringArray(in @nullable String[] input, out @nullable String[] repeated);
+ test_package.Foo repeatFoo(in test_package.Foo inFoo);
+ void renameFoo(inout test_package.Foo foo, String name);
+ void renameBar(inout test_package.Foo foo, String name);
+ int getF(in test_package.Foo foo);
+ int NewMethodThatReturns10();
+ const int kZero = 0;
+ const int kOne = 1;
+ const int kOnes = -1;
+ const String kEmpty = "";
+ const String kFoo = "foo";
+}
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/api/2/test_package/RegularPolygon.aidl b/tests/tests/binder_ndk/libbinder_ndk_test/api/2/test_package/RegularPolygon.aidl
new file mode 100644
index 0000000..14c6c2e
--- /dev/null
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/api/2/test_package/RegularPolygon.aidl
@@ -0,0 +1,6 @@
+package test_package;
+parcelable RegularPolygon {
+ String name = "square";
+ int numSides = 4;
+ float sideLength = 1.000000f;
+}
diff --git a/tests/tests/carrierapi/src/android/carrierapi/cts/CarrierApiTest.java b/tests/tests/carrierapi/src/android/carrierapi/cts/CarrierApiTest.java
index 8d7041b..7cb5331 100644
--- a/tests/tests/carrierapi/src/android/carrierapi/cts/CarrierApiTest.java
+++ b/tests/tests/carrierapi/src/android/carrierapi/cts/CarrierApiTest.java
@@ -21,9 +21,9 @@
import static android.carrierapi.cts.IccUtils.hexStringToBytes;
import static android.telephony.IccOpenLogicalChannelResponse.INVALID_CHANNEL;
import static android.telephony.IccOpenLogicalChannelResponse.STATUS_NO_ERROR;
-import static android.telephony.IccOpenLogicalChannelResponse.STATUS_UNKNOWN_ERROR;
import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertNotEquals;
import android.content.BroadcastReceiver;
import android.content.ContentProviderClient;
@@ -82,17 +82,21 @@
// 11.1.17.1
private static final int MAX_LOGICAL_CHANNEL = 3;
// Class bytes. The logical channel used should be included for bits b2b1. TS 102 221 Table 11.5
+ private static final String CLA_ENVELOPE = "80";
private static final int CLA_GET_RESPONSE = 0x00;
private static final int CLA_MANAGE_CHANNEL = 0x00;
private static final int CLA_READ_BINARY = 0x00;
private static final int CLA_SELECT = 0x00;
private static final int CLA_STATUS = 0x80;
+ private static final String CLA_STATUS_STRING = "80";
// APDU Instruction Bytes. TS 102 221 Section 10.1.2
+ private static final String COMMAND_ENVELOPE = "C2";
private static final int COMMAND_GET_RESPONSE = 0xC0;
private static final int COMMAND_MANAGE_CHANNEL = 0x70;
private static final int COMMAND_READ_BINARY = 0xB0;
private static final int COMMAND_SELECT = 0xA4;
private static final int COMMAND_STATUS = 0xF2;
+ private static final String COMMAND_STATUS_STRING = "F2";
// Status words. TS 102 221 Section 10.2.1
private static final byte[] STATUS_NORMAL = {(byte) 0x90, (byte) 0x00};
private static final String STATUS_NORMAL_STRING = "9000";
@@ -107,6 +111,7 @@
private static final String ICCID_FILE_ID = "2FE2";
// File ID for the master file. TS 102 221
private static final String MF_FILE_ID = "3F00";
+ private static final int MF_FILE_ID_HEX = 0x3F00;
// File ID for the MF Access Rule Reference. TS 102 221
private static final String MF_ARR_FILE_ID = "2F06";
private static final String ALPHA_TAG_A = "tagA";
@@ -458,13 +463,13 @@
verifyValidIccOpenLogicalChannelResponse(response);
mTelephonyManager.iccCloseLogicalChannel(response.getChannel());
- // Only values 0x00, 0x04, 0x08, and 0x0C are allowed for p2. Any p2 values that produce
- // non '9000'/'62xx'/'63xx' status words are treated as an error and the channel is not
- // opened.
- p2 = 0x02;
+ // Valid p2 values are defined in TS 102 221 Table 11.2. Per Table 11.2, 0xF0 should be
+ // invalid. Any p2 values that produce non '9000'/'62xx'/'63xx' status words are treated as
+ // an error and the channel is not opened.
+ p2 = 0xF0;
response = mTelephonyManager.iccOpenLogicalChannel("", p2);
assertEquals(INVALID_CHANNEL, response.getChannel());
- assertEquals(STATUS_UNKNOWN_ERROR, response.getStatus());
+ assertNotEquals(STATUS_NO_ERROR, response.getStatus());
}
/**
@@ -765,6 +770,69 @@
}
}
+ /**
+ * This test verifies that {@link TelephonyManager#iccExchangeSimIO(int, int, int, int, int,
+ * String)} correctly transmits iccIO commands to the UICC card. First, the MF is selected via a
+ * SELECT apdu via the basic channel, then a STATUS AT-command is sent.
+ */
+ public void testIccExchangeSimIO() {
+ if (!hasCellular) return;
+
+ // select the MF first. This makes sure the next STATUS AT-command returns a FCP template
+ // for the right file.
+ int cla = CLA_SELECT;
+ int p1 = 0; // select EF by FID
+ int p2 = 0x0C; // requesting FCP template
+ int p3 = 2; // length of 'data' payload
+ String data = MF_FILE_ID;
+ String response = mTelephonyManager
+ .iccTransmitApduBasicChannel(cla, COMMAND_SELECT, p1, p2, p3, data);
+ assertEquals(STATUS_NORMAL_STRING, response);
+
+ // The iccExchangeSimIO command implements the +CRSM command defined in TS 27.007 section
+ // 8.18. A STATUS command is sent and the returned value will be an FCP template.
+ byte[] result = mTelephonyManager.iccExchangeSimIO(
+ 0, // fileId: not required for STATUS
+ COMMAND_STATUS, // command: STATUS
+ 0, // p1: not required for STATUS
+ 0, // p2: not required for STATUS
+ 0, // p3: not required for STATUS
+ ""); // filePath: not required for STATUS
+ String resultString = bytesToHexString(result);
+ assertTrue("TelephonyManager#iccExchangeSimIO should not give an empty response",
+ !resultString.isEmpty());
+ // TODO(b/131353609): uncomment logic to fully check TelMan#iccExchangeSimIO response
+ // FcpTemplate fcpTemplate = FcpTemplate.parseFcpTemplate(resultString);
+ // assertTrue(containsFileId(fcpTemplate, MF_FILE_ID));
+ // assertEquals("iccExchangeSimIO returned non-normal Status byte: " + resultString,
+ // STATUS_NORMAL_STRING, fcpTemplate.getStatus());
+ }
+
+ /**
+ * This test checks that a STATUS apdu can be sent as an encapsulated envelope to the UICC via
+ * {@link TelephonyManager#sendEnvelopeWithStatus(String)}.
+ */
+ public void testSendEnvelopeWithStatus() {
+ // STATUS apdu as hex String
+ String envelope =
+ CLA_STATUS_STRING
+ + COMMAND_STATUS_STRING
+ + "00" // p1: no indication of application status
+ + "00"; // p2: identical parameters to
+ String lc = "0" + (envelope.length() / 2); // number of bytes in data field
+ String response = mTelephonyManager.sendEnvelopeWithStatus(
+ CLA_ENVELOPE
+ + COMMAND_ENVELOPE
+ + "00" // p1: value required for Envelope command
+ + "00" // p2: value required for Envelope command
+ + lc
+ + envelope);
+ assertNotNull("sendEnvelopeWithStatus returned: " + response, response);
+ // TODO(b/131422420): uncomment logic to fully check TelMan#sendEnvelopeWithStatus response
+ // assertEquals("sendEnvelopeWithStatus returned: " + response,
+ // STATUS_NORMAL_STRING, response);
+ }
+
private void verifyValidIccOpenLogicalChannelResponse(IccOpenLogicalChannelResponse response) {
// The assigned channel should be between the min and max allowed channel numbers
int channel = response.getChannel();
diff --git a/tests/tests/classloaderfactory/Android.bp b/tests/tests/classloaderfactory/Android.bp
index 57f1c27..d920635 100644
--- a/tests/tests/classloaderfactory/Android.bp
+++ b/tests/tests/classloaderfactory/Android.bp
@@ -12,11 +12,24 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-java_library_static {
- name: "CtsClassLoaderFactoryTestCasesSecondaryDex",
- srcs: [
- "src-ex/**/*.java",
- ],
- installable: false,
- compile_dex: true,
+android_test_helper_app {
+ name: "ClassLoaderFactoryTestSecondary",
+ srcs: [ "src-ex/**/*.java" ],
+ manifest: "src-ex/AndroidManifest.xml",
}
+
+filegroup {
+ name: "cts-classloaderfactorytest-srcs",
+ srcs: [ "src/**/*.java" ],
+}
+
+java_defaults {
+ name: "cts_classloaderfactorytest_defaults",
+ static_libs: [
+ "androidx.test.rules",
+ "ctstestrunner-axt",
+ "junit",
+ ],
+ java_resources: [ ":ClassLoaderFactoryTestSecondary" ],
+ sdk_version: "test_current",
+}
\ No newline at end of file
diff --git a/tests/tests/classloaderfactory/Android.mk b/tests/tests/classloaderfactory/Android.mk
deleted file mode 100644
index 347fe67..0000000
--- a/tests/tests/classloaderfactory/Android.mk
+++ /dev/null
@@ -1,29 +0,0 @@
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH := $(call my-dir)
-
-# Copy secondary APK to CTS target directory.
-include $(CLEAR_VARS)
-LOCAL_MODULE := cts-classloaderfactory-secondary-jar
-LOCAL_MODULE_STEM := classloaderfactory-secondary.jar
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH = $(TARGET_OUT_TESTCASES)
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-include $(BUILD_SYSTEM)/base_rules.mk
-my_secondary_apk_src := $(call intermediates-dir-for,JAVA_LIBRARIES,CtsClassLoaderFactoryTestCasesSecondaryDex,,COMMON)/javalib.jar
-$(eval $(call copy-one-file,$(my_secondary_apk_src),$(LOCAL_BUILT_MODULE)))
-my_secondary_apk_src :=
-
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/tests/classloaderfactory/base_test.mk b/tests/tests/classloaderfactory/base_test.mk
deleted file mode 100644
index 4414d91..0000000
--- a/tests/tests/classloaderfactory/base_test.mk
+++ /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.
-
-LOCAL_SDK_VERSION := test_current
-
-# don't include this package in any target
-LOCAL_MODULE_TAGS := optional
-
-# and when built explicitly put it in the data partition
-LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
- ctstestrunner-axt \
- androidx.test.rules \
- junit
-
-LOCAL_SRC_FILES := $(call all-java-files-under, ../src)
-
-LOCAL_ADDITIONAL_DEPENDENCIES += \
- $(TARGET_OUT_TESTCASES)/classloaderfactory-secondary.jar
-
-# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-
-include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/classloaderfactory/src-ex/AndroidManifest.xml b/tests/tests/classloaderfactory/src-ex/AndroidManifest.xml
new file mode 100644
index 0000000..2d497ed
--- /dev/null
+++ b/tests/tests/classloaderfactory/src-ex/AndroidManifest.xml
@@ -0,0 +1,22 @@
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT 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.classloaderfactory.cts"
+ android:versionCode="1"
+ android:versionName="1.0" >
+ <uses-sdk android:minSdkVersion="29" />
+ <application />
+</manifest>
diff --git a/tests/tests/classloaderfactory/src/android/app/classloaderfactory/cts/AppComponentFactoryTest.java b/tests/tests/classloaderfactory/src/android/app/classloaderfactory/cts/AppComponentFactoryTest.java
index f33c7e0..e70497b 100644
--- a/tests/tests/classloaderfactory/src/android/app/classloaderfactory/cts/AppComponentFactoryTest.java
+++ b/tests/tests/classloaderfactory/src/android/app/classloaderfactory/cts/AppComponentFactoryTest.java
@@ -20,20 +20,41 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.ApplicationInfo;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
public class AppComponentFactoryTest {
- public static final String SECONDARY_APK_PATH =
- "/data/local/tmp/classloaderfactory-test/secondary.jar";
+ private static final String SECONDARY_APK_RESOURCE = "ClassLoaderFactoryTestSecondary.apk";
private static final String CLASS_PACKAGE_NAME =
AppComponentFactoryTest.class.getPackage().getName();
+ static File writeSecondaryApkToDisk(ApplicationInfo aInfo) throws Exception {
+ File outFile = File.createTempFile(AppComponentFactoryTest.class.getSimpleName(), ".apk",
+ new File(aInfo.dataDir));
+
+ InputStream is = AppComponentFactoryTest.class.getClassLoader().getResourceAsStream(
+ SECONDARY_APK_RESOURCE);
+ FileOutputStream os = new FileOutputStream(outFile);
+ final byte[] buffer = new byte[4096];
+ int n;
+ while ((n = is.read(buffer)) >= 0) {
+ os.write(buffer, 0, n);
+ }
+ os.close();
+
+ return outFile;
+ }
+
private final Context mContext = InstrumentationRegistry.getContext();
@Test
diff --git a/tests/tests/classloaderfactory/src/android/app/classloaderfactory/cts/InMemoryDexClassLoaderFactory.java b/tests/tests/classloaderfactory/src/android/app/classloaderfactory/cts/InMemoryDexClassLoaderFactory.java
index 6dda553..d255ce2 100644
--- a/tests/tests/classloaderfactory/src/android/app/classloaderfactory/cts/InMemoryDexClassLoaderFactory.java
+++ b/tests/tests/classloaderfactory/src/android/app/classloaderfactory/cts/InMemoryDexClassLoaderFactory.java
@@ -31,7 +31,7 @@
try {
// InMemoryDexClassLoader will not load a zip file. Must extract
// dex files into ByteBuffers.
- ZipFile zipFile = new ZipFile(AppComponentFactoryTest.SECONDARY_APK_PATH);
+ ZipFile zipFile = new ZipFile(AppComponentFactoryTest.writeSecondaryApkToDisk(aInfo));
ArrayList<ByteBuffer> dexFiles = new ArrayList<>();
for (int dexId = 0;; dexId++) {
@@ -68,6 +68,5 @@
} catch (Exception ex) {
throw new RuntimeException(ex);
}
-
}
}
diff --git a/tests/tests/classloaderfactory/src/android/app/classloaderfactory/cts/PathClassLoaderFactory.java b/tests/tests/classloaderfactory/src/android/app/classloaderfactory/cts/PathClassLoaderFactory.java
index 2abdeba2..867588b 100644
--- a/tests/tests/classloaderfactory/src/android/app/classloaderfactory/cts/PathClassLoaderFactory.java
+++ b/tests/tests/classloaderfactory/src/android/app/classloaderfactory/cts/PathClassLoaderFactory.java
@@ -17,12 +17,18 @@
import android.app.AppComponentFactory;
import android.content.pm.ApplicationInfo;
import dalvik.system.PathClassLoader;
+import java.io.File;
// AppComponentFactory which sets up PathClassLoader as the main class loader
// of the process.
public class PathClassLoaderFactory extends AppComponentFactory {
@Override
public ClassLoader instantiateClassLoader(ClassLoader defaultCL, ApplicationInfo aInfo) {
- return new PathClassLoader(AppComponentFactoryTest.SECONDARY_APK_PATH, defaultCL);
+ try {
+ File apkFile = AppComponentFactoryTest.writeSecondaryApkToDisk(aInfo);
+ return new PathClassLoader(apkFile.getAbsolutePath(), defaultCL);
+ } catch (Exception ex) {
+ throw new RuntimeException(ex);
+ }
}
}
diff --git a/tests/tests/classloaderfactory/test-memcl/Android.bp b/tests/tests/classloaderfactory/test-memcl/Android.bp
new file mode 100644
index 0000000..8bdf322
--- /dev/null
+++ b/tests/tests/classloaderfactory/test-memcl/Android.bp
@@ -0,0 +1,25 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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: "CtsClassLoaderFactoryInMemoryDexClassLoaderTestCases",
+ defaults: [ "cts_classloaderfactorytest_defaults" ],
+ srcs: [ ":cts-classloaderfactorytest-srcs" ],
+ test_suites: [
+ "cts",
+ "vts",
+ "general-tests",
+ "cts_instant",
+ ],
+}
diff --git a/tests/tests/classloaderfactory/test-memcl/Android.mk b/tests/tests/classloaderfactory/test-memcl/Android.mk
deleted file mode 100644
index 94277b8..0000000
--- a/tests/tests/classloaderfactory/test-memcl/Android.mk
+++ /dev/null
@@ -1,18 +0,0 @@
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-LOCAL_PACKAGE_NAME := CtsClassLoaderFactoryInMemoryDexClassLoaderTestCases
-include $(LOCAL_PATH)/../base_test.mk
diff --git a/tests/tests/classloaderfactory/test-memcl/AndroidTest.xml b/tests/tests/classloaderfactory/test-memcl/AndroidTest.xml
index f37296e..4400ab5 100644
--- a/tests/tests/classloaderfactory/test-memcl/AndroidTest.xml
+++ b/tests/tests/classloaderfactory/test-memcl/AndroidTest.xml
@@ -18,13 +18,6 @@
<option name="config-descriptor:metadata" key="component" value="misc" />
<option name="config-descriptor:metadata" key="parameter" value="instant_app" />
<option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
- <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
- <option name="run-command" value="mkdir -p /data/local/tmp/classloaderfactory-test" />
- <option name="teardown-command" value="rm -rf /data/local/tmp/classloaderfactory-test" />
- </target_preparer>
- <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
- <option name="push" value="classloaderfactory-secondary.jar->/data/local/tmp/classloaderfactory-test/secondary.jar" />
- </target_preparer>
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="CtsClassLoaderFactoryInMemoryDexClassLoaderTestCases.apk" />
diff --git a/tests/tests/classloaderfactory/test-pathcl/Android.bp b/tests/tests/classloaderfactory/test-pathcl/Android.bp
new file mode 100644
index 0000000..924e298
--- /dev/null
+++ b/tests/tests/classloaderfactory/test-pathcl/Android.bp
@@ -0,0 +1,25 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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: "CtsClassLoaderFactoryPathClassLoaderTestCases",
+ defaults: [ "cts_classloaderfactorytest_defaults" ],
+ srcs: [ ":cts-classloaderfactorytest-srcs" ],
+ test_suites: [
+ "cts",
+ "vts",
+ "general-tests",
+ "cts_instant",
+ ],
+}
diff --git a/tests/tests/classloaderfactory/test-pathcl/Android.mk b/tests/tests/classloaderfactory/test-pathcl/Android.mk
deleted file mode 100644
index 28e0774..0000000
--- a/tests/tests/classloaderfactory/test-pathcl/Android.mk
+++ /dev/null
@@ -1,18 +0,0 @@
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-LOCAL_PACKAGE_NAME := CtsClassLoaderFactoryPathClassLoaderTestCases
-include $(LOCAL_PATH)/../base_test.mk
diff --git a/tests/tests/classloaderfactory/test-pathcl/AndroidTest.xml b/tests/tests/classloaderfactory/test-pathcl/AndroidTest.xml
index 4db5f2d..50c79ef 100644
--- a/tests/tests/classloaderfactory/test-pathcl/AndroidTest.xml
+++ b/tests/tests/classloaderfactory/test-pathcl/AndroidTest.xml
@@ -18,13 +18,6 @@
<option name="config-descriptor:metadata" key="component" value="misc" />
<option name="config-descriptor:metadata" key="parameter" value="instant_app" />
<option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
- <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
- <option name="run-command" value="mkdir -p /data/local/tmp/classloaderfactory-test" />
- <option name="teardown-command" value="rm -rf /data/local/tmp/classloaderfactory-test" />
- </target_preparer>
- <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
- <option name="push" value="classloaderfactory-secondary.jar->/data/local/tmp/classloaderfactory-test/secondary.jar" />
- </target_preparer>
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="CtsClassLoaderFactoryPathClassLoaderTestCases.apk" />
diff --git a/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java b/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java
index cc5d1b4..a9a705a 100644
--- a/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java
+++ b/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java
@@ -439,4 +439,9 @@
Intent intent = new Intent(Settings.ACTION_REQUEST_ENABLE_CONTENT_CAPTURE);
assertCanBeHandled(intent);
}
+
+ public void testVoiceInputSettingsIntent() {
+ Intent intent = new Intent(Settings.ACTION_VOICE_INPUT_SETTINGS);
+ assertCanBeHandled(intent);
+ }
}
diff --git a/tests/tests/content/src/android/content/pm/cts/PackageManagerTest.java b/tests/tests/content/src/android/content/pm/cts/PackageManagerTest.java
index 54e1c4b..3658d04 100644
--- a/tests/tests/content/src/android/content/pm/cts/PackageManagerTest.java
+++ b/tests/tests/content/src/android/content/pm/cts/PackageManagerTest.java
@@ -27,6 +27,7 @@
import static android.content.pm.PackageManager.GET_SERVICES;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
import android.content.ComponentName;
import android.content.Intent;
@@ -44,14 +45,18 @@
import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
+import android.content.pm.Signature;
+import android.os.SystemProperties;
import android.platform.test.annotations.AppModeFull;
import android.test.AndroidTestCase;
import android.text.TextUtils;
+import android.util.Log;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
+import java.util.stream.Collectors;
/**
* This test is based on the declarations in AndroidManifest.xml. We create mock declarations
@@ -60,6 +65,8 @@
*/
@AppModeFull // TODO(Instant) Figure out which APIs should work.
public class PackageManagerTest extends AndroidTestCase {
+ private static final String TAG = "PackageManagerTest";
+
private PackageManager mPackageManager;
private static final String PACKAGE_NAME = "android.content.cts";
private static final String CONTENT_PKG_NAME = "android.content.cts";
@@ -83,6 +90,8 @@
// There are 11 activities/activity-alias in AndroidManifest
private static final int NUM_OF_ACTIVITIES_IN_MANIFEST = 11;
+ private static final String SHIM_APEX_PACKAGE_NAME = "com.android.apex.cts.shim";
+
@Override
protected void setUp() throws Exception {
super.setUp();
@@ -791,4 +800,123 @@
assertEquals(PACKAGE_NAME, savedInfo.packageName);
assertEquals(PermissionInfo.PROTECTION_NORMAL, savedInfo.protectionLevel);
}
+
+ public void testGetPackageInfo_ApexSupported_ApexPackage_MatchesApex() throws Exception {
+ // This really should be a assumeTrue(isUpdatingApexSupported()), but JUnit3 doesn't support
+ // assumptions framework.
+ // TODO: change to assumeTrue after migrating tests to JUnit4.
+ if (!isUpdatingApexSupported()) {
+ Log.i(TAG, "Device doesn't support updating APEX");
+ return;
+ }
+ PackageInfo packageInfo = mPackageManager.getPackageInfo(SHIM_APEX_PACKAGE_NAME,
+ PackageManager.MATCH_APEX);
+ assertShimApexInfoIsCorrect(packageInfo);
+ }
+
+ public void testGetPackageInfo_ApexSupported_ApexPackage_DoesNotMatchApex() {
+ // This really should be a assumeTrue(isUpdatingApexSupported()), but JUnit3 doesn't support
+ // assumptions framework.
+ // TODO: change to assumeTrue after migrating tests to JUnit4.
+ if (!isUpdatingApexSupported()) {
+ Log.i(TAG, "Device doesn't support updating APEX");
+ return;
+ }
+ try {
+ mPackageManager.getPackageInfo(SHIM_APEX_PACKAGE_NAME, 0 /* flags */);
+ fail("NameNotFoundException expected");
+ } catch (NameNotFoundException expected) {
+ }
+ }
+
+ public void testGetPackageInfo_ApexNotSupported_ApexPackage_MatchesApex() {
+ if (isUpdatingApexSupported()) {
+ Log.i(TAG, "Device supports updating APEX");
+ return;
+ }
+ try {
+ mPackageManager.getPackageInfo(SHIM_APEX_PACKAGE_NAME, PackageManager.MATCH_APEX);
+ fail("NameNotFoundException expected");
+ } catch (NameNotFoundException expected) {
+ }
+ }
+
+ public void testGetPackageInfo_ApexNotSupported_ApexPackage_DoesNotMatchApex() {
+ if (isUpdatingApexSupported()) {
+ Log.i(TAG, "Device supports updating APEX");
+ return;
+ }
+ try {
+ mPackageManager.getPackageInfo(SHIM_APEX_PACKAGE_NAME, 0);
+ fail("NameNotFoundException expected");
+ } catch (NameNotFoundException expected) {
+ }
+ }
+
+ public void testGetInstalledPackages_ApexSupported_MatchesApex() {
+ if (!isUpdatingApexSupported()) {
+ Log.i(TAG, "Device doesn't support updating APEX");
+ return;
+ }
+ List<PackageInfo> installedPackages = mPackageManager.getInstalledPackages(
+ PackageManager.MATCH_APEX);
+ List<PackageInfo> shimApex = installedPackages.stream().filter(
+ packageInfo -> packageInfo.packageName.equals(SHIM_APEX_PACKAGE_NAME)).collect(
+ Collectors.toList());
+ assertWithMessage("More than one shim apex found").that(shimApex).hasSize(1);
+ assertShimApexInfoIsCorrect(shimApex.get(0));
+ }
+
+ public void testGetInstalledPackages_ApexSupported_DoesNotMatchApex() {
+ if (!isUpdatingApexSupported()) {
+ Log.i(TAG, "Device doesn't support updating APEX");
+ return;
+ }
+ List<PackageInfo> installedPackages = mPackageManager.getInstalledPackages(0);
+ List<PackageInfo> shimApex = installedPackages.stream().filter(
+ packageInfo -> packageInfo.packageName.equals(SHIM_APEX_PACKAGE_NAME)).collect(
+ Collectors.toList());
+ assertWithMessage("Shim apex wasn't supposed to be found").that(shimApex).isEmpty();
+ }
+
+ public void testGetInstalledPackages_ApexNotSupported_MatchesApex() {
+ if (isUpdatingApexSupported()) {
+ Log.i(TAG, "Device supports updating APEX");
+ return;
+ }
+ List<PackageInfo> installedPackages = mPackageManager.getInstalledPackages(
+ PackageManager.MATCH_APEX);
+ List<PackageInfo> shimApex = installedPackages.stream().filter(
+ packageInfo -> packageInfo.packageName.equals(SHIM_APEX_PACKAGE_NAME)).collect(
+ Collectors.toList());
+ assertWithMessage("Shim apex wasn't supposed to be found").that(shimApex).isEmpty();
+ }
+
+ public void testGetInstalledPackages_ApexNotSupported_DoesNotMatchApex() {
+ if (isUpdatingApexSupported()) {
+ Log.i(TAG, "Device supports updating APEX");
+ return;
+ }
+ List<PackageInfo> installedPackages = mPackageManager.getInstalledPackages(0);
+ List<PackageInfo> shimApex = installedPackages.stream().filter(
+ packageInfo -> packageInfo.packageName.equals(SHIM_APEX_PACKAGE_NAME)).collect(
+ Collectors.toList());
+ assertWithMessage("Shim apex wasn't supposed to be found").that(shimApex).isEmpty();
+ }
+
+ private boolean isUpdatingApexSupported() {
+ return SystemProperties.getBoolean("ro.apex.updatable", false);
+ }
+
+ private static void assertShimApexInfoIsCorrect(PackageInfo packageInfo) {
+ assertThat(packageInfo.packageName).isEqualTo(SHIM_APEX_PACKAGE_NAME);
+ assertThat(packageInfo.getLongVersionCode()).isEqualTo(1);
+ assertThat(packageInfo.isApex).isTrue();
+ assertThat(packageInfo.applicationInfo.sourceDir).isEqualTo(
+ "/system/apex/com.android.apex.cts.shim.apex");
+ // Verify that legacy mechanism for handling signatures is supported.
+ Signature[] pastSigningCertificates =
+ packageInfo.signingInfo.getSigningCertificateHistory();
+ assertThat(packageInfo.signatures).asList().containsExactly(pastSigningCertificates);
+ }
}
diff --git a/tests/tests/database/AndroidTest.xml b/tests/tests/database/AndroidTest.xml
index 9c53d2d..d48422a 100644
--- a/tests/tests/database/AndroidTest.xml
+++ b/tests/tests/database/AndroidTest.xml
@@ -17,6 +17,7 @@
<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" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="CtsDatabaseTestCases.apk" />
diff --git a/tests/tests/deviceconfig/src/android/deviceconfig/cts/DeviceConfigApiPermissionTests.java b/tests/tests/deviceconfig/src/android/deviceconfig/cts/DeviceConfigApiPermissionTests.java
index ab8088a..77806f0 100644
--- a/tests/tests/deviceconfig/src/android/deviceconfig/cts/DeviceConfigApiPermissionTests.java
+++ b/tests/tests/deviceconfig/src/android/deviceconfig/cts/DeviceConfigApiPermissionTests.java
@@ -16,7 +16,7 @@
package android.deviceconfig.cts;
-import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import android.provider.DeviceConfig;
@@ -194,7 +194,8 @@
try {
String property = DeviceConfig.getProperty(NAMESPACE, KEY);
- assertThat(property).isEqualTo(VALUE);
+ assertEquals("Value read from DeviceConfig API does not match written value.",
+ property, VALUE);
} catch (SecurityException e) {
violations.append("DeviceConfig.getProperty() must be accessible with"
+ " READ_DEVICE_CONFIG permission\n");
@@ -226,14 +227,25 @@
try {
DeviceConfig.setProperty(PUBLIC_NAMESPACE, KEY, VALUE, /*makeDefault=*/ false);
- violations.append("DeviceConfig.setProperty() must not be accessible with"
- + " WRITE_DEVICE_CONFIG permission\n");
+ violations.append("DeviceConfig.setProperty() for public namespaces must not be "
+ + "accessible without WRITE_DEVICE_CONFIG permission\n");
} catch (SecurityException e) {
}
+ InstrumentationRegistry.getInstrumentation().getUiAutomation()
+ .adoptShellPermissionIdentity(WRITE_DEVICE_CONFIG_PERMISSION);
+
+ try {
+ DeviceConfig.setProperty(PUBLIC_NAMESPACE, KEY, VALUE, /*makeDefault=*/ false);
+ } catch (SecurityException e) {
+ violations.append("DeviceConfig.setProperty() must be accessible with"
+ + " WRITE_DEVICE_CONFIG permission\n");
+ }
+
try {
String property = DeviceConfig.getProperty(PUBLIC_NAMESPACE, KEY);
- assertThat(property).isEqualTo(VALUE);
+ assertEquals("Value read from DeviceConfig API public namespace does not match written"
+ + " value.", property, VALUE);
} catch (SecurityException e) {
violations.append("DeviceConfig.getProperty() for public namespaces must be accessible"
+ "without READ_DEVICE_CONFIG permission\n");
diff --git a/tests/tests/dreams/AndroidTest.xml b/tests/tests/dreams/AndroidTest.xml
index d04dfdd..6281169 100644
--- a/tests/tests/dreams/AndroidTest.xml
+++ b/tests/tests/dreams/AndroidTest.xml
@@ -17,6 +17,7 @@
<option name="test-suite-tag" value="cts" />
<option name="config-descriptor:metadata" key="component" value="sysui" />
<option name="config-descriptor:metadata" key="parameter" value="instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
<option name="not-shardable" value="true" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
diff --git a/tests/tests/externalservice/common/src/android/externalservice/common/RunningServiceInfo.java b/tests/tests/externalservice/common/src/android/externalservice/common/RunningServiceInfo.java
new file mode 100644
index 0000000..4ad6a05
--- /dev/null
+++ b/tests/tests/externalservice/common/src/android/externalservice/common/RunningServiceInfo.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.externalservice.common;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * A class that encapsulates information about a running service process
+ * and information about how it was created.
+ */
+public class RunningServiceInfo implements Parcelable {
+ /** The UNIX user ID of the running service process. */
+ public int uid;
+
+ /** The UNIX process ID of the running service process. */
+ public int pid;
+
+ /** The package name that the process is running under. */
+ public String packageName;
+
+ /** The value reported from the test's ZygotePreload.getZygotePid(). */
+ public int zygotePid;
+
+ /** The value reported from the test's ZygotePreload.getZygotePackage(). */
+ public String zygotePackage;
+
+ public void writeToParcel(Parcel dest, int parcelableFlags) {
+ dest.writeInt(uid);
+ dest.writeInt(pid);
+ dest.writeString(packageName);
+ dest.writeInt(zygotePid);
+ dest.writeString(zygotePackage);
+ }
+
+ public static final Parcelable.Creator<RunningServiceInfo> CREATOR
+ = new Parcelable.Creator<RunningServiceInfo>() {
+ public RunningServiceInfo createFromParcel(Parcel source) {
+ RunningServiceInfo info = new RunningServiceInfo();
+ info.uid = source.readInt();
+ info.pid = source.readInt();
+ info.packageName = source.readString();
+ info.zygotePid = source.readInt();
+ info.zygotePackage = source.readString();
+ return info;
+ }
+ public RunningServiceInfo[] newArray(int size) {
+ return new RunningServiceInfo[size];
+ }
+ };
+
+ public int describeContents() {
+ return 0;
+ }
+}
diff --git a/tests/tests/externalservice/common/src/android/externalservice/common/ServiceMessages.java b/tests/tests/externalservice/common/src/android/externalservice/common/ServiceMessages.java
index 8d23bc0..09faa6b 100644
--- a/tests/tests/externalservice/common/src/android/externalservice/common/ServiceMessages.java
+++ b/tests/tests/externalservice/common/src/android/externalservice/common/ServiceMessages.java
@@ -21,13 +21,14 @@
public class ServiceMessages {
// No arguments. Gets the UID and PID of the service.
public static final int MSG_IDENTIFY = IBinder.FIRST_CALL_TRANSACTION + 1;
- // Response to MSG_IDENTIFY. arg1 is the UID, arg2 is the PID.
+ // Response to MSG_IDENTIFY. Bundle data contain a single key, noted below.
public static final int MSG_IDENTIFY_RESPONSE = MSG_IDENTIFY + 100;
- // Bundle key in MSG_IDENTIFY_RESPONSE containing the package name.
- public static final String IDENTIFY_PACKAGE = "packageName";
+ // Bundle key in MSG_IDENTIFY_RESPONSE containing the RunningServiceInfo object.
+ public static final String IDENTIFY_INFO = "serviceInfo";
- // No arguments. Starts an external service.
+ // Starts an external service. arg1 should be 1 if the process should be started via
+ // an app zygote, and 0 if the service should be started normally.
public static final int MSG_CREATE_EXTERNAL_SERVICE = IBinder.FIRST_CALL_TRANSACTION + 2;
// Responds to MSG_CREATE_EXTERNAL_SERVICE. obj is the IBinder on success, null on failure.
public static final int MSG_CREATE_EXTERNAL_SERVICE_RESPONSE =
diff --git a/tests/tests/externalservice/service/AndroidManifest.xml b/tests/tests/externalservice/service/AndroidManifest.xml
index 74d0825..b47c1b4 100644
--- a/tests/tests/externalservice/service/AndroidManifest.xml
+++ b/tests/tests/externalservice/service/AndroidManifest.xml
@@ -18,7 +18,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android.externalservice.service">
- <application android:label="External Service Host">
+ <application android:label="External Service Host"
+ android:zygotePreloadName="android.externalservice.service.ZygotePreload">
<uses-library android:name="android.test.runner" />
<!-- Service used to start .ExternalService from this package. -->
@@ -45,11 +46,16 @@
android:externalService="true"
android:exported="true"/>
- <!-- The only valid, externalService entry. -->
+ <!-- The valid externalService entries. -->
<service android:name=".ExternalService"
android:isolatedProcess="true"
android:externalService="true"
android:exported="true"/>
+ <service android:name=".ExternalServiceWithZygote"
+ android:isolatedProcess="true"
+ android:externalService="true"
+ android:exported="true"
+ android:useAppZygote="true"/>
</application>
</manifest>
diff --git a/tests/tests/externalservice/service/src/android/externalservice/service/BaseService.java b/tests/tests/externalservice/service/src/android/externalservice/service/BaseService.java
index a14adbe..6690144 100644
--- a/tests/tests/externalservice/service/src/android/externalservice/service/BaseService.java
+++ b/tests/tests/externalservice/service/src/android/externalservice/service/BaseService.java
@@ -27,6 +27,7 @@
import android.os.RemoteException;
import android.util.Log;
+import android.externalservice.common.RunningServiceInfo;
import android.externalservice.common.ServiceMessages;
public class BaseService extends Service {
@@ -53,10 +54,15 @@
Log.d(TAG, "Received message: " + msg);
switch (msg.what) {
case ServiceMessages.MSG_IDENTIFY:
- Message reply = Message.obtain(null, ServiceMessages.MSG_IDENTIFY_RESPONSE,
- Process.myUid(), Process.myPid());
- reply.getData().putString(ServiceMessages.IDENTIFY_PACKAGE,
- mContext.getPackageName());
+ Message reply = Message.obtain(null, ServiceMessages.MSG_IDENTIFY_RESPONSE);
+ RunningServiceInfo serviceInfo = new RunningServiceInfo();
+ serviceInfo.uid = Process.myUid();
+ serviceInfo.pid = Process.myPid();
+ serviceInfo.packageName = mContext.getPackageName();
+ serviceInfo.zygotePid = ZygotePreload.getZygotePid();
+ serviceInfo.zygotePackage = ZygotePreload.getZygotePackage();
+ reply.getData().putParcelable(ServiceMessages.IDENTIFY_INFO,
+ serviceInfo);
try {
msg.replyTo.send(reply);
} catch (RemoteException e) {
diff --git a/tests/framework/base/windowmanager/dummyTests/dummySdk28/src/android/server/wm/DummySdk28Test.java b/tests/tests/externalservice/service/src/android/externalservice/service/ExternalServiceWithZygote.java
similarity index 67%
rename from tests/framework/base/windowmanager/dummyTests/dummySdk28/src/android/server/wm/DummySdk28Test.java
rename to tests/tests/externalservice/service/src/android/externalservice/service/ExternalServiceWithZygote.java
index 991d082..d70957a 100644
--- a/tests/framework/base/windowmanager/dummyTests/dummySdk28/src/android/server/wm/DummySdk28Test.java
+++ b/tests/tests/externalservice/service/src/android/externalservice/service/ExternalServiceWithZygote.java
@@ -14,17 +14,7 @@
* limitations under the License.
*/
-package android.server.wm;
+package android.externalservice.service;
-import org.junit.Ignore;
-import org.junit.Test;
-
-public class DummySdk28Test {
-
- @Test
- @Ignore
- public void dummyTest() {
- // TODO(b/129909356): Rename CtsActivityManagerDeviceSdk28TestCases to
- // CtsWindowManagerSdk28TestCases.
- }
+public class ExternalServiceWithZygote extends BaseService {
}
diff --git a/tests/tests/externalservice/service/src/android/externalservice/service/ServiceCreator.java b/tests/tests/externalservice/service/src/android/externalservice/service/ServiceCreator.java
index 3af5c98..861f4df 100644
--- a/tests/tests/externalservice/service/src/android/externalservice/service/ServiceCreator.java
+++ b/tests/tests/externalservice/service/src/android/externalservice/service/ServiceCreator.java
@@ -47,7 +47,15 @@
final Context context = ServiceCreator.this;
final String pkgName = context.getPackageName();
Intent intent = new Intent();
- intent.setComponent(new ComponentName(pkgName, pkgName+".ExternalService"));
+
+ String serviceName;
+ boolean useZygote = msg.arg1 == 1;
+ if (useZygote) {
+ serviceName = ".ExternalServiceWithZygote";
+ } else {
+ serviceName = ".ExternalService";
+ }
+ intent.setComponent(new ComponentName(pkgName, pkgName+serviceName));
CreatorConnection conn = new CreatorConnection(msg.replyTo);
boolean result = context.bindService(intent, conn,
Context.BIND_AUTO_CREATE | Context.BIND_EXTERNAL_SERVICE);
diff --git a/tests/tests/externalservice/service/src/android/externalservice/service/ZygotePreload.java b/tests/tests/externalservice/service/src/android/externalservice/service/ZygotePreload.java
new file mode 100644
index 0000000..9a7c7b3
--- /dev/null
+++ b/tests/tests/externalservice/service/src/android/externalservice/service/ZygotePreload.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.externalservice.service;
+
+import android.content.pm.ApplicationInfo;
+import android.os.Process;
+import android.util.Log;
+
+public class ZygotePreload implements android.app.ZygotePreload {
+ private static final String TAG = "ZygotePreload";
+
+ private static int sZygotePid = -1;
+
+ private static String sZygotePackage = null;
+
+ public static int getZygotePid() {
+ return sZygotePid;
+ }
+
+ public static String getZygotePackage() {
+ return sZygotePackage;
+ }
+
+ public void doPreload(ApplicationInfo info) {
+ sZygotePid = Process.myPid();
+ sZygotePackage = info.packageName;
+ Log.d(TAG, "Doing ZygotePreload, sZygotePid=" + sZygotePid);
+ }
+}
diff --git a/tests/tests/externalservice/src/android/externalservice/cts/ExternalServiceTest.java b/tests/tests/externalservice/src/android/externalservice/cts/ExternalServiceTest.java
index 37c8613..fa7a032 100644
--- a/tests/tests/externalservice/src/android/externalservice/cts/ExternalServiceTest.java
+++ b/tests/tests/externalservice/src/android/externalservice/cts/ExternalServiceTest.java
@@ -32,6 +32,7 @@
import android.test.AndroidTestCase;
import android.util.Log;
+import android.externalservice.common.RunningServiceInfo;
import android.externalservice.common.ServiceMessages;
public class ExternalServiceTest extends AndroidTestCase {
@@ -139,13 +140,14 @@
// Check the identity of the service.
Messenger remote = new Messenger(mConnection.service);
- ServiceIdentity id = identifyService(remote);
+ RunningServiceInfo id = identifyService(remote);
assertNotNull(id);
assertFalse(id.uid == 0 || id.pid == 0);
assertNotEquals(Process.myUid(), id.uid);
assertNotEquals(Process.myPid(), id.pid);
assertEquals(getContext().getPackageName(), id.packageName);
+ assertEquals(-1, id.zygotePid);
}
/** Tests that the APK providing the externalService can bind the service itself, and that
@@ -162,7 +164,7 @@
// Get the identity of the creator.
Messenger remoteCreator = new Messenger(creatorConnection.service);
- ServiceIdentity creatorId = identifyService(remoteCreator);
+ RunningServiceInfo creatorId = identifyService(remoteCreator);
assertNotNull(creatorId);
assertFalse(creatorId.uid == 0 || creatorId.pid == 0);
@@ -196,7 +198,7 @@
// Get the connection to the creator's service.
assertNotNull(creatorMsg.obj);
Messenger remoteCreatorService = (Messenger) creatorMsg.obj;
- ServiceIdentity creatorServiceId = identifyService(remoteCreatorService);
+ RunningServiceInfo creatorServiceId = identifyService(remoteCreatorService);
assertNotNull(creatorServiceId);
assertFalse(creatorServiceId.uid == 0 || creatorId.pid == 0);
@@ -208,7 +210,7 @@
assertTrue(getContext().bindService(intent, mConnection,
Context.BIND_AUTO_CREATE | Context.BIND_EXTERNAL_SERVICE));
assertTrue(mCondition.block(CONDITION_TIMEOUT));
- ServiceIdentity serviceId = identifyService(new Messenger(mConnection.service));
+ RunningServiceInfo serviceId = identifyService(new Messenger(mConnection.service));
assertNotNull(serviceId);
assertFalse(serviceId.uid == 0 || serviceId.pid == 0);
@@ -232,6 +234,11 @@
assertNotEquals(creatorServiceId.uid, serviceId.uid);
assertNotEquals(creatorServiceId.pid, serviceId.pid);
+ assertEquals(-1, creatorServiceId.zygotePid);
+ assertEquals(-1, serviceId.zygotePid);
+ assertNull(creatorServiceId.zygotePackage);
+ assertNull(serviceId.zygotePackage);
+
assertNotEquals(myPkg, creatorId.packageName);
assertNotEquals(myPkg, creatorServiceId.packageName);
assertEquals(creatorId.packageName, creatorServiceId.packageName);
@@ -253,7 +260,7 @@
assertTrue(mCondition.block(CONDITION_TIMEOUT));
- ServiceIdentity idOne = identifyService(new Messenger(initialConn.service));
+ RunningServiceInfo idOne = identifyService(new Messenger(initialConn.service));
assertNotNull(idOne);
assertFalse(idOne.uid == 0 || idOne.pid == 0);
@@ -266,7 +273,7 @@
assertTrue(mCondition.block(CONDITION_TIMEOUT));
- ServiceIdentity idTwo = identifyService(new Messenger(prioConn.service));
+ RunningServiceInfo idTwo = identifyService(new Messenger(prioConn.service));
assertNotNull(idTwo);
assertFalse(idTwo.uid == 0 || idTwo.pid == 0);
@@ -281,32 +288,148 @@
getContext().unbindService(initialConn);
}
- /** Contains information about the security principal of a Service. */
- private static class ServiceIdentity {
- int uid;
- int pid;
- String packageName;
+ /** Tests binding an externalService that is started by an app zygote. */
+ public void testBindExternalServiceWithZygote() {
+ // Start the service and wait for connection.
+ Intent intent = new Intent();
+ intent.setComponent(
+ new ComponentName(sServicePackage, sServicePackage+".ExternalServiceWithZygote"));
+
+ mCondition.close();
+ assertTrue(getContext().bindService(intent, mConnection,
+ Context.BIND_AUTO_CREATE | Context.BIND_EXTERNAL_SERVICE));
+
+ assertTrue(mCondition.block(CONDITION_TIMEOUT));
+ assertEquals(getContext().getPackageName(), mConnection.name.getPackageName());
+ assertNotSame(sServicePackage, mConnection.name.getPackageName());
+
+ // Check the identity of the service.
+ Messenger remote = new Messenger(mConnection.service);
+ RunningServiceInfo id = identifyService(remote);
+ assertNotNull(id);
+
+ assertFalse(id.uid == 0 || id.pid == 0);
+ assertNotEquals(Process.myUid(), id.uid);
+ assertNotEquals(Process.myPid(), id.pid);
+ assertEquals(getContext().getPackageName(), id.packageName);
+ assertNotEquals(id.pid, id.zygotePid);
+ assertNotEquals(Process.myPid(), id.zygotePid);
+ assertNotEquals(-1, id.zygotePid);
+ assertEquals(sServicePackage, id.zygotePackage);
}
- /** Given a Messenger, this will message the service to retrieve its UID, PID, and package name.
- * On success, returns a ServiceIdentity. On failure, returns null. */
- private ServiceIdentity identifyService(Messenger service) {
- final ServiceIdentity id = new ServiceIdentity();
- Handler handler = new Handler(Looper.getMainLooper()) {
+ /** Tests that an externalService that also is also useAppZygote=true shares the same
+ * zygote, even when bound by different packages.. */
+ public void testBindExternalServiceWithZygoteWithRunningOwn() {
+ // Start the service that will create the externalService.
+ final Connection creatorConnection = new Connection();
+ Intent intent = new Intent();
+ intent.setComponent(new ComponentName(sServicePackage, sServicePackage+".ServiceCreator"));
+
+ mCondition.close();
+ assertTrue(getContext().bindService(intent, creatorConnection, Context.BIND_AUTO_CREATE));
+ assertTrue(mCondition.block(CONDITION_TIMEOUT));
+
+ // Have the creator actually start its service.
+ Messenger remoteCreator = new Messenger(creatorConnection.service);
+ final Message creatorMsg = Message.obtain(null, ServiceMessages.MSG_CREATE_EXTERNAL_SERVICE,
+ /* use zygote */ 1, 0);
+ Handler creatorHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
Log.d(TAG, "Received message: " + msg);
switch (msg.what) {
- case ServiceMessages.MSG_IDENTIFY_RESPONSE:
- id.uid = msg.arg1;
- id.pid = msg.arg2;
- id.packageName = msg.getData().getString(ServiceMessages.IDENTIFY_PACKAGE);
+ case ServiceMessages.MSG_CREATE_EXTERNAL_SERVICE_RESPONSE:
+ creatorMsg.copyFrom(msg);
mCondition.open();
break;
}
super.handleMessage(msg);
}
};
+ Messenger localCreator = new Messenger(creatorHandler);
+ creatorMsg.replyTo = localCreator;
+ try {
+ mCondition.close();
+ remoteCreator.send(creatorMsg);
+ } catch (RemoteException e) {
+ fail("Unexpected remote exception" + e);
+ return;
+ }
+ assertTrue(mCondition.block(CONDITION_TIMEOUT));
+
+ // Get the connection to the creator's service.
+ assertNotNull(creatorMsg.obj);
+ Messenger remoteCreatorService = (Messenger) creatorMsg.obj;
+ RunningServiceInfo creatorServiceId = identifyService(remoteCreatorService);
+ assertNotNull(creatorServiceId);
+ assertFalse(creatorServiceId.uid == 0);
+
+ // Create an external service from this (the test) process.
+ intent = new Intent();
+ intent.setComponent(
+ new ComponentName(sServicePackage, sServicePackage+".ExternalServiceWithZygote"));
+
+ mCondition.close();
+ assertTrue(getContext().bindService(intent, mConnection,
+ Context.BIND_AUTO_CREATE | Context.BIND_EXTERNAL_SERVICE));
+ assertTrue(mCondition.block(CONDITION_TIMEOUT));
+ RunningServiceInfo serviceId = identifyService(new Messenger(mConnection.service));
+ assertNotNull(serviceId);
+ assertFalse(serviceId.uid == 0 || serviceId.pid == 0);
+
+ // Make sure that both services were started by the same zygote.
+ assertNotEquals(-1, creatorServiceId.zygotePid);
+ assertNotEquals(-1, serviceId.zygotePid);
+ assertNotEquals(creatorServiceId.pid, creatorServiceId.zygotePid);
+ assertNotEquals(serviceId.pid, serviceId.zygotePid);
+ assertEquals(sServicePackage, creatorServiceId.zygotePackage);
+ assertEquals(sServicePackage, serviceId.zygotePackage);
+ assertEquals(creatorServiceId.zygotePid, serviceId.zygotePid);
+
+ int myUid = Process.myUid();
+ int myPid = Process.myPid();
+ String myPkg = getContext().getPackageName();
+
+ assertNotEquals(myUid, creatorServiceId.uid);
+ assertNotEquals(myUid, serviceId.uid);
+ assertNotEquals(myPid, creatorServiceId.pid);
+ assertNotEquals(myPid, serviceId.pid);
+
+ assertNotEquals(creatorServiceId.uid, serviceId.uid);
+ assertNotEquals(creatorServiceId.pid, serviceId.pid);
+
+ assertNotEquals(myPkg, creatorServiceId.packageName);
+ assertEquals(myPkg, serviceId.packageName);
+
+ getContext().unbindService(creatorConnection);
+ }
+
+ /** Given a Messenger, this will message the service to retrieve its UID, PID, and package name.
+ * On success, returns a RunningServiceInfo. On failure, returns null. */
+ private RunningServiceInfo identifyService(Messenger service) {
+ class IdentifyHandler extends Handler {
+ IdentifyHandler() {
+ super(Looper.getMainLooper());
+ }
+
+ RunningServiceInfo mInfo;
+
+ @Override
+ public void handleMessage(Message msg) {
+ Log.d(TAG, "Received message: " + msg);
+ switch (msg.what) {
+ case ServiceMessages.MSG_IDENTIFY_RESPONSE:
+ msg.getData().setClassLoader(RunningServiceInfo.class.getClassLoader());
+ mInfo = msg.getData().getParcelable(ServiceMessages.IDENTIFY_INFO);
+ mCondition.open();
+ break;
+ }
+ super.handleMessage(msg);
+ }
+ };
+
+ IdentifyHandler handler = new IdentifyHandler();
Messenger local = new Messenger(handler);
Message msg = Message.obtain(null, ServiceMessages.MSG_IDENTIFY);
@@ -321,7 +444,7 @@
if (!mCondition.block(CONDITION_TIMEOUT))
return null;
- return id;
+ return handler.mInfo;
}
private class Connection implements ServiceConnection {
diff --git a/tests/tests/graphics/jni/VulkanTestHelpers.cpp b/tests/tests/graphics/jni/VulkanTestHelpers.cpp
index c2dd5b4..17d952f 100644
--- a/tests/tests/graphics/jni/VulkanTestHelpers.cpp
+++ b/tests/tests/graphics/jni/VulkanTestHelpers.cpp
@@ -263,27 +263,6 @@
};
VK_CALL(vkCreateImage(mInit->device(), &createInfo, nullptr, &mImage));
- VkImageMemoryRequirementsInfo2 memReqsInfo;
- memReqsInfo.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2;
- memReqsInfo.pNext = nullptr;
- memReqsInfo.image = mImage;
-
- VkMemoryDedicatedRequirements dedicatedMemReqs;
- dedicatedMemReqs.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS;
- dedicatedMemReqs.pNext = nullptr;
-
- VkMemoryRequirements2 memReqs;
- memReqs.sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2;
- memReqs.pNext = &dedicatedMemReqs;
-
- PFN_vkGetImageMemoryRequirements2KHR getImageMemoryRequirements =
- (PFN_vkGetImageMemoryRequirements2KHR)vkGetDeviceProcAddr(
- mInit->device(), "vkGetImageMemoryRequirements2KHR");
- ASSERT(getImageMemoryRequirements);
- getImageMemoryRequirements(mInit->device(), &memReqsInfo, &memReqs);
- ASSERT(VK_TRUE == dedicatedMemReqs.prefersDedicatedAllocation);
- ASSERT(VK_TRUE == dedicatedMemReqs.requiresDedicatedAllocation);
-
VkImportAndroidHardwareBufferInfoANDROID androidHardwareBufferInfo{
.sType = VK_STRUCTURE_TYPE_IMPORT_ANDROID_HARDWARE_BUFFER_INFO_ANDROID,
.pNext = nullptr,
@@ -317,6 +296,27 @@
ASSERT(bindImageMemory);
VK_CALL(bindImageMemory(mInit->device(), 1, &bindImageInfo));
+ VkImageMemoryRequirementsInfo2 memReqsInfo;
+ memReqsInfo.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2;
+ memReqsInfo.pNext = nullptr;
+ memReqsInfo.image = mImage;
+
+ VkMemoryDedicatedRequirements dedicatedMemReqs;
+ dedicatedMemReqs.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS;
+ dedicatedMemReqs.pNext = nullptr;
+
+ VkMemoryRequirements2 memReqs;
+ memReqs.sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2;
+ memReqs.pNext = &dedicatedMemReqs;
+
+ PFN_vkGetImageMemoryRequirements2KHR getImageMemoryRequirements =
+ (PFN_vkGetImageMemoryRequirements2KHR)vkGetDeviceProcAddr(
+ mInit->device(), "vkGetImageMemoryRequirements2KHR");
+ ASSERT(getImageMemoryRequirements);
+ getImageMemoryRequirements(mInit->device(), &memReqsInfo, &memReqs);
+ ASSERT(VK_TRUE == dedicatedMemReqs.prefersDedicatedAllocation);
+ ASSERT(VK_TRUE == dedicatedMemReqs.requiresDedicatedAllocation);
+
if (useExternalFormat /* TODO: || explicit format requires conversion */) {
VkSamplerYcbcrConversionCreateInfo conversionCreateInfo{
.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO,
diff --git a/tests/tests/graphics/jni/android_graphics_cts_BitmapTest.cpp b/tests/tests/graphics/jni/android_graphics_cts_BitmapTest.cpp
index b08f384..d44c14f 100644
--- a/tests/tests/graphics/jni/android_graphics_cts_BitmapTest.cpp
+++ b/tests/tests/graphics/jni/android_graphics_cts_BitmapTest.cpp
@@ -67,6 +67,18 @@
AHardwareBuffer_unlock(hardware_buffer, nullptr);
}
+static jint getFormat(JNIEnv* env, jclass, jobject jbitmap) {
+ AndroidBitmapInfo info;
+ info.format = ANDROID_BITMAP_FORMAT_NONE;
+ int err = 0;
+ err = AndroidBitmap_getInfo(env, jbitmap, &info);
+ if (err != ANDROID_BITMAP_RESULT_SUCCESS) {
+ fail(env, "AndroidBitmap_getInfo failed, err=%d", err);
+ return ANDROID_BITMAP_FORMAT_NONE;
+ }
+ return info.format;
+}
+
static JNINativeMethod gMethods[] = {
{ "nValidateBitmapInfo", "(Landroid/graphics/Bitmap;IIZ)V",
(void*) validateBitmapInfo },
@@ -74,6 +86,7 @@
(void*) validateNdkAccessAfterRecycle },
{ "nFillRgbaHwBuffer", "(Landroid/hardware/HardwareBuffer;)V",
(void*) fillRgbaHardwareBuffer },
+ { "nGetFormat", "(Landroid/graphics/Bitmap;)I", (void*) getFormat },
};
int register_android_graphics_cts_BitmapTest(JNIEnv* env) {
diff --git a/tests/tests/graphics/res/drawable/gradientdrawable.xml b/tests/tests/graphics/res/drawable/gradientdrawable.xml
index 3d147dd..821d7bd 100644
--- a/tests/tests/graphics/res/drawable/gradientdrawable.xml
+++ b/tests/tests/graphics/res/drawable/gradientdrawable.xml
@@ -21,7 +21,7 @@
android:opticalInsetRight="3px"
android:opticalInsetBottom="4px">
<gradient android:startColor="#ffffffff" android:centerColor="#ffff0000"
- android:endColor="#0000ffff" />
+ android:endColor="#0000ffff" android:angle="45"/>
<corners android:radius="8px" />
<padding android:left="4px" android:top="2px"
android:right="6px" android:bottom="10px" />
diff --git a/tests/tests/graphics/src/android/graphics/cts/BitmapTest.java b/tests/tests/graphics/src/android/graphics/cts/BitmapTest.java
index 5a05e5f..bbba7a1 100644
--- a/tests/tests/graphics/src/android/graphics/cts/BitmapTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/BitmapTest.java
@@ -239,6 +239,7 @@
assertEquals(10, ret.getWidth());
assertEquals(10, ret.getHeight());
assertEquals(Config.RGB_565, ret.getConfig());
+ assertEquals(ANDROID_BITMAP_FORMAT_RGB_565, nGetFormat(ret));
}
@Test(expected=IllegalArgumentException.class)
@@ -261,6 +262,7 @@
ret = Bitmap.createBitmap(mBitmap, 10, 10, 50, 50);
assertNotNull(ret);
assertFalse(mBitmap.equals(ret));
+ assertEquals(ANDROID_BITMAP_FORMAT_RGBA_8888, nGetFormat(mBitmap));
}
@Test(expected=IllegalArgumentException.class)
@@ -2179,6 +2181,10 @@
private static native void nFillRgbaHwBuffer(HardwareBuffer hwBuffer);
+ private static final int ANDROID_BITMAP_FORMAT_RGBA_8888 = 1;
+ private static final int ANDROID_BITMAP_FORMAT_RGB_565 = 4;
+ private static native int nGetFormat(Bitmap bitmap);
+
private static HardwareBuffer createTestBuffer(int width, int height, boolean cpuAccess) {
long usage = HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE;
if (cpuAccess) {
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 65d921a..539c546 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/GradientDrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/GradientDrawableTest.java
@@ -22,7 +22,6 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
-import static org.testng.Assert.assertThrows;
import android.content.Context;
import android.content.res.ColorStateList;
@@ -735,10 +734,26 @@
Bitmap bitmap = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
- assertThrows(IllegalArgumentException.class, () -> {
+ try {
radiusDrawable.setBounds(0, 0, 10, 10);
radiusDrawable.draw(canvas);
- });
+ } catch (Exception e) {
+ fail("Threw exception: " + e + " with negative radius");
+ }
+ }
+
+ @Test
+ public void testInflatedGradientOrientationUpdated() {
+ final Context context = new ContextThemeWrapper(InstrumentationRegistry.getTargetContext(),
+ R.style.Theme_MixedGradientTheme);
+
+ GradientDrawable drawable = (GradientDrawable)
+ context.getDrawable(R.drawable.gradientdrawable);
+
+ assertEquals(Orientation.BL_TR, drawable.getOrientation());
+
+ drawable.setOrientation(Orientation.BOTTOM_TOP);
+ assertEquals(Orientation.BOTTOM_TOP, drawable.getOrientation());
}
private void verifyPreloadDensityInner(Resources res, int densityDpi)
diff --git a/tests/tests/jni/libjnitest/android_jni_cts_LinkerNamespacesTest.cpp b/tests/tests/jni/libjnitest/android_jni_cts_LinkerNamespacesTest.cpp
index 7f7cde1..25f4b0e 100644
--- a/tests/tests/jni/libjnitest/android_jni_cts_LinkerNamespacesTest.cpp
+++ b/tests/tests/jni/libjnitest/android_jni_cts_LinkerNamespacesTest.cpp
@@ -395,7 +395,7 @@
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.
- /*test_system_load_library=*/false,
+ /*test_system_load_library=*/true,
check_absence, &errors)) {
success = false;
}
diff --git a/tests/tests/jni/src/android/jni/cts/JniStaticTest.java b/tests/tests/jni/src/android/jni/cts/JniStaticTest.java
index 8604778..6f51d31 100644
--- a/tests/tests/jni/src/android/jni/cts/JniStaticTest.java
+++ b/tests/tests/jni/src/android/jni/cts/JniStaticTest.java
@@ -337,9 +337,8 @@
for (String lib : libs) {
File f = new File(systemBaseDir, lib);
- // TODO (b/124218500): Re-enable this check when the app compat issue is resolved.
- // assertFalse("The same native library should exist in the Runtime APEX."
- // + " It should not exist in /system: " + f , f.exists());
+ assertFalse("The same native library should exist in the Runtime APEX."
+ + " It should not exist in /system: " + f , f.exists());
String error = LinkerNamespacesHelper.tryDlopen(f.toString());
assertNotNull("The native library file does not exist in the file system, "
+ "but dlopen(" + f + ", RTLD_NOW) succeeds.", error);
diff --git a/tests/tests/location/AndroidManifest.xml b/tests/tests/location/AndroidManifest.xml
index 551de06..bd887b5 100644
--- a/tests/tests/location/AndroidManifest.xml
+++ b/tests/tests/location/AndroidManifest.xml
@@ -25,6 +25,7 @@
</application>
<uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION"/>
+ <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"/>
diff --git a/tests/tests/location2/AndroidManifest.xml b/tests/tests/location2/AndroidManifest.xml
index ff9bc3c..d78c3f8 100644
--- a/tests/tests/location2/AndroidManifest.xml
+++ b/tests/tests/location2/AndroidManifest.xml
@@ -24,6 +24,7 @@
<uses-library android:name="android.test.runner" />
</application>
+ <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS"/>
diff --git a/tests/tests/media/AndroidManifest.xml b/tests/tests/media/AndroidManifest.xml
index ab2ff54..7d1e6ea 100644
--- a/tests/tests/media/AndroidManifest.xml
+++ b/tests/tests/media/AndroidManifest.xml
@@ -30,6 +30,7 @@
<uses-permission android:name="android.permission.SET_VOLUME_KEY_LONG_PRESS_LISTENER" />
<uses-permission android:name="android.permission.SET_MEDIA_KEY_LISTENER" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
+ <uses-permission android:name="android.permission.INSTANT_APP_FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
diff --git a/tests/tests/media/AndroidTest.xml b/tests/tests/media/AndroidTest.xml
index 8438d99..70def5b 100644
--- a/tests/tests/media/AndroidTest.xml
+++ b/tests/tests/media/AndroidTest.xml
@@ -17,6 +17,7 @@
<option name="test-suite-tag" value="cts" />
<option name="config-descriptor:metadata" key="component" value="media" />
<option name="config-descriptor:metadata" key="parameter" value="instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher">
<option name="target" value="host" />
<option name="config-filename" value="cts" />
diff --git a/tests/tests/media/res/raw/loudsoftoggmkv.mkv b/tests/tests/media/res/raw/loudsoftoggmkv.mkv
new file mode 100644
index 0000000..d4d62dc
--- /dev/null
+++ b/tests/tests/media/res/raw/loudsoftoggmkv.mkv
Binary files differ
diff --git a/tests/tests/media/res/raw/loudsoftoggmp4.mp4 b/tests/tests/media/res/raw/loudsoftoggmp4.mp4
new file mode 100644
index 0000000..8e1154e
--- /dev/null
+++ b/tests/tests/media/res/raw/loudsoftoggmp4.mp4
Binary files differ
diff --git a/tests/tests/media/res/raw/monotestoggmkv.mkv b/tests/tests/media/res/raw/monotestoggmkv.mkv
new file mode 100644
index 0000000..0d1c6d0
--- /dev/null
+++ b/tests/tests/media/res/raw/monotestoggmkv.mkv
Binary files differ
diff --git a/tests/tests/media/res/raw/monotestoggmp4.mp4 b/tests/tests/media/res/raw/monotestoggmp4.mp4
new file mode 100644
index 0000000..bed86e9
--- /dev/null
+++ b/tests/tests/media/res/raw/monotestoggmp4.mp4
Binary files differ
diff --git a/tests/tests/media/res/raw/segment000001_m2ts.mp4 b/tests/tests/media/res/raw/segment000001_m2ts.mp4
new file mode 100644
index 0000000..fc8daba
--- /dev/null
+++ b/tests/tests/media/res/raw/segment000001_m2ts.mp4
Binary files differ
diff --git a/tests/tests/media/res/raw/sinesweepflacmkv.mkv b/tests/tests/media/res/raw/sinesweepflacmkv.mkv
new file mode 100644
index 0000000..2f622cd
--- /dev/null
+++ b/tests/tests/media/res/raw/sinesweepflacmkv.mkv
Binary files differ
diff --git a/tests/tests/media/res/raw/sinesweepflacmp4.mp4 b/tests/tests/media/res/raw/sinesweepflacmp4.mp4
new file mode 100644
index 0000000..f397afa
--- /dev/null
+++ b/tests/tests/media/res/raw/sinesweepflacmp4.mp4
Binary files differ
diff --git a/tests/tests/media/res/raw/sinesweepoggmp4.mp4 b/tests/tests/media/res/raw/sinesweepoggmp4.mp4
new file mode 100644
index 0000000..8bfbbf7
--- /dev/null
+++ b/tests/tests/media/res/raw/sinesweepoggmp4.mp4
Binary files differ
diff --git a/tests/tests/media/res/raw/sinesweepopusmp4.mp4 b/tests/tests/media/res/raw/sinesweepopusmp4.mp4
new file mode 100644
index 0000000..6894908
--- /dev/null
+++ b/tests/tests/media/res/raw/sinesweepopusmp4.mp4
Binary files differ
diff --git a/tests/tests/media/res/raw/sinesweepvorbismp4.mp4 b/tests/tests/media/res/raw/sinesweepvorbismp4.mp4
new file mode 100644
index 0000000..660ca02
--- /dev/null
+++ b/tests/tests/media/res/raw/sinesweepvorbismp4.mp4
Binary files differ
diff --git a/tests/tests/media/res/raw/video_1280x720_av1_hdr_static_3mbps.webm b/tests/tests/media/res/raw/video_1280x720_av1_hdr_static_3mbps.webm
new file mode 100644
index 0000000..0e332c2
--- /dev/null
+++ b/tests/tests/media/res/raw/video_1280x720_av1_hdr_static_3mbps.webm
Binary files differ
diff --git a/tests/tests/media/res/raw/video_1280x720_mp4_mpeg2_3000kbps_30fps_aac_stereo_128kbps_48000hz.mp4 b/tests/tests/media/res/raw/video_1280x720_mp4_mpeg2_3000kbps_30fps_aac_stereo_128kbps_48000hz.mp4
new file mode 100644
index 0000000..0490c74
--- /dev/null
+++ b/tests/tests/media/res/raw/video_1280x720_mp4_mpeg2_3000kbps_30fps_aac_stereo_128kbps_48000hz.mp4
Binary files differ
diff --git a/tests/tests/media/res/raw/video_1280x720_mp4_mpeg2_6000kbps_30fps_aac_stereo_128kbps_48000hz.mp4 b/tests/tests/media/res/raw/video_1280x720_mp4_mpeg2_6000kbps_30fps_aac_stereo_128kbps_48000hz.mp4
new file mode 100644
index 0000000..55cc26cee
--- /dev/null
+++ b/tests/tests/media/res/raw/video_1280x720_mp4_mpeg2_6000kbps_30fps_aac_stereo_128kbps_48000hz.mp4
Binary files differ
diff --git a/tests/tests/media/res/raw/video_1280x720_webm_av1_2000kbps_30fps_vorbis_stereo_128kbps_48000hz.webm b/tests/tests/media/res/raw/video_1280x720_webm_av1_2000kbps_30fps_vorbis_stereo_128kbps_48000hz.webm
new file mode 100644
index 0000000..abaea3f
--- /dev/null
+++ b/tests/tests/media/res/raw/video_1280x720_webm_av1_2000kbps_30fps_vorbis_stereo_128kbps_48000hz.webm
Binary files differ
diff --git a/tests/tests/media/res/raw/video_1920x1080_mp4_mpeg2_12000kbps_30fps_aac_stereo_128kbps_48000hz.mp4 b/tests/tests/media/res/raw/video_1920x1080_mp4_mpeg2_12000kbps_30fps_aac_stereo_128kbps_48000hz.mp4
new file mode 100644
index 0000000..d0704c4
--- /dev/null
+++ b/tests/tests/media/res/raw/video_1920x1080_mp4_mpeg2_12000kbps_30fps_aac_stereo_128kbps_48000hz.mp4
Binary files differ
diff --git a/tests/tests/media/res/raw/video_1920x1080_webm_av1_7000kbps_60fps_vorbis_stereo_128kbps_48000hz.webm b/tests/tests/media/res/raw/video_1920x1080_webm_av1_7000kbps_60fps_vorbis_stereo_128kbps_48000hz.webm
new file mode 100644
index 0000000..e2277e1
--- /dev/null
+++ b/tests/tests/media/res/raw/video_1920x1080_webm_av1_7000kbps_60fps_vorbis_stereo_128kbps_48000hz.webm
Binary files differ
diff --git a/tests/tests/media/res/raw/video_320x180_webm_av1_200kbps_30fps_vorbis_stereo_128kbps_48000hz.webm b/tests/tests/media/res/raw/video_320x180_webm_av1_200kbps_30fps_vorbis_stereo_128kbps_48000hz.webm
new file mode 100644
index 0000000..7cf844e
--- /dev/null
+++ b/tests/tests/media/res/raw/video_320x180_webm_av1_200kbps_30fps_vorbis_stereo_128kbps_48000hz.webm
Binary files differ
diff --git a/tests/tests/media/res/raw/video_352x288_mp4_mpeg2_1000kbps_30fps_aac_stereo_128kbps_48000hz.mp4 b/tests/tests/media/res/raw/video_352x288_mp4_mpeg2_1000kbps_30fps_aac_stereo_128kbps_48000hz.mp4
new file mode 100644
index 0000000..25814db
--- /dev/null
+++ b/tests/tests/media/res/raw/video_352x288_mp4_mpeg2_1000kbps_30fps_aac_stereo_128kbps_48000hz.mp4
Binary files differ
diff --git a/tests/tests/media/res/raw/video_3840x2160_mp4_mpeg2_20000kbps_30fps_aac_stereo_128kbps_48000hz.mp4 b/tests/tests/media/res/raw/video_3840x2160_mp4_mpeg2_20000kbps_30fps_aac_stereo_128kbps_48000hz.mp4
new file mode 100644
index 0000000..c8c63a5
--- /dev/null
+++ b/tests/tests/media/res/raw/video_3840x2160_mp4_mpeg2_20000kbps_30fps_aac_stereo_128kbps_48000hz.mp4
Binary files differ
diff --git a/tests/tests/media/res/raw/video_3840x2160_webm_av1_11000kbps_30fps_vorbis_stereo_128kbps_48000hz.webm b/tests/tests/media/res/raw/video_3840x2160_webm_av1_11000kbps_30fps_vorbis_stereo_128kbps_48000hz.webm
new file mode 100644
index 0000000..a61e8f9
--- /dev/null
+++ b/tests/tests/media/res/raw/video_3840x2160_webm_av1_11000kbps_30fps_vorbis_stereo_128kbps_48000hz.webm
Binary files differ
diff --git a/tests/tests/media/res/raw/video_3840x2160_webm_av1_18000kbps_60fps_vorbis_stereo_128kbps_48000hz.webm b/tests/tests/media/res/raw/video_3840x2160_webm_av1_18000kbps_60fps_vorbis_stereo_128kbps_48000hz.webm
new file mode 100644
index 0000000..51e4d27
--- /dev/null
+++ b/tests/tests/media/res/raw/video_3840x2160_webm_av1_18000kbps_60fps_vorbis_stereo_128kbps_48000hz.webm
Binary files differ
diff --git a/tests/tests/media/res/raw/video_480x360_mp4_mpeg2_1500kbps_30fps_aac_stereo_128kbps_48000hz.mp4 b/tests/tests/media/res/raw/video_480x360_mp4_mpeg2_1500kbps_30fps_aac_stereo_128kbps_48000hz.mp4
new file mode 100644
index 0000000..33f66a0
--- /dev/null
+++ b/tests/tests/media/res/raw/video_480x360_mp4_mpeg2_1500kbps_30fps_aac_stereo_128kbps_48000hz.mp4
Binary files differ
diff --git a/tests/tests/media/res/raw/video_480x360_webm_av1_400kbps_30fps_vorbis_stereo_128kbps_48000hz.webm b/tests/tests/media/res/raw/video_480x360_webm_av1_400kbps_30fps_vorbis_stereo_128kbps_48000hz.webm
new file mode 100644
index 0000000..5671197
--- /dev/null
+++ b/tests/tests/media/res/raw/video_480x360_webm_av1_400kbps_30fps_vorbis_stereo_128kbps_48000hz.webm
Binary files differ
diff --git a/tests/tests/media/res/raw/video_640x360_mp4_mpeg2_2000kbps_30fps_aac_stereo_128kbps_48000hz.mp4 b/tests/tests/media/res/raw/video_640x360_mp4_mpeg2_2000kbps_30fps_aac_stereo_128kbps_48000hz.mp4
new file mode 100644
index 0000000..cc502a4
--- /dev/null
+++ b/tests/tests/media/res/raw/video_640x360_mp4_mpeg2_2000kbps_30fps_aac_stereo_128kbps_48000hz.mp4
Binary files differ
diff --git a/tests/tests/media/res/raw/video_640x360_webm_av1_470kbps_30fps_vorbis_stereo_128kbps_48000hz.webm b/tests/tests/media/res/raw/video_640x360_webm_av1_470kbps_30fps_vorbis_stereo_128kbps_48000hz.webm
new file mode 100644
index 0000000..1d99b2c
--- /dev/null
+++ b/tests/tests/media/res/raw/video_640x360_webm_av1_470kbps_30fps_vorbis_stereo_128kbps_48000hz.webm
Binary files differ
diff --git a/tests/tests/media/res/raw/video_720x480_mp4_mpeg2_2000kbps_30fps_aac_stereo_128kbps_48000hz.mp4 b/tests/tests/media/res/raw/video_720x480_mp4_mpeg2_2000kbps_30fps_aac_stereo_128kbps_48000hz.mp4
new file mode 100644
index 0000000..c6187fa
--- /dev/null
+++ b/tests/tests/media/res/raw/video_720x480_mp4_mpeg2_2000kbps_30fps_aac_stereo_128kbps_48000hz.mp4
Binary files differ
diff --git a/tests/tests/media/src/android/media/cts/AdaptivePlaybackTest.java b/tests/tests/media/src/android/media/cts/AdaptivePlaybackTest.java
index 3391aa3..1d519df 100644
--- a/tests/tests/media/src/android/media/cts/AdaptivePlaybackTest.java
+++ b/tests/tests/media/src/android/media/cts/AdaptivePlaybackTest.java
@@ -72,6 +72,14 @@
R.raw.bbb_s1_352x288_mp4_hevc_mp2_600kbps_30fps_aac_he_stereo_96kbps_48000hz);
}
+ public Iterable<Codec> Mpeg2(CodecFactory factory) {
+ return factory.createCodecList(
+ mContext,
+ MediaFormat.MIMETYPE_VIDEO_MPEG2,
+ R.raw.video_640x360_mp4_mpeg2_2000kbps_30fps_aac_stereo_128kbps_48000hz,
+ R.raw.video_1280x720_mp4_mpeg2_3000kbps_30fps_aac_stereo_128kbps_48000hz);
+ }
+
public Iterable<Codec> H263(CodecFactory factory) {
return factory.createCodecList(
mContext,
@@ -107,6 +115,15 @@
R.raw.bbb_s1_320x180_webm_vp9_0p11_600kbps_30fps_vorbis_mono_64kbps_48000hz);
}
+ public Iterable<Codec> AV1(CodecFactory factory) {
+ return factory.createCodecList(
+ mContext,
+ MediaFormat.MIMETYPE_VIDEO_AV1,
+ R.raw.video_480x360_webm_av1_400kbps_30fps_vorbis_stereo_128kbps_48000hz,
+ R.raw.video_1280x720_webm_av1_2000kbps_30fps_vorbis_stereo_128kbps_48000hz,
+ R.raw.video_320x180_webm_av1_200kbps_30fps_vorbis_stereo_128kbps_48000hz);
+ }
+
CodecFactory ALL = new CodecFactory();
CodecFactory SW = new SWCodecFactory();
CodecFactory HW = new HWCodecFactory();
@@ -115,19 +132,21 @@
public Iterable<Codec> HEVC() { return HEVC(ALL); }
public Iterable<Codec> VP8() { return VP8(ALL); }
public Iterable<Codec> VP9() { return VP9(ALL); }
+ public Iterable<Codec> AV1() { return AV1(ALL); }
+ public Iterable<Codec> Mpeg2() { return Mpeg2(ALL); }
public Iterable<Codec> Mpeg4() { return Mpeg4(ALL); }
public Iterable<Codec> H263() { return H263(ALL); }
public Iterable<Codec> AllCodecs() {
- return chain(H264(ALL), HEVC(ALL), VP8(ALL), VP9(ALL), Mpeg4(ALL), H263(ALL));
+ return chain(H264(ALL), HEVC(ALL), VP8(ALL), VP9(ALL), AV1(ALL), Mpeg2(ALL), Mpeg4(ALL), H263(ALL));
}
public Iterable<Codec> SWCodecs() {
- return chain(H264(SW), HEVC(SW), VP8(SW), VP9(SW), Mpeg4(SW), H263(SW));
+ return chain(H264(SW), HEVC(SW), VP8(SW), VP9(SW), AV1(SW), Mpeg2(SW), Mpeg4(SW), H263(SW));
}
public Iterable<Codec> HWCodecs() {
- return chain(H264(HW), HEVC(HW), VP8(HW), VP9(HW), Mpeg4(HW), H263(HW));
+ return chain(H264(HW), HEVC(HW), VP8(HW), VP9(HW), AV1(HW), Mpeg2(HW), Mpeg4(HW), H263(HW));
}
/* tests for adaptive codecs */
@@ -184,6 +203,8 @@
public void runHEVC() { ex(HEVC(), allTests); }
public void runVP8() { ex(VP8(), allTests); }
public void runVP9() { ex(VP9(), allTests); }
+ public void runAV1() { ex(AV1(), allTests); }
+ public void runMpeg2() { ex(Mpeg2(), allTests); }
public void runMpeg4() { ex(Mpeg4(), allTests); }
public void runH263() { ex(H263(), allTests); }
@@ -191,6 +212,8 @@
public void onlyHEVCHW() { ex(HEVC(HW), allTests); }
public void onlyVP8HW() { ex(VP8(HW), allTests); }
public void onlyVP9HW() { ex(VP9(HW), allTests); }
+ public void onlyAV1HW() { ex(AV1(HW), allTests); }
+ public void onlyMpeg2HW() { ex(Mpeg2(HW), allTests); }
public void onlyMpeg4HW() { ex(Mpeg4(HW), allTests); }
public void onlyH263HW() { ex(H263(HW), allTests); }
@@ -198,6 +221,8 @@
public void onlyHEVCSW() { ex(HEVC(SW), allTests); }
public void onlyVP8SW() { ex(VP8(SW), allTests); }
public void onlyVP9SW() { ex(VP9(SW), allTests); }
+ public void onlyAV1SW() { ex(AV1(SW), allTests); }
+ public void onlyMpeg2SW() { ex(Mpeg2(SW), allTests); }
public void onlyMpeg4SW() { ex(Mpeg4(SW), allTests); }
public void onlyH263SW() { ex(H263(SW), allTests); }
@@ -209,6 +234,8 @@
public void testHEVC_adaptiveEarlyEos() { ex(HEVC(), adaptiveEarlyEos); }
public void testVP8_adaptiveEarlyEos() { ex(VP8(), adaptiveEarlyEos); }
public void testVP9_adaptiveEarlyEos() { ex(VP9(), adaptiveEarlyEos); }
+ public void testAV1_adaptiveEarlyEos() { ex(AV1(), adaptiveEarlyEos); }
+ public void testMpeg2_adaptiveEarlyEos() { ex(Mpeg2(), adaptiveEarlyEos); }
public void testMpeg4_adaptiveEarlyEos() { ex(Mpeg4(), adaptiveEarlyEos); }
public void testH263_adaptiveEarlyEos() { ex(H263(), adaptiveEarlyEos); }
@@ -216,6 +243,8 @@
public void testHEVC_adaptiveEosFlushSeek() { ex(HEVC(), adaptiveEosFlushSeek); }
public void testVP8_adaptiveEosFlushSeek() { ex(VP8(), adaptiveEosFlushSeek); }
public void testVP9_adaptiveEosFlushSeek() { ex(VP9(), adaptiveEosFlushSeek); }
+ public void testAV1_adaptiveEosFlushSeek() { ex(AV1(), adaptiveEosFlushSeek); }
+ public void testMpeg2_adaptiveEosFlushSeek() { ex(Mpeg2(), adaptiveEosFlushSeek); }
public void testMpeg4_adaptiveEosFlushSeek() { ex(Mpeg4(), adaptiveEosFlushSeek); }
public void testH263_adaptiveEosFlushSeek() { ex(H263(), adaptiveEosFlushSeek); }
@@ -223,6 +252,8 @@
public void testHEVC_adaptiveSkipAhead() { ex(HEVC(), adaptiveSkipAhead); }
public void testVP8_adaptiveSkipAhead() { ex(VP8(), adaptiveSkipAhead); }
public void testVP9_adaptiveSkipAhead() { ex(VP9(), adaptiveSkipAhead); }
+ public void testAV1_adaptiveSkipAhead() { ex(AV1(), adaptiveSkipAhead); }
+ public void testMpeg2_adaptiveSkipAhead() { ex(Mpeg2(), adaptiveSkipAhead); }
public void testMpeg4_adaptiveSkipAhead() { ex(Mpeg4(), adaptiveSkipAhead); }
public void testH263_adaptiveSkipAhead() { ex(H263(), adaptiveSkipAhead); }
@@ -230,6 +261,8 @@
public void testHEVC_adaptiveSkipBack() { ex(HEVC(), adaptiveSkipBack); }
public void testVP8_adaptiveSkipBack() { ex(VP8(), adaptiveSkipBack); }
public void testVP9_adaptiveSkipBack() { ex(VP9(), adaptiveSkipBack); }
+ public void testAV1_adaptiveSkipBack() { ex(AV1(), adaptiveSkipBack); }
+ public void testMpeg2_adaptiveSkipBack() { ex(Mpeg2(), adaptiveSkipBack); }
public void testMpeg4_adaptiveSkipBack() { ex(Mpeg4(), adaptiveSkipBack); }
public void testH263_adaptiveSkipBack() { ex(H263(), adaptiveSkipBack); }
@@ -237,6 +270,8 @@
public void testHEVC_adaptiveReconfigDrc() { ex(HEVC(), adaptiveReconfigDrc); }
public void testVP8_adaptiveReconfigDrc() { ex(VP8(), adaptiveReconfigDrc); }
public void testVP9_adaptiveReconfigDrc() { ex(VP9(), adaptiveReconfigDrc); }
+ public void testAV1_adaptiveReconfigDrc() { ex(AV1(), adaptiveReconfigDrc); }
+ public void testMpeg2_adaptiveReconfigDrc() { ex(Mpeg2(), adaptiveReconfigDrc); }
public void testMpeg4_adaptiveReconfigDrc() { ex(Mpeg4(), adaptiveReconfigDrc); }
public void testH263_adaptiveReconfigDrc() { ex(H263(), adaptiveReconfigDrc); }
@@ -244,6 +279,8 @@
public void testHEVC_adaptiveSmallReconfigDrc() { ex(HEVC(), adaptiveSmallReconfigDrc); }
public void testVP8_adaptiveSmallReconfigDrc() { ex(VP8(), adaptiveSmallReconfigDrc); }
public void testVP9_adaptiveSmallReconfigDrc() { ex(VP9(), adaptiveSmallReconfigDrc); }
+ public void testAV1_adaptiveSmallReconfigDrc() { ex(AV1(), adaptiveSmallReconfigDrc); }
+ public void testMpeg2_adaptiveSmallReconfigDrc() { ex(Mpeg2(), adaptiveSmallReconfigDrc); }
public void testMpeg4_adaptiveSmallReconfigDrc() { ex(Mpeg4(), adaptiveSmallReconfigDrc); }
public void testH263_adaptiveSmallReconfigDrc() { ex(H263(), adaptiveSmallReconfigDrc); }
@@ -251,6 +288,8 @@
public void testHEVC_adaptiveDrc() { ex(HEVC(), adaptiveDrc); }
public void testVP8_adaptiveDrc() { ex(VP8(), adaptiveDrc); }
public void testVP9_adaptiveDrc() { ex(VP9(), adaptiveDrc); }
+ public void testAV1_adaptiveDrc() { ex(AV1(), adaptiveDrc); }
+ public void testMpeg2_adaptiveDrc() { ex(Mpeg2(), adaptiveDrc); }
public void testMpeg4_adaptiveDrc() { ex(Mpeg4(), adaptiveDrc); }
public void testH263_adaptiveDrc() { ex(H263(), adaptiveDrc); }
@@ -258,16 +297,22 @@
public void testHEVC_adaptiveDrcEarlyEos() { ex(HEVC(), new AdaptiveDrcEarlyEosTest()); }
public void testVP8_adaptiveDrcEarlyEos() { ex(VP8(), new AdaptiveDrcEarlyEosTest()); }
public void testVP9_adaptiveDrcEarlyEos() { ex(VP9(), new AdaptiveDrcEarlyEosTest()); }
+ public void testAV1_adaptiveDrcEarlyEos() { ex(AV1(), new AdaptiveDrcEarlyEosTest()); }
+ public void testMpeg2_adaptiveDrcEarlyEos(){ ex(Mpeg2(), new AdaptiveDrcEarlyEosTest()); }
public void testH264_adaptiveSmallDrc() { ex(H264(), adaptiveSmallDrc); }
public void testHEVC_adaptiveSmallDrc() { ex(HEVC(), adaptiveSmallDrc); }
public void testVP8_adaptiveSmallDrc() { ex(VP8(), adaptiveSmallDrc); }
public void testVP9_adaptiveSmallDrc() { ex(VP9(), adaptiveSmallDrc); }
+ public void testAV1_adaptiveSmallDrc() { ex(AV1(), adaptiveSmallDrc); }
+ public void testMpeg2_adaptiveSmallDrc() { ex(Mpeg2(), adaptiveSmallDrc); }
public void testH264_earlyEos() { ex(H264(), earlyEos); }
public void testHEVC_earlyEos() { ex(HEVC(), earlyEos); }
public void testVP8_earlyEos() { ex(VP8(), earlyEos); }
public void testVP9_earlyEos() { ex(VP9(), earlyEos); }
+ public void testAV1_earlyEos() { ex(AV1(), earlyEos); }
+ public void testMpeg2_earlyEos() { ex(Mpeg2(), earlyEos); }
public void testMpeg4_earlyEos() { ex(Mpeg4(), earlyEos); }
public void testH263_earlyEos() { ex(H263(), earlyEos); }
@@ -275,6 +320,8 @@
public void testHEVC_eosFlushSeek() { ex(HEVC(), eosFlushSeek); }
public void testVP8_eosFlushSeek() { ex(VP8(), eosFlushSeek); }
public void testVP9_eosFlushSeek() { ex(VP9(), eosFlushSeek); }
+ public void testAV1_eosFlushSeek() { ex(AV1(), eosFlushSeek); }
+ public void testMpeg2_eosFlushSeek() { ex(Mpeg2(), eosFlushSeek); }
public void testMpeg4_eosFlushSeek() { ex(Mpeg4(), eosFlushSeek); }
public void testH263_eosFlushSeek() { ex(H263(), eosFlushSeek); }
@@ -282,6 +329,8 @@
public void testHEVC_flushConfigureDrc() { ex(HEVC(), flushConfigureDrc); }
public void testVP8_flushConfigureDrc() { ex(VP8(), flushConfigureDrc); }
public void testVP9_flushConfigureDrc() { ex(VP9(), flushConfigureDrc); }
+ public void testAV1_flushConfigureDrc() { ex(AV1(), flushConfigureDrc); }
+ public void testMpeg2_flushConfigureDrc() { ex(Mpeg2(), flushConfigureDrc); }
public void testMpeg4_flushConfigureDrc() { ex(Mpeg4(), flushConfigureDrc); }
public void testH263_flushConfigureDrc() { ex(H263(), flushConfigureDrc); }
@@ -1369,17 +1418,19 @@
public CodecCapabilities capabilities;
public Media[] mediaList;
public boolean adaptive;
- public Codec(String n, CodecCapabilities c, Media[] m) {
- name = n;
+ public boolean vendor;
+ public Codec(MediaCodecInfo info, CodecCapabilities c, Media[] m) {
+ name = info.getName();
capabilities = c;
List<Media> medias = new ArrayList<Media>();
if (capabilities == null) {
adaptive = false;
+ vendor = true;
} else {
Log.w(TAG, "checking capabilities of " + name + " for " + m[0].getMime());
adaptive = capabilities.isFeatureSupported(CodecCapabilities.FEATURE_AdaptivePlayback);
-
+ vendor = info.isVendor();
for (Media media : m) {
if (media.getHeight() >= 720 &&
!capabilities.isFormatSupported(media.getFormat())) {
@@ -1430,13 +1481,16 @@
/* enumerate codecs */
MediaCodecList mcl = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
for (MediaCodecInfo codecInfo : mcl.getCodecInfos()) {
+ if (codecInfo.isAlias()) {
+ continue;
+ }
if (codecInfo.isEncoder()) {
continue;
}
for (String type : codecInfo.getSupportedTypes()) {
if (type.equals(mime)) {
add(new Codec(
- codecInfo.getName(),
+ codecInfo,
codecInfo.getCapabilitiesForType(mime),
mediaList));
break;
@@ -1455,7 +1509,7 @@
public CodecFamilySpecific(
Context context, String mime, boolean isGoogle, int ... resources) {
for (Codec c: new CodecFamily(context, mime, resources)) {
- if (MediaUtils.isGoogle(c.name) == isGoogle) {
+ if (!c.vendor == isGoogle) {
add(c);
}
}
diff --git a/tests/tests/media/src/android/media/cts/AudioAttributesTest.java b/tests/tests/media/src/android/media/cts/AudioAttributesTest.java
index a7cb4ea..0666efe 100644
--- a/tests/tests/media/src/android/media/cts/AudioAttributesTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioAttributesTest.java
@@ -96,4 +96,20 @@
final int policy = aa.getAllowedCapturePolicy();
assertEquals("Wrong default capture policy", AudioAttributes.ALLOW_CAPTURE_BY_ALL, policy);
}
+
+ // -----------------------------------------------------------------
+ // Regression tests
+ // ----------------------------------
+ // Test against regression where setLegacyStreamType() was creating a different Builder
+ public void testSetLegacyStreamOnBuilder() throws Exception {
+ final int stream = AudioManager.STREAM_MUSIC;
+ AudioAttributes.Builder builder1 = new AudioAttributes.Builder();
+ builder1.setLegacyStreamType(stream);
+ AudioAttributes attr1 = builder1.build();
+
+ AudioAttributes.Builder builder2 = new AudioAttributes.Builder().setLegacyStreamType(stream);
+ AudioAttributes attr2 = builder2.build();
+
+ assertEquals(attr1, attr2);
+ }
}
diff --git a/tests/tests/media/src/android/media/cts/AudioHelper.java b/tests/tests/media/src/android/media/cts/AudioHelper.java
index 7add051..7208e51 100644
--- a/tests/tests/media/src/android/media/cts/AudioHelper.java
+++ b/tests/tests/media/src/android/media/cts/AudioHelper.java
@@ -18,18 +18,29 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import com.android.compatibility.common.util.DeviceReportLog;
+import com.android.compatibility.common.util.ResultType;
+import com.android.compatibility.common.util.ResultUnit;
import java.nio.ByteBuffer;
import org.junit.Assert;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.media.AudioAttributes;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioRecord;
+import android.media.AudioTimestamp;
import android.media.AudioTrack;
import android.os.Looper;
import android.os.PersistableBundle;
+import android.util.Log;
+
+import androidx.test.InstrumentationRegistry;
// Used for statistics and loopers in listener tests.
// See AudioRecordTest.java and AudioTrack_ListenerTest.java.
@@ -240,6 +251,151 @@
}
}
+ public static class TimestampVerifier {
+
+ // CDD 5.6 1ms timestamp accuracy
+ private static final double TEST_MAX_JITTER_MS_ALLOWED = 6.; // a sanity check
+ private static final double TEST_STD_JITTER_MS_ALLOWED = 3.; // flaky tolerance 3x
+ private static final double TEST_STD_JITTER_MS_WARN = 1.; // CDD requirement warning
+
+ // CDD 5.6 100ms track startup latency
+ private static final double TEST_STARTUP_TIME_MS_ALLOWED = 500.; // flaky tolerance 5x
+ private static final double TEST_STARTUP_TIME_MS_WARN = 100.; // CDD requirement warning
+
+ private static final int MILLIS_PER_SECOND = 1000;
+ private static final long NANOS_PER_MILLISECOND = 1000000;
+ private static final long NANOS_PER_SECOND = NANOS_PER_MILLISECOND * MILLIS_PER_SECOND;
+ private static final String REPORT_LOG_NAME = "CtsMediaTestCases";
+
+ private final String mTag;
+ private final int mSampleRate;
+
+ // Running statistics
+ private int mCount = 0;
+ private long mLastFrames = 0;
+ private long mLastTimeNs = 0;
+ private int mJitterCount = 0;
+ private double mMeanJitterMs = 0.;
+ private double mSecondMomentJitterMs = 0.;
+ private double mMaxAbsJitterMs = 0.;
+ private int mWarmupCount = 0;
+
+ public TimestampVerifier(@Nullable String tag, @IntRange(from=4000) int sampleRate) {
+ mTag = tag; // Log accepts null
+ mSampleRate = sampleRate;
+ }
+
+ public int getJitterCount() { return mJitterCount; }
+ public double getMeanJitterMs() { return mMeanJitterMs; }
+ public double getStdJitterMs() { return Math.sqrt(mSecondMomentJitterMs / mJitterCount); }
+ public double getMaxAbsJitterMs() { return mMaxAbsJitterMs; }
+ public double getStartTimeNs() {
+ return mLastTimeNs - (mLastFrames * NANOS_PER_SECOND / mSampleRate);
+ }
+
+ public void add(@NonNull AudioTimestamp ts) {
+ final long frames = ts.framePosition;
+ final long timeNs = ts.nanoTime;
+
+ assertTrue("timestamps must have causal time", System.nanoTime() >= timeNs);
+
+ if (mCount > 0) { // need delta info from previous iteration (skipping first)
+ final long deltaFrames = frames - mLastFrames;
+ final long deltaTimeNs = timeNs - mLastTimeNs;
+
+ if (deltaFrames == 0 && deltaTimeNs == 0) return;
+
+ final double deltaFramesNs = (double)deltaFrames * NANOS_PER_SECOND / mSampleRate;
+ final double jitterMs = (deltaTimeNs - deltaFramesNs) // actual - expected
+ * (1. / NANOS_PER_MILLISECOND);
+
+ Log.d(mTag, "frames(" + frames
+ + ") timeNs(" + timeNs
+ + ") lastframes(" + mLastFrames
+ + ") lastTimeNs(" + mLastTimeNs
+ + ") deltaFrames(" + deltaFrames
+ + ") deltaTimeNs(" + deltaTimeNs
+ + ") jitterMs(" + jitterMs + ")");
+ assertTrue("timestamp time should be increasing", deltaTimeNs >= 0);
+ assertTrue("timestamp frames should be increasing", deltaFrames >= 0);
+
+ if (mLastFrames != 0) {
+ if (mWarmupCount++ > 1) { // ensure device is warmed up
+ // Welford's algorithm
+ // https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance
+ ++mJitterCount;
+ final double delta = jitterMs - mMeanJitterMs;
+ mMeanJitterMs += delta / mJitterCount;
+ final double delta2 = jitterMs - mMeanJitterMs;
+ mSecondMomentJitterMs += delta * delta2;
+
+ // jitterMs is signed, so max uses abs() here.
+ final double absJitterMs = Math.abs(jitterMs);
+ if (absJitterMs > mMaxAbsJitterMs) {
+ mMaxAbsJitterMs = absJitterMs;
+ }
+ }
+ }
+ }
+ ++mCount;
+ mLastFrames = frames;
+ mLastTimeNs = timeNs;
+ }
+
+ public void verifyAndLog(long trackStartTimeNs, @Nullable String logName) {
+ // enough timestamps?
+ assertTrue("need at least 2 jitter measurements", mJitterCount >= 2);
+
+ // Compute startup time and std jitter.
+ final int startupTimeMs =
+ (int) ((getStartTimeNs() - trackStartTimeNs) / NANOS_PER_MILLISECOND);
+ final double stdJitterMs = getStdJitterMs();
+
+ // Check startup time
+ if (startupTimeMs > TEST_STARTUP_TIME_MS_WARN) {
+ Log.w(mTag, "CDD warning: startup time " + startupTimeMs
+ + " > " + TEST_STARTUP_TIME_MS_WARN);
+ }
+ assertTrue("expect startupTimeMs " + startupTimeMs
+ + " < " + TEST_STARTUP_TIME_MS_ALLOWED,
+ startupTimeMs < TEST_STARTUP_TIME_MS_ALLOWED);
+
+ // Check maximum jitter
+ assertTrue("expect maxAbsJitterMs(" + mMaxAbsJitterMs + ") < "
+ + TEST_MAX_JITTER_MS_ALLOWED,
+ mMaxAbsJitterMs < TEST_MAX_JITTER_MS_ALLOWED);
+
+ // Check std jitter
+ if (stdJitterMs > TEST_STD_JITTER_MS_WARN) {
+ Log.w(mTag, "CDD warning: std timestamp jitter " + stdJitterMs
+ + " > " + TEST_STD_JITTER_MS_WARN);
+ }
+ assertTrue("expect stdJitterMs " + stdJitterMs + " < " + TEST_STD_JITTER_MS_ALLOWED,
+ stdJitterMs < TEST_STD_JITTER_MS_ALLOWED);
+
+ Log.d(mTag, "startupTimeMs(" + startupTimeMs
+ + ") meanJitterMs(" + mMeanJitterMs
+ + ") maxAbsJitterMs(" + mMaxAbsJitterMs
+ + ") stdJitterMs(" + stdJitterMs
+ + ")");
+
+ // Log results if logName is provided
+ if (logName != null) {
+ DeviceReportLog log = new DeviceReportLog(REPORT_LOG_NAME, logName);
+ // ReportLog needs at least one Value and Summary.
+ log.addValue("startup_time_ms", startupTimeMs,
+ ResultType.LOWER_BETTER, ResultUnit.MS);
+ log.addValue("maximum_abs_jitter_ms", mMaxAbsJitterMs,
+ ResultType.LOWER_BETTER, ResultUnit.MS);
+ log.addValue("mean_jitter_ms", mMeanJitterMs,
+ ResultType.LOWER_BETTER, ResultUnit.MS);
+ log.setSummary("std_jitter_ms", stdJitterMs,
+ ResultType.LOWER_BETTER, ResultUnit.MS);
+ log.submit(InstrumentationRegistry.getInstrumentation());
+ }
+ }
+ }
+
/* AudioRecordAudit extends AudioRecord to allow concurrent playback
* of read content to an AudioTrack. This is for testing only.
* For general applications, it is NOT recommended to extend AudioRecord.
diff --git a/tests/tests/media/src/android/media/cts/AudioPlaybackConfigurationTest.java b/tests/tests/media/src/android/media/cts/AudioPlaybackConfigurationTest.java
index 207c2a2..6256175 100644
--- a/tests/tests/media/src/android/media/cts/AudioPlaybackConfigurationTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioPlaybackConfigurationTest.java
@@ -16,8 +16,13 @@
package android.media.cts;
+import static android.media.AudioAttributes.ALLOW_CAPTURE_BY_ALL;
+import static android.media.AudioAttributes.ALLOW_CAPTURE_BY_NONE;
+import static android.media.AudioAttributes.ALLOW_CAPTURE_BY_SYSTEM;
+
import android.content.pm.PackageManager;
import android.media.AudioAttributes;
+import android.media.AudioAttributes.CapturePolicy;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.SoundPool;
@@ -77,6 +82,7 @@
final AudioAttributes aa = (new AudioAttributes.Builder())
.setUsage(TEST_USAGE)
.setContentType(TEST_CONTENT)
+ .setAllowedCapturePolicy(ALLOW_CAPTURE_BY_NONE)
.build();
mMp = MediaPlayer.create(getContext(), R.raw.sine1khzs40dblong,
aa, am.generateAudioSessionId());
@@ -120,6 +126,7 @@
final AudioAttributes aa = (new AudioAttributes.Builder())
.setUsage(TEST_USAGE)
.setContentType(TEST_CONTENT)
+ .setAllowedCapturePolicy(ALLOW_CAPTURE_BY_ALL)
.build();
List<AudioPlaybackConfiguration> configs = am.getActivePlaybackConfigurations();
@@ -308,6 +315,7 @@
final AudioAttributes aa = (new AudioAttributes.Builder())
.setUsage(TEST_USAGE)
.setContentType(TEST_CONTENT)
+ .setAllowedCapturePolicy(ALLOW_CAPTURE_BY_SYSTEM)
.build();
mSp = new SoundPool.Builder()
@@ -398,17 +406,27 @@
}
private static boolean hasAttr(List<AudioPlaybackConfiguration> configs, AudioAttributes aa) {
- Iterator<AudioPlaybackConfiguration> it = configs.iterator();
- while (it.hasNext()) {
- final AudioPlaybackConfiguration apc = it.next();
+ for (AudioPlaybackConfiguration apc : configs) {
if (apc.getAudioAttributes().getContentType() == aa.getContentType()
- && apc.getAudioAttributes().getUsage() == aa.getUsage()) {
+ && apc.getAudioAttributes().getUsage() == aa.getUsage()
+ && apc.getAudioAttributes().getFlags() == aa.getFlags()
+ && anonymizeCapturePolicy(apc.getAudioAttributes().getAllowedCapturePolicy())
+ == aa.getAllowedCapturePolicy()) {
return true;
}
}
return false;
}
+ /** ALLOW_CAPTURE_BY_SYSTEM is anonymized to ALLOW_CAPTURE_BY_NONE. */
+ @CapturePolicy
+ private static int anonymizeCapturePolicy(@CapturePolicy int policy) {
+ if (policy == ALLOW_CAPTURE_BY_SYSTEM) {
+ return ALLOW_CAPTURE_BY_NONE;
+ }
+ return policy;
+ }
+
private boolean isValidPlatform(String testName) {
if (!getContext().getPackageManager()
.hasSystemFeature(PackageManager.FEATURE_AUDIO_OUTPUT)) {
diff --git a/tests/tests/media/src/android/media/cts/AudioRecordTest.java b/tests/tests/media/src/android/media/cts/AudioRecordTest.java
index 32a1816..b7e565d 100644
--- a/tests/tests/media/src/android/media/cts/AudioRecordTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioRecordTest.java
@@ -87,6 +87,7 @@
}
};
private static final int RECORD_DURATION_MS = 500;
+ private static final int TEST_TIMING_TOLERANCE_MS = 70;
@Before
public void setUp() throws Exception {
@@ -531,9 +532,9 @@
AudioRecord record = null;
try {
- final int NANOS_PER_MILLIS = 1000000;
- final long RECORD_TIME_IN_MS = 2000;
- final long RECORD_TIME_IN_NANOS = RECORD_TIME_IN_MS * NANOS_PER_MILLIS;
+ final int NANOS_PER_MILLISECOND = 1000000;
+ final long RECORD_TIME_MS = 2000;
+ final long RECORD_TIME_NS = RECORD_TIME_MS * NANOS_PER_MILLISECOND;
final int RECORD_ENCODING = AudioFormat.ENCODING_PCM_16BIT; // fixed at this time.
final int RECORD_CHANNEL_MASK = AudioFormat.CHANNEL_IN_STEREO;
final int RECORD_SAMPLE_RATE = 23456; // requires resampling
@@ -554,34 +555,36 @@
final int bytesPerFrame = numChannels * bytesPerSample;
// careful about integer overflow in the formula below:
final int targetFrames =
- (int)((long)RECORD_TIME_IN_MS * RECORD_SAMPLE_RATE / 1000);
+ (int)((long)RECORD_TIME_MS * RECORD_SAMPLE_RATE / 1000);
final int targetSamples = targetFrames * numChannels;
final int BUFFER_FRAMES = 512;
final int BUFFER_SAMPLES = BUFFER_FRAMES * numChannels;
final int tries = 2;
for (int i = 0; i < tries; ++i) {
- long startTime = System.nanoTime();
- long startTimeBoot = android.os.SystemClock.elapsedRealtimeNanos();
+ final long trackStartTimeNs = System.nanoTime();
+ final long trackStartTimeBootNs = android.os.SystemClock.elapsedRealtimeNanos();
record.startRecording();
- AudioTimestamp startTs = new AudioTimestamp();
+ final AudioTimestamp ts = new AudioTimestamp();
int samplesRead = 0;
- boolean timestampRead = false;
// For 16 bit data, use shorts
- short[] shortData = new short[BUFFER_SAMPLES];
+ final short[] shortData = new short[BUFFER_SAMPLES];
+ final AudioHelper.TimestampVerifier tsVerifier =
+ new AudioHelper.TimestampVerifier(TAG, RECORD_SAMPLE_RATE);
+
while (samplesRead < targetSamples) {
- int amount = samplesRead == 0 ? numChannels :
- Math.min(BUFFER_SAMPLES, targetSamples - samplesRead);
- int ret = record.read(shortData, 0, amount);
- assertEquals(TEST_NAME, amount, ret);
+ final int amount = samplesRead == 0 ? numChannels :
+ Math.min(BUFFER_SAMPLES, targetSamples - samplesRead);
+ final int ret = record.read(shortData, 0, amount);
+ assertEquals("read incorrect amount", amount, ret);
// timestamps follow a different path than data, so it is conceivable
// that first data arrives before the first timestamp is ready.
- if (!timestampRead) {
- timestampRead =
- record.getTimestamp(startTs, AudioTimestamp.TIMEBASE_MONOTONIC)
- == AudioRecord.SUCCESS;
+
+ if (record.getTimestamp(ts, AudioTimestamp.TIMEBASE_MONOTONIC)
+ == AudioRecord.SUCCESS) {
+ tsVerifier.add(ts);
}
samplesRead += ret;
}
@@ -606,9 +609,10 @@
assertEquals(stopTs.framePosition, stopTsBoot.framePosition);
assertTrue(stopTs.framePosition >= targetFrames);
- assertTrue(stopTs.nanoTime - startTime > RECORD_TIME_IN_NANOS);
- assertTrue(stopTsBoot.nanoTime - startTimeBoot > RECORD_TIME_IN_NANOS);
- verifyContinuousTimestamps(startTs, stopTs, RECORD_SAMPLE_RATE);
+ assertTrue(stopTs.nanoTime - trackStartTimeNs > RECORD_TIME_NS);
+ assertTrue(stopTsBoot.nanoTime - trackStartTimeBootNs > RECORD_TIME_NS);
+
+ tsVerifier.verifyAndLog(trackStartTimeNs, "test_timestamp" /* logName */);
}
} finally {
if (record != null) {
@@ -969,16 +973,14 @@
mAudioRecord.startRecording();
assertEquals(AudioRecord.RECORDSTATE_RECORDING, mAudioRecord.getRecordingState());
- try {
- Thread.sleep(RECORD_DURATION_MS);
- } catch (InterruptedException e) {
- }
+ callback.await(TEST_TIMING_TOLERANCE_MS);
assertTrue(callback.mCalled);
assertTrue(callback.mConfigs.size() <= 1);
if (callback.mConfigs.size() == 1) {
checkRecordingConfig(callback.mConfigs.get(0));
}
+ Thread.sleep(RECORD_DURATION_MS);
mAudioRecord.unregisterAudioRecordingCallback(callback);
}
diff --git a/tests/tests/media/src/android/media/cts/AudioRecordingConfigurationTest.java b/tests/tests/media/src/android/media/cts/AudioRecordingConfigurationTest.java
index 4258c03..c285be7 100644
--- a/tests/tests/media/src/android/media/cts/AudioRecordingConfigurationTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioRecordingConfigurationTest.java
@@ -34,6 +34,8 @@
import java.lang.reflect.Method;
import java.util.ArrayList;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
import java.util.Iterator;
import java.util.List;
@@ -174,7 +176,7 @@
assertEquals(AudioRecord.STATE_INITIALIZED, mAudioRecord.getState());
mAudioRecord.startRecording();
assertEquals(AudioRecord.RECORDSTATE_RECORDING, mAudioRecord.getRecordingState());
- Thread.sleep(TEST_TIMING_TOLERANCE_MS);
+ callback.await(TEST_TIMING_TOLERANCE_MS);
assertTrue("AudioRecordingCallback not called after start", callback.mCalled);
Thread.sleep(TEST_TIMING_TOLERANCE_MS);
@@ -193,16 +195,17 @@
// stopping recording: callback is called with no match
callback.reset();
mAudioRecord.stop();
- Thread.sleep(SLEEP_AFTER_STOP_FOR_INACTIVITY_MS);
+ callback.await(TEST_TIMING_TOLERANCE_MS);
assertTrue("AudioRecordingCallback not called after stop", callback.mCalled);
assertEquals("Should not have found record configurations", callback.mConfigs.size(),
0);
+ Thread.sleep(SLEEP_AFTER_STOP_FOR_INACTIVITY_MS);
// unregister callback and start recording again
am.unregisterAudioRecordingCallback(callback);
callback.reset();
mAudioRecord.startRecording();
- Thread.sleep(TEST_TIMING_TOLERANCE_MS);
+ callback.await(TEST_TIMING_TOLERANCE_MS);
assertFalse("Unregistered callback was called", callback.mCalled);
// just call the callback once directly so it's marked as tested
@@ -249,25 +252,36 @@
}
static class MyAudioRecordingCallback extends AudioManager.AudioRecordingCallback {
- boolean mCalled = false;
+ boolean mCalled;
List<AudioRecordingConfiguration> mConfigs;
- final int mTestSource;
- final int mTestSession;
+ private final int mTestSource;
+ private final int mTestSession;
+ private CountDownLatch mCountDownLatch;
void reset() {
+ mCountDownLatch = new CountDownLatch(1);
mCalled = false;
- mConfigs = null;
+ mConfigs = new ArrayList<AudioRecordingConfiguration>();
}
MyAudioRecordingCallback(int session, int source) {
mTestSource = source;
mTestSession = session;
+ reset();
}
@Override
public void onRecordingConfigChanged(List<AudioRecordingConfiguration> configs) {
mCalled = true;
mConfigs = configs;
+ mCountDownLatch.countDown();
+ }
+
+ void await(long timeoutMs) {
+ try {
+ mCountDownLatch.await(timeoutMs, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ }
}
}
diff --git a/tests/tests/media/src/android/media/cts/AudioTrackTest.java b/tests/tests/media/src/android/media/cts/AudioTrackTest.java
index 01c7919..d52dc16 100644
--- a/tests/tests/media/src/android/media/cts/AudioTrackTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioTrackTest.java
@@ -58,7 +58,6 @@
private final long WAIT_MSEC = 200;
private final int OFFSET_DEFAULT = 0;
private final int OFFSET_NEGATIVE = -10;
- private static final String REPORT_LOG_NAME = "CtsMediaTestCases";
private void log(String testName, String message) {
Log.v(TAG, "[" + testName + "] " + message);
@@ -2126,13 +2125,11 @@
private void doTestTimestamp(int sampleRate, int channelMask, int encoding, int transferMode,
String streamName) throws Exception {
// constants for test
- final String TEST_NAME = "testGetTimestamp";
final int TEST_LOOP_CNT = 10;
final int TEST_BUFFER_MS = 100;
final int TEST_USAGE = AudioAttributes.USAGE_MEDIA;
- // For jitter we allow 30 msec in frames. This is a large margin.
- // Often this is just 0 or 1 frames, but that can depend on hardware.
- final int TEST_JITTER_FRAMES_ALLOWED = sampleRate * 30 / 1000;
+
+ final int MILLIS_PER_SECOND = 1000;
// -------- initialization --------------
final int frameSize =
@@ -2161,124 +2158,74 @@
.setTransferMode(transferMode)
.build();
assertEquals(AudioTrack.STATE_INITIALIZED, track.getState());
- // We generally use a transfer size of 100ms for testing, but in rare cases
- // (e.g. Bluetooth) this needs to be larger to exceed the internal track buffer.
- final int frameCount =
- Math.max(track.getBufferCapacityInFrames(), sampleRate * TEST_BUFFER_MS / 1000);
- track.play();
- ByteBuffer data = ByteBuffer.allocate(frameCount * frameSize);
- data.order(java.nio.ByteOrder.nativeOrder()).limit(frameCount * frameSize);
- AudioTimestamp timestamp = new AudioTimestamp();
- long framesWritten = 0, lastFramesPresented = 0, lastFramesPresentedAt = 0;
- int cumulativeJitterCount = 0;
- int differentials = 0;
- float cumulativeJitter = 0;
- float maxJitter = 0;
- for (int i = 0; i < TEST_LOOP_CNT; i++) {
- final long writeTime = System.nanoTime();
+ try {
+ // We generally use a transfer size of 100ms for testing, but in rare cases
+ // (e.g. Bluetooth) this needs to be larger to exceed the internal track buffer.
+ final int frameCount =
+ Math.max(track.getBufferCapacityInFrames(),
+ sampleRate * TEST_BUFFER_MS / MILLIS_PER_SECOND);
+ track.play();
- data.position(0);
- assertEquals(data.limit(),
- track.write(data, data.limit(), AudioTrack.WRITE_BLOCKING));
- assertEquals(data.position(), data.limit());
- framesWritten += data.limit() / frameSize;
+ // Android nanoTime implements MONOTONIC, same as our audio timestamps.
+ final long trackStartTimeNs = System.nanoTime();
- // track.getTimestamp may return false if there are no physical HAL outputs.
- // This may occur on TV devices without connecting an HDMI monitor.
- // It may also be true immediately after start-up, as the mixing thread could
- // be idle, but since we've already pushed much more than the minimum buffer size,
- // that is unlikely.
- // Nevertheless, we don't want to have unnecessary failures, so we ignore the
- // first iteration if we don't get a timestamp.
- final boolean result = track.getTimestamp(timestamp);
- assertTrue("timestamp could not be read", result || i == 0);
- if (!result) {
- continue;
- }
+ final ByteBuffer data = ByteBuffer.allocate(frameCount * frameSize);
+ data.order(java.nio.ByteOrder.nativeOrder()).limit(frameCount * frameSize);
+ final AudioTimestamp timestamp = new AudioTimestamp();
- final long framesPresented = timestamp.framePosition;
- final long framesPresentedAt = timestamp.nanoTime;
+ long framesWritten = 0;
+ final AudioHelper.TimestampVerifier tsVerifier =
+ new AudioHelper.TimestampVerifier(TAG, sampleRate);
+ for (int i = 0; i < TEST_LOOP_CNT; ++i) {
+ final long trackWriteTimeNs = System.nanoTime();
- // We read timestamp here to ensure that seen is greater than presented.
- // This is an "on-the-fly" read without pausing because pausing may cause the
- // timestamp to become stale and affect our jitter measurements.
- final int framesSeen = track.getPlaybackHeadPosition();
- assertTrue("server frames ahead of client frames", framesWritten >= framesSeen);
- assertTrue("presented frames ahead of server frames", framesSeen >= framesPresented);
+ data.position(0);
+ assertEquals("write did not complete",
+ data.limit(), track.write(data, data.limit(), AudioTrack.WRITE_BLOCKING));
+ assertEquals("write did not fill buffer",
+ data.position(), data.limit());
+ framesWritten += data.limit() / frameSize;
- if (i > 0) { // need delta info from previous iteration (skipping first)
- final long deltaFrames = framesPresented - lastFramesPresented;
- final long deltaTime = framesPresentedAt - lastFramesPresentedAt;
- final long NANOSECONDS_PER_SECOND = 1000000000;
- final long expectedFrames = deltaTime * sampleRate / NANOSECONDS_PER_SECOND;
- final long jitterFrames = Math.abs(deltaFrames - expectedFrames);
-
- Log.d(TAG, "framesWritten(" + framesWritten
- + ") framesSeen(" + framesSeen
- + ") framesPresented(" + framesPresented
- + ") framesPresentedAt(" + framesPresentedAt
- + ") lastframesPresented(" + lastFramesPresented
- + ") lastFramesPresentedAt(" + lastFramesPresentedAt
- + ") deltaFrames(" + deltaFrames
- + ") deltaTime(" + deltaTime
- + ") expectedFrames(" + expectedFrames
- + ") writeTime(" + writeTime
- + ") jitter(" + jitterFrames + ")");
- assertTrue("timestamp time should be increasing", deltaTime >= 0);
- assertTrue("timestamp frames should be increasing", deltaFrames >= 0);
-
- // the first nonzero value may have a jump, wait for the second.
- if (lastFramesPresented != 0) {
- if (differentials++ > 1) {
- // We check that the timestamp position is reasonably accurate.
- assertTrue("jitterFrames(" + jitterFrames + ") < "
- + TEST_JITTER_FRAMES_ALLOWED,
- jitterFrames < TEST_JITTER_FRAMES_ALLOWED);
- cumulativeJitter += jitterFrames;
- cumulativeJitterCount++;
- if (jitterFrames > maxJitter) {
- maxJitter = jitterFrames;
- }
- }
- final long NANOS_PER_SECOND = 1000000000;
- final long closeTimeNs = frameCount * 2 * NANOS_PER_SECOND / sampleRate;
- // We check that the timestamp time is reasonably current.
- assertTrue("framesPresentedAt(" + framesPresentedAt
- + ") close to writeTime(" + writeTime
- + ") tolerance(" + closeTimeNs
- + ")", Math.abs(framesPresentedAt - writeTime) <= closeTimeNs);
- assertTrue("timestamps must have causal time",
- writeTime >= lastFramesPresentedAt);
+ // track.getTimestamp may return false if there are no physical HAL outputs.
+ // This may occur on TV devices without connecting an HDMI monitor.
+ // It may also be true immediately after start-up, as the mixing thread could
+ // be idle, but since we've already pushed much more than the minimum buffer size,
+ // that is unlikely.
+ // Nevertheless, we don't want to have unnecessary failures, so we ignore the
+ // first iteration if we don't get a timestamp.
+ final boolean result = track.getTimestamp(timestamp);
+ assertTrue("timestamp could not be read", result || i == 0);
+ if (!result) {
+ continue;
}
- }
- lastFramesPresented = framesPresented;
- lastFramesPresentedAt = framesPresentedAt;
- }
- // Full drain.
- Thread.sleep(1000 /* millis */);
- // check that we are really at the end of playback.
- assertTrue("timestamp should be valid while draining", track.getTimestamp(timestamp));
- // fast tracks and sw emulated tracks may not fully drain. we log the status here.
- if (framesWritten != timestamp.framePosition) {
- Log.d(TAG, "timestamp should fully drain. written: "
- + framesWritten + " position: " + timestamp.framePosition);
- }
- assertTrue("sufficient nonzero timestamps", differentials > 2);
- track.release();
- // Log the average jitter
- if (cumulativeJitterCount > 0) {
- DeviceReportLog log = new DeviceReportLog(REPORT_LOG_NAME, streamName);
- final float averageJitterInFrames = cumulativeJitter / cumulativeJitterCount;
- final float averageJitterInMs = averageJitterInFrames * 1000 / sampleRate;
- final float maxJitterInMs = maxJitter * 1000 / sampleRate;
- // ReportLog needs at least one Value and Summary.
- log.addValue("maximum_jitter", maxJitterInMs,
- ResultType.LOWER_BETTER, ResultUnit.MS);
- log.setSummary("average_jitter", averageJitterInMs,
- ResultType.LOWER_BETTER, ResultUnit.MS);
- log.submit(InstrumentationRegistry.getInstrumentation());
+ tsVerifier.add(timestamp);
+
+ // Ensure that seen is greater than presented.
+ // This is an "on-the-fly" read without pausing because pausing may cause the
+ // timestamp to become stale and affect our jitter measurements.
+ final long framesPresented = timestamp.framePosition;
+ final int framesSeen = track.getPlaybackHeadPosition();
+ assertTrue("server frames ahead of client frames",
+ framesWritten >= framesSeen);
+ assertTrue("presented frames ahead of server frames",
+ framesSeen >= framesPresented);
+ }
+ // Full drain.
+ Thread.sleep(1000 /* millis */);
+ // check that we are really at the end of playback.
+ assertTrue("timestamp should be valid while draining", track.getTimestamp(timestamp));
+ // fast tracks and sw emulated tracks may not fully drain. we log the status here.
+ if (framesWritten != timestamp.framePosition) {
+ Log.d(TAG, "timestamp should fully drain. written: "
+ + framesWritten + " position: " + timestamp.framePosition);
+ }
+
+ tsVerifier.verifyAndLog(trackStartTimeNs, streamName);
+
+ } finally {
+ track.release();
}
}
diff --git a/tests/tests/media/src/android/media/cts/CodecState.java b/tests/tests/media/src/android/media/cts/CodecState.java
index c5570c5..5f1d322 100644
--- a/tests/tests/media/src/android/media/cts/CodecState.java
+++ b/tests/tests/media/src/android/media/cts/CodecState.java
@@ -338,12 +338,12 @@
if (mAudioTrack != null) {
ByteBuffer buffer = mCodecOutputBuffers[index];
+ byte[] audioArray = new byte[info.size];
+ buffer.get(audioArray);
buffer.clear();
- ByteBuffer audioBuffer = ByteBuffer.allocate(buffer.remaining());
- audioBuffer.put(buffer);
- audioBuffer.clear();
- mAudioTrack.write(audioBuffer, info.size, info.presentationTimeUs*1000);
+ mAudioTrack.write(ByteBuffer.wrap(audioArray), info.size,
+ info.presentationTimeUs*1000);
mCodec.releaseOutputBuffer(index, false /* render */);
diff --git a/tests/tests/media/src/android/media/cts/DecoderTest.java b/tests/tests/media/src/android/media/cts/DecoderTest.java
old mode 100755
new mode 100644
index f1655a0..699df64
--- a/tests/tests/media/src/android/media/cts/DecoderTest.java
+++ b/tests/tests/media/src/android/media/cts/DecoderTest.java
@@ -156,14 +156,30 @@
decode(R.raw.sinesweepogg, 168.f);
testTimeStampOrdering(R.raw.sinesweepogg);
}
+ public void testDecodeOggMkv() throws Exception {
+ decode(R.raw.sinesweepoggmkv, 168.f);
+ testTimeStampOrdering(R.raw.sinesweepoggmkv);
+ }
+ public void testDecodeOggMp4() throws Exception {
+ decode(R.raw.sinesweepoggmp4, 168.f);
+ testTimeStampOrdering(R.raw.sinesweepoggmp4);
+ }
public void testDecodeWav() throws Exception {
decode(R.raw.sinesweepwav, 0.0f);
testTimeStampOrdering(R.raw.sinesweepwav);
}
+ public void testDecodeFlacMkv() throws Exception {
+ decode(R.raw.sinesweepflacmkv, 0.0f);
+ testTimeStampOrdering(R.raw.sinesweepflacmkv);
+ }
public void testDecodeFlac() throws Exception {
decode(R.raw.sinesweepflac, 0.0f);
testTimeStampOrdering(R.raw.sinesweepflac);
}
+ public void testDecodeFlacMp4() throws Exception {
+ decode(R.raw.sinesweepflacmp4, 0.0f);
+ testTimeStampOrdering(R.raw.sinesweepflacmp4);
+ }
public void testDecodeMonoMp3() throws Exception {
monoTest(R.raw.monotestmp3, 44100);
@@ -179,6 +195,14 @@
monoTest(R.raw.monotestogg, 44100);
testTimeStampOrdering(R.raw.monotestogg);
}
+ public void testDecodeMonoOggMkv() throws Exception {
+ monoTest(R.raw.monotestoggmkv, 44100);
+ testTimeStampOrdering(R.raw.monotestoggmkv);
+ }
+ public void testDecodeMonoOggMp4() throws Exception {
+ monoTest(R.raw.monotestoggmp4, 44100);
+ testTimeStampOrdering(R.raw.monotestoggmp4);
+ }
public void testDecodeMonoGsm() throws Exception {
if (MediaUtils.hasCodecsForResource(mContext, R.raw.monotestgsm)) {
@@ -196,10 +220,16 @@
public void testDecodeVorbis() throws Exception {
testTimeStampOrdering(R.raw.sinesweepvorbis);
}
+ public void testDecodeVorbisMp4() throws Exception {
+ testTimeStampOrdering(R.raw.sinesweepvorbismp4);
+ }
public void testDecodeOpus() throws Exception {
testTimeStampOrdering(R.raw.sinesweepopus);
}
+ public void testDecodeOpusMp4() throws Exception {
+ testTimeStampOrdering(R.raw.sinesweepopusmp4);
+ }
public void testDecode51M4a() throws Exception {
decodeToMemory(R.raw.sinesweep51m4a, RESET_MODE_NONE, CONFIG_MODE_NONE, -1, null);
@@ -721,6 +751,14 @@
staticInfo, true /*metadataInContainer*/);
}
+ public void testAV1HdrStaticMetadata() throws Exception {
+ final String staticInfo =
+ "00 d0 84 80 3e c2 33 c4 86 4c 1d b8 0b 13 3d 42" +
+ "40 e8 03 64 00 e8 03 2c 01 " ;
+ testHdrStaticMetadata(R.raw.video_1280x720_av1_hdr_static_3mbps,
+ staticInfo, false /*metadataInContainer*/);
+ }
+
public void testH265HDR10StaticMetadata() throws Exception {
// Expected value of MediaFormat.KEY_HDR_STATIC_INFO key.
// The associated value is a ByteBuffer. This buffer contains the raw contents of the
@@ -776,6 +814,11 @@
// it here so that we only test HDR when decoder supports it.
format.setInteger(MediaFormat.KEY_PROFILE,
MediaCodecInfo.CodecProfileLevel.VP9Profile2HDR);
+ } else if (MediaFormat.MIMETYPE_VIDEO_AV1.equals(mime)) {
+ // The muxer might not have put AV1 CSD in the webm, we manually patch
+ // it here so that we only test HDR when decoder supports it.
+ format.setInteger(MediaFormat.KEY_PROFILE,
+ MediaCodecInfo.CodecProfileLevel.AV1ProfileMain10HDR10);
} else {
fail("Codec " + mime + " shouldn't be tested with this test!");
}
@@ -1757,9 +1800,15 @@
testDecodeWithEOSOnLastBuffer(R.raw.sinesweepm4a);
testDecodeWithEOSOnLastBuffer(R.raw.sinesweepmp3lame);
testDecodeWithEOSOnLastBuffer(R.raw.sinesweepmp3smpb);
+ testDecodeWithEOSOnLastBuffer(R.raw.sinesweepopus);
+ testDecodeWithEOSOnLastBuffer(R.raw.sinesweepopusmp4);
testDecodeWithEOSOnLastBuffer(R.raw.sinesweepwav);
+ testDecodeWithEOSOnLastBuffer(R.raw.sinesweepflacmkv);
testDecodeWithEOSOnLastBuffer(R.raw.sinesweepflac);
+ testDecodeWithEOSOnLastBuffer(R.raw.sinesweepflacmp4);
testDecodeWithEOSOnLastBuffer(R.raw.sinesweepogg);
+ testDecodeWithEOSOnLastBuffer(R.raw.sinesweepoggmkv);
+ testDecodeWithEOSOnLastBuffer(R.raw.sinesweepoggmp4);
}
/* setting EOS on the last full input buffer should be equivalent to setting EOS on an empty
@@ -1849,6 +1898,10 @@
testDecode(R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_128kbps_22050hz, 122);
}
+ public void testCodecBasicMpeg2() throws Exception {
+ testDecode(R.raw.video_480x360_mp4_mpeg2_1500kbps_30fps_aac_stereo_128kbps_48000hz, 300);
+ }
+
public void testCodecBasicMpeg4() throws Exception {
testDecode(R.raw.video_480x360_mp4_mpeg4_860kbps_25fps_aac_stereo_128kbps_44100hz, 249);
}
@@ -1861,6 +1914,10 @@
testDecode(R.raw.video_480x360_webm_vp9_333kbps_25fps_vorbis_stereo_128kbps_48000hz, 240);
}
+ public void testCodecBasicAV1() throws Exception {
+ testDecode(R.raw.video_480x360_webm_av1_400kbps_30fps_vorbis_stereo_128kbps_48000hz, 300);
+ }
+
public void testH264Decode320x240() throws Exception {
testDecode(R.raw.bbb_s1_320x240_mp4_h264_mp2_800kbps_30fps_aac_lc_5ch_240kbps_44100hz, 300);
}
@@ -2056,6 +2113,36 @@
R.raw.bbb_s2_3840x2160_webm_vp9_0p51_20mbps_60fps_vorbis_6ch_384kbps_32000hz, 300);
}
+ public void testAV1Decode320x180() throws Exception {
+ testDecode(R.raw.video_320x180_webm_av1_200kbps_30fps_vorbis_stereo_128kbps_48000hz, 300);
+ }
+
+ public void testAV1Decode640x360() throws Exception {
+ testDecode(
+ R.raw.video_640x360_webm_av1_470kbps_30fps_vorbis_stereo_128kbps_48000hz,
+ 300);
+ }
+
+ public void testAV1Decode30fps1280x720() throws Exception {
+ testDecode(
+ R.raw.video_1280x720_webm_av1_2000kbps_30fps_vorbis_stereo_128kbps_48000hz, 300);
+ }
+
+ public void testAV1Decode60fps1920x1080() throws Exception {
+ testDecode(
+ R.raw.video_1920x1080_webm_av1_7000kbps_60fps_vorbis_stereo_128kbps_48000hz, 300);
+ }
+
+ public void testAV1Decode30fps3840x2160() throws Exception {
+ testDecode(
+ R.raw.video_3840x2160_webm_av1_11000kbps_30fps_vorbis_stereo_128kbps_48000hz, 150);
+ }
+
+ public void testAV1Decode60fps3840x2160() throws Exception {
+ testDecode(
+ R.raw.video_3840x2160_webm_av1_18000kbps_60fps_vorbis_stereo_128kbps_48000hz, 300);
+ }
+
public void testHEVCDecode352x288() throws Exception {
testDecode(
R.raw.bbb_s1_352x288_mp4_hevc_mp2_600kbps_30fps_aac_he_stereo_96kbps_48000hz, 300);
@@ -2102,6 +2189,38 @@
R.raw.bbb_s2_3840x2160_mp4_hevc_mp51_20mbps_60fps_aac_lc_6ch_384kbps_32000hz, 300);
}
+ public void testMpeg2Decode352x288() throws Exception {
+ testDecode(R.raw.video_352x288_mp4_mpeg2_1000kbps_30fps_aac_stereo_128kbps_48000hz, 300);
+ }
+
+ public void testMpeg2Decode720x480() throws Exception {
+ testDecode(R.raw.video_720x480_mp4_mpeg2_2000kbps_30fps_aac_stereo_128kbps_48000hz, 300);
+ }
+
+ public void testMpeg2Decode30fps1280x720Tv() throws Exception {
+ if (checkTv()) {
+ assertTrue(MediaUtils.canDecodeVideo(MediaFormat.MIMETYPE_VIDEO_MPEG2, 1280, 720, 30));
+ }
+ }
+
+ public void testMpeg2Decode30fps1280x720() throws Exception {
+ testDecode(R.raw.video_1280x720_mp4_mpeg2_6000kbps_30fps_aac_stereo_128kbps_48000hz, 150);
+ }
+
+ public void testMpeg2Decode30fps1920x1080Tv() throws Exception {
+ if (checkTv()) {
+ assertTrue(MediaUtils.canDecodeVideo(MediaFormat.MIMETYPE_VIDEO_MPEG2, 1920, 1080, 30));
+ }
+ }
+
+ public void testMpeg2Decode30fps1920x1080() throws Exception {
+ testDecode(R.raw.video_1920x1080_mp4_mpeg2_12000kbps_30fps_aac_stereo_128kbps_48000hz, 150);
+ }
+
+ public void testMpeg2Decode30fps3840x2160() throws Exception {
+ testDecode(R.raw.video_3840x2160_mp4_mpeg2_20000kbps_30fps_aac_stereo_128kbps_48000hz, 150);
+ }
+
private void testCodecEarlyEOS(int resid, int eosFrame) throws Exception {
if (!MediaUtils.checkCodecForResource(mContext, resid, 0 /* track */)) {
return; // skip
@@ -2129,6 +2248,12 @@
120 /* eosframe */);
}
+ public void testCodecEarlyEOSMpeg2() throws Exception {
+ testCodecEarlyEOS(
+ R.raw.video_480x360_mp4_mpeg2_1500kbps_30fps_aac_stereo_128kbps_48000hz,
+ 120 /* eosframe */);
+ }
+
public void testCodecEarlyEOSMpeg4() throws Exception {
testCodecEarlyEOS(
R.raw.video_480x360_mp4_mpeg4_860kbps_25fps_aac_stereo_128kbps_44100hz,
@@ -2147,6 +2272,12 @@
120 /* eosframe */);
}
+ public void testCodecEarlyEOSAV1() throws Exception {
+ testCodecEarlyEOS(
+ R.raw.video_480x360_webm_av1_400kbps_30fps_vorbis_stereo_128kbps_48000hz,
+ 120 /* eosframe */);
+ }
+
public void testCodecResetsH264WithoutSurface() throws Exception {
testCodecResets(
R.raw.video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz, null);
@@ -2169,6 +2300,17 @@
R.raw.bbb_s1_720x480_mp4_hevc_mp3_1600kbps_30fps_aac_he_6ch_240kbps_48000hz, s);
}
+ public void testCodecResetsMpeg2WithoutSurface() throws Exception {
+ testCodecResets(
+ R.raw.video_1280x720_mp4_mpeg2_6000kbps_30fps_aac_stereo_128kbps_48000hz, null);
+ }
+
+ public void testCodecResetsMpeg2WithSurface() throws Exception {
+ Surface s = getActivity().getSurfaceHolder().getSurface();
+ testCodecResets(
+ R.raw.video_176x144_mp4_mpeg2_105kbps_25fps_aac_stereo_128kbps_44100hz, s);
+ }
+
public void testCodecResetsH263WithoutSurface() throws Exception {
testCodecResets(
R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_128kbps_22050hz, null);
@@ -2207,12 +2349,23 @@
R.raw.video_480x360_webm_vp9_333kbps_25fps_vorbis_stereo_128kbps_48000hz, null);
}
+ public void testCodecResetsAV1WithoutSurface() throws Exception {
+ testCodecResets(
+ R.raw.video_480x360_webm_av1_400kbps_30fps_vorbis_stereo_128kbps_48000hz, null);
+ }
+
public void testCodecResetsVP9WithSurface() throws Exception {
Surface s = getActivity().getSurfaceHolder().getSurface();
testCodecResets(
R.raw.video_480x360_webm_vp9_333kbps_25fps_vorbis_stereo_128kbps_48000hz, s);
}
+ public void testCodecResetsAV1WithSurface() throws Exception {
+ Surface s = getActivity().getSurfaceHolder().getSurface();
+ testCodecResets(
+ R.raw.video_480x360_webm_av1_400kbps_30fps_vorbis_stereo_128kbps_48000hz, s);
+ }
+
// public void testCodecResetsOgg() throws Exception {
// testCodecResets(R.raw.sinesweepogg, null);
// }
@@ -2666,6 +2819,12 @@
new int[] {1, 17, 23, 49});
}
+ public void testEOSBehaviorMpeg2() throws Exception {
+ testEOSBehavior(R.raw.video_480x360_mp4_mpeg2_1500kbps_30fps_aac_stereo_128kbps_48000hz, 17);
+ testEOSBehavior(R.raw.video_480x360_mp4_mpeg2_1500kbps_30fps_aac_stereo_128kbps_48000hz, 23);
+ testEOSBehavior(R.raw.video_480x360_mp4_mpeg2_1500kbps_30fps_aac_stereo_128kbps_48000hz, 49);
+ }
+
public void testEOSBehaviorH263() throws Exception {
// this video has an I frame every 12 frames.
testEOSBehavior(
@@ -2694,6 +2853,13 @@
new int[] {1, 44, 45, 55, 43});
}
+ public void testEOSBehaviorAV1() throws Exception {
+ // this video has an I frame at 44
+ testEOSBehavior(
+ R.raw.video_480x360_webm_av1_400kbps_30fps_vorbis_stereo_128kbps_48000hz,
+ new int[] {1, 44, 45, 55, 43});
+ }
+
/* from EncodeDecodeTest */
private static boolean isRecognizedFormat(int colorFormat) {
// Log.d(TAG, "color format: " + String.format("0x%08x", colorFormat));
@@ -2854,6 +3020,8 @@
public void testFlush() throws Exception {
testFlush(R.raw.loudsoftwav);
testFlush(R.raw.loudsoftogg);
+ testFlush(R.raw.loudsoftoggmkv);
+ testFlush(R.raw.loudsoftoggmp4);
testFlush(R.raw.loudsoftmp3);
testFlush(R.raw.loudsoftaac);
testFlush(R.raw.loudsoftfaac);
@@ -3048,6 +3216,9 @@
List<CodecCapabilities> caps = new ArrayList<CodecCapabilities>();
for (int i = 0; i < numCodecs; i++) {
MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i);
+ if (codecInfo.isAlias()) {
+ continue;
+ }
if (codecInfo.isEncoder()) {
continue;
}
diff --git a/tests/tests/media/src/android/media/cts/DecoderTestXheAac.java b/tests/tests/media/src/android/media/cts/DecoderTestXheAac.java
index 7da538a..4a2b9db 100755
--- a/tests/tests/media/src/android/media/cts/DecoderTestXheAac.java
+++ b/tests/tests/media/src/android/media/cts/DecoderTestXheAac.java
@@ -71,6 +71,9 @@
final MediaCodecList mediaCodecList = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
final MediaCodecInfo[] mediaCodecInfos = mediaCodecList.getCodecInfos();
for (MediaCodecInfo mediaCodecInfo : mediaCodecInfos) {
+ if (mediaCodecInfo.isAlias()) {
+ continue;
+ }
if (mediaCodecInfo.isEncoder()) {
continue;
}
diff --git a/tests/tests/media/src/android/media/cts/ExtractDecodeEditEncodeMuxTest.java b/tests/tests/media/src/android/media/cts/ExtractDecodeEditEncodeMuxTest.java
index 6acd5b6..dd7131b 100644
--- a/tests/tests/media/src/android/media/cts/ExtractDecodeEditEncodeMuxTest.java
+++ b/tests/tests/media/src/android/media/cts/ExtractDecodeEditEncodeMuxTest.java
@@ -1216,6 +1216,9 @@
for (int i = 0; i < numCodecs; i++) {
MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i);
+ if (codecInfo.isAlias()) {
+ continue;
+ }
if (!codecInfo.isEncoder()) {
continue;
}
diff --git a/tests/tests/media/src/android/media/cts/ImageReaderDecoderTest.java b/tests/tests/media/src/android/media/cts/ImageReaderDecoderTest.java
index d31a73b..cecb4d0 100644
--- a/tests/tests/media/src/android/media/cts/ImageReaderDecoderTest.java
+++ b/tests/tests/media/src/android/media/cts/ImageReaderDecoderTest.java
@@ -323,7 +323,7 @@
ArrayList<Decoder> result = new ArrayList<Decoder>();
for (MediaCodecInfo info : mcl.getCodecInfos()) {
- if (info.isEncoder() || MediaUtils.isGoogle(info.getName()) != goog) {
+ if (info.isEncoder() || info.isAlias() || !info.isVendor() != goog) {
continue;
}
CodecCapabilities caps = null;
diff --git a/tests/tests/media/src/android/media/cts/MediaCodecCapabilitiesTest.java b/tests/tests/media/src/android/media/cts/MediaCodecCapabilitiesTest.java
index ad0aa36..25829c5 100644
--- a/tests/tests/media/src/android/media/cts/MediaCodecCapabilitiesTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaCodecCapabilitiesTest.java
@@ -735,6 +735,7 @@
type.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_OPUS ) ||
type.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_RAW ) ||
type.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_VORBIS ) ||
+ type.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_AV1 ) ||
type.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_AVC ) ||
type.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_H263 ) ||
type.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_HEVC ) ||
diff --git a/tests/tests/media/src/android/media/cts/MediaCodecClearKeyPlayer.java b/tests/tests/media/src/android/media/cts/MediaCodecClearKeyPlayer.java
index 9723684..e3ef811 100644
--- a/tests/tests/media/src/android/media/cts/MediaCodecClearKeyPlayer.java
+++ b/tests/tests/media/src/android/media/cts/MediaCodecClearKeyPlayer.java
@@ -437,7 +437,11 @@
return format.containsKey(key) ? format.getInteger(key) : 0;
}
+ // Find first secure decoder for media type. If none found, return
+ // the name of the first regular codec with ".secure" suffix added.
+ // If all else fails, return null.
protected String getSecureDecoderNameForMime(String mime) {
+ String firstDecoderName = null;
int n = MediaCodecList.getCodecCount();
for (int i = 0; i < n; ++i) {
MediaCodecInfo info = MediaCodecList.getCodecInfoAt(i);
@@ -450,10 +454,18 @@
for (int j = 0; j < supportedTypes.length; ++j) {
if (supportedTypes[j].equalsIgnoreCase(mime)) {
- return info.getName() + ".secure";
+ if (info.getCapabilitiesForType(mime).isFeatureSupported(
+ MediaCodecInfo.CodecCapabilities.FEATURE_AdaptivePlayback)) {
+ return info.getName();
+ } else if (firstDecoderName == null) {
+ firstDecoderName = info.getName();
+ }
}
}
}
+ if (firstDecoderName != null) {
+ return firstDecoderName + ".secure";
+ }
return null;
}
diff --git a/tests/tests/media/src/android/media/cts/MediaCodecListTest.java b/tests/tests/media/src/android/media/cts/MediaCodecListTest.java
index 3c4f6fd..0ce4803 100644
--- a/tests/tests/media/src/android/media/cts/MediaCodecListTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaCodecListTest.java
@@ -19,6 +19,8 @@
import static android.media.MediaCodecInfo.CodecCapabilities.FEATURE_SecurePlayback;
import static android.media.MediaCodecInfo.CodecCapabilities.FEATURE_TunneledPlayback;
+import com.android.compatibility.common.util.PropertyUtil;
+
import android.content.pm.PackageManager;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
@@ -31,6 +33,7 @@
import android.platform.test.annotations.RequiresDevice;
import android.test.AndroidTestCase;
import android.util.Log;
+import android.util.Pair;
import android.util.Size;
import androidx.test.filters.SmallTest;
@@ -101,6 +104,10 @@
}
}
+ public static boolean hasExpandedCodecInfo() {
+ return PropertyUtil.isVendorApiLevelNewerThan(29);
+ }
+
public static void testMediaCodecXmlFileExist() {
File file = new File(MEDIA_CODEC_XML_FILE);
File vendorFile = new File(VENDOR_MEDIA_CODEC_XML_FILE);
@@ -712,15 +719,82 @@
.covers(new VideoCapabilities.PerformancePoint(1280, 720, 68)));
}
- public void verifyPerformancePoints(
+ private void verifyPerformancePoints(
MediaCodecInfo info, String mediaType,
List<VideoCapabilities.PerformancePoint> points) {
+ List<VideoCapabilities.PerformancePoint> standardPoints = Arrays.asList(
+ VideoCapabilities.PerformancePoint.UHD_240,
+ VideoCapabilities.PerformancePoint.UHD_200,
+ VideoCapabilities.PerformancePoint.UHD_120,
+ VideoCapabilities.PerformancePoint.UHD_100,
+ VideoCapabilities.PerformancePoint.UHD_60,
+ VideoCapabilities.PerformancePoint.UHD_50,
+ VideoCapabilities.PerformancePoint.UHD_30,
+ VideoCapabilities.PerformancePoint.UHD_25,
+ VideoCapabilities.PerformancePoint.UHD_24,
+ VideoCapabilities.PerformancePoint.FHD_240,
+ VideoCapabilities.PerformancePoint.FHD_200,
+ VideoCapabilities.PerformancePoint.FHD_120,
+ VideoCapabilities.PerformancePoint.FHD_100,
+ VideoCapabilities.PerformancePoint.FHD_60,
+ VideoCapabilities.PerformancePoint.FHD_50,
+ VideoCapabilities.PerformancePoint.FHD_30,
+ VideoCapabilities.PerformancePoint.FHD_25,
+ VideoCapabilities.PerformancePoint.FHD_24,
+ VideoCapabilities.PerformancePoint.HD_240,
+ VideoCapabilities.PerformancePoint.HD_200,
+ VideoCapabilities.PerformancePoint.HD_120,
+ VideoCapabilities.PerformancePoint.HD_100,
+ VideoCapabilities.PerformancePoint.HD_60,
+ VideoCapabilities.PerformancePoint.HD_50,
+ VideoCapabilities.PerformancePoint.HD_30,
+ VideoCapabilities.PerformancePoint.HD_25,
+ VideoCapabilities.PerformancePoint.HD_24,
+ VideoCapabilities.PerformancePoint.SD_60,
+ VideoCapabilities.PerformancePoint.SD_50,
+ VideoCapabilities.PerformancePoint.SD_48,
+ VideoCapabilities.PerformancePoint.SD_30,
+ VideoCapabilities.PerformancePoint.SD_25,
+ VideoCapabilities.PerformancePoint.SD_24);
+
// Components must list all supported standard performance points unless those performance
// points are covered by other listed standard performance points.
-
-
- // TODO: verify performance points listed conform to the requirements ... once those
- // requirements are agreed upon.
+ for (VideoCapabilities.PerformancePoint pp : points) {
+ if (standardPoints.contains(pp)) {
+ // standard points must not be covered by other listed standard points
+ for (VideoCapabilities.PerformancePoint pp2 : points) {
+ if (!standardPoints.contains(pp2)) {
+ continue;
+ }
+ // using object equality to determine otherness
+ assertFalse("standard " + pp2 + " for " + info.getCanonicalName()
+ + " for media type " + mediaType + " covers standard " + pp,
+ pp2 != pp && pp2.covers(pp));
+ }
+ } else {
+ // non-standard points must list all covered standard point not covered by another
+ // listed standard point
+ for (VideoCapabilities.PerformancePoint spp : standardPoints) {
+ if (pp.covers(spp)) {
+ // Must be either listed or covered by another standard. Since a point
+ // covers itself, it is sufficient to check that it is covered by a listed
+ // standard point.
+ boolean covered = false;
+ for (VideoCapabilities.PerformancePoint pp2 : points) {
+ // using object equality to determine otherness
+ if (standardPoints.contains(pp2) && pp2.covers(spp)) {
+ covered = true;
+ break;
+ }
+ }
+ assertTrue(pp + " for " + info.getCanonicalName() + " for media type "
+ + mediaType + " covers standard " + spp
+ + " that is not covered by a listed standard point",
+ covered);
+ }
+ }
+ }
+ }
}
public void testAllHardwareAcceleratedVideoCodecsPublishPerformancePoints() {
@@ -737,20 +811,46 @@
FEATURE_TunneledPlayback,
};
+ Set<Pair<String, Integer>> describedTypes = new HashSet<>(); // mediaType - featureIndex
+ Set<Pair<String, Integer>> supportedTypes = new HashSet<>(); // mediaType - featureIndex
+
+ // Once any hardware codec performance is described, we assume that all hardware codecs
+ // must be described, even if we cannot confirm expanded codec info support.
+ boolean hasPerformancePoints = hasExpandedCodecInfo();
+ if (!hasPerformancePoints) {
+ for (MediaCodecInfo info : mAllInfos) {
+ String[] types = info.getSupportedTypes();
+ for (int j = 0; j < types.length; ++j) {
+ String mediaType = types[j];
+ CodecCapabilities cap = info.getCapabilitiesForType(mediaType);
+ VideoCapabilities videoCap = cap.getVideoCapabilities();
+ if (videoCap != null
+ && videoCap.getSupportedPerformancePoints() != null) {
+ hasPerformancePoints = true;
+ break;
+ }
+ }
+ if (hasPerformancePoints) {
+ break;
+ }
+ }
+ }
+
for (MediaCodecInfo info : mAllInfos) {
String[] types = info.getSupportedTypes();
for (int j = 0; j < types.length; ++j) {
- CodecCapabilities cap = info.getCapabilitiesForType(types[j]);
+ String mediaType = types[j];
+ CodecCapabilities cap = info.getCapabilitiesForType(mediaType);
MediaFormat defaultFormat = cap.getDefaultFormat();
VideoCapabilities videoCap = cap.getVideoCapabilities();
Log.d(TAG, "codec: " + info.getName() + " canonical: " + info.getCanonicalName()
- + " type: " + types[j]);
+ + " type: " + mediaType);
if (videoCap == null) {
- assertFalse("no video capabilities for video media type " + types[j] + " of "
+ assertFalse("no video capabilities for video media type " + mediaType + " of "
+ info.getName(),
- types[j].toLowerCase().startsWith("video/"));
+ mediaType.toLowerCase().startsWith("video/"));
continue;
}
@@ -775,16 +875,29 @@
supportedFeatureConfigs.add(cfg_ix);
}
}
- int[] supportedFeatureConfigsArray =
- supportedFeatureConfigs.stream().mapToInt(Integer::intValue).toArray();
- Log.d(TAG, "codec supports configs "
- + Arrays.toString(supportedFeatureConfigsArray));
+ Log.d(TAG, "codec supports configs " + Arrays.toString(
+ supportedFeatureConfigs.stream().mapToInt(Integer::intValue).toArray()));
+ boolean isMandatory = mandatoryTypes.contains(mediaType);
+ if (info.isHardwareAccelerated()) {
+ for (Integer cfg_ix : supportedFeatureConfigs) {
+ Pair<String, Integer> type = Pair.create(mediaType, cfg_ix);
+ if (hasPerformancePoints && isMandatory) {
+ supportedTypes.add(type);
+ }
+ if (pps != null && pps.size() > 0) {
+ describedTypes.add(type);
+ }
+ }
+ }
+
if (pps == null) {
- // Hardware-accelerated video components must publish performance points,
- // even if it is an empty list.
- assertFalse("HW-accelerated codec '" + info.getName()
- + "' must publish performance points", info.isHardwareAccelerated());
+ if (hasExpandedCodecInfo()) {
+ // Hardware-accelerated video components must publish performance points,
+ // even if it is an empty list.
+ assertFalse("HW-accelerated codec '" + info.getName()
+ + "' must publish performance points", info.isHardwareAccelerated());
+ }
continue;
}
@@ -792,7 +905,7 @@
// At least one hardware accelerated codec for each media type (including secure
// codecs) must publish valid performance points for AVC/VP8/VP9/HEVC/AV1.
if (pps.size() == 0) {
- if (mandatoryTypes.contains(types[j])) {
+ if (isMandatory) {
Log.d(TAG, "empty performance points list published by HW accelerated" +
"component " + info.getName() + " for " + types[j]);
}
@@ -800,8 +913,15 @@
for (VideoCapabilities.PerformancePoint p : pps) {
Log.d(TAG, "got performance point " + p);
}
+ verifyPerformancePoints(info, types[j], pps);
}
}
}
+
+ for (Pair<String, Integer> type : supportedTypes) {
+ assertTrue("codecs for media type " + type.first + " in configuration " + type.second
+ + " do not have substantial performance point data",
+ describedTypes.contains(type));
+ }
}
}
diff --git a/tests/tests/media/src/android/media/cts/MediaCodecTest.java b/tests/tests/media/src/android/media/cts/MediaCodecTest.java
index c61e5d7..3802110 100644
--- a/tests/tests/media/src/android/media/cts/MediaCodecTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaCodecTest.java
@@ -1750,10 +1750,7 @@
private static boolean supportsCodec(String mimeType, boolean encoder) {
MediaCodecList list = new MediaCodecList(MediaCodecList.ALL_CODECS);
for (MediaCodecInfo info : list.getCodecInfos()) {
- if (encoder && !info.isEncoder()) {
- continue;
- }
- if (!encoder && info.isEncoder()) {
+ if (encoder != info.isEncoder()) {
continue;
}
@@ -2051,13 +2048,10 @@
// check if float
final MediaFormat actualFormat =
encode ? mCodec.getInputFormat() : mCodec.getOutputFormat();
- Integer actualEncoding = null;
- try {
- actualEncoding = actualFormat.getInteger(MediaFormat.KEY_PCM_ENCODING);
- } catch (Exception e) {
- ; // trying to get a non-existent key throws exception
- }
- mIsFloat = actualEncoding != null && actualEncoding == AudioFormat.ENCODING_PCM_FLOAT;
+
+ mIsFloat = actualFormat.getInteger(
+ MediaFormat.KEY_PCM_ENCODING, AudioFormat.ENCODING_PCM_16BIT)
+ == AudioFormat.ENCODING_PCM_FLOAT;
mCodec.start();
}
diff --git a/tests/tests/media/src/android/media/cts/MediaExtractorTest.java b/tests/tests/media/src/android/media/cts/MediaExtractorTest.java
index e82ffcb..799e263 100644
--- a/tests/tests/media/src/android/media/cts/MediaExtractorTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaExtractorTest.java
@@ -28,6 +28,7 @@
import android.media.MediaFormat;
import android.media.cts.R;
import android.os.PersistableBundle;
+import android.platform.test.annotations.AppModeFull;
import android.test.AndroidTestCase;
import android.util.Log;
import android.webkit.cts.CtsTestServer;
@@ -272,6 +273,7 @@
}
}
+ @AppModeFull(reason = "Instant apps cannot bind sockets.")
public void testExtractorGetCachedDuration() throws Exception {
CtsTestServer foo = new CtsTestServer(getContext());
String url = foo.getAssetUrl("ringer.mp3");
@@ -416,13 +418,10 @@
}
}
assertNotNull("MediaExtractor cannot find mime type " + mime, mFormat);
- Integer actualEncoding = null;
- try {
- actualEncoding = mFormat.getInteger(MediaFormat.KEY_PCM_ENCODING);
- } catch (Exception e) {
- ; // trying to get a non-existent key throws exception
- }
- mIsFloat = actualEncoding != null && actualEncoding == AudioFormat.ENCODING_PCM_FLOAT;
+ mIsFloat = mFormat.getInteger(
+ MediaFormat.KEY_PCM_ENCODING, AudioFormat.ENCODING_PCM_16BIT)
+ == AudioFormat.ENCODING_PCM_FLOAT;
+
}
public MediaExtractorStream(
diff --git a/tests/tests/media/src/android/media/cts/MediaPlayerTest.java b/tests/tests/media/src/android/media/cts/MediaPlayerTest.java
index 721d39a..6a10e40 100644
--- a/tests/tests/media/src/android/media/cts/MediaPlayerTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaPlayerTest.java
@@ -1694,6 +1694,17 @@
playVideoTest(R.raw.mkv_audio_pcms16le, -1, -1);
}
+ public void testLocalVideo_segment000001_m2ts()
+ throws Exception {
+ if (checkLoadResource(R.raw.segment000001)) {
+ mMediaPlayer.stop();
+ assertTrue(checkLoadResource(R.raw.segment000001_m2ts));
+ playLoadedVideo(320, 240, 0);
+ } else {
+ MediaUtils.skipTest("no mp2 support, skipping m2ts");
+ }
+ }
+
private void readSubtitleTracks() throws Exception {
mSubtitleTrackIndex.clear();
MediaPlayer.TrackInfo[] trackInfos = mMediaPlayer.getTrackInfo();
diff --git a/tests/tests/media/src/android/media/cts/MediaRandomTest.java b/tests/tests/media/src/android/media/cts/MediaRandomTest.java
index 3db1d13..306b451 100644
--- a/tests/tests/media/src/android/media/cts/MediaRandomTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaRandomTest.java
@@ -170,12 +170,18 @@
afd.close();
}
}
+ public void testPlayerRandomActionAV1() throws Exception {
+ testPlayerRandomAction(R.raw.video_480x360_webm_av1_400kbps_30fps_vorbis_stereo_128kbps_48000hz);
+ }
public void testPlayerRandomActionH264() throws Exception {
testPlayerRandomAction(R.raw.video_480x360_mp4_h264_500kbps_30fps_aac_stereo_128kbps_44100hz);
}
public void testPlayerRandomActionHEVC() throws Exception {
testPlayerRandomAction(R.raw.video_480x360_mp4_hevc_650kbps_30fps_aac_stereo_128kbps_48000hz);
}
+ public void testPlayerRandomActionMpeg2() throws Exception {
+ testPlayerRandomAction(R.raw.video_480x360_mp4_mpeg2_1500kbps_30fps_aac_stereo_128kbps_48000hz);
+ }
private void testPlayerRandomAction(int resid) throws Exception {
Watchdog watchdog = new Watchdog(5000);
try {
diff --git a/tests/tests/media/src/android/media/cts/MediaRecorderTest.java b/tests/tests/media/src/android/media/cts/MediaRecorderTest.java
index ac68057..0e9ec05 100644
--- a/tests/tests/media/src/android/media/cts/MediaRecorderTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaRecorderTest.java
@@ -80,6 +80,7 @@
private static final int RECORD_TIME_LAPSE_MS = 6000;
private static final int RECORD_TIME_LONG_MS = 20000;
private static final int RECORDED_DUR_TOLERANCE_MS = 1000;
+ private static final int TEST_TIMING_TOLERANCE_MS = 70;
// Tolerate 4 frames off at maximum
private static final float RECORDED_DUR_TOLERANCE_FRAMES = 4f;
private static final int VIDEO_WIDTH = 176;
@@ -1655,12 +1656,13 @@
configureDefaultMediaRecorder();
mMediaRecorder.prepare();
mMediaRecorder.start();
- Thread.sleep(1000);
+ callback.await(TEST_TIMING_TOLERANCE_MS);
assertTrue(callback.mCalled);
assertTrue(callback.mConfigs.size() <= 1);
if (callback.mConfigs.size() == 1) {
checkRecordingConfig(callback.mConfigs.get(0));
}
+ Thread.sleep(RECORD_TIME_MS);
mMediaRecorder.stop();
mMediaRecorder.unregisterAudioRecordingCallback(callback);
}
diff --git a/tests/tests/media/src/android/media/cts/MediaSession2Test.java b/tests/tests/media/src/android/media/cts/MediaSession2Test.java
index eea22fe..d46f7b3 100644
--- a/tests/tests/media/src/android/media/cts/MediaSession2Test.java
+++ b/tests/tests/media/src/android/media/cts/MediaSession2Test.java
@@ -195,7 +195,6 @@
@Test
public void testSession2Token_extrasSetToNull() {
try (MediaSession2 session = new MediaSession2.Builder(mContext)
- .setExtras(null)
.build()) {
Session2Token token = session.getToken();
assertSame(Bundle.EMPTY, token.getExtras());
diff --git a/tests/tests/media/src/android/media/cts/MediaSessionManagerTest.java b/tests/tests/media/src/android/media/cts/MediaSessionManagerTest.java
index 96cf427..f0e63ef 100644
--- a/tests/tests/media/src/android/media/cts/MediaSessionManagerTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaSessionManagerTest.java
@@ -300,6 +300,53 @@
}
}
+ public void testGetSession2TokensWithTwoSessions() throws Exception {
+ final Context context = getInstrumentation().getTargetContext();
+ Handler handler = createHandler();
+ Executor handlerExecutor = (runnable) -> {
+ if (handler != null) {
+ handler.post(() -> {
+ runnable.run();
+ });
+ }
+ };
+
+ Session2TokenListener listener = new Session2TokenListener();
+ mSessionManager.addOnSession2TokensChangedListener(listener, handler);
+
+ try (MediaSession2 session1 = new MediaSession2.Builder(context)
+ .setSessionCallback(handlerExecutor, new Session2Callback())
+ .setId("testGetSession2TokensWithTwoSessions_session1")
+ .build()) {
+
+ assertTrue(listener.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ Session2Token session1Token = session1.getToken();
+ assertTrue(listContainsToken(mSessionManager.getSession2Tokens(), session1Token));
+
+ // Create another session and check the result of getSession2Token().
+ listener.resetCountDownLatch();
+ Session2Token session2Token = null;
+ try (MediaSession2 session2 = new MediaSession2.Builder(context)
+ .setSessionCallback(handlerExecutor, new Session2Callback())
+ .setId("testGetSession2TokensWithTwoSessions_session2")
+ .build()) {
+
+ assertTrue(listener.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ session2Token = session2.getToken();
+ assertNotNull(session2Token);
+ assertTrue(listContainsToken(mSessionManager.getSession2Tokens(), session1Token));
+ assertTrue(listContainsToken(mSessionManager.getSession2Tokens(), session2Token));
+
+ listener.resetCountDownLatch();
+ }
+
+ // Since the session2 is closed, getSession2Tokens() shouldn't include session2's token.
+ assertTrue(listener.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ assertTrue(listContainsToken(mSessionManager.getSession2Tokens(), session1Token));
+ assertFalse(listContainsToken(mSessionManager.getSession2Tokens(), session2Token));
+ }
+ }
+
public void testAddAndRemoveSession2TokensListener() throws Exception {
final Context context = getInstrumentation().getTargetContext();
Handler handler = createHandler();
diff --git a/tests/tests/media/src/android/media/cts/NativeDecoderTest.java b/tests/tests/media/src/android/media/cts/NativeDecoderTest.java
index c2d1392..18346ea 100644
--- a/tests/tests/media/src/android/media/cts/NativeDecoderTest.java
+++ b/tests/tests/media/src/android/media/cts/NativeDecoderTest.java
@@ -116,16 +116,24 @@
public void testExtractor() throws Exception {
testExtractor(R.raw.sinesweepogg);
+ testExtractor(R.raw.sinesweepoggmkv);
+ testExtractor(R.raw.sinesweepoggmp4);
testExtractor(R.raw.sinesweepmp3lame);
testExtractor(R.raw.sinesweepmp3smpb);
+ testExtractor(R.raw.sinesweepopus);
+ testExtractor(R.raw.sinesweepopusmp4);
testExtractor(R.raw.sinesweepm4a);
+ testExtractor(R.raw.sinesweepflacmkv);
testExtractor(R.raw.sinesweepflac);
+ testExtractor(R.raw.sinesweepflacmp4);
testExtractor(R.raw.sinesweepwav);
testExtractor(R.raw.video_1280x720_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz);
testExtractor(R.raw.bbb_s3_1280x720_webm_vp8_8mbps_60fps_opus_6ch_384kbps_48000hz);
testExtractor(R.raw.bbb_s4_1280x720_webm_vp9_0p31_4mbps_30fps_opus_stereo_128kbps_48000hz);
+ testExtractor(R.raw.video_1280x720_webm_av1_2000kbps_30fps_vorbis_stereo_128kbps_48000hz);
testExtractor(R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_mono_24kbps_11025hz);
+ testExtractor(R.raw.video_480x360_mp4_mpeg2_1500kbps_30fps_aac_stereo_128kbps_48000hz);
testExtractor(R.raw.video_480x360_mp4_mpeg4_860kbps_25fps_aac_stereo_128kbps_44100hz);
CtsTestServer foo = new CtsTestServer(mContext);
@@ -309,16 +317,23 @@
public void testDecoder() throws Exception {
int testsRun =
testDecoder(R.raw.sinesweepogg) +
+ testDecoder(R.raw.sinesweepoggmkv) +
+ testDecoder(R.raw.sinesweepoggmp4) +
testDecoder(R.raw.sinesweepmp3lame) +
testDecoder(R.raw.sinesweepmp3smpb) +
+ testDecoder(R.raw.sinesweepopus) +
+ testDecoder(R.raw.sinesweepopusmp4) +
testDecoder(R.raw.sinesweepm4a) +
+ testDecoder(R.raw.sinesweepflacmkv) +
testDecoder(R.raw.sinesweepflac) +
+ testDecoder(R.raw.sinesweepflacmp4) +
testDecoder(R.raw.sinesweepwav) +
testDecoder(R.raw.video_1280x720_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz) +
testDecoder(R.raw.bbb_s1_640x360_webm_vp8_2mbps_30fps_vorbis_5ch_320kbps_48000hz) +
testDecoder(R.raw.bbb_s1_640x360_webm_vp9_0p21_1600kbps_30fps_vorbis_stereo_128kbps_48000hz) +
testDecoder(R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_mono_24kbps_11025hz) +
+ testDecoder(R.raw.video_480x360_mp4_mpeg2_1500kbps_30fps_aac_stereo_128kbps_48000hz);
testDecoder(R.raw.video_480x360_mp4_mpeg4_860kbps_25fps_aac_stereo_128kbps_44100hz);
if (testsRun == 0) {
MediaUtils.skipTest("no decoders found");
@@ -547,8 +562,12 @@
testVideoPlayback(
R.raw.bbb_s1_640x360_webm_vp9_0p21_1600kbps_30fps_vorbis_stereo_128kbps_48000hz) +
testVideoPlayback(
+ R.raw.video_640x360_webm_av1_470kbps_30fps_vorbis_stereo_128kbps_48000hz) +
+ testVideoPlayback(
R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_mono_24kbps_11025hz) +
testVideoPlayback(
+ R.raw.video_176x144_mp4_mpeg2_105kbps_25fps_aac_stereo_128kbps_44100hz) +
+ testVideoPlayback(
R.raw.video_480x360_mp4_mpeg4_860kbps_25fps_aac_stereo_128kbps_44100hz);
if (testsRun == 0) {
MediaUtils.skipTest("no decoders found");
diff --git a/tests/tests/media/src/android/media/cts/VideoDecoderPerfTest.java b/tests/tests/media/src/android/media/cts/VideoDecoderPerfTest.java
index 57ff9d3..cd035e7 100644
--- a/tests/tests/media/src/android/media/cts/VideoDecoderPerfTest.java
+++ b/tests/tests/media/src/android/media/cts/VideoDecoderPerfTest.java
@@ -25,6 +25,7 @@
import android.media.MediaCodecInfo.VideoCapabilities;
import android.media.MediaExtractor;
import android.media.MediaFormat;
+import android.os.Build;
import android.platform.test.annotations.AppModeFull;
import android.util.Log;
import android.util.Pair;
@@ -83,6 +84,15 @@
super.tearDown();
}
+ // Performance numbers only make sense on real devices, so skip on non-real devices
+ public static boolean frankenDevice() {
+ if (("Android".equals(Build.BRAND) || "generic".equals(Build.BRAND)) &&
+ (Build.MODEL.startsWith("AOSP on ") || Build.PRODUCT.startsWith("aosp_"))) {
+ return true;
+ }
+ return false;
+ }
+
private void decode(String name, int resourceId, MediaFormat format) throws Exception {
int width = format.getInteger(MediaFormat.KEY_WIDTH);
int height = format.getInteger(MediaFormat.KEY_HEIGHT);
@@ -91,6 +101,10 @@
// Ensure we can finish this test within the test timeout. Allow 25% slack (4/5).
long maxTimeMs = Math.min(
MAX_TEST_TIMEOUT_MS * 4 / 5 / NUMBER_OF_REPEATS, MAX_TIME_MS);
+ // reduce test run on non-real device
+ if (frankenDevice()) {
+ maxTimeMs /= 10;
+ }
double measuredFps[] = new double[NUMBER_OF_REPEATS];
for (int i = 0; i < NUMBER_OF_REPEATS; ++i) {
@@ -107,7 +121,12 @@
String error =
MediaPerfUtils.verifyAchievableFrameRates(name, mime, width, height, measuredFps);
- assertNull(error, error);
+ if (frankenDevice() && error != null) {
+ // ensure there is data, but don't insist that it is correct
+ assertFalse(error, error.startsWith("Failed to get "));
+ } else {
+ assertNull(error, error);
+ }
mSamplesInMemory.clear();
}
diff --git a/tests/tests/media/src/android/media/cts/VideoEncoderTest.java b/tests/tests/media/src/android/media/cts/VideoEncoderTest.java
index 55f1608..c7018a2 100644
--- a/tests/tests/media/src/android/media/cts/VideoEncoderTest.java
+++ b/tests/tests/media/src/android/media/cts/VideoEncoderTest.java
@@ -1195,7 +1195,7 @@
ArrayList<Encoder> result = new ArrayList<Encoder>();
for (MediaCodecInfo info : mcl.getCodecInfos()) {
- if (!info.isEncoder() || MediaUtils.isGoogle(info.getName()) != goog) {
+ if (!info.isEncoder() || !info.isVendor() != goog) {
continue;
}
CodecCapabilities caps = null;
diff --git a/tests/tests/media/src/android/media/cts/VpxCodecTestBase.java b/tests/tests/media/src/android/media/cts/VpxCodecTestBase.java
index 48faf49..a18a9d5 100644
--- a/tests/tests/media/src/android/media/cts/VpxCodecTestBase.java
+++ b/tests/tests/media/src/android/media/cts/VpxCodecTestBase.java
@@ -99,10 +99,6 @@
this.codecName = codecName;
this.colorFormat = colorFormat;
}
- public boolean isGoogleCodec() {
- return MediaUtils.isGoogle(codecName);
- }
-
public final String codecName; // OpenMax component name for VPx codec.
public final int colorFormat; // Color format supported by codec.
}
@@ -128,7 +124,7 @@
CodecProperties codecProperties = null;
String mime = format.getString(MediaFormat.KEY_MIME);
- // Loop through the list of omx components in case platform specific codec
+ // Loop through the list of codec components in case platform specific codec
// is requested.
MediaCodecList mcl = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
for (MediaCodecInfo codecInfo : mcl.getCodecInfos()) {
@@ -138,8 +134,7 @@
Log.v(TAG, codecInfo.getName());
// TODO: remove dependence of Google from the test
// Check if this is Google codec - we should ignore it.
- boolean isGoogleCodec = MediaUtils.isGoogle(codecInfo.getName());
- if (!isGoogleCodec && forceGoogleCodec) {
+ if (codecInfo.isVendor() && forceGoogleCodec) {
continue;
}
@@ -166,8 +161,8 @@
codecColorFormat);
Log.v(TAG, "Found target codec " + codecProperties.codecName +
". Color: 0x" + Integer.toHexString(codecColorFormat));
- // return first HW codec found
- if (!isGoogleCodec) {
+ // return first vendor codec (hopefully HW) found
+ if (!codecInfo.isVendor()) {
return codecProperties;
}
}
diff --git a/tests/tests/media/src/android/media/cts/VpxEncoderTest.java b/tests/tests/media/src/android/media/cts/VpxEncoderTest.java
index 654875f..586628b 100644
--- a/tests/tests/media/src/android/media/cts/VpxEncoderTest.java
+++ b/tests/tests/media/src/android/media/cts/VpxEncoderTest.java
@@ -68,7 +68,7 @@
// Maximum allowed average PSNR difference of the encoder running in a looper thread with 0 ms
// buffer dequeue timeout comparing to the encoder running in a callee's thread with 100 ms
// buffer dequeue timeout.
- private static final double MAX_ASYNC_AVERAGE_PSNR_DIFFERENCE = 0.5;
+ private static final double MAX_ASYNC_AVERAGE_PSNR_DIFFERENCE = 1.5;
// Maximum allowed minimum PSNR difference of the encoder running in a looper thread
// comparing to the encoder running in a callee's thread.
private static final double MAX_ASYNC_MINIMUM_PSNR_DIFFERENCE = 2;
diff --git a/tests/tests/nativemedia/aaudio/AndroidTest.xml b/tests/tests/nativemedia/aaudio/AndroidTest.xml
index b796ea6..12da94e 100644
--- a/tests/tests/nativemedia/aaudio/AndroidTest.xml
+++ b/tests/tests/nativemedia/aaudio/AndroidTest.xml
@@ -16,6 +16,8 @@
<configuration description="Config for CTS Native Media AAudio test cases">
<option name="config-descriptor:metadata" key="component" value="media" />
<option name="test-suite-tag" value="cts" />
+ <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
<option name="not-shardable" value="true" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
diff --git a/tests/tests/net/src/android/net/cts/UrlQuerySanitizerTest.java b/tests/tests/net/src/android/net/cts/UrlQuerySanitizerTest.java
index 7076ea2..2d615bb 100644
--- a/tests/tests/net/src/android/net/cts/UrlQuerySanitizerTest.java
+++ b/tests/tests/net/src/android/net/cts/UrlQuerySanitizerTest.java
@@ -170,6 +170,12 @@
String initialPercentSign = "title=%B5";
assertEquals(expectedPlus, uqs.unescape(initialPlus));
assertEquals(expectedPercentSignHex, uqs.unescape(initialPercentSign));
+ String expectedPlusThenPercentSign = "Joe Random, User";
+ String plusThenPercentSign = "Joe+Random%2C%20User";
+ assertEquals(expectedPlusThenPercentSign, uqs.unescape(plusThenPercentSign));
+ String expectedPercentSignThenPlus = "Joe, Random User";
+ String percentSignThenPlus = "Joe%2C+Random+User";
+ assertEquals(expectedPercentSignThenPlus, uqs.unescape(percentSignThenPlus));
assertTrue(uqs.decodeHexDigit('0') >= 0);
assertTrue(uqs.decodeHexDigit('b') >= 0);
diff --git a/tests/tests/net/src/android/net/wifi/cts/WifiManagerTest.java b/tests/tests/net/src/android/net/wifi/cts/WifiManagerTest.java
index 1d66682..8ea9afe 100644
--- a/tests/tests/net/src/android/net/wifi/cts/WifiManagerTest.java
+++ b/tests/tests/net/src/android/net/wifi/cts/WifiManagerTest.java
@@ -744,21 +744,16 @@
allowedUIDs.add(Process.SHELL_UID);
allowedUIDs.add(Process.PHONE_UID);
allowedUIDs.add(Process.NETWORK_STACK_UID);
+ allowedUIDs.add(Process.NFC_UID);
// only quick settings is allowed to bind to the BIND_QUICK_SETTINGS_TILE permission, using
- // this fact to determined allowed package name for sysui
- String validPkg = "";
- final List<PackageInfo> sysuiPackage = pm.getPackagesHoldingPermissions(new String[] {
+ // this fact to determined allowed package name for sysui. This is a signature permission,
+ // so allow any package with this permission.
+ final List<PackageInfo> sysuiPackages = pm.getPackagesHoldingPermissions(new String[] {
android.Manifest.permission.BIND_QUICK_SETTINGS_TILE
}, PackageManager.MATCH_UNINSTALLED_PACKAGES);
-
- if (sysuiPackage.size() > 1) {
- fail("The BIND_QUICK_SETTINGS_TILE permission must only be held by one package");
- }
-
- if (sysuiPackage.size() == 1) {
- validPkg = sysuiPackage.get(0).packageName;
- allowedPackages.add(validPkg);
+ for (PackageInfo info : sysuiPackages) {
+ allowedPackages.add(info.packageName);
}
// the captive portal flow also currently holds the NETWORK_SETTINGS permission
diff --git a/tests/tests/networksecurityconfig/src/android/security/net/config/cts/BaseTestCase.java b/tests/tests/networksecurityconfig/src/android/security/net/config/cts/BaseTestCase.java
index 865b959..f5b0140 100644
--- a/tests/tests/networksecurityconfig/src/android/security/net/config/cts/BaseTestCase.java
+++ b/tests/tests/networksecurityconfig/src/android/security/net/config/cts/BaseTestCase.java
@@ -28,6 +28,10 @@
public class BaseTestCase extends AndroidTestCase {
@Override
public void setUp() throws Exception {
+ // Instant Apps cannot access WifiManager, skip wifi check.
+ if (getContext().getPackageManager().isInstantApp()) {
+ return;
+ }
WifiManager wifiManager = (WifiManager) getContext().getSystemService(Context.WIFI_SERVICE);
if (!wifiManager.isWifiEnabled()) {
SystemUtil.runShellCommand("svc wifi enable"); // toggle wifi on.
diff --git a/tests/tests/neuralnetworks/Android.mk b/tests/tests/neuralnetworks/Android.mk
index 2b941d2..7d3b4c5 100644
--- a/tests/tests/neuralnetworks/Android.mk
+++ b/tests/tests/neuralnetworks/Android.mk
@@ -21,6 +21,12 @@
LOCAL_MODULE := CtsNNAPITests_static
LOCAL_SRC_FILES := \
$(call all-cpp-files-under,generated/tests) \
+ $(call all-cpp-files-under,fuzzing/operation_signatures) \
+ fuzzing/OperationManager.cpp \
+ fuzzing/RandomGraphGenerator.cpp \
+ fuzzing/RandomGraphGeneratorUtils.cpp \
+ fuzzing/RandomVariable.cpp \
+ fuzzing/TestRandomGraph.cpp \
TestGenerated.cpp \
TestMemory.cpp \
TestTrivialModel.cpp \
@@ -36,7 +42,7 @@
LOCAL_C_INCLUDES += frameworks/ml/nn/common/include
LOCAL_C_INCLUDES += frameworks/ml/nn/tools/test_generator/include
-LOCAL_CFLAGS := -Werror -Wall -DNNTEST_ONLY_PUBLIC_API
+LOCAL_CFLAGS := -Werror -Wall -DNNTEST_ONLY_PUBLIC_API -DNNTEST_CTS
LOCAL_SHARED_LIBRARIES := libandroid liblog libneuralnetworks
LOCAL_STATIC_LIBRARIES := libgtest_ndk_c++ libgmock_ndk
diff --git a/tests/tests/neuralnetworks/AndroidTest.xml b/tests/tests/neuralnetworks/AndroidTest.xml
index ea96932..1045b85 100644
--- a/tests/tests/neuralnetworks/AndroidTest.xml
+++ b/tests/tests/neuralnetworks/AndroidTest.xml
@@ -26,8 +26,8 @@
<test class="com.android.tradefed.testtype.GTest" >
<option name="native-test-device-path" value="/data/local/tmp" />
<option name="module-name" value="CtsNNAPITestCases" />
- <option name="runtime-hint" value="2m" />
- <!-- test-timeout unit is ms, value = 2 min -->
- <option name="native-test-timeout" value="120000" />
+ <option name="runtime-hint" value="10m" />
+ <!-- test-timeout unit is ms, value = 10 min -->
+ <option name="native-test-timeout" value="600000" />
</test>
</configuration>
diff --git a/tests/tests/neuralnetworks/benchmark/AndroidTest.xml b/tests/tests/neuralnetworks/benchmark/AndroidTest.xml
index dd309c7..2492614 100644
--- a/tests/tests/neuralnetworks/benchmark/AndroidTest.xml
+++ b/tests/tests/neuralnetworks/benchmark/AndroidTest.xml
@@ -16,6 +16,8 @@
<configuration description="Configuration for CTS NNAPI Benchmark Tests">
<option name="test-suite-tag" value="cts" />
<option name="config-descriptor:metadata" key="component" value="neuralnetworks" />
+ <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="CtsNNAPIBenchmarkTestCases.apk" />
diff --git a/tests/tests/os/Android.mk b/tests/tests/os/Android.mk
index a5231cc..aca23e3 100644
--- a/tests/tests/os/Android.mk
+++ b/tests/tests/os/Android.mk
@@ -51,7 +51,7 @@
# Tag this module as a cts test artifact
LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-LOCAL_SDK_VERSION := test_current
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_JAVA_LIBRARIES += android.test.runner.stubs
LOCAL_JAVA_LIBRARIES += android.test.base.stubs
diff --git a/tests/tests/os/AndroidManifest.xml b/tests/tests/os/AndroidManifest.xml
index 586a157..4092538 100644
--- a/tests/tests/os/AndroidManifest.xml
+++ b/tests/tests/os/AndroidManifest.xml
@@ -44,6 +44,7 @@
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
<uses-permission android:name="android.permission.DEVICE_POWER" />
<uses-permission android:name="android.permission.POWER_SAVER" />
+ <uses-permission android:name="android.permission.INSTALL_DYNAMIC_SYSTEM" />
<uses-permission android:name="android.os.cts.permission.TEST_GRANTED" />
<application
diff --git a/tests/tests/os/src/android/os/cts/LocaleListTest.java b/tests/tests/os/src/android/os/cts/LocaleListTest.java
index 611260a..8eee8c6d 100644
--- a/tests/tests/os/src/android/os/cts/LocaleListTest.java
+++ b/tests/tests/os/src/android/os/cts/LocaleListTest.java
@@ -532,6 +532,6 @@
assertFalse(LocaleList.isPseudoLocale(ULocale.forLanguageTag("fa-IR")));
assertFalse(LocaleList.isPseudoLocale(ULocale.forLanguageTag("zh-CN")));
assertFalse(LocaleList.isPseudoLocale(ULocale.forLanguageTag("fr-CA")));
- assertFalse(LocaleList.isPseudoLocale(null));
+ assertFalse(LocaleList.isPseudoLocale((ULocale) null));
}
}
diff --git a/tests/tests/os/src/android/os/cts/ParcelFileDescriptorTest.java b/tests/tests/os/src/android/os/cts/ParcelFileDescriptorTest.java
index bde03f5c..f0f2d36 100644
--- a/tests/tests/os/src/android/os/cts/ParcelFileDescriptorTest.java
+++ b/tests/tests/os/src/android/os/cts/ParcelFileDescriptorTest.java
@@ -31,6 +31,7 @@
import android.os.ParcelFileDescriptor.AutoCloseInputStream;
import android.os.Parcelable;
import android.os.cts.ParcelFileDescriptorPeer.FutureCloseListener;
+import android.platform.test.annotations.AppModeFull;
import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;
@@ -95,6 +96,7 @@
}
@Test
+ @AppModeFull // opening a listening socket not permitted for instant apps
public void testFromSocket() throws Throwable {
final int PORT = 12222;
final int DATA = 1;
diff --git a/tests/tests/os/src/android/os/image/cts/DynamicSystemClientTest.java b/tests/tests/os/src/android/os/image/cts/DynamicSystemClientTest.java
new file mode 100644
index 0000000..21a2908
--- /dev/null
+++ b/tests/tests/os/src/android/os/image/cts/DynamicSystemClientTest.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.app.Instrumentation;
+import android.net.Uri;
+import android.os.SystemProperties;
+import android.os.image.DynamicSystemClient;
+import android.util.FeatureFlagUtils;
+
+import androidx.test.InstrumentationRegistry;
+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;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class DynamicSystemClientTest implements DynamicSystemClient.OnStatusChangedListener {
+ private boolean mUpdated;
+ private Instrumentation mInstrumentation;
+
+ public void onStatusChanged(int status, int cause, long progress, Throwable detail) {
+ mUpdated = true;
+ }
+
+ @Before
+ public void setUp() {
+ mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ mInstrumentation.getUiAutomation().adoptShellPermissionIdentity();
+ }
+
+ private boolean featureFlagEnabled() {
+ return SystemProperties.getBoolean(
+ FeatureFlagUtils.PERSIST_PREFIX + FeatureFlagUtils.DYNAMIC_SYSTEM, false);
+ }
+
+ @Test
+ public void testDynamicSystemClient() {
+ if (!featureFlagEnabled()) {
+ return;
+ }
+ DynamicSystemClient dSClient = new DynamicSystemClient(mInstrumentation.getTargetContext());
+ dSClient.setOnStatusChangedListener(this);
+ try {
+ dSClient.bind();
+ } catch (SecurityException e) {
+ fail();
+ }
+ Uri uri = Uri.parse("https://www.google.com/").buildUpon().build();
+ mUpdated = false;
+ try {
+ dSClient.start(uri, 1024L << 10);
+ } catch (SecurityException e) {
+ fail();
+ }
+ try {
+ Thread.sleep(3 * 1000);
+ } catch (InterruptedException e) {
+ fail();
+ }
+ assertTrue(mUpdated);
+ try {
+ dSClient.unbind();
+ } catch (SecurityException e) {
+ fail();
+ }
+ }
+
+ @After
+ public void tearDown() {
+ mInstrumentation.getUiAutomation().dropShellPermissionIdentity();
+ }
+}
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 1fb7baa..ba49b04 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
@@ -28,7 +28,6 @@
import android.content.pm.PackageManager;
import android.os.RemoteException;
-import androidx.test.filters.FlakyTest;
import androidx.test.InstrumentationRegistry;
import org.junit.After;
@@ -84,7 +83,6 @@
}
@Test
- @FlakyTest(bugId = 129859594)
public void testFailInconsistentMultiPackageCommit() throws Exception {
// Test inconsistency in staged settings
for (boolean staged : new boolean[]{false, true}) {
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 266795c..34b78c3 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
@@ -19,11 +19,9 @@
import android.app.AppOpsManager.MODE_ALLOWED
import android.content.pm.ApplicationInfo.CATEGORY_MAPS
import android.content.pm.ApplicationInfo.CATEGORY_UNDEFINED
-import android.content.pm.PackageInstaller
import android.content.pm.PackageInstaller.STATUS_FAILURE_ABORTED
import android.content.pm.PackageInstaller.STATUS_SUCCESS
import android.platform.test.annotations.AppModeFull
-import android.platform.test.annotations.AppModeInstant
import androidx.test.InstrumentationRegistry
import androidx.test.runner.AndroidJUnit4
import com.android.compatibility.common.util.AppOpsUtils
@@ -32,11 +30,11 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import java.lang.NullPointerException
private const val INSTALL_BUTTON_ID = "button1"
private const val CANCEL_BUTTON_ID = "button2"
+@AppModeFull(reason = "Instant apps cannot create installer sessions")
@RunWith(AndroidJUnit4::class)
class SessionTest : PackageInstallerTestBase() {
private val context = InstrumentationRegistry.getTargetContext()
@@ -50,17 +48,6 @@
/**
* Check that we can install an app via a package-installer session
*/
- @AppModeInstant(reason = "Only instant apps cannot create installer sessions")
- @Test(expected = NullPointerException::class)
- fun instantAppsCannotCreateInstallSessions() {
- pm.packageInstaller.createSession(
- PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL))
- }
-
- /**
- * Check that we can install an app via a package-installer session
- */
- @AppModeFull(reason = "Instant apps cannot create installer sessions")
@Test
fun confirmInstallation() {
startInstallationViaSession()
@@ -81,7 +68,6 @@
/**
* Check that we can set an app category for an app we installed
*/
- @AppModeFull(reason = "Instant apps cannot create installer sessions")
@Test
fun setAppCategory() {
startInstallationViaSession()
@@ -102,7 +88,6 @@
* Install an app via a package-installer session, but then cancel it when the package installer
* pops open.
*/
- @AppModeFull(reason = "Instant apps cannot create installer sessions")
@Test
fun cancelInstallation() {
startInstallationViaSession()
diff --git a/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/SessionTestInstant.kt b/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/SessionTestInstant.kt
new file mode 100644
index 0000000..7949381
--- /dev/null
+++ b/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/SessionTestInstant.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.content.pm.PackageInstaller
+import android.platform.test.annotations.AppModeInstant
+import androidx.test.InstrumentationRegistry
+import androidx.test.runner.AndroidJUnit4
+import org.junit.Test
+import org.junit.runner.RunWith
+import java.lang.NullPointerException
+
+@AppModeInstant(reason = "This just makes sure that instant apps cannot run the SessionTest")
+@RunWith(AndroidJUnit4::class)
+class SessionTestInstant {
+ private val context = InstrumentationRegistry.getTargetContext()
+ private val pm = context.packageManager
+
+ /**
+ * Check that an instant app cannot install an app via a package-installer session
+ */
+ @Test(expected = NullPointerException::class)
+ fun instantAppsCannotCreateInstallSessions() {
+ pm.packageInstaller.createSession(
+ PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL))
+ }
+}
diff --git a/tests/tests/permission/AndroidTest.xml b/tests/tests/permission/AndroidTest.xml
index 57a8716..2dba4fd 100644
--- a/tests/tests/permission/AndroidTest.xml
+++ b/tests/tests/permission/AndroidTest.xml
@@ -51,6 +51,8 @@
<option name="push" value="CtsAppThatAccessesStorageOnCommand28v3.apk->/data/local/tmp/cts/permissions/CtsAppThatAccessesStorageOnCommand28v3.apk" />
<option name="push" value="CtsAppWithSharedUidThatRequestsPermissions.apk->/data/local/tmp/cts/permissions/CtsAppWithSharedUidThatRequestsPermissions.apk" />
<option name="push" value="CtsAppWithSharedUidThatRequestsNoPermissions.apk->/data/local/tmp/cts/permissions/CtsAppWithSharedUidThatRequestsNoPermissions.apk" />
+ <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" />
</target_preparer>
<!-- Remove additional apps if installed -->
diff --git a/tests/tests/permission/AppWithSharedUidThatRequestLocationPermission28/Android.bp b/tests/tests/permission/AppWithSharedUidThatRequestLocationPermission28/Android.bp
new file mode 100644
index 0000000..15da653
--- /dev/null
+++ b/tests/tests/permission/AppWithSharedUidThatRequestLocationPermission28/Android.bp
@@ -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.
+//
+
+android_test_helper_app {
+ name: "CtsAppWithSharedUidThatRequestsLocationPermission28",
+ defaults: ["cts_defaults"],
+
+ sdk_version: "current",
+
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "vts",
+ "general-tests",
+ "cts_instant",
+ ],
+}
diff --git a/tests/tests/permission/AppWithSharedUidThatRequestLocationPermission28/AndroidManifest.xml b/tests/tests/permission/AppWithSharedUidThatRequestLocationPermission28/AndroidManifest.xml
new file mode 100644
index 0000000..ec69d15
--- /dev/null
+++ b/tests/tests/permission/AppWithSharedUidThatRequestLocationPermission28/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.permission.cts.appthatrequestpermission"
+ android:versionCode="2"
+ android:sharedUserId="android.permission.cts.appthatrequestpermission">
+
+ <uses-sdk android:minSdkVersion="28" android:targetSdkVersion="28" />
+
+ <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
+
+ <application />
+</manifest>
+
diff --git a/tests/tests/permission/AppWithSharedUidThatRequestLocationPermission29/Android.bp b/tests/tests/permission/AppWithSharedUidThatRequestLocationPermission29/Android.bp
new file mode 100644
index 0000000..a369df7
--- /dev/null
+++ b/tests/tests/permission/AppWithSharedUidThatRequestLocationPermission29/Android.bp
@@ -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.
+//
+
+android_test_helper_app {
+ name: "CtsAppWithSharedUidThatRequestsLocationPermission29",
+ defaults: ["cts_defaults"],
+
+ sdk_version: "current",
+
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "vts",
+ "general-tests",
+ "cts_instant",
+ ],
+}
diff --git a/tests/tests/permission/AppWithSharedUidThatRequestLocationPermission29/AndroidManifest.xml b/tests/tests/permission/AppWithSharedUidThatRequestLocationPermission29/AndroidManifest.xml
new file mode 100644
index 0000000..2341922
--- /dev/null
+++ b/tests/tests/permission/AppWithSharedUidThatRequestLocationPermission29/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.permission.cts.appthatrequestpermission"
+ android:versionCode="1"
+ android:sharedUserId="android.permission.cts.appthatrequestpermission">
+
+ <!-- STOPSHIP: Set to apk level that shipped the location tristate -->
+ <!-- <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29" /> -->
+
+ <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
+
+ <application />
+</manifest>
+
diff --git a/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java b/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java
index 59582d1..8ed0afd 100644
--- a/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java
+++ b/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java
@@ -37,6 +37,8 @@
import androidx.test.filters.MediumTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.compatibility.common.util.PropertyUtil;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -343,8 +345,10 @@
@MediumTest
@Test
public void testProcNetSane() throws Exception {
- for (String file : procNetFiles) {
- procNetSane("/proc/net/" + file);
+ if (PropertyUtil.isVendorApiLevelNewerThan(28)) {
+ for (String file : procNetFiles) {
+ procNetSane("/proc/net/" + file);
+ }
}
}
diff --git a/tests/tests/permission/src/android/permission/cts/LocationAccessCheckTest.java b/tests/tests/permission/src/android/permission/cts/LocationAccessCheckTest.java
index 68b4317..232cd1f 100644
--- a/tests/tests/permission/src/android/permission/cts/LocationAccessCheckTest.java
+++ b/tests/tests/permission/src/android/permission/cts/LocationAccessCheckTest.java
@@ -36,6 +36,8 @@
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
import android.app.UiAutomation;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -61,7 +63,6 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.FlakyTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.server.job.nano.JobSchedulerServiceDumpProto;
@@ -74,8 +75,6 @@
import org.junit.Test;
import org.junit.runner.RunWith;
-import static java.util.concurrent.TimeUnit.MILLISECONDS;
-
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.util.Arrays;
@@ -87,7 +86,6 @@
@RunWith(AndroidJUnit4.class)
@AppModeFull(reason = "Cannot set system settings as instant app. Also we never show a location "
+ "access check notification for instant apps.")
-@FlakyTest
public class LocationAccessCheckTest {
private static final String LOG_TAG = LocationAccessCheckTest.class.getSimpleName();
@@ -199,7 +197,7 @@
if (System.currentTimeMillis() - start < timeout) {
Log.d(LOG_TAG, "Ignoring exception", e);
- Thread.sleep(100);
+ Thread.sleep(500);
} else {
throw e;
}
@@ -530,23 +528,23 @@
assertNotNull(getNotification(false));
}
- // @Test
- // public void notificationIsShownAgainAfterUninstallAndReinstall() throws Throwable {
- // accessLocation();
- // getNotification(true);
- //
- // uninstallBackgroundAccessApp();
- //
- // // Wait until package permission controller has cleared the state
- // Thread.sleep(2000);
- //
- // installBackgroundAccessApp();
- //
- // eventually(() -> {
- // accessLocation();
- // assertNotNull(getNotification(false));
- // }, UNEXPECTED_TIMEOUT_MILLIS);
- // }
+ @Test
+ public void notificationIsShownAgainAfterUninstallAndReinstall() throws Throwable {
+ accessLocation();
+ getNotification(true);
+
+ uninstallBackgroundAccessApp();
+
+ // Wait until package permission controller has cleared the state
+ Thread.sleep(2000);
+
+ installBackgroundAccessApp();
+
+ eventually(() -> {
+ accessLocation();
+ assertNotNull(getNotification(false));
+ }, UNEXPECTED_TIMEOUT_MILLIS);
+ }
@Test
public void removeNotificationOnUninstall() throws Throwable {
diff --git a/tests/tests/permission/src/android/permission/cts/MainlineNetworkStackPermissionTest.java b/tests/tests/permission/src/android/permission/cts/MainlineNetworkStackPermissionTest.java
index 3ae5edb..adac0be 100644
--- a/tests/tests/permission/src/android/permission/cts/MainlineNetworkStackPermissionTest.java
+++ b/tests/tests/permission/src/android/permission/cts/MainlineNetworkStackPermissionTest.java
@@ -25,12 +25,13 @@
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PermissionInfo;
+import android.platform.test.annotations.AppModeFull;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
-import org.junit.runner.RunWith;
import org.junit.Test;
+import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
public class MainlineNetworkStackPermissionTest{
@@ -41,6 +42,7 @@
* and is a system package
*/
@Test
+ @AppModeFull(reason = "Instant apps cannot access PackageManager#getPermissionInfo")
public void testPackageWithMainlineNetworkStackPermission() throws Exception {
final PackageManager packageManager = mContext.getPackageManager();
assertNotNull("Unable to find PackageManager.", packageManager);
diff --git a/tests/tests/permission/src/android/permission/cts/PermissionUtils.java b/tests/tests/permission/src/android/permission/cts/PermissionUtils.java
index 3163fe8..7e0d441 100644
--- a/tests/tests/permission/src/android/permission/cts/PermissionUtils.java
+++ b/tests/tests/permission/src/android/permission/cts/PermissionUtils.java
@@ -39,6 +39,7 @@
import android.app.UiAutomation;
import android.content.Context;
import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
import android.content.pm.PermissionInfo;
import android.os.Process;
import android.os.UserHandle;
@@ -128,8 +129,10 @@
*
* @return {@code true} iff the permission is granted
*/
- static boolean isPermissionGranted(@NonNull String packageName, @NonNull String permission) {
- return sContext.getPackageManager().checkPermission(permission, packageName)
+ static boolean isPermissionGranted(@NonNull String packageName, @NonNull String permission)
+ throws Exception {
+ return sContext.checkPermission(permission, Process.myPid(),
+ sContext.getPackageManager().getPackageUid(packageName, 0))
== PERMISSION_GRANTED;
}
@@ -170,7 +173,8 @@
* @param packageName The app that should have the permission granted
* @param permission The permission to grant
*/
- static void grantPermission(@NonNull String packageName, @NonNull String permission) {
+ static void grantPermission(@NonNull String packageName, @NonNull String permission)
+ throws Exception {
sUiAutomation.grantRuntimePermission(packageName, permission);
if (permission.equals(ACCESS_BACKGROUND_LOCATION)) {
@@ -194,6 +198,31 @@
}
/**
+ * Revoke a permission from an app.
+ *
+ * <p>This correctly handles pre-M apps by setting the app-ops.
+ * <p>This also correctly handles the location background permission, but does not handle any
+ * other background permission
+ *
+ * @param packageName The app that should have the permission revoked
+ * @param permission The permission to revoke
+ */
+ static void revokePermission(@NonNull String packageName, @NonNull String permission)
+ throws Exception {
+ sUiAutomation.revokeRuntimePermission(packageName, permission);
+
+ if (permission.equals(ACCESS_BACKGROUND_LOCATION)) {
+ // The app-op for background location is encoded into the mode of the foreground
+ // location
+ if (isGranted(packageName, ACCESS_COARSE_LOCATION)) {
+ setAppOp(packageName, ACCESS_COARSE_LOCATION, MODE_FOREGROUND);
+ }
+ } else {
+ setAppOp(packageName, permission, MODE_IGNORED);
+ }
+ }
+
+ /**
* Clear permission state (not app-op state) of package.
*
* @param packageName Package to clear
diff --git a/tests/tests/permission/src/android/permission/cts/SharedUidPermissionsTest.java b/tests/tests/permission/src/android/permission/cts/SharedUidPermissionsTest.java
index aa0e2c9..2f8d9e6 100644
--- a/tests/tests/permission/src/android/permission/cts/SharedUidPermissionsTest.java
+++ b/tests/tests/permission/src/android/permission/cts/SharedUidPermissionsTest.java
@@ -16,30 +16,34 @@
package android.permission.cts;
-import static android.Manifest.permission.READ_CALENDAR;
+import static android.Manifest.permission.INTERNET;
import static android.Manifest.permission.READ_CONTACTS;
import static android.permission.cts.PermissionUtils.grantPermission;
import static android.permission.cts.PermissionUtils.install;
import static android.permission.cts.PermissionUtils.isPermissionGranted;
+import static android.permission.cts.PermissionUtils.revokePermission;
import static android.permission.cts.PermissionUtils.uninstallApp;
import static com.google.common.truth.Truth.assertThat;
import android.platform.test.annotations.AppModeFull;
-import android.util.Log;
import androidx.test.runner.AndroidJUnit4;
+import org.junit.After;
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
+@AppModeFull(reason = "Instant apps cannot read properties of other packages which is needed "
+ + "to grant permissions to them.")
public class SharedUidPermissionsTest {
- private static final String LOG_TAG = SharedUidPermissionsTest.class.getSimpleName();
-
/** The package name of all apps used in the test */
- private static final String PKG_THAT_REQUESTS_PERMISSIONS = "android.permission.cts.appthatrequestpermission";
- private static final String PKG_THAT_REQUESTS_NO_PERMISSIONS = "android.permission.cts.appthatrequestnopermission";
+ private static final String PKG_THAT_REQUESTS_PERMISSIONS =
+ "android.permission.cts.appthatrequestpermission";
+ private static final String PKG_THAT_REQUESTS_NO_PERMISSIONS =
+ "android.permission.cts.appthatrequestnopermission";
private static final String TMP_DIR = "/data/local/tmp/cts/permissions/";
private static final String APK_THAT_REQUESTS_PERMISSIONS =
@@ -47,30 +51,90 @@
private static final String APK_THAT_REQUESTS_NO_PERMISSIONS =
TMP_DIR + "CtsAppWithSharedUidThatRequestsNoPermissions.apk";
+ @Before
+ @After
+ public void uninstallTestApps() {
+ uninstallApp(PKG_THAT_REQUESTS_PERMISSIONS);
+ uninstallApp(PKG_THAT_REQUESTS_NO_PERMISSIONS);
+ }
+
@Test
- @AppModeFull(reason = "Instant apps cannot read properties of other packages which is needed "
- + "to grant permissions to them.")
- public void appsWithSharedUidsSharePermissions() throws Exception {
+ public void packageGainsRuntimePermissionsWhenJoiningSharedUid() throws Exception {
+ install(APK_THAT_REQUESTS_PERMISSIONS);
+ grantPermission(PKG_THAT_REQUESTS_PERMISSIONS, READ_CONTACTS);
+ install(APK_THAT_REQUESTS_NO_PERMISSIONS);
+
+ assertThat(isPermissionGranted(PKG_THAT_REQUESTS_PERMISSIONS, READ_CONTACTS)).isTrue();
+ assertThat(isPermissionGranted(PKG_THAT_REQUESTS_NO_PERMISSIONS, READ_CONTACTS)).isTrue();
+ }
+
+ @Test
+ public void packageGainsNormalPermissionsWhenJoiningSharedUid() throws Exception {
install(APK_THAT_REQUESTS_PERMISSIONS);
install(APK_THAT_REQUESTS_NO_PERMISSIONS);
- try {
- grantPermission(PKG_THAT_REQUESTS_PERMISSIONS, READ_CONTACTS);
- grantPermission(PKG_THAT_REQUESTS_PERMISSIONS, READ_CALENDAR);
+ assertThat(isPermissionGranted(PKG_THAT_REQUESTS_PERMISSIONS, INTERNET)).isTrue();
+ assertThat(isPermissionGranted(PKG_THAT_REQUESTS_NO_PERMISSIONS, INTERNET)).isTrue();
+ }
- // Permissions are shared
- assertThat(isPermissionGranted(PKG_THAT_REQUESTS_NO_PERMISSIONS, READ_CONTACTS)).isTrue();
- assertThat(isPermissionGranted(PKG_THAT_REQUESTS_NO_PERMISSIONS, READ_CALENDAR)).isTrue();
+ @Test
+ public void grantingRuntimePermissionAffectsAllPackageInSharedUid() throws Exception {
+ install(APK_THAT_REQUESTS_PERMISSIONS);
+ install(APK_THAT_REQUESTS_NO_PERMISSIONS);
+ grantPermission(PKG_THAT_REQUESTS_PERMISSIONS, READ_CONTACTS);
- uninstallApp(PKG_THAT_REQUESTS_PERMISSIONS);
+ assertThat(isPermissionGranted(PKG_THAT_REQUESTS_PERMISSIONS, READ_CONTACTS)).isTrue();
+ assertThat(isPermissionGranted(PKG_THAT_REQUESTS_NO_PERMISSIONS, READ_CONTACTS)).isTrue();
+ }
- // When the app requesting the permissions is uninstalled, the other apps are no longer
- // granted the permissions it requested.
- assertThat(isPermissionGranted(PKG_THAT_REQUESTS_NO_PERMISSIONS, READ_CONTACTS)).isFalse();
- assertThat(isPermissionGranted(PKG_THAT_REQUESTS_NO_PERMISSIONS, READ_CALENDAR)).isFalse();
- } finally {
- uninstallApp(PKG_THAT_REQUESTS_PERMISSIONS);
- uninstallApp(PKG_THAT_REQUESTS_NO_PERMISSIONS);
- }
+ @Test
+ public void revokingRuntimePermissionAffectsAllPackageInSharedUid() throws Exception {
+ install(APK_THAT_REQUESTS_PERMISSIONS);
+ install(APK_THAT_REQUESTS_NO_PERMISSIONS);
+ grantPermission(PKG_THAT_REQUESTS_PERMISSIONS, READ_CONTACTS);
+ revokePermission(PKG_THAT_REQUESTS_PERMISSIONS, READ_CONTACTS);
+
+ assertThat(isPermissionGranted(PKG_THAT_REQUESTS_PERMISSIONS, READ_CONTACTS)).isFalse();
+ assertThat(isPermissionGranted(PKG_THAT_REQUESTS_NO_PERMISSIONS, READ_CONTACTS)).isFalse();
+ }
+
+ @Test(expected = SecurityException.class)
+ public void runtimePermissionsCannotBeRevokedOnPackageThatDoesNotDeclarePermission()
+ throws Exception {
+ install(APK_THAT_REQUESTS_PERMISSIONS);
+ install(APK_THAT_REQUESTS_NO_PERMISSIONS);
+ grantPermission(PKG_THAT_REQUESTS_PERMISSIONS, READ_CONTACTS);
+
+ revokePermission(APK_THAT_REQUESTS_NO_PERMISSIONS, READ_CONTACTS);
+ }
+
+ @Test(expected = SecurityException.class)
+ public void runtimePermissionsCannotBeGrantedOnPackageThatDoesNotDeclarePermission()
+ throws Exception {
+ install(APK_THAT_REQUESTS_PERMISSIONS);
+ install(APK_THAT_REQUESTS_NO_PERMISSIONS);
+
+ grantPermission(APK_THAT_REQUESTS_NO_PERMISSIONS, READ_CONTACTS);
+ }
+
+ @Test
+ public void sharedUidLoosesRuntimePermissionWhenLastAppDeclaringItGetsUninstalled()
+ throws Exception {
+ install(APK_THAT_REQUESTS_PERMISSIONS);
+ install(APK_THAT_REQUESTS_NO_PERMISSIONS);
+ grantPermission(PKG_THAT_REQUESTS_PERMISSIONS, READ_CONTACTS);
+ uninstallApp(PKG_THAT_REQUESTS_PERMISSIONS);
+
+ assertThat(isPermissionGranted(PKG_THAT_REQUESTS_NO_PERMISSIONS, READ_CONTACTS)).isFalse();
+ }
+
+ @Test
+ public void sharedUidLoosesNormalPermissionWhenLastAppDeclaringItGetsUninstalled()
+ throws Exception {
+ install(APK_THAT_REQUESTS_PERMISSIONS);
+ install(APK_THAT_REQUESTS_NO_PERMISSIONS);
+ uninstallApp(PKG_THAT_REQUESTS_PERMISSIONS);
+
+ assertThat(isPermissionGranted(PKG_THAT_REQUESTS_NO_PERMISSIONS, INTERNET)).isFalse();
}
}
diff --git a/tests/tests/permission/src/android/permission/cts/SplitPermissionTest.java b/tests/tests/permission/src/android/permission/cts/SplitPermissionTest.java
index 5809cfd..7731a11 100644
--- a/tests/tests/permission/src/android/permission/cts/SplitPermissionTest.java
+++ b/tests/tests/permission/src/android/permission/cts/SplitPermissionTest.java
@@ -29,6 +29,7 @@
import static android.permission.cts.PermissionUtils.getPermissions;
import static android.permission.cts.PermissionUtils.grantPermission;
import static android.permission.cts.PermissionUtils.isGranted;
+import static android.permission.cts.PermissionUtils.revokePermission;
import static android.permission.cts.PermissionUtils.setAppOp;
import static android.permission.cts.PermissionUtils.setPermissionFlags;
import static android.permission.cts.PermissionUtils.uninstallApp;
@@ -78,36 +79,15 @@
TMP_DIR + "CtsAppThatRequestsLocationPermission22.apk";
private static final String APK_LOCATION_BACKGROUND_29 =
TMP_DIR + "CtsAppThatRequestsLocationAndBackgroundPermission29.apk";
+ private static final String APK_SHARED_UID_LOCATION_29 =
+ TMP_DIR + "CtsAppWithSharedUidThatRequestsLocationPermission29.apk";
+ private static final String APK_SHARED_UID_LOCATION_28 =
+ TMP_DIR + "CtsAppWithSharedUidThatRequestsLocationPermission28.apk";
private static final UiAutomation sUiAutomation =
InstrumentationRegistry.getInstrumentation().getUiAutomation();
/**
- * Revoke a permission from an app.
- *
- * <p>This correctly handles pre-M apps by setting the app-ops.
- * <p>This also correctly handles the location background permission, but does not handle any
- * other background permission
- *
- * @param packageName The app that should have the permission revoked
- * @param permission The permission to revoke
- */
- private void revokePermission(@NonNull String packageName, @NonNull String permission)
- throws Exception {
- sUiAutomation.revokeRuntimePermission(packageName, permission);
-
- if (permission.equals(ACCESS_BACKGROUND_LOCATION)) {
- // The app-op for background location is encoded into the mode of the foreground
- // location
- if (isGranted(packageName, ACCESS_COARSE_LOCATION)) {
- setAppOp(packageName, ACCESS_COARSE_LOCATION, MODE_FOREGROUND);
- }
- } else {
- setAppOp(packageName, permission, MODE_IGNORED);
- }
- }
-
- /**
* Assert that {@link #APP_PKG} requests a certain permission.
*
* @param permName The permission that needs to be requested
@@ -277,6 +257,22 @@
}
/**
+ * If a permission was granted before the split happens, the new permission should inherit the
+ * granted state.
+ *
+ * <p>App using a shared uid
+ */
+ @Test
+ public void inheritGrantedPermissionStateSharedUidApp() throws Exception {
+ install(APK_SHARED_UID_LOCATION_29);
+ grantPermission(APP_PKG, ACCESS_COARSE_LOCATION);
+
+ install(APK_SHARED_UID_LOCATION_28);
+
+ assertPermissionGranted(ACCESS_BACKGROUND_LOCATION);
+ }
+
+ /**
* If a permission has flags before the split happens, the new permission should inherit the
* flags.
*
diff --git a/tests/tests/permission2/Android.bp b/tests/tests/permission2/Android.bp
new file mode 100644
index 0000000..2ad4e7b
--- /dev/null
+++ b/tests/tests/permission2/Android.bp
@@ -0,0 +1,43 @@
+//
+// 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.
+//
+
+android_test {
+ name: "CtsPermission2TestCases",
+ defaults: ["cts_defaults"],
+
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "vts",
+ "general-tests",
+ "cts_instant",
+ ],
+
+ libs: ["android.test.base.stubs"],
+
+ static_libs: [
+ "androidx.test.core",
+ "compatibility-device-util-axt",
+ "ctstestrunner-axt",
+ "guava",
+ "androidx.test.ext.junit-nodeps",
+ "truth-prebuilt"
+ ],
+
+ srcs: ["src/**/*.java"],
+
+ sdk_version: "test_current",
+}
diff --git a/tests/tests/permission2/res/raw/OWNERS b/tests/tests/permission2/res/raw/OWNERS
index 17e828d..f46dbfe 100644
--- a/tests/tests/permission2/res/raw/OWNERS
+++ b/tests/tests/permission2/res/raw/OWNERS
@@ -6,3 +6,4 @@
yamasani@google.com
michaelwr@google.com
narayan@google.com
+per-file automotive_android_manifest.xml = sgurun@google.com
\ 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 f931968..9fe8524 100644
--- a/tests/tests/permission2/res/raw/android_manifest.xml
+++ b/tests/tests/permission2/res/raw/android_manifest.xml
@@ -827,6 +827,7 @@
android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_sdcardRead"
android:description="@string/permdesc_sdcardRead"
+ android:permissionFlags="softRestricted|immutablyRestricted"
android:protectionLevel="dangerous" />
<!-- Allows an application to write to external storage.
@@ -847,6 +848,7 @@
android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_sdcardWrite"
android:description="@string/permdesc_sdcardWrite"
+ android:permissionFlags="softRestricted|immutablyRestricted"
android:protectionLevel="dangerous" />
<!-- Allows an application to access any geographic locations persisted in the
diff --git a/tests/tests/permission2/res/raw/automotive_android_manifest.xml b/tests/tests/permission2/res/raw/automotive_android_manifest.xml
index 975796d..fac0789 100644
--- a/tests/tests/permission2/res/raw/automotive_android_manifest.xml
+++ b/tests/tests/permission2/res/raw/automotive_android_manifest.xml
@@ -21,48 +21,22 @@
android:sharedUserId="android.uid.system">
<original-package android:name="com.android.car" />
-
- <permission-group
+ <permission-group
android:name="android.car.permission-group.CAR_MONITORING"
- android:icon="@drawable/car_ic_mode"
+ android:icon="@drawable/perm_group_car"
android:description="@string/car_permission_desc"
android:label="@string/car_permission_label" />
<permission
- android:name="android.car.permission.ADJUST_CAR_CABIN"
- android:protectionLevel="system|signature"
- android:label="@string/car_permission_label_cabin"
- android:description="@string/car_permission_desc_cabin" />
- <permission
android:name="android.car.permission.CAR_ENERGY"
android:permissionGroup="android.car.permission-group.CAR_MONITORING"
android:protectionLevel="dangerous"
android:label="@string/car_permission_label_energy"
android:description="@string/car_permission_desc_energy" />
<permission
- android:name="android.car.permission.CAR_EXTERIOR_ENVIRONMENT"
- android:protectionLevel="normal"
- android:label="@string/car_permission_label_car_exterior_environment"
- android:description="@string/car_permission_desc_car_exterior_environment" />
- <permission
- android:name="android.car.permission.CAR_ENERGY_PORTS"
- android:protectionLevel="normal"
- android:label="@string/car_permission_label_car_energy_ports"
- android:description="@string/car_permission_desc_car_energy_ports" />
- <permission
- android:name="android.car.permission.CAR_POWERTRAIN"
- android:protectionLevel="normal"
- android:label="@string/car_permission_label_car_powertrain"
- android:description="@string/car_permission_desc_car_powertrain" />
- <permission
- android:name="android.car.permission.CAR_EXTERIOR_LIGHTS"
+ android:name="android.car.permission.CAR_IDENTIFICATION"
android:protectionLevel="system|signature"
- android:label="@string/car_permission_label_car_exterior_lights"
- android:description="@string/car_permission_desc_car_exterior_lights" />
- <permission
- android:name="android.car.permission.CONTROL_CAR_EXTERIOR_LIGHTS"
- android:protectionLevel="system|signature"
- android:label="@string/car_permission_label_control_car_exterior_lights"
- android:description="@string/car_permission_desc_control_car_exterior_lights" />
+ android:label="@string/car_permission_label_car_identification"
+ android:description="@string/car_permission_desc_car_identification" />
<permission
android:name="android.car.permission.CONTROL_CAR_CLIMATE"
android:protectionLevel="system|signature"
@@ -94,32 +68,42 @@
android:label="@string/car_permission_label_mileage"
android:description="@string/car_permission_desc_mileage" />
<permission
+ android:name="android.car.permission.CAR_TIRES"
+ android:protectionLevel="system|signature"
+ android:label="@string/car_permission_label_car_tires"
+ android:description="@string/car_permission_desc_car_tires" />
+ <permission
+ android:name="android.car.permission.READ_CAR_STEERING"
+ android:protectionLevel="system|signature"
+ android:label="@string/car_permission_label_car_steering"
+ android:description="@string/car_permission_desc_car_steering" />
+ <permission
+ android:name="android.car.permission.READ_CAR_DISPLAY_UNITS"
+ android:protectionLevel="normal"
+ android:label="@string/car_permission_label_read_car_display_units"
+ android:description="@string/car_permission_desc_read_car_display_units" />
+ <permission
+ android:name="android.car.permission.CONTROL_CAR_DISPLAY_UNITS"
+ android:protectionLevel="normal"
+ android:label="@string/car_permission_label_control_car_display_units"
+ android:description="@string/car_permission_desc_control_car_display_units" />
+ <permission
android:name="android.car.permission.CAR_SPEED"
android:permissionGroup="android.permission-group.LOCATION"
android:protectionLevel="dangerous"
android:label="@string/car_permission_label_speed"
android:description="@string/car_permission_desc_speed" />
<permission
+ android:name="android.car.permission.CAR_ENERGY_PORTS"
+ android:protectionLevel="normal"
+ android:label="@string/car_permission_label_car_energy_ports"
+ android:description="@string/car_permission_desc_car_energy_ports" />
+ <permission
android:name="android.car.permission.CAR_ENGINE_DETAILED"
android:protectionLevel="system|signature"
android:label="@string/car_permission_label_car_engine_detailed"
android:description="@string/car_permission_desc_car_engine_detailed" />
<permission
- android:name="android.car.permission.CAR_TIRES"
- android:protectionLevel="system|signature"
- android:label="@string/car_permission_label_car_tires"
- android:description="@string/car_permission_desc_car_tires" />
- <permission
- android:name="android.car.permission.CAR_IDENTIFICATION"
- android:protectionLevel="system|signature"
- android:label="@string/car_permission_label_car_identification"
- android:description="@string/car_permission_desc_car_identification" />
- <permission
- android:name="android.car.permission.CAR_INFO"
- android:protectionLevel="normal"
- android:label="@string/car_permission_label_car_info"
- android:description="@string/car_permission_desc_car_info" />
- <permission
android:name="android.car.permission.CAR_DYNAMICS_STATE"
android:protectionLevel="system|signature"
android:label="@string/car_permission_label_vehicle_dynamics_state"
@@ -135,11 +119,61 @@
android:label="@string/car_permission_label_projection"
android:description="@string/car_permission_desc_projection" />
<permission
+ android:name="android.car.permission.ACCESS_CAR_PROJECTION_STATUS"
+ android:protectionLevel="system|signature"
+ android:label="@string/car_permission_label_access_projection_status"
+ android:description="@string/car_permission_desc_access_projection_status" />
+ <permission
+ android:name="android.car.permission.BIND_PROJECTION_SERVICE"
+ android:protectionLevel="signature"
+ android:label="@string/car_permission_label_bind_projection_service"
+ android:description="@string/car_permission_desc_bind_projection_service" />
+ <permission
android:name="android.car.permission.CAR_MOCK_VEHICLE_HAL"
android:protectionLevel="system|signature"
android:label="@string/car_permission_label_mock_vehicle_hal"
android:description="@string/car_permission_desc_mock_vehicle_hal" />
<permission
+ android:name="android.car.permission.CAR_INFO"
+ android:protectionLevel="normal"
+ android:label="@string/car_permission_label_car_info"
+ android:description="@string/car_permission_desc_car_info" />
+ <permission
+ android:name="android.car.permission.CAR_EXTERIOR_ENVIRONMENT"
+ android:protectionLevel="normal"
+ android:label="@string/car_permission_label_car_exterior_environment"
+ android:description="@string/car_permission_desc_car_exterior_environment" />
+ <permission
+ android:name="android.car.permission.CAR_EXTERIOR_LIGHTS"
+ android:protectionLevel="system|signature"
+ android:label="@string/car_permission_label_car_exterior_lights"
+ android:description="@string/car_permission_desc_car_exterior_lights" />
+ <permission
+ android:name="android.car.permission.CONTROL_CAR_EXTERIOR_LIGHTS"
+ android:protectionLevel="system|signature"
+ android:label="@string/car_permission_label_control_car_exterior_lights"
+ android:description="@string/car_permission_desc_control_car_exterior_lights" />
+ <permission
+ android:name="android.car.permission.READ_CAR_INTERIOR_LIGHTS"
+ android:protectionLevel="system|signature"
+ android:label="@string/car_permission_label_car_interior_lights"
+ android:description="@string/car_permission_desc_car_interior_lights" />
+ <permission
+ android:name="android.car.permission.CONTROL_CAR_INTERIOR_LIGHTS"
+ android:protectionLevel="system|signature"
+ android:label="@string/car_permission_label_control_car_interior_lights"
+ android:description="@string/car_permission_desc_control_car_interior_lights" />
+ <permission
+ android:name="android.car.permission.CAR_POWER"
+ android:protectionLevel="system|signature"
+ android:label="@string/car_permission_label_car_power"
+ android:description="@string/car_permission_desc_car_power" />
+ <permission
+ android:name="android.car.permission.CAR_POWERTRAIN"
+ android:protectionLevel="normal"
+ android:label="@string/car_permission_label_car_powertrain"
+ android:description="@string/car_permission_desc_car_powertrain" />
+ <permission
android:name="android.car.permission.CAR_NAVIGATION_MANAGER"
android:protectionLevel="system|signature"
android:label="@string/car_permission_car_navigation_manager"
@@ -150,10 +184,15 @@
android:label="@string/car_permission_label_diag_read"
android:description="@string/car_permission_desc_diag_read" />
<permission
- android:name="android.car.permission.CLEAR_CAR_DIAGNOSTICS"
- android:protectionLevel="system|signature"
- android:label="@string/car_permission_label_diag_clear"
- android:description="@string/car_permission_desc_diag_clear" />
+ android:name="android.car.permission.CLEAR_CAR_DIAGNOSTICS"
+ android:protectionLevel="system|signature"
+ android:label="@string/car_permission_label_diag_clear"
+ android:description="@string/car_permission_desc_diag_clear" />
+ <permission
+ android:name="android.car.permission.BIND_VMS_CLIENT"
+ android:protectionLevel="signature"
+ android:label="@string/car_permission_label_bind_vms_client"
+ android:description="@string/car_permission_desc_bind_vms_client" />
<permission
android:name="android.car.permission.VMS_PUBLISHER"
android:protectionLevel="system|signature"
@@ -189,33 +228,59 @@
android:description="@string/car_permission_desc_audio_settings" />
<permission
- android:name="android.car.permission.BIND_INSTRUMENT_CLUSTER_RENDERER_SERVICE"
- android:protectionLevel="signature"
- android:label="@string/car_permission_label_bind_instrument_cluster_rendering"
- android:description="@string/car_permission_desc_bind_instrument_cluster_rendering"/>
+ android:name="android.car.permission.RECEIVE_CAR_AUDIO_DUCKING_EVENTS"
+ android:protectionLevel="system|signature"
+ android:label="@string/car_permission_label_receive_ducking"
+ android:description="@string/car_permission_desc_receive_ducking" />
<permission
- android:name="android.car.permission.BIND_CAR_INPUT_SERVICE"
- android:protectionLevel="signature"
- android:label="@string/car_permission_label_bind_input_service"
- android:description="@string/car_permission_desc_bind_input_service"/>
+ android:name="android.car.permission.BIND_INSTRUMENT_CLUSTER_RENDERER_SERVICE"
+ android:protectionLevel="signature"
+ android:label="@string/car_permission_label_bind_instrument_cluster_rendering"
+ android:description="@string/car_permission_desc_bind_instrument_cluster_rendering"/>
<permission
- android:name="android.car.permission.CAR_DISPLAY_IN_CLUSTER"
- android:protectionLevel="system|signature"
- android:label="@string/car_permission_car_display_in_cluster"
- android:description="@string/car_permission_desc_car_display_in_cluster" />
+ android:name="android.car.permission.BIND_CAR_INPUT_SERVICE"
+ android:protectionLevel="signature"
+ android:label="@string/car_permission_label_bind_input_service"
+ android:description="@string/car_permission_desc_bind_input_service"/>
- <permission android:name="android.car.permission.CAR_INSTRUMENT_CLUSTER_CONTROL"
- android:protectionLevel="system|signature"
- android:label="@string/car_permission_car_cluster_control"
- android:description="@string/car_permission_desc_car_cluster_control" />
+ <permission
+ android:name="android.car.permission.CAR_DISPLAY_IN_CLUSTER"
+ android:protectionLevel="system|signature"
+ android:label="@string/car_permission_car_display_in_cluster"
+ android:description="@string/car_permission_desc_car_display_in_cluster" />
- <permission android:name="android.car.permission.STORAGE_MONITORING"
+ <permission
+ android:name="android.car.permission.CAR_INSTRUMENT_CLUSTER_CONTROL"
+ android:protectionLevel="system|signature"
+ android:label="@string/car_permission_car_cluster_control"
+ android:description="@string/car_permission_desc_car_cluster_control" />
+
+ <permission
+ android:name="android.car.permission.CAR_HANDLE_USB_AOAP_DEVICE"
+ android:protectionLevel="system|signature"
+ android:label="@string/car_permission_label_car_handle_usb_aoap_device"
+ android:description="@string/car_permission_desc_car_handle_usb_aoap_device" />
+
+ <permission
+ android:name="android.car.permission.CAR_UX_RESTRICTIONS_CONFIGURATION"
+ android:protectionLevel="system|signature"
+ android:label="@string/car_permission_label_car_ux_restrictions_configuration"
+ android:description="@string/car_permission_desc_car_ux_restrictions_configuration" />
+
+ <permission
+ android:name="android.car.permission.STORAGE_MONITORING"
android:protectionLevel="system|signature"
android:label="@string/car_permission_label_storage_monitoring"
android:description="@string/car_permission_desc_storage_monitoring" />
+ <permission
+ android:name="android.car.permission.CAR_ENROLL_TRUST"
+ android:protectionLevel="system|signature"
+ android:label="@string/car_permission_label_enroll_trust"
+ android:description="@string/car_permission_desc_enroll_trust" />
+
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.DEVICE_POWER" />
<uses-permission android:name="android.permission.GRANT_RUNTIME_PERMISSIONS" />
@@ -236,8 +301,9 @@
<uses-permission android:name="android.permission.MANAGE_USERS" />
<uses-permission android:name="android.permission.LOCATION_HARDWARE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+ <uses-permission android:name="android.permission.PROVIDE_TRUST_AGENT" />
- <application android:label="Car service"
+ <application android:label="@string/app_title"
android:directBootAware="true"
android:allowBackup="false"
android:persistent="true">
@@ -250,9 +316,26 @@
</intent-filter>
</service>
<service android:name=".PerUserCarService" android:exported="false" />
+
+ <service
+ android:name="com.android.car.trust.CarBleTrustAgent"
+ android:permission="android.permission.BIND_TRUST_AGENT"
+ android:singleUser="true">
+ <intent-filter>
+ <action android:name="android.service.trust.TrustAgentService" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ <!-- Warning: the meta data must be included if the service is direct boot aware.
+ If not included, the device will crash before boot completes. Rendering the
+ device unusable. -->
+ <meta-data android:name="android.service.trust.trustagent"
+ android:resource="@xml/car_trust_agent"/>
+ </service>
<activity android:name="com.android.car.pm.ActivityBlockingActivity"
android:excludeFromRecents="true"
- android:exported="false">
+ android:theme="@android:style/Theme.Translucent.NoTitleBar"
+ android:exported="false"
+ android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</intent-filter>
diff --git a/tests/tests/permission2/src/android/permission2/cts/PermissionPolicyTest.java b/tests/tests/permission2/src/android/permission2/cts/PermissionPolicyTest.java
index e0e19c8..9e09a7b 100644
--- a/tests/tests/permission2/src/android/permission2/cts/PermissionPolicyTest.java
+++ b/tests/tests/permission2/src/android/permission2/cts/PermissionPolicyTest.java
@@ -16,8 +16,12 @@
package android.permission2.cts;
+import static android.content.pm.PermissionInfo.FLAG_INSTALLED;
+import static android.content.pm.PermissionInfo.PROTECTION_MASK_BASE;
import static android.os.Build.VERSION.SECURITY_PATCH;
+import static com.google.common.truth.Truth.assertThat;
+
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
@@ -26,13 +30,18 @@
import android.content.pm.PermissionInfo;
import android.os.storage.StorageManager;
import android.platform.test.annotations.AppModeFull;
-import android.test.AndroidTestCase;
-import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
import android.util.Xml;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
import org.xmlpull.v1.XmlPullParser;
import java.io.InputStream;
@@ -50,7 +59,8 @@
* Tests for permission policy on the platform.
*/
@AppModeFull(reason = "Instant apps cannot read the system servers permission")
-public class PermissionPolicyTest extends AndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+public class PermissionPolicyTest {
private static final Date HIDE_NON_SYSTEM_OVERLAY_WINDOWS_PATCH_DATE = parseDate("2017-11-01");
private static final String HIDE_NON_SYSTEM_OVERLAY_WINDOWS_PERMISSION
= "android.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS";
@@ -68,15 +78,21 @@
private static final String ATTR_NAME = "name";
private static final String ATTR_PERMISSION_GROUP = "permissionGroup";
+ private static final String ATTR_PERMISSION_FLAGS = "permissionFlags";
private static final String ATTR_PROTECTION_LEVEL = "protectionLevel";
+ private static final String ATTR_BACKGROUND_PERMISSION = "backgroundPermission";
- public void testPlatformPermissionPolicyUnaltered() throws Exception {
+ private static final Context sContext =
+ InstrumentationRegistry.getInstrumentation().getTargetContext();
+
+ @Test
+ public void platformPermissionPolicyIsUnaltered() throws Exception {
Map<String, PermissionInfo> declaredPermissionsMap =
- getPermissionsForPackage(getContext(), PLATFORM_PACKAGE_NAME);
+ getPermissionsForPackage(sContext, PLATFORM_PACKAGE_NAME);
List<String> offendingList = new ArrayList<>();
- List<PermissionGroupInfo> declaredGroups = getContext().getPackageManager()
+ List<PermissionGroupInfo> declaredGroups = sContext.getPackageManager()
.getAllPermissionGroups(0);
Set<String> declaredGroupsSet = new ArraySet<>();
for (PermissionGroupInfo declaredGroup : declaredGroups) {
@@ -85,15 +101,16 @@
Set<String> expectedPermissionGroups = loadExpectedPermissionGroupNames(
R.raw.android_manifest);
- List<PermissionInfo> expectedPermissions = loadExpectedPermissions(R.raw.android_manifest);
+ List<ExpectedPermissionInfo> expectedPermissions = loadExpectedPermissions(
+ R.raw.android_manifest);
- if (getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
+ if (sContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
expectedPermissions.addAll(loadExpectedPermissions(R.raw.automotive_android_manifest));
declaredPermissionsMap.putAll(
- getPermissionsForPackage(getContext(), AUTOMOTIVE_SERVICE_PACKAGE_NAME));
+ getPermissionsForPackage(sContext, AUTOMOTIVE_SERVICE_PACKAGE_NAME));
}
- for (PermissionInfo expectedPermission : expectedPermissions) {
+ for (ExpectedPermissionInfo expectedPermission : expectedPermissions) {
String expectedPermissionName = expectedPermission.name;
if (shouldSkipPermission(expectedPermissionName)) {
continue;
@@ -111,9 +128,9 @@
// OEMs cannot change permission protection
final int expectedProtection = expectedPermission.protectionLevel
- & PermissionInfo.PROTECTION_MASK_BASE;
+ & PROTECTION_MASK_BASE;
final int declaredProtection = declaredPermission.protectionLevel
- & PermissionInfo.PROTECTION_MASK_BASE;
+ & PROTECTION_MASK_BASE;
if (expectedProtection != declaredProtection) {
offendingList.add(
String.format(
@@ -121,8 +138,21 @@
expectedPermissionName, declaredProtection, expectedProtection));
}
+ // OEMs cannot change permission flags
+ final int expectedFlags = expectedPermission.flags;
+ final int declaredFlags = (declaredPermission.flags & ~FLAG_INSTALLED);
+ if (expectedFlags != declaredFlags) {
+ offendingList.add(
+ String.format(
+ "Permission %s invalid flags %x, expected %x",
+ expectedPermissionName,
+ declaredFlags,
+ expectedFlags));
+ }
+
// OEMs cannot change permission protection flags
- final int expectedProtectionFlags = expectedPermission.getProtectionFlags();
+ final int expectedProtectionFlags =
+ expectedPermission.protectionLevel & ~PROTECTION_MASK_BASE;
final int declaredProtectionFlags = declaredPermission.getProtectionFlags();
if (expectedProtectionFlags != declaredProtectionFlags) {
offendingList.add(
@@ -148,6 +178,17 @@
"Permission group " + expectedPermission.group + " must be defined");
}
}
+
+ // OEMs cannot change background permission mapping
+ if (!Objects.equals(expectedPermission.backgroundPermission,
+ declaredPermission.backgroundPermission)) {
+ offendingList.add(
+ String.format(
+ "Permission %s invalid background permission %s, expected %s",
+ expectedPermissionName,
+ declaredPermission.backgroundPermission,
+ expectedPermission.backgroundPermission));
+ }
}
// OEMs cannot define permissions in the platform namespace
@@ -185,16 +226,12 @@
}
// Fail on any offending item
- String errMsg =
- String.format(
- "Platform Permission Policy Unaltered:\n%s",
- TextUtils.join("\n", offendingList));
- assertTrue(errMsg, offendingList.isEmpty());
+ assertThat(offendingList).named("list of offending permissions").isEmpty();
}
- private List<PermissionInfo> loadExpectedPermissions(int resourceId) throws Exception {
- List<PermissionInfo> permissions = new ArrayList<>();
- try (InputStream in = getContext().getResources().openRawResource(resourceId)) {
+ private List<ExpectedPermissionInfo> loadExpectedPermissions(int resourceId) throws Exception {
+ List<ExpectedPermissionInfo> permissions = new ArrayList<>();
+ try (InputStream in = sContext.getResources().openRawResource(resourceId)) {
XmlPullParser parser = Xml.newPullParser();
parser.setInput(in, null);
@@ -206,11 +243,14 @@
continue;
}
if (TAG_PERMISSION.equals(parser.getName())) {
- PermissionInfo permissionInfo = new PermissionInfo();
- permissionInfo.name = parser.getAttributeValue(null, ATTR_NAME);
- permissionInfo.group = parser.getAttributeValue(null, ATTR_PERMISSION_GROUP);
- permissionInfo.protectionLevel = parseProtectionLevel(
- parser.getAttributeValue(null, ATTR_PROTECTION_LEVEL));
+ ExpectedPermissionInfo permissionInfo = new ExpectedPermissionInfo(
+ parser.getAttributeValue(null, ATTR_NAME),
+ parser.getAttributeValue(null, ATTR_PERMISSION_GROUP),
+ parser.getAttributeValue(null, ATTR_BACKGROUND_PERMISSION),
+ parsePermissionFlags(
+ parser.getAttributeValue(null, ATTR_PERMISSION_FLAGS)),
+ parseProtectionLevel(
+ parser.getAttributeValue(null, ATTR_PROTECTION_LEVEL)));
permissions.add(permissionInfo);
} else {
Log.e(LOG_TAG, "Unknown tag " + parser.getName());
@@ -220,9 +260,9 @@
// STOPSHIP: remove this once isolated storage is always enabled
if (!StorageManager.hasIsolatedStorage()) {
- Iterator<PermissionInfo> it = permissions.iterator();
+ Iterator<ExpectedPermissionInfo> it = permissions.iterator();
while (it.hasNext()) {
- final PermissionInfo pi = it.next();
+ final ExpectedPermissionInfo pi = it.next();
switch (pi.name) {
case android.Manifest.permission.ACCESS_MEDIA_LOCATION:
case android.Manifest.permission.WRITE_OBB:
@@ -237,7 +277,7 @@
private Set<String> loadExpectedPermissionGroupNames(int resourceId) throws Exception {
ArraySet<String> permissionGroups = new ArraySet<>();
- try (InputStream in = getContext().getResources().openRawResource(resourceId)) {
+ try (InputStream in = sContext.getResources().openRawResource(resourceId)) {
XmlPullParser parser = Xml.newPullParser();
parser.setInput(in, null);
@@ -258,6 +298,35 @@
return permissionGroups;
}
+ private static int parsePermissionFlags(@Nullable String permissionFlagsString) {
+ if (permissionFlagsString == null) {
+ return 0;
+ }
+
+ int protectionFlags = 0;
+ String[] fragments = permissionFlagsString.split("\\|");
+ for (String fragment : fragments) {
+ switch (fragment.trim()) {
+ case "removed": {
+ protectionFlags |= PermissionInfo.FLAG_REMOVED;
+ } break;
+ case "costsMoney": {
+ protectionFlags |= PermissionInfo.FLAG_COSTS_MONEY;
+ } break;
+ case "hardRestricted": {
+ protectionFlags |= PermissionInfo.FLAG_HARD_RESTRICTED;
+ } break;
+ case "immutablyRestricted": {
+ protectionFlags |= PermissionInfo.FLAG_IMMUTABLY_RESTRICTED;
+ } break;
+ case "softRestricted": {
+ protectionFlags |= PermissionInfo.FLAG_SOFT_RESTRICTED;
+ } break;
+ }
+ }
+ return protectionFlags;
+ }
+
private static int parseProtectionLevel(String protectionLevelString) {
int protectionLevel = 0;
String[] fragments = protectionLevelString.split("\\|");
@@ -366,4 +435,21 @@
HIDE_NON_SYSTEM_OVERLAY_WINDOWS_PERMISSION.equals(permissionName);
}
+
+ private class ExpectedPermissionInfo {
+ final @NonNull String name;
+ final @Nullable String group;
+ final @Nullable String backgroundPermission;
+ final int flags;
+ final int protectionLevel;
+
+ private ExpectedPermissionInfo(@NonNull String name, @Nullable String group,
+ @Nullable String backgroundPermission, int flags, int protectionLevel) {
+ this.name = name;
+ this.group = group;
+ this.backgroundPermission = backgroundPermission;
+ this.flags = flags;
+ this.protectionLevel = protectionLevel;
+ }
+ }
}
diff --git a/tests/tests/proto/AndroidTest.xml b/tests/tests/proto/AndroidTest.xml
index 05df543..6237e8f 100644
--- a/tests/tests/proto/AndroidTest.xml
+++ b/tests/tests/proto/AndroidTest.xml
@@ -16,6 +16,8 @@
<configuration description="Configuration for Proto Tests">
<option name="test-suite-tag" value="cts" />
<option name="config-descriptor:metadata" key="component" value="metrics" />
+ <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="CtsProtoTestCases.apk" />
diff --git a/tests/tests/provider/src/android/provider/cts/DocumentsContractTest.java b/tests/tests/provider/src/android/provider/cts/DocumentsContractTest.java
index 5f4ae7b..89b231d 100644
--- a/tests/tests/provider/src/android/provider/cts/DocumentsContractTest.java
+++ b/tests/tests/provider/src/android/provider/cts/DocumentsContractTest.java
@@ -69,6 +69,9 @@
import android.provider.DocumentsContract.Path;
import android.provider.DocumentsProvider;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -78,9 +81,6 @@
import java.util.Arrays;
import java.util.List;
-import androidx.test.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
-
@RunWith(AndroidJUnit4.class)
public class DocumentsContractTest {
private static final String AUTHORITY = "com.example";
@@ -142,6 +142,23 @@
}
@Test
+ public void testRootUri_returnFalse() {
+ final String auth = "com.example";
+ final String rootId = "rootId";
+ final PackageManager pm = mock(PackageManager.class);
+ final List<ResolveInfo> infoList = new ArrayList<>();
+
+ doReturn(pm).when(mContext).getPackageManager();
+ doReturn(infoList).when(pm).queryIntentContentProviders(any(Intent.class), anyInt());
+
+ final Uri uri = DocumentsContract.buildRootUri(auth, rootId);
+
+ assertEquals(auth, uri.getAuthority());
+ assertEquals(rootId, DocumentsContract.getRootId(uri));
+ assertFalse(DocumentsContract.isRootUri(mContext, uri));
+ }
+
+ @Test
public void testRootsUri() {
final String auth = "com.example";
final PackageManager pm = mock(PackageManager.class);
@@ -162,6 +179,20 @@
}
@Test
+ public void testRootsUri_returnsFalse() {
+ final String auth = "com.example";
+ final PackageManager pm = mock(PackageManager.class);
+ final List<ResolveInfo> infoList = new ArrayList<>();
+
+ doReturn(pm).when(mContext).getPackageManager();
+ doReturn(infoList).when(pm).queryIntentContentProviders(any(Intent.class), anyInt());
+
+ final Uri uri = DocumentsContract.buildRootsUri(auth);
+ assertEquals(auth, uri.getAuthority());
+ assertFalse(DocumentsContract.isRootsUri(mContext, uri));
+ }
+
+ @Test
public void testDocumentUri() {
final String auth = "com.example";
final String docId = "doc:12";
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStorePendingTest.java b/tests/tests/provider/src/android/provider/cts/MediaStorePendingTest.java
index 409875d..85d4109 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStorePendingTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStorePendingTest.java
@@ -30,6 +30,7 @@
import android.content.ContentResolver;
import android.content.ContentUris;
+import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
@@ -386,6 +387,53 @@
assertEquals(Environment.DIRECTORY_DCIM, rawFile.getParentFile().getParentFile().getName());
}
+ @Test
+ public void testMutableColumns() throws Exception {
+ // Stage pending content
+ final ContentValues values = new ContentValues();
+ values.put(MediaColumns.MIME_TYPE, "image/png");
+ values.put(MediaColumns.IS_PENDING, 1);
+ values.put(MediaColumns.HEIGHT, 32);
+ final Uri uri = mResolver.insert(mExternalImages, values);
+ try (InputStream in = mContext.getResources().openRawResource(R.raw.scenery);
+ OutputStream out = mResolver.openOutputStream(uri)) {
+ FileUtils.copy(in, out);
+ }
+
+ // Verify that initial values are present
+ try (Cursor c = mResolver.query(uri, null, null, null)) {
+ c.moveToFirst();
+ assertEquals(32, c.getLong(c.getColumnIndexOrThrow(MediaColumns.HEIGHT)));
+ }
+
+ // Verify that we can update values while pending
+ values.clear();
+ values.put(MediaColumns.HEIGHT, 64);
+ mResolver.update(uri, values, null, null);
+ try (Cursor c = mResolver.query(uri, null, null, null)) {
+ c.moveToFirst();
+ assertEquals(64, c.getLong(c.getColumnIndexOrThrow(MediaColumns.HEIGHT)));
+ }
+
+ // Publishing triggers scan of underlying file
+ values.clear();
+ values.put(MediaColumns.IS_PENDING, 0);
+ mResolver.update(uri, values, null, null);
+ try (Cursor c = mResolver.query(uri, null, null, null)) {
+ c.moveToFirst();
+ assertEquals(107, c.getLong(c.getColumnIndexOrThrow(MediaColumns.HEIGHT)));
+ }
+
+ // Ignored now that we're published
+ values.clear();
+ values.put(MediaColumns.HEIGHT, 48);
+ mResolver.update(uri, values, null, null);
+ try (Cursor c = mResolver.query(uri, null, null, null)) {
+ c.moveToFirst();
+ assertEquals(107, c.getLong(c.getColumnIndexOrThrow(MediaColumns.HEIGHT)));
+ }
+ }
+
private void assertCreatePending(PendingParams params) {
MediaStoreUtils.createPending(mContext, params);
}
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_DownloadsTest.java b/tests/tests/provider/src/android/provider/cts/MediaStore_DownloadsTest.java
index 51b9907..1c45687 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_DownloadsTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStore_DownloadsTest.java
@@ -43,6 +43,7 @@
import androidx.test.InstrumentationRegistry;
+import org.junit.Assume;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -105,6 +106,9 @@
@Test
public void testScannedDownload() throws Exception {
+ Assume.assumeTrue(MediaStore.VOLUME_EXTERNAL.equals(mVolumeName)
+ || MediaStore.VOLUME_EXTERNAL_PRIMARY.equals(mVolumeName));
+
final File downloadFile = new File(mDownloadsDir, "colors.txt");
downloadFile.createNewFile();
final String fileContents = "RED;GREEN;BLUE";
@@ -116,6 +120,9 @@
@Test
public void testScannedMediaDownload() throws Exception {
+ Assume.assumeTrue(MediaStore.VOLUME_EXTERNAL.equals(mVolumeName)
+ || MediaStore.VOLUME_EXTERNAL_PRIMARY.equals(mVolumeName));
+
final File downloadFile = new File(mDownloadsDir, "scenery.png");
downloadFile.createNewFile();
try (InputStream in = mContext.getResources().openRawResource(R.raw.scenery);
@@ -135,6 +142,9 @@
@Test
public void testMediaInDownloadsDir() throws Exception {
+ Assume.assumeTrue(MediaStore.VOLUME_EXTERNAL.equals(mVolumeName)
+ || MediaStore.VOLUME_EXTERNAL_PRIMARY.equals(mVolumeName));
+
final String displayName = "cts" + System.nanoTime();
final Uri insertUri = insertImage(displayName, "test image",
new File(mDownloadsDir, displayName + ".jpg"), "image/jpeg", R.raw.scenery);
diff --git a/tests/tests/role/CtsRoleTestApp/AndroidManifest.xml b/tests/tests/role/CtsRoleTestApp/AndroidManifest.xml
index bbc3486..4ae5d5a 100644
--- a/tests/tests/role/CtsRoleTestApp/AndroidManifest.xml
+++ b/tests/tests/role/CtsRoleTestApp/AndroidManifest.xml
@@ -85,5 +85,13 @@
<data android:scheme="http" />
</intent-filter>
</activity>
+
+ <!-- Assistant -->
+ <activity android:name=".AssistantActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.ASSIST" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
</application>
</manifest>
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 2fef392..a62b12c 100644
--- a/tests/tests/role/src/android/app/role/cts/RoleManagerTest.java
+++ b/tests/tests/role/src/android/app/role/cts/RoleManagerTest.java
@@ -279,6 +279,14 @@
respondToRoleRequest(false);
}
+ @Test
+ public void requestAssistantRoleThenDeniedAutomatically() throws InterruptedException {
+ requestRole(RoleManager.ROLE_ASSISTANT);
+ Pair<Integer, Intent> result = waitForResult();
+
+ assertThat(result.first).isEqualTo(Activity.RESULT_CANCELED);
+ }
+
private void requestRole(@NonNull String roleName) {
Intent intent = new Intent()
.setComponent(new ComponentName(APP_PACKAGE_NAME, APP_REQUEST_ROLE_ACTIVITY_NAME))
diff --git a/tests/tests/security/AndroidTest.xml b/tests/tests/security/AndroidTest.xml
index fd1043f..1d97ea2 100644
--- a/tests/tests/security/AndroidTest.xml
+++ b/tests/tests/security/AndroidTest.xml
@@ -16,6 +16,7 @@
<configuration description="Config for CTS security test cases">
<option name="test-suite-tag" value="cts" />
<option name="config-descriptor:metadata" key="component" value="security" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="CtsSecurityTestCases.apk" />
diff --git a/tests/tests/security/jni/android_security_cts_EncryptionTest.cpp b/tests/tests/security/jni/android_security_cts_EncryptionTest.cpp
index 47f760a..9d255bd 100644
--- a/tests/tests/security/jni/android_security_cts_EncryptionTest.cpp
+++ b/tests/tests/security/jni/android_security_cts_EncryptionTest.cpp
@@ -33,43 +33,6 @@
#define TEST_ITERATIONS 100 /* MiB */
#define TEST_THRESHOLD 2000 /* ms */
-/*
- * Detect if filesystem is already encrypted looking at the file
- * system type. It should be possible to check this first but fall
- * back to checking a property value if this is not possible to
- * verify.
- */
-static jboolean checkEncryptedFileSystem() {
- struct statfs buf;
- if ((-1 != statfs("/data", &buf)) &&
- (buf.f_type == 0xf15f /* ecryptfs */)) {
- return true;
- }
- return false;
-}
-
-/*
- * Function: deviceIsEncrypted
- * Purpose: Check the device is encrypted
- * Parameters: none
- * Returns: boolean: (true) if encrypted, (false) otherwise
- * Exceptions: none
- */
-static jboolean android_security_cts_EncryptionTest_deviceIsEncrypted(JNIEnv *, jobject)
-{
- if (checkEncryptedFileSystem()) {
- return true;
- }
-
- char prop_value[PROP_VALUE_MAX];
- property_get("ro.crypto.state", prop_value, "");
-
- jboolean rc = !strcmp(prop_value, "encrypted");
- ALOGE("EncryptionTest::deviceIsEncrypted: %d", rc);
-
- return rc;
-}
-
static inline uint64_t ns()
{
struct timespec ts;
@@ -78,12 +41,7 @@
}
/*
- * Function: aesIsFast
- * Purpose: Test if AES performance is sufficient to require encryption
- * Parameters: none
- * Returns: boolean: (true) if AES performance is acceptable, (false) otherwise
- * Exceptions: InvalidKeyException if EVP_DecryptInit fails, OutOfMemoryError
- * if memory allocation fails.
+ * Test if AES performance is sufficient to require encryption
*/
static jboolean android_security_cts_EncryptionTest_aesIsFast(JNIEnv *env, jobject)
{
@@ -136,8 +94,6 @@
}
static JNINativeMethod gMethods[] = {
- { "deviceIsEncrypted", "()Z",
- (void *) android_security_cts_EncryptionTest_deviceIsEncrypted },
{ "aesIsFast", "()Z",
(void *) android_security_cts_EncryptionTest_aesIsFast }
};
diff --git a/tests/tests/security/src/android/security/cts/EncryptionTest.java b/tests/tests/security/src/android/security/cts/EncryptionTest.java
index 07b39de..f021033a 100644
--- a/tests/tests/security/src/android/security/cts/EncryptionTest.java
+++ b/tests/tests/security/src/android/security/cts/EncryptionTest.java
@@ -22,39 +22,65 @@
import android.test.AndroidTestCase;
import junit.framework.TestCase;
-import android.content.Context;
+import android.os.Build;
import android.util.Log;
-import java.io.BufferedReader;
-import java.io.FileReader;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
@SecurityTest
public class EncryptionTest extends AndroidTestCase {
-
static {
System.loadLibrary("ctssecurity_jni");
}
- private static final int MIN_API_LEVEL = 23;
+ private static final int MIN_ENCRYPTION_REQUIRED_API_LEVEL = 23;
+
+ // First API level where there are no speed exemptions.
+ private static final int MIN_ALL_SPEEDS_API_LEVEL = Build.VERSION_CODES.Q;
+
+ // First API level at which file based encryption must be used.
+ private static final int MIN_FBE_REQUIRED_API_LEVEL = Build.VERSION_CODES.Q;
private static final String TAG = "EncryptionTest";
- private static native boolean deviceIsEncrypted();
-
private static native boolean aesIsFast();
- private boolean isRequired() {
- // Optional before MIN_API_LEVEL
- return PropertyUtil.getFirstApiLevel() >= MIN_API_LEVEL;
+ private void handleUnencryptedDevice() {
+ if (PropertyUtil.getFirstApiLevel() < MIN_ENCRYPTION_REQUIRED_API_LEVEL) {
+ Log.d(TAG, "Exempt from encryption due to an old starting API level.");
+ return;
+ }
+ // In older API levels, we grant an exemption if AES is not fast enough.
+ if (PropertyUtil.getFirstApiLevel() < MIN_ALL_SPEEDS_API_LEVEL) {
+ // Note: aesIsFast() takes ~2 second to run, so it's worth rearranging
+ // test logic to delay calling this.
+ if (!aesIsFast()) {
+ Log.d(TAG, "Exempt from encryption because AES performance is too low.");
+ return;
+ }
+ }
+ fail("Device encryption is required");
+ }
+
+ private void handleEncryptedDevice() {
+ if ("file".equals(PropertyUtil.getProperty("ro.crypto.type"))) {
+ Log.d(TAG, "Device is encrypted with file-based encryption.");
+ // TODO(b/111311698): If we're able to determine if the hardware
+ // has AES instructions, confirm that AES, and only AES,
+ // is in use. If the hardware does not have AES instructions,
+ // confirm that either AES or Adiantum is in use.
+ return;
+ }
+ if (PropertyUtil.getFirstApiLevel() < MIN_FBE_REQUIRED_API_LEVEL) {
+ Log.d(TAG, "Device is encrypted.");
+ return;
+ }
+ fail("File-based encryption is required");
}
public void testEncryption() throws Exception {
- if (!isRequired() || deviceIsEncrypted()) {
- return;
+ if ("encrypted".equals(PropertyUtil.getProperty("ro.crypto.state"))) {
+ handleEncryptedDevice();
+ } else {
+ handleUnencryptedDevice();
}
-
- // Required if performance is sufficient
- assertFalse("Device encryption is required", aesIsFast());
}
}
diff --git a/tests/tests/selinux/selinuxTargetSdk25/AndroidTest.xml b/tests/tests/selinux/selinuxTargetSdk25/AndroidTest.xml
index f5a5f28..ae9576e 100644
--- a/tests/tests/selinux/selinuxTargetSdk25/AndroidTest.xml
+++ b/tests/tests/selinux/selinuxTargetSdk25/AndroidTest.xml
@@ -17,6 +17,7 @@
<option name="test-suite-tag" value="cts" />
<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="multi_abi" />
<option name="not-shardable" value="true" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
diff --git a/tests/tests/selinux/selinuxTargetSdk27/AndroidTest.xml b/tests/tests/selinux/selinuxTargetSdk27/AndroidTest.xml
index 8cb520a..78e5ad4 100644
--- a/tests/tests/selinux/selinuxTargetSdk27/AndroidTest.xml
+++ b/tests/tests/selinux/selinuxTargetSdk27/AndroidTest.xml
@@ -17,6 +17,7 @@
<option name="test-suite-tag" value="cts" />
<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="multi_abi" />
<option name="not-shardable" value="true" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
diff --git a/tests/tests/selinux/selinuxTargetSdk28/AndroidTest.xml b/tests/tests/selinux/selinuxTargetSdk28/AndroidTest.xml
index c587bf7..71453c9 100644
--- a/tests/tests/selinux/selinuxTargetSdk28/AndroidTest.xml
+++ b/tests/tests/selinux/selinuxTargetSdk28/AndroidTest.xml
@@ -17,6 +17,7 @@
<option name="test-suite-tag" value="cts" />
<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="multi_abi" />
<option name="not-shardable" value="true" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
diff --git a/tests/tests/selinux/selinuxTargetSdkCurrent/AndroidTest.xml b/tests/tests/selinux/selinuxTargetSdkCurrent/AndroidTest.xml
index 3ba1f45..f3f7d84 100644
--- a/tests/tests/selinux/selinuxTargetSdkCurrent/AndroidTest.xml
+++ b/tests/tests/selinux/selinuxTargetSdkCurrent/AndroidTest.xml
@@ -17,6 +17,7 @@
<option name="test-suite-tag" value="cts" />
<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="multi_abi" />
<option name="not-shardable" value="true" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
diff --git a/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerMaxCountTest.java b/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerMaxCountTest.java
index 8b18931..5643cad 100644
--- a/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerMaxCountTest.java
+++ b/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerMaxCountTest.java
@@ -39,10 +39,15 @@
makeShortcut("s2"),
makeShortcut("s3"),
makeShortcut("s4"),
- makeShortcut("s5")
+ makeShortcut("s5"),
+ makeShortcut("s6"),
+ makeShortcut("s7"),
+ makeShortcut("s8"),
+ makeShortcut("s9"),
+ makeShortcut("s10")
)));
assertWith(getManager().getDynamicShortcuts())
- .haveIds("s1", "s2", "s3", "s4", "s5")
+ .haveIds("s1", "s2", "s3", "s4", "s5", "s6", "s7", "s8", "s9", "s10")
.areAllDynamic()
.areAllEnabled();
assertTrue(getManager().setDynamicShortcuts(list(
@@ -50,7 +55,12 @@
makeShortcut("s2x"),
makeShortcut("s3x"),
makeShortcut("s4x"),
- makeShortcut("s5x")
+ makeShortcut("s5x"),
+ makeShortcut("s6x"),
+ makeShortcut("s7x"),
+ makeShortcut("s8x"),
+ makeShortcut("s9x"),
+ makeShortcut("s10x")
)));
assertDynamicShortcutCountExceeded(() -> {
@@ -60,10 +70,15 @@
makeShortcut("s3y"),
makeShortcut("s4y"),
makeShortcut("s5y"),
- makeShortcut("s6y")));
+ makeShortcut("s6y"),
+ makeShortcut("s7y"),
+ makeShortcut("s8y"),
+ makeShortcut("s9y"),
+ makeShortcut("s10y"),
+ makeShortcut("s11y")));
});
assertWith(getManager().getDynamicShortcuts())
- .haveIds("s1x", "s2x", "s3x", "s4x", "s5x")
+ .haveIds("s1x", "s2x", "s3x", "s4x", "s5x", "s6x", "s7x", "s8x", "s9x", "s10x")
.areAllDynamic()
.areAllEnabled();
@@ -72,15 +87,15 @@
makeShortcut("s1y")));
});
assertWith(getManager().getDynamicShortcuts())
- .haveIds("s1x", "s2x", "s3x", "s4x", "s5x")
+ .haveIds("s1x", "s2x", "s3x", "s4x", "s5x", "s6x", "s7x", "s8x", "s9x", "s10x")
.areAllDynamic()
.areAllEnabled();
- getManager().removeDynamicShortcuts(list("s5x"));
+ getManager().removeDynamicShortcuts(list("s10x"));
assertTrue(getManager().addDynamicShortcuts(list(
makeShortcut("s1y"))));
assertWith(getManager().getDynamicShortcuts())
- .haveIds("s1x", "s2x", "s3x", "s4x", "s1y")
+ .haveIds("s1x", "s2x", "s3x", "s4x", "s5x", "s6x", "s7x", "s8x", "s9x", "s1y")
.areAllDynamic()
.areAllEnabled();
@@ -91,10 +106,15 @@
makeShortcut("s2"),
makeShortcut("s3"),
makeShortcut("s4"),
- makeShortcut("s5")
+ makeShortcut("s5"),
+ makeShortcut("s6"),
+ makeShortcut("s7"),
+ makeShortcut("s8"),
+ makeShortcut("s9"),
+ makeShortcut("s10")
)));
assertWith(getManager().getDynamicShortcuts())
- .haveIds("s1", "s2", "s3", "s4", "s5")
+ .haveIds("s1", "s2", "s3", "s4", "s5", "s6", "s7", "s8", "s9", "s10")
.areAllDynamic()
.areAllEnabled();
});
@@ -136,7 +156,7 @@
// Note since max counts is per activity, testNumDynamicShortcuts_single should just pass.
testNumDynamicShortcuts();
- // Launcher_manifest_1 has one manifest, so can only add 4 dynamic shortcuts.
+ // Launcher_manifest_1 has one manifest, so can only add 9 dynamic shortcuts.
runWithCallerWithStrictMode(mPackageContext1, () -> {
setTargetActivityOverride("Launcher_manifest_1");
@@ -144,11 +164,16 @@
makeShortcut("s1"),
makeShortcut("s2"),
makeShortcut("s3"),
- makeShortcut("s4")
+ makeShortcut("s4"),
+ makeShortcut("s5"),
+ makeShortcut("s6"),
+ makeShortcut("s7"),
+ makeShortcut("s8"),
+ makeShortcut("s9")
)));
assertWith(getManager().getDynamicShortcuts())
.selectByActivity(getActivity("Launcher_manifest_1"))
- .haveIds("s1", "s2", "s3", "s4")
+ .haveIds("s1", "s2", "s3", "s4", "s5", "s6", "s7", "s8", "s9")
.areAllEnabled();
assertDynamicShortcutCountExceeded(() -> getManager().setDynamicShortcuts(list(
@@ -156,27 +181,37 @@
makeShortcut("s2x"),
makeShortcut("s3x"),
makeShortcut("s4x"),
- makeShortcut("s5x")
+ makeShortcut("s5x"),
+ makeShortcut("s6x"),
+ makeShortcut("s7x"),
+ makeShortcut("s8x"),
+ makeShortcut("s9x"),
+ makeShortcut("s10x")
)));
// Not changed.
assertWith(getManager().getDynamicShortcuts())
.selectByActivity(getActivity("Launcher_manifest_1"))
- .haveIds("s1", "s2", "s3", "s4")
+ .haveIds("s1", "s2", "s3", "s4", "s5", "s6", "s7", "s8", "s9")
.areAllEnabled();
});
- // Launcher_manifest_2 has two manifests, so can only add 3.
+ // Launcher_manifest_2 has two manifests, so can only add 8.
runWithCallerWithStrictMode(mPackageContext1, () -> {
setTargetActivityOverride("Launcher_manifest_2");
assertTrue(getManager().addDynamicShortcuts(list(
makeShortcut("s1"),
makeShortcut("s2"),
- makeShortcut("s3")
+ makeShortcut("s3"),
+ makeShortcut("s4"),
+ makeShortcut("s5"),
+ makeShortcut("s6"),
+ makeShortcut("s7"),
+ makeShortcut("s8")
)));
assertWith(getManager().getDynamicShortcuts())
.selectByActivity(getActivity("Launcher_manifest_2"))
- .haveIds("s1", "s2", "s3")
+ .haveIds("s1", "s2", "s3", "s4", "s5", "s6", "s7", "s8")
.areAllEnabled();
assertDynamicShortcutCountExceeded(() -> getManager().addDynamicShortcuts(list(
@@ -185,7 +220,7 @@
// Not added.
assertWith(getManager().getDynamicShortcuts())
.selectByActivity(getActivity("Launcher_manifest_2"))
- .haveIds("s1", "s2", "s3")
+ .haveIds("s1", "s2", "s3", "s4", "s5", "s6", "s7", "s8")
.areAllEnabled();
});
}
@@ -198,11 +233,16 @@
makeShortcut("s2"),
makeShortcut("s3"),
makeShortcut("s4"),
- makeShortcut("s5")
+ makeShortcut("s5"),
+ makeShortcut("s6"),
+ makeShortcut("s7"),
+ makeShortcut("s8"),
+ makeShortcut("s9"),
+ makeShortcut("s10")
)));
assertWith(getManager().getDynamicShortcuts())
.selectByActivity(getActivity("Launcher"))
- .haveIds("s1", "s2", "s3", "s4", "s5")
+ .haveIds("s1", "s2", "s3", "s4", "s5", "s6", "s7", "s8", "s9", "s10")
.areAllDynamic()
.areAllEnabled();
@@ -213,17 +253,22 @@
makeShortcut("s2b"),
makeShortcut("s3b"),
makeShortcut("s4b"),
- makeShortcut("s5b")
+ makeShortcut("s5b"),
+ makeShortcut("s6b"),
+ makeShortcut("s7b"),
+ makeShortcut("s8b"),
+ makeShortcut("s9b"),
+ makeShortcut("s10b")
)));
assertWith(getManager().getDynamicShortcuts())
.selectByActivity(getActivity("Launcher"))
- .haveIds("s1", "s2", "s3", "s4", "s5")
+ .haveIds("s1", "s2", "s3", "s4", "s5", "s6", "s7", "s8", "s9", "s10")
.areAllDynamic()
.areAllEnabled()
.revertToOriginalList()
.selectByActivity(getActivity("Launcher2"))
- .haveIds("s1b", "s2b", "s3b", "s4b", "s5b")
+ .haveIds("s1b", "s2b", "s3b", "s4b", "s5b", "s6b", "s7b", "s8b", "s9b", "s10b")
.areAllDynamic()
.areAllEnabled();
@@ -234,13 +279,13 @@
assertWith(getManager().getDynamicShortcuts())
.selectByActivity(getActivity("Launcher"))
- .haveIds("s1", "s2", "s3", "s4", "s5")
+ .haveIds("s1", "s2", "s3", "s4", "s5", "s6", "s7", "s8", "s9", "s10")
.areAllDynamic()
.areAllEnabled()
.revertToOriginalList()
.selectByActivity(getActivity("Launcher2"))
- .haveIds("s1b", "s2b", "s3b", "s4b", "s5b")
+ .haveIds("s1b", "s2b", "s3b", "s4b", "s5b", "s6b", "s7b", "s8b", "s9b", "s10b")
.areAllDynamic()
.areAllEnabled();
@@ -252,13 +297,13 @@
assertWith(getManager().getDynamicShortcuts())
.selectByActivity(getActivity("Launcher"))
- .haveIds("s1b", "s2", "s3", "s4", "s5")
+ .haveIds("s1b", "s2", "s3", "s4", "s5", "s6", "s7", "s8", "s9", "s10")
.areAllDynamic()
.areAllEnabled()
.revertToOriginalList()
.selectByActivity(getActivity("Launcher2"))
- .haveIds("s1", "s2b", "s3b", "s4b", "s5b")
+ .haveIds("s1", "s2b", "s3b", "s4b", "s5b", "s6b", "s7b", "s8b", "s9b", "s10b")
.areAllDynamic()
.areAllEnabled();
});
@@ -271,7 +316,12 @@
makeShortcut("s2"),
makeShortcut("s3"),
makeShortcut("s4"),
- makeShortcut("s5")
+ makeShortcut("s5"),
+ makeShortcut("s6"),
+ makeShortcut("s7"),
+ makeShortcut("s8"),
+ makeShortcut("s9"),
+ makeShortcut("s10")
)));
});
@@ -279,25 +329,31 @@
runWithCallerWithStrictMode(mLauncherContext1, () -> {
getLauncherApps().pinShortcuts(mPackageContext1.getPackageName(),
- list("s1", "s2", "s3", "s4", "s5"), getUserHandle());
+ list("s1", "s2", "s3", "s4", "s5", "s6", "s7", "s8", "s9", "s10"),
+ getUserHandle());
});
runWithCallerWithStrictMode(mPackageContext1, () -> {
assertTrue(getManager().setDynamicShortcuts(list(
- makeShortcut("s6"),
- makeShortcut("s7"),
- makeShortcut("s8"),
- makeShortcut("s9"),
- makeShortcut("s10")
+ makeShortcut("s1b"),
+ makeShortcut("s2b"),
+ makeShortcut("s3b"),
+ makeShortcut("s4b"),
+ makeShortcut("s5b"),
+ makeShortcut("s6b"),
+ makeShortcut("s7b"),
+ makeShortcut("s8b"),
+ makeShortcut("s9b"),
+ makeShortcut("s10b")
)));
assertWith(getManager().getDynamicShortcuts())
- .haveIds("s6", "s7", "s8", "s9", "s10")
+ .haveIds("s1b", "s2b", "s3b", "s4b", "s5b", "s6b", "s7b", "s8b", "s9b", "s10b")
.areAllEnabled()
.areAllNotPinned();
assertWith(getManager().getPinnedShortcuts())
- .haveIds("s1", "s2", "s3", "s4", "s5")
+ .haveIds("s1", "s2", "s3", "s4", "s5", "s6", "s7", "s8", "s9", "s10")
.areAllEnabled()
.areAllNotDynamic();
});
diff --git a/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerMiscTest.java b/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerMiscTest.java
index 46c8661..b3a6fa1 100644
--- a/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerMiscTest.java
+++ b/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerMiscTest.java
@@ -34,7 +34,7 @@
public void testMiscApis() throws Exception {
ShortcutManager manager = getTestContext().getSystemService(ShortcutManager.class);
- assertEquals(5, manager.getMaxShortcutCountPerActivity());
+ assertEquals(10, manager.getMaxShortcutCountPerActivity());
// during the test, this process always considered to be in the foreground.
assertFalse(manager.isRateLimitingActive());
diff --git a/tests/tests/simpleperf/AndroidTest.xml b/tests/tests/simpleperf/AndroidTest.xml
index ad94b98..bf6cb9e 100644
--- a/tests/tests/simpleperf/AndroidTest.xml
+++ b/tests/tests/simpleperf/AndroidTest.xml
@@ -16,6 +16,8 @@
<configuration description="Config for CTS Simpleperf test cases">
<option name="test-suite-tag" value="cts" />
<option name="config-descriptor:metadata" key="component" value="bionic" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
<option name="cleanup" value="true" />
<option name="push" value="CtsSimpleperfTestCases->/data/local/tmp/CtsSimpleperfTestCases" />
diff --git a/tests/tests/syncmanager/src/android/content/syncmanager/cts/CtsSyncManagerTest.java b/tests/tests/syncmanager/src/android/content/syncmanager/cts/CtsSyncManagerTest.java
index 04e5b62..2d4a90c 100644
--- a/tests/tests/syncmanager/src/android/content/syncmanager/cts/CtsSyncManagerTest.java
+++ b/tests/tests/syncmanager/src/android/content/syncmanager/cts/CtsSyncManagerTest.java
@@ -246,8 +246,9 @@
addAccountAndLetInitialSyncRun(ACCOUNT_1_A, APP1_AUTHORITY);
// App should be brought out of the NEVER bucket to handle the sync
- assertEquals(UsageStatsManager.STANDBY_BUCKET_WORKING_SET,
- AmUtils.getStandbyBucket(APP1_PACKAGE));
+ assertTrue("Standby bucket should be WORKING_SET or better",
+ AmUtils.getStandbyBucket(APP1_PACKAGE)
+ <= UsageStatsManager.STANDBY_BUCKET_WORKING_SET);
// Check the sync request parameters.
Response res = mRpc.invoke(APP1_PACKAGE,
diff --git a/tests/tests/systemui/AndroidTest.xml b/tests/tests/systemui/AndroidTest.xml
index a1405cf9..11418e1 100644
--- a/tests/tests/systemui/AndroidTest.xml
+++ b/tests/tests/systemui/AndroidTest.xml
@@ -17,6 +17,7 @@
<option name="test-suite-tag" value="cts" />
<option name="config-descriptor:metadata" key="component" value="sysui" />
<option name="config-descriptor:metadata" key="parameter" value="instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
<option name="not-shardable" value="true" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
diff --git a/tests/tests/telecom/Android.mk b/tests/tests/telecom/Android.mk
index 1249565..ddf5518 100644
--- a/tests/tests/telecom/Android.mk
+++ b/tests/tests/telecom/Android.mk
@@ -27,6 +27,7 @@
LOCAL_STATIC_JAVA_LIBRARIES := \
compatibility-device-util-axt \
ctstestrunner-axt \
+ androidx.test.core \
androidx.test.rules
LOCAL_JAVA_LIBRARIES := android.test.base.stubs
@@ -44,7 +45,6 @@
CallScreeningServiceTestApp/aidl/
LOCAL_SDK_VERSION := test_current
-LOCAL_MIN_SDK_VERSION := 21
# Tag this module as a cts test artifact
LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
diff --git a/tests/tests/telecom/AndroidManifest.xml b/tests/tests/telecom/AndroidManifest.xml
index fd24f86..b0cf10b 100644
--- a/tests/tests/telecom/AndroidManifest.xml
+++ b/tests/tests/telecom/AndroidManifest.xml
@@ -22,7 +22,6 @@
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
<uses-permission android:name="android.permission.MANAGE_OWN_CALLS" />
- <uses-permission android:name="android.permission.MANAGE_ROLE_HOLDERS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.READ_CALL_LOG" />
<uses-permission android:name="android.permission.REGISTER_CALL_PROVIDER" />
diff --git a/tests/tests/telecom/src/android/telecom/cts/CallRedirectionServiceTest.java b/tests/tests/telecom/src/android/telecom/cts/CallRedirectionServiceTest.java
index 96c6295..936c746 100644
--- a/tests/tests/telecom/src/android/telecom/cts/CallRedirectionServiceTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/CallRedirectionServiceTest.java
@@ -108,7 +108,7 @@
}
mCallRedirectionServiceController.setRedirectCall(
SAMPLE_HANDLE, SAMPLE_PHONE_ACCOUNT, false);
- placeAndVerifyCall();
+ // TODO write the test placeAndVerifyCall();
// TODO verify redirection information
}
@@ -117,7 +117,7 @@
return;
}
mCallRedirectionServiceController.setCancelCall();
- placeAndVerifyCall();
+ // TODO write the test placeAndVerifyCall();
// TODO verify redirection information
}
@@ -126,7 +126,7 @@
return;
}
mCallRedirectionServiceController.setPlaceCallUnmodified();
- placeAndVerifyCall();
+ // TODO write the test placeAndVerifyCall();
// TODO verify redirection information
}
/**
@@ -179,7 +179,8 @@
LinkedBlockingQueue<Boolean> queue = new LinkedBlockingQueue(1);
runWithShellPermissionIdentity(() -> mRoleManager.addRoleHolderAsUser(roleName,
- packageName, 0, user, executor, successful -> {
+ packageName, RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP, user, executor,
+ successful -> {
try {
queue.put(successful);
} catch (InterruptedException e) {
@@ -197,7 +198,8 @@
LinkedBlockingQueue<Boolean> queue = new LinkedBlockingQueue(1);
runWithShellPermissionIdentity(() -> mRoleManager.removeRoleHolderAsUser(roleName,
- packageName, 0, user, executor, successful -> {
+ packageName, RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP, user, executor,
+ successful -> {
try {
queue.put(successful);
} catch (InterruptedException e) {
diff --git a/tests/tests/telecom/src/android/telecom/cts/DefaultDialerOperationsTest.java b/tests/tests/telecom/src/android/telecom/cts/DefaultDialerOperationsTest.java
index f45ccd8..8fa9549 100644
--- a/tests/tests/telecom/src/android/telecom/cts/DefaultDialerOperationsTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/DefaultDialerOperationsTest.java
@@ -186,7 +186,11 @@
mTelecomManager.getAdnUriForPhoneAccount(mPhoneAccountHandle);
}
- public void testSetDefaultDialerNoDialIntent_notSupported() throws Exception {
+ /**
+ * TODO: Re-enable this test when CTS tests are refactored.
+ * @throws Exception
+ */
+ public void donotTestSetDefaultDialerNoDialIntent_notSupported() throws Exception {
if (!TestUtils.shouldTestTelecom(mContext)) {
return;
}
@@ -196,9 +200,8 @@
try {
pm.setComponentEnabledSetting(name, PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP);
-
- final String result =
- TestUtils.setDefaultDialer(getInstrumentation(), TestUtils.PACKAGE);
+ TestUtils.setDefaultDialer(getInstrumentation(), TestUtils.PACKAGE);
+ final String result = TestUtils.getDefaultDialer(getInstrumentation());
assertNotSame(result, TestUtils.PACKAGE);
assertTrue("Expected failure indicating that this was not an installed dialer app",
result.contains("is not an installed Dialer app"));
@@ -208,7 +211,8 @@
}
// Now that the activity is present again in the package manager, this should succeed.
- final String result = TestUtils.setDefaultDialer(getInstrumentation(), TestUtils.PACKAGE);
+ TestUtils.setDefaultDialer(getInstrumentation(), TestUtils.PACKAGE);
+ final String result = TestUtils.getDefaultDialer(getInstrumentation());
assertTrue("Expected success message indicating that " + TestUtils.PACKAGE + " was set as "
+ "default dialer.", result.contains("set as default dialer"));
assertEquals(TestUtils.PACKAGE, TestUtils.getDefaultDialer(getInstrumentation()));
diff --git a/tests/tests/telecom/src/android/telecom/cts/ThirdPartyCallScreeningServiceTest.java b/tests/tests/telecom/src/android/telecom/cts/ThirdPartyCallScreeningServiceTest.java
index 431f2ba..f2b4512 100644
--- a/tests/tests/telecom/src/android/telecom/cts/ThirdPartyCallScreeningServiceTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/ThirdPartyCallScreeningServiceTest.java
@@ -340,7 +340,8 @@
LinkedBlockingQueue<Boolean> queue = new LinkedBlockingQueue(1);
runWithShellPermissionIdentity(() -> mRoleManager.addRoleHolderAsUser(roleName,
- packageName, 0, user, executor, successful -> {
+ packageName, RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP, user, executor,
+ successful -> {
try {
queue.put(successful);
} catch (InterruptedException e) {
@@ -358,7 +359,8 @@
LinkedBlockingQueue<Boolean> queue = new LinkedBlockingQueue(1);
runWithShellPermissionIdentity(() -> mRoleManager.removeRoleHolderAsUser(roleName,
- packageName, 0, user, executor, successful -> {
+ packageName, RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP, user, executor,
+ successful -> {
try {
queue.put(successful);
} catch (InterruptedException e) {
diff --git a/tests/tests/telecom2/Android.mk b/tests/tests/telecom2/Android.mk
index fddb7e1..1a207b7 100644
--- a/tests/tests/telecom2/Android.mk
+++ b/tests/tests/telecom2/Android.mk
@@ -47,8 +47,7 @@
--extra-packages android.telecom.cts \
--rename-manifest-package android.telecom2.cts \
-LOCAL_SDK_VERSION := current
-LOCAL_MIN_SDK_VERSION := 21
+LOCAL_SDK_VERSION := test_current
# Tag this module as a cts test artifact
LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
diff --git a/tests/tests/telecom3/Android.mk b/tests/tests/telecom3/Android.mk
index ec81b78..ff195bd 100644
--- a/tests/tests/telecom3/Android.mk
+++ b/tests/tests/telecom3/Android.mk
@@ -44,8 +44,7 @@
--extra-packages android.telecom.cts \
--rename-manifest-package android.telecom3.cts \
-LOCAL_SDK_VERSION := current
-LOCAL_MIN_SDK_VERSION := 25
+LOCAL_SDK_VERSION := test_current
# Tag this module as a cts test artifact
LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/PhoneStateListenerTest.java b/tests/tests/telephony/current/src/android/telephony/cts/PhoneStateListenerTest.java
index cae46b6..8c29609 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/PhoneStateListenerTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/PhoneStateListenerTest.java
@@ -35,7 +35,6 @@
import android.telephony.TelephonyManager;
import android.net.ConnectivityManager;
import android.telephony.ims.ImsReasonInfo;
-import android.test.AndroidTestCase;
import android.util.Log;
import com.android.compatibility.common.util.ShellIdentityUtils;
@@ -178,6 +177,67 @@
}
@Test
+ public void testOnUnRegisterFollowedByRegister() throws Throwable {
+ if (mCm.getNetworkInfo(ConnectivityManager.TYPE_MOBILE) == null) {
+ Log.d(TAG, "Skipping test that requires ConnectivityManager.TYPE_MOBILE");
+ return;
+ }
+
+ TestThread t = new TestThread(new Runnable() {
+ public void run() {
+ Looper.prepare();
+
+ mListener = new PhoneStateListener() {
+ @Override
+ public void onServiceStateChanged(ServiceState serviceState) {
+ synchronized(mLock) {
+ mOnServiceStateChangedCalled = true;
+ mLock.notify();
+ }
+ }
+ };
+ mTelephonyManager.listen(mListener, PhoneStateListener.LISTEN_SERVICE_STATE);
+
+ Looper.loop();
+ }
+ });
+
+ assertFalse(mOnServiceStateChangedCalled);
+ t.start();
+
+ synchronized (mLock) {
+ if (!mOnServiceStateChangedCalled){
+ mLock.wait(WAIT_TIME);
+ }
+ }
+ t.checkException();
+ assertTrue(mOnServiceStateChangedCalled);
+
+ // reset and un-register
+ mOnServiceStateChangedCalled = false;
+ if (mListener != null) {
+ // un-register the listener
+ mTelephonyManager.listen(mListener, PhoneStateListener.LISTEN_NONE);
+ }
+ synchronized (mLock) {
+ if (!mOnServiceStateChangedCalled){
+ mLock.wait(WAIT_TIME);
+ }
+ }
+ assertFalse(mOnServiceStateChangedCalled);
+
+ // re-register the listener
+ mTelephonyManager.listen(mListener, PhoneStateListener.LISTEN_SERVICE_STATE);
+ synchronized (mLock) {
+ if (!mOnServiceStateChangedCalled){
+ mLock.wait(WAIT_TIME);
+ }
+ }
+ t.checkException();
+ assertTrue(mOnServiceStateChangedCalled);
+ }
+
+ @Test
public void testOnSignalStrengthChanged() throws Throwable {
if (mCm.getNetworkInfo(ConnectivityManager.TYPE_MOBILE) == null) {
Log.d(TAG, "Skipping test that requires ConnectivityManager.TYPE_MOBILE");
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 18fda6f..7bb487e 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/SubscriptionManagerTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/SubscriptionManagerTest.java
@@ -21,6 +21,7 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.telephony.TelephonyManager.SET_OPPORTUNISTIC_SUB_SUCCESS;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -58,7 +59,10 @@
import java.util.List;
import java.util.UUID;
import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
+import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@@ -490,6 +494,130 @@
}
}
+ @Test
+ public void testGetEnabledSubscriptionId() {
+ if (!isSupported()) return;
+ int slotId = SubscriptionManager.getSlotIndex(mSubId);
+ if (!SubscriptionManager.isValidSlotIndex(slotId)) {
+ fail("Invalid slot id " + slotId + " for subscription id " + mSubId);
+ }
+ int enabledSubId = executeWithShellPermissionAndDefault(-1, mSm,
+ (sm) -> sm.getEnabledSubscriptionId(slotId));
+ assertEquals(mSubId, enabledSubId);
+ }
+
+ @Test
+ public void testSetAndCheckSubscriptionEnabled() {
+ if (!isSupported()) return;
+ boolean enabled = executeWithShellPermissionAndDefault(false, mSm,
+ (sm) -> sm.isSubscriptionEnabled(mSubId));
+ if (isDSDS()) {
+ // Change it to a different value
+ changeAndVerifySubscriptionEnabledValue(mSubId, !enabled);
+ // Reset it back to original
+ changeAndVerifySubscriptionEnabledValue(mSubId, enabled);
+ } else {
+ boolean changeSuccessfully = executeWithShellPermissionAndDefault(false, mSm,
+ (sm) -> sm.setSubscriptionEnabled(mSubId, !enabled));
+ assertFalse(changeSuccessfully);
+ }
+ }
+
+ @Test
+ public void testSetPreferredDataSubscriptionId() {
+ if (!isSupported()) return;
+ int preferredSubId = executeWithShellPermissionAndDefault(-1, mSm,
+ (sm) -> sm.getPreferredDataSubscriptionId());
+
+ final LinkedBlockingQueue<Integer> resultQueue = new LinkedBlockingQueue<>(1);
+ Executor executor = new Executor() {
+ @Override
+ public void execute(Runnable command) {
+ command.run();
+ }
+ };
+
+ Consumer<Integer> consumer = new Consumer<Integer>() {
+ @Override
+ public void accept(Integer res) {
+ if (res == null) {
+ resultQueue.offer(-1);
+ } else {
+ resultQueue.offer(res);
+ }
+ }
+ };
+
+ int [] subList = mSm.getActiveSubscriptionIdList();
+ boolean changes = false;
+
+ for (int subId : subList) {
+ if (subId != preferredSubId) {
+ int newPreferredSubId = subId;
+ // Change to a new value.
+ ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mSm,
+ (sm) -> sm.setPreferredDataSubscriptionId(newPreferredSubId, false,
+ executor, consumer));
+ int res = -1;
+ try {
+ res = resultQueue.poll(2, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ fail("Cannot get the modem result in time");
+ }
+ assertEquals(SET_OPPORTUNISTIC_SUB_SUCCESS, res);
+ int newGetValue = executeWithShellPermissionAndDefault(-1, mSm,
+ (sm) -> sm.getPreferredDataSubscriptionId());
+ assertEquals(newPreferredSubId, newGetValue);
+ changes = true;
+ break;
+ }
+ }
+
+ // Reset back, or set the duplicate.
+ if (SubscriptionManager.isValidSubscriptionId(preferredSubId)) {
+ ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mSm,
+ (sm) -> sm.setPreferredDataSubscriptionId(preferredSubId, false,
+ executor, consumer));
+ int res = -1;
+ try {
+ res = resultQueue.poll(2, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ fail("Cannot get the modem result in time");
+ }
+ // Duplicate setting ends up with nothing.
+ if (!changes) {
+ assertEquals(-1, res);
+ } else {
+ assertEquals(SET_OPPORTUNISTIC_SUB_SUCCESS, res);
+ int resetGetValue = executeWithShellPermissionAndDefault(-1, mSm,
+ (sm) -> sm.getPreferredDataSubscriptionId());
+ assertEquals(resetGetValue, preferredSubId);
+ }
+ }
+ }
+
+ private void changeAndVerifySubscriptionEnabledValue(int subId, boolean targetValue) {
+ boolean changeSuccessfully = executeWithShellPermissionAndDefault(false, mSm,
+ (sm) -> sm.setSubscriptionEnabled(subId, targetValue));
+ if (!changeSuccessfully) {
+ fail("Cannot change subscription " + subId
+ + " from " + !targetValue + " to " + targetValue);
+ }
+ boolean res = executeWithShellPermissionAndDefault(targetValue, mSm,
+ (sm) -> sm.isSubscriptionEnabled(subId));
+ assertEquals(targetValue, res);
+ }
+
+ private <T, U> T executeWithShellPermissionAndDefault(T defaultValue, U targetObject,
+ ShellIdentityUtils.ShellPermissionMethodHelper<T, U> helper) {
+ try {
+ return ShellIdentityUtils.invokeMethodWithShellPermissions(targetObject, helper);
+ } catch (Exception e) {
+ // do nothing, return default
+ }
+ return defaultValue;
+ }
+
private void assertOverrideSuccess(SubscriptionPlan... plans) {
mSm.setSubscriptionPlans(mSubId, Arrays.asList(plans));
mSm.setSubscriptionOverrideCongested(mSubId, false, 0);
@@ -551,6 +679,12 @@
.hasSystemFeature(PackageManager.FEATURE_TELEPHONY);
}
+ private static boolean isDSDS() {
+ TelephonyManager tm = InstrumentationRegistry.getContext()
+ .getSystemService(TelephonyManager.class);
+ return tm != null && tm.getPhoneCount() > 1;
+ }
+
private static void setSubPlanOwner(int subId, String packageName) throws Exception {
SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(),
"cmd netpolicy set sub-plan-owner " + subId + " " + packageName);
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java b/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java
index 1946772..f61dd54 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java
@@ -40,6 +40,7 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
+import android.os.SystemProperties;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
@@ -905,8 +906,13 @@
TelephonyManager.RADIO_POWER_ON);
assertThat(mRadioRebootTriggered).isFalse();
assertThat(mHasRadioPowerOff).isFalse();
- ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
+ boolean success = ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
(tm) -> tm.rebootRadio());
+ //skip this test if not supported or unsuccessful (success=false)
+ if(!success) {
+ return;
+ }
+
t.start();
synchronized (mLock) {
// reboot takes longer time
diff --git a/tests/tests/telephony/sdk28/src/android/telephony/sdk28/cts/PhoneStateListenerTest.java b/tests/tests/telephony/sdk28/src/android/telephony/sdk28/cts/PhoneStateListenerTest.java
new file mode 100644
index 0000000..859d4e9
--- /dev/null
+++ b/tests/tests/telephony/sdk28/src/android/telephony/sdk28/cts/PhoneStateListenerTest.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.telephony.sdk28.cts;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.os.Looper;
+import android.telephony.PhoneStateListener;
+import android.telephony.ServiceState;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+
+import com.android.compatibility.common.util.TestThread;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import static androidx.test.InstrumentationRegistry.getContext;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class PhoneStateListenerTest {
+
+ public static final long WAIT_TIME = 1000;
+ private boolean mOnServiceStateChangedCalled;
+ private TelephonyManager mTelephonyManager;
+ private PhoneStateListener mListener;
+ private final Object mLock = new Object();
+ private static final String TAG = "android.telephony.cts.PhoneStateListenerTest";
+ private static ConnectivityManager mCm;
+
+ @Before
+ public void setUp() throws Exception {
+ mTelephonyManager =
+ (TelephonyManager)getContext().getSystemService(Context.TELEPHONY_SERVICE);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ if (mListener != null) {
+ // unregister the listener
+ mTelephonyManager.listen(mListener, PhoneStateListener.LISTEN_NONE);
+ }
+ }
+
+ @Test
+ public void testPhoneStateListener() {
+ Looper.prepare();
+ new PhoneStateListener();
+ }
+
+ @Test
+ public void testOnUnRegisterFollowedByRegister() throws Throwable {
+
+ TestThread t = new TestThread(new Runnable() {
+ public void run() {
+ Looper.prepare();
+
+ mListener = new PhoneStateListener() {
+ @Override
+ public void onServiceStateChanged(ServiceState serviceState) {
+ synchronized(mLock) {
+ mOnServiceStateChangedCalled = true;
+ mLock.notify();
+ }
+ }
+ };
+ mTelephonyManager.listen(mListener, PhoneStateListener.LISTEN_SERVICE_STATE);
+
+ Looper.loop();
+ }
+ });
+
+ assertFalse(mOnServiceStateChangedCalled);
+ t.start();
+
+ synchronized (mLock) {
+ if (!mOnServiceStateChangedCalled){
+ mLock.wait(WAIT_TIME);
+ }
+ }
+ t.checkException();
+ assertTrue(mOnServiceStateChangedCalled);
+
+ // reset and un-register
+ mOnServiceStateChangedCalled = false;
+ if (mListener != null) {
+ // un-register the listener
+ mTelephonyManager.listen(mListener, PhoneStateListener.LISTEN_NONE);
+ }
+ synchronized (mLock) {
+ if (!mOnServiceStateChangedCalled){
+ mLock.wait(WAIT_TIME);
+ }
+ }
+ assertFalse(mOnServiceStateChangedCalled);
+
+ // re-register the listener
+ mTelephonyManager.listen(mListener, PhoneStateListener.LISTEN_SERVICE_STATE);
+ synchronized (mLock) {
+ if (!mOnServiceStateChangedCalled){
+ mLock.wait(WAIT_TIME);
+ }
+ }
+ t.checkException();
+ assertTrue(mOnServiceStateChangedCalled);
+ }
+}
diff --git a/tests/tests/telephony3/src/android/telephony3/cts/TelephonyManagerTest.java b/tests/tests/telephony3/src/android/telephony3/cts/TelephonyManagerTest.java
index 2cc379f..cd960b8 100644
--- a/tests/tests/telephony3/src/android/telephony3/cts/TelephonyManagerTest.java
+++ b/tests/tests/telephony3/src/android/telephony3/cts/TelephonyManagerTest.java
@@ -23,6 +23,7 @@
import android.content.Context;
import android.os.Build;
import android.telephony.TelephonyManager;
+import android.util.Log;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
@@ -86,7 +87,8 @@
fail("An app targeting pre-Q with the READ_PHONE_STATE permission granted must "
+ "receive null (or "
+ Build.UNKNOWN
- + " for Build#getSerial) when querying for device identifiers");
+ + " for Build#getSerial) when querying for device identifiers, caught "
+ + "SecurityException instead: " + e);
}
}
}
diff --git a/tests/tests/telephony4/AndroidTest.xml b/tests/tests/telephony4/AndroidTest.xml
index 636ecbb1..057a2eb 100644
--- a/tests/tests/telephony4/AndroidTest.xml
+++ b/tests/tests/telephony4/AndroidTest.xml
@@ -16,6 +16,8 @@
<configuration description="Config for CTS Telephony4 test cases">
<option name="test-suite-tag" value="cts" />
<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="not-shardable" value="true" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
diff --git a/tests/tests/telephonyprovider/src/android/telephonyprovider/cts/TelephonyProviderTest.java b/tests/tests/telephonyprovider/src/android/telephonyprovider/cts/TelephonyProviderTest.java
index f79439b..05acf32 100644
--- a/tests/tests/telephonyprovider/src/android/telephonyprovider/cts/TelephonyProviderTest.java
+++ b/tests/tests/telephonyprovider/src/android/telephonyprovider/cts/TelephonyProviderTest.java
@@ -51,4 +51,17 @@
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 SecurityExceptio");
+ } catch (SecurityException e) {
+ // expected
+ }
+ }
}
diff --git a/tests/tests/transition/src/android/transition/cts/SharedElementCallbackTest.java b/tests/tests/transition/src/android/transition/cts/SharedElementCallbackTest.java
new file mode 100644
index 0000000..0a4e48c
--- /dev/null
+++ b/tests/tests/transition/src/android/transition/cts/SharedElementCallbackTest.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.transition.cts;
+
+import android.app.SharedElementCallback;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.graphics.ColorSpace;
+import android.graphics.Matrix;
+import android.graphics.RectF;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Parcelable;
+import android.view.View;
+import android.widget.ImageView;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class SharedElementCallbackTest {
+ private static class Callback extends SharedElementCallback {}
+
+ @Test
+ public void testSnapshot() {
+ final int SIZE = 10;
+ ColorSpace cs = ColorSpace.get(ColorSpace.Named.DISPLAY_P3);
+ Bitmap bitmap = Bitmap.createBitmap(SIZE, SIZE, Bitmap.Config.ARGB_8888, true, cs);
+ bitmap.eraseColor(Color.BLUE);
+ bitmap = bitmap.copy(Bitmap.Config.HARDWARE, false);
+ Context context = InstrumentationRegistry.getContext();
+ ImageView originalView = new ImageView(context);
+ originalView.setImageBitmap(bitmap);
+
+ Callback cb = new Callback();
+ Matrix matrix = new Matrix();
+ RectF screenBounds = new RectF(0, 0, SIZE, SIZE);
+ Parcelable snapshot = cb.onCaptureSharedElementSnapshot(originalView, matrix, screenBounds);
+ assertNotNull(snapshot);
+
+ View view = cb.onCreateSnapshotView(context, snapshot);
+ assertNotNull(view);
+ assertTrue(view instanceof ImageView);
+
+ ImageView finalView = (ImageView) view;
+ Drawable drawable = finalView.getDrawable();
+ assertTrue(drawable instanceof BitmapDrawable);
+ BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
+ Bitmap finalBitmap = bitmapDrawable.getBitmap();
+ assertNotNull(finalView);
+
+ assertSame(Bitmap.Config.HARDWARE, finalBitmap.getConfig());
+ assertSame(cs, finalBitmap.getColorSpace());
+ }
+}
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 1390ccf..828d9b5 100755
--- a/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTest.java
+++ b/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTest.java
@@ -435,7 +435,7 @@
uiAutomation.destroy();
assertTrue(UiAutomationTestA11yService.sConnectedInstance.isConnected());
getInstrumentation().getUiAutomation(); // Should suppress
- assertFalse(UiAutomationTestA11yService.sConnectedInstance.isConnected());
+ waitForAccessibilityServiceToUnbind();
} finally {
turnAccessibilityOff();
}
@@ -454,7 +454,7 @@
UiAutomation suppressingUiAutomation = getInstrumentation().getUiAutomation();
// We verify above that the connection is broken here. Make sure we see a new one
// after we destroy it
- UiAutomationTestA11yService.sConnectedInstance = null;
+ waitForAccessibilityServiceToUnbind();
suppressingUiAutomation.destroy();
waitForAccessibilityServiceToStart();
} finally {
@@ -474,7 +474,7 @@
getInstrumentation().getUiAutomation();
// We verify above that the connection is broken here. Make sure we see a new one
// after we change the flags
- UiAutomationTestA11yService.sConnectedInstance = null;
+ waitForAccessibilityServiceToUnbind();
getInstrumentation()
.getUiAutomation(UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES);
waitForAccessibilityServiceToStart();
@@ -541,12 +541,12 @@
private void waitForAccessibilityServiceToStart() {
long timeoutTimeMillis = SystemClock.uptimeMillis() + TIMEOUT_FOR_SERVICE_ENABLE;
while (SystemClock.uptimeMillis() < timeoutTimeMillis) {
- synchronized(UiAutomationTestA11yService.sWaitObjectForConnecting) {
+ synchronized(UiAutomationTestA11yService.sWaitObjectForConnectOrUnbind) {
if (UiAutomationTestA11yService.sConnectedInstance != null) {
return;
}
try {
- UiAutomationTestA11yService.sWaitObjectForConnecting.wait(
+ UiAutomationTestA11yService.sWaitObjectForConnectOrUnbind.wait(
timeoutTimeMillis - SystemClock.uptimeMillis());
} catch (InterruptedException e) {
// Ignored; loop again
@@ -556,6 +556,24 @@
throw new RuntimeException("Test accessibility service not starting");
}
+ private void waitForAccessibilityServiceToUnbind() {
+ long timeoutTimeMillis = SystemClock.uptimeMillis() + TIMEOUT_FOR_SERVICE_ENABLE;
+ while (SystemClock.uptimeMillis() < timeoutTimeMillis) {
+ synchronized(UiAutomationTestA11yService.sWaitObjectForConnectOrUnbind) {
+ if (UiAutomationTestA11yService.sConnectedInstance == null) {
+ return;
+ }
+ try {
+ UiAutomationTestA11yService.sWaitObjectForConnectOrUnbind.wait(
+ timeoutTimeMillis - SystemClock.uptimeMillis());
+ } catch (InterruptedException e) {
+ // Ignored; loop again
+ }
+ }
+ }
+ throw new RuntimeException("Test accessibility service doesn't unbind");
+ }
+
private void turnAccessibilityOff() {
getInstrumentation().getUiAutomation().destroy();
final Object waitLockForA11yOff = new Object();
@@ -574,7 +592,6 @@
ContentResolver cr = context.getContentResolver();
Settings.Secure.putString(
cr, Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, null);
- UiAutomationTestA11yService.sConnectedInstance = null;
long timeoutTimeMillis = SystemClock.uptimeMillis() + TIMEOUT_FOR_SERVICE_ENABLE;
while (SystemClock.uptimeMillis() < timeoutTimeMillis) {
synchronized (waitLockForA11yOff) {
diff --git a/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTestA11yService.java b/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTestA11yService.java
index 1808630..d062ed5 100644
--- a/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTestA11yService.java
+++ b/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTestA11yService.java
@@ -26,12 +26,16 @@
*/
public class UiAutomationTestA11yService extends AccessibilityService {
private static String LOG_TAG = "UiAutomationTest";
- public static Object sWaitObjectForConnecting = new Object();
+ public static Object sWaitObjectForConnectOrUnbind = new Object();
public static UiAutomationTestA11yService sConnectedInstance;
@Override
public boolean onUnbind(Intent intent) {
+ synchronized (sWaitObjectForConnectOrUnbind) {
+ sConnectedInstance = null;
+ sWaitObjectForConnectOrUnbind.notifyAll();
+ }
Log.v(LOG_TAG, "onUnbind [" + this + "]");
return false;
}
@@ -52,9 +56,9 @@
@Override
protected void onServiceConnected() {
- synchronized (sWaitObjectForConnecting) {
+ synchronized (sWaitObjectForConnectOrUnbind) {
sConnectedInstance = this;
- sWaitObjectForConnecting.notifyAll();
+ sWaitObjectForConnectOrUnbind.notifyAll();
}
Log.v(LOG_TAG, "onServiceConnected [" + this + "]");
}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/WideColorGamutTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/WideColorGamutTests.java
index 0942ec8..8ea5834 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/WideColorGamutTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/WideColorGamutTests.java
@@ -151,11 +151,30 @@
.001f));
}
+ private static Color plus(Color a, Color b) {
+ final ColorSpace cs = a.getColorSpace();
+ Assert.assertSame(cs, b.getColorSpace());
+
+ float[] ac = a.getComponents();
+ float[] bc = b.getComponents();
+ float[] result = new float[ac.length];
+ for (int i = 0; i < ac.length; ++i) {
+ // BlendMode.PLUS clamps to [0,1]
+ result[i] = Math.max(Math.min(ac[i] + bc[i], 1.0f), 0.0f);
+ }
+ return Color.valueOf(result, cs);
+ }
+
@Test
public void testCanvasDrawColorLongBlendMode() {
final Color greenP3 = Color.valueOf(0, 1.0f, 0, 1.0f, DISPLAY_P3);
final Color redP3 = Color.valueOf(1.0f, 0, 0, 1.0f, DISPLAY_P3);
- final Color expected = Color.valueOf(1.0f, 1.0f, 0, 1.0f, DISPLAY_P3);
+
+ final ColorSpace displaySpace = displaySpace();
+ final Color greenDisplay = greenP3.convert(displaySpace);
+ final Color redDisplay = redP3.convert(displaySpace);
+
+ final Color expected = plus(greenDisplay, redDisplay);
createTest()
.addCanvasClient((canvas, width, height) -> {
canvas.drawColor(greenP3.pack());
@@ -167,16 +186,19 @@
.002f));
}
- @Test
- public void testProPhoto() {
- Color blueProPhoto = Color.valueOf(0, 0, 1, 1,
- ColorSpace.get(ColorSpace.Named.PRO_PHOTO_RGB));
+ private ColorSpace displaySpace() {
Context context = InstrumentationRegistry.getInstrumentation().getContext();
WindowManager window = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display defaultDisplay = window.getDefaultDisplay();
ColorSpace displaySpace = defaultDisplay.getPreferredWideGamutColorSpace();
- final Color blueDisplay = blueProPhoto.convert(displaySpace == null
- ? ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB) : displaySpace);
+ return displaySpace == null ? ColorSpace.get(ColorSpace.Named.SRGB) : displaySpace;
+ }
+
+ @Test
+ public void testProPhoto() {
+ Color blueProPhoto = Color.valueOf(0, 0, 1, 1,
+ ColorSpace.get(ColorSpace.Named.PRO_PHOTO_RGB));
+ final Color blueDisplay = blueProPhoto.convert(displaySpace());
createTest()
.addCanvasClient("RGBA16F_ProPhoto", (canvas, width, height) -> {
AssetManager assets = getActivity().getResources().getAssets();
diff --git a/tests/tests/view/AndroidTest.xml b/tests/tests/view/AndroidTest.xml
index 1ee295b..4f53b76 100644
--- a/tests/tests/view/AndroidTest.xml
+++ b/tests/tests/view/AndroidTest.xml
@@ -16,7 +16,8 @@
<configuration description="Config for CTS View test cases">
<option name="test-suite-tag" value="cts" />
<option name="config-descriptor:metadata" key="component" value="uitoolkit" />
- <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+ <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="CtsViewTestCases.apk" />
diff --git a/tests/tests/view/sdk28/AndroidTest.xml b/tests/tests/view/sdk28/AndroidTest.xml
index 004e8b5..bec8054 100644
--- a/tests/tests/view/sdk28/AndroidTest.xml
+++ b/tests/tests/view/sdk28/AndroidTest.xml
@@ -18,6 +18,7 @@
<option name="test-suite-tag" value="cts" />
<option name="config-descriptor:metadata" key="component" value="uitoolkit" />
<option name="config-descriptor:metadata" key="parameter" value="instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="CtsViewTestCasesSdk28.apk" />
diff --git a/tests/tests/view/src/android/view/cts/PixelCopyTest.java b/tests/tests/view/src/android/view/cts/PixelCopyTest.java
index 1c93897..85c4094 100644
--- a/tests/tests/view/src/android/view/cts/PixelCopyTest.java
+++ b/tests/tests/view/src/android/view/cts/PixelCopyTest.java
@@ -789,21 +789,21 @@
private void assertBitmapEdgeColor(Bitmap bitmap, int edgeColor) {
// Just quickly sample a few pixels on the edge and assert
// they are edge color, then assert that just inside the edge is a different color
- assertBitmapColor("Top edge", bitmap, edgeColor, bitmap.getWidth() / 2, 0);
- assertBitmapNotColor("Top edge", bitmap, edgeColor, bitmap.getWidth() / 2, 1);
+ assertBitmapColor("Top edge", bitmap, edgeColor, bitmap.getWidth() / 2, 1);
+ assertBitmapNotColor("Top edge", bitmap, edgeColor, bitmap.getWidth() / 2, 2);
- assertBitmapColor("Left edge", bitmap, edgeColor, 0, bitmap.getHeight() / 2);
- assertBitmapNotColor("Left edge", bitmap, edgeColor, 1, bitmap.getHeight() / 2);
+ assertBitmapColor("Left edge", bitmap, edgeColor, 1, bitmap.getHeight() / 2);
+ assertBitmapNotColor("Left edge", bitmap, edgeColor, 2, bitmap.getHeight() / 2);
assertBitmapColor("Bottom edge", bitmap, edgeColor,
- bitmap.getWidth() / 2, bitmap.getHeight() - 1);
- assertBitmapNotColor("Bottom edge", bitmap, edgeColor,
bitmap.getWidth() / 2, bitmap.getHeight() - 2);
+ assertBitmapNotColor("Bottom edge", bitmap, edgeColor,
+ bitmap.getWidth() / 2, bitmap.getHeight() - 3);
assertBitmapColor("Right edge", bitmap, edgeColor,
- bitmap.getWidth() - 1, bitmap.getHeight() / 2);
- assertBitmapNotColor("Right edge", bitmap, edgeColor,
bitmap.getWidth() - 2, bitmap.getHeight() / 2);
+ assertBitmapNotColor("Right edge", bitmap, edgeColor,
+ bitmap.getWidth() - 3, bitmap.getHeight() / 2);
}
private boolean pixelsAreSame(int ideal, int given, int threshold) {
diff --git a/tests/tests/view/src/android/view/cts/PixelCopyViewProducerActivity.java b/tests/tests/view/src/android/view/cts/PixelCopyViewProducerActivity.java
index 054de45..07aa4fd 100644
--- a/tests/tests/view/src/android/view/cts/PixelCopyViewProducerActivity.java
+++ b/tests/tests/view/src/android/view/cts/PixelCopyViewProducerActivity.java
@@ -157,22 +157,23 @@
protected void onDraw(Canvas canvas) {
int cx = getWidth() / 2;
int cy = getHeight() / 2;
+ final int BORDER_WIDTH = 2;
canvas.drawColor(Color.YELLOW);
- mRect.set(1, 1, cx, cy);
+ mRect.set(BORDER_WIDTH, BORDER_WIDTH, cx, cy);
mPaint.setColor(Color.RED);
canvas.drawRect(mRect, mPaint);
- mRect.set(cx, 1, getWidth() - 1, cy);
+ mRect.set(cx, BORDER_WIDTH, getWidth() - BORDER_WIDTH, cy);
mPaint.setColor(Color.GREEN);
canvas.drawRect(mRect, mPaint);
- mRect.set(1, cy, cx, getHeight() - 1);
+ mRect.set(BORDER_WIDTH, cy, cx, getHeight() - BORDER_WIDTH);
mPaint.setColor(Color.BLUE);
canvas.drawRect(mRect, mPaint);
- mRect.set(cx, cy, getWidth() - 1, getHeight() - 1);
+ mRect.set(cx, cy, getWidth() - BORDER_WIDTH, getHeight() - BORDER_WIDTH);
mPaint.setColor(Color.BLACK);
canvas.drawRect(mRect, mPaint);
}
diff --git a/tests/tests/view/surfacevalidator/src/android/view/cts/surfacevalidator/CapturedActivity.java b/tests/tests/view/surfacevalidator/src/android/view/cts/surfacevalidator/CapturedActivity.java
index ce94185..84a7999 100644
--- a/tests/tests/view/surfacevalidator/src/android/view/cts/surfacevalidator/CapturedActivity.java
+++ b/tests/tests/view/surfacevalidator/src/android/view/cts/surfacevalidator/CapturedActivity.java
@@ -196,7 +196,9 @@
}
final long timeOutMs = mOnEmbedded ? 125000 : 62500;
- final long endCaptureDelayMs = START_CAPTURE_DELAY_MS + getCaptureDurationMs();
+ final long captureDuration = animationTestCase.hasAnimation() ?
+ getCaptureDurationMs() : 200;
+ final long endCaptureDelayMs = START_CAPTURE_DELAY_MS + captureDuration;
final long endDelayMs = endCaptureDelayMs + 1000;
int count = 0;
diff --git a/tests/tests/view/surfacevalidator/src/android/view/cts/surfacevalidator/PixelChecker.java b/tests/tests/view/surfacevalidator/src/android/view/cts/surfacevalidator/PixelChecker.java
index 860d11b..821352c 100644
--- a/tests/tests/view/surfacevalidator/src/android/view/cts/surfacevalidator/PixelChecker.java
+++ b/tests/tests/view/surfacevalidator/src/android/view/cts/surfacevalidator/PixelChecker.java
@@ -15,9 +15,18 @@
*/
package android.view.cts.surfacevalidator;
+import android.graphics.Rect;
+import android.media.Image;
+import android.os.Trace;
+
+import java.nio.ByteBuffer;
+
public abstract class PixelChecker {
+ private int mBlackishPixelCount = 0;
private PixelColor mPixelColor;
+ private static final int PIXEL_STRIDE = 4;
+
public PixelChecker() {
mPixelColor = new PixelColor();
}
@@ -30,5 +39,55 @@
return mPixelColor;
}
+ public boolean validatePlane(Image.Plane plane, Rect boundsToCheck, int width, int height) {
+ int rowStride = plane.getRowStride();
+ ByteBuffer buffer = plane.getBuffer();
+
+ Trace.beginSection("compare and sum");
+
+ final short maxAlpha = getColor().mMaxAlpha;
+ final short minAlpha = getColor().mMinAlpha;
+ final short maxRed = getColor().mMaxRed;
+ final short minRed = getColor().mMinRed;
+ final short maxGreen = getColor().mMaxGreen;
+ final short minGreen = getColor().mMinGreen;
+ final short maxBlue = getColor().mMaxBlue;
+ final short minBlue = getColor().mMinBlue;
+
+ mBlackishPixelCount = 0;
+
+ final int bytesWidth = boundsToCheck.width() * PIXEL_STRIDE;
+ byte[] scanline = new byte[bytesWidth];
+ for (int row = boundsToCheck.top; row < boundsToCheck.bottom; row++) {
+ buffer.position(rowStride * row + boundsToCheck.left * PIXEL_STRIDE);
+ buffer.get(scanline, 0, scanline.length);
+ for (int i = 0; i < bytesWidth; i += PIXEL_STRIDE) {
+ // Format is RGBA_8888 not ARGB_8888
+ final int red = scanline[i + 0] & 0xFF;
+ final int green = scanline[i + 1] & 0xFF;
+ final int blue = scanline[i + 2] & 0xFF;
+ final int alpha = scanline[i + 3] & 0xFF;
+
+ if (alpha <= maxAlpha
+ && alpha >= minAlpha
+ && red <= maxRed
+ && red >= minRed
+ && green <= maxGreen
+ && green >= minGreen
+ && blue <= maxBlue
+ && blue >= minBlue) {
+ mBlackishPixelCount++;
+ }
+ }
+ }
+ Trace.endSection();
+
+ return checkPixels(mBlackishPixelCount, width, height);
+ }
+
+ public String getLastError() {
+ return "pixel count = " + mBlackishPixelCount + ")";
+ }
+
public abstract boolean checkPixels(int matchingPixelCount, int width, int height);
}
diff --git a/tests/tests/view/surfacevalidator/src/android/view/cts/surfacevalidator/RectChecker.java b/tests/tests/view/surfacevalidator/src/android/view/cts/surfacevalidator/RectChecker.java
new file mode 100644
index 0000000..c97fc0c
--- /dev/null
+++ b/tests/tests/view/surfacevalidator/src/android/view/cts/surfacevalidator/RectChecker.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.cts.surfacevalidator;
+
+import android.graphics.Rect;
+import android.media.Image;
+import android.os.Trace;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+
+
+public class RectChecker extends PixelChecker {
+ private ArrayList<Target> mTargets = new ArrayList<Target>();
+
+ private static final int PIXEL_STRIDE = 4;
+
+ public static class Target {
+ PixelColor mPixelColor;
+ Rect mTargetRect;
+
+ public Target(Rect r, int p) {
+ mPixelColor = new PixelColor(p);
+ mTargetRect = r;
+ }
+ };
+
+ public RectChecker(Target... targets) {
+ for (Target t : targets) {
+ mTargets.add(t);
+ }
+ }
+
+ public RectChecker(Rect r, int p) {
+ this(new Target(r, p));
+ }
+
+ public boolean validatePlane(Image.Plane plane, Rect boundsToCheck,
+ int width, int height) {
+ for (Target t : mTargets) {
+ if (validatePlaneForTarget(t, plane, boundsToCheck, width, height) == false) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public boolean validatePlaneForTarget(Target t, Image.Plane plane, Rect boundsToCheck,
+ int width, int height) {
+ int rowStride = plane.getRowStride();
+ ByteBuffer buffer = plane.getBuffer();
+
+ Trace.beginSection("check");
+
+ int startY = boundsToCheck.top + t.mTargetRect.top;
+ int endY = t.mTargetRect.bottom;
+ int startX = (boundsToCheck.left + t.mTargetRect.left) * PIXEL_STRIDE;
+ int bytesWidth = t.mTargetRect.width() * PIXEL_STRIDE;
+
+ final short maxAlpha = t.mPixelColor.mMaxAlpha;
+ final short minAlpha = t.mPixelColor.mMinAlpha;
+ final short maxRed = t.mPixelColor.mMaxRed;
+ final short minRed = t.mPixelColor.mMinRed;
+ final short maxGreen = t.mPixelColor.mMaxGreen;
+ final short minGreen = t.mPixelColor.mMinGreen;
+ final short maxBlue = t.mPixelColor.mMaxBlue;
+ final short minBlue = t.mPixelColor.mMinBlue;
+
+ byte[] scanline = new byte[bytesWidth];
+ for (int row = startY; row < endY; row++) {
+ buffer.position(rowStride * row + startX);
+ buffer.get(scanline, 0, scanline.length);
+ for (int i = 0; i < bytesWidth; i += PIXEL_STRIDE) {
+ // Format is RGBA_8888 not ARGB_8888
+ final int red = scanline[i + 0] & 0xFF;
+ final int green = scanline[i + 1] & 0xFF;
+ final int blue = scanline[i + 2] & 0xFF;
+ final int alpha = scanline[i + 3] & 0xFF;
+
+ if (alpha <= maxAlpha
+ && alpha >= minAlpha
+ && red <= maxRed
+ && red >= minRed
+ && green <= maxGreen
+ && green >= minGreen
+ && blue <= maxBlue
+ && blue >= minBlue) {
+ continue;
+ } else {
+ return false;
+ }
+ }
+ }
+ Trace.endSection();
+
+ return true;
+ }
+
+ public String getLastError() {
+ return "(couldn't find target Rect)";
+ }
+
+ public boolean checkPixels(int matchingPixelCount, int width, int height) {
+ return false;
+ }
+}
diff --git a/tests/tests/view/surfacevalidator/src/android/view/cts/surfacevalidator/SurfacePixelValidator2.java b/tests/tests/view/surfacevalidator/src/android/view/cts/surfacevalidator/SurfacePixelValidator2.java
index 88a8b17..26b43c4 100644
--- a/tests/tests/view/surfacevalidator/src/android/view/cts/surfacevalidator/SurfacePixelValidator2.java
+++ b/tests/tests/view/surfacevalidator/src/android/view/cts/surfacevalidator/SurfacePixelValidator2.java
@@ -29,7 +29,6 @@
import android.util.SparseArray;
import android.view.Surface;
-import java.nio.ByteBuffer;
public class SurfacePixelValidator2 {
private static final String TAG = "SurfacePixelValidator";
@@ -64,58 +63,18 @@
throw new IllegalStateException("pixel stride != " + PIXEL_STRIDE + "? "
+ plane.getPixelStride());
}
- int rowStride = plane.getRowStride();
- ByteBuffer buffer = plane.getBuffer();
Trace.endSection();
- Trace.beginSection("compare and sum");
+ boolean success = mPixelChecker.validatePlane(plane, mBoundsToCheck, mWidth, mHeight);
- final short maxAlpha = mPixelChecker.getColor().mMaxAlpha;
- final short minAlpha = mPixelChecker.getColor().mMinAlpha;
- final short maxRed = mPixelChecker.getColor().mMaxRed;
- final short minRed = mPixelChecker.getColor().mMinRed;
- final short maxGreen = mPixelChecker.getColor().mMaxGreen;
- final short minGreen = mPixelChecker.getColor().mMinGreen;
- final short maxBlue = mPixelChecker.getColor().mMaxBlue;
- final short minBlue = mPixelChecker.getColor().mMinBlue;
-
- int blackishPixelCount = 0;
-
- final int bytesWidth = mBoundsToCheck.width() * PIXEL_STRIDE;
- byte[] scanline = new byte[bytesWidth];
- for (int row = mBoundsToCheck.top; row < mBoundsToCheck.bottom; row++) {
- buffer.position(rowStride * row + mBoundsToCheck.left * PIXEL_STRIDE);
- buffer.get(scanline, 0, scanline.length);
- for (int i = 0; i < bytesWidth; i += PIXEL_STRIDE) {
- // Format is RGBA_8888 not ARGB_8888
- final int red = scanline[i + 0] & 0xFF;
- final int green = scanline[i + 1] & 0xFF;
- final int blue = scanline[i + 2] & 0xFF;
- final int alpha = scanline[i + 3] & 0xFF;
-
- if (alpha <= maxAlpha
- && alpha >= minAlpha
- && red <= maxRed
- && red >= minRed
- && green <= maxGreen
- && green >= minGreen
- && blue <= maxBlue
- && blue >= minBlue) {
- blackishPixelCount++;
- }
- }
- }
- Trace.endSection();
-
- boolean success = mPixelChecker.checkPixels(blackishPixelCount, mWidth, mHeight);
synchronized (mResultLock) {
if (success) {
mResultSuccessFrames++;
} else {
mResultFailureFrames++;
int totalFramesSeen = mResultSuccessFrames + mResultFailureFrames;
- Log.d(TAG, "Failure (pixel count = " + blackishPixelCount
- + ") occurred on frame " + totalFramesSeen);
+ Log.d(TAG, "Failure (" + mPixelChecker.getLastError() + ") occurred on frame "
+ + totalFramesSeen);
if (mFirstFailures.size() < MAX_CAPTURED_FAILURES) {
Log.d(TAG, "Capturing bitmap #" + mFirstFailures.size());
diff --git a/tests/tests/voiceinteraction/Android.mk b/tests/tests/voiceinteraction/Android.mk
index 2af5e9f..ac78196 100644
--- a/tests/tests/voiceinteraction/Android.mk
+++ b/tests/tests/voiceinteraction/Android.mk
@@ -21,7 +21,12 @@
# and when built explicitly put it in the data partition
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-LOCAL_STATIC_JAVA_LIBRARIES := CtsVoiceInteractionCommon ctstestrunner-axt compatibility-device-util-axt
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ CtsVoiceInteractionCommon \
+ ctstestrunner-axt \
+ compatibility-device-util-axt \
+ androidx.test.ext.junit
+
LOCAL_JAVA_LIBRARIES := android.test.runner.stubs android.test.base.stubs
@@ -33,7 +38,7 @@
LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := test_current
include $(BUILD_CTS_PACKAGE)
include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/tests/voiceinteraction/AndroidTest.xml b/tests/tests/voiceinteraction/AndroidTest.xml
index 36f0892..eb7a7ad 100644
--- a/tests/tests/voiceinteraction/AndroidTest.xml
+++ b/tests/tests/voiceinteraction/AndroidTest.xml
@@ -16,6 +16,7 @@
<configuration description="Config for CTS Voice Interaction 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="instant_app" />
<option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
<option name="not-shardable" value="true" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
diff --git a/tests/tests/voiceinteraction/common/src/android/voiceinteraction/common/Utils.java b/tests/tests/voiceinteraction/common/src/android/voiceinteraction/common/Utils.java
index fc8916e..94bef39 100644
--- a/tests/tests/voiceinteraction/common/src/android/voiceinteraction/common/Utils.java
+++ b/tests/tests/voiceinteraction/common/src/android/voiceinteraction/common/Utils.java
@@ -15,12 +15,11 @@
*/
package android.voiceinteraction.common;
-import android.app.VoiceInteractor;
import android.app.VoiceInteractor.PickOptionRequest.Option;
+import android.content.LocusId;
import android.os.Bundle;
import java.util.ArrayList;
-import java.util.Arrays;
public class Utils {
public enum TestCaseType {
@@ -34,8 +33,11 @@
PICKOPTION_REQUEST_CANCEL_TEST,
COMMANDREQUEST_TEST,
COMMANDREQUEST_CANCEL_TEST,
- SUPPORTS_COMMANDS_TEST,
+ SUPPORTS_COMMANDS_TEST
}
+
+ public static final long OPERATION_TIMEOUT_MS = 5000;
+
public static final String TESTCASE_TYPE = "testcase_type";
public static final String TESTINFO = "testinfo";
public static final String BROADCAST_INTENT = "android.intent.action.VOICE_TESTAPP";
@@ -64,6 +66,48 @@
public static final String PRIVATE_OPTIONS_KEY = "private_key";
public static final String PRIVATE_OPTIONS_VALUE = "private_value";
+ public static final String DIRECT_ACTION_EXTRA_KEY = "directActionExtraKey";
+ public static final String DIRECT_ACTION_EXTRA_VALUE = "directActionExtraValue";
+ public static final String DIRECT_ACTION_FILE_NAME = "directActionFileName";
+ public static final String DIRECT_ACTION_FILE_CONTENT = "directActionFileContent";
+ public static final String DIRECT_ACTION_AUTHORITY =
+ "android.voiceinteraction.testapp.fileprovider";
+
+ public static final String DIRECT_ACTIONS_KEY_CALLBACK = "callback";
+ public static final String DIRECT_ACTIONS_KEY_CANCEL_CALLBACK = "cancelCallback";
+ public static final String DIRECT_ACTIONS_KEY_CONTROL = "control";
+ public static final String DIRECT_ACTIONS_KEY_COMMAND = "command";
+ public static final String DIRECT_ACTIONS_KEY_RESULT = "result";
+ public static final String DIRECT_ACTIONS_KEY_ACTION = "action";
+ public static final String DIRECT_ACTIONS_KEY_ARGUMENTS = "arguments";
+ public static final String DIRECT_ACTIONS_KEY_CLASS = "class";
+
+ public static final String DIRECT_ACTIONS_SESSION_CMD_PERFORM_ACTION = "performAction";
+ public static final String DIRECT_ACTIONS_SESSION_CMD_PERFORM_ACTION_CANCEL =
+ "performActionCancel";
+ public static final String DIRECT_ACTIONS_SESSION_CMD_DETECT_ACTIONS_CHANGED =
+ "detectActionsChanged";
+ public static final String DIRECT_ACTIONS_SESSION_CMD_GET_ACTIONS = "getActions";
+ public static final String DIRECT_ACTIONS_SESSION_CMD_FINISH = "hide";
+
+ public static final String DIRECT_ACTIONS_ACTIVITY_CMD_DESTROYED_INTERACTOR =
+ "destroyedInteractor";
+ public static final String DIRECT_ACTIONS_ACTIVITY_CMD_FINISH = "finish";
+ public static final String DIRECT_ACTIONS_ACTIVITY_CMD_INVALIDATE_ACTIONS = "invalidateActions";
+
+ public static final String DIRECT_ACTIONS_RESULT_PERFORMED = "performed";
+ public static final String DIRECT_ACTIONS_RESULT_CANCELLED = "cancelled";
+ public static final String DIRECT_ACTIONS_RESULT_EXECUTING = "executing";
+
+
+ public static final String DIRECT_ACTIONS_ACTION_ID = "actionId";
+ public static final Bundle DIRECT_ACTIONS_ACTION_EXTRAS = new Bundle();
+ static {
+ DIRECT_ACTIONS_ACTION_EXTRAS.putString(DIRECT_ACTION_EXTRA_KEY,
+ DIRECT_ACTION_EXTRA_VALUE);
+ }
+ public static final LocusId DIRECT_ACTIONS_LOCUS_ID = new LocusId("locusId");
+
public static final String toBundleString(Bundle bundle) {
if (bundle == null) {
return "*** Bundle is null ****";
diff --git a/tests/tests/voiceinteraction/service/Android.mk b/tests/tests/voiceinteraction/service/Android.mk
index f19cf74..abae526 100644
--- a/tests/tests/voiceinteraction/service/Android.mk
+++ b/tests/tests/voiceinteraction/service/Android.mk
@@ -30,6 +30,6 @@
# Tag this module as a cts test artifact
LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := test_current
include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/DirectActionsSession.java b/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/DirectActionsSession.java
new file mode 100644
index 0000000..fa2e5e3
--- /dev/null
+++ b/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/DirectActionsSession.java
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions andf
+ * limitations under the License.
+ */
+
+package android.voiceinteraction.service;
+
+import android.app.DirectAction;
+import android.content.Context;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.RemoteCallback;
+import android.service.voice.VoiceInteractionSession;
+import android.voiceinteraction.common.Utils;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.function.Consumer;
+
+/**
+ * Sessions for testing direct action related functionality
+ */
+public class DirectActionsSession extends VoiceInteractionSession {
+ private final ReentrantLock mLock = new ReentrantLock();
+ private final Condition mCondition = mLock.newCondition();
+
+ // GuardedBy("mLock")
+ private @Nullable ActivityId mActivityId;
+
+ // GuardedBy("mLock")
+ private boolean mActionsInvalidated;
+
+ private static final int OPERATION_TIMEOUT_MS = 1000000;//5000;
+
+ public DirectActionsSession(@NonNull Context context) {
+ super(context);
+ }
+
+ @Override
+ public void onShow(Bundle args, int showFlags) {
+ final RemoteCallback callback = args.getParcelable(Utils.DIRECT_ACTIONS_KEY_CALLBACK);
+
+ final RemoteCallback control = new RemoteCallback((cmdArgs) -> {
+ final String command = cmdArgs.getString(Utils.DIRECT_ACTIONS_KEY_COMMAND);
+ final RemoteCallback commandCallback = cmdArgs.getParcelable(
+ Utils.DIRECT_ACTIONS_KEY_CALLBACK);
+ switch (command) {
+ case Utils.DIRECT_ACTIONS_SESSION_CMD_PERFORM_ACTION: {
+ executeWithAssist((result) -> performDirectAction(cmdArgs, result),
+ commandCallback);
+ } break;
+ case Utils.DIRECT_ACTIONS_SESSION_CMD_PERFORM_ACTION_CANCEL: {
+ executeWithAssist((result) -> performDirectActionAndCancel(cmdArgs, result),
+ commandCallback);
+ } break;
+ case Utils.DIRECT_ACTIONS_SESSION_CMD_GET_ACTIONS: {
+ executeWithAssist(this::getDirectActions, commandCallback);
+ } break;
+ case Utils.DIRECT_ACTIONS_SESSION_CMD_FINISH: {
+ executeWithAssist(this::performHide, commandCallback);
+ } break;
+ case Utils.DIRECT_ACTIONS_SESSION_CMD_DETECT_ACTIONS_CHANGED: {
+ executeWithAssist(this::detectDirectActionsInvalidated, commandCallback);
+ } break;
+ }
+ });
+
+ final Bundle result = new Bundle();
+ result.putParcelable(Utils.DIRECT_ACTIONS_KEY_CONTROL, control);
+ callback.sendResult(result);
+ }
+
+ @Override
+ public void onHandleAssist(AssistState state) {
+ if (state.getIndex() == 0) {
+ mLock.lock();
+ try {
+ mActivityId = state.getActivityId();
+ mCondition.signalAll();
+ } finally {
+ mLock.unlock();
+ }
+ }
+ }
+
+ @Override
+ public void onDirectActionsInvalidated(ActivityId activityId) {
+ mLock.lock();
+ try {
+ mActionsInvalidated = true;
+ mCondition.signalAll();
+ } finally {
+ mLock.unlock();
+ }
+ }
+
+ private void executeWithAssist(@Nullable Consumer<Bundle> command,
+ @NonNull RemoteCallback callback) {
+ mLock.lock();
+ try {
+ if (mActivityId == null) {
+ try {
+ mCondition.await(OPERATION_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ /* ignore */
+ }
+ }
+ final Bundle result = new Bundle();
+ if (mActivityId != null) {
+ command.accept(result);
+ callback.sendResult(result);
+ } else {
+ callback.sendResult(result);
+ }
+ } finally {
+ mLock.unlock();
+ }
+ }
+
+ private void getDirectActions(@NonNull Bundle outResult) {
+ final ArrayList<DirectAction> actions = new ArrayList<>();
+
+ final CountDownLatch latch = new CountDownLatch(1);
+
+ mLock.lock();
+ try {
+ requestDirectActions(mActivityId,null, AsyncTask.THREAD_POOL_EXECUTOR, (b) -> {
+ actions.addAll(b);
+ latch.countDown();
+ });
+ } finally {
+ mLock.unlock();
+ }
+ try {
+ latch.await(OPERATION_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ /* ignore */
+ }
+
+ outResult.putParcelableArrayList(Utils.DIRECT_ACTIONS_KEY_RESULT, actions);
+ }
+
+ private void performDirectAction(@NonNull Bundle args, @NonNull Bundle outResult) {
+ final DirectAction action = args.getParcelable(Utils.DIRECT_ACTIONS_KEY_ACTION);
+ final Bundle arguments = args.getBundle(Utils.DIRECT_ACTIONS_KEY_ARGUMENTS);
+
+ final Bundle result = new Bundle();
+ final CountDownLatch latch = new CountDownLatch(1);
+ performDirectAction(action, arguments, null, AsyncTask.THREAD_POOL_EXECUTOR, (b) -> {
+ result.putAll(b);
+ latch.countDown();
+ });
+ try {
+ latch.await(OPERATION_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ /* ignore */
+ }
+
+ outResult.putBundle(Utils.DIRECT_ACTIONS_KEY_RESULT, result);
+ }
+
+ private void performDirectActionAndCancel(@NonNull Bundle args, @NonNull Bundle outResult) {
+ final DirectAction action = args.getParcelable(Utils.DIRECT_ACTIONS_KEY_ACTION);
+ final Bundle arguments = args.getBundle(Utils.DIRECT_ACTIONS_KEY_ARGUMENTS);
+ final Bundle result = new Bundle();
+
+ final CountDownLatch cancelLatch = new CountDownLatch(1);
+ final RemoteCallback cancelCallback = new RemoteCallback((b) -> {
+ result.clear();
+ result.putAll(b);
+ cancelLatch.countDown();
+ });
+ arguments.putParcelable(Utils.DIRECT_ACTIONS_KEY_CANCEL_CALLBACK, cancelCallback);
+
+ final CancellationSignal cancellationSignal = new CancellationSignal();
+
+ final CountDownLatch resultLatch = new CountDownLatch(1);
+
+ performDirectAction(action, arguments, cancellationSignal,
+ AsyncTask.THREAD_POOL_EXECUTOR, (b) ->
+ resultLatch.countDown()
+ );
+
+ try {
+ resultLatch.await(OPERATION_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ /* ignore */
+ }
+
+ cancellationSignal.cancel();
+
+ try {
+ cancelLatch.await(OPERATION_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ /* ignore */
+ }
+
+ outResult.putBundle(Utils.DIRECT_ACTIONS_KEY_RESULT, result);
+ }
+
+ private void detectDirectActionsInvalidated(@NonNull Bundle outResult) {
+ mLock.lock();
+ try {
+ if (!mActionsInvalidated) {
+ try {
+ mCondition.await(OPERATION_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ /* ignore */
+ }
+ }
+ outResult.putBoolean(Utils.DIRECT_ACTIONS_KEY_RESULT, mActionsInvalidated);
+ mActionsInvalidated = false;
+ } finally {
+ mLock.unlock();
+ }
+ }
+
+ private void performHide(@NonNull Bundle outResult) {
+ finish();
+ outResult.putBoolean(Utils.DIRECT_ACTIONS_KEY_RESULT, true);
+ }
+}
diff --git a/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/MainInteractionService.java b/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/MainInteractionService.java
index 1af7bd5..037540f 100644
--- a/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/MainInteractionService.java
+++ b/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/MainInteractionService.java
@@ -17,11 +17,12 @@
package android.voiceinteraction.service;
import android.content.ComponentName;
-import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.service.voice.VoiceInteractionService;
+import android.service.voice.VoiceInteractionSession;
import android.util.Log;
+import android.voiceinteraction.common.Utils;
public class MainInteractionService extends VoiceInteractionService {
static final String TAG = "MainInteractionService";
@@ -43,21 +44,28 @@
}
private void maybeStart() {
- if (mIntent == null || !mReady) {
+ if (mIntent == null || !mReady) {
Log.wtf(TAG, "Can't start session because either intent is null or onReady() "
+ "is not called yet. mIntent = " + mIntent + ", mReady = " + mReady);
} else {
- Log.i(TAG, "Yay! about to start session with TestApp");
- if (isActiveService(this, new ComponentName(this, getClass()))) {
- Bundle args = new Bundle();
- Intent intent = new Intent();
- intent.setComponent(new ComponentName("android.voiceinteraction.testapp",
- "android.voiceinteraction.testapp.TestApp"));
- args.putParcelable("intent", intent);
- showSession(args, 0);
+ Bundle args = mIntent.getExtras();
+ final String className = (args != null)
+ ? args.getString(Utils.DIRECT_ACTIONS_KEY_CLASS) : null;
+ if (className == null) {
+ Log.i(TAG, "Yay! about to start session with TestApp");
+ if (isActiveService(this, new ComponentName(this, getClass()))) {
+ args = new Bundle();
+ Intent intent = new Intent();
+ intent.setComponent(new ComponentName("android.voiceinteraction.testapp",
+ "android.voiceinteraction.testapp.TestApp"));
+ args.putParcelable("intent", intent);
+ showSession(args, 0);
+ } else {
+ Log.wtf(TAG, "**** Not starting MainInteractionService because" +
+ " it is not set as the current voice interaction service");
+ }
} else {
- Log.wtf(TAG, "**** Not starting MainInteractionService because" +
- " it is not set as the current voice interaction service");
+ showSession(args, VoiceInteractionSession.SHOW_WITH_ASSIST);
}
}
}
diff --git a/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/MainInteractionSessionService.java b/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/MainInteractionSessionService.java
index 43bfc8d..feb8d62 100644
--- a/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/MainInteractionSessionService.java
+++ b/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/MainInteractionSessionService.java
@@ -16,13 +16,31 @@
package android.voiceinteraction.service;
+import android.content.Context;
import android.os.Bundle;
import android.service.voice.VoiceInteractionSession;
import android.service.voice.VoiceInteractionSessionService;
+import android.voiceinteraction.common.Utils;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
public class MainInteractionSessionService extends VoiceInteractionSessionService {
@Override
public VoiceInteractionSession onNewSession(Bundle args) {
- return new MainInteractionSession(this);
+ final String className = (args != null)
+ ? args.getString(Utils.DIRECT_ACTIONS_KEY_CLASS) : null;
+ if (className == null) {
+ return new MainInteractionSession(this);
+ } else {
+ try {
+ final Constructor<?> constructor = Class.forName(className)
+ .getConstructor(Context.class);
+ return (VoiceInteractionSession) constructor.newInstance(this);
+ } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException
+ | InstantiationException | InvocationTargetException e) {
+ throw new RuntimeException("Cannot instantiate class: " + className, e);
+ }
+ }
}
}
diff --git a/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/VoiceInteractionMain.java b/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/VoiceInteractionMain.java
index 388b47f..303c0a1 100644
--- a/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/VoiceInteractionMain.java
+++ b/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/VoiceInteractionMain.java
@@ -17,15 +17,11 @@
package android.voiceinteraction.service;
import android.app.Activity;
-import android.content.Intent;
import android.content.ComponentName;
-import android.content.Context;
+import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
-import java.util.ArrayList;
-import java.util.Arrays;
-
public class VoiceInteractionMain extends Activity {
static final String TAG = "VoiceInteractionMain";
@@ -34,7 +30,12 @@
super.onCreate(savedInstanceState);
Intent intent = new Intent();
intent.setComponent(new ComponentName(this, MainInteractionService.class));
+ final Bundle intentExtras = getIntent().getExtras();
+ if (intentExtras != null) {
+ intent.putExtras(intentExtras);
+ }
ComponentName serviceName = startService(intent);
Log.i(TAG, "Started service: " + serviceName);
+ finish();
}
}
diff --git a/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/DirectActionsTest.java b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/DirectActionsTest.java
new file mode 100644
index 0000000..85871ec
--- /dev/null
+++ b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/DirectActionsTest.java
@@ -0,0 +1,349 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions andf
+ * limitations under the License.
+ */
+
+package android.voiceinteraction.cts;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.DirectAction;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.RemoteCallback;
+import android.voiceinteraction.common.Utils;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * Tests for the direction action related functions.
+ */
+@RunWith(AndroidJUnit4.class)
+public class DirectActionsTest {
+ private static final long OPERATION_TIMEOUT_MS = 10000000;//5000;
+
+ private final @NonNull SessionControl mSessionControl = new SessionControl();
+ private final @NonNull ActivityControl mActivityControl = new ActivityControl();
+
+ @Test
+ public void testPerformDirectAction() throws Exception {
+ mActivityControl.startActivity();
+ mSessionControl.startVoiceInteractionSession();
+ try {
+ // Get the actions.
+ final List<DirectAction> actions = mSessionControl.getDirectActions();
+
+ // Only the expected action should be reported
+ final DirectAction action = getExpectedDirectActionAssertively(actions);
+
+ // Perform the expected action.
+ final Bundle result = mSessionControl.performDirectAction(action,
+ createActionArguments());
+
+ // Assert the action completed successfully.
+ assertActionSucceeded(result);
+ } finally {
+ mSessionControl.stopVoiceInteractionSession();
+ mActivityControl.finishActivity();
+ }
+ }
+
+ @Test
+ public void testCancelPerformedDirectAction() throws Exception {
+ mActivityControl.startActivity();
+ mSessionControl.startVoiceInteractionSession();
+ try {
+ // Get the actions.
+ final List<DirectAction> actions = mSessionControl.getDirectActions();
+
+ // Only the expected action should be reported
+ final DirectAction action = getExpectedDirectActionAssertively(actions);
+
+ // Perform the expected action.
+ final Bundle result = mSessionControl.performDirectActionAndCancel(action,
+ createActionArguments());
+
+ // Assert the action was cancelled.
+ assertActionCancelled(result);
+ } finally {
+ mSessionControl.stopVoiceInteractionSession();
+ mActivityControl.finishActivity();
+ }
+ }
+
+ @Test
+ public void testVoiceInteractorDestroy() throws Exception {
+ mActivityControl.startActivity();
+ mSessionControl.startVoiceInteractionSession();
+ try {
+ // Get the actions to set up the VoiceInteractor
+ mSessionControl.getDirectActions();
+
+ assertThat(mActivityControl.detectInteractorDestroyed(() -> {
+ try {
+ mSessionControl.stopVoiceInteractionSession();
+ } catch (TimeoutException e) {
+ /* ignore */
+ }
+ })).isTrue();
+ } finally {
+ mSessionControl.stopVoiceInteractionSession();
+ mActivityControl.finishActivity();
+ }
+ }
+
+ @Test
+ public void testNotifyDirectActionsChanged() throws Exception {
+ mActivityControl.startActivity();
+ mSessionControl.startVoiceInteractionSession();
+ try {
+ // Get the actions to set up the VoiceInteractor
+ mSessionControl.getDirectActions();
+
+ assertThat(mSessionControl.detectDirectActionsInvalidated(() -> {
+ try {
+ mActivityControl.invalidateDirectActions();
+ } catch (TimeoutException e) {
+ /* ignore */
+ }
+ })).isTrue();
+ } finally {
+ mSessionControl.stopVoiceInteractionSession();
+ mActivityControl.finishActivity();
+ }
+ }
+
+ private class SessionControl {
+ private @Nullable RemoteCallback mControl;
+
+ private void startVoiceInteractionSession() throws TimeoutException {
+ final CountDownLatch latch = new CountDownLatch(1);
+
+ final RemoteCallback callback = new RemoteCallback((result) -> {
+ mControl = result.getParcelable(Utils.DIRECT_ACTIONS_KEY_CONTROL);
+ latch.countDown();
+ });
+
+ final Intent intent = new Intent();
+ intent.putExtra(Utils.DIRECT_ACTIONS_KEY_CLASS,
+ "android.voiceinteraction.service.DirectActionsSession");
+ intent.setClassName("android.voiceinteraction.service",
+ "android.voiceinteraction.service.VoiceInteractionMain");
+ intent.putExtra(Utils.DIRECT_ACTIONS_KEY_CALLBACK, callback);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+ getContext().startActivity(intent);
+
+ try {
+ if (!latch.await(OPERATION_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+ throw new TimeoutException();
+ }
+ } catch (InterruptedException e) {
+ /* cannot happen */
+ }
+ }
+
+ private void stopVoiceInteractionSession() throws TimeoutException {
+ executeCommand(Utils.DIRECT_ACTIONS_SESSION_CMD_FINISH,
+ null /*directAction*/, null /*arguments*/, null /*postActionCommand*/);
+ }
+
+ @Nullable List<DirectAction> getDirectActions() throws TimeoutException {
+ final ArrayList<DirectAction> actions = new ArrayList<>();
+ final Bundle result = executeCommand(Utils.DIRECT_ACTIONS_SESSION_CMD_GET_ACTIONS,
+ null /*directAction*/, null /*arguments*/, null /*postActionCommand*/);
+ actions.addAll(result.getParcelableArrayList(Utils.DIRECT_ACTIONS_KEY_RESULT));
+ return actions;
+ }
+
+ @Nullable Bundle performDirectAction(@NonNull DirectAction directAction,
+ @NonNull Bundle arguments) throws TimeoutException {
+ return executeCommand(Utils.DIRECT_ACTIONS_SESSION_CMD_PERFORM_ACTION,
+ directAction, arguments, null /*postActionCommand*/);
+ }
+
+ @Nullable Bundle performDirectActionAndCancel(@NonNull DirectAction directAction,
+ @NonNull Bundle arguments) throws TimeoutException {
+ return executeCommand(Utils.DIRECT_ACTIONS_SESSION_CMD_PERFORM_ACTION_CANCEL,
+ directAction, arguments, null /*postActionCommand*/);
+ }
+
+ @Nullable boolean detectDirectActionsInvalidated(@NonNull Runnable postActionCommand)
+ throws TimeoutException {
+ final Bundle result = executeCommand(
+ Utils.DIRECT_ACTIONS_SESSION_CMD_DETECT_ACTIONS_CHANGED,
+ null /*directAction*/, null /*arguments*/, postActionCommand);
+ return result.getBoolean(Utils.DIRECT_ACTIONS_KEY_RESULT);
+ }
+
+ @Nullable Bundle executeCommand(@NonNull String action, @Nullable DirectAction directAction,
+ @Nullable Bundle arguments, @Nullable Runnable postActionCommand)
+ throws TimeoutException {
+ final CountDownLatch latch = new CountDownLatch(1);
+
+ final Bundle result = new Bundle();
+
+ final RemoteCallback callback = new RemoteCallback((b) -> {
+ result.putAll(b);
+ latch.countDown();
+ });
+
+ final Bundle command = new Bundle();
+ command.putString(Utils.DIRECT_ACTIONS_KEY_COMMAND, action);
+ command.putParcelable(Utils.DIRECT_ACTIONS_KEY_ACTION, directAction);
+ command.putBundle(Utils.DIRECT_ACTIONS_KEY_ARGUMENTS, arguments);
+ command.putParcelable(Utils.DIRECT_ACTIONS_KEY_CALLBACK, callback);
+
+ mControl.sendResult(command);
+
+ if (postActionCommand != null) {
+ postActionCommand.run();
+ }
+
+ try {
+ if (!latch.await(OPERATION_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+ throw new TimeoutException();
+ }
+ } catch (InterruptedException e) {
+ /* cannot happen */
+ }
+
+ return result;
+ }
+ }
+
+ private final class ActivityControl {
+ private @Nullable RemoteCallback mControl;
+
+ void startActivity() throws TimeoutException {
+ final CountDownLatch latch = new CountDownLatch(1);
+
+ final RemoteCallback callback = new RemoteCallback((result) -> {
+ mControl = result.getParcelable(Utils.DIRECT_ACTIONS_KEY_CONTROL);
+ latch.countDown();
+ });
+
+ final Intent intent = new Intent();
+ intent.setClassName("android.voiceinteraction.testapp",
+ "android.voiceinteraction.testapp.DirectActionsActivity");
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.putExtra(Utils.DIRECT_ACTIONS_KEY_CALLBACK, callback);
+
+ getContext().startActivity(intent);
+
+ try {
+ if (!latch.await(OPERATION_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+ throw new TimeoutException();
+ }
+ } catch (InterruptedException e) {
+ /* cannot happen */
+ }
+ }
+
+ private boolean detectInteractorDestroyed(Runnable destroyTrigger) throws TimeoutException {
+ final Bundle result = executeRemoteCommand(
+ Utils.DIRECT_ACTIONS_ACTIVITY_CMD_DESTROYED_INTERACTOR,
+ destroyTrigger);
+ return result.getBoolean(Utils.DIRECT_ACTIONS_KEY_RESULT);
+ }
+
+ void finishActivity() throws TimeoutException {
+ executeRemoteCommand(Utils.DIRECT_ACTIONS_ACTIVITY_CMD_FINISH,
+ null /*postActionCommand*/);
+ }
+
+ void invalidateDirectActions() throws TimeoutException {
+ executeRemoteCommand(Utils.DIRECT_ACTIONS_ACTIVITY_CMD_INVALIDATE_ACTIONS,
+ null /*postActionCommand*/);
+ }
+
+ Bundle executeRemoteCommand(@NonNull String action,
+ @Nullable Runnable postActionCommand) throws TimeoutException {
+ final Bundle result = new Bundle();
+
+ final CountDownLatch latch = new CountDownLatch(1);
+
+ final RemoteCallback callback = new RemoteCallback((b) -> {
+ if (b != null) {
+ result.putAll(b);
+ }
+ latch.countDown();
+ });
+
+ final Bundle command = new Bundle();
+ command.putString(Utils.DIRECT_ACTIONS_KEY_COMMAND, action);
+ command.putParcelable(Utils.DIRECT_ACTIONS_KEY_CALLBACK, callback);
+
+ mControl.sendResult(command);
+
+ if (postActionCommand != null) {
+ postActionCommand.run();
+ }
+
+ try {
+ if (!latch.await(OPERATION_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+ throw new TimeoutException();
+ }
+ } catch (InterruptedException e) {
+ /* cannot happen */
+ }
+ return result;
+ }
+ }
+
+ private @NonNull DirectAction getExpectedDirectActionAssertively(
+ @Nullable List<DirectAction> actions) {
+ final DirectAction action = actions.get(0);
+ assertThat(action.getId()).isEqualTo(Utils.DIRECT_ACTIONS_ACTION_ID);
+ assertThat(action.getExtras().getString(Utils.DIRECT_ACTION_EXTRA_KEY))
+ .isEqualTo(Utils.DIRECT_ACTION_EXTRA_VALUE);
+ assertThat(action.getLocusId().getId()).isEqualTo(Utils.DIRECT_ACTIONS_LOCUS_ID.getId());
+ return action;
+ }
+
+ private @NonNull Bundle createActionArguments() {
+ final Bundle args = new Bundle();
+ args.putString(Utils.DIRECT_ACTIONS_KEY_ARGUMENTS, Utils.DIRECT_ACTIONS_KEY_ARGUMENTS);
+ return args;
+ }
+
+ private void assertActionSucceeded(@NonNull Bundle result) {
+ final Bundle bundle = result.getBundle(Utils.DIRECT_ACTIONS_KEY_RESULT);
+ final String status = bundle.getString(Utils.DIRECT_ACTIONS_KEY_RESULT);
+ assertThat(Utils.DIRECT_ACTIONS_RESULT_PERFORMED).isEqualTo(status);
+ }
+
+ private void assertActionCancelled(@NonNull Bundle result) {
+ final Bundle bundle = result.getBundle(Utils.DIRECT_ACTIONS_KEY_RESULT);
+ final String status = bundle.getString(Utils.DIRECT_ACTIONS_KEY_RESULT);
+ assertThat(Utils.DIRECT_ACTIONS_RESULT_CANCELLED).isEqualTo(status);
+ }
+
+ private static @NonNull Context getContext() {
+ return InstrumentationRegistry.getInstrumentation().getContext();
+ }
+}
diff --git a/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/LocalVoiceInteractionTest.java b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/LocalVoiceInteractionTest.java
index 66c945a..9bfc7c4 100644
--- a/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/LocalVoiceInteractionTest.java
+++ b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/LocalVoiceInteractionTest.java
@@ -16,64 +16,66 @@
package android.voiceinteraction.cts;
-import android.content.BroadcastReceiver;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.junit.Assert.fail;
+
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.Bundle;
-import android.test.ActivityInstrumentationTestCase2;
-import android.util.Log;
-import junit.framework.Assert;
+import com.android.compatibility.common.util.RequiredFeatureRule;
-import java.util.ArrayList;
-import java.util.List;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
-import android.voiceinteraction.common.Utils;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.rule.ActivityTestRule;
-public class LocalVoiceInteractionTest
- extends ActivityInstrumentationTestCase2<TestLocalInteractionActivity> {
+@RunWith(AndroidJUnit4.class)
+public class LocalVoiceInteractionTest {
private static final int TIMEOUT_MS = 20 * 1000;
- private TestLocalInteractionActivity mTestActivity;
- private Context mContext;
- private final CountDownLatch mLatchStart = new CountDownLatch(1);
- private final CountDownLatch mLatchStop = new CountDownLatch(1);
- protected boolean mHasFeature;
+ // TODO: use PackageManager's / make it @TestApi
protected static final String FEATURE_VOICE_RECOGNIZERS = "android.software.voice_recognizers";
- public LocalVoiceInteractionTest() {
- super(TestLocalInteractionActivity.class);
- }
+ private TestLocalInteractionActivity mTestActivity;
+ private final Context mContext = getInstrumentation().getTargetContext();
+ private final CountDownLatch mLatchStart = new CountDownLatch(1);
+ private final CountDownLatch mLatchStop = new CountDownLatch(1);
- @Override
- protected void setUp() throws Exception {
- super.setUp();
+ @Rule
+ public final ActivityTestRule<TestLocalInteractionActivity> mActivityTestRule =
+ new ActivityTestRule<>(TestLocalInteractionActivity.class, false, false);
+
+ @Rule
+ public final RequiredFeatureRule mRequiredFeatureRule = new RequiredFeatureRule(
+ FEATURE_VOICE_RECOGNIZERS);
+
+ @Before
+ public void setUp() throws Exception {
startTestActivity();
- mContext = getInstrumentation().getTargetContext();
- mHasFeature = mContext.getPackageManager().hasSystemFeature(FEATURE_VOICE_RECOGNIZERS);
}
private void startTestActivity() throws Exception {
Intent intent = new Intent();
intent.setAction("android.intent.action.TEST_LOCAL_INTERACTION_ACTIVITY");
- intent.setComponent(new ComponentName(getInstrumentation().getContext(),
- TestLocalInteractionActivity.class));
- setActivityIntent(intent);
- mTestActivity = getActivity();
+ intent.setComponent(new ComponentName(mContext, TestLocalInteractionActivity.class));
+ mTestActivity = mActivityTestRule.launchActivity(intent);
}
+ @Test
public void testLifecycle() throws Exception {
- if (!mHasFeature) {
- return;
- }
-
- assertTrue("Doesn't support LocalVoiceInteraction",
- mTestActivity.isLocalVoiceInteractionSupported());
+ assertWithMessage("Doesn't support LocalVoiceInteraction")
+ .that(mTestActivity.isLocalVoiceInteractionSupported()).isTrue();
mTestActivity.startLocalInteraction(mLatchStart);
if (!mLatchStart.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
fail("Failed to start voice interaction in " + TIMEOUT_MS + "msec");
diff --git a/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/TestStartActivity.java b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/TestStartActivity.java
index 682667f..920d828 100644
--- a/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/TestStartActivity.java
+++ b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/TestStartActivity.java
@@ -19,12 +19,9 @@
import android.app.Activity;
import android.content.Intent;
import android.content.ComponentName;
-import android.content.Context;
import android.os.Bundle;
import android.util.Log;
-import android.voiceinteraction.common.Utils;
-
public class TestStartActivity extends Activity {
static final String TAG = "TestStartActivity";
diff --git a/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/VoiceInteractionTest.java b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/VoiceInteractionTest.java
index 732cff2..21e4e6c 100644
--- a/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/VoiceInteractionTest.java
+++ b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/VoiceInteractionTest.java
@@ -16,57 +16,67 @@
package android.voiceinteraction.cts;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.fail;
+
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
-import android.test.ActivityInstrumentationTestCase2;
import android.util.Log;
+import android.voiceinteraction.common.Utils;
-import junit.framework.Assert;
+import com.android.compatibility.common.util.RequiredFeatureRule;
-import java.util.ArrayList;
-import java.util.List;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
-import android.voiceinteraction.common.Utils;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.rule.ActivityTestRule;
-public class VoiceInteractionTest extends ActivityInstrumentationTestCase2<TestStartActivity> {
+@RunWith(AndroidJUnit4.class)
+public class VoiceInteractionTest {
static final String TAG = "VoiceInteractionTest";
private static final int TIMEOUT_MS = 20 * 1000;
+ // TODO: use PackageManager's / make it @TestApi
+ protected static final String FEATURE_VOICE_RECOGNIZERS = "android.software.voice_recognizers";
+
private TestStartActivity mTestActivity;
- private Context mContext;
+ private final Context mContext = getInstrumentation().getTargetContext();
private TestResultsReceiver mReceiver;
private Bundle mResults;
private final CountDownLatch mLatch = new CountDownLatch(1);
- // TODO: convert test to JUnit4 so we can use @RequiredFeatureRule instead
- protected boolean mHasFeature;
- protected static final String FEATURE_VOICE_RECOGNIZERS = "android.software.voice_recognizers";
- public VoiceInteractionTest() {
- super(TestStartActivity.class);
- }
+ @Rule
+ public final ActivityTestRule<TestStartActivity> mActivityTestRule =
+ new ActivityTestRule<>(TestStartActivity.class, false, false);
- @Override
- protected void setUp() throws Exception {
- super.setUp();
+ @Rule
+ public final RequiredFeatureRule mRequiredFeatureRule = new RequiredFeatureRule(
+ FEATURE_VOICE_RECOGNIZERS);
- mContext = getInstrumentation().getTargetContext();
- mHasFeature = mContext.getPackageManager().hasSystemFeature(FEATURE_VOICE_RECOGNIZERS);
- if (!mHasFeature) return;
-
+ @Before
+ public void setUp() throws Exception {
mReceiver = new TestResultsReceiver();
mContext.registerReceiver(mReceiver, new IntentFilter(Utils.BROADCAST_INTENT));
startTestActivity();
}
- @Override
- protected void tearDown() throws Exception {
- if (mHasFeature && mReceiver != null) {
+ @After
+ public void tearDown() throws Exception {
+ if (mReceiver != null) {
try {
mContext.unregisterReceiver(mReceiver);
} catch (IllegalArgumentException e) {
@@ -76,25 +86,18 @@
}
mReceiver = null;
}
- super.tearDown();
}
private void startTestActivity() throws Exception {
Intent intent = new Intent();
intent.setAction("android.intent.action.TEST_START_ACTIVITY");
- intent.setComponent(new ComponentName(getInstrumentation().getContext(),
- TestStartActivity.class));
- setActivityIntent(intent);
- mTestActivity = getActivity();
+ intent.setComponent(new ComponentName(mContext, TestStartActivity.class));
+ mTestActivity = mActivityTestRule.launchActivity(intent);
}
+ @Test
public void testAll() throws Exception {
- if (!mHasFeature) {
- Log.i(TAG, "The device doesn't support feature: " + FEATURE_VOICE_RECOGNIZERS);
- return;
- }
-
- VoiceInteractionTestReceiver.waitSessionStarted(this, 5, TimeUnit.SECONDS);
+ VoiceInteractionTestReceiver.waitSessionStarted(5, TimeUnit.SECONDS);
if (!mLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
fail("Failed to receive broadcast in " + TIMEOUT_MS + "msec");
@@ -114,7 +117,7 @@
verifySingleTestcaseResult(t, singleResult);
}
}
- assertEquals(0, numFails);
+ assertThat(numFails).isEqualTo(0);
mTestActivity.finish();
}
@@ -122,37 +125,37 @@
Log.i(TAG, "Received testresult: " + result + " for " + testCaseType);
switch (testCaseType) {
case ABORT_REQUEST_CANCEL_TEST:
- assertTrue(result.equals(Utils.ABORT_REQUEST_CANCEL_SUCCESS));
+ assertThat(result.equals(Utils.ABORT_REQUEST_CANCEL_SUCCESS)).isTrue();
break;
case ABORT_REQUEST_TEST:
- assertTrue(result.equals(Utils.ABORT_REQUEST_SUCCESS));
+ assertThat(result.equals(Utils.ABORT_REQUEST_SUCCESS)).isTrue();
break;
case COMMANDREQUEST_TEST:
- assertTrue(result.equals(Utils.COMMANDREQUEST_SUCCESS));
+ assertThat(result.equals(Utils.COMMANDREQUEST_SUCCESS)).isTrue();
break;
case COMMANDREQUEST_CANCEL_TEST:
- assertTrue(result.equals(Utils.COMMANDREQUEST_CANCEL_SUCCESS));
+ assertThat(result.equals(Utils.COMMANDREQUEST_CANCEL_SUCCESS)).isTrue();
break;
case COMPLETION_REQUEST_CANCEL_TEST:
- assertTrue(result.equals(Utils.COMPLETION_REQUEST_CANCEL_SUCCESS));
+ assertThat(result.equals(Utils.COMPLETION_REQUEST_CANCEL_SUCCESS)).isTrue();
break;
case COMPLETION_REQUEST_TEST:
- assertTrue(result.equals(Utils.COMPLETION_REQUEST_SUCCESS));
+ assertThat(result.equals(Utils.COMPLETION_REQUEST_SUCCESS)).isTrue();
break;
case CONFIRMATION_REQUEST_CANCEL_TEST:
- assertTrue(result.equals(Utils.CONFIRMATION_REQUEST_CANCEL_SUCCESS));
+ assertThat(result.equals(Utils.CONFIRMATION_REQUEST_CANCEL_SUCCESS)).isTrue();
break;
case CONFIRMATION_REQUEST_TEST:
- assertTrue(result.equals(Utils.CONFIRMATION_REQUEST_SUCCESS));
+ assertThat(result.equals(Utils.CONFIRMATION_REQUEST_SUCCESS)).isTrue();
break;
case PICKOPTION_REQUEST_CANCEL_TEST:
- assertTrue(result.equals(Utils.PICKOPTION_REQUEST_CANCEL_SUCCESS));
+ assertThat(result.equals(Utils.PICKOPTION_REQUEST_CANCEL_SUCCESS)).isTrue();
break;
case PICKOPTION_REQUEST_TEST:
- assertTrue(result.equals(Utils.PICKOPTION_REQUEST_SUCCESS));
+ assertThat(result.equals(Utils.PICKOPTION_REQUEST_SUCCESS)).isTrue();
break;
case SUPPORTS_COMMANDS_TEST:
- assertTrue(result.equals(Utils.SUPPORTS_COMMANDS_SUCCESS));
+ assertThat(result.equals(Utils.SUPPORTS_COMMANDS_SUCCESS)).isTrue();
break;
default:
Log.wtf(TAG, "not expected");
diff --git a/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/VoiceInteractionTestReceiver.java b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/VoiceInteractionTestReceiver.java
index 7f875c4..14ae14b 100644
--- a/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/VoiceInteractionTestReceiver.java
+++ b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/VoiceInteractionTestReceiver.java
@@ -16,13 +16,13 @@
package android.voiceinteraction.cts;
+import static org.junit.Assert.fail;
+
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
-import junit.framework.TestCase;
-
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -31,14 +31,13 @@
private static CountDownLatch sServiceStartedLatch = new CountDownLatch(1);
private static Intent sReceivedIntent;
- public static void waitSessionStarted(TestCase testCase, long timeout, TimeUnit unit)
- throws InterruptedException {
- if (!sServiceStartedLatch.await(5, TimeUnit.SECONDS)) {
- testCase.fail("Timed out waiting for session to start");
+ public static void waitSessionStarted(long timeout, TimeUnit unit) throws InterruptedException {
+ if (!sServiceStartedLatch.await(timeout, unit)) {
+ fail("Timed out waiting for session to start");
}
String error = sReceivedIntent.getStringExtra("error");
if (error != null) {
- testCase.fail(error);
+ fail(error);
}
}
diff --git a/tests/tests/voiceinteraction/testapp/Android.mk b/tests/tests/voiceinteraction/testapp/Android.mk
index 1c37e51..0191d61 100644
--- a/tests/tests/voiceinteraction/testapp/Android.mk
+++ b/tests/tests/voiceinteraction/testapp/Android.mk
@@ -21,13 +21,14 @@
# and when built explicitly put it in the data partition
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-LOCAL_STATIC_JAVA_LIBRARIES := CtsVoiceInteractionCommon
+LOCAL_STATIC_JAVA_LIBRARIES := CtsVoiceInteractionCommon \
+ androidx.core_core \
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_PACKAGE_NAME := CtsVoiceInteractionApp
-LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := test_current
# Tag this module as a cts test artifact
LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
diff --git a/tests/tests/voiceinteraction/testapp/AndroidManifest.xml b/tests/tests/voiceinteraction/testapp/AndroidManifest.xml
index 15069ec..4e7156b 100644
--- a/tests/tests/voiceinteraction/testapp/AndroidManifest.xml
+++ b/tests/tests/voiceinteraction/testapp/AndroidManifest.xml
@@ -30,5 +30,11 @@
<category android:name="android.intent.category.VOICE" />
</intent-filter>
</activity>
+
+ <activity android:name=".DirectActionsActivity"
+ android:label="Direct actions activity"
+ android:exported="true">
+ </activity>
+
</application>
</manifest>
diff --git a/tests/tests/voiceinteraction/testapp/res/xml/file_paths.xml b/tests/tests/voiceinteraction/testapp/res/xml/file_paths.xml
new file mode 100644
index 0000000..5d18332
--- /dev/null
+++ b/tests/tests/voiceinteraction/testapp/res/xml/file_paths.xml
@@ -0,0 +1,20 @@
+<?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.
+-->
+
+<paths xmlns:android="http://schemas.android.com/apk/res/android">
+ <files-path name="files" path="/" />
+</paths>
+
diff --git a/tests/tests/voiceinteraction/testapp/src/android/voiceinteraction/testapp/DirectActionsActivity.java b/tests/tests/voiceinteraction/testapp/src/android/voiceinteraction/testapp/DirectActionsActivity.java
new file mode 100644
index 0000000..3457507
--- /dev/null
+++ b/tests/tests/voiceinteraction/testapp/src/android/voiceinteraction/testapp/DirectActionsActivity.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.voiceinteraction.testapp;
+
+import android.app.Activity;
+import android.app.DirectAction;
+import android.app.VoiceInteractor;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.RemoteCallback;
+import android.voiceinteraction.common.Utils;
+
+import androidx.annotation.NonNull;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
+
+/**
+ * Activity to test direct action behaviors.
+ */
+public final class DirectActionsActivity extends Activity {
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ final Bundle args = getIntent().getExtras();
+ final RemoteCallback callback = args.getParcelable(Utils.DIRECT_ACTIONS_KEY_CALLBACK);
+
+ final RemoteCallback control = new RemoteCallback((cmdArgs) -> {
+ final String command = cmdArgs.getString(Utils.DIRECT_ACTIONS_KEY_COMMAND);
+ switch (command) {
+ case Utils.DIRECT_ACTIONS_ACTIVITY_CMD_DESTROYED_INTERACTOR: {
+ final RemoteCallback commandCallback = cmdArgs.getParcelable(
+ Utils.DIRECT_ACTIONS_KEY_CALLBACK);
+ detectDestroyedInteractor(commandCallback);
+ } break;
+ case Utils.DIRECT_ACTIONS_ACTIVITY_CMD_FINISH: {
+ final RemoteCallback commandCallback = cmdArgs.getParcelable(
+ Utils.DIRECT_ACTIONS_KEY_CALLBACK);
+ doFinish(commandCallback);
+ } break;
+ case Utils.DIRECT_ACTIONS_ACTIVITY_CMD_INVALIDATE_ACTIONS: {
+ final RemoteCallback commandCallback = cmdArgs.getParcelable(
+ Utils.DIRECT_ACTIONS_KEY_CALLBACK);
+ invalidateDirectActions(commandCallback);
+ } break;
+ }
+ });
+
+ final Bundle result = new Bundle();
+ result.putParcelable(Utils.DIRECT_ACTIONS_KEY_CONTROL, control);
+ callback.sendResult(result);
+ }
+
+ @Override
+ public void onGetDirectActions(@NonNull CancellationSignal cancellationSignal,
+ @NonNull Consumer<List<DirectAction>> callback) {
+ if (getVoiceInteractor() == null) {
+ callback.accept(Collections.emptyList());
+ return;
+ }
+ final DirectAction action = new DirectAction.Builder(Utils.DIRECT_ACTIONS_ACTION_ID)
+ .setExtras(Utils.DIRECT_ACTIONS_ACTION_EXTRAS)
+ .setLocusId(Utils.DIRECT_ACTIONS_LOCUS_ID)
+ .build();
+
+ final ArrayList<DirectAction> actions = new ArrayList<>();
+ actions.add(action);
+ callback.accept(actions);
+ }
+
+ @Override
+ public void onPerformDirectAction(String actionId, Bundle arguments,
+ CancellationSignal cancellationSignal, Consumer<Bundle> callback) {
+ if (arguments == null || !arguments.getString(Utils.DIRECT_ACTIONS_KEY_ARGUMENTS)
+ .equals(Utils.DIRECT_ACTIONS_KEY_ARGUMENTS)) {
+ reportActionFailed(callback);
+ return;
+ }
+ final RemoteCallback cancelCallback = arguments.getParcelable(
+ Utils.DIRECT_ACTIONS_KEY_CANCEL_CALLBACK);
+ if (cancelCallback != null) {
+ cancellationSignal.setOnCancelListener(() -> reportActionCancelled(
+ cancelCallback::sendResult));
+ reportActionExecuting(callback);
+ } else {
+ reportActionPerformed(callback);
+ }
+ }
+
+ private void detectDestroyedInteractor(@NonNull RemoteCallback callback) {
+ final Bundle result = new Bundle();
+ final CountDownLatch latch = new CountDownLatch(1);
+
+ final VoiceInteractor interactor = getVoiceInteractor();
+ interactor.registerOnDestroyedCallback(AsyncTask.THREAD_POOL_EXECUTOR, () -> {
+ if (interactor.isDestroyed() && getVoiceInteractor() == null) {
+ result.putBoolean(Utils.DIRECT_ACTIONS_KEY_RESULT, true);
+ }
+ latch.countDown();
+ });
+
+ try {
+ latch.await(Utils.OPERATION_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ /* ignore */
+ }
+
+ callback.sendResult(result);
+ }
+
+ private void invalidateDirectActions(@NonNull RemoteCallback callback) {
+ getVoiceInteractor().notifyDirectActionsChanged();
+ final Bundle result = new Bundle();
+ result.putBoolean(Utils.DIRECT_ACTIONS_KEY_RESULT, true);
+ callback.sendResult(result);
+ }
+
+ private void doFinish(@NonNull RemoteCallback callback) {
+ finish();
+ final Bundle result = new Bundle();
+ result.putBoolean(Utils.DIRECT_ACTIONS_KEY_RESULT, true);
+ callback.sendResult(result);
+ }
+
+ private static void reportActionPerformed(Consumer<Bundle> callback) {
+ final Bundle result = new Bundle();
+ result.putString(Utils.DIRECT_ACTIONS_KEY_RESULT,
+ Utils.DIRECT_ACTIONS_RESULT_PERFORMED);
+ callback.accept(result);
+ }
+
+ private static void reportActionCancelled(Consumer<Bundle> callback) {
+ final Bundle result = new Bundle();
+ result.putString(Utils.DIRECT_ACTIONS_KEY_RESULT,
+ Utils.DIRECT_ACTIONS_RESULT_CANCELLED);
+ callback.accept(result);
+ }
+
+ private static void reportActionExecuting(Consumer<Bundle> callback) {
+ final Bundle result = new Bundle();
+ result.putString(Utils.DIRECT_ACTIONS_KEY_RESULT,
+ Utils.DIRECT_ACTIONS_RESULT_EXECUTING);
+ callback.accept(result);
+ }
+
+ private static void reportActionFailed(Consumer<Bundle> callback) {
+ callback.accept( new Bundle());
+ }
+}
diff --git a/tests/tests/voiceinteraction/testapp/src/android/voiceinteraction/testapp/TestApp.java b/tests/tests/voiceinteraction/testapp/src/android/voiceinteraction/testapp/TestApp.java
index 11f330e..cb498b6 100644
--- a/tests/tests/voiceinteraction/testapp/src/android/voiceinteraction/testapp/TestApp.java
+++ b/tests/tests/voiceinteraction/testapp/src/android/voiceinteraction/testapp/TestApp.java
@@ -69,43 +69,43 @@
mTestInProgress = (Utils.TestCaseType.values())[mIndex++];
testSetup();
switch (mTestInProgress) {
- case ABORT_REQUEST_TEST:
- case ABORT_REQUEST_CANCEL_TEST:
- abortRequest();
- break;
+ case ABORT_REQUEST_TEST:
+ case ABORT_REQUEST_CANCEL_TEST:
+ abortRequest();
+ break;
- case COMPLETION_REQUEST_TEST:
- case COMPLETION_REQUEST_CANCEL_TEST:
- completionRequest();
- break;
+ case COMPLETION_REQUEST_TEST:
+ case COMPLETION_REQUEST_CANCEL_TEST:
+ completionRequest();
+ break;
- case CONFIRMATION_REQUEST_TEST:
- case CONFIRMATION_REQUEST_CANCEL_TEST:
- confirmationRequest();
- break;
+ case CONFIRMATION_REQUEST_TEST:
+ case CONFIRMATION_REQUEST_CANCEL_TEST:
+ confirmationRequest();
+ break;
- case PICKOPTION_REQUEST_TEST:
- case PICKOPTION_REQUEST_CANCEL_TEST:
- pickOptionRequest();
- break;
+ case PICKOPTION_REQUEST_TEST:
+ case PICKOPTION_REQUEST_CANCEL_TEST:
+ pickOptionRequest();
+ break;
- case COMMANDREQUEST_TEST:
- case COMMANDREQUEST_CANCEL_TEST:
- commandRequest();
- break;
+ case COMMANDREQUEST_TEST:
+ case COMMANDREQUEST_CANCEL_TEST:
+ commandRequest();
+ break;
- case SUPPORTS_COMMANDS_TEST:
- String[] commands = {Utils.TEST_COMMAND};
- boolean[] supported = mInteractor.supportsCommands(commands);
- Log.i(TAG, "from supportsCommands: " + Arrays.toString(supported));
- if (supported.length == 1 && supported[0]) {
- addTestResult(Utils.SUPPORTS_COMMANDS_SUCCESS);
- } else {
- addTestResult(Utils.TEST_ERROR + " supported commands failure!");
- }
- saveTestResults();
- continueTests();
- break;
+ case SUPPORTS_COMMANDS_TEST:
+ String[] commands = {Utils.TEST_COMMAND};
+ boolean[] supported = mInteractor.supportsCommands(commands);
+ Log.i(TAG, "from supportsCommands: " + Arrays.toString(supported));
+ if (supported.length == 1 && supported[0]) {
+ addTestResult(Utils.SUPPORTS_COMMANDS_SUCCESS);
+ } else {
+ addTestResult(Utils.TEST_ERROR + " supported commands failure!");
+ }
+ saveTestResults();
+ continueTests();
+ break;
}
}
@@ -133,11 +133,11 @@
private void broadcastResults() {
Intent intent = new Intent(Utils.BROADCAST_INTENT)
- .addFlags(Intent.FLAG_RECEIVER_FOREGROUND | Intent.FLAG_RECEIVER_REGISTERED_ONLY)
- .putExtras(mTotalInfo);
+ .addFlags(Intent.FLAG_RECEIVER_FOREGROUND | Intent.FLAG_RECEIVER_REGISTERED_ONLY)
+ .putExtras(mTotalInfo);
Log.i(TAG, "broadcasting: " + intent + ", Bundle = " + mTotalInfo);
sendOrderedBroadcast(intent, null, new DoneReceiver(),
- null, Activity.RESULT_OK, null, null);
+ null, Activity.RESULT_OK, null, null);
}
private void confirmationRequest() {
@@ -233,7 +233,7 @@
", selections = " + Utils.toOptionsString(selections) +
", recvd bundle =" + Utils.toBundleString(result));
if ((selections.length != 1) ||
- !selections[0].getLabel().toString().equals(Utils.PICKOPTON_3)) {
+ !selections[0].getLabel().toString().equals(Utils.PICKOPTON_3)) {
Utils.addErrorResult(result,
"Pickoption Selections Not received correctly in TestApp.");
} else {
@@ -285,4 +285,4 @@
Log.i(TAG, "Done_broadcast " + intent.getAction());
}
}
-}
+}
\ No newline at end of file
diff --git a/tests/tests/widget/src/android/widget/cts/PopupMenuTest.java b/tests/tests/widget/src/android/widget/cts/PopupMenuTest.java
index c63c676..7c656ba 100644
--- a/tests/tests/widget/src/android/widget/cts/PopupMenuTest.java
+++ b/tests/tests/widget/src/android/widget/cts/PopupMenuTest.java
@@ -16,6 +16,8 @@
package android.widget.cts;
+import static com.android.compatibility.common.util.CtsMockitoUtils.within;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
@@ -32,6 +34,7 @@
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
+import android.os.SystemClock;
import android.view.Gravity;
import android.view.Menu;
import android.view.MenuInflater;
@@ -45,20 +48,25 @@
import androidx.test.InstrumentationRegistry;
import androidx.test.annotation.UiThreadTest;
-import androidx.test.filters.MediumTest;
+import androidx.test.filters.LargeTest;
import androidx.test.rule.ActivityTestRule;
import androidx.test.runner.AndroidJUnit4;
import com.android.compatibility.common.util.CtsTouchUtils;
import com.android.compatibility.common.util.WidgetTestUtils;
+import junit.framework.Assert;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
-@MediumTest
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+@LargeTest
@RunWith(AndroidJUnit4.class)
public class PopupMenuTest {
private Instrumentation mInstrumentation;
@@ -89,11 +97,23 @@
@After
public void teardown() throws Throwable {
if (mPopupMenu != null) {
- mActivityRule.runOnUiThread(() -> {
- mPopupMenu.dismiss();
+ final CountDownLatch latch = new CountDownLatch(1);
+
+ try {
+ mActivityRule.runOnUiThread(() -> {
+ mPopupMenu.setOnDismissListener((PopupMenu menu) -> {
+ latch.countDown();
+ });
+ mPopupMenu.dismiss();
+ });
+
+ Assert.assertTrue("Expected dismissal occurred within 5 seconds",
+ latch.await(5, TimeUnit.SECONDS));
+ } catch (Throwable t) {
+ throw new RuntimeException(t);
+ } finally {
mPopupMenu = null;
- });
- mInstrumentation.waitForIdleSync();
+ }
}
}
@@ -116,18 +136,24 @@
@Test
public void testPopulateViaInflater() throws Throwable {
- mBuilder = new Builder().inflateWithInflater(true);
+ mBuilder = new Builder().inflateWithInflater(true).withDismissListener();
+
mActivityRule.runOnUiThread(mBuilder::show);
- mInstrumentation.waitForIdleSync();
+ SystemClock.sleep(2000);
+ // Verify that nothing has dismissed the popup menu since it was shown
+ verify(mBuilder.mOnDismissListener, never()).onDismiss(mPopupMenu);
verifyMenuContent();
}
@Test
public void testDirectPopulate() throws Throwable {
- mBuilder = new Builder().inflateWithInflater(false);
+ mBuilder = new Builder().inflateWithInflater(false).withDismissListener();
+
mActivityRule.runOnUiThread(mBuilder::show);
- mInstrumentation.waitForIdleSync();
+ SystemClock.sleep(2000);
+ // Verify that nothing has dismissed the popup menu since it was shown
+ verify(mBuilder.mOnDismissListener, never()).onDismiss(mPopupMenu);
verifyMenuContent();
}
@@ -153,20 +179,24 @@
@Test
public void testDismissalViaAPI() throws Throwable {
mBuilder = new Builder().withDismissListener();
- mActivityRule.runOnUiThread(mBuilder::show);
- mInstrumentation.waitForIdleSync();
+ mActivityRule.runOnUiThread(mBuilder::show);
+ SystemClock.sleep(2000);
+ // Verify that nothing has dismissed the popup menu since it was shown
verify(mBuilder.mOnDismissListener, never()).onDismiss(mPopupMenu);
mActivityRule.runOnUiThread(mPopupMenu::dismiss);
- mInstrumentation.waitForIdleSync();
+ // Verify that the popup menu has been dismissed exactly once
+ verify(mBuilder.mOnDismissListener, within(1000)).onDismiss(mPopupMenu);
verify(mBuilder.mOnDismissListener, times(1)).onDismiss(mPopupMenu);
mActivityRule.runOnUiThread(mPopupMenu::dismiss);
- mInstrumentation.waitForIdleSync();
+ SystemClock.sleep(2000);
// Shouldn't have any more interactions with our dismiss listener since the menu was
// already dismissed when we called dismiss()
verifyNoMoreInteractions(mBuilder.mOnDismissListener);
+
+ mPopupMenu = null;
}
@Test
@@ -177,27 +207,31 @@
mBuilder = new Builder().withDismissListener()
.withPopupStyleResource(R.style.PopupWindow_NullTransitions);
mActivityRule.runOnUiThread(mBuilder::show);
- mInstrumentation.waitForIdleSync();
+ SystemClock.sleep(2000);
+ // Verify that nothing has dismissed the popup menu since it was shown
verify(mBuilder.mOnDismissListener, never()).onDismiss(mPopupMenu);
mActivityRule.runOnUiThread(
() -> mPopupMenu.getMenu().performIdentifierAction(R.id.action_share, 0));
- mInstrumentation.waitForIdleSync();
+ SystemClock.sleep(500);
mActivityRule.runOnUiThread(
() -> mPopupMenu.getMenu().findItem(R.id.action_share).getSubMenu().
performIdentifierAction(R.id.action_share_email, 0));
- mInstrumentation.waitForIdleSync();
+ SystemClock.sleep(500);
mActivityRule.runOnUiThread(mPopupMenu::dismiss);
- mInstrumentation.waitForIdleSync();
+ // Verify that the popup menu has been dismissed exactly once
+ verify(mBuilder.mOnDismissListener, within(1000)).onDismiss(mPopupMenu);
verify(mBuilder.mOnDismissListener, times(1)).onDismiss(mPopupMenu);
mActivityRule.runOnUiThread(mPopupMenu::dismiss);
- mInstrumentation.waitForIdleSync();
+ SystemClock.sleep(2000);
// Shouldn't have any more interactions with our dismiss listener since the menu was
// already dismissed when we called dismiss()
verifyNoMoreInteractions(mBuilder.mOnDismissListener);
+
+ mPopupMenu = null;
}
@Test
@@ -209,7 +243,9 @@
.withPopupMenuContent(R.menu.popup_menu_single)
.withPopupStyleResource(R.style.PopupWindow_NullTransitions);
mActivityRule.runOnUiThread(mBuilder::show);
- mInstrumentation.waitForIdleSync();
+ SystemClock.sleep(2000);
+ // Verify that nothing has dismissed the popup menu since it was shown
+ verify(mBuilder.mOnDismissListener, never()).onDismiss(mPopupMenu);
// The call below uses Instrumentation to emulate a tap outside the bounds of the
// displayed popup menu. This tap is then treated by the framework to be "split" as
@@ -221,8 +257,11 @@
// sequences as well.
CtsTouchUtils.emulateTapOnView(mInstrumentation, mActivityRule, mBuilder.mAnchor, 10, -20);
- // At this point our popup should have notified its dismiss listener
+ // Verify that the popup menu has been dismissed exactly once
+ verify(mBuilder.mOnDismissListener, within(1000)).onDismiss(mPopupMenu);
verify(mBuilder.mOnDismissListener, times(1)).onDismiss(mPopupMenu);
+
+ mPopupMenu = null;
}
@Test
@@ -241,8 +280,11 @@
mPopupMenu.getMenu().findItem(R.id.action_highlight));
// Popup menu should be automatically dismissed on selecting an item
+ verify(mBuilder.mOnDismissListener, within(1000)).onDismiss(mPopupMenu);
verify(mBuilder.mOnDismissListener, times(1)).onDismiss(mPopupMenu);
verifyNoMoreInteractions(mBuilder.mOnDismissListener);
+
+ mPopupMenu = null;
}
@Test
@@ -253,7 +295,7 @@
mBuilder = new Builder().withDismissListener().withMenuItemClickListener()
.withPopupStyleResource(R.style.PopupWindow_NullTransitions);
mActivityRule.runOnUiThread(mBuilder::show);
- mInstrumentation.waitForIdleSync();
+ SystemClock.sleep(2000);
// Verify that our menu item click listener hasn't been called yet
verify(mBuilder.mOnMenuItemClickListener, never()).onMenuItemClick(any(MenuItem.class));
@@ -279,12 +321,15 @@
// Popup menu should be automatically dismissed on selecting an item
verify(mBuilder.mOnDismissListener, times(1)).onDismiss(mPopupMenu);
verifyNoMoreInteractions(mBuilder.mOnDismissListener);
+
+ mPopupMenu = null;
}
@Test
public void testItemViewAttributes() throws Throwable {
mBuilder = new Builder().withDismissListener().withAnchorId(R.id.anchor_upper_left);
- WidgetTestUtils.runOnMainAndLayoutSync(mActivityRule, mBuilder::show, true);
+ mActivityRule.runOnUiThread(mBuilder::show);
+ SystemClock.sleep(2000);
Menu menu = mPopupMenu.getMenu();
ListView menuItemList = mPopupMenu.getMenuListView();
@@ -322,7 +367,8 @@
private void testGroupDivider(boolean groupDividerEnabled) throws Throwable {
mBuilder = new Builder().withGroupDivider(groupDividerEnabled)
.withAnchorId(R.id.anchor_upper_left);
- WidgetTestUtils.runOnMainAndLayoutSync(mActivityRule, mBuilder::show, true);
+ mActivityRule.runOnUiThread(mBuilder::show);
+ SystemClock.sleep(2000);
Menu menu = mPopupMenu.getMenu();
ListView menuItemList = mPopupMenu.getMenuListView();
@@ -359,7 +405,8 @@
private void testForceShowIcon(boolean forceShowIcon) throws Throwable {
mBuilder = new Builder().withForceShowIcon(forceShowIcon);
- WidgetTestUtils.runOnMainAndLayoutSync(mActivityRule, mBuilder::configure, true);
+ mActivityRule.runOnUiThread(mBuilder::configure);
+
final TestColorDrawable drawable = new TestColorDrawable(Color.BLUE);
mPopupMenu.getMenu().getItem(0).setIcon(drawable);
WidgetTestUtils.runOnMainAndDrawSync(
diff --git a/tests/tests/widget/src/android/widget/cts/SearchViewTest.java b/tests/tests/widget/src/android/widget/cts/SearchViewTest.java
index 08dd6a5..5b64009 100644
--- a/tests/tests/widget/src/android/widget/cts/SearchViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/SearchViewTest.java
@@ -28,19 +28,19 @@
import static org.mockito.Mockito.when;
import android.app.Activity;
-import android.app.Instrumentation;
import android.content.res.Resources;
import android.text.InputType;
import android.text.TextUtils;
import android.view.inputmethod.EditorInfo;
import android.widget.SearchView;
-import androidx.test.InstrumentationRegistry;
import androidx.test.annotation.UiThreadTest;
import androidx.test.filters.MediumTest;
import androidx.test.rule.ActivityTestRule;
import androidx.test.runner.AndroidJUnit4;
+import com.android.compatibility.common.util.WidgetTestUtils;
+
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -52,7 +52,6 @@
@MediumTest
@RunWith(AndroidJUnit4.class)
public class SearchViewTest {
- private Instrumentation mInstrumentation;
private Activity mActivity;
private SearchView mSearchView;
@@ -62,7 +61,6 @@
@Before
public void setup() {
- mInstrumentation = InstrumentationRegistry.getInstrumentation();
mActivity = mActivityRule.getActivity();
mSearchView = (SearchView) mActivity.findViewById(R.id.search_view);
}
@@ -123,8 +121,8 @@
mSearchView.setIconified(false);
});
- mActivityRule.runOnUiThread(() -> mSearchView.setIconified(true));
- mInstrumentation.waitForIdleSync();
+ WidgetTestUtils.runOnMainAndLayoutSync(mActivityRule, mSearchView,
+ () -> mSearchView.setIconified(true), true);
// Since our search view is marked with iconifiedByDefault=false, call to setIconified
// with true us going to be ignored, as detailed in the class-level documentation of
@@ -144,8 +142,8 @@
when(mockDenyCloseListener.onClose()).thenReturn(Boolean.TRUE);
mSearchView.setOnCloseListener(mockDenyCloseListener);
- mActivityRule.runOnUiThread(() -> mSearchView.setIconified(true));
- mInstrumentation.waitForIdleSync();
+ WidgetTestUtils.runOnMainAndLayoutSync(mActivityRule, mSearchView,
+ () -> mSearchView.setIconified(true), true);
// Our mock listener is configured to return true from its onClose, thereby preventing
// the iconify request to be completed. Check that the listener was called and that the
@@ -166,8 +164,8 @@
when(mockAllowCloseListener.onClose()).thenReturn(Boolean.FALSE);
mSearchView.setOnCloseListener(mockAllowCloseListener);
- mActivityRule.runOnUiThread(() -> mSearchView.setIconified(true));
- mInstrumentation.waitForIdleSync();
+ WidgetTestUtils.runOnMainAndLayoutSync(mActivityRule, mSearchView,
+ () -> mSearchView.setIconified(true), true);
// Our mock listener is configured to return false from its onClose, thereby allowing
// the iconify request to be completed. Check that the listener was called and that the
@@ -183,15 +181,16 @@
final int maxWidth2 = res.getDimensionPixelSize(R.dimen.search_view_max_width2);
// Set search view to not be iconified before running max-width tests
- mActivityRule.runOnUiThread(() -> mSearchView.setIconified(false));
-
- mActivityRule.runOnUiThread(() -> mSearchView.setMaxWidth(maxWidth1));
- mInstrumentation.waitForIdleSync();
+ WidgetTestUtils.runOnMainAndLayoutSync(mActivityRule, mSearchView,
+ () -> {
+ mSearchView.setIconified(false);
+ mSearchView.setMaxWidth(maxWidth1);
+ }, true);
assertEquals(maxWidth1, mSearchView.getMaxWidth());
assertTrue(mSearchView.getWidth() <= maxWidth1);
- mActivityRule.runOnUiThread(() -> mSearchView.setMaxWidth(maxWidth2));
- mInstrumentation.waitForIdleSync();
+ WidgetTestUtils.runOnMainAndLayoutSync(mActivityRule, mSearchView,
+ () -> mSearchView.setMaxWidth(maxWidth2), true);
assertEquals(maxWidth2, mSearchView.getMaxWidth());
assertTrue(mSearchView.getWidth() <= maxWidth2);
}
diff --git a/tests/tests/widget/src/android/widget/cts/SpinnerTest.java b/tests/tests/widget/src/android/widget/cts/SpinnerTest.java
index 99852e8..d5d5428 100644
--- a/tests/tests/widget/src/android/widget/cts/SpinnerTest.java
+++ b/tests/tests/widget/src/android/widget/cts/SpinnerTest.java
@@ -49,6 +49,7 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.compatibility.common.util.CtsTouchUtils;
+import com.android.compatibility.common.util.PollingCheck;
import com.android.compatibility.common.util.WidgetTestUtils;
import org.junit.Before;
@@ -404,9 +405,8 @@
// Dismiss the popup with the emulated back key
mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_BACK);
- mInstrumentation.waitForIdleSync();
// Verify that we're not showing the popup
- assertFalse(mSpinnerDropdownMode.isPopupShowing());
+ PollingCheck.waitFor(() -> !mSpinnerDropdownMode.isPopupShowing());
// Set yellow background on the popup
mActivityRule.runOnUiThread(() ->
@@ -444,9 +444,8 @@
// Dismiss the popup with the emulated back key
mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_BACK);
- mInstrumentation.waitForIdleSync();
// Verify that we're not showing the popup
- assertFalse(mSpinnerDialogMode.isPopupShowing());
+ PollingCheck.waitFor(() -> !mSpinnerDropdownMode.isPopupShowing());
// Set yellow background on the popup
mActivityRule.runOnUiThread(() ->
diff --git a/tests/tests/widget/src/android/widget/cts/ToastTest.java b/tests/tests/widget/src/android/widget/cts/ToastTest.java
index efc5440..72b5af8 100644
--- a/tests/tests/widget/src/android/widget/cts/ToastTest.java
+++ b/tests/tests/widget/src/android/widget/cts/ToastTest.java
@@ -23,7 +23,6 @@
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
-import android.app.Instrumentation;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
@@ -37,6 +36,8 @@
import android.widget.ImageView;
import android.widget.Toast;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.test.InstrumentationRegistry;
import androidx.test.annotation.UiThreadTest;
import androidx.test.filters.LargeTest;
@@ -47,11 +48,16 @@
import com.android.compatibility.common.util.SystemUtil;
import com.android.compatibility.common.util.TestUtils;
+import junit.framework.Assert;
+
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
@LargeTest
@RunWith(AndroidJUnit4.class)
public class ToastTest {
@@ -63,7 +69,6 @@
private static final long TIME_OUT = 5000L;
private Toast mToast;
private Context mContext;
- private Instrumentation mInstrumentation;
private boolean mLayoutDone;
private ViewTreeObserver.OnGlobalLayoutListener mLayoutListener;
@@ -73,7 +78,6 @@
@Before
public void setup() {
- mInstrumentation = InstrumentationRegistry.getInstrumentation();
mContext = InstrumentationRegistry.getTargetContext();
mLayoutListener = () -> mLayoutDone = true;
}
@@ -118,7 +122,6 @@
private void makeToast() throws Throwable {
mActivityRule.runOnUiThread(
() -> mToast = Toast.makeText(mContext, TEST_TOAST_TEXT, Toast.LENGTH_LONG));
- mInstrumentation.waitForIdleSync();
}
@Test
@@ -131,8 +134,7 @@
assertNull(view.getParent());
assertEquals(View.VISIBLE, view.getVisibility());
- mActivityRule.runOnUiThread(mToast::show);
- mInstrumentation.waitForIdleSync();
+ runOnMainAndDrawSync(view, mToast::show);
// view will be attached to screen when show it
assertEquals(View.VISIBLE, view.getVisibility());
@@ -160,7 +162,6 @@
mToast.show();
mToast.cancel();
});
- mInstrumentation.waitForIdleSync();
assertNotShowToast(view);
}
@@ -174,11 +175,10 @@
Drawable drawable = mContext.getResources().getDrawable(R.drawable.pass);
imageView.setImageDrawable(drawable);
- mActivityRule.runOnUiThread(() -> {
+ runOnMainAndDrawSync(imageView, () -> {
mToast.setView(imageView);
mToast.show();
});
- mInstrumentation.waitForIdleSync();
assertSame(imageView, mToast.getView());
assertShowAndHide(imageView);
}
@@ -187,8 +187,7 @@
public void testAccessDuration() throws Throwable {
long start = SystemClock.uptimeMillis();
makeToast();
- mActivityRule.runOnUiThread(mToast::show);
- mInstrumentation.waitForIdleSync();
+ runOnMainAndDrawSync(mToast.getView(), mToast::show);
assertEquals(Toast.LENGTH_LONG, mToast.getDuration());
View view = mToast.getView();
@@ -196,11 +195,10 @@
long longDuration = SystemClock.uptimeMillis() - start;
start = SystemClock.uptimeMillis();
- mActivityRule.runOnUiThread(() -> {
+ runOnMainAndDrawSync(mToast.getView(), () -> {
mToast.setDuration(Toast.LENGTH_SHORT);
mToast.show();
});
- mInstrumentation.waitForIdleSync();
assertEquals(Toast.LENGTH_SHORT, mToast.getDuration());
view = mToast.getView();
@@ -218,8 +216,7 @@
mToast.show();
};
long start = SystemClock.uptimeMillis();
- mActivityRule.runOnUiThread(showToast);
- mInstrumentation.waitForIdleSync();
+ runOnMainAndDrawSync(mToast.getView(), showToast);
assertShowAndHide(mToast.getView());
final long shortDuration = SystemClock.uptimeMillis() - start;
@@ -232,8 +229,7 @@
waitForA11yRecommendedTimeoutChanged(mContext,
ACCESSIBILITY_STATE_WAIT_TIMEOUT_MS, a11ySettingDuration);
start = SystemClock.uptimeMillis();
- mActivityRule.runOnUiThread(showToast);
- mInstrumentation.waitForIdleSync();
+ runOnMainAndDrawSync(mToast.getView(), showToast);
assertShowAndHide(mToast.getView());
final long a11yDuration = SystemClock.uptimeMillis() - start;
assertTrue(a11yDuration >= a11ySettingDuration);
@@ -284,12 +280,11 @@
final float horizontal1 = 1.0f;
final float vertical1 = 1.0f;
- mActivityRule.runOnUiThread(() -> {
+ runOnMainAndDrawSync(view, () -> {
mToast.setMargin(horizontal1, vertical1);
mToast.show();
registerLayoutListener(mToast.getView());
});
- mInstrumentation.waitForIdleSync();
assertShowToast(view);
assertEquals(horizontal1, mToast.getHorizontalMargin(), 0.0f);
@@ -305,12 +300,11 @@
final float horizontal2 = 0.1f;
final float vertical2 = 0.1f;
- mActivityRule.runOnUiThread(() -> {
+ runOnMainAndDrawSync(view, () -> {
mToast.setMargin(horizontal2, vertical2);
mToast.show();
registerLayoutListener(mToast.getView());
});
- mInstrumentation.waitForIdleSync();
assertShowToast(view);
assertEquals(horizontal2, mToast.getHorizontalMargin(), 0.0f);
@@ -342,12 +336,11 @@
@Test
public void testAccessGravity() throws Throwable {
makeToast();
- mActivityRule.runOnUiThread(() -> {
+ runOnMainAndDrawSync(mToast.getView(), () -> {
mToast.setGravity(Gravity.CENTER, 0, 0);
mToast.show();
registerLayoutListener(mToast.getView());
});
- mInstrumentation.waitForIdleSync();
View view = mToast.getView();
assertShowToast(view);
assertEquals(Gravity.CENTER, mToast.getGravity());
@@ -358,12 +351,11 @@
view.getLocationOnScreen(centerXY);
assertShowAndHide(view);
- mActivityRule.runOnUiThread(() -> {
+ runOnMainAndDrawSync(mToast.getView(), () -> {
mToast.setGravity(Gravity.BOTTOM, 0, 0);
mToast.show();
registerLayoutListener(mToast.getView());
});
- mInstrumentation.waitForIdleSync();
view = mToast.getView();
assertShowToast(view);
assertEquals(Gravity.BOTTOM, mToast.getGravity());
@@ -381,12 +373,11 @@
final int xOffset = 20;
final int yOffset = 10;
- mActivityRule.runOnUiThread(() -> {
+ runOnMainAndDrawSync(mToast.getView(), () -> {
mToast.setGravity(Gravity.BOTTOM, xOffset, yOffset);
mToast.show();
registerLayoutListener(mToast.getView());
});
- mInstrumentation.waitForIdleSync();
view = mToast.getView();
assertShowToast(view);
assertEquals(Gravity.BOTTOM, mToast.getGravity());
@@ -425,7 +416,7 @@
@UiThreadTest
@Test(expected=NullPointerException.class)
- public void testMaketTextFromStringNullContext() {
+ public void testMakeTextFromStringNullContext() {
Toast.makeText(null, "test", Toast.LENGTH_LONG);
}
@@ -448,7 +439,7 @@
@UiThreadTest
@Test(expected=NullPointerException.class)
- public void testMaketTextFromResourceNullContext() {
+ public void testMakeTextFromResourceNullContext() {
Toast.makeText(null, R.string.hello_android, Toast.LENGTH_SHORT);
}
@@ -490,4 +481,38 @@
toast.setView(null);
toast.setText(null);
}
+
+ private void runOnMainAndDrawSync(@NonNull final View toastView,
+ @Nullable final Runnable runner) {
+ final CountDownLatch latch = new CountDownLatch(1);
+
+ try {
+ mActivityRule.runOnUiThread(() -> {
+ final ViewTreeObserver.OnDrawListener listener =
+ new ViewTreeObserver.OnDrawListener() {
+ @Override
+ public void onDraw() {
+ // posting so that the sync happens after the draw that's about
+ // to happen
+ toastView.post(() -> {
+ toastView.getViewTreeObserver().removeOnDrawListener(this);
+ latch.countDown();
+ });
+ }
+ };
+
+ toastView.getViewTreeObserver().addOnDrawListener(listener);
+
+ if (runner != null) {
+ runner.run();
+ }
+ toastView.invalidate();
+ });
+
+ Assert.assertTrue("Expected toast draw pass occurred within 5 seconds",
+ latch.await(5, TimeUnit.SECONDS));
+ } catch (Throwable t) {
+ throw new RuntimeException(t);
+ }
+ }
}
diff --git a/tests/tests/widget/src/android/widget/cts/ToolbarTest.java b/tests/tests/widget/src/android/widget/cts/ToolbarTest.java
index 227aeeb..2e3dca6 100644
--- a/tests/tests/widget/src/android/widget/cts/ToolbarTest.java
+++ b/tests/tests/widget/src/android/widget/cts/ToolbarTest.java
@@ -45,6 +45,7 @@
import androidx.test.rule.ActivityTestRule;
import androidx.test.runner.AndroidJUnit4;
+import com.android.compatibility.common.util.PollingCheck;
import com.android.compatibility.common.util.WidgetTestUtils;
import org.junit.Before;
@@ -167,13 +168,11 @@
// Ask to show overflow menu and check that it's showing
mActivityRule.runOnUiThread(() -> mMainToolbar.showOverflowMenu());
- mInstrumentation.waitForIdleSync();
- assertTrue(mMainToolbar.isOverflowMenuShowing());
+ PollingCheck.waitFor(() -> mMainToolbar.isOverflowMenuShowing());
// Ask to hide the overflow menu and check that it's not showing
mActivityRule.runOnUiThread(() -> mMainToolbar.hideOverflowMenu());
- mInstrumentation.waitForIdleSync();
- assertFalse(mMainToolbar.isOverflowMenuShowing());
+ PollingCheck.waitFor(() -> !mMainToolbar.isOverflowMenuShowing());
}
@Test
@@ -185,8 +184,7 @@
// Ask to show overflow menu and check that it's showing
mActivityRule.runOnUiThread(mMainToolbar::showOverflowMenu);
- mInstrumentation.waitForIdleSync();
- assertTrue(mMainToolbar.isOverflowMenuShowing());
+ PollingCheck.waitFor(() -> mMainToolbar.isOverflowMenuShowing());
// Register a mock menu item click listener on the toolbar
Toolbar.OnMenuItemClickListener menuItemClickListener =
@@ -203,8 +201,7 @@
// Ask to dismiss all the popups and check that we're not showing the overflow menu
mActivityRule.runOnUiThread(mMainToolbar::dismissPopupMenus);
- mInstrumentation.waitForIdleSync();
- assertFalse(mMainToolbar.isOverflowMenuShowing());
+ PollingCheck.waitFor(() -> !mMainToolbar.isOverflowMenuShowing());
}
@Test
@@ -234,29 +231,25 @@
// action view
final MenuItem searchMenuItem = mMainToolbar.getMenu().findItem(R.id.action_search);
mActivityRule.runOnUiThread(searchMenuItem::expandActionView);
- mInstrumentation.waitForIdleSync();
- assertTrue(searchMenuItem.isActionViewExpanded());
- assertTrue(mMainToolbar.hasExpandedActionView());
+ PollingCheck.waitFor(() ->
+ searchMenuItem.isActionViewExpanded() && mMainToolbar.hasExpandedActionView());
// Collapse search menu item's action view and verify that main toolbar doesn't have an
// expanded action view
mActivityRule.runOnUiThread(searchMenuItem::collapseActionView);
- mInstrumentation.waitForIdleSync();
- assertFalse(searchMenuItem.isActionViewExpanded());
- assertFalse(mMainToolbar.hasExpandedActionView());
+ PollingCheck.waitFor(() ->
+ !searchMenuItem.isActionViewExpanded() && !mMainToolbar.hasExpandedActionView());
// Expand search menu item's action view again
mActivityRule.runOnUiThread(searchMenuItem::expandActionView);
- mInstrumentation.waitForIdleSync();
- assertTrue(searchMenuItem.isActionViewExpanded());
- assertTrue(mMainToolbar.hasExpandedActionView());
+ PollingCheck.waitFor(() ->
+ searchMenuItem.isActionViewExpanded() && mMainToolbar.hasExpandedActionView());
// Now collapse search menu item's action view via toolbar's API and verify that main
// toolbar doesn't have an expanded action view
mActivityRule.runOnUiThread(mMainToolbar::collapseActionView);
- mInstrumentation.waitForIdleSync();
- assertFalse(searchMenuItem.isActionViewExpanded());
- assertFalse(mMainToolbar.hasExpandedActionView());
+ PollingCheck.waitFor(() ->
+ !searchMenuItem.isActionViewExpanded() && !mMainToolbar.hasExpandedActionView());
}
@Test
@@ -294,7 +287,8 @@
() -> mMainToolbar.inflateMenu(R.menu.toolbar_menu_search));
final MenuItem searchMenuItem = mMainToolbar.getMenu().findItem(R.id.action_search);
mActivityRule.runOnUiThread(searchMenuItem::expandActionView);
- mInstrumentation.waitForIdleSync();
+ PollingCheck.waitFor(() ->
+ searchMenuItem.isActionViewExpanded() && mMainToolbar.hasExpandedActionView());
WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mMainToolbar,
() -> mMainToolbar.setCollapseIcon(R.drawable.icon_green));
diff --git a/tests/tests/widget/src/android/widget/cts/VideoViewTest.java b/tests/tests/widget/src/android/widget/cts/VideoViewTest.java
index 986e024..1e81ae0 100644
--- a/tests/tests/widget/src/android/widget/cts/VideoViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/VideoViewTest.java
@@ -29,7 +29,6 @@
import static org.mockito.Mockito.verifyZeroInteractions;
import android.app.Activity;
-import android.app.Instrumentation;
import android.content.Context;
import android.media.AudioAttributes;
import android.media.AudioManager;
@@ -41,7 +40,6 @@
import android.widget.MediaController;
import android.widget.VideoView;
-import androidx.test.InstrumentationRegistry;
import androidx.test.annotation.UiThreadTest;
import androidx.test.filters.LargeTest;
import androidx.test.rule.ActivityTestRule;
@@ -84,7 +82,6 @@
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.build();
- private Instrumentation mInstrumentation;
private Activity mActivity;
private VideoView mVideoView;
private String mVideoPath;
@@ -95,7 +92,6 @@
@Before
public void setup() throws Throwable {
- mInstrumentation = InstrumentationRegistry.getInstrumentation();
mActivity = mActivityRule.getActivity();
mVideoView = (VideoView) mActivity.findViewById(R.id.videoview);
@@ -124,7 +120,6 @@
MediaController mediaController = new MediaController(mActivity);
mVideoView.setMediaController(mediaController);
});
- mInstrumentation.waitForIdleSync();
}
@UiThreadTest
@@ -224,7 +219,6 @@
mVideoView.setVideoPath(path);
mVideoView.start();
});
- mInstrumentation.waitForIdleSync();
verify(mockErrorListener, within(TIME_OUT)).onError(
any(MediaPlayer.class), anyInt(), anyInt());
@@ -245,7 +239,6 @@
mVideoView.setOnPreparedListener(mockPreparedListener);
mActivityRule.runOnUiThread(() -> mVideoView.setVideoPath(mVideoPath));
- mInstrumentation.waitForIdleSync();
verify(mockPreparedListener, within(TIME_OUT)).onPrepared(any(MediaPlayer.class));
verify(mockPreparedListener, times(1)).onPrepared(any(MediaPlayer.class));
diff --git a/tests/tests/widget/src/android/widget/cts/ViewFlipperTest.java b/tests/tests/widget/src/android/widget/cts/ViewFlipperTest.java
index 1cd5c26..a223b97 100644
--- a/tests/tests/widget/src/android/widget/cts/ViewFlipperTest.java
+++ b/tests/tests/widget/src/android/widget/cts/ViewFlipperTest.java
@@ -22,7 +22,6 @@
import static org.junit.Assert.assertTrue;
import android.app.Activity;
-import android.app.Instrumentation;
import android.os.SystemClock;
import android.util.AttributeSet;
import android.util.Xml;
@@ -30,7 +29,6 @@
import android.widget.TextView;
import android.widget.ViewFlipper;
-import androidx.test.InstrumentationRegistry;
import androidx.test.annotation.UiThreadTest;
import androidx.test.filters.LargeTest;
import androidx.test.filters.MediumTest;
@@ -49,7 +47,6 @@
@MediumTest
@RunWith(AndroidJUnit4.class)
public class ViewFlipperTest {
- private Instrumentation mInstrumentation;
private Activity mActivity;
@Rule
@@ -58,7 +55,6 @@
@Before
public void setup() {
- mInstrumentation = InstrumentationRegistry.getInstrumentation();
mActivity = mActivityRule.getActivity();
}
@@ -113,7 +109,6 @@
// wait for a longer time to make sure the view flipping is completed.
SystemClock.sleep(flipInterval + 200);
- mInstrumentation.waitForIdleSync();
mActivityRule.runOnUiThread(() -> {
ViewFlipper viewFlipper =
(ViewFlipper) mActivity.findViewById(R.id.viewflipper_test);
@@ -127,7 +122,6 @@
});
SystemClock.sleep(flipInterval + 200);
- mInstrumentation.waitForIdleSync();
mActivityRule.runOnUiThread(() -> {
ViewFlipper viewFlipper =
(ViewFlipper) mActivity.findViewById(R.id.viewflipper_test);
diff --git a/tests/tests/widget/src/android/widget/cts/util/ListUtil.java b/tests/tests/widget/src/android/widget/cts/util/ListUtil.java
index 8215b9f..4c1b4fd 100644
--- a/tests/tests/widget/src/android/widget/cts/util/ListUtil.java
+++ b/tests/tests/widget/src/android/widget/cts/util/ListUtil.java
@@ -38,33 +38,6 @@
}
/**
- * Set the selected position of the list view.
- * @param pos The desired position.
- */
- public final void setSelectedPosition(final int pos) {
- mListView.post(new Runnable() {
- public void run() {
- mListView.setSelection(pos);
- }
- });
- mInstrumentation.waitForIdleSync();
- }
-
- /**
- * Get the top of the list.
- */
- public final int getListTop() {
- return mListView.getListPaddingTop();
- }
-
- /**
- * Get the bottom of the list.
- */
- public final int getListBottom() {
- return mListView.getHeight() - mListView.getListPaddingBottom();
- }
-
- /**
* Arrow (up or down as appropriate) to the desired position in the list.
* @param desiredPos The desired position
* @throws IllegalStateException if the position can't be reached within 20 presses.
diff --git a/tests/video/AndroidTest.xml b/tests/video/AndroidTest.xml
index 1c02dae..aa1b576 100644
--- a/tests/video/AndroidTest.xml
+++ b/tests/video/AndroidTest.xml
@@ -27,5 +27,7 @@
<!-- test-timeout unit is ms, value = 10 min -->
<option name="test-timeout" value="600000" />
<option name="runtime-hint" value="42m45s" />
+ <!-- disable isolated storage so tests can access dynamic config stored in /sdcard. -->
+ <option name="isolated-storage" value="false" />
</test>
</configuration>
diff --git a/tests/video/src/android/video/cts/VideoEncoderDecoderTest.java b/tests/video/src/android/video/cts/VideoEncoderDecoderTest.java
index 07e7c6c..9ce6ada 100644
--- a/tests/video/src/android/video/cts/VideoEncoderDecoderTest.java
+++ b/tests/video/src/android/video/cts/VideoEncoderDecoderTest.java
@@ -28,6 +28,7 @@
import android.media.cts.CodecImage;
import android.media.cts.CodecUtils;
import android.media.cts.YUVImage;
+import android.os.Build;
import android.util.Log;
import android.util.Pair;
import android.util.Range;
@@ -141,6 +142,15 @@
private TestConfig mTestConfig;
+ // Performance numbers only make sense on real devices, so skip on non-real devices
+ public static boolean frankenDevice() {
+ if (("Android".equals(Build.BRAND) || "generic".equals(Build.BRAND)) &&
+ (Build.MODEL.startsWith("AOSP on ") || Build.PRODUCT.startsWith("aosp_"))) {
+ return true;
+ }
+ return false;
+ }
+
@Override
protected void setUp() throws Exception {
mEncodedOutputBuffer = new LinkedList<Pair<ByteBuffer, BufferInfo>>();
@@ -696,6 +706,10 @@
mTestConfig.mMaxTimeMs = Math.min(
mTestConfig.mMaxTimeMs, MAX_TEST_TIMEOUT_MS / 5 * 4 / codingPasses
/ mTestConfig.mNumberOfRepeat);
+ // reduce test-run on non-real devices
+ if (frankenDevice()) {
+ mTestConfig.mMaxTimeMs /= 10;
+ }
mVideoWidth = w;
mVideoHeight = h;
@@ -802,7 +816,12 @@
if (isPerf) {
String error = MediaPerfUtils.verifyAchievableFrameRates(
encoderName, mimeType, w, h, measuredFps);
- assertNull(error, error);
+ if (frankenDevice() && error != null) {
+ // ensure there is data, but don't insist that it is correct
+ assertFalse(error, error.startsWith("Failed to get "));
+ } else {
+ assertNull(error, error);
+ }
}
assertTrue(success);
}
diff --git a/tests/vr/AndroidTest.xml b/tests/vr/AndroidTest.xml
index ee9d9cb..cca1a02a 100644
--- a/tests/vr/AndroidTest.xml
+++ b/tests/vr/AndroidTest.xml
@@ -17,7 +17,7 @@
<option name="test-suite-tag" value="cts" />
<option name="config-descriptor:metadata" key="component" value="vr" />
<option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
- <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="CtsVrTestCases.apk" />