Merge "Enable verbose logging for the test app."
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index e09cccd..7b9c427 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -37,6 +37,7 @@
tests/tests/widget/
common/device-side/util/
hostsidetests/stagedinstall/
+ hostsidetests/userspacereboot/
tests/tests/packageinstaller/atomicinstall/
ktlint_hook = ${REPO_ROOT}/prebuilts/ktlint/ktlint.py -f ${PREUPLOAD_FILES}
diff --git a/apps/CameraITS/tests/scene2_e/test_continuous_picture.py b/apps/CameraITS/tests/scene2_e/test_continuous_picture.py
new file mode 100644
index 0000000..3f64e3a
--- /dev/null
+++ b/apps/CameraITS/tests/scene2_e/test_continuous_picture.py
@@ -0,0 +1,98 @@
+# Copyright 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os.path
+import its.caps
+import its.device
+import its.image
+import its.objects
+
+CONTINUOUS_PICTURE_MODE = 4
+CONVERGED_3A = [[2, 2, 2], [2, 5, 2], [2, 6, 2]] # [AE, AF, AWB]
+# AE_STATES: {0: INACTIVE, 1: SEARCHING, 2: CONVERGED, 3: LOCKED,
+# 4: FLASH_REQ, 5: PRECAPTURE}
+# AF_STATES: {0: INACTIVE, 1: PASSIVE_SCAN, 2: PASSIVE_FOCUSED,
+# 3: PASSIVE_UNFOCUSED, 4: ACTIVE_SCAN, 5: FOCUS_LOCKED,
+# 6: NOT_FOCUSED_LOCKED}
+# AWB_STATES: {0: INACTIVE, 1: SEARCHING, 2: CONVERGED, 3: LOCKED}
+NAME = os.path.basename(__file__).split('.')[0]
+NUM_FRAMES = 50
+W, H = 640, 480
+
+
+def capture_frames(cam, debug):
+ """Capture frames."""
+ cap_data_list = []
+ req = its.objects.auto_capture_request()
+ req['android.control.afMode'] = CONTINUOUS_PICTURE_MODE
+ fmt = {'format': 'yuv', 'width': W, 'height': H}
+ caps = cam.do_capture([req]*NUM_FRAMES, fmt)
+
+ # extract frame metadata and frame
+ for i, cap in enumerate(caps):
+ cap_data = {}
+ md = cap['metadata']
+ exp = md['android.sensor.exposureTime']
+ iso = md['android.sensor.sensitivity']
+ fd = md['android.lens.focalLength']
+ ae_state = md['android.control.aeState']
+ af_state = md['android.control.afState']
+ awb_state = md['android.control.awbState']
+ fd_str = 'infinity'
+ if fd != 0.0:
+ fd_str = str(round(1.0E2/fd, 2)) + 'cm'
+ img = its.image.convert_capture_to_rgb_image(cap)
+ tile = its.image.get_image_patch(img, 0.45, 0.45, 0.1, 0.1)
+ g = its.image.compute_image_means(tile)[1]
+ print '%d, iso: %d, exp: %.2fms, fd: %s, avg: %.3f' % (
+ i, iso, exp*1E-6, fd_str, g),
+ print '[ae,af,awb]: [%d,%d,%d]' % (ae_state, af_state, awb_state)
+ cap_data['exp'] = exp
+ cap_data['iso'] = iso
+ cap_data['fd'] = fd
+ cap_data['3a_state'] = [ae_state, af_state, awb_state]
+ cap_data['avg'] = g
+ cap_data_list.append(cap_data)
+ if debug:
+ its.image.write_image(img, '%s_%d.jpg' % (NAME, i))
+ return cap_data_list
+
+
+def main():
+ """Test 3A converges in CONTINUOUS_PICTURE mode.
+
+ Set camera into CONTINUOUS_PICTURE mode and do NUM_FRAMES capture.
+ By the end of NUM_FRAMES capture, 3A should be in converged state.
+ """
+
+ # check for skip conditions and do 3a up front
+ with its.device.ItsSession() as cam:
+ props = cam.get_camera_properties()
+ props = cam.override_with_hidden_physical_camera_props(props)
+ its.caps.skip_unless(its.caps.continuous_picture(props) and
+ its.caps.read_3a(props))
+ debug = its.caps.debug_mode()
+ cam.do_3a()
+
+ # ensure 3a settles in CONTINUOUS_PICTURE mode with no scene change
+ cap_data = capture_frames(cam, debug)
+ final_3a = cap_data[NUM_FRAMES-1]['3a_state']
+ msg = '\n Last frame [ae, af, awb] state: [%d, %d, %d]' % (
+ final_3a[0], final_3a[1], final_3a[2])
+ msg += '\n Converged states:' + str(CONVERGED_3A)
+ assert final_3a in CONVERGED_3A, msg
+
+
+if __name__ == '__main__':
+ main()
diff --git a/apps/CameraITS/tests/scene4/test_multi_camera_alignment.py b/apps/CameraITS/tests/scene4/test_multi_camera_alignment.py
index 976bee0..75b7a5d 100644
--- a/apps/CameraITS/tests/scene4/test_multi_camera_alignment.py
+++ b/apps/CameraITS/tests/scene4/test_multi_camera_alignment.py
@@ -30,6 +30,7 @@
ALIGN_TOL = 0.01 # multiplied by sensor diagonal to convert to pixels
CIRCLE_RTOL = 0.1
GYRO_REFERENCE = 1
+LENS_FACING_BACK = 1 # 0: FRONT, 1: BACK, 2: EXTERNAL
UNDEFINED_REFERENCE = 2
NAME = os.path.basename(__file__).split('.')[0]
TRANS_REF_MATRIX = np.array([0, 0, 0])
@@ -333,7 +334,7 @@
its.caps.logical_multi_camera(props))
# Convert chart_distance for lens facing back
- if props['android.lens.facing']:
+ if props['android.lens.facing'] == LENS_FACING_BACK:
# API spec defines +z is pointing out from screen
print 'lens facing BACK'
chart_distance *= -1
diff --git a/apps/CameraITS/tools/run_all_tests.py b/apps/CameraITS/tools/run_all_tests.py
index 20fd98b..021a324 100644
--- a/apps/CameraITS/tools/run_all_tests.py
+++ b/apps/CameraITS/tools/run_all_tests.py
@@ -113,7 +113,8 @@
['test_num_faces', 30]
],
'scene2_e': [
- ['test_num_faces', 30]
+ ['test_num_faces', 30],
+ ['test_continuous_picture', 30]
],
'scene3': [],
'scene4': [],
diff --git a/apps/CtsVerifier/res/layout/audio_frequency_mic_activity.xml b/apps/CtsVerifier/res/layout/audio_frequency_mic_activity.xml
index 272a9fc..a0a715f 100644
--- a/apps/CtsVerifier/res/layout/audio_frequency_mic_activity.xml
+++ b/apps/CtsVerifier/res/layout/audio_frequency_mic_activity.xml
@@ -32,6 +32,8 @@
android:orientation="vertical"
>
+ <include layout="@layout/audio_refmic_layout"/>
+
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
diff --git a/apps/CtsVerifier/res/layout/audio_frequency_speaker_activity.xml b/apps/CtsVerifier/res/layout/audio_frequency_speaker_activity.xml
index 435f5a7..a9aeafa 100644
--- a/apps/CtsVerifier/res/layout/audio_frequency_speaker_activity.xml
+++ b/apps/CtsVerifier/res/layout/audio_frequency_speaker_activity.xml
@@ -30,70 +30,72 @@
android:layout_height="wrap_content"
android:orientation="vertical">
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:scrollbars="vertical"
- android:gravity="bottom"
- android:id="@+id/info_text"
- android:text="@string/audio_frequency_speaker_instructions"/>
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
- <Button
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:id="@+id/audio_frequency_speaker_mic_ready_btn"
- android:text="@string/audio_frequency_speaker_mic_ready_btn"
- android:nextFocusForward="@+id/audio_frequency_speaker_test_btn"
- android:nextFocusDown="@+id/audio_frequency_speaker_test_btn"
- android:nextFocusRight="@+id/audio_frequency_speaker_test_btn" />
+ <include layout="@layout/audio_refmic_layout"/>
<TextView
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:text="@string/audio_frequency_speaker_usb_status"
- android:id="@+id/audio_frequency_speaker_usb_status"/>
+ android:scrollbars="vertical"
+ android:gravity="bottom"
+ android:id="@+id/info_text"
+ android:text="@string/audio_frequency_speaker_instructions"/>
<LinearLayout
- android:orientation="vertical"
android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <LinearLayout
- android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+ <Button
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:id="@+id/audio_frequency_speaker_layout">
- <Button
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/audio_frequency_speaker_test_btn"
- android:id="@+id/audio_frequency_speaker_test_btn"
- android:nextFocusForward="@+id/pass_button"
- android:nextFocusUp="@+id/audio_frequency_speaker_mic_ready_btn"
- android:nextFocusDown="@+id/pass_button"
- android:nextFocusLeft="@+id/audio_frequency_speaker_mic_ready_btn"
- android:nextFocusRight="@+id/pass_button" />
-
- <ProgressBar
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:id="@+id/audio_frequency_speaker_progress_bar"/>
- </LinearLayout>
+ android:id="@+id/audio_frequency_speaker_mic_ready_btn"
+ android:text="@string/audio_frequency_speaker_mic_ready_btn"
+ android:nextFocusForward="@+id/audio_frequency_speaker_test_btn"
+ android:nextFocusDown="@+id/audio_frequency_speaker_test_btn"
+ android:nextFocusRight="@+id/audio_frequency_speaker_test_btn" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:text="@string/audio_frequency_speaker_results_text"
- android:id="@+id/audio_frequency_speaker_results_text"/>
+ android:text="@string/audio_frequency_speaker_usb_status"
+ android:id="@+id/audio_frequency_speaker_usb_status"/>
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:id="@+id/audio_frequency_speaker_layout">
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/audio_frequency_speaker_test_btn"
+ android:id="@+id/audio_frequency_speaker_test_btn"
+ android:nextFocusForward="@+id/pass_button"
+ android:nextFocusUp="@+id/audio_frequency_speaker_mic_ready_btn"
+ android:nextFocusDown="@+id/pass_button"
+ android:nextFocusLeft="@+id/audio_frequency_speaker_mic_ready_btn"
+ android:nextFocusRight="@+id/pass_button" />
+
+ <ProgressBar
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/audio_frequency_speaker_progress_bar"/>
+ </LinearLayout>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/audio_frequency_speaker_results_text"
+ android:id="@+id/audio_frequency_speaker_results_text"/>
+
+ </LinearLayout>
</LinearLayout>
- </LinearLayout>
- <include layout="@layout/pass_fail_buttons"/>
+ <include layout="@layout/pass_fail_buttons"/>
</LinearLayout>
</ScrollView>
diff --git a/apps/CtsVerifier/res/layout/audio_frequency_unprocessed_activity.xml b/apps/CtsVerifier/res/layout/audio_frequency_unprocessed_activity.xml
index d02ef0b..8032f93 100644
--- a/apps/CtsVerifier/res/layout/audio_frequency_unprocessed_activity.xml
+++ b/apps/CtsVerifier/res/layout/audio_frequency_unprocessed_activity.xml
@@ -23,6 +23,8 @@
android:layout_height="wrap_content"
android:orientation="vertical">
+ <include layout="@layout/audio_refmic_layout"/>
+
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
diff --git a/apps/CtsVerifier/res/layout/audio_frequency_voice_recognition_activity.xml b/apps/CtsVerifier/res/layout/audio_frequency_voice_recognition_activity.xml
index c6cd0cd..1ccd947 100644
--- a/apps/CtsVerifier/res/layout/audio_frequency_voice_recognition_activity.xml
+++ b/apps/CtsVerifier/res/layout/audio_frequency_voice_recognition_activity.xml
@@ -31,6 +31,8 @@
android:layout_height="wrap_content"
android:orientation="vertical">
+ <include layout="@layout/audio_refmic_layout"/>
+
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
diff --git a/apps/CtsVerifier/res/layout/audio_refmic_layout.xml b/apps/CtsVerifier/res/layout/audio_refmic_layout.xml
new file mode 100644
index 0000000..bd806a1
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/audio_refmic_layout.xml
@@ -0,0 +1,38 @@
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/refmic_test_question"/>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:soundEffectsEnabled="false"
+ android:text="@string/refmic_test_yes"
+ android:id="@+id/refmic_tests_yes_btn" />
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:soundEffectsEnabled="false"
+ android:text="@string/refmic_test_no"
+ android:id="@+id/refmic_tests_no_btn" />
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:soundEffectsEnabled="false"
+ android:text="@string/refmic_test_info"
+ android:id="@+id/refmic_test_info_btn" />
+ </LinearLayout>
+</LinearLayout>
diff --git a/apps/CtsVerifier/res/layout/uap_attribs_panel.xml b/apps/CtsVerifier/res/layout/uap_attribs_panel.xml
index 6c33d9f..b9c5d3e 100644
--- a/apps/CtsVerifier/res/layout/uap_attribs_panel.xml
+++ b/apps/CtsVerifier/res/layout/uap_attribs_panel.xml
@@ -9,6 +9,8 @@
android:layout_width="match_parent"
android:layout_height="wrap_content">
+ <include layout="@layout/uap_usb_confirm"/>
+
<include layout="@layout/uap_profile_header"/>
<LinearLayout
diff --git a/apps/CtsVerifier/res/layout/uap_buttons_panel.xml b/apps/CtsVerifier/res/layout/uap_buttons_panel.xml
index 4fdc757..355bfb6 100644
--- a/apps/CtsVerifier/res/layout/uap_buttons_panel.xml
+++ b/apps/CtsVerifier/res/layout/uap_buttons_panel.xml
@@ -9,6 +9,8 @@
android:layout_width="match_parent"
android:layout_height="wrap_content">
+ <include layout="@layout/uap_usb_confirm"/>
+
<include layout="@layout/uap_profile_header"/>
<LinearLayout
diff --git a/apps/CtsVerifier/res/layout/uap_play_panel.xml b/apps/CtsVerifier/res/layout/uap_play_panel.xml
index 2faa50c..30ef884 100644
--- a/apps/CtsVerifier/res/layout/uap_play_panel.xml
+++ b/apps/CtsVerifier/res/layout/uap_play_panel.xml
@@ -9,6 +9,8 @@
android:layout_width="match_parent"
android:layout_height="wrap_content">
+ <include layout="@layout/uap_usb_confirm"/>
+
<include layout="@layout/uap_profile_header"/>
<LinearLayout
diff --git a/apps/CtsVerifier/res/layout/uap_record_panel.xml b/apps/CtsVerifier/res/layout/uap_record_panel.xml
index 2deb738..a5bfcfc 100644
--- a/apps/CtsVerifier/res/layout/uap_record_panel.xml
+++ b/apps/CtsVerifier/res/layout/uap_record_panel.xml
@@ -9,6 +9,8 @@
android:layout_width="match_parent"
android:layout_height="wrap_content">
+ <include layout="@layout/uap_usb_confirm"/>
+
<include layout="@layout/uap_profile_header"/>
<LinearLayout
diff --git a/apps/CtsVerifier/res/layout/uap_usb_confirm.xml b/apps/CtsVerifier/res/layout/uap_usb_confirm.xml
new file mode 100644
index 0000000..ea73b88
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/uap_usb_confirm.xml
@@ -0,0 +1,38 @@
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/uap_test_question"/>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:soundEffectsEnabled="false"
+ android:text="@string/uap_test_yes"
+ android:id="@+id/uap_tests_yes_btn" />
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:soundEffectsEnabled="false"
+ android:text="@string/uap_test_no"
+ android:id="@+id/uap_tests_no_btn" />
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:soundEffectsEnabled="false"
+ android:text="@string/uap_test_info"
+ android:id="@+id/uap_test_info_btn" />
+ </LinearLayout>
+</LinearLayout>
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index 3df427f..4f9cc0a9 100755
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -2125,8 +2125,6 @@
<string name="nls_unblock_app">Please unblock the linked application and return here.</string>
<string name="nls_block_channel">Please block the linked notification channel and return here.</string>
<string name="nls_block_group">Please block the linked notification channel group and return here.</string>
- <string name="nls_cannot_enable_service">Please make sure you cannot enable
- \"Notification Listener for CTS Verifier\" and return here.</string>
<string name="nls_disable_service">Please disable \"Notification Listener for CTS Verifier\"
under Apps > Gear Icon > Special Access > Notification Access and return here.</string>
<string name="nls_start_settings">Launch Settings</string>
@@ -2158,11 +2156,10 @@
<string name="nas_note_missed_enqueued">Check that notification was not enqueued.</string>
<string name="cp_test">Condition Provider test</string>
<string name="cp_service_name">Condition Provider for CTS Verifier</string>
- <string name="cp_info">This test checks that on non-low ram a ConditionProviderService can be enabled
+ <string name="cp_info">This test checks that ConditionProviderService can be enabled
and disabled, and that once enabled the service is able to create, query, edit, and delete
- automatic zen rules. On low ram devices condition providers should not be bound.
+ automatic zen rules.
</string>
- <string name="cp_cannot_enable_service">Please make sure you cannot enable \"CTS Verifier\" under Do Not Disturb access and return here.</string>
<string name="cp_enable_service">Please enable \"CTS Verifier\" under Do Not Disturb access and return here.</string>
<string name="cp_disable_service">Please disable \"CTS Verifier\" under Do Not Disturb access and return here.</string>
<string name="cp_start_settings">Launch Settings</string>
@@ -5413,4 +5410,30 @@
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>
+
+ <string name="uap_test_no">No</string>
+ <string name="uap_test_yes">Yes</string>
+ <string name="uap_test_info">Info</string>
+ <string name="uap_test_question">Does this device allow for the connectiono of a USB reference microphone?</string>
+ <string name="uap_mic_dlg_caption">USB Host Mode Audio Required</string>
+ <string name="uap_mic_dlg_text">This test requires a USB audio peripheral to be connected to the device.
+ If the device under test does not support USB Host Mode Audio (either because it does not have a
+ USB port, or USB Host Mode Audio has been removed from the OS) you can be granted a provisional
+ pass on this test by pressing the \"No\" button and indicating \"Test Pass\" at the bottom.\n
+ Note: Handheld devices supporting USB host mode MUST support USB audio class (CDD 7.7 .2/H-1-1)\n
+ Note: Devices declaring feature android.hardware.audio.pro MUST implement USB host mode (CDD 5.10 C-1-3) and if they omit a 4 conductor 3.5mm audio jack MUST support USB audio class (CDD 5.10 C-3-1)
+ </string>
+
+ <string name="refmic_test_no">No</string>
+ <string name="refmic_test_yes">Yes</string>
+ <string name="refmic_test_info">Info</string>
+ <string name="refmic_test_question">Does this device allow for the connection of a USB reference microphone?</string>
+ <string name="ref_mic_dlg_caption">Reference Mic Required</string>
+ <string name="ref_mic_dlg_text">This test requires a USB Reference Mic to be connected to the device.
+ If the device under test does not support USB Host Mode Audio (either because it does not have a
+ USB port, or USB Host Mode Audio has been removed from the OS) you can be granted a provisional
+ pass on this test by pressing the \"No\" button and indicating \"Test Pass\" at the bottom.\n
+ Note: Handheld devices supporting USB host mode MUST support USB audio class (CDD 7.7 .2/H-1-1)\n
+ Note: Devices declaring feature android.hardware.audio.pro MUST implement USB host mode (CDD 5.10 C-1-3) and if they omit a 4 conductor 3.5mm audio jack MUST support USB audio class (CDD 5.10 C-3-1)
+ </string>
</resources>
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyActivity.java
index 3b97e37..ffc5f41 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyActivity.java
@@ -53,6 +53,65 @@
public int mMaxLevel = 0;
+ private OnBtnClickListener mBtnClickListener = new OnBtnClickListener();
+ //
+ // Common UI Handling
+ protected void connectRefMicUI() {
+ findViewById(R.id.refmic_tests_yes_btn).setOnClickListener(mBtnClickListener);
+ findViewById(R.id.refmic_tests_no_btn).setOnClickListener(mBtnClickListener);
+ findViewById(R.id.refmic_test_info_btn).setOnClickListener(mBtnClickListener);
+
+ enableTestUI(false);
+ }
+
+ private void showRefMicInfoDialog() {
+ new AlertDialog.Builder(this)
+ .setTitle(R.string.ref_mic_dlg_caption)
+ .setMessage(R.string.ref_mic_dlg_text)
+ .setPositiveButton(R.string.audio_general_ok, null)
+ .show();
+ }
+
+ private class OnBtnClickListener implements OnClickListener {
+ @Override
+ public void onClick(View v) {
+ switch (v.getId()) {
+ case R.id.refmic_tests_yes_btn:
+ recordRefMicStatus(true);
+ enableTestUI(true);
+ // disable test button so that they will now run the test(s)
+ getPassButton().setEnabled(false);
+ break;
+
+ case R.id.refmic_tests_no_btn:
+ recordRefMicStatus(false);
+ enableTestUI(false);
+ // Allow the user to "pass" the test.
+ getPassButton().setEnabled(true);
+ break;
+
+ case R.id.refmic_test_info_btn:
+ showRefMicInfoDialog();
+ break;
+ }
+ }
+ }
+
+ private void recordRefMicStatus(boolean has) {
+ getReportLog().addValue(
+ "User reported ref mic availability: ",
+ has ? 1.0 : 0,
+ ResultType.NEUTRAL,
+ ResultUnit.NONE);
+ }
+
+ //
+ // Overrides
+ //
+ void enableTestUI(boolean enable) {
+
+ }
+
public void setMaxLevel() {
AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
mMaxLevel = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyMicActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyMicActivity.java
index 129fb72..e296f86 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyMicActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyMicActivity.java
@@ -238,7 +238,22 @@
5.0, -50.0, /* start top,bottom value */
5.0, -50.0 /* stop top,bottom value */);
+ connectRefMicUI();
}
+
+ //
+ // Overrides
+ //
+ void enableTestUI(boolean enable) {
+ mButtonTestNoise.setEnabled(enable);
+ mButtonPlayNoise.setEnabled(enable);
+
+ mButtonTestUsbBackground.setEnabled(enable);
+
+ mButtonTestUsbNoise.setEnabled(enable);
+ mButtonPlayUsbNoise.setEnabled(enable);
+ }
+
private void playerToggleButton(int buttonId) {
if (playerIsPlaying()) {
playerStopAll();
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencySpeakerActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencySpeakerActivity.java
index 294e48d..f954b0c 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencySpeakerActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencySpeakerActivity.java
@@ -205,6 +205,15 @@
5.0, -50.0, /* start top,bottom value */
5.0, -50.0 /* stop top,bottom value */);
+ connectRefMicUI();
+ }
+
+ //
+ // Overrides
+ //
+ void enableTestUI(boolean enable) {
+ mLoopbackPlugReady.setEnabled(enable);
+ mTestButton.setEnabled(enable);
}
/**
@@ -212,8 +221,8 @@
*/
private void enableLayout(boolean enable) {
for (int i = 0; i < mLinearLayout.getChildCount(); i++) {
- View view = mLinearLayout.getChildAt(i);
- view.setEnabled(enable);
+ mLoopbackPlugReady.setEnabled(enable);
+ mTestButton.setEnabled(enable);
}
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyUnprocessedActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyUnprocessedActivity.java
index 009dd58..fb8460b 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyUnprocessedActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyUnprocessedActivity.java
@@ -292,6 +292,24 @@
mResultsMic = new Results("mic_response", mBands);
mResultsTone = new Results("tone_response", mBandsTone);
mResultsBack = new Results("background_response", mBandsBack);
+
+ connectRefMicUI();
+ }
+
+ //
+ // Overrides
+ //
+ void enableTestUI(boolean enable) {
+ mButtonTestTone.setEnabled(enable);
+ mButtonPlayTone.setEnabled(enable);
+
+ mButtonTestNoise.setEnabled(enable);
+ mButtonPlayNoise.setEnabled(enable);
+
+ mButtonTestUsbBackground.setEnabled(enable);
+
+ mButtonTestUsbNoise.setEnabled(enable);
+ mButtonPlayUsbNoise.setEnabled(enable);
}
private void playerToggleButton(int buttonId, int sourceId) {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyVoiceRecognitionActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyVoiceRecognitionActivity.java
index 4017973..efe666a 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyVoiceRecognitionActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyVoiceRecognitionActivity.java
@@ -329,6 +329,23 @@
mResultsMic = new Results("mic_response", BANDS_MIC);
mResultsTone = new Results("tone_response", BANDS_TONE);
mResultsBack = new Results("background_response", BANDS_BACKGROUND);
+ connectRefMicUI();
+ }
+
+ //
+ // Overrides
+ //
+ void enableTestUI(boolean enable) {
+ mButtonTestTone.setEnabled(enable);
+ mButtonPlayTone.setEnabled(enable);
+
+ mButtonTestNoise.setEnabled(enable);
+ mButtonPlayNoise.setEnabled(enable);
+
+ mButtonTestUsbBackground.setEnabled(enable);
+
+ mButtonTestUsbNoise.setEnabled(enable);
+ mButtonPlayUsbNoise.setEnabled(enable);
}
private void playerToggleButton(int buttonId, int sourceId) {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/RingerModeActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/RingerModeActivity.java
index fdb57fc..ebd2e6f 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/RingerModeActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/RingerModeActivity.java
@@ -108,10 +108,6 @@
@Override
protected List<InteractiveTestCase> createTestItems() {
List<InteractiveTestCase> tests = new ArrayList<>();
- if (supportsConditionProviders()) {
- tests.add(new PassTest());
- return tests;
- }
tests.add(new SetModeAllTest());
tests.add(new SetModePriorityTest());
tests.add(new TestAccessRingerModeDndOn());
@@ -136,12 +132,6 @@
return tests;
}
- private boolean supportsConditionProviders() {
- ActivityManager am = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
- return !am.isLowRamDevice()
- || mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH);
- }
-
private int getVolumeDelta(int volume) {
return 1;
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralActivity.java
index 140757d..8f0a9b0 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralActivity.java
@@ -16,12 +16,18 @@
package com.android.cts.verifier.audio;
+import android.app.AlertDialog;
+import com.android.compatibility.common.util.ReportLog;
+import com.android.compatibility.common.util.ResultType;
+import com.android.compatibility.common.util.ResultUnit;
import android.media.AudioDeviceCallback;
import android.media.AudioDeviceInfo;
import android.media.AudioManager;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
+import android.view.View;
+import android.view.View.OnClickListener;
import android.widget.TextView;
import com.android.cts.verifier.audio.peripheralprofile.PeripheralProfile;
@@ -55,6 +61,68 @@
private TextView mPeripheralNameTx;
+ private OnBtnClickListener mBtnClickListener = new OnBtnClickListener();
+
+ //
+ // Common UI Handling
+ //
+ protected void connectUSBPeripheralUI() {
+ findViewById(R.id.uap_tests_yes_btn).setOnClickListener(mBtnClickListener);
+ findViewById(R.id.uap_tests_no_btn).setOnClickListener(mBtnClickListener);
+ findViewById(R.id.uap_test_info_btn).setOnClickListener(mBtnClickListener);
+
+ // Leave the default state in tact
+ // enableTestUI(false);
+ }
+
+ private void showUAPInfoDialog() {
+ new AlertDialog.Builder(this)
+ .setTitle(R.string.uap_mic_dlg_caption)
+ .setMessage(R.string.uap_mic_dlg_text)
+ .setPositiveButton(R.string.audio_general_ok, null)
+ .show();
+ }
+
+ private class OnBtnClickListener implements OnClickListener {
+ @Override
+ public void onClick(View v) {
+ switch (v.getId()) {
+ case R.id.uap_tests_yes_btn:
+ recordUSBAudioStatus(true);
+ enableTestUI(true);
+ // disable test button so that they will now run the test(s)
+ getPassButton().setEnabled(false);
+ break;
+
+ case R.id.uap_tests_no_btn:
+ recordUSBAudioStatus(false);
+ enableTestUI(false);
+ // Allow the user to "pass" the test.
+ getPassButton().setEnabled(true);
+ break;
+
+ case R.id.uap_test_info_btn:
+ showUAPInfoDialog();
+ break;
+ }
+ }
+ }
+
+ private void recordUSBAudioStatus(boolean has) {
+ getReportLog().addValue(
+ "User reported USB Host Audio Support: ",
+ has ? 1.0 : 0,
+ ResultType.NEUTRAL,
+ ResultUnit.NONE);
+ }
+
+ //
+ // Overrides
+ //
+ void enableTestUI(boolean enable) {
+
+ }
+
public USBAudioPeripheralActivity(boolean mandatedRequired) {
super();
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralAttributesActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralAttributesActivity.java
index 5029160..23ed91a 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralAttributesActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralAttributesActivity.java
@@ -47,6 +47,8 @@
setPassFailButtonClickListeners();
setInfoResources(R.string.usbaudio_attribs_test, R.string.usbaudio_attribs_info, -1);
+
+ connectUSBPeripheralUI();
}
//
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralButtonsActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralButtonsActivity.java
index de3ce7f..823f326 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralButtonsActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralButtonsActivity.java
@@ -83,6 +83,8 @@
setInfoResources(R.string.usbaudio_buttons_test, R.string.usbaudio_buttons_info, -1);
showDisableAssistantDialog();
+
+ connectUSBPeripheralUI();
}
private void showButtonsState() {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralPlayActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralPlayActivity.java
index de9016a..4ae5ec3 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralPlayActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralPlayActivity.java
@@ -50,11 +50,17 @@
setPassFailButtonClickListeners();
setInfoResources(R.string.usbaudio_play_test, R.string.usbaudio_play_info, -1);
+
+ connectUSBPeripheralUI();
}
//
// USBAudioPeripheralActivity
// Headset not publicly available, violates CTS Verifier additional equipment guidelines.
+ void enableTestUI(boolean enable) {
+ mPlayBtn.setEnabled(enable);
+ }
+
public void updateConnectStatus() {
mPlayBtn.setEnabled(mIsPeripheralAttached);
getPassButton().setEnabled(mIsPeripheralAttached);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralRecordActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralRecordActivity.java
index 268201c..d51eac3 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralRecordActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralRecordActivity.java
@@ -139,11 +139,18 @@
setPassFailButtonClickListeners();
setInfoResources(R.string.usbaudio_record_test, R.string.usbaudio_record_info, -1);
+
+ connectUSBPeripheralUI();
}
//
// USBAudioPeripheralActivity
//
+ void enableTestUI(boolean enable) {
+ mRecordBtn.setEnabled(enable);
+ mRecordLoopbackBtn.setEnabled(enable);
+ }
+
public void updateConnectStatus() {
mRecordBtn.setEnabled(mIsPeripheralAttached);
mRecordLoopbackBtn.setEnabled(mIsPeripheralAttached);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/AttentionManagementVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/AttentionManagementVerifierActivity.java
index 8764dbe..8391816 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/AttentionManagementVerifierActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/AttentionManagementVerifierActivity.java
@@ -20,12 +20,10 @@
import static com.android.cts.verifier.notifications.MockListener.JSON_MATCHES_ZEN_FILTER;
import static com.android.cts.verifier.notifications.MockListener.JSON_TAG;
-import android.app.ActivityManager;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.ContentProviderOperation;
-import android.content.Context;
import android.content.OperationApplicationException;
import android.database.Cursor;
import android.media.AudioAttributes;
@@ -94,37 +92,30 @@
@Override
protected List<InteractiveTestCase> createTestItems() {
-
List<InteractiveTestCase> tests = new ArrayList<>(17);
- ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
- if (am.isLowRamDevice()) {
- tests.add(new CannotBeEnabledTest());
- tests.add(new ServiceStoppedTest());
- } else {
- tests.add(new IsEnabledTest());
- tests.add(new ServiceStartedTest());
- tests.add(new InsertContactsTest());
- tests.add(new NoneInterceptsAllMessagesTest());
- tests.add(new NoneInterceptsAlarmEventReminderCategoriesTest());
- tests.add(new PriorityInterceptsSomeMessagesTest());
+ tests.add(new IsEnabledTest());
+ tests.add(new ServiceStartedTest());
+ tests.add(new InsertContactsTest());
+ tests.add(new NoneInterceptsAllMessagesTest());
+ tests.add(new NoneInterceptsAlarmEventReminderCategoriesTest());
+ tests.add(new PriorityInterceptsSomeMessagesTest());
- if (getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.O_MR1) {
- // Tests targeting P and above:
- tests.add(new PriorityInterceptsAlarmsTest());
- tests.add(new PriorityInterceptsMediaSystemOtherTest());
- }
-
- tests.add(new AllInterceptsNothingMessagesTest());
- tests.add(new AllInterceptsNothingDiffCategoriesTest());
- tests.add(new DefaultOrderTest());
- tests.add(new PriorityOrderTest());
- tests.add(new InterruptionOrderTest());
- tests.add(new AmbientBitsTest());
- tests.add(new LookupUriOrderTest());
- tests.add(new EmailOrderTest());
- tests.add(new PhoneOrderTest());
- tests.add(new DeleteContactsTest());
+ if (getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.O_MR1) {
+ // Tests targeting P and above:
+ tests.add(new PriorityInterceptsAlarmsTest());
+ tests.add(new PriorityInterceptsMediaSystemOtherTest());
}
+
+ tests.add(new AllInterceptsNothingMessagesTest());
+ tests.add(new AllInterceptsNothingDiffCategoriesTest());
+ tests.add(new DefaultOrderTest());
+ tests.add(new PriorityOrderTest());
+ tests.add(new InterruptionOrderTest());
+ tests.add(new AmbientBitsTest());
+ tests.add(new LookupUriOrderTest());
+ tests.add(new EmailOrderTest());
+ tests.add(new PhoneOrderTest());
+ tests.add(new DeleteContactsTest());
return tests;
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/ConditionProviderVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/ConditionProviderVerifierActivity.java
index bad4639..2260f6d 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/ConditionProviderVerifierActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/ConditionProviderVerifierActivity.java
@@ -21,9 +21,7 @@
import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_REMOVED;
import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_UNKNOWN;
import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
-import static android.provider.Settings.EXTRA_APP_PACKAGE;
-import android.app.ActivityManager;
import android.app.AutomaticZenRule;
import android.app.NotificationManager;
import android.content.ComponentName;
@@ -71,32 +69,26 @@
@Override
protected List<InteractiveTestCase> createTestItems() {
List<InteractiveTestCase> tests = new ArrayList<>(9);
- ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
- if (am.isLowRamDevice()) {
- tests.add(new CannotBeEnabledTest());
- tests.add(new ServiceStoppedTest());
- } else {
- tests.add(new IsEnabledTest());
- tests.add(new ServiceStartedTest());
- tests.add(new CreateAutomaticZenRuleTest());
- tests.add(new CreateAutomaticZenRuleWithZenPolicyTest());
- tests.add(new UpdateAutomaticZenRuleTest());
- tests.add(new UpdateAutomaticZenRuleWithZenPolicyTest());
- tests.add(new GetAutomaticZenRuleTest());
- tests.add(new GetAutomaticZenRulesTest());
- tests.add(new VerifyRulesIntent());
- tests.add(new VerifyRulesAvailableToUsers());
- tests.add(new ReceiveRuleDisableNoticeTest());
- tests.add(new ReceiveRuleEnabledNoticeTest());
- tests.add(new ReceiveRuleDeletedNoticeTest());
- tests.add(new SubscribeAutomaticZenRuleTest());
- tests.add(new DeleteAutomaticZenRuleTest());
- tests.add(new UnsubscribeAutomaticZenRuleTest());
- tests.add(new RequestUnbindTest());
- tests.add(new RequestBindTest());
- tests.add(new IsDisabledTest());
- tests.add(new ServiceStoppedTest());
- }
+ tests.add(new IsEnabledTest());
+ tests.add(new ServiceStartedTest());
+ tests.add(new CreateAutomaticZenRuleTest());
+ tests.add(new CreateAutomaticZenRuleWithZenPolicyTest());
+ tests.add(new UpdateAutomaticZenRuleTest());
+ tests.add(new UpdateAutomaticZenRuleWithZenPolicyTest());
+ tests.add(new GetAutomaticZenRuleTest());
+ tests.add(new GetAutomaticZenRulesTest());
+ tests.add(new VerifyRulesIntent());
+ tests.add(new VerifyRulesAvailableToUsers());
+ tests.add(new ReceiveRuleDisableNoticeTest());
+ tests.add(new ReceiveRuleEnabledNoticeTest());
+ tests.add(new ReceiveRuleDeletedNoticeTest());
+ tests.add(new SubscribeAutomaticZenRuleTest());
+ tests.add(new DeleteAutomaticZenRuleTest());
+ tests.add(new UnsubscribeAutomaticZenRuleTest());
+ tests.add(new RequestUnbindTest());
+ tests.add(new RequestBindTest());
+ tests.add(new IsDisabledTest());
+ tests.add(new ServiceStoppedTest());
return tests;
}
@@ -138,45 +130,6 @@
}
}
- protected class CannotBeEnabledTest extends InteractiveTestCase {
- @Override
- protected View inflate(ViewGroup parent) {
- return createNlsSettingsItem(parent, R.string.cp_cannot_enable_service);
- }
-
- @Override
- boolean autoStart() {
- return true;
- }
-
- @Override
- protected void test() {
- mNm.cancelAll();
- Intent settings = new Intent(Settings.ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS);
- if (settings.resolveActivity(mPackageManager) == null) {
- logFail("no settings activity");
- status = FAIL;
- } else {
- if (mNm.isNotificationPolicyAccessGranted()) {
- status = FAIL;
- } else {
- status = PASS;
- }
- next();
- }
- }
-
- protected void tearDown() {
- // wait for the service to start
- delay();
- }
-
- @Override
- protected Intent getIntent() {
- return new Intent(Settings.ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS);
- }
- }
-
protected class ServiceStartedTest extends InteractiveTestCase {
int mRetries = 5;
@Override
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/InteractiveVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/InteractiveVerifierActivity.java
index 296df5d..aa5e9c0 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/InteractiveVerifierActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/InteractiveVerifierActivity.java
@@ -36,6 +36,7 @@
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
+
import com.android.cts.verifier.PassFailButtons;
import com.android.cts.verifier.R;
@@ -524,47 +525,6 @@
}
}
- protected class CannotBeEnabledTest extends InteractiveTestCase {
- @Override
- protected View inflate(ViewGroup parent) {
- return createNlsSettingsItem(parent, R.string.nls_cannot_enable_service);
- }
-
- @Override
- boolean autoStart() {
- return true;
- }
-
- @Override
- protected void test() {
- mNm.cancelAll();
- Intent settings = new Intent(ACTION_NOTIFICATION_LISTENER_SETTINGS);
- if (settings.resolveActivity(mPackageManager) == null) {
- logFail("no settings activity");
- status = FAIL;
- } else {
- String listeners = Secure.getString(getContentResolver(),
- ENABLED_NOTIFICATION_LISTENERS);
- if (listeners != null && listeners.contains(LISTENER_PATH)) {
- status = FAIL;
- } else {
- status = PASS;
- }
- next();
- }
- }
-
- protected void tearDown() {
- // wait for the service to start
- delay();
- }
-
- @Override
- protected Intent getIntent() {
- return new Intent(ACTION_NOTIFICATION_LISTENER_SETTINGS);
- }
- }
-
protected class ServiceStartedTest extends InteractiveTestCase {
@Override
protected View inflate(ViewGroup parent) {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/NotificationListenerVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/NotificationListenerVerifierActivity.java
index 7297dbe..348c7a3 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/NotificationListenerVerifierActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/NotificationListenerVerifierActivity.java
@@ -35,7 +35,6 @@
import static com.android.cts.verifier.notifications.MockListener.REASON_LISTENER_CANCEL;
import android.annotation.SuppressLint;
-import android.app.ActivityManager;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
@@ -108,39 +107,32 @@
@Override
protected List<InteractiveTestCase> createTestItems() {
List<InteractiveTestCase> tests = new ArrayList<>(17);
- ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
- if (am.isLowRamDevice()) {
- tests.add(new CannotBeEnabledTest());
- tests.add(new ServiceStoppedTest());
- tests.add(new NotificationNotReceivedTest());
- } else {
- tests.add(new IsEnabledTest());
- tests.add(new ServiceStartedTest());
- tests.add(new NotificationReceivedTest());
- tests.add(new DataIntactTest());
- tests.add(new AudiblyAlertedTest());
- tests.add(new DismissOneTest());
- tests.add(new DismissOneWithReasonTest());
- tests.add(new DismissOneWithStatsTest());
- tests.add(new DismissAllTest());
- tests.add(new SnoozeNotificationForTimeTest());
- tests.add(new SnoozeNotificationForTimeCancelTest());
- tests.add(new GetSnoozedNotificationTest());
- tests.add(new EnableHintsTest());
- tests.add(new ReceiveAppBlockNoticeTest());
- tests.add(new ReceiveAppUnblockNoticeTest());
- if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
- tests.add(new ReceiveChannelBlockNoticeTest());
- tests.add(new ReceiveGroupBlockNoticeTest());
- }
- tests.add(new RequestUnbindTest());
- tests.add(new RequestBindTest());
- tests.add(new MessageBundleTest());
- tests.add(new EnableHintsTest());
- tests.add(new IsDisabledTest());
- tests.add(new ServiceStoppedTest());
- tests.add(new NotificationNotReceivedTest());
+ tests.add(new IsEnabledTest());
+ tests.add(new ServiceStartedTest());
+ tests.add(new NotificationReceivedTest());
+ tests.add(new DataIntactTest());
+ tests.add(new AudiblyAlertedTest());
+ tests.add(new DismissOneTest());
+ tests.add(new DismissOneWithReasonTest());
+ tests.add(new DismissOneWithStatsTest());
+ tests.add(new DismissAllTest());
+ tests.add(new SnoozeNotificationForTimeTest());
+ tests.add(new SnoozeNotificationForTimeCancelTest());
+ tests.add(new GetSnoozedNotificationTest());
+ tests.add(new EnableHintsTest());
+ tests.add(new ReceiveAppBlockNoticeTest());
+ tests.add(new ReceiveAppUnblockNoticeTest());
+ if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
+ tests.add(new ReceiveChannelBlockNoticeTest());
+ tests.add(new ReceiveGroupBlockNoticeTest());
}
+ tests.add(new RequestUnbindTest());
+ tests.add(new RequestBindTest());
+ tests.add(new MessageBundleTest());
+ tests.add(new EnableHintsTest());
+ tests.add(new IsDisabledTest());
+ tests.add(new ServiceStoppedTest());
+ tests.add(new NotificationNotReceivedTest());
return tests;
}
diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/RetryRule.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/RetryRule.java
index ca0e3e2..a24239c 100644
--- a/common/device-side/util-axt/src/com/android/compatibility/common/util/RetryRule.java
+++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/RetryRule.java
@@ -31,16 +31,8 @@
private static final String TAG = "RetryRule";
- /**
- * An interface is used to clean up testing objects between the retries to make sure
- * the testing environment is clean.
- */
- public interface RetryCleaner {
- void clean();
- }
-
private final int mMaxAttempts;
- private final RetryCleaner mCleaner;
+ private final Runnable mCleaner;
/**
* Retries the underlying test when it catches a {@link RetryableException}.
@@ -57,12 +49,12 @@
* Retries the underlying test when it catches a {@link RetryableException}.
*
* @param retries number of retries. Use {@code 0} to disable rule.
- * @param cleaner a {@link RetryCleaner} to clean up the objects generated by the testing
+ * @param cleaner a {@link Runnable} to clean up the objects generated by the testing
* between retries
*
* @throws IllegalArgumentException if {@code retries} is less than {@code 0}.
*/
- public RetryRule(int retries, @Nullable RetryCleaner cleaner) {
+ public RetryRule(int retries, @Nullable Runnable cleaner) {
if (retries < 0) {
throw new IllegalArgumentException("retries must be more than 0");
}
@@ -105,7 +97,7 @@
}
caught = e;
if (i != mMaxAttempts && mCleaner != null) {
- mCleaner.clean();
+ mCleaner.run();
}
}
Log.w(TAG, "Arrrr! " + name + " failed at attempt " + i + "/" + mMaxAttempts
diff --git a/common/device-side/util-axt/tests/src/com/android/compatibility/common/util/RetryRuleTest.java b/common/device-side/util-axt/tests/src/com/android/compatibility/common/util/RetryRuleTest.java
index 644d95f..571a1b8 100644
--- a/common/device-side/util-axt/tests/src/com/android/compatibility/common/util/RetryRuleTest.java
+++ b/common/device-side/util-axt/tests/src/com/android/compatibility/common/util/RetryRuleTest.java
@@ -19,11 +19,15 @@
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.testng.Assert.assertThrows;
import static org.testng.Assert.expectThrows;
+import androidx.annotation.Nullable;
+
import org.junit.Test;
import org.junit.runner.Description;
import org.junit.runner.RunWith;
@@ -43,15 +47,22 @@
private final int mNumberFailures;
private int mNumberCalls;
private final T mException;
+ private final Runnable mCleaner;
RetryableStatement(int numberFailures, T exception) {
+ this(numberFailures, exception, null);
+ }
+
+ RetryableStatement(int numberFailures, T exception, @Nullable Runnable cleaner) {
mNumberFailures = numberFailures;
mException = exception;
+ mCleaner = cleaner;
}
@Override
public void evaluate() throws Throwable {
mNumberCalls++;
+ verifyCleaner();
if (mNumberCalls <= mNumberFailures) {
throw mException;
}
@@ -61,6 +72,13 @@
public String toString() {
return "RetryableStatement: failures=" + mNumberFailures + ", calls=" + mNumberCalls;
}
+
+ private void verifyCleaner() {
+ if (mCleaner == null) {
+ return;
+ }
+ verify(mCleaner, times(mNumberCalls - 1)).run();
+ }
}
private @Mock Statement mMockStatement;
@@ -68,6 +86,8 @@
@Test
public void testInvalidConstructor() throws Throwable {
assertThrows(IllegalArgumentException.class, () -> new RetryRule(-1));
+ assertThrows(IllegalArgumentException.class, () -> new RetryRule(-1, null));
+ assertThrows(IllegalArgumentException.class, () -> new RetryRule(-1, () -> {}));
}
@Test
@@ -78,6 +98,57 @@
}
@Test
+ public void testDoCleanOnRetryableException() throws Throwable {
+ final Runnable cleaner = mock(Runnable.class);
+ final RetryRule rule = new RetryRule(2, cleaner);
+
+ rule.apply(new RetryableStatement<RetryableException>(2, sRetryableException, cleaner),
+ mDescription).evaluate();
+
+ verify(cleaner, times(2)).run();
+ }
+
+ @Test
+ public void testKeepLastStatusWhenFailOnRetryableException() throws Throwable {
+ final Runnable cleaner = mock(Runnable.class);
+ final RetryRule rule = new RetryRule(2, cleaner);
+
+ final RetryableException actualException = expectThrows(RetryableException.class,
+ () -> rule.apply(
+ new RetryableStatement<RetryableException>(3, sRetryableException, cleaner),
+ mDescription).evaluate());
+
+ assertThat(actualException).isSameAs(sRetryableException);
+ verify(cleaner, times(2)).run();
+ }
+
+ @Test
+ public void testNeverCleanWhenStatementPass() throws Throwable {
+ final Runnable cleaner = mock(Runnable.class);
+ final RetryRule rule = new RetryRule(2, cleaner);
+
+ rule.apply(mMockStatement, mDescription).evaluate();
+
+ verify(mMockStatement, times(1)).evaluate();
+ verify(cleaner, never()).run();
+ }
+
+ @Test
+ public void testNeverCleanWhenDisabledAndStatementThrowsRetryableException() throws Throwable {
+ final RetryableException exception = new RetryableException("Y U NO?");
+ final Runnable cleaner = mock(Runnable.class);
+ final RetryRule rule = new RetryRule(0, cleaner);
+ doThrow(exception).when(mMockStatement).evaluate();
+
+ final RetryableException actualException = expectThrows(RetryableException.class,
+ () -> rule.apply(mMockStatement, mDescription).evaluate());
+
+ assertThat(actualException).isSameAs(exception);
+ verify(mMockStatement, times(1)).evaluate();
+ verify(cleaner, never()).run();
+ }
+
+ @Test
public void testPassOnRetryableExceptionWithTimeout() throws Throwable {
final Timeout timeout = new Timeout("YOUR TIME IS GONE", 1, 2, 10);
final RetryableException exception = new RetryableException(timeout, "Y U NO?");
diff --git a/hostsidetests/appsecurity/test-apps/EncryptionApp/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/EncryptionApp/AndroidManifest.xml
index 1231874..a6f7b1d 100644
--- a/hostsidetests/appsecurity/test-apps/EncryptionApp/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/EncryptionApp/AndroidManifest.xml
@@ -16,7 +16,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.cts.encryptionapp">
- <application android:label="EncryptionApp">
+ <application android:label="EncryptionApp" android:forceQueryable="true">
<activity android:name=".UnawareActivity"
android:exported="true">
<intent-filter>
diff --git a/hostsidetests/appsecurity/test-apps/EncryptionApp/src/com/android/cts/encryptionapp/EncryptionAppTest.java b/hostsidetests/appsecurity/test-apps/EncryptionApp/src/com/android/cts/encryptionapp/EncryptionAppTest.java
index 7d22318..88601e5 100644
--- a/hostsidetests/appsecurity/test-apps/EncryptionApp/src/com/android/cts/encryptionapp/EncryptionAppTest.java
+++ b/hostsidetests/appsecurity/test-apps/EncryptionApp/src/com/android/cts/encryptionapp/EncryptionAppTest.java
@@ -44,7 +44,6 @@
import android.provider.Settings;
import android.support.test.uiautomator.UiDevice;
import android.test.InstrumentationTestCase;
-import android.text.format.DateUtils;
import android.util.Log;
import android.view.KeyEvent;
@@ -419,8 +418,19 @@
private boolean queryFileExists(Uri fileUri) {
Cursor c = mDe.getContentResolver().query(fileUri, null, null, null, null);
+ if (c == null) {
+ Log.w(TAG, "Couldn't query for file " + fileUri + "; returning false");
+ return false;
+ }
+
c.moveToFirst();
+
int colIndex = c.getColumnIndex("exists");
+ if (colIndex < 0) {
+ Log.e(TAG, "Column 'exists' does not exist; returning false");
+ return false;
+ }
+
return c.getInt(colIndex) == 1;
}
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/SplitApp/AndroidManifest.xml
index 71d13ae..04ed8f4 100644
--- a/hostsidetests/appsecurity/test-apps/SplitApp/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/AndroidManifest.xml
@@ -23,7 +23,7 @@
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
- <application android:label="SplitApp" android:multiArch="true">
+ <application android:label="SplitApp" android:multiArch="true" android:forceQueryable="true">
<activity android:name=".MyActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
diff --git a/hostsidetests/blobstore/test-apps/BlobStoreHelperApp/src/com/android/cts/blob/helper/DataPersistenceTest.java b/hostsidetests/blobstore/test-apps/BlobStoreHelperApp/src/com/android/cts/blob/helper/DataPersistenceTest.java
index a280997..5cfd669 100644
--- a/hostsidetests/blobstore/test-apps/BlobStoreHelperApp/src/com/android/cts/blob/helper/DataPersistenceTest.java
+++ b/hostsidetests/blobstore/test-apps/BlobStoreHelperApp/src/com/android/cts/blob/helper/DataPersistenceTest.java
@@ -149,7 +149,7 @@
}
private SharedPreferences getSharedPreferences() {
- return mContext.getSharedPreferences(mContext.getPackageName(),
- Context.MODE_PRIVATE);
+ return mContext.createDeviceProtectedStorageContext().getSharedPreferences(
+ mContext.getPackageName(), Context.MODE_PRIVATE);
}
}
diff --git a/hostsidetests/bootstats/Android.bp b/hostsidetests/bootstats/Android.bp
index ca1e292..07f9c84 100644
--- a/hostsidetests/bootstats/Android.bp
+++ b/hostsidetests/bootstats/Android.bp
@@ -17,12 +17,12 @@
defaults: ["cts_defaults"],
// Only compile source java files in this apk.
srcs: ["src/**/*.java"],
- static_libs: ["framework-protos"],
libs: [
"cts-tradefed",
"tradefed",
"compatibility-host-util",
"libprotobuf-java-full",
+ "platformprotos",
],
// tag this module as a cts test artifact
test_suites: [
diff --git a/hostsidetests/bootstats/src/android/bootstats/cts/BootStatsHostTest.java b/hostsidetests/bootstats/src/android/bootstats/cts/BootStatsHostTest.java
index 9ec3dad..e5eb36c 100644
--- a/hostsidetests/bootstats/src/android/bootstats/cts/BootStatsHostTest.java
+++ b/hostsidetests/bootstats/src/android/bootstats/cts/BootStatsHostTest.java
@@ -16,7 +16,7 @@
package android.bootstats.cts;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.os.AtomsProto.Atom;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.log.LogUtil.CLog;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
@@ -56,37 +56,53 @@
Thread.sleep(10000);
// find logs and parse them
- // ex: sysui_multi_action: [757,804,799,ota_boot_complete,801,85,802,1]
- // ex: 757,804,799,counter_name,801,bucket_value,802,increment_value
- final String bucketTag = Integer.toString(MetricsEvent.RESERVED_FOR_LOGBUILDER_BUCKET);
- final String counterNameTag = Integer.toString(MetricsEvent.RESERVED_FOR_LOGBUILDER_NAME);
- final String counterNamePattern = counterNameTag + ",boot_complete,";
- final String multiActionPattern = "sysui_multi_action: [";
+ // ex: Atom 239->10
+ // ex: Atom 240->9
+ final String bootTimeEventDurationReported =
+ Integer.toString(Atom.BOOT_TIME_EVENT_DURATION_REPORTED_FIELD_NUMBER);
+ final String bootTimeEventDurationReportedPattern = "Atom "
+ + bootTimeEventDurationReported + "->";
+ final String bootTimeEventElapsedTimeReported =
+ Integer.toString(Atom.BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED_FIELD_NUMBER);
+ final String bootTimeEventElapsedTimeReportedPattern = "Atom "
+ + bootTimeEventElapsedTimeReported + "->";
- final String log = getDevice().executeShellCommand("logcat --buffer=events -d");
+ final String log = getDevice().executeShellCommand("cmd stats print-stats");
- int counterNameIndex = log.indexOf(counterNamePattern);
- Assert.assertTrue("did not find boot logs", counterNameIndex != -1);
+ int bootTimeEventDurationReportedIndex =
+ log.indexOf(bootTimeEventDurationReportedPattern);
+ Assert.assertTrue("did not find boot duration logs",
+ bootTimeEventDurationReportedIndex != -1);
+ // extract the number after ->, e.g., 10 inside 239->10
+ int valueIndex = bootTimeEventDurationReportedIndex +
+ bootTimeEventDurationReportedPattern.length();
+ int value = getIntValue(log, valueIndex);
+ Assert.assertTrue("boot duration time smaller than 1", value > 1);
- int multiLogStart = log.lastIndexOf(multiActionPattern, counterNameIndex);
- multiLogStart += multiActionPattern.length();
- int multiLogEnd = log.indexOf("]", multiLogStart);
- String[] multiLogDataStrings = log.substring(multiLogStart, multiLogEnd).split(",");
+ int bootTimeEventElapsedTimeReportedIndex =
+ log.indexOf(bootTimeEventElapsedTimeReportedPattern);
+ Assert.assertTrue("did not find boot elapsed time logs",
+ bootTimeEventElapsedTimeReportedIndex != -1);
- boolean foundBucket = false;
- int bootTime = 0;
- for (int i = 0; i < multiLogDataStrings.length; i += 2) {
- if (bucketTag.equals(multiLogDataStrings[i])) {
- foundBucket = true;
- Assert.assertTrue("histogram data was truncated",
- (i + 1) < multiLogDataStrings.length);
- bootTime = Integer.valueOf(multiLogDataStrings[i + 1]);
+ // extract the number after ->, e.g., 9 inside Atom 240->9
+ valueIndex = bootTimeEventElapsedTimeReportedIndex +
+ bootTimeEventElapsedTimeReportedPattern.length();
+ value = getIntValue(log, valueIndex);
+ Assert.assertTrue("boot elapsed time smaller than 1", value > 1);
+ }
+
+ // extract the value from the string starting from index till EOL
+ private int getIntValue(String str, int index) throws Exception {
+ int lastIndex = index;
+ for (int i = index; i < str.length(); i++) {
+ if (str.charAt(i) == '\n') {
+ lastIndex = i;
+ break;
}
}
- Assert.assertTrue("log line did not contain a tag " + bucketTag, foundBucket);
- Assert.assertTrue("reported boot time must be less than observed boot time",
- bootTime < upperBoundSeconds);
- Assert.assertTrue("reported boot time must be non-zero", bootTime > 0);
+ String valueStr = str.substring(index, lastIndex);
+ int value = Integer.valueOf(valueStr);
+ return value;
}
private boolean isBootCompleted() throws Exception {
diff --git a/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/AndroidManifest.xml b/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/AndroidManifest.xml
index 02b480a..5564c3a 100644
--- a/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/AndroidManifest.xml
+++ b/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/AndroidManifest.xml
@@ -42,6 +42,8 @@
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
+
+ <activity android:name=".CrossProfileSameTaskLauncherActivity" android:exported="true"/>
</application>
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
diff --git a/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/res/layout/cross_profile_same_task_launcher.xml b/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/res/layout/cross_profile_same_task_launcher.xml
new file mode 100644
index 0000000..8d9cf94
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/res/layout/cross_profile_same_task_launcher.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="CrossProfileSameTaskLauncherActivity"
+ />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/CrossProfileAppsStartActivityTest.java b/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/CrossProfileAppsStartActivityTest.java
index 87878a1..be5c4b1 100644
--- a/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/CrossProfileAppsStartActivityTest.java
+++ b/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/CrossProfileAppsStartActivityTest.java
@@ -24,6 +24,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
+import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -36,7 +37,6 @@
import android.support.test.uiautomator.UiDevice;
import android.support.test.uiautomator.UiObject2;
import android.support.test.uiautomator.Until;
-import android.util.Log;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
@@ -104,7 +104,7 @@
intent.setComponent(MainActivity.getComponentName(mContext));
ShellIdentityUtils.dropShellPermissionIdentity();
- mCrossProfileApps.startActivity(intent, mTargetUser);
+ mCrossProfileApps.startActivity(intent, mTargetUser, /* callingActivity= */ null);
}
@Test
@@ -113,8 +113,11 @@
intent.setComponent(MainActivity.getComponentName(mContext));
ShellIdentityUtils.dropShellPermissionIdentity();
- ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mCrossProfileApps,
- crossProfileApps -> crossProfileApps.startActivity(intent, mTargetUser), INTERACT_ACROSS_PROFILES);
+ ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(
+ mCrossProfileApps,
+ crossProfileApps -> crossProfileApps.startActivity(
+ intent, mTargetUser, /* callingActivity= */ null),
+ INTERACT_ACROSS_PROFILES);
// Look for the text view to verify that MainActivity is started.
UiObject2 textView = mDevice.wait(Until.findObject(By.res(ID_USER_TEXTVIEW)),
@@ -130,8 +133,11 @@
intent.setComponent(MainActivity.getComponentName(mContext));
ShellIdentityUtils.dropShellPermissionIdentity();
- ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mCrossProfileApps,
- crossProfileApps -> crossProfileApps.startActivity(intent, mTargetUser), INTERACT_ACROSS_USERS);
+ ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(
+ mCrossProfileApps,
+ crossProfileApps -> crossProfileApps.startActivity(
+ intent, mTargetUser, /* callingActivity= */ null),
+ INTERACT_ACROSS_USERS);
// Look for the text view to verify that MainActivity is started.
UiObject2 textView = mDevice.wait(Until.findObject(By.res(ID_USER_TEXTVIEW)),
@@ -147,8 +153,11 @@
intent.setComponent(MainActivity.getComponentName(mContext));
ShellIdentityUtils.dropShellPermissionIdentity();
- ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mCrossProfileApps,
- crossProfileApps -> crossProfileApps.startActivity(intent, mTargetUser), INTERACT_ACROSS_USERS_FULL);
+ ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(
+ mCrossProfileApps,
+ crossProfileApps -> crossProfileApps.startActivity(
+ intent, mTargetUser, /* callingActivity= */ null),
+ INTERACT_ACROSS_USERS_FULL);
// Look for the text view to verify that MainActivity is started.
UiObject2 textView = mDevice.wait(Until.findObject(By.res(ID_USER_TEXTVIEW)),
@@ -164,9 +173,10 @@
Intent nonMainActivityImplicitIntent = new Intent();
nonMainActivityImplicitIntent.setAction(Intent.ACTION_VIEW);
- ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mCrossProfileApps,
+ ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(
+ mCrossProfileApps,
crossProfileApps -> crossProfileApps.startActivity(
- nonMainActivityImplicitIntent, mTargetUser));
+ nonMainActivityImplicitIntent, mTargetUser, /* callingActivity= */ null));
}
@Test
@@ -175,9 +185,10 @@
mainActivityIntent.setComponent(MainActivity.getComponentName(mContext));
try {
- ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mCrossProfileApps,
+ ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(
+ mCrossProfileApps,
crossProfileApps -> mCrossProfileApps.startActivity(
- mainActivityIntent, mTargetUser));
+ mainActivityIntent, mTargetUser, /* callingActivity= */ null));
// Look for the text view to verify that MainActivity is started.
UiObject2 textView = mDevice.wait(Until.findObject(By.res(ID_USER_TEXTVIEW)),
@@ -196,9 +207,10 @@
nonMainActivityIntent.setComponent(NonMainActivity.getComponentName(mContext));
try {
- ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mCrossProfileApps,
+ ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(
+ mCrossProfileApps,
crossProfileApps -> mCrossProfileApps.startActivity(
- nonMainActivityIntent, mTargetUser));
+ nonMainActivityIntent, mTargetUser, /* callingActivity= */ null));
// Look for the text view to verify that NonMainActivity is started.
UiObject2 textView = mDevice.wait(Until.findObject(By.res(ID_USER_TEXTVIEW2)),
@@ -212,7 +224,33 @@
}
/**
- * Calls {@link CrossProfileApps#startActivity(Intent, UserHandle)}. This can then be used by
+ * Starts an activity in the same task in the target user. Asserts that the activity is
+ * correctly started in the correct user, but the host-side test should verify that the tasks
+ * are the same using the log messages printed by each activity.
+ */
+ @Test
+ public void testStartActivityIntent_sameTaskByDefault() throws Exception {
+ try {
+ final Intent crossProfileSameTaskCheckerIntent = new Intent();
+ crossProfileSameTaskCheckerIntent.setComponent(
+ CrossProfileSameTaskLauncherActivity.getComponentName(mContext));
+ crossProfileSameTaskCheckerIntent.putExtra(
+ CrossProfileSameTaskLauncherActivity.TARGET_USER_EXTRA, mTargetUser);
+ mContext.startActivity(crossProfileSameTaskCheckerIntent);
+
+ // Look for the text view to verify that NonMainActivity is started.
+ UiObject2 textView = mDevice.wait(Until.findObject(By.res(ID_USER_TEXTVIEW2)),
+ TIMEOUT_WAIT_UI);
+ assertNotNull("Failed to start non-main activity in target user", textView);
+ assertEquals("Non-Main Activity is started in wrong user",
+ String.valueOf(mUserSerialNumber), textView.getText());
+ } catch (Exception e) {
+ fail("unable to start cross-profile activity in the same task: " + e);
+ }
+ }
+
+ /**
+ * Calls {@link CrossProfileApps#startActivity(Intent, UserHandle, Activity)}. This can then be used by
* host-side tests.
*/
@Test
@@ -222,8 +260,8 @@
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(
mCrossProfileApps,
- crossProfileApps ->
- mCrossProfileApps.startActivity(nonMainActivityIntent, mTargetUser));
+ crossProfileApps -> mCrossProfileApps.startActivity(
+ nonMainActivityIntent, mTargetUser, /* callingActivity= */ null));
}
@Test
@@ -267,8 +305,10 @@
Intent nonExportedActivityIntent = new Intent();
nonExportedActivityIntent.setComponent(NonExportedActivity.getComponentName(mContext));
- ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mCrossProfileApps,
- crossProfileApps -> mCrossProfileApps.startActivity(nonExportedActivityIntent, mTargetUser));
+ ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(
+ mCrossProfileApps,
+ crossProfileApps -> mCrossProfileApps.startActivity(
+ nonExportedActivityIntent, mTargetUser, /* callingActivity= */ null));
// Look for the text view to verify that NonExportedActivity is started.
UiObject2 textView = mDevice.wait(Until.findObject(By.res(ID_USER_TEXTVIEW2)),
@@ -290,9 +330,8 @@
otherPackageIntent.setComponent(new ComponentName(
"com.android.cts.launcherapps.simpleapp",
"com.android.cts.launcherapps.simpleapp.SimpleActivity"));
- mCrossProfileApps.startActivity(otherPackageIntent,
- mTargetUser
- );
+ mCrossProfileApps.startActivity(
+ otherPackageIntent, mTargetUser, /* callingActivity= */ null);
}
@Test(expected = SecurityException.class)
diff --git a/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/CrossProfileSameTaskLauncherActivity.java b/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/CrossProfileSameTaskLauncherActivity.java
new file mode 100644
index 0000000..9d1f209
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/CrossProfileSameTaskLauncherActivity.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.crossprofileappstest;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.CrossProfileApps;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.util.Log;
+
+import androidx.annotation.Nullable;
+
+import com.android.compatibility.common.util.ShellIdentityUtils;
+
+/**
+ * An activity that launches the {@link NonMainActivity} in a different user within the same task.
+ *
+ * <p>Logs its task ID with the following format:
+ * "CrossProfileSameTaskLauncherActivity#taskId#[taskId]#", where [taskId] is the actual task ID,
+ * such as NonMainActivity#taskId#4#.
+ */
+public class CrossProfileSameTaskLauncherActivity extends Activity {
+ static final String TARGET_USER_EXTRA = "TARGET_USER";
+
+ private static final String LOG_TAG = "CrossProfileSameTaskChe"; // 23 chars max
+
+ @Override
+ public void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ final Intent intent = getIntent();
+ if (!intent.hasExtra(TARGET_USER_EXTRA)) {
+ throw new IllegalStateException(
+ "ActivityForwarder started without the extra: " + TARGET_USER_EXTRA);
+ }
+ setContentView(R.layout.cross_profile_same_task_launcher);
+ Log.w(LOG_TAG, "CrossProfileSameTaskLauncherActivity#taskId#" + getTaskId() + "#");
+ final UserHandle targetUser = intent.getParcelableExtra(TARGET_USER_EXTRA);
+ final Intent nonMainActivityIntent = new Intent();
+ nonMainActivityIntent.setComponent(NonMainActivity.getComponentName(this));
+ ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(
+ getSystemService(CrossProfileApps.class),
+ crossProfileApps
+ -> crossProfileApps.startActivity(nonMainActivityIntent, targetUser, this));
+ }
+
+ static ComponentName getComponentName(Context context) {
+ return new ComponentName(context, CrossProfileSameTaskLauncherActivity.class);
+ }
+}
diff --git a/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/NonMainActivity.java b/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/NonMainActivity.java
index 4131a98..c2ad97c 100644
--- a/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/NonMainActivity.java
+++ b/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/NonMainActivity.java
@@ -21,14 +21,23 @@
import android.os.Bundle;
import android.os.Process;
import android.os.UserManager;
+import android.util.Log;
import android.widget.TextView;
+/**
+ * An activity that is not the main activity of the application.
+ *
+ * <p>Logs its task ID with the following format: "NonMainActivity#taskId#[taskId]#", where [taskId]
+ * is the actual task ID, such as NonMainActivity#taskId#4#.
+ */
public class NonMainActivity extends Activity {
+ private static final String LOG_TAG = "NonMainActivity";
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.non_main);
+ Log.w(LOG_TAG, "NonMainActivity#taskId#" + getTaskId() + "#");
}
@Override
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/AccountManagementParentTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/AccountManagementParentTest.java
new file mode 100644
index 0000000..0aa1a79
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/AccountManagementParentTest.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.deviceandprofileowner;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.android.cts.deviceandprofileowner.BaseDeviceAdminTest.ADMIN_RECEIVER_COMPONENT;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.Context;
+import android.test.InstrumentationTestCase;
+
+public class AccountManagementParentTest extends InstrumentationTestCase {
+ private static final String SOME_ACCOUNT_TYPE = "com.example.account.type";
+
+ private DevicePolicyManager mDevicePolicyManager;
+ private Context mContext;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mContext = getInstrumentation().getContext();
+
+ mDevicePolicyManager = mContext.getSystemService(DevicePolicyManager.class);
+ assertNotNull(mDevicePolicyManager);
+ }
+
+ public void testSetAccountManagementDisabledOnParent() {
+ DevicePolicyManager parentDevicePolicyManager = getParentInstance();
+
+ parentDevicePolicyManager.setAccountManagementDisabled(ADMIN_RECEIVER_COMPONENT,
+ SOME_ACCOUNT_TYPE, true);
+ assertThat(
+ parentDevicePolicyManager.getAccountTypesWithManagementDisabled()).asList()
+ .containsExactly(SOME_ACCOUNT_TYPE);
+ // Ensure that account management is not restricted on the managed profile itself.
+ assertThat(mDevicePolicyManager.getAccountTypesWithManagementDisabled()).isEmpty();
+ }
+
+ public void testAccountManagementDisabled() {
+ assertThat(
+ mDevicePolicyManager.getAccountTypesWithManagementDisabled()).asList()
+ .containsExactly(SOME_ACCOUNT_TYPE);
+ }
+
+ public void testEnableAccountManagement() {
+ DevicePolicyManager parentDevicePolicyManager = getParentInstance();
+
+ parentDevicePolicyManager.setAccountManagementDisabled(ADMIN_RECEIVER_COMPONENT,
+ SOME_ACCOUNT_TYPE, false);
+ assertThat(parentDevicePolicyManager.getAccountTypesWithManagementDisabled()).isEmpty();
+ }
+
+ private DevicePolicyManager getParentInstance() {
+ assertThat(mDevicePolicyManager.isAdminActive(ADMIN_RECEIVER_COMPONENT)).isTrue();
+ assertThat(mDevicePolicyManager.isProfileOwnerApp(
+ ADMIN_RECEIVER_COMPONENT.getPackageName())).isTrue();
+ assertThat(mDevicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile()).isTrue();
+
+ DevicePolicyManager parentDevicePolicyManager =
+ mDevicePolicyManager.getParentProfileInstance(ADMIN_RECEIVER_COMPONENT);
+ assertThat(parentDevicePolicyManager).isNotNull();
+
+ return parentDevicePolicyManager;
+ }
+}
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/BaseDeviceAdminTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/BaseDeviceAdminTest.java
index 212d20f..889e91a 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/BaseDeviceAdminTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/BaseDeviceAdminTest.java
@@ -69,6 +69,7 @@
protected DevicePolicyManager mDevicePolicyManager;
protected UserManager mUserManager;
protected Context mContext;
+ protected boolean mHasSecureLockScreen;
static CountDownLatch mOnPasswordExpiryTimeoutCalled;
private final String mTag = getClass().getSimpleName();
@@ -84,6 +85,9 @@
mUserManager = mContext.getSystemService(UserManager.class);
assertNotNull(mUserManager);
+ mHasSecureLockScreen = mContext.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_SECURE_LOCK_SCREEN);
+
assertTrue(mDevicePolicyManager.isAdminActive(ADMIN_RECEIVER_COMPONENT));
assertTrue("App is neither device nor profile owner",
mDevicePolicyManager.isProfileOwnerApp(PACKAGE_NAME) ||
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/KeyguardDisabledFeaturesTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/KeyguardDisabledFeaturesTest.java
index 23c62fc..304b67a 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/KeyguardDisabledFeaturesTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/KeyguardDisabledFeaturesTest.java
@@ -18,8 +18,6 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.testng.Assert.assertThrows;
-
import android.app.admin.DevicePolicyManager;
public class KeyguardDisabledFeaturesTest extends BaseDeviceAdminTest {
@@ -32,9 +30,19 @@
assertThat(mDevicePolicyManager.getKeyguardDisabledFeatures(
ADMIN_RECEIVER_COMPONENT)).isEqualTo(
DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA);
+
+ removeKeyguardDisableFeatures(mDevicePolicyManager);
+ mDevicePolicyManager.setKeyguardDisabledFeatures(ADMIN_RECEIVER_COMPONENT,
+ DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS);
+
+ // Check if the admin has disabled notifications specifically for the keyguard
+ assertThat(mDevicePolicyManager.getKeyguardDisabledFeatures(
+ ADMIN_RECEIVER_COMPONENT)).isEqualTo(
+ DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS);
+ removeKeyguardDisableFeatures(mDevicePolicyManager);
}
- public void testSetKeyguardDisabledFeatures_onParentSilentIgnore() {
+ public void testSetKeyguardDisabledFeatures_onParentSilentIgnoreWhenCallerIsNotOrgOwnedPO() {
DevicePolicyManager parentDevicePolicyManager =
mDevicePolicyManager.getParentProfileInstance(ADMIN_RECEIVER_COMPONENT);
@@ -56,5 +64,20 @@
assertThat(parentDevicePolicyManager.getKeyguardDisabledFeatures(
ADMIN_RECEIVER_COMPONENT)).isEqualTo(
DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA);
+
+ removeKeyguardDisableFeatures(parentDevicePolicyManager);
+ parentDevicePolicyManager.setKeyguardDisabledFeatures(ADMIN_RECEIVER_COMPONENT,
+ DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS);
+
+ // Check if the admin has disabled notifications specifically for the keyguard
+ assertThat(parentDevicePolicyManager.getKeyguardDisabledFeatures(
+ ADMIN_RECEIVER_COMPONENT)).isEqualTo(
+ DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS);
+ removeKeyguardDisableFeatures(parentDevicePolicyManager);
+ }
+
+ private void removeKeyguardDisableFeatures(DevicePolicyManager devicePolicyManager) {
+ devicePolicyManager.setKeyguardDisabledFeatures(ADMIN_RECEIVER_COMPONENT,
+ DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_NONE);
}
}
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/LockTaskTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/LockTaskTest.java
index 2f9bc37..78b5b2e 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/LockTaskTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/LockTaskTest.java
@@ -15,6 +15,7 @@
*/
package com.android.cts.deviceandprofileowner;
+import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK;
import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_GLOBAL_ACTIONS;
import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_HOME;
import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_KEYGUARD;
@@ -35,7 +36,11 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
+import android.support.test.uiautomator.By;
+import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.Until;
import android.util.Log;
+import android.view.KeyEvent;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
@@ -134,11 +139,13 @@
private final Object mReceiverActivityRunningLock = new Object();
private Context mContext;
+ private UiDevice mUiDevice;
private ActivityManager mActivityManager;
private DevicePolicyManager mDevicePolicyManager;
public void setUp() {
mContext = InstrumentationRegistry.getContext();
+ mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
mDevicePolicyManager = mContext.getSystemService(DevicePolicyManager.class);
mDevicePolicyManager.setLockTaskPackages(ADMIN_COMPONENT, new String[0]);
@@ -302,6 +309,43 @@
stopAndFinish(UTILITY_ACTIVITY);
}
+ // When LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK is set, un-whitelisted apps cannot
+ // be launched in the same task.
+ public void testStartActivity_withinTask_blockInTask() throws Exception {
+ mDevicePolicyManager.setLockTaskPackages(ADMIN_COMPONENT, new String[] { PACKAGE_NAME });
+ mDevicePolicyManager.setLockTaskFeatures(
+ ADMIN_COMPONENT, LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK);
+ startLockTask(UTILITY_ACTIVITY);
+ waitForResume();
+
+ mIsReceiverActivityRunning = false;
+ Intent launchIntent = createReceiverActivityIntent(false /*newTask*/, false /*shouldWait*/);
+ Intent lockTaskUtility = getLockTaskUtility(UTILITY_ACTIVITY);
+ lockTaskUtility.putExtra(LockTaskUtilityActivity.START_ACTIVITY, launchIntent);
+ mContext.startActivity(lockTaskUtility);
+
+ synchronized (mReceiverActivityRunningLock) {
+ mReceiverActivityRunningLock.wait(ACTIVITY_RESUMED_TIMEOUT_MILLIS);
+ assertFalse(mIsReceiverActivityRunning);
+ }
+
+ Log.d(TAG, "Waiting for the system dialog");
+ mUiDevice.waitForIdle();
+ assertTrue(mUiDevice.wait(
+ Until.hasObject(By.textContains(RECEIVER_ACTIVITY_PACKAGE_NAME)),
+ ACTIVITY_RESUMED_TIMEOUT_MILLIS));
+ Log.d(TAG, "dialog found");
+
+ // Dismiss the system dialog
+ mUiDevice.pressKeyCode(KeyEvent.KEYCODE_ENTER);
+ mUiDevice.pressKeyCode(KeyEvent.KEYCODE_ENTER);
+ assertFalse(mUiDevice.wait(
+ Until.hasObject(By.textContains(RECEIVER_ACTIVITY_PACKAGE_NAME)),
+ ACTIVITY_RESUMED_TIMEOUT_MILLIS));
+
+ mDevicePolicyManager.setLockTaskFeatures(ADMIN_COMPONENT, 0);
+ }
+
// This launches a whitelisted activity that is not part of the current task.
// This should be permitted as a part of lock task.
public void testStartActivity_outsideTaskWhitelisted() throws Exception {
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/PersonalAppsSuspensionTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/PersonalAppsSuspensionTest.java
index 9f0d3fb..5de41ab 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/PersonalAppsSuspensionTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/PersonalAppsSuspensionTest.java
@@ -50,4 +50,15 @@
assertThat(mDpm.getPersonalAppsSuspendedReasons(ADMIN))
.isEqualTo(DevicePolicyManager.PERSONAL_APPS_NOT_SUSPENDED);
}
+
+ @Test
+ public void testSetManagedProfileMaximumTimeOff1Sec() {
+ mDpm.setManagedProfileMaximumTimeOff(ADMIN, 1000);
+ }
+
+ @Test
+ public void testPersonalAppsSuspendedByTimeout() {
+ assertThat(mDpm.getPersonalAppsSuspendedReasons(ADMIN))
+ .isEqualTo(DevicePolicyManager.PERSONAL_APPS_SUSPENDED_PROFILE_TIMEOUT);
+ }
}
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/SecurityLoggingTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/SecurityLoggingTest.java
similarity index 83%
rename from hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/SecurityLoggingTest.java
rename to hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/SecurityLoggingTest.java
index ed4c19f..58a377e 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/SecurityLoggingTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/SecurityLoggingTest.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.cts.deviceowner;
+package com.android.cts.deviceandprofileowner;
import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
@@ -62,16 +62,21 @@
import android.content.SharedPreferences;
import android.os.Parcel;
import android.os.Process;
+import android.os.UserHandle;
import android.os.UserManager;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;
import android.security.keystore.KeyProtection;
+import android.text.TextUtils;
+import android.util.Log;
import androidx.test.InstrumentationRegistry;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
+import junit.framework.AssertionFailedError;
+
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
@@ -85,7 +90,8 @@
import javax.crypto.spec.SecretKeySpec;
-public class SecurityLoggingTest extends BaseDeviceOwnerTest {
+public class SecurityLoggingTest extends BaseDeviceAdminTest {
+ private static final String TAG = "SecurityLoggingTest";
private static final String ARG_BATCH_NUMBER = "batchNumber";
private static final String PREF_KEY_PREFIX = "batch-last-id-";
private static final String PREF_NAME = "batchIds";
@@ -123,8 +129,8 @@
.put(TAG_KEY_GENERATED, of(I, S, I))
.put(TAG_KEY_IMPORT, of(I, S, I))
.put(TAG_KEY_DESTRUCTION, of(I, S, I))
- .put(TAG_CERT_AUTHORITY_INSTALLED, of(I, S))
- .put(TAG_CERT_AUTHORITY_REMOVED, of(I, S))
+ .put(TAG_CERT_AUTHORITY_INSTALLED, of(I, S, I))
+ .put(TAG_CERT_AUTHORITY_REMOVED, of(I, S, I))
.put(TAG_USER_RESTRICTION_ADDED, of(S, I, S))
.put(TAG_USER_RESTRICTION_REMOVED, of(S, I, S))
.put(TAG_CRYPTO_SELF_TEST_COMPLETED, of(I))
@@ -170,6 +176,7 @@
private static final int SUCCESS_INDEX = 0;
private static final int ALIAS_INDEX = 1;
private static final int UID_INDEX = 2;
+ private static final int USERID_INDEX = 2;
private static final int SUBJECT_INDEX = 1;
private static final int ADMIN_PKG_INDEX = 0;
private static final int ADMIN_USER_INDEX = 1;
@@ -208,7 +215,7 @@
*/
public void testRetrievingSecurityLogsThrowsSecurityException() {
try {
- mDevicePolicyManager.retrieveSecurityLogs(getWho());
+ mDevicePolicyManager.retrieveSecurityLogs(ADMIN_RECEIVER_COMPONENT);
fail("did not throw expected SecurityException");
} catch (SecurityException expected) {
}
@@ -220,7 +227,7 @@
*/
public void testRetrievingPreviousSecurityLogsThrowsSecurityException() {
try {
- mDevicePolicyManager.retrievePreRebootSecurityLogs(getWho());
+ mDevicePolicyManager.retrievePreRebootSecurityLogs(ADMIN_RECEIVER_COMPONENT);
fail("did not throw expected SecurityException");
} catch (SecurityException expected) {
}
@@ -236,6 +243,10 @@
verifyKeystoreEventsPresent(events);
verifyKeyChainEventsPresent(events);
verifyAdminEventsPresent(events);
+ verifyAdbShellCommand(events); // Event generated from host side logic
+ if (mDevicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile()) {
+ verifyEventsRedacted(events);
+ }
}
private void verifyAutomaticEventsPresent(List<SecurityEvent> events) {
@@ -264,6 +275,39 @@
verifyUserRestrictionEventsPresent(events);
verifyCameraPolicyEvents(events);
}
+ private void verifyAdbShellCommand(List<SecurityEvent> events) {
+ // Won't be able to locate the command on org-owned devices, as it will be redacted.
+ if (!mDevicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile()) {
+ findEvent("adb command", events,
+ e -> e.getTag() == TAG_ADB_SHELL_CMD &&
+ e.getData().equals("whoami"));
+
+ }
+ }
+
+ private void verifyEventsRedacted(List<SecurityEvent> events) {
+ final int userId = Process.myUserHandle().getIdentifier();
+ for (SecurityEvent event : events) {
+ switch (event.getTag()) {
+ case TAG_ADB_SHELL_CMD:
+ assertTrue(TextUtils.isEmpty((String) event.getData()));
+ break;
+ case TAG_APP_PROCESS_START:
+ case TAG_KEY_GENERATED:
+ case TAG_KEY_IMPORT:
+ case TAG_KEY_DESTRUCTION:
+ assertEquals(userId, UserHandle.getUserId(getInt(event, UID_INDEX)));
+ break;
+ case TAG_CERT_AUTHORITY_INSTALLED:
+ case TAG_CERT_AUTHORITY_REMOVED:
+ assertEquals(userId, getInt(event, USERID_INDEX));
+ break;
+ case TAG_KEY_INTEGRITY_VIOLATION:
+ assertEquals(userId, UserHandle.getUserId(getInt(event, 1)));
+ break;
+ }
+ }
+ }
/**
* Generates events for positive test cases.
@@ -303,15 +347,26 @@
// Retry once after seeping for 1 second, in case "dpm force-security-logs" hasn't taken
// effect just yet.
for (int i = 0; i < 2 && events == null; i++) {
- events = mDevicePolicyManager.retrieveSecurityLogs(getWho());
+ events = mDevicePolicyManager.retrieveSecurityLogs(ADMIN_RECEIVER_COMPONENT);
if (events == null) Thread.sleep(1000);
}
-
- verifySecurityLogs(events);
+ try {
+ verifySecurityLogs(events);
+ } catch (AssertionFailedError e) {
+ dumpSecurityLogs(events);
+ throw e;
+ }
return events;
}
+ private void dumpSecurityLogs(List<SecurityEvent> events) {
+ Log.d(TAG, "Security events dump:");
+ for (SecurityEvent event : events) {
+ Log.d(TAG, event.toString());
+ }
+ }
+
/**
* Test: check that there are no gaps between ids in two consecutive batches. Shared preference
* is used to store these numbers between test invocations.
@@ -470,9 +525,9 @@
* security logging is enabled after its execution.
*/
public void testEnablingSecurityLogging() {
- assertFalse(mDevicePolicyManager.isSecurityLoggingEnabled(getWho()));
- mDevicePolicyManager.setSecurityLoggingEnabled(getWho(), true);
- assertTrue(mDevicePolicyManager.isSecurityLoggingEnabled(getWho()));
+ assertFalse(mDevicePolicyManager.isSecurityLoggingEnabled(ADMIN_RECEIVER_COMPONENT));
+ mDevicePolicyManager.setSecurityLoggingEnabled(ADMIN_RECEIVER_COMPONENT, true);
+ assertTrue(mDevicePolicyManager.isSecurityLoggingEnabled(ADMIN_RECEIVER_COMPONENT));
}
/**
@@ -480,11 +535,11 @@
* disabled after its execution.
*/
public void testDisablingSecurityLogging() {
- mDevicePolicyManager.setSecurityLoggingEnabled(getWho(), false);
- assertFalse(mDevicePolicyManager.isSecurityLoggingEnabled(getWho()));
+ mDevicePolicyManager.setSecurityLoggingEnabled(ADMIN_RECEIVER_COMPONENT, false);
+ assertFalse(mDevicePolicyManager.isSecurityLoggingEnabled(ADMIN_RECEIVER_COMPONENT));
// Verify that logs are actually not available.
- assertNull(mDevicePolicyManager.retrieveSecurityLogs(getWho()));
+ assertNull(mDevicePolicyManager.retrieveSecurityLogs(ADMIN_RECEIVER_COMPONENT));
}
/**
@@ -492,11 +547,12 @@
* null.
*/
public void testSecurityLoggingRetrievalRateLimited() {
- final List<SecurityEvent> logs = mDevicePolicyManager.retrieveSecurityLogs(getWho());
+ final List<SecurityEvent> logs = mDevicePolicyManager.retrieveSecurityLogs(
+ ADMIN_RECEIVER_COMPONENT);
// if logs is null it means that that attempt was rate limited => test PASS
if (logs != null) {
- assertNull(mDevicePolicyManager.retrieveSecurityLogs(getWho()));
- assertNull(mDevicePolicyManager.retrieveSecurityLogs(getWho()));
+ assertNull(mDevicePolicyManager.retrieveSecurityLogs(ADMIN_RECEIVER_COMPONENT));
+ assertNull(mDevicePolicyManager.retrieveSecurityLogs(ADMIN_RECEIVER_COMPONENT));
}
}
@@ -546,7 +602,8 @@
}
private void installCaCert() {
- mDevicePolicyManager.installCaCert(getWho(), TEST_CA.getBytes());
+ assertTrue(
+ mDevicePolicyManager.installCaCert(ADMIN_RECEIVER_COMPONENT, TEST_CA.getBytes()));
}
private void verifyCertInstalledEventPresent(List<SecurityEvent> events) {
@@ -557,7 +614,7 @@
}
private void uninstallCaCert() {
- mDevicePolicyManager.uninstallCaCert(getWho(), TEST_CA.getBytes());
+ mDevicePolicyManager.uninstallCaCert(ADMIN_RECEIVER_COMPONENT, TEST_CA.getBytes());
}
private void verifyCertUninstalledEventPresent(List<SecurityEvent> events) {
@@ -568,21 +625,21 @@
}
private void generatePasswordComplexityEvents() {
- mDevicePolicyManager.setPasswordQuality(getWho(), PASSWORD_QUALITY_COMPLEX);
- mDevicePolicyManager.setPasswordMinimumLength(getWho(), TEST_PWD_LENGTH);
- mDevicePolicyManager.setPasswordMinimumLetters(getWho(), TEST_PWD_CHARS);
- mDevicePolicyManager.setPasswordMinimumNonLetter(getWho(), TEST_PWD_CHARS);
- mDevicePolicyManager.setPasswordMinimumUpperCase(getWho(), TEST_PWD_CHARS);
- mDevicePolicyManager.setPasswordMinimumLowerCase(getWho(), TEST_PWD_CHARS);
- mDevicePolicyManager.setPasswordMinimumNumeric(getWho(), TEST_PWD_CHARS);
- mDevicePolicyManager.setPasswordMinimumSymbols(getWho(), TEST_PWD_CHARS);
+ mDevicePolicyManager.setPasswordQuality(ADMIN_RECEIVER_COMPONENT, PASSWORD_QUALITY_COMPLEX);
+ mDevicePolicyManager.setPasswordMinimumLength(ADMIN_RECEIVER_COMPONENT, TEST_PWD_LENGTH);
+ mDevicePolicyManager.setPasswordMinimumLetters(ADMIN_RECEIVER_COMPONENT, TEST_PWD_CHARS);
+ mDevicePolicyManager.setPasswordMinimumNonLetter(ADMIN_RECEIVER_COMPONENT, TEST_PWD_CHARS);
+ mDevicePolicyManager.setPasswordMinimumUpperCase(ADMIN_RECEIVER_COMPONENT, TEST_PWD_CHARS);
+ mDevicePolicyManager.setPasswordMinimumLowerCase(ADMIN_RECEIVER_COMPONENT, TEST_PWD_CHARS);
+ mDevicePolicyManager.setPasswordMinimumNumeric(ADMIN_RECEIVER_COMPONENT, TEST_PWD_CHARS);
+ mDevicePolicyManager.setPasswordMinimumSymbols(ADMIN_RECEIVER_COMPONENT, TEST_PWD_CHARS);
}
private void verifyPasswordComplexityEventsPresent(List<SecurityEvent> events) {
final int userId = Process.myUserHandle().getIdentifier();
// This reflects default values for password complexity event payload fields.
final Object[] expectedPayload = new Object[] {
- getWho().getPackageName(), // admin package
+ ADMIN_RECEIVER_COMPONENT.getPackageName(), // admin package
userId, // admin user
userId, // target user
0, // default password length
@@ -617,37 +674,40 @@
private void generateLockingPolicyEvents() {
if (mHasSecureLockScreen) {
- mDevicePolicyManager.setPasswordExpirationTimeout(getWho(),
+ mDevicePolicyManager.setPasswordExpirationTimeout(ADMIN_RECEIVER_COMPONENT,
TEST_PWD_EXPIRATION_TIMEOUT);
- mDevicePolicyManager.setPasswordHistoryLength(getWho(), TEST_PWD_HISTORY_LENGTH);
- mDevicePolicyManager.setMaximumFailedPasswordsForWipe(getWho(), TEST_PWD_MAX_ATTEMPTS);
+ mDevicePolicyManager.setPasswordHistoryLength(ADMIN_RECEIVER_COMPONENT,
+ TEST_PWD_HISTORY_LENGTH);
+ mDevicePolicyManager.setMaximumFailedPasswordsForWipe(ADMIN_RECEIVER_COMPONENT,
+ TEST_PWD_MAX_ATTEMPTS);
}
- mDevicePolicyManager.setKeyguardDisabledFeatures(getWho(), KEYGUARD_DISABLE_FINGERPRINT);
- mDevicePolicyManager.setMaximumTimeToLock(getWho(), TEST_MAX_TIME_TO_LOCK);
+ mDevicePolicyManager.setKeyguardDisabledFeatures(ADMIN_RECEIVER_COMPONENT,
+ KEYGUARD_DISABLE_FINGERPRINT);
+ mDevicePolicyManager.setMaximumTimeToLock(ADMIN_RECEIVER_COMPONENT, TEST_MAX_TIME_TO_LOCK);
mDevicePolicyManager.lockNow();
}
private void verifyLockingPolicyEventsPresent(List<SecurityEvent> events) {
final int userId = Process.myUserHandle().getIdentifier();
-
+ final String packageName = ADMIN_RECEIVER_COMPONENT.getPackageName();
if (mHasSecureLockScreen) {
findEvent("set password expiration", events,
e -> e.getTag() == TAG_PASSWORD_EXPIRATION_SET &&
- getString(e, ADMIN_PKG_INDEX).equals(getWho().getPackageName()) &&
+ getString(e, ADMIN_PKG_INDEX).equals(packageName) &&
getInt(e, ADMIN_USER_INDEX) == userId &&
getInt(e, TARGET_USER_INDEX) == userId &&
getLong(e, PWD_EXPIRATION_INDEX) == TEST_PWD_EXPIRATION_TIMEOUT);
findEvent("set password history length", events,
e -> e.getTag() == TAG_PASSWORD_HISTORY_LENGTH_SET &&
- getString(e, ADMIN_PKG_INDEX).equals(getWho().getPackageName()) &&
+ getString(e, ADMIN_PKG_INDEX).equals(packageName) &&
getInt(e, ADMIN_USER_INDEX) == userId &&
getInt(e, TARGET_USER_INDEX) == userId &&
getInt(e, PWD_HIST_LEN_INDEX) == TEST_PWD_HISTORY_LENGTH);
findEvent("set password attempts", events,
e -> e.getTag() == TAG_MAX_PASSWORD_ATTEMPTS_SET &&
- getString(e, ADMIN_PKG_INDEX).equals(getWho().getPackageName()) &&
+ getString(e, ADMIN_PKG_INDEX).equals(packageName) &&
getInt(e, ADMIN_USER_INDEX) == userId &&
getInt(e, TARGET_USER_INDEX) == userId &&
getInt(e, MAX_PWD_ATTEMPTS_INDEX) == TEST_PWD_MAX_ATTEMPTS);
@@ -655,21 +715,21 @@
findEvent("set keyguard disabled features", events,
e -> e.getTag() == TAG_KEYGUARD_DISABLED_FEATURES_SET &&
- getString(e, ADMIN_PKG_INDEX).equals(getWho().getPackageName()) &&
+ getString(e, ADMIN_PKG_INDEX).equals(packageName) &&
getInt(e, ADMIN_USER_INDEX) == userId &&
getInt(e, TARGET_USER_INDEX) == userId &&
getInt(e, KEYGUARD_FEATURES_INDEX) == KEYGUARD_DISABLE_FINGERPRINT);
findEvent("set screen lock timeout", events,
e -> e.getTag() == TAG_MAX_SCREEN_LOCK_TIMEOUT_SET &&
- getString(e, ADMIN_PKG_INDEX).equals(getWho().getPackageName()) &&
+ getString(e, ADMIN_PKG_INDEX).equals(packageName) &&
getInt(e, ADMIN_USER_INDEX) == userId &&
getInt(e, TARGET_USER_INDEX) == userId &&
getLong(e, MAX_SCREEN_TIMEOUT_INDEX) == TEST_MAX_TIME_TO_LOCK);
findEvent("set screen lock timeout", events,
e -> e.getTag() == TAG_REMOTE_LOCK &&
- getString(e, ADMIN_PKG_INDEX).equals(getWho().getPackageName()) &&
+ getString(e, ADMIN_PKG_INDEX).equals(packageName) &&
getInt(e, ADMIN_USER_INDEX) == userId);
}
@@ -681,8 +741,10 @@
}
private void generateUserRestrictionEvents() {
- mDevicePolicyManager.addUserRestriction(getWho(), UserManager.DISALLOW_FUN);
- mDevicePolicyManager.clearUserRestriction(getWho(), UserManager.DISALLOW_FUN);
+ mDevicePolicyManager.addUserRestriction(ADMIN_RECEIVER_COMPONENT,
+ UserManager.DISALLOW_PRINTING);
+ mDevicePolicyManager.clearUserRestriction(ADMIN_RECEIVER_COMPONENT,
+ UserManager.DISALLOW_PRINTING);
}
private void verifyUserRestrictionEventsPresent(List<SecurityEvent> events) {
@@ -694,14 +756,15 @@
final int userId = Process.myUserHandle().getIdentifier();
findEvent(description, events,
e -> e.getTag() == tag &&
- getString(e, ADMIN_PKG_INDEX).equals(getWho().getPackageName()) &&
+ getString(e, ADMIN_PKG_INDEX).equals(
+ ADMIN_RECEIVER_COMPONENT.getPackageName()) &&
getInt(e, ADMIN_USER_INDEX) == userId &&
- UserManager.DISALLOW_FUN.equals(getString(e, USER_RESTRICTION_INDEX)));
+ UserManager.DISALLOW_PRINTING.equals(getString(e, USER_RESTRICTION_INDEX)));
}
private void generateCameraPolicyEvents() {
- mDevicePolicyManager.setCameraDisabled(getWho(), true);
- mDevicePolicyManager.setCameraDisabled(getWho(), false);
+ mDevicePolicyManager.setCameraDisabled(ADMIN_RECEIVER_COMPONENT, true);
+ mDevicePolicyManager.setCameraDisabled(ADMIN_RECEIVER_COMPONENT, false);
}
private void verifyCameraPolicyEvents(List<SecurityEvent> events) {
@@ -709,14 +772,16 @@
findEvent("set camera disabled", events,
e -> e.getTag() == TAG_CAMERA_POLICY_SET &&
- getString(e, ADMIN_PKG_INDEX).equals(getWho().getPackageName()) &&
+ getString(e, ADMIN_PKG_INDEX).equals(
+ ADMIN_RECEIVER_COMPONENT.getPackageName()) &&
getInt(e, ADMIN_USER_INDEX) == userId &&
getInt(e, TARGET_USER_INDEX) == userId &&
getInt(e, CAMERA_DISABLED_INDEX) == 1);
findEvent("set camera enabled", events,
e -> e.getTag() == TAG_CAMERA_POLICY_SET &&
- getString(e, ADMIN_PKG_INDEX).equals(getWho().getPackageName()) &&
+ getString(e, ADMIN_PKG_INDEX).equals(
+ ADMIN_RECEIVER_COMPONENT.getPackageName()) &&
getInt(e, ADMIN_USER_INDEX) == userId &&
getInt(e, TARGET_USER_INDEX) == userId &&
getInt(e, CAMERA_DISABLED_INDEX) == 0);
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/RequestSetLocationProviderAllowedTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/RequestSetLocationProviderAllowedTest.java
deleted file mode 100644
index 6b30207..0000000
--- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/RequestSetLocationProviderAllowedTest.java
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.deviceowner;
-
-import static android.location.LocationManager.EXTRA_PROVIDER_NAME;
-import static android.location.LocationManager.MODE_CHANGED_ACTION;
-import static android.location.LocationManager.PROVIDERS_CHANGED_ACTION;
-
-import android.app.admin.DevicePolicyManager;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.location.Criteria;
-import android.location.LocationManager;
-
-import androidx.test.platform.app.InstrumentationRegistry;
-
-import com.android.compatibility.common.util.LocationUtils;
-
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Test {@link DevicePolicyManager#requestSetLocationProviderAllowed}.
- */
-public class RequestSetLocationProviderAllowedTest extends BaseDeviceOwnerTest {
-
- private static final long TIMEOUT_MS = 5000;
-
- private static final String TEST_PROVIDER = "test_provider";
-
- private LocationManager mLocationManager;
-
- private boolean mLocationEnabled;
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
-
- // allow us to add mock location providers
- LocationUtils.registerMockLocationProvider(mInstrumentation, /*enable=*/true);
-
- mLocationManager = mContext.getSystemService(LocationManager.class);
-
- mLocationEnabled = mLocationManager.isLocationEnabled();
- if (!mLocationEnabled) {
- setLocationEnabledAndAssert(true);
- }
- }
-
- @Override
- protected void tearDown() throws Exception {
- if (!mLocationEnabled) {
- setLocationEnabledAndAssert(false);
- }
-
- LocationUtils.registerMockLocationProvider(mInstrumentation, /*enable=*/false);
- super.tearDown();
- }
-
- public void testRequestSetLocationProviderAllowedTest() throws Exception {
- mLocationManager.addTestProvider(TEST_PROVIDER,
- /* requiresNetwork=*/ true,
- /* requiresSatellite=*/ false,
- /* requiresCell=*/ true,
- /* hasMonetaryCost=*/ false,
- /* supportsAltitude=*/ false,
- /* supportsSpeed=*/ false,
- /* supportsBearing=*/ false,
- Criteria.POWER_MEDIUM,
- Criteria.ACCURACY_FINE);
- try {
- mLocationManager.setTestProviderEnabled(TEST_PROVIDER, false);
-
- requestProviderAllowedAndAssert(TEST_PROVIDER, true);
- requestProviderAllowedAndAssert(TEST_PROVIDER, false);
- } finally {
- mLocationManager.removeTestProvider(TEST_PROVIDER);
- }
- }
-
- private void requestProviderAllowedAndAssert(String provider, boolean allowed) throws Exception {
- final CountDownLatch latch = new CountDownLatch(1);
- final BroadcastReceiver receiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (provider.equals(intent.getStringExtra(EXTRA_PROVIDER_NAME))) {
- latch.countDown();
- }
- }
- };
- mContext.registerReceiver(receiver, new IntentFilter(PROVIDERS_CHANGED_ACTION));
-
- try {
- mDevicePolicyManager.requestSetLocationProviderAllowed(getWho(), provider, allowed);
- assertTrue("timed out waiting for provider change broadcast",
- latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
- assertEquals(allowed, mLocationManager.isProviderEnabled(provider));
- } finally {
- mContext.unregisterReceiver(receiver);
- }
- }
-
- private void setLocationEnabledAndAssert(boolean enabled) throws Exception {
- final CountDownLatch latch = new CountDownLatch(1);
- final BroadcastReceiver receiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- latch.countDown();
- }
- };
- mContext.registerReceiver(receiver, new IntentFilter(MODE_CHANGED_ACTION));
-
- try {
- mDevicePolicyManager.setLocationEnabled(getWho(), enabled);
- assertTrue("timed out waiting for location mode change broadcast",
- latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
- } finally {
- mContext.unregisterReceiver(receiver);
- }
- }
-}
diff --git a/hostsidetests/devicepolicy/app/DummyLauncher/Android.bp b/hostsidetests/devicepolicy/app/DummyLauncher/Android.bp
new file mode 100644
index 0000000..dd0ab0a
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/DummyLauncher/Android.bp
@@ -0,0 +1,28 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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: "DummyLauncher",
+ defaults: ["cts_defaults"],
+ srcs: ["src/**/*.java"],
+ static_libs: [
+ "cts-devicepolicy-suspensionchecker",
+ ],
+ test_suites: [
+ "arcts",
+ "cts",
+ "vts",
+ "general-tests",
+ ],
+}
diff --git a/hostsidetests/devicepolicy/app/DummyLauncher/AndroidManifest.xml b/hostsidetests/devicepolicy/app/DummyLauncher/AndroidManifest.xml
new file mode 100644
index 0000000..a80e9b4
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/DummyLauncher/AndroidManifest.xml
@@ -0,0 +1,40 @@
+<!--
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.dummylauncher">
+ <application android:label="Dummy Launcher">
+ <activity android:name="android.app.Activity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.HOME"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ </intent-filter>
+ </activity>
+ <activity android:name=".QuietModeToggleActivity"
+ android:exported="true"/>
+ </application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.cts.dummylauncher">
+ <meta-data
+ android:name="listener"
+ android:value="com.android.cts.runner.CtsTestRunListener"/>
+ </instrumentation>
+</manifest>
diff --git a/hostsidetests/devicepolicy/app/DummyLauncher/src/com/android/cts/dummylauncher/QuietModeToggleActivity.java b/hostsidetests/devicepolicy/app/DummyLauncher/src/com/android/cts/dummylauncher/QuietModeToggleActivity.java
new file mode 100644
index 0000000..e659f15
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/DummyLauncher/src/com/android/cts/dummylauncher/QuietModeToggleActivity.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.dummylauncher;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.os.Process;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.Log;
+
+import java.util.List;
+
+public class QuietModeToggleActivity extends Activity {
+ private static final String TAG = "QuietModeToggleActivity";
+ private static final String EXTRA_QUIET_MODE_STATE =
+ "com.android.cts.dummyactivity.QUIET_MODE_STATE";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ toggleQuietMode();
+ finish();
+ }
+
+ private void toggleQuietMode() {
+ final boolean quietModeState = getIntent().getBooleanExtra(EXTRA_QUIET_MODE_STATE, false);
+ final UserManager userManager = UserManager.get(this);
+
+ final List<UserHandle> users = userManager.getUserProfiles();
+ if (users.size() != 2) {
+ Log.e(TAG, "Unexpected number of profiles: " + users.size());
+ return;
+ }
+
+ final UserHandle profileHandle =
+ users.get(0).equals(Process.myUserHandle()) ? users.get(1) : users.get(0);
+
+ final String quietModeStateString = quietModeState ? "enabled" : "disabled";
+ if (userManager.isQuietModeEnabled(profileHandle) == quietModeState) {
+ Log.w(TAG, "Quiet mode is already " + quietModeStateString);
+ return;
+ }
+
+ userManager.requestQuietModeEnabled(quietModeState, profileHandle);
+ Log.i(TAG, "Quiet mode for user " + profileHandle + " was set to " + quietModeStateString);
+ }
+}
\ No newline at end of file
diff --git a/hostsidetests/devicepolicy/app/LauncherTestsSupport/src/com/android/cts/launchertests/support/LauncherCallbackTestsService.java b/hostsidetests/devicepolicy/app/LauncherTestsSupport/src/com/android/cts/launchertests/support/LauncherCallbackTestsService.java
index a5e8da1..7c3b3ee 100644
--- a/hostsidetests/devicepolicy/app/LauncherTestsSupport/src/com/android/cts/launchertests/support/LauncherCallbackTestsService.java
+++ b/hostsidetests/devicepolicy/app/LauncherTestsSupport/src/com/android/cts/launchertests/support/LauncherCallbackTestsService.java
@@ -1,5 +1,6 @@
/*
- * Copyright (C) 2014 The Android Open Sour *
+ * Copyright (C) 2014 The Android Open Source Project
+ *
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ManagedProfileTest.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/CrossProfileIntentFilterTest.java
similarity index 68%
rename from hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ManagedProfileTest.java
rename to hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/CrossProfileIntentFilterTest.java
index 91f2046..15123d4 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ManagedProfileTest.java
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/CrossProfileIntentFilterTest.java
@@ -16,29 +16,33 @@
package com.android.cts.managedprofile;
import static com.android.cts.managedprofile.BaseManagedProfileTest.ADMIN_RECEIVER_COMPONENT;
+import static com.google.common.truth.Truth.assertThat;
import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
import android.test.ActivityInstrumentationTestCase2;
import androidx.test.InstrumentationRegistry;
+import java.util.List;
+
/**
* Test for {@link DevicePolicyManager#addCrossProfileIntentFilter} API.
*
- * Note that it expects that there is an activity responding to {@code PrimaryUserActivity.ACTION}
- * in the primary profile, one to {@code ManagedProfileActivity.ACTION} in the secondary profile,
- * and one to {@code AllUsersActivity.ACTION} in both profiles.
+ * <p>Note that it expects that there is an activity responding to {@code PrimaryUserActivity
+ * .ACTION} in the primary profile, one to {@code ManagedProfileActivity.ACTION} in the secondary
+ * profile, and one to {@code AllUsersActivity.ACTION} in both profiles.
*/
-public class ManagedProfileTest extends ActivityInstrumentationTestCase2<TestActivity> {
+public class CrossProfileIntentFilterTest extends ActivityInstrumentationTestCase2<TestActivity> {
private PackageManager mPackageManager;
private DevicePolicyManager mDevicePolicyManager;
- public ManagedProfileTest() {
+ public CrossProfileIntentFilterTest() {
super(TestActivity.class);
}
@@ -65,8 +69,11 @@
testIntentFilter.addAction(PrimaryUserActivity.ACTION);
mDevicePolicyManager.addCrossProfileIntentFilter(ADMIN_RECEIVER_COMPONENT,
testIntentFilter, DevicePolicyManager.FLAG_PARENT_CAN_ACCESS_MANAGED);
- assertEquals(1, mPackageManager.queryIntentActivities(
- new Intent(PrimaryUserActivity.ACTION), /* flags = */ 0).size());
+ final List<ResolveInfo> activities =
+ mPackageManager.queryIntentActivities(
+ new Intent(PrimaryUserActivity.ACTION), /* flags = */ 0);
+ assertThat(activities).hasSize(1);
+ assertThat(activitiesIncludeCrossProfileIntentForwarderActivity(activities)).isTrue();
mDevicePolicyManager.clearCrossProfileIntentFilters(ADMIN_RECEIVER_COMPONENT);
@@ -85,8 +92,11 @@
mDevicePolicyManager.addCrossProfileIntentFilter(ADMIN_RECEIVER_COMPONENT,
testIntentFilter, DevicePolicyManager.FLAG_PARENT_CAN_ACCESS_MANAGED);
- assertEquals(1, mPackageManager.queryIntentActivities(
- new Intent(PrimaryUserActivity.ACTION), /* flags = */ 0).size());
+ final List<ResolveInfo> activities =
+ mPackageManager.queryIntentActivities(
+ new Intent(PrimaryUserActivity.ACTION), /* flags = */ 0);
+ assertThat(activities).hasSize(1);
+ assertThat(activitiesIncludeCrossProfileIntentForwarderActivity(activities)).isTrue();
getActivity().startActivity(PrimaryUserActivity.ACTION);
assertTrue(getActivity().checkActivityStarted());
}
@@ -100,13 +110,16 @@
mDevicePolicyManager.addCrossProfileIntentFilter(ADMIN_RECEIVER_COMPONENT,
testIntentFilter, DevicePolicyManager.FLAG_PARENT_CAN_ACCESS_MANAGED);
- assertEquals(2, mPackageManager.queryIntentActivities(
- new Intent(AllUsersActivity.ACTION), /* flags = */ 0).size());
+ final List<ResolveInfo> activities =
+ mPackageManager.queryIntentActivities(
+ new Intent(AllUsersActivity.ACTION), /* flags = */ 0);
+ assertThat(activities).hasSize(2);
+ assertThat(activitiesIncludeCrossProfileIntentForwarderActivity(activities)).isTrue();
// If we used startActivity(), the user would have a disambiguation dialog presented which
// requires human intervention, so we won't be testing like that
}
- public void testAddCrossProfileIntentFilter_managed() {
+ public void testAddCrossProfileIntentFilter_work() {
assertEquals(1, mPackageManager.queryIntentActivities(
new Intent(ManagedProfileActivity.ACTION), /* flags = */ 0).size());
@@ -116,9 +129,22 @@
testIntentFilter, DevicePolicyManager.FLAG_PARENT_CAN_ACCESS_MANAGED);
// We should still be resolving in the profile
- assertEquals(1, mPackageManager.queryIntentActivities(
- new Intent(ManagedProfileActivity.ACTION), /* flags = */ 0).size());
+ final List<ResolveInfo> activities =
+ mPackageManager.queryIntentActivities(
+ new Intent(ManagedProfileActivity.ACTION), /* flags = */ 0);
+ assertThat(activities).hasSize(1);
+ assertThat(activitiesIncludeCrossProfileIntentForwarderActivity(activities)).isFalse();
getActivity().startActivity(ManagedProfileActivity.ACTION);
assertTrue(getActivity().checkActivityStarted());
}
+
+ private boolean activitiesIncludeCrossProfileIntentForwarderActivity(
+ List<ResolveInfo> activities) {
+ for (ResolveInfo activity : activities) {
+ if (activity.isCrossProfileIntentForwarderActivity()) {
+ return true;
+ }
+ }
+ return false;
+ }
}
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ParentProfileTest.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ParentProfileTest.java
index 54edd96..69bc1ac 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ParentProfileTest.java
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ParentProfileTest.java
@@ -94,6 +94,8 @@
.add("isApplicationHidden")
.add("setScreenCaptureDisabled")
.add("getScreenCaptureDisabled")
+ .add("getAccountTypesWithManagementDisabled")
+ .add("setAccountManagementDisabled")
.build();
private static final String LOG_TAG = "ParentProfileTest";
diff --git a/hostsidetests/devicepolicy/app/SuspensionChecker/src/com/android/cts/suspensionchecker/ActivityLaunchTest.java b/hostsidetests/devicepolicy/app/SuspensionChecker/src/com/android/cts/suspensionchecker/ActivityLaunchTest.java
index 8b6e354..55a361e 100644
--- a/hostsidetests/devicepolicy/app/SuspensionChecker/src/com/android/cts/suspensionchecker/ActivityLaunchTest.java
+++ b/hostsidetests/devicepolicy/app/SuspensionChecker/src/com/android/cts/suspensionchecker/ActivityLaunchTest.java
@@ -18,12 +18,17 @@
import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.fail;
+
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.concurrent.TimeUnit;
+
/**
* Class to test whether activities from this package can be launched and not suspended.
*/
@@ -40,4 +45,17 @@
assertThat(LaunchCheckingActivity.checkLaunch(InstrumentationRegistry.getContext()))
.isFalse();
}
+
+ @Test
+ public void testWaitForActivityNotLaunchable() throws Exception {
+ // Wait up to 1 minute checking if the app is not launchable anymore.
+ final long deadline = System.nanoTime() + TimeUnit.MINUTES.toNanos(1);
+ while (System.nanoTime() < deadline) {
+ if (!LaunchCheckingActivity.checkLaunch(InstrumentationRegistry.getContext())) {
+ return;
+ }
+ Thread.sleep(1000);
+ }
+ fail("Activity is still launchable");
+ }
}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
index 54e9497..a6b92a6 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
@@ -74,6 +74,9 @@
@RunWith(DeviceJUnit4ClassRunner.class)
public abstract class BaseDevicePolicyTest extends BaseHostJUnit4Test {
+ //The maximum time to wait for user to be unlocked.
+ private static final long USER_UNLOCK_TIMEOUT_NANO = 30_000_000_000L;
+ private static final String USER_STATE_UNLOCKED = "RUNNING_UNLOCKED";
@Option(
name = "skip-device-admin-feature-check",
description = "Flag that allows to skip the check for android.software.device_admin "
@@ -227,6 +230,18 @@
executeShellCommand("input keyevent KEYCODE_HOME");
}
+ void waitForUserUnlock(int userId) throws Exception {
+ final String command = String.format("am get-started-user-state %d", userId);
+ final long deadline = System.nanoTime() + USER_UNLOCK_TIMEOUT_NANO;
+ while (System.nanoTime() <= deadline) {
+ if (getDevice().executeShellCommand(command).startsWith(USER_STATE_UNLOCKED)) {
+ return;
+ }
+ Thread.sleep(1000);
+ }
+ fail("User is not unlocked.");
+ }
+
@After
public void tearDown() throws Exception {
// reset the package verifier setting to its original value
@@ -985,4 +1000,15 @@
getDevice().pushFile(file, TEST_UPDATE_LOCATION + "/" + fileName);
file.delete();
}
+
+ boolean hasService(String service) {
+ String command = "service check " + service;
+ try {
+ String commandOutput = getDevice().executeShellCommand(command);
+ return !commandOutput.contains("not found");
+ } catch (Exception e) {
+ CLog.w("Exception running '" + command + "': " + e);
+ return false;
+ }
+ }
}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseManagedProfileTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseManagedProfileTest.java
index 2a82338..f9cf486 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseManagedProfileTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseManagedProfileTest.java
@@ -41,9 +41,6 @@
private static final String MANAGED_PROFILE_APK = "CtsManagedProfileApp.apk";
private static final String NOTIFICATION_PKG =
"com.android.cts.managedprofiletests.notificationsender";
- //The maximum time to wait for user to be unlocked.
- private static final long USER_UNLOCK_TIMEOUT_NANO = 30_000_000_000L;
- private static final String USER_STATE_UNLOCKED = "RUNNING_UNLOCKED";
protected int mParentUserId;
// ID of the profile we'll create. This will always be a profile of the parent.
protected int mProfileUserId;
@@ -70,22 +67,10 @@
installAppAsUser(MANAGED_PROFILE_APK, USER_ALL);
setProfileOwnerOrFail(MANAGED_PROFILE_PKG + "/" + ADMIN_RECEIVER_TEST_CLASS,
mProfileUserId);
- waitForUserUnlock();
+ waitForUserUnlock(mProfileUserId);
}
}
- void waitForUserUnlock() throws Exception {
- final String command = String.format("am get-started-user-state %d", mProfileUserId);
- final long deadline = System.nanoTime() + USER_UNLOCK_TIMEOUT_NANO;
- while (System.nanoTime() <= deadline) {
- if (getDevice().executeShellCommand(command).startsWith(USER_STATE_UNLOCKED)) {
- return;
- }
- Thread.sleep(100);
- }
- fail("Profile user is not unlocked.");
- }
-
@Override
public void tearDown() throws Exception {
if (mHasFeature) {
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/CrossProfileAppsHostSideTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/CrossProfileAppsHostSideTest.java
index eff3ce7..d427645 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/CrossProfileAppsHostSideTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/CrossProfileAppsHostSideTest.java
@@ -7,17 +7,25 @@
import static com.android.cts.devicepolicy.metrics.DevicePolicyEventLogVerifier.assertMetricsLogged;
import static com.android.cts.devicepolicy.metrics.DevicePolicyEventLogVerifier.isStatsdEnabled;
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.fail;
+
import android.platform.test.annotations.FlakyTest;
import android.platform.test.annotations.LargeTest;
import com.android.cts.devicepolicy.metrics.DevicePolicyEventWrapper;
import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.result.InputStreamSource;
+import com.android.tradefed.util.StreamUtil;
import org.junit.Test;
import java.io.FileNotFoundException;
import java.util.Collections;
import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import javax.annotation.Nullable;
@@ -158,6 +166,33 @@
@LargeTest
@Test
+ public void testStartActivityIntent_sameTaskByDefault() throws Exception {
+ if (!mHasManagedUserFeature) {
+ return;
+ }
+ getDevice().clearLogcat();
+ verifyCrossProfileAppsApi(
+ mProfileId,
+ mPrimaryUserId,
+ START_ACTIVITY_TEST_CLASS,
+ "testStartActivityIntent_sameTaskByDefault");
+ assertThat(findTaskId("CrossProfileSameTaskLauncherActivity"))
+ .isEqualTo(findTaskId("NonMainActivity"));
+ }
+
+ private int findTaskId(String className) throws Exception {
+ final Matcher matcher =
+ Pattern.compile(className + "#taskId#" + "(.*?)" + "#").matcher(readLogcat());
+ boolean isFound = matcher.find();
+ if (!isFound) {
+ fail("Task not found for " + className);
+ return -1;
+ }
+ return Integer.parseInt(matcher.group(1));
+ }
+
+ @LargeTest
+ @Test
public void testPrimaryUserToSecondaryUser() throws Exception {
if (!mCanTestMultiUser) {
return;
@@ -257,4 +292,14 @@
return Collections.singletonMap(PARAM_TARGET_USER,
Integer.toString(getUserSerialNumber(targetUserId)));
}
+
+ private String readLogcat() throws Exception {
+ getDevice().stopLogcat();
+ final String logcat;
+ try (InputStreamSource logcatStream = getDevice().getLogcat()) {
+ logcat = StreamUtil.getStringFromSource(logcatStream);
+ }
+ getDevice().startLogcat();
+ return logcat;
+ }
}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
index 3a41d0f..09c05aa 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
@@ -2095,6 +2095,11 @@
runDeviceTestsAsUser(DEVICE_ADMIN_PKG, className, testName, mUserId);
}
+ protected void executeDeviceTestMethod(String className, String testName,
+ Map<String, String> params) throws Exception {
+ runDeviceTestsAsUser(DEVICE_ADMIN_PKG, className, testName, mUserId, params);
+ }
+
private void installAppPermissionAppAsUser()
throws FileNotFoundException, DeviceNotAvailableException {
installAppAsUser(PERMISSIONS_APP_APK, false, mUserId);
@@ -2312,19 +2317,4 @@
getDevice().executeShellCommand(
restricted ? RESTRICT_BACKGROUND_ON_CMD : RESTRICT_BACKGROUND_OFF_CMD);
}
-
- // TODO: copied from RequiredServiceRule, which is on compatibility-device-util
- // (and we use compatibility-host-util)
- public boolean hasService(String service) {
- // TODO: ideally should call SystemServiceManager directly, but we would need to open
- // some @Testing APIs for that.
- String command = "service check " + service;
- try {
- String commandOutput = getDevice().executeShellCommand(command);
- return !commandOutput.contains("not found");
- } catch (Exception e) {
- CLog.w("Exception running '" + command + "': " + e);
- return false;
- }
- }
}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
index a4e8877..e45e647 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
@@ -78,9 +78,6 @@
private static final String TEST_APP_PKG = "android.packageinstaller.emptytestapp.cts";
private static final String TEST_APP_LOCATION = "/data/local/tmp/cts/packageinstaller/";
- private static final String ARG_SECURITY_LOGGING_BATCH_NUMBER = "batchNumber";
- private static final int SECURITY_EVENTS_BATCH_SIZE = 100;
-
private static final String ARG_NETWORK_LOGGING_BATCH_COUNT = "batchCount";
private static final String LAUNCHER_TESTS_HAS_LAUNCHER_ACTIVITY_APK =
@@ -456,97 +453,6 @@
}
@Test
- public void testSecurityLoggingWithTwoUsers() throws Exception {
- if (!mHasFeature || !canCreateAdditionalUsers(1)) {
- return;
- }
-
- final int userId = createUser();
- try {
- // The feature can be enabled, but in a "paused" state. Attempting to retrieve logs
- // should throw security exception.
- executeDeviceTestMethod(".SecurityLoggingTest", "testEnablingSecurityLogging");
- executeDeviceTestMethod(".SecurityLoggingTest",
- "testRetrievingSecurityLogsThrowsSecurityException");
- executeDeviceTestMethod(".SecurityLoggingTest",
- "testRetrievingPreviousSecurityLogsThrowsSecurityException");
- } finally {
- removeUser(userId);
- executeDeviceTestMethod(".SecurityLoggingTest", "testDisablingSecurityLogging");
- }
- }
-
- @FlakyTest(bugId = 137093665)
- @Test
- public void testSecurityLoggingWithSingleUser() throws Exception {
- if (!mHasFeature) {
- return;
- }
- // Backup stay awake setting because testGenerateLogs() will turn it off.
- final String stayAwake = getDevice().getSetting("global", "stay_on_while_plugged_in");
- try {
- // Turn logging on.
- executeDeviceTestMethod(".SecurityLoggingTest", "testEnablingSecurityLogging");
- // Reboot to ensure ro.device_owner is set to true in logd and logging is on.
- rebootAndWaitUntilReady();
-
- // Generate various types of events on device side and check that they are logged.
- executeDeviceTestMethod(".SecurityLoggingTest", "testGenerateLogs");
- getDevice().executeShellCommand("dpm force-security-logs");
- executeDeviceTestMethod(".SecurityLoggingTest", "testVerifyGeneratedLogs");
-
- // Reboot the device, so the security event ids are reset.
- rebootAndWaitUntilReady();
-
- // Verify event ids are consistent across a consecutive batch.
- for (int batchNumber = 0; batchNumber < 3; batchNumber++) {
- generateDummySecurityLogs();
- getDevice().executeShellCommand("dpm force-security-logs");
- executeDeviceTestMethod(".SecurityLoggingTest", "testVerifyLogIds",
- Collections.singletonMap(ARG_SECURITY_LOGGING_BATCH_NUMBER,
- Integer.toString(batchNumber)));
- }
-
- // Immediately attempting to fetch events again should fail.
- executeDeviceTestMethod(".SecurityLoggingTest",
- "testSecurityLoggingRetrievalRateLimited");
- // Turn logging off.
- executeDeviceTestMethod(".SecurityLoggingTest", "testDisablingSecurityLogging");
- } finally {
- // Restore stay awake setting.
- if (stayAwake != null) {
- getDevice().setSetting("global", "stay_on_while_plugged_in", stayAwake);
- }
- }
- }
-
- @Test
- public void testSecurityLoggingEnabledLogged() throws Exception {
- if (!mHasFeature || !isStatsdEnabled(getDevice())) {
- return;
- }
- assertMetricsLogged(getDevice(), () -> {
- executeDeviceTestMethod(".SecurityLoggingTest", "testEnablingSecurityLogging");
- executeDeviceTestMethod(".SecurityLoggingTest", "testDisablingSecurityLogging");
- }, new DevicePolicyEventWrapper.Builder(EventId.SET_SECURITY_LOGGING_ENABLED_VALUE)
- .setAdminPackageName(DEVICE_OWNER_PKG)
- .setBoolean(true)
- .build(),
- new DevicePolicyEventWrapper.Builder(EventId.SET_SECURITY_LOGGING_ENABLED_VALUE)
- .setAdminPackageName(DEVICE_OWNER_PKG)
- .setBoolean(false)
- .build());
-
- }
-
- private void generateDummySecurityLogs() throws DeviceNotAvailableException {
- // Trigger security events of type TAG_ADB_SHELL_CMD.
- for (int i = 0; i < SECURITY_EVENTS_BATCH_SIZE; i++) {
- getDevice().executeShellCommand("echo just_testing_" + i);
- }
- }
-
- @Test
public void testNetworkLoggingWithTwoUsers() throws Exception {
if (!mHasFeature || !canCreateAdditionalUsers(1)) {
return;
@@ -789,14 +695,6 @@
}
@Test
- public void testRequestSetLocationProviderAllowed() throws Exception {
- if (!mHasFeature) {
- return;
- }
- executeDeviceOwnerTest("RequestSetLocationProviderAllowedTest");
- }
-
- @Test
public void testDeviceOwnerProvisioning() throws Exception {
if (!mHasFeature) {
return;
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileCrossProfileTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileCrossProfileTest.java
index 6e7477b..450dc44 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileCrossProfileTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileCrossProfileTest.java
@@ -73,7 +73,7 @@
if (isStatsdEnabled(getDevice())) {
assertMetricsLogged(getDevice(), () -> {
runDeviceTestsAsUser(
- MANAGED_PROFILE_PKG, MANAGED_PROFILE_PKG + ".ManagedProfileTest",
+ MANAGED_PROFILE_PKG, MANAGED_PROFILE_PKG + ".CrossProfileIntentFilterTest",
"testAddCrossProfileIntentFilter_all", mProfileUserId);
}, new DevicePolicyEventWrapper.Builder(ADD_CROSS_PROFILE_INTENT_FILTER_VALUE)
.setAdminPackageName(MANAGED_PROFILE_PKG)
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfilePasswordTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfilePasswordTest.java
index edafb01..736b15d 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfilePasswordTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfilePasswordTest.java
@@ -180,7 +180,7 @@
try {
rebootAndWaitUntilReady();
verifyUserCredential(password, mPrimaryUserId);
- waitForUserUnlock();
+ waitForUserUnlock(mProfileUserId);
installAppAsUser(SIMPLE_APP_APK, mProfileUserId);
runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".SanityTest", mProfileUserId);
} finally {
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerTest.java
index d28ecd9..794cb06 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerTest.java
@@ -37,6 +37,7 @@
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -49,6 +50,9 @@
private static final String DELEGATION_NETWORK_LOGGING = "delegation-network-logging";
+ private static final String ARG_SECURITY_LOGGING_BATCH_NUMBER = "batchNumber";
+ private static final int SECURITY_EVENTS_BATCH_SIZE = 100;
+
@Override
public void setUp() throws Exception {
super.setUp();
@@ -302,6 +306,97 @@
.build());
}
+ @FlakyTest(bugId = 137093665)
+ @Test
+ public void testSecurityLoggingWithSingleUser() throws Exception {
+ if (!mHasFeature) {
+ return;
+ }
+ // Backup stay awake setting because testGenerateLogs() will turn it off.
+ final String stayAwake = getDevice().getSetting("global", "stay_on_while_plugged_in");
+ try {
+ // Turn logging on.
+ executeDeviceTestMethod(".SecurityLoggingTest", "testEnablingSecurityLogging");
+ // Reboot to ensure ro.device_owner is set to true in logd and logging is on.
+ rebootAndWaitUntilReady();
+ waitForUserUnlock(mUserId);
+
+ // Generate various types of events on device side and check that they are logged.
+ executeDeviceTestMethod(".SecurityLoggingTest", "testGenerateLogs");
+ getDevice().executeShellCommand("whoami"); // Generate adb command securty event
+ getDevice().executeShellCommand("dpm force-security-logs");
+ executeDeviceTestMethod(".SecurityLoggingTest", "testVerifyGeneratedLogs");
+
+ // Reboot the device, so the security event ids are reset.
+ rebootAndWaitUntilReady();
+
+ // Verify event ids are consistent across a consecutive batch.
+ for (int batchNumber = 0; batchNumber < 3; batchNumber++) {
+ generateDummySecurityLogs();
+ getDevice().executeShellCommand("dpm force-security-logs");
+ executeDeviceTestMethod(".SecurityLoggingTest", "testVerifyLogIds",
+ Collections.singletonMap(ARG_SECURITY_LOGGING_BATCH_NUMBER,
+ Integer.toString(batchNumber)));
+ }
+
+ // Immediately attempting to fetch events again should fail.
+ executeDeviceTestMethod(".SecurityLoggingTest",
+ "testSecurityLoggingRetrievalRateLimited");
+ } finally {
+ // Turn logging off.
+ executeDeviceTestMethod(".SecurityLoggingTest", "testDisablingSecurityLogging");
+ // Restore stay awake setting.
+ if (stayAwake != null) {
+ getDevice().setSetting("global", "stay_on_while_plugged_in", stayAwake);
+ }
+ }
+ }
+
+ @Test
+ public void testSecurityLoggingEnabledLogged() throws Exception {
+ if (!mHasFeature || !isStatsdEnabled(getDevice())) {
+ return;
+ }
+ assertMetricsLogged(getDevice(), () -> {
+ executeDeviceTestMethod(".SecurityLoggingTest", "testEnablingSecurityLogging");
+ executeDeviceTestMethod(".SecurityLoggingTest", "testDisablingSecurityLogging");
+ }, new DevicePolicyEventWrapper.Builder(EventId.SET_SECURITY_LOGGING_ENABLED_VALUE)
+ .setAdminPackageName(DEVICE_ADMIN_PKG)
+ .setBoolean(true)
+ .build(),
+ new DevicePolicyEventWrapper.Builder(EventId.SET_SECURITY_LOGGING_ENABLED_VALUE)
+ .setAdminPackageName(DEVICE_ADMIN_PKG)
+ .setBoolean(false)
+ .build());
+ }
+
+ @Test
+ public void testSecurityLoggingWithTwoUsers() throws Exception {
+ if (!mHasFeature || !canCreateAdditionalUsers(1)) {
+ return;
+ }
+
+ final int userId = createUser();
+ try {
+ // The feature can be enabled, but in a "paused" state. Attempting to retrieve logs
+ // should throw security exception.
+ executeDeviceTestMethod(".SecurityLoggingTest", "testEnablingSecurityLogging");
+ executeDeviceTestMethod(".SecurityLoggingTest",
+ "testRetrievingSecurityLogsThrowsSecurityException");
+ executeDeviceTestMethod(".SecurityLoggingTest",
+ "testRetrievingPreviousSecurityLogsThrowsSecurityException");
+ } finally {
+ removeUser(userId);
+ executeDeviceTestMethod(".SecurityLoggingTest", "testDisablingSecurityLogging");
+ }
+ }
+
+ private void generateDummySecurityLogs() throws Exception {
+ // Trigger security events of type TAG_ADB_SHELL_CMD.
+ for (int i = 0; i < SECURITY_EVENTS_BATCH_SIZE; i++) {
+ getDevice().executeShellCommand("echo just_testing_" + i);
+ }
+ }
private int createSecondaryUserAsProfileOwner() throws Exception {
final int userId = createUser();
installAppAsUser(INTENT_RECEIVER_APK, userId);
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedManagedProfileOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedManagedProfileOwnerTest.java
index d6e5a9e..bb75420 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedManagedProfileOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedManagedProfileOwnerTest.java
@@ -273,7 +273,8 @@
return;
}
runDeviceTestsAsUser(DEVICE_ADMIN_PKG, ".KeyguardDisabledFeaturesTest",
- "testSetKeyguardDisabledFeatures_onParentSilentIgnore", mUserId);
+ "testSetKeyguardDisabledFeatures_onParentSilentIgnoreWhenCallerIsNotOrgOwnedPO",
+ mUserId);
}
@FlakyTest
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/OrgOwnedProfileOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/OrgOwnedProfileOwnerTest.java
index 6643eef..9973fac 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/OrgOwnedProfileOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/OrgOwnedProfileOwnerTest.java
@@ -52,8 +52,16 @@
private static final String DUMMY_IME_APK = "DummyIme.apk";
private static final String DUMMY_IME_PKG = "com.android.cts.dummyime";
private static final String DUMMY_IME_COMPONENT = DUMMY_IME_PKG + "/.DummyIme";
+ private static final String DUMMY_LAUNCHER_APK = "DummyLauncher.apk";
+ private static final String DUMMY_LAUNCHER_COMPONENT =
+ "com.android.cts.dummylauncher/android.app.Activity";
+ private static final String QUIET_MODE_TOGGLE_ACTIVITY =
+ "com.android.cts.dummylauncher/.QuietModeToggleActivity";
+ private static final String EXTRA_QUIET_MODE_STATE =
+ "com.android.cts.dummyactivity.QUIET_MODE_STATE";
+ public static final String SUSPENSION_CHECKER_CLASS =
+ "com.android.cts.suspensionchecker.ActivityLaunchTest";
- private int mParentUserId = -1;
protected int mUserId;
private boolean mHasProfileToRemove = true;
private boolean mHasSecondaryProfileToRemove = false;
@@ -69,14 +77,13 @@
if (mHasFeature) {
removeTestUsers();
- mParentUserId = mPrimaryUserId;
createManagedProfile();
}
}
private void createManagedProfile() throws Exception {
- mUserId = createManagedProfile(mParentUserId);
- switchUser(mParentUserId);
+ mUserId = createManagedProfile(mPrimaryUserId);
+ switchUser(mPrimaryUserId);
startUserAndWait(mUserId);
installAppAsUser(DEVICE_ADMIN_APK, mUserId);
@@ -125,13 +132,14 @@
mHasProfileToRemove = false;
try {
- installAppAsUser(DEVICE_ADMIN_APK, mParentUserId);
- setDeviceOwner(DEVICE_ADMIN_COMPONENT_FLATTENED, mParentUserId, /*expectFailure*/false);
+ installAppAsUser(DEVICE_ADMIN_APK, mPrimaryUserId);
+ assertTrue(setDeviceOwner(DEVICE_ADMIN_COMPONENT_FLATTENED,
+ mPrimaryUserId, /*expectFailure*/false));
mHasSecondaryProfileToRemove = true;
runDeviceTestsAsUser(DEVICE_ADMIN_PKG, ".LockScreenInfoTest", "testLockInfoIsNull",
- mParentUserId);
+ mPrimaryUserId);
} finally {
- removeAdmin(DEVICE_ADMIN_COMPONENT_FLATTENED, mParentUserId);
+ removeAdmin(DEVICE_ADMIN_COMPONENT_FLATTENED, mPrimaryUserId);
getDevice().uninstallPackage(DEVICE_ADMIN_PKG);
}
}
@@ -265,6 +273,44 @@
.build());
}
+ @FlakyTest(bugId = 137093665)
+ @Test
+ public void testSecurityLogging() throws Exception {
+ if (!mHasFeature) {
+ return;
+ }
+ // Backup stay awake setting because testGenerateLogs() will turn it off.
+ final String stayAwake = getDevice().getSetting("global", "stay_on_while_plugged_in");
+ try {
+ // Turn logging on.
+ runDeviceTestsAsUser(DEVICE_ADMIN_PKG, ".SecurityLoggingTest",
+ "testEnablingSecurityLogging", mUserId);
+ // Reboot to ensure ro.device_owner is set to true in logd and logging is on.
+ rebootAndWaitUntilReady();
+ waitForUserUnlock(mUserId);
+
+ // Generate various types of events on device side and check that they are logged.
+ runDeviceTestsAsUser(DEVICE_ADMIN_PKG,".SecurityLoggingTest",
+ "testGenerateLogs", mUserId);
+ getDevice().executeShellCommand("whoami"); // Generate adb command securty event
+ getDevice().executeShellCommand("dpm force-security-logs");
+ runDeviceTestsAsUser(DEVICE_ADMIN_PKG, ".SecurityLoggingTest",
+ "testVerifyGeneratedLogs", mUserId);
+
+ // Immediately attempting to fetch events again should fail.
+ runDeviceTestsAsUser(DEVICE_ADMIN_PKG, ".SecurityLoggingTest",
+ "testSecurityLoggingRetrievalRateLimited", mUserId);
+ } finally {
+ // Turn logging off.
+ runDeviceTestsAsUser(DEVICE_ADMIN_PKG, ".SecurityLoggingTest",
+ "testDisablingSecurityLogging", mUserId);
+ // Restore stay awake setting.
+ if (stayAwake != null) {
+ getDevice().setSetting("global", "stay_on_while_plugged_in", stayAwake);
+ }
+ }
+ }
+
private void failToCreateUser() throws Exception {
String command ="pm create-user " + "TestUser_" + System.currentTimeMillis();
String commandOutput = getDevice().executeShellCommand(command);
@@ -274,25 +320,6 @@
assertEquals("Error:", tokens[0]);
}
- protected int createUser() throws Exception {
- String command ="pm create-user " + "TestUser_" + System.currentTimeMillis();
- String commandOutput = getDevice().executeShellCommand(command);
-
- String[] tokens = commandOutput.split("\\s+");
- assertTrue(tokens.length > 0);
- assertEquals("Success:", tokens[0]);
- int userId = Integer.parseInt(tokens[tokens.length-1]);
- startUser(userId);
- return userId;
- }
-
- protected void removeUser(int userId) throws Exception {
- if (listUsers().contains(userId) && userId != USER_SYSTEM) {
- String command = "am stop-user -w -f " + userId;
- getDevice().executeShellCommand(command);
- }
- }
-
@Test
public void testSetTime() throws Exception {
if (!mHasFeature) {
@@ -389,7 +416,7 @@
}
@Test
- public void testApplicationHidden() throws Exception {
+ public void testApplicationHiddenParent() throws Exception {
if (!mHasFeature) {
return;
}
@@ -438,6 +465,10 @@
@Test
public void testPersonalAppsSuspensionIme() throws Exception {
+ if (!mHasFeature) {
+ return;
+ }
+
installAppAsUser(DEVICE_ADMIN_APK, mPrimaryUserId);
setupIme(mPrimaryUserId, DUMMY_IME_APK, DUMMY_IME_COMPONENT);
setPersonalAppsSuspended(true);
@@ -446,6 +477,24 @@
setPersonalAppsSuspended(false);
}
+ @Test
+ public void testCanRestrictAccountManagementOnParentProfile() throws Exception {
+ if (!mHasFeature) {
+ return;
+ }
+
+ runDeviceTestsAsUser(DEVICE_ADMIN_PKG, ".AccountManagementParentTest",
+ "testSetAccountManagementDisabledOnParent", mUserId);
+ installAppAsUser(DEVICE_ADMIN_APK, mPrimaryUserId);
+ try {
+ runDeviceTestsAsUser(DEVICE_ADMIN_PKG, ".AccountManagementParentTest",
+ "testAccountManagementDisabled", mPrimaryUserId);
+ } finally {
+ runDeviceTestsAsUser(DEVICE_ADMIN_PKG, ".AccountManagementParentTest",
+ "testEnableAccountManagement", mUserId);
+ }
+ }
+
private void setupIme(int userId, String imeApk, String imePackage) throws Exception {
installAppAsUser(imeApk, userId);
// Wait until IMS service is registered by the system.
@@ -468,7 +517,7 @@
private void assertCanStartPersonalApp(String packageName, boolean canStart)
throws DeviceNotAvailableException {
runDeviceTestsAsUser(packageName, "com.android.cts.suspensionchecker.ActivityLaunchTest",
- canStart ? "testCanStartActivity" : "testCannotStartActivity", mParentUserId);
+ canStart ? "testCanStartActivity" : "testCannotStartActivity", mPrimaryUserId);
}
@Test
@@ -502,17 +551,6 @@
userId, /* expectFailure */ false));
}
- private boolean hasService(String service) {
- String command = "service check " + service;
- try {
- String commandOutput = getDevice().executeShellCommand(command);
- return !commandOutput.contains("not found");
- } catch (Exception e) {
- LogUtil.CLog.w("Exception running '" + command + "': " + e);
- return false;
- }
- }
-
@Test
public void testSetPersonalAppsSuspendedLogged() throws Exception {
if (!mHasFeature|| !isStatsdEnabled(getDevice())) {
@@ -550,4 +588,47 @@
.setTimePeriod(0)
.build());
}
+
+ @Test
+ public void testWorkProfileMaximumTimeOff() throws Exception {
+ if (!mHasFeature) {
+ return;
+ }
+ installAppAsUser(DEVICE_ADMIN_APK, mPrimaryUserId);
+ runDeviceTestsAsUser(DEVICE_ADMIN_PKG, ".PersonalAppsSuspensionTest",
+ "testSetManagedProfileMaximumTimeOff1Sec", mUserId);
+
+ final String defaultLauncher = getDefaultLauncher();
+ try {
+ installAppAsUser(DUMMY_LAUNCHER_APK, true, true, mPrimaryUserId);
+ setAndStartLauncher(DUMMY_LAUNCHER_COMPONENT);
+ toggleQuietMode(true);
+ // Verify that at some point personal app becomes impossible to launch.
+ runDeviceTestsAsUser(DEVICE_ADMIN_PKG, SUSPENSION_CHECKER_CLASS,
+ "testWaitForActivityNotLaunchable", mPrimaryUserId);
+ toggleQuietMode(false);
+ // Ensure the profile is properly started before wipe broadcast is sent in teardown.
+ waitForUserUnlock(mUserId);
+ runDeviceTestsAsUser(DEVICE_ADMIN_PKG, ".PersonalAppsSuspensionTest",
+ "testPersonalAppsSuspendedByTimeout", mUserId);
+ } finally {
+ setAndStartLauncher(defaultLauncher);
+ }
+ }
+
+ private void toggleQuietMode(boolean quietModeEnable) throws Exception {
+ final String str = String.format("am start-activity -n %s --ez %s %s",
+ QUIET_MODE_TOGGLE_ACTIVITY, EXTRA_QUIET_MODE_STATE, quietModeEnable);
+ executeShellCommand(str);
+ }
+
+ private void setAndStartLauncher(String component) throws Exception {
+ String output = getDevice().executeShellCommand(String.format(
+ "cmd package set-home-activity --user %d %s", mPrimaryUserId, component));
+ assertTrue("failed to set home activity", output.contains("Success"));
+ output = getDevice().executeShellCommand(
+ String.format("cmd shortcut clear-default-launcher --user %d", mPrimaryUserId));
+ assertTrue("failed to clear default launcher", output.contains("Success"));
+ executeShellCommand("am start -W -n " + component);
+ }
}
diff --git a/hostsidetests/hdmicec/OWNERS b/hostsidetests/hdmicec/OWNERS
index 254c58d..e7f5c32 100644
--- a/hostsidetests/hdmicec/OWNERS
+++ b/hostsidetests/hdmicec/OWNERS
@@ -1,3 +1,3 @@
-# Bug component: 141606867
+# Bug component: 114946
nchalko@google.com
amyjojo@google.com
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/HdmiCecClientWrapper.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/HdmiCecClientWrapper.java
index a4bacf4..f9b1899 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/HdmiCecClientWrapper.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/HdmiCecClientWrapper.java
@@ -186,7 +186,6 @@
*/
public void sendUserControlPress(CecDevice source, CecDevice destination,
int keycode, boolean holdKey) throws Exception {
- checkCecClient();
String key = String.format("%02x", keycode);
String command = "tx " + source + destination + ":" +
CecMessage.USER_CONTROL_PRESSED + ":" + key;
@@ -208,6 +207,19 @@
mOutputConsole.flush();
}
+ /**
+ * Sends a series of <UCP> [firstKeycode] from source to destination through the output console
+ * of the cec-communication channel immediately followed by <UCP> [secondKeycode]. No <UCR>
+ * message is sent.
+ */
+ public void sendUserControlInterruptedPressAndHold(CecDevice source, CecDevice destination,
+ int firstKeycode, int secondKeycode, boolean holdKey) throws Exception {
+ sendUserControlPress(source, destination, firstKeycode, holdKey);
+ /* Sleep less than 200ms between press and release */
+ TimeUnit.MILLISECONDS.sleep(100);
+ sendUserControlPress(source, destination, secondKeycode, false);
+ }
+
/** Sends a message to the output console of the cec-client */
public void sendConsoleMessage(String message) throws Exception {
checkCecClient();
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/audio/HdmiCecRemoteControlPassThroughTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/audio/HdmiCecRemoteControlPassThroughTest.java
index a90b09f..9931e26 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/audio/HdmiCecRemoteControlPassThroughTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/audio/HdmiCecRemoteControlPassThroughTest.java
@@ -174,4 +174,45 @@
HdmiCecConstants.CEC_CONTROL_BACK, true);
lookForLog("Long press KEYCODE_BACK");
}
+
+ /**
+ * Test 11.2.13-4
+ * Tests that the device responds correctly to a <User Control Pressed> [firstKeycode] press
+ * and hold operation when interrupted by a <User Control Pressed> [secondKeycode] before a
+ * <User Control Released> [firstKeycode] is sent.
+ */
+ @Test
+ public void cect_11_2_13_4_UserControlInterruptedPressAndHoldWithNoRelease() throws Exception {
+ ITestDevice device = getDevice();
+ // Clear activity
+ device.executeShellCommand(CLEAR_COMMAND);
+ // Clear logcat.
+ device.executeAdbCommand("logcat", "-c");
+ // Start the APK and wait for it to complete.
+ device.executeShellCommand(START_COMMAND);
+ hdmiCecClient.sendUserControlInterruptedPressAndHold(CecDevice.TV,
+ CecDevice.AUDIO_SYSTEM, HdmiCecConstants.CEC_CONTROL_UP,
+ HdmiCecConstants.CEC_CONTROL_BACK, true);
+ lookForLog("Long press KEYCODE_DPAD_UP");
+ hdmiCecClient.sendUserControlInterruptedPressAndHold(CecDevice.TV,
+ CecDevice.AUDIO_SYSTEM, HdmiCecConstants.CEC_CONTROL_DOWN,
+ HdmiCecConstants.CEC_CONTROL_UP, true);
+ lookForLog("Long press KEYCODE_DPAD_DOWN");
+ hdmiCecClient.sendUserControlInterruptedPressAndHold(CecDevice.TV,
+ CecDevice.AUDIO_SYSTEM, HdmiCecConstants.CEC_CONTROL_LEFT,
+ HdmiCecConstants.CEC_CONTROL_DOWN, true);
+ lookForLog("Long press KEYCODE_DPAD_LEFT");
+ hdmiCecClient.sendUserControlInterruptedPressAndHold(CecDevice.TV,
+ CecDevice.AUDIO_SYSTEM, HdmiCecConstants.CEC_CONTROL_RIGHT,
+ HdmiCecConstants.CEC_CONTROL_LEFT, true);
+ lookForLog("Long press KEYCODE_DPAD_RIGHT");
+ hdmiCecClient.sendUserControlInterruptedPressAndHold(CecDevice.TV,
+ CecDevice.AUDIO_SYSTEM, HdmiCecConstants.CEC_CONTROL_SELECT,
+ HdmiCecConstants.CEC_CONTROL_RIGHT, true);
+ lookForLog("Long press KEYCODE_DPAD_CENTER");
+ hdmiCecClient.sendUserControlInterruptedPressAndHold(CecDevice.TV,
+ CecDevice.AUDIO_SYSTEM, HdmiCecConstants.CEC_CONTROL_BACK,
+ HdmiCecConstants.CEC_CONTROL_SELECT, true);
+ lookForLog("Long press KEYCODE_BACK");
+ }
}
diff --git a/hostsidetests/jvmti/base/jni/Android.bp b/hostsidetests/jvmti/base/jni/Android.bp
index 3567373..4940df3 100644
--- a/hostsidetests/jvmti/base/jni/Android.bp
+++ b/hostsidetests/jvmti/base/jni/Android.bp
@@ -17,6 +17,7 @@
srcs: [
"cts_agent.cpp",
+ "cts_logging.cpp",
"tagging.cpp",
"tracking.cpp",
"redefine.cpp",
diff --git a/hostsidetests/jvmti/base/jni/cts_agent.cpp b/hostsidetests/jvmti/base/jni/cts_agent.cpp
index 3f3a836..55f2471 100644
--- a/hostsidetests/jvmti/base/jni/cts_agent.cpp
+++ b/hostsidetests/jvmti/base/jni/cts_agent.cpp
@@ -29,6 +29,7 @@
extern void register_android_jvmti_cts_JvmtiRedefineClassesTest(jvmtiEnv*, JNIEnv*);
extern void register_android_jvmti_cts_JvmtiTaggingTest(jvmtiEnv*, JNIEnv*);
extern void register_android_jvmti_cts_JvmtiTrackingTest(jvmtiEnv*, JNIEnv*);
+extern void register_android_jvmti_cts_JvmtiRunTestBasedTest(jvmtiEnv*, JNIEnv*);
static void InformMainAttach(jvmtiEnv* jenv,
JNIEnv* env,
@@ -42,6 +43,7 @@
register_android_jvmti_cts_JvmtiRedefineClassesTest(jenv, env);
register_android_jvmti_cts_JvmtiTaggingTest(jenv, env);
register_android_jvmti_cts_JvmtiTrackingTest(jenv, env);
+ register_android_jvmti_cts_JvmtiRunTestBasedTest(jenv, env);
// Use JNI to load the class.
ScopedLocalRef<jclass> klass(env, GetClass(jenv, env, class_name, nullptr));
diff --git a/hostsidetests/jvmti/base/jni/cts_logging.cpp b/hostsidetests/jvmti/base/jni/cts_logging.cpp
new file mode 100644
index 0000000..b37e1f9
--- /dev/null
+++ b/hostsidetests/jvmti/base/jni/cts_logging.cpp
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+#include <jni.h>
+#include <jvmti.h>
+
+#include "android-base/logging.h"
+#include "jni_binder.h"
+#include "jvmti_helper.h"
+#include "scoped_local_ref.h"
+#include "test_env.h"
+
+namespace art {
+
+extern "C" JNIEXPORT void JNICALL
+Java_android_jvmti_cts_JvmtiRunTestBasedTest_setupExtraLogging(JNIEnv* env, jclass) {
+ JvmtiErrorToException(env, jvmti_env, jvmti_env->SetVerboseFlag(JVMTI_VERBOSE_OTHER, true));
+}
+
+static JNINativeMethod gMethods[] = {
+ { "setupExtraLogging", "()V",
+ (void*)Java_android_jvmti_cts_JvmtiRunTestBasedTest_setupExtraLogging },
+};
+void register_android_jvmti_cts_JvmtiRunTestBasedTest(jvmtiEnv* jenv, JNIEnv* env) {
+ ScopedLocalRef<jclass> klass(env, GetClass(jenv, env,
+ "android/jvmti/cts/JvmtiRunTestBasedTest", nullptr));
+ if (klass.get() == nullptr) {
+ env->ExceptionClear();
+ return;
+ }
+
+ env->RegisterNatives(klass.get(), gMethods, sizeof(gMethods) / sizeof(JNINativeMethod));
+ if (env->ExceptionCheck()) {
+ env->ExceptionClear();
+ LOG(ERROR) << "Could not register natives for JvmtiRedefineClassesTest class";
+ }
+}
+
+} // namespace art
diff --git a/hostsidetests/jvmti/base/run-test-based-app/src/android/jvmti/cts/JvmtiRunTestBasedTest.java b/hostsidetests/jvmti/base/run-test-based-app/src/android/jvmti/cts/JvmtiRunTestBasedTest.java
index fcb718a..a59fd3f 100644
--- a/hostsidetests/jvmti/base/run-test-based-app/src/android/jvmti/cts/JvmtiRunTestBasedTest.java
+++ b/hostsidetests/jvmti/base/run-test-based-app/src/android/jvmti/cts/JvmtiRunTestBasedTest.java
@@ -18,11 +18,14 @@
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
+import java.io.IOException;
+import java.io.OutputStream;
import java.io.PrintStream;
import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;
import android.content.pm.PackageManager;
+import android.util.Log;
import org.junit.After;
import org.junit.Before;
@@ -36,13 +39,49 @@
private PrintStream oldOut, oldErr;
private ByteArrayOutputStream bufferedOut, bufferedErr;
+ private class TeeLogcatOutputStream extends OutputStream {
+ private OutputStream os;
+ private ByteArrayOutputStream lc_os;
+ public TeeLogcatOutputStream(OutputStream os) {
+ this.lc_os = new ByteArrayOutputStream();
+ this.os = os;
+ }
+ public void write(int b) throws IOException {
+ os.write(b);
+ if (b == (int)'\n') {
+ lc_os.flush();
+ Log.i(mActivity.getPackageName(), "Test Output: " + lc_os.toString());
+ lc_os.reset();
+ } else {
+ lc_os.write(b);
+ }
+ }
+ public void close() throws IOException {
+ flush();
+ os.close();
+ lc_os.close();
+ }
+ public void flush() throws IOException {
+ os.flush();
+ lc_os.flush();
+ }
+ }
+
@Before
public void setUp() throws Exception {
oldOut = System.out;
oldErr = System.err;
+ bufferedOut = new ByteArrayOutputStream();
+ bufferedErr = new ByteArrayOutputStream();
- System.setOut(new PrintStream(bufferedOut = new ByteArrayOutputStream(), true));
- System.setErr(new PrintStream(bufferedErr = new ByteArrayOutputStream(), true));
+ if (doExtraLogging()) {
+ setupExtraLogging();
+ System.setOut(new PrintStream(new TeeLogcatOutputStream(bufferedOut), true));
+ System.setErr(new PrintStream(new TeeLogcatOutputStream(bufferedErr), true));
+ } else {
+ System.setOut(new PrintStream(bufferedOut, true));
+ System.setErr(new PrintStream(bufferedErr, true));
+ }
}
@After
@@ -51,6 +90,16 @@
System.setErr(oldErr);
}
+ private native void setupExtraLogging();
+
+ protected boolean doExtraLogging() throws Exception {
+ return mActivity
+ .getPackageManager()
+ .getApplicationInfo(mActivity.getPackageName(), PackageManager.GET_META_DATA)
+ .metaData
+ .getBoolean("android.jvmti.cts.run_test.extra_logging", /*defaultValue*/false);
+ }
+
protected int getTestNumber() throws Exception {
return mActivity.getPackageManager().getApplicationInfo(mActivity.getPackageName(),
PackageManager.GET_META_DATA).metaData.getInt("android.jvmti.cts.run_test_nr");
diff --git a/hostsidetests/jvmti/run-tests/test-1924/app/AndroidManifest.xml b/hostsidetests/jvmti/run-tests/test-1924/app/AndroidManifest.xml
index 4388209..176ef69 100644
--- a/hostsidetests/jvmti/run-tests/test-1924/app/AndroidManifest.xml
+++ b/hostsidetests/jvmti/run-tests/test-1924/app/AndroidManifest.xml
@@ -21,6 +21,8 @@
<application android:debuggable="true">
<uses-library android:name="android.test.runner" />
<meta-data android:name="android.jvmti.cts.run_test_nr" android:value="1924" />
+ <!-- Perform extra logging to try to get to the bottom of b/144947842 -->
+ <meta-data android:name="android.jvmti.cts.run_test.extra_logging" android:value="true" />
<activity android:name="android.jvmti.JvmtiActivity" >
</activity>
</application>
diff --git a/hostsidetests/jvmti/run-tests/test-1925/app/AndroidManifest.xml b/hostsidetests/jvmti/run-tests/test-1925/app/AndroidManifest.xml
index b3fab4f..9b8f768 100644
--- a/hostsidetests/jvmti/run-tests/test-1925/app/AndroidManifest.xml
+++ b/hostsidetests/jvmti/run-tests/test-1925/app/AndroidManifest.xml
@@ -21,6 +21,8 @@
<application android:debuggable="true">
<uses-library android:name="android.test.runner" />
<meta-data android:name="android.jvmti.cts.run_test_nr" android:value="1925" />
+ <!-- Perform extra logging to try to get to the bottom of b/144947842 -->
+ <meta-data android:name="android.jvmti.cts.run_test.extra_logging" android:value="true" />
<activity android:name="android.jvmti.JvmtiActivity" >
</activity>
</application>
diff --git a/hostsidetests/jvmti/run-tests/test-1926/app/AndroidManifest.xml b/hostsidetests/jvmti/run-tests/test-1926/app/AndroidManifest.xml
index ee5da05..a07a28f 100644
--- a/hostsidetests/jvmti/run-tests/test-1926/app/AndroidManifest.xml
+++ b/hostsidetests/jvmti/run-tests/test-1926/app/AndroidManifest.xml
@@ -21,6 +21,8 @@
<application android:debuggable="true">
<uses-library android:name="android.test.runner" />
<meta-data android:name="android.jvmti.cts.run_test_nr" android:value="1926" />
+ <!-- Perform extra logging to try to get to the bottom of b/144947842 -->
+ <meta-data android:name="android.jvmti.cts.run_test.extra_logging" android:value="true" />
<activity android:name="android.jvmti.JvmtiActivity" >
</activity>
</application>
diff --git a/hostsidetests/jvmti/run-tests/test-1936/app/AndroidManifest.xml b/hostsidetests/jvmti/run-tests/test-1936/app/AndroidManifest.xml
index 57cf5cd..d51b1b6 100644
--- a/hostsidetests/jvmti/run-tests/test-1936/app/AndroidManifest.xml
+++ b/hostsidetests/jvmti/run-tests/test-1936/app/AndroidManifest.xml
@@ -21,6 +21,8 @@
<application android:debuggable="true">
<uses-library android:name="android.test.runner" />
<meta-data android:name="android.jvmti.cts.run_test_nr" android:value="1936" />
+ <!-- Perform extra logging to try to get to the bottom of b/144947842 -->
+ <meta-data android:name="android.jvmti.cts.run_test.extra_logging" android:value="true" />
<activity android:name="android.jvmti.JvmtiActivity" >
</activity>
</application>
diff --git a/hostsidetests/jvmti/run-tests/test-1982/app/AndroidManifest.xml b/hostsidetests/jvmti/run-tests/test-1982/app/AndroidManifest.xml
index 1bed7f1..1d29560 100644
--- a/hostsidetests/jvmti/run-tests/test-1982/app/AndroidManifest.xml
+++ b/hostsidetests/jvmti/run-tests/test-1982/app/AndroidManifest.xml
@@ -21,6 +21,8 @@
<application android:debuggable="true">
<uses-library android:name="android.test.runner" />
<meta-data android:name="android.jvmti.cts.run_test_nr" android:value="1982" />
+ <!-- Perform extra logging to try to get to the bottom of b/144947842 -->
+ <meta-data android:name="android.jvmti.cts.run_test.extra_logging" android:value="true" />
<activity android:name="android.jvmti.JvmtiActivity" >
</activity>
</application>
diff --git a/hostsidetests/jvmti/run-tests/test-1995/app/AndroidManifest.xml b/hostsidetests/jvmti/run-tests/test-1995/app/AndroidManifest.xml
index 6f1d556..b04fa7b 100644
--- a/hostsidetests/jvmti/run-tests/test-1995/app/AndroidManifest.xml
+++ b/hostsidetests/jvmti/run-tests/test-1995/app/AndroidManifest.xml
@@ -21,6 +21,8 @@
<application android:debuggable="true">
<uses-library android:name="android.test.runner" />
<meta-data android:name="android.jvmti.cts.run_test_nr" android:value="1995" />
+ <!-- Perform extra logging to try to get to the bottom of b/144947842 -->
+ <meta-data android:name="android.jvmti.cts.run_test.extra_logging" android:value="true" />
<activity android:name="android.jvmti.JvmtiActivity" >
</activity>
</application>
diff --git a/hostsidetests/jvmti/run-tests/test-2004/app/AndroidManifest.xml b/hostsidetests/jvmti/run-tests/test-2004/app/AndroidManifest.xml
index c5ecaec..673d7d0 100644
--- a/hostsidetests/jvmti/run-tests/test-2004/app/AndroidManifest.xml
+++ b/hostsidetests/jvmti/run-tests/test-2004/app/AndroidManifest.xml
@@ -21,6 +21,8 @@
<application android:debuggable="true">
<uses-library android:name="android.test.runner" />
<meta-data android:name="android.jvmti.cts.run_test_nr" android:value="2004" />
+ <!-- Perform extra logging to try to get to the bottom of b/144947842 -->
+ <meta-data android:name="android.jvmti.cts.run_test.extra_logging" android:value="true" />
<activity android:name="android.jvmti.JvmtiActivity" >
</activity>
</application>
diff --git a/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/DeviceJUnit4ClassRunnerWithParameters.java b/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/DeviceJUnit4ClassRunnerWithParameters.java
index f8e5576..944e258 100644
--- a/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/DeviceJUnit4ClassRunnerWithParameters.java
+++ b/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/DeviceJUnit4ClassRunnerWithParameters.java
@@ -18,12 +18,15 @@
import com.android.tradefed.build.IBuildInfo;
import com.android.tradefed.config.Option;
import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.invoker.TestInformation;
import com.android.tradefed.testtype.HostTest;
import com.android.tradefed.testtype.IAbi;
import com.android.tradefed.testtype.IAbiReceiver;
import com.android.tradefed.testtype.IBuildReceiver;
import com.android.tradefed.testtype.IDeviceTest;
import com.android.tradefed.testtype.ISetOptionReceiver;
+import com.android.tradefed.testtype.ITestInformationReceiver;
+
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
@@ -41,7 +44,7 @@
* Custom JUnit4 parameterized test runner that also accommodate {@link IDeviceTest}.
*/
public class DeviceJUnit4ClassRunnerWithParameters extends BlockJUnit4ClassRunnerWithParameters
- implements IDeviceTest, IBuildReceiver, IAbiReceiver, ISetOptionReceiver {
+ implements IDeviceTest, IBuildReceiver, IAbiReceiver, ISetOptionReceiver, ITestInformationReceiver {
@Option(
name = HostTest.SET_OPTION_NAME,
@@ -49,6 +52,7 @@
)
private Set<String> mKeyValueOptions = new HashSet<>();
+ private TestInformation mTestInformation;
private ITestDevice mDevice;
private IBuildInfo mBuildInfo;
private IAbi mAbi;
@@ -78,6 +82,15 @@
return mDevice;
}
+ @Override
+ public void setTestInformation(TestInformation testInformation) {
+ mTestInformation = testInformation;
+ }
+
+ @Override
+ public TestInformation getTestInformation() {
+ return mTestInformation;
+ }
@Override
public Description getDescription() {
@@ -122,6 +135,9 @@
if (testObj instanceof IAbiReceiver) {
((IAbiReceiver) testObj).setAbi(mAbi);
}
+ if (testObj instanceof ITestInformationReceiver) {
+ ((ITestInformationReceiver) testObj).setTestInformation(mTestInformation);
+ }
HostTest.setOptionToLoadedObject(testObj, new ArrayList<String>(mKeyValueOptions));
return testObj;
}
diff --git a/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/DeviceJUnit4Parameterized.java b/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/DeviceJUnit4Parameterized.java
index e28d81d..ea7ce7f 100644
--- a/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/DeviceJUnit4Parameterized.java
+++ b/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/DeviceJUnit4Parameterized.java
@@ -20,6 +20,7 @@
import com.android.tradefed.config.Option;
import com.android.tradefed.config.OptionSetter;
import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.invoker.TestInformation;
import com.android.tradefed.log.LogUtil.CLog;
import com.android.tradefed.testtype.HostTest;
import com.android.tradefed.testtype.IAbi;
@@ -27,20 +28,21 @@
import com.android.tradefed.testtype.IBuildReceiver;
import com.android.tradefed.testtype.IDeviceTest;
import com.android.tradefed.testtype.ISetOptionReceiver;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import org.junit.runner.Description;
+import com.android.tradefed.testtype.ITestInformationReceiver;
+
import org.junit.runner.Runner;
import org.junit.runner.notification.RunNotifier;
import org.junit.runners.Parameterized;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
/**
* Custom JUnit4 parameterized test runner that also accommodate {@link IDeviceTest}.
*/
public class DeviceJUnit4Parameterized extends Parameterized
- implements IDeviceTest, IBuildReceiver, IAbiReceiver, ISetOptionReceiver {
+ implements IDeviceTest, IBuildReceiver, IAbiReceiver, ISetOptionReceiver, ITestInformationReceiver {
@Option(
name = HostTest.SET_OPTION_NAME,
@@ -49,6 +51,7 @@
private Set<String> mKeyValueOptions = new HashSet<>();
private ITestDevice mDevice;
+ private TestInformation mTestInformation;
private List<Runner> mRunners;
public DeviceJUnit4Parameterized(Class<?> klass) throws Throwable {
@@ -89,13 +92,27 @@
return mDevice;
}
-
@Override
protected List<Runner> getChildren() {
return mRunners;
}
@Override
+ public void setTestInformation(TestInformation testInformation) {
+ mTestInformation = testInformation;
+ for (Runner runner : mRunners) {
+ if (runner instanceof ITestInformationReceiver) {
+ ((ITestInformationReceiver) runner).setTestInformation(mTestInformation);
+ }
+ }
+ }
+
+ @Override
+ public TestInformation getTestInformation() {
+ return mTestInformation;
+ }
+
+ @Override
protected void runChild(Runner runner, RunNotifier notifier) {
try {
OptionSetter setter = new OptionSetter(runner);
diff --git a/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/MediaBitstreamsTest.java b/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/MediaBitstreamsTest.java
index 24a96a7..69a7d9c 100644
--- a/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/MediaBitstreamsTest.java
+++ b/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/MediaBitstreamsTest.java
@@ -25,12 +25,22 @@
import com.android.tradefed.config.OptionClass;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.invoker.TestInformation;
import com.android.tradefed.log.LogUtil.CLog;
import com.android.tradefed.testtype.IAbi;
import com.android.tradefed.testtype.IAbiReceiver;
import com.android.tradefed.testtype.IBuildReceiver;
import com.android.tradefed.testtype.IDeviceTest;
+import com.android.tradefed.testtype.ITestInformationReceiver;
import com.android.tradefed.util.FileUtil;
+
+import org.junit.Assert;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlPullParserFactory;
+
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileFilter;
@@ -53,18 +63,12 @@
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
-import org.junit.Assert;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlPullParserFactory;
/**
* Test that verifies video bitstreams decode pixel perfectly
*/
@OptionClass(alias="media-bitstreams-test")
-public abstract class MediaBitstreamsTest implements IDeviceTest, IBuildReceiver, IAbiReceiver {
+public abstract class MediaBitstreamsTest implements IDeviceTest, IBuildReceiver, IAbiReceiver, ITestInformationReceiver {
@Option(name = MediaBitstreams.OPT_HOST_BITSTREAMS_PATH,
description = "Absolute path of Ittiam bitstreams (host)",
@@ -132,6 +136,7 @@
private IAbi mAbi;
private ITestDevice mDevice;
+ private TestInformation mTestInfo;
static File getDefaultBitstreamsDir() {
File mediaDir = MediaPreparer.getDefaultMediaDir();
@@ -239,6 +244,16 @@
return mDevice;
}
+ @Override
+ public void setTestInformation(TestInformation testInformation) {
+ mTestInfo = testInformation;
+ }
+
+ @Override
+ public TestInformation getTestInformation() {
+ return mTestInfo;
+ }
+
/*
* Returns true if all necessary media files exist on the device, and false otherwise.
*
@@ -453,6 +468,7 @@
public void testGetBitstreamsFormats() throws DeviceNotAvailableException, IOException {
ReportProcessor processor = new ProcessBitstreamsFormats();
processor.processDeviceReport(
+ getTestInformation(),
getDevice(),
getCurrentMethod(),
MediaBitstreams.KEY_BITSTREAMS_FORMATS_XML);
@@ -497,6 +513,7 @@
SupportedBitstreamsProcessor preparer;
preparer = new SupportedBitstreamsProcessor(prefix, mDebugTargetDevice);
preparer.processDeviceReport(
+ getTestInformation(),
device,
MediaBitstreams.K_TEST_GET_SUPPORTED_BITSTREAMS,
MediaBitstreams.KEY_SUPPORTED_BITSTREAMS_TXT);
@@ -547,6 +564,7 @@
ReportProcessor processor;
processor = new ProcessBitstreamsValidation(toPush, curMethod);
processor.processDeviceReport(
+ getTestInformation(),
device,
curMethod,
MediaBitstreams.KEY_BITSTREAMS_VALIDATION_TXT);
diff --git a/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/ReportProcessor.java b/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/ReportProcessor.java
index b334b0b..c924cbc 100644
--- a/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/ReportProcessor.java
+++ b/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/ReportProcessor.java
@@ -19,6 +19,7 @@
import com.android.tradefed.config.Configuration;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.invoker.TestInformation;
import com.android.tradefed.log.LogUtil.CLog;
import com.android.tradefed.metrics.proto.MetricMeasurement.Metric;
import com.android.tradefed.result.ITestInvocationListener;
@@ -139,6 +140,7 @@
}
private boolean runDeviceTest(
+ TestInformation testInfo,
ITestDevice device, String method, String reportKey, int testTimeout,
long shellTimeout)
throws DeviceNotAvailableException {
@@ -160,7 +162,7 @@
// AndroidJUnitTest requires a IConfiguration to work properly, add a stub to this
// implementation to avoid an NPE.
instrTest.setConfiguration(new Configuration("stub", "stub"));
- instrTest.run(new MediaBitstreamsListener());
+ instrTest.run(testInfo, new MediaBitstreamsListener());
return checkFile(reportKey);
@@ -178,12 +180,12 @@
return true;
}
- void processDeviceReport(
+ void processDeviceReport(TestInformation testInfo,
ITestDevice device, String method, String reportKey)
throws DeviceNotAvailableException, IOException {
try {
setUp(device);
- while (!runDeviceTest(device, method, reportKey, 0, 0)) {
+ while (!runDeviceTest(testInfo, device, method, reportKey, 0, 0)) {
if (!recover(device, mMetrics.get(reportKey))) {
return;
}
diff --git a/hostsidetests/media/src/android/media/session/cts/MediaSessionManagerHostTest.java b/hostsidetests/media/src/android/media/session/cts/MediaSessionManagerHostTest.java
index 07fa856..750e98c 100644
--- a/hostsidetests/media/src/android/media/session/cts/MediaSessionManagerHostTest.java
+++ b/hostsidetests/media/src/android/media/session/cts/MediaSessionManagerHostTest.java
@@ -24,7 +24,6 @@
import android.media.cts.BaseMultiUserTest;
import android.media.cts.MediaSessionTestHelperConstants;
-
import android.platform.test.annotations.AppModeFull;
import android.platform.test.annotations.AppModeInstant;
import android.platform.test.annotations.RequiresDevice;
@@ -61,8 +60,6 @@
private final List<Integer> mNotificationListeners = new ArrayList<>();
- private boolean mNotificationListenerDisabled;
-
@Override
public void setUp() throws Exception {
super.setUp();
@@ -70,7 +67,6 @@
// Ensure that the previously running media session test helper app doesn't exist.
getDevice().uninstallPackage(MEDIA_SESSION_TEST_HELPER_PKG);
mNotificationListeners.clear();
- mNotificationListenerDisabled = "true".equals(getDevice().getProperty("ro.config.low_ram"));
}
@Override
@@ -101,11 +97,6 @@
}
private void testGetActiveSessions_primaryUser(boolean instant) throws Exception {
- if (mNotificationListenerDisabled) {
- CLog.logAndDisplay(LogLevel.INFO,
- "NotificationListener is disabled. Test won't run.");
- return;
- }
int primaryUserId = getDevice().getPrimaryUserId();
setAllowGetActiveSessionForTest(true, primaryUserId);
@@ -144,11 +135,6 @@
"Cannot create a new user. Skipping multi-user test cases.");
return;
}
- if (mNotificationListenerDisabled) {
- CLog.logAndDisplay(LogLevel.INFO,
- "NotificationListener is disabled. Test won't run.");
- return;
- }
// Test if another user can get the session.
int newUser = createAndStartUser();
@@ -183,11 +169,6 @@
"Cannot create a new user. Skipping multi-user test cases.");
return;
}
- if (mNotificationListenerDisabled) {
- CLog.logAndDisplay(LogLevel.INFO,
- "NotificationListener is disabled. Test won't run.");
- return;
- }
// Test if another restricted profile can get the session.
// Remove the created user first not to exceed system's user number limit.
@@ -223,11 +204,6 @@
"Device doesn't support managed profiles. Test won't run.");
return;
}
- if (mNotificationListenerDisabled) {
- CLog.logAndDisplay(LogLevel.INFO,
- "NotificationListener is disabled. Test won't run.");
- return;
- }
// Test if another managed profile can get the session.
// Remove the created user first not to exceed system's user number limit.
@@ -241,12 +217,6 @@
@AppModeFull
@RequiresDevice
public void testGetActiveSessions_noSession2() throws Exception {
- if (mNotificationListenerDisabled) {
- CLog.logAndDisplay(LogLevel.INFO,
- "NotificationListener is disabled. Test won't run.");
- return;
- }
-
int primaryUserId = getDevice().getPrimaryUserId();
setAllowGetActiveSessionForTest(true, primaryUserId);
@@ -264,12 +234,6 @@
@AppModeFull
@RequiresDevice
public void testGetActiveSessions_withSession2() throws Exception {
- if (mNotificationListenerDisabled) {
- CLog.logAndDisplay(LogLevel.INFO,
- "NotificationListener is disabled. Test won't run.");
- return;
- }
-
int primaryUserId = getDevice().getPrimaryUserId();
setAllowGetActiveSessionForTest(true, primaryUserId);
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/DumpOnFailureRule.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/DumpOnFailureRule.java
index cedd62a..5ecb399 100644
--- a/hostsidetests/net/app/src/com/android/cts/net/hostside/DumpOnFailureRule.java
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/DumpOnFailureRule.java
@@ -16,6 +16,7 @@
package com.android.cts.net.hostside;
import static com.android.cts.net.hostside.AbstractRestrictBackgroundNetworkTestCase.TAG;
+import static com.android.cts.net.hostside.AbstractRestrictBackgroundNetworkTestCase.TEST_APP2_PKG;
import static com.android.cts.net.hostside.AbstractRestrictBackgroundNetworkTestCase.TEST_PKG;
import android.os.Environment;
@@ -58,7 +59,7 @@
for (String cmd : new String[] {
"dumpsys netpolicy",
"dumpsys network_management",
- "dumpsys usagestats " + TEST_PKG,
+ "dumpsys usagestats " + TEST_PKG + " " + TEST_APP2_PKG,
"dumpsys usagestats appstandby",
}) {
dumpCommandOutput(out, cmd);
diff --git a/hostsidetests/packagemanager/dynamicmime/Android.bp b/hostsidetests/packagemanager/dynamicmime/Android.bp
new file mode 100644
index 0000000..e23c99a
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/Android.bp
@@ -0,0 +1,30 @@
+// 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.
+java_test_host {
+ name: "CtsDynamicMimeHostTestCases",
+ defaults: ["cts_defaults"],
+ srcs: ["src/**/*.java"],
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "vts",
+ "general-tests",
+ ],
+ libs: [
+ "cts-tradefed",
+ "tradefed",
+ "compatibility-host-util",
+ "truth-prebuilt",
+ ],
+}
\ No newline at end of file
diff --git a/hostsidetests/packagemanager/dynamicmime/AndroidTest.xml b/hostsidetests/packagemanager/dynamicmime/AndroidTest.xml
new file mode 100644
index 0000000..56ab6ca
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/AndroidTest.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT 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 the CTS dynamic MIME 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" />
+ <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="install-arg" value="-t" />
+ <option name="test-file-name" value="CtsDynamicMimeHelperApp.apk" />
+ <option name="test-file-name" value="CtsDynamicMimeTestApp.apk" />
+ </target_preparer>
+
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="run-command" value="mkdir -p /data/local/tmp/dynamic-mime-test" />
+ <option name="teardown-command" value="rm -rf /data/local/tmp/dynamic-mime-test"/>
+ </target_preparer>
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+ <option name="cleanup" value="true" />
+ <option name="push" value="CtsDynamicMimeUpdateAppFirstGroup.apk->/data/local/tmp/dynamic-mime-test/CtsDynamicMimeUpdateAppFirstGroup.apk"/>
+ <option name="push" value="CtsDynamicMimeUpdateAppSecondGroup.apk->/data/local/tmp/dynamic-mime-test/CtsDynamicMimeUpdateAppSecondGroup.apk"/>
+ <option name="push" value="CtsDynamicMimeUpdateAppBothGroups.apk->/data/local/tmp/dynamic-mime-test/CtsDynamicMimeUpdateAppBothGroups.apk"/>
+ <option name="push" value="CtsDynamicMimePreferredApp.apk->/data/local/tmp/dynamic-mime-test/CtsDynamicMimePreferredApp.apk"/>
+ </target_preparer>
+
+ <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
+ <option name="jar" value="CtsDynamicMimeHostTestCases.jar" />
+ </test>
+
+</configuration>
diff --git a/hostsidetests/packagemanager/dynamicmime/OWNERS b/hostsidetests/packagemanager/dynamicmime/OWNERS
new file mode 100644
index 0000000..af9eee9
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 136635677
+tantoshchuk@google.com
\ No newline at end of file
diff --git a/hostsidetests/packagemanager/dynamicmime/app/Android.bp b/hostsidetests/packagemanager/dynamicmime/app/Android.bp
new file mode 100644
index 0000000..1e983e1
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/app/Android.bp
@@ -0,0 +1,88 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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: "CtsDynamicMimeHelperApp",
+ defaults: ["cts_support_defaults"],
+ static_libs: ["CtsDynamicMimeCommon"],
+ srcs: ["src/**/*.java"],
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "vts",
+ "general-tests",
+ ],
+ sdk_version: "test_current",
+ manifest: "manifests/AndroidManifest_helper.xml",
+}
+
+android_test_helper_app {
+ name: "CtsDynamicMimeUpdateAppFirstGroup",
+ defaults: ["cts_support_defaults"],
+ static_libs: ["CtsDynamicMimeCommon"],
+ srcs: ["src/**/*.java"],
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "vts",
+ "general-tests",
+ ],
+ sdk_version: "test_current",
+ manifest: "manifests/AndroidManifest_update_firstGroup.xml",
+}
+
+android_test_helper_app {
+ name: "CtsDynamicMimeUpdateAppSecondGroup",
+ defaults: ["cts_support_defaults"],
+ static_libs: ["CtsDynamicMimeCommon"],
+ srcs: ["src/**/*.java"],
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "vts",
+ "general-tests",
+ ],
+ sdk_version: "test_current",
+ manifest: "manifests/AndroidManifest_update_secondGroup.xml",
+}
+
+android_test_helper_app {
+ name: "CtsDynamicMimeUpdateAppBothGroups",
+ defaults: ["cts_support_defaults"],
+ static_libs: ["CtsDynamicMimeCommon"],
+ srcs: ["src/**/*.java"],
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "vts",
+ "general-tests",
+ ],
+ sdk_version: "test_current",
+ manifest: "manifests/AndroidManifest_update_bothGroups.xml",
+}
+
+android_test_helper_app {
+ name: "CtsDynamicMimePreferredApp",
+ defaults: ["cts_support_defaults"],
+ static_libs: ["CtsDynamicMimeCommon"],
+ srcs: ["src/**/*.java"],
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "vts",
+ "general-tests",
+ ],
+ sdk_version: "test_current",
+ manifest: "manifests/AndroidManifest_preferred.xml",
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/app/manifests/AndroidManifest_helper.xml b/hostsidetests/packagemanager/dynamicmime/app/manifests/AndroidManifest_helper.xml
new file mode 100644
index 0000000..1373430
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/app/manifests/AndroidManifest_helper.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT 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.dynamicmime.helper">
+ <application android:testOnly="true">
+ <uses-library android:name="android.test.runner" />
+ <activity android:name="android.dynamicmime.common.activity.FirstActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.SEND"/>
+ <action android:name="android.dynamicmime.helper.FILTER_INFO_HOOK_group_first"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ <data android:mimeGroup="group_first"/>
+ </intent-filter>
+ </activity>
+ <activity android:name="android.dynamicmime.common.activity.SecondActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.SEND"/>
+ <action android:name="android.dynamicmime.helper.FILTER_INFO_HOOK_group_second"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ <data android:mimeGroup="group_second"/>
+ </intent-filter>
+ </activity>
+
+ <receiver android:name="android.dynamicmime.app.AppMimeGroupsReceiver"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.dynamicmime.UPDATE_MIME_GROUP_REQUEST"/>
+ </intent-filter>
+ </receiver>
+ </application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.dynamicmime.helper" >
+ </instrumentation>
+</manifest>
diff --git a/hostsidetests/packagemanager/dynamicmime/app/manifests/AndroidManifest_preferred.xml b/hostsidetests/packagemanager/dynamicmime/app/manifests/AndroidManifest_preferred.xml
new file mode 100644
index 0000000..83cdf0e
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/app/manifests/AndroidManifest_preferred.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT 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.dynamicmime.preferred">
+ <application android:testOnly="true"
+ android:label="TestApp.Application">
+ <uses-library android:name="android.test.runner" />
+ <activity android:name="android.dynamicmime.common.activity.FirstActivity">
+ <intent-filter android:label="TestApp.FirstActivity">
+ <action android:name="android.intent.action.SEND"/>
+ <action android:name="android.dynamicmime.preferred.FILTER_INFO_HOOK_group_first"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ <data android:mimeGroup="group_first"/>
+ </intent-filter>
+ </activity>
+
+ <activity android:name="android.dynamicmime.common.activity.TwoGroupsActivity">
+ <intent-filter android:label="TestApp.TwoGroupsActivity">
+ <action android:name="android.intent.action.SEND"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ <action android:name="android.dynamicmime.preferred.FILTER_INFO_HOOK_group_both"/>
+ <data android:mimeGroup="group_third"/>
+ <data android:mimeGroup="group_second"/>
+ </intent-filter>
+ </activity>
+
+ <receiver android:name="android.dynamicmime.app.AppMimeGroupsReceiver"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.dynamicmime.UPDATE_MIME_GROUP_REQUEST"/>
+ </intent-filter>
+ </receiver>
+ </application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.dynamicmime.preferred">
+ </instrumentation>
+</manifest>
diff --git a/hostsidetests/packagemanager/dynamicmime/app/manifests/AndroidManifest_update_bothGroups.xml b/hostsidetests/packagemanager/dynamicmime/app/manifests/AndroidManifest_update_bothGroups.xml
new file mode 100644
index 0000000..802d13c
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/app/manifests/AndroidManifest_update_bothGroups.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT 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.dynamicmime.update">
+ <application android:testOnly="true">
+ <uses-library android:name="android.test.runner" />
+ <activity android:name="android.dynamicmime.common.activity.FirstActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.SEND"/>
+ <action android:name="android.dynamicmime.update.FILTER_INFO_HOOK_group_first"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ <data android:mimeGroup="group_first"/>
+ </intent-filter>
+ </activity>
+ <activity android:name="android.dynamicmime.common.activity.SecondActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.SEND"/>
+ <action android:name="android.dynamicmime.update.FILTER_INFO_HOOK_group_second"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ <data android:mimeGroup="group_second"/>
+ </intent-filter>
+ </activity>
+
+ <receiver android:name="android.dynamicmime.app.AppMimeGroupsReceiver"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.dynamicmime.UPDATE_MIME_GROUP_REQUEST"/>
+ </intent-filter>
+ </receiver>
+ </application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.dynamicmime.update" >
+ </instrumentation>
+</manifest>
diff --git a/hostsidetests/packagemanager/dynamicmime/app/manifests/AndroidManifest_update_firstGroup.xml b/hostsidetests/packagemanager/dynamicmime/app/manifests/AndroidManifest_update_firstGroup.xml
new file mode 100644
index 0000000..adc93cf
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/app/manifests/AndroidManifest_update_firstGroup.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT 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.dynamicmime.update">
+ <application android:testOnly="true">
+ <uses-library android:name="android.test.runner" />
+ <activity android:name="android.dynamicmime.common.activity.FirstActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.SEND"/>
+ <action android:name="android.dynamicmime.update.FILTER_INFO_HOOK_group_first"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ <data android:mimeGroup="group_first"/>
+ </intent-filter>
+ </activity>
+ <activity android:name="android.dynamicmime.common.activity.SecondActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.SEND"/>
+ <action android:name="android.dynamicmime.update.FILTER_INFO_HOOK_group_second"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ </intent-filter>
+ </activity>
+
+ <receiver android:name="android.dynamicmime.app.AppMimeGroupsReceiver"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.dynamicmime.UPDATE_MIME_GROUP_REQUEST"/>
+ </intent-filter>
+ </receiver>
+ </application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.dynamicmime.update" >
+ </instrumentation>
+</manifest>
diff --git a/hostsidetests/packagemanager/dynamicmime/app/manifests/AndroidManifest_update_secondGroup.xml b/hostsidetests/packagemanager/dynamicmime/app/manifests/AndroidManifest_update_secondGroup.xml
new file mode 100644
index 0000000..e93a3d5
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/app/manifests/AndroidManifest_update_secondGroup.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT 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.dynamicmime.update">
+ <application android:testOnly="true">
+ <uses-library android:name="android.test.runner" />
+ <activity android:name="android.dynamicmime.common.activity.FirstActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.SEND"/>
+ <action android:name="android.dynamicmime.update.FILTER_INFO_HOOK_group_first"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ </intent-filter>
+ </activity>
+ <activity android:name="android.dynamicmime.common.activity.SecondActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.SEND"/>
+ <action android:name="android.dynamicmime.update.FILTER_INFO_HOOK_group_second"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ <data android:mimeGroup="group_second"/>
+ </intent-filter>
+ </activity>
+
+ <receiver android:name="android.dynamicmime.app.AppMimeGroupsReceiver"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.dynamicmime.UPDATE_MIME_GROUP_REQUEST"/>
+ </intent-filter>
+ </receiver>
+ </application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.dynamicmime.update" >
+ </instrumentation>
+</manifest>
diff --git a/hostsidetests/packagemanager/dynamicmime/app/src/android/dynamicmime/app/AppMimeGroupsReceiver.java b/hostsidetests/packagemanager/dynamicmime/app/src/android/dynamicmime/app/AppMimeGroupsReceiver.java
new file mode 100644
index 0000000..7c79fef
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/app/src/android/dynamicmime/app/AppMimeGroupsReceiver.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.dynamicmime.app;
+
+import static android.dynamicmime.common.Constants.ACTION_REQUEST;
+import static android.dynamicmime.common.Constants.ACTION_RESPONSE;
+import static android.dynamicmime.common.Constants.EXTRA_GROUP;
+import static android.dynamicmime.common.Constants.EXTRA_MIMES;
+import static android.dynamicmime.common.Constants.EXTRA_REQUEST;
+import static android.dynamicmime.common.Constants.EXTRA_RESPONSE;
+import static android.dynamicmime.common.Constants.REQUEST_CLEAR;
+import static android.dynamicmime.common.Constants.REQUEST_GET;
+import static android.dynamicmime.common.Constants.REQUEST_SET;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.util.ArraySet;
+
+import java.util.Set;
+
+/**
+ * Receiver of {@link android.dynamicmime.testapp.util.AppMimeGroups} commands
+ */
+public class AppMimeGroupsReceiver extends BroadcastReceiver {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (!ACTION_REQUEST.equals(intent.getAction())) {
+ return;
+ }
+
+ String mimeGroup = intent.getStringExtra(EXTRA_GROUP);
+ String[] mimeTypes = intent.getStringArrayExtra(EXTRA_MIMES);
+ int requestCode = intent.getIntExtra(EXTRA_REQUEST, -1);
+
+ Intent response = new Intent(ACTION_RESPONSE);
+ switch (requestCode) {
+ case REQUEST_SET:
+ context.getPackageManager().setMimeGroup(mimeGroup, new ArraySet<>(mimeTypes));
+ break;
+ case REQUEST_CLEAR:
+ context.getPackageManager().clearMimeGroup(mimeGroup);
+ break;
+ case REQUEST_GET:
+ response.putExtra(EXTRA_RESPONSE, getMimeGroup(context, mimeGroup));
+ break;
+ default:
+ //do not respond with broadcast to indicate that something is wrong
+ return;
+ }
+
+ context.sendBroadcast(response);
+ }
+
+ private String[] getMimeGroup(Context context, String mimeGroup) {
+ Set<String> mimeTypes = context.getPackageManager().getMimeGroup(mimeGroup);
+ return mimeTypes != null ? mimeTypes.toArray(new String[0]) : null;
+ }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/common/Android.bp b/hostsidetests/packagemanager/dynamicmime/common/Android.bp
new file mode 100644
index 0000000..a80aba7
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/common/Android.bp
@@ -0,0 +1,20 @@
+// 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.
+
+java_library_static {
+ name: "CtsDynamicMimeCommon",
+ defaults: ["cts_defaults"],
+ sdk_version: "test_current",
+ srcs: ["src/**/*.java"],
+}
\ No newline at end of file
diff --git a/hostsidetests/packagemanager/dynamicmime/common/src/android/dynamicmime/common/Constants.java b/hostsidetests/packagemanager/dynamicmime/common/src/android/dynamicmime/common/Constants.java
new file mode 100644
index 0000000..e3289ce
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/common/src/android/dynamicmime/common/Constants.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.dynamicmime.common;
+
+public class Constants {
+ public static final String ACTION_REQUEST = "android.dynamicmime.UPDATE_MIME_GROUP_REQUEST";
+ public static final String ACTION_RESPONSE = "android.dynamicmime.UPDATE_MIME_GROUP_RESPONSE";
+
+ public static final String EXTRA_GROUP = "EXTRA_GROUP";
+ public static final String EXTRA_MIMES = "EXTRA_MIME";
+ public static final String EXTRA_REQUEST = "EXTRA_REQUEST";
+ public static final String EXTRA_RESPONSE = "EXTRA_RESPONSE";
+
+ public static final String GROUP_FIRST = "group_first";
+ public static final String GROUP_SECOND = "group_second";
+ public static final String GROUP_THIRD = "group_third";
+ public static final String GROUP_UNDEFINED = "undefined";
+
+ public static final String ALIAS_BOTH_GROUPS = "group_both";
+ public static final String ALIAS_BOTH_GROUPS_AND_TYPE = "groups_and_type";
+
+ public static final String[] GROUPS = {
+ GROUP_FIRST, GROUP_SECOND, GROUP_UNDEFINED
+ };
+
+ public static final int REQUEST_CLEAR = 1;
+ public static final int REQUEST_SET = 2;
+ public static final int REQUEST_GET = 3;
+
+ public static final String MIME_TEXT_PLAIN = "text/plain";
+ public static final String MIME_TEXT_XML = "text/xml";
+ public static final String MIME_TEXT_ANY = "text/*";
+ public static final String MIME_IMAGE_PNG = "image/png";
+ public static final String MIME_IMAGE_JPEG = "image/jpeg";
+ public static final String MIME_IMAGE_ANY = "image/*";
+ public static final String MIME_ANY = "*/*";
+
+ public static final String PACKAGE_TEST_APP = "android.dynamicmime.testapp";
+ public static final String PACKAGE_ACTIVITIES = "android.dynamicmime.common.activity";
+ public static final String PACKAGE_HELPER_APP = "android.dynamicmime.helper";
+ public static final String PACKAGE_UPDATE_APP = "android.dynamicmime.update";
+ public static final String PACKAGE_PREFERRED_APP = "android.dynamicmime.preferred";
+
+ public static final String ACTIVITY_FIRST = ".FirstActivity";
+ public static final String ACTIVITY_SECOND = ".SecondActivity";
+ public static final String ACTIVITY_BOTH = ".TwoGroupsActivity";
+ public static final String ACTIVITY_BOTH_AND_TYPE = ".TwoGroupsAndTypeActivity";
+
+ public static final String DATA_DIR = "/data/local/tmp/dynamic-mime-test/";
+
+ public static final String APK_PREFERRED_APP = DATA_DIR + "CtsDynamicMimePreferredApp.apk";
+ public static final String APK_BOTH_GROUPS = DATA_DIR + "CtsDynamicMimeUpdateAppBothGroups.apk";
+ public static final String APK_FIRST_GROUP = DATA_DIR + "CtsDynamicMimeUpdateAppFirstGroup.apk";
+ public static final String APK_SECOND_GROUP = DATA_DIR +
+ "CtsDynamicMimeUpdateAppSecondGroup.apk";
+
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/common/src/android/dynamicmime/common/activity/FirstActivity.java b/hostsidetests/packagemanager/dynamicmime/common/src/android/dynamicmime/common/activity/FirstActivity.java
new file mode 100644
index 0000000..94aeb8f
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/common/src/android/dynamicmime/common/activity/FirstActivity.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.dynamicmime.common.activity;
+
+import android.app.Activity;
+
+public class FirstActivity extends Activity {
+}
\ No newline at end of file
diff --git a/hostsidetests/packagemanager/dynamicmime/common/src/android/dynamicmime/common/activity/SecondActivity.java b/hostsidetests/packagemanager/dynamicmime/common/src/android/dynamicmime/common/activity/SecondActivity.java
new file mode 100644
index 0000000..3ce0ff3
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/common/src/android/dynamicmime/common/activity/SecondActivity.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.dynamicmime.common.activity;
+
+import android.app.Activity;
+
+public class SecondActivity extends Activity {
+}
\ No newline at end of file
diff --git a/hostsidetests/packagemanager/dynamicmime/common/src/android/dynamicmime/common/activity/TwoGroupsActivity.java b/hostsidetests/packagemanager/dynamicmime/common/src/android/dynamicmime/common/activity/TwoGroupsActivity.java
new file mode 100644
index 0000000..9171f0b
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/common/src/android/dynamicmime/common/activity/TwoGroupsActivity.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.dynamicmime.common.activity;
+
+import android.app.Activity;
+
+public class TwoGroupsActivity extends Activity {
+}
\ No newline at end of file
diff --git a/hostsidetests/packagemanager/dynamicmime/common/src/android/dynamicmime/common/activity/TwoGroupsAndTypeActivity.java b/hostsidetests/packagemanager/dynamicmime/common/src/android/dynamicmime/common/activity/TwoGroupsAndTypeActivity.java
new file mode 100644
index 0000000..0a2f04d
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/common/src/android/dynamicmime/common/activity/TwoGroupsAndTypeActivity.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.dynamicmime.common.activity;
+
+import android.app.Activity;
+
+public class TwoGroupsAndTypeActivity extends Activity {
+}
\ No newline at end of file
diff --git a/hostsidetests/packagemanager/dynamicmime/src/android/dynamicmime/cts/PreferredActivitiesTestCases.java b/hostsidetests/packagemanager/dynamicmime/src/android/dynamicmime/cts/PreferredActivitiesTestCases.java
new file mode 100644
index 0000000..25ac458
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/src/android/dynamicmime/cts/PreferredActivitiesTestCases.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.dynamicmime.cts;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Preferred Activities test cases
+ *
+ * Verifies that preferred activity is removed after any change to any MIME group
+ * declared by preferred activity package
+ */
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class PreferredActivitiesTestCases extends BaseHostJUnit4Test {
+ private static final String PACKAGE_TEST_APP = "android.dynamicmime.testapp";
+
+ @Before
+ public void setUp() throws DeviceNotAvailableException {
+ // wake up and unlock device
+ getDevice().executeShellCommand("input keyevent KEYCODE_WAKEUP");
+ getDevice().disableKeyguard();
+ }
+
+ @Test
+ public void testRemoveFromGroup() throws DeviceNotAvailableException {
+ runDeviceTest("testRemoveFromGroup");
+ }
+
+ @Test
+ public void testAddToGroup() throws DeviceNotAvailableException {
+ runDeviceTest("testAddToGroup");
+ }
+
+ @Test
+ public void testClearGroup() throws DeviceNotAvailableException {
+ runDeviceTest("testClearGroup");
+ }
+
+ @Test
+ public void testModifyGroupWithoutActualGroupChanges() throws DeviceNotAvailableException {
+ runDeviceTest("testModifyGroupWithoutActualGroupChanges");
+ }
+
+ @Test
+ public void testModifyGroupWithoutActualIntentFilterChanges()
+ throws DeviceNotAvailableException {
+ runDeviceTest("testModifyGroupWithoutActualIntentFilterChanges");
+ }
+
+ private void runDeviceTest(String testMethodName) throws DeviceNotAvailableException {
+ runDeviceTests(PACKAGE_TEST_APP, PACKAGE_TEST_APP + ".preferred.PreferredActivitiesTest",
+ testMethodName);
+ }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/src/android/dynamicmime/cts/RebootTestCases.java b/hostsidetests/packagemanager/dynamicmime/src/android/dynamicmime/cts/RebootTestCases.java
new file mode 100644
index 0000000..26ac19f
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/src/android/dynamicmime/cts/RebootTestCases.java
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.dynamicmime.cts;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Reboot test cases
+ *
+ * Reuses existing test cases from {@link android.dynamicmime.testapp.ComplexFilterTest}
+ * and {@link android.dynamicmime.testapp.SingleAppTest}
+ * by "inserting" device reboot between setup part (MIME group commands) and verification part
+ * (MIME group assertions) in each test case
+ *
+ * @see android.dynamicmime.testapp.reboot.PreRebootSingleAppTest
+ * @see android.dynamicmime.testapp.reboot.PostRebootSingleAppTest
+ * @see #runTestWithReboot(String, String)
+ */
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class RebootTestCases extends BaseHostJUnit4Test {
+ private static final String PACKAGE_TEST_APP = "android.dynamicmime.testapp";
+ private static final String PACKAGE_REBOOT_TESTS = PACKAGE_TEST_APP + ".reboot";
+
+ @Test
+ public void testGroupWithExactType() throws DeviceNotAvailableException {
+ runTestWithReboot("SingleAppTest", "testGroupWithExactType");
+ }
+
+ @Test
+ public void testGroupWithWildcardType() throws DeviceNotAvailableException {
+ runTestWithReboot("SingleAppTest", "testGroupWithWildcardType");
+ }
+
+ @Test
+ public void testGroupWithAnyType() throws DeviceNotAvailableException {
+ runTestWithReboot("SingleAppTest", "testGroupWithAnyType");
+ }
+
+ @Test
+ public void testAddSimilarTypes() throws DeviceNotAvailableException {
+ runTestWithReboot("SingleAppTest", "testAddSimilarTypes");
+ }
+
+ @Test
+ public void testAddDifferentTypes() throws DeviceNotAvailableException {
+ runTestWithReboot("SingleAppTest", "testAddDifferentTypes");
+ }
+
+ @Test
+ public void testRemoveExactType() throws DeviceNotAvailableException {
+ runTestWithReboot("SingleAppTest", "testRemoveExactType");
+ }
+
+ @Test
+ public void testRemoveDifferentType() throws DeviceNotAvailableException {
+ runTestWithReboot("SingleAppTest", "testRemoveDifferentType");
+ }
+
+ @Test
+ public void testRemoveSameBaseType_keepSpecific() throws DeviceNotAvailableException {
+ runTestWithReboot("SingleAppTest", "testRemoveSameBaseType_keepSpecific");
+ }
+
+ @Test
+ public void testRemoveSameBaseType_keepWildcard() throws DeviceNotAvailableException {
+ runTestWithReboot("SingleAppTest", "testRemoveSameBaseType_keepWildcard");
+ }
+
+ @Test
+ public void testRemoveAnyType_keepSpecific() throws DeviceNotAvailableException {
+ runTestWithReboot("SingleAppTest", "testRemoveAnyType_keepSpecific");
+ }
+
+ @Test
+ public void testRemoveAnyType_keepWildcard() throws DeviceNotAvailableException {
+ runTestWithReboot("SingleAppTest", "testRemoveAnyType_keepWildcard");
+ }
+
+ @Test
+ public void testResetWithoutIntersection() throws DeviceNotAvailableException {
+ runTestWithReboot("SingleAppTest", "testResetWithoutIntersection");
+ }
+
+ @Test
+ public void testResetWithIntersection() throws DeviceNotAvailableException {
+ runTestWithReboot("SingleAppTest", "testResetWithIntersection");
+ }
+
+ @Test
+ public void testResetToEmpty() throws DeviceNotAvailableException {
+ runTestWithReboot("SingleAppTest", "testResetToEmpty");
+ }
+
+ @Test
+ public void testClear() throws DeviceNotAvailableException {
+ runTestWithReboot("SingleAppTest", "testClear");
+ }
+
+ @Test
+ public void testUndefinedGroupIsNullAfterAdd() throws DeviceNotAvailableException {
+ runTestWithReboot("SingleAppTest", "testUndefinedGroupIsNullAfterAdd");
+ }
+
+ @Test
+ public void testUndefinedGroupIsNullAfterSet() throws DeviceNotAvailableException {
+ runTestWithReboot("SingleAppTest", "testUndefinedGroupIsNullAfterSet");
+ }
+
+ @Test
+ public void testUndefinedGroupIsNullAfterRemove() throws DeviceNotAvailableException {
+ runTestWithReboot("SingleAppTest", "testUndefinedGroupIsNullAfterRemove");
+ }
+
+ @Test
+ public void testUndefinedGroupIsNullAfterClear() throws DeviceNotAvailableException {
+ runTestWithReboot("SingleAppTest", "testUndefinedGroupIsNullAfterClear");
+ }
+
+ @Test
+ public void testDefinedGroupNotNullAfterRemove() throws DeviceNotAvailableException {
+ runTestWithReboot("SingleAppTest", "testDefinedGroupNotNullAfterRemove");
+ }
+
+ @Test
+ public void testDefinedGroupNotNullAfterClear() throws DeviceNotAvailableException {
+ runTestWithReboot("SingleAppTest", "testDefinedGroupNotNullAfterClear");
+ }
+
+ @Test
+ public void testDefinedGroupNotNullAfterSetEmpty() throws DeviceNotAvailableException {
+ runTestWithReboot("SingleAppTest", "testDefinedGroupNotNullAfterSetEmpty");
+ }
+
+ @Test
+ public void testDefinedGroupNotNullAfterSetNull() throws DeviceNotAvailableException {
+ runTestWithReboot("SingleAppTest", "testDefinedGroupNotNullAfterSetNull");
+ }
+
+ @Test
+ public void testMimeGroupsIndependentAdd() throws DeviceNotAvailableException {
+ runTestWithReboot("SingleAppTest", "testMimeGroupsIndependentAdd");
+ }
+
+ @Test
+ public void testMimeGroupsIndependentAddToBoth() throws DeviceNotAvailableException {
+ runTestWithReboot("SingleAppTest", "testMimeGroupsIndependentAddToBoth");
+ }
+
+ @Test
+ public void testMimeGroupsIndependentRemove() throws DeviceNotAvailableException {
+ runTestWithReboot("SingleAppTest", "testMimeGroupsIndependentRemove");
+ }
+
+ @Test
+ public void testMimeGroupsIndependentClear() throws DeviceNotAvailableException {
+ runTestWithReboot("SingleAppTest", "testMimeGroupsIndependentClear");
+ }
+
+ @Test
+ public void testMimeGroupsIndependentClearBoth() throws DeviceNotAvailableException {
+ runTestWithReboot("SingleAppTest", "testMimeGroupsIndependentClearBoth");
+ }
+
+ @Test
+ public void testMimeGroupsIndependentSet() throws DeviceNotAvailableException {
+ runTestWithReboot("SingleAppTest", "testMimeGroupsIndependentSet");
+ }
+
+ @Test
+ public void testMimeGroupsIndependentSetBoth() throws DeviceNotAvailableException {
+ runTestWithReboot("SingleAppTest", "testMimeGroupsIndependentSetBoth");
+ }
+
+ @Test
+ public void testMimeGroupsNotIntersect() throws DeviceNotAvailableException {
+ runTestWithReboot("ComplexFilterTest", "testMimeGroupsNotIntersect");
+ }
+
+ @Test
+ public void testMimeGroupsIntersect() throws DeviceNotAvailableException {
+ runTestWithReboot("ComplexFilterTest", "testMimeGroupsIntersect");
+ }
+
+ @Test
+ public void testRemoveTypeFromIntersection() throws DeviceNotAvailableException {
+ runTestWithReboot("ComplexFilterTest", "testRemoveTypeFromIntersection");
+ }
+
+ @Test
+ public void testRemoveIntersectionFromBothGroups() throws DeviceNotAvailableException {
+ runTestWithReboot("ComplexFilterTest", "testRemoveIntersectionFromBothGroups");
+ }
+
+ @Test
+ public void testClearGroupWithoutIntersection() throws DeviceNotAvailableException {
+ runTestWithReboot("ComplexFilterTest", "testClearGroupWithoutIntersection");
+ }
+
+ @Test
+ public void testClearGroupWithIntersection() throws DeviceNotAvailableException {
+ runTestWithReboot("ComplexFilterTest", "testClearGroupWithIntersection");
+ }
+
+ @Test
+ public void testMimeGroupNotIntersectWithStaticType() throws DeviceNotAvailableException {
+ runTestWithReboot("ComplexFilterTest", "testMimeGroupNotIntersectWithStaticType");
+ }
+
+ @Test
+ public void testMimeGroupIntersectWithStaticType() throws DeviceNotAvailableException {
+ runTestWithReboot("ComplexFilterTest", "testMimeGroupIntersectWithStaticType");
+ }
+
+ @Test
+ public void testRemoveStaticTypeFromMimeGroup() throws DeviceNotAvailableException {
+ runTestWithReboot("ComplexFilterTest", "testRemoveStaticTypeFromMimeGroup");
+ }
+
+ @Test
+ public void testClearGroupContainsStaticType() throws DeviceNotAvailableException {
+ runTestWithReboot("ComplexFilterTest", "testClearGroupContainsStaticType");
+ }
+
+ @Test
+ public void testClearGroupNotContainStaticType() throws DeviceNotAvailableException {
+ runTestWithReboot("ComplexFilterTest", "testClearGroupNotContainStaticType");
+ }
+
+ private void runTestWithReboot(String testClassName, String testMethodName)
+ throws DeviceNotAvailableException {
+ runPreReboot(testClassName, testMethodName);
+ getDevice().reboot();
+ runPostReboot(testClassName, testMethodName);
+ }
+
+ private void runPostReboot(String testClassName, String testMethodName)
+ throws DeviceNotAvailableException {
+ runDeviceTests(PACKAGE_TEST_APP, PACKAGE_REBOOT_TESTS + ".PostReboot" + testClassName,
+ testMethodName);
+ }
+
+ private void runPreReboot(String testClassName, String testMethodName)
+ throws DeviceNotAvailableException {
+ runDeviceTests(PACKAGE_TEST_APP, PACKAGE_REBOOT_TESTS + ".PreReboot" + testClassName,
+ testMethodName);
+ }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/src/android/dynamicmime/cts/TestCases.java b/hostsidetests/packagemanager/dynamicmime/src/android/dynamicmime/cts/TestCases.java
new file mode 100644
index 0000000..d3a2e89
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/src/android/dynamicmime/cts/TestCases.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.dynamicmime.cts;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Invokes device-side tests as is, no need for any host-side setup
+ */
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class TestCases extends BaseHostJUnit4Test {
+ private static final String PACKAGE_TEST_APP = "android.dynamicmime.testapp";
+
+ @Test
+ public void testDynamicMimeWithSingleApp() throws DeviceNotAvailableException {
+ runDeviceTests("SingleAppTest");
+ }
+
+ @Test
+ public void testDynamicMimeWithMultipleApps() throws DeviceNotAvailableException {
+ runDeviceTests("MultipleAppsTest");
+ }
+
+ @Test
+ public void testDynamicMimeWithMultipleGroupsPerFilter() throws DeviceNotAvailableException {
+ runDeviceTests("ComplexFilterTest");
+ }
+
+ @Test
+ public void testDynamicMimeWithAppUpdate() throws DeviceNotAvailableException {
+ runDeviceTests("update.SameGroupsTest");
+ }
+
+ @Test
+ public void testDynamicMimeWithChangedGroupsAppUpdate() throws DeviceNotAvailableException {
+ runDeviceTests("update.ChangedGroupsTest");
+ }
+
+ private void runDeviceTests(String className)
+ throws DeviceNotAvailableException {
+ runDeviceTests(PACKAGE_TEST_APP, PACKAGE_TEST_APP + "." + className);
+ }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/test/Android.bp b/hostsidetests/packagemanager/dynamicmime/test/Android.bp
new file mode 100644
index 0000000..207f050
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/Android.bp
@@ -0,0 +1,35 @@
+// 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.
+android_test_helper_app {
+ name: "CtsDynamicMimeTestApp",
+ defaults: ["cts_defaults"],
+ static_libs: [
+ "androidx.test.ext.junit",
+ "androidx.test.rules",
+ "compatibility-device-util-axt",
+ "CtsDynamicMimeCommon",
+ "hamcrest-library",
+ "android-support-test",
+ "androidx.test.uiautomator_uiautomator",
+ ],
+ srcs: ["src/**/*.java"],
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "vts",
+ "general-tests",
+ ],
+ sdk_version: "test_current",
+ platform_apis: true,
+}
\ No newline at end of file
diff --git a/hostsidetests/packagemanager/dynamicmime/test/AndroidManifest.xml b/hostsidetests/packagemanager/dynamicmime/test/AndroidManifest.xml
new file mode 100644
index 0000000..d31dcf3
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/AndroidManifest.xml
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT 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.dynamicmime.testapp">
+ <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
+ <application>
+ <uses-library android:name="android.test.runner" />
+ <activity android:name="android.dynamicmime.common.activity.FirstActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.SEND"/>
+ <action android:name="android.dynamicmime.testapp.FILTER_INFO_HOOK_group_first"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ <data android:mimeGroup="group_first"/>
+ </intent-filter>
+ </activity>
+ <activity android:name="android.dynamicmime.common.activity.SecondActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.SEND"/>
+ <action android:name="android.dynamicmime.testapp.FILTER_INFO_HOOK_group_second"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ <data android:mimeGroup="group_second"/>
+ </intent-filter>
+ </activity>
+
+ <activity android:name="android.dynamicmime.common.activity.TwoGroupsActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.SEND"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ <action android:name="android.dynamicmime.testapp.FILTER_INFO_HOOK_group_both"/>
+ <data android:mimeGroup="group_first"/>
+ <data android:mimeGroup="group_second"/>
+ </intent-filter>
+ </activity>
+
+ <activity android:name="android.dynamicmime.common.activity.TwoGroupsAndTypeActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.SEND"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ <action android:name="android.dynamicmime.testapp.FILTER_INFO_HOOK_groups_and_type"/>
+ <data android:mimeType="text/plain"/>
+ <data android:mimeGroup="group_first"/>
+ <data android:mimeGroup="group_second"/>
+ </intent-filter>
+ </activity>
+ </application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.dynamicmime.testapp">
+ </instrumentation>
+</manifest>
diff --git a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/BaseDynamicMimeTest.java b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/BaseDynamicMimeTest.java
new file mode 100644
index 0000000..ab16c74
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/BaseDynamicMimeTest.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.dynamicmime.testapp;
+
+import android.app.Instrumentation;
+import android.content.Context;
+import android.dynamicmime.testapp.assertions.MimeGroupAssertions;
+import android.dynamicmime.testapp.commands.MimeGroupCommands;
+import android.dynamicmime.testapp.util.MimeGroupOperations;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.After;
+
+/**
+ * Base class for dynamic MIME handlers feature test cases
+ */
+public abstract class BaseDynamicMimeTest extends MimeGroupOperations {
+ public BaseDynamicMimeTest(MimeGroupCommands commands, MimeGroupAssertions assertions) {
+ super(commands, assertions);
+ }
+
+ protected static Context context() {
+ return instrumentation().getContext();
+ }
+
+ protected static Context targetContext() {
+ return instrumentation().getTargetContext();
+ }
+
+ protected static Instrumentation instrumentation() {
+ return InstrumentationRegistry.getInstrumentation();
+ }
+
+ @After
+ public void tearDown() {
+ clearGroups();
+ }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/ComplexFilterTest.java b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/ComplexFilterTest.java
new file mode 100644
index 0000000..0ad981c
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/ComplexFilterTest.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.dynamicmime.testapp;
+
+import static android.dynamicmime.common.Constants.ALIAS_BOTH_GROUPS;
+import static android.dynamicmime.common.Constants.ALIAS_BOTH_GROUPS_AND_TYPE;
+import static android.dynamicmime.common.Constants.GROUP_FIRST;
+import static android.dynamicmime.common.Constants.GROUP_SECOND;
+import static android.dynamicmime.common.Constants.MIME_IMAGE_PNG;
+import static android.dynamicmime.common.Constants.MIME_TEXT_ANY;
+import static android.dynamicmime.common.Constants.MIME_TEXT_PLAIN;
+
+import android.dynamicmime.testapp.assertions.AssertionsByIntentResolution;
+import android.dynamicmime.testapp.commands.MimeGroupCommands;
+
+import org.junit.Test;
+
+/**
+ * Test cases for:
+ * - multiple MIME groups per intent filter
+ * - MIME groups and android:mimeType attribute in one filter
+ */
+public class ComplexFilterTest extends BaseDynamicMimeTest {
+ public ComplexFilterTest() {
+ super(MimeGroupCommands.testApp(context()),
+ AssertionsByIntentResolution.testApp(context()));
+ }
+
+ @Test
+ public void testMimeGroupsNotIntersect() {
+ setMimeGroup(GROUP_FIRST, MIME_IMAGE_PNG);
+ setMimeGroup(GROUP_SECOND, MIME_TEXT_ANY);
+
+ assertBothGroups(MIME_IMAGE_PNG, MIME_TEXT_ANY);
+ }
+
+ @Test
+ public void testMimeGroupsIntersect() {
+ setMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN, MIME_IMAGE_PNG);
+ setMimeGroup(GROUP_SECOND, MIME_IMAGE_PNG, MIME_TEXT_ANY);
+
+ assertBothGroups(MIME_TEXT_PLAIN, MIME_TEXT_ANY, MIME_IMAGE_PNG);
+ }
+
+ @Test
+ public void testRemoveTypeFromIntersection() {
+ setMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN, MIME_IMAGE_PNG);
+ setMimeGroup(GROUP_SECOND, MIME_IMAGE_PNG, MIME_TEXT_ANY);
+ removeMimeTypeFromGroup(GROUP_FIRST, MIME_IMAGE_PNG);
+
+ assertBothGroups(MIME_TEXT_PLAIN, MIME_TEXT_ANY, MIME_IMAGE_PNG);
+ }
+
+ @Test
+ public void testRemoveIntersectionFromBothGroups() {
+ setMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN, MIME_IMAGE_PNG);
+ setMimeGroup(GROUP_SECOND, MIME_IMAGE_PNG, MIME_TEXT_ANY);
+ removeMimeTypeFromGroup(GROUP_FIRST, MIME_IMAGE_PNG);
+ removeMimeTypeFromGroup(GROUP_SECOND, MIME_IMAGE_PNG);
+
+ assertBothGroups(MIME_TEXT_PLAIN, MIME_TEXT_ANY);
+ }
+
+ @Test
+ public void testClearGroupWithoutIntersection() {
+ setMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN);
+ setMimeGroup(GROUP_SECOND, MIME_IMAGE_PNG);
+ clearMimeGroup(GROUP_FIRST);
+
+ assertBothGroups(MIME_IMAGE_PNG);
+ }
+
+ @Test
+ public void testClearGroupWithIntersection() {
+ setMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN, MIME_IMAGE_PNG);
+ setMimeGroup(GROUP_SECOND, MIME_IMAGE_PNG, MIME_TEXT_ANY);
+ clearMimeGroup(GROUP_FIRST);
+
+ assertBothGroups(MIME_IMAGE_PNG, MIME_TEXT_ANY);
+ }
+
+ @Test
+ public void testMimeGroupNotIntersectWithStaticType() {
+ setMimeGroup(GROUP_FIRST, MIME_IMAGE_PNG);
+
+ assertGroupsAndType(MIME_IMAGE_PNG, MIME_TEXT_PLAIN);
+ }
+
+ @Test
+ public void testMimeGroupIntersectWithStaticType() {
+ setMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN);
+
+ assertGroupsAndType(MIME_TEXT_PLAIN);
+ }
+
+ @Test
+ public void testRemoveStaticTypeFromMimeGroup() {
+ setMimeGroup(GROUP_FIRST, MIME_IMAGE_PNG, MIME_TEXT_PLAIN);
+ removeMimeTypeFromGroup(GROUP_FIRST, MIME_TEXT_PLAIN);
+
+ assertGroupsAndType(MIME_IMAGE_PNG, MIME_TEXT_PLAIN);
+ }
+
+ @Test
+ public void testClearGroupContainsStaticType() {
+ setMimeGroup(GROUP_FIRST, MIME_IMAGE_PNG, MIME_TEXT_PLAIN);
+ clearMimeGroup(GROUP_FIRST);
+
+ assertGroupsAndType(MIME_TEXT_PLAIN);
+ }
+
+ @Test
+ public void testClearGroupNotContainStaticType() {
+ setMimeGroup(GROUP_FIRST, MIME_IMAGE_PNG, MIME_TEXT_ANY);
+ clearMimeGroup(GROUP_FIRST);
+
+ assertGroupsAndType(MIME_TEXT_PLAIN);
+ }
+
+ private void assertGroupsAndType(String... expectedTypes) {
+ assertMimeGroup(ALIAS_BOTH_GROUPS_AND_TYPE, expectedTypes);
+ }
+
+ private void assertBothGroups(String... expectedTypes) {
+ assertMimeGroup(ALIAS_BOTH_GROUPS, expectedTypes);
+ }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/MultipleAppsTest.java b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/MultipleAppsTest.java
new file mode 100644
index 0000000..fa5876d
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/MultipleAppsTest.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.dynamicmime.testapp;
+
+import static android.dynamicmime.common.Constants.GROUP_FIRST;
+import static android.dynamicmime.common.Constants.MIME_IMAGE_ANY;
+import static android.dynamicmime.common.Constants.MIME_TEXT_ANY;
+
+import android.dynamicmime.testapp.assertions.MimeGroupAssertions;
+import android.dynamicmime.testapp.commands.MimeGroupCommands;
+import android.dynamicmime.testapp.util.MimeGroupOperations;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test cases that verify independence of MIME groups from different apps
+ */
+@RunWith(AndroidJUnit4.class)
+public class MultipleAppsTest extends BaseDynamicMimeTest {
+ public MultipleAppsTest() {
+ super(MimeGroupCommands.testApp(context()), MimeGroupAssertions.testApp(context()));
+ }
+
+ @After
+ @Override
+ public void tearDown() {
+ helperApp().clearGroups();
+ super.tearDown();
+ }
+
+ @Test
+ public void testMimeGroupsIndependentSet() {
+ helperApp().setMimeGroup(GROUP_FIRST, MIME_TEXT_ANY);
+
+ assertMimeGroupIsEmpty(GROUP_FIRST);
+ helperApp().assertMimeGroup(GROUP_FIRST, MIME_TEXT_ANY);
+
+ setMimeGroup(GROUP_FIRST, MIME_IMAGE_ANY);
+
+ assertMimeGroup(GROUP_FIRST, MIME_IMAGE_ANY);
+ helperApp().assertMimeGroup(GROUP_FIRST, MIME_TEXT_ANY);
+ }
+
+ @Test
+ public void testMimeGroupsIndependentReset() {
+ setMimeGroup(GROUP_FIRST, MIME_IMAGE_ANY);
+ helperApp().setMimeGroup(GROUP_FIRST, MIME_TEXT_ANY);
+
+ removeMimeTypeFromGroup(GROUP_FIRST, MIME_TEXT_ANY);
+ helperApp().removeMimeTypeFromGroup(GROUP_FIRST, MIME_IMAGE_ANY);
+
+ assertMimeGroup(GROUP_FIRST, MIME_IMAGE_ANY);
+ helperApp().assertMimeGroup(GROUP_FIRST, MIME_TEXT_ANY);
+ }
+
+ @Test
+ public void testMimeGroupsIndependentClear() {
+ setMimeGroup(GROUP_FIRST, MIME_IMAGE_ANY, MIME_TEXT_ANY);
+ helperApp().setMimeGroup(GROUP_FIRST, MIME_TEXT_ANY);
+
+ clearMimeGroup(GROUP_FIRST);
+
+ assertMimeGroupIsEmpty(GROUP_FIRST);
+ helperApp().assertMimeGroup(GROUP_FIRST, MIME_TEXT_ANY);
+
+ setMimeGroup(GROUP_FIRST, MIME_TEXT_ANY);
+ helperApp().clearMimeGroup(GROUP_FIRST);
+
+ assertMimeGroup(GROUP_FIRST, MIME_TEXT_ANY);
+ helperApp().assertMimeGroupIsEmpty(GROUP_FIRST);
+ }
+
+ /**
+ * {@link MimeGroupOperations} implementation to interact with sample app MIME groups
+ */
+ private MimeGroupOperations helperApp() {
+ return new MimeGroupOperations(MimeGroupCommands.helperApp(context()),
+ MimeGroupAssertions.helperApp(context()));
+ }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/SingleAppTest.java b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/SingleAppTest.java
new file mode 100644
index 0000000..84eab63
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/SingleAppTest.java
@@ -0,0 +1,294 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.dynamicmime.testapp;
+
+import static android.dynamicmime.common.Constants.GROUP_FIRST;
+import static android.dynamicmime.common.Constants.GROUP_SECOND;
+import static android.dynamicmime.common.Constants.GROUP_UNDEFINED;
+import static android.dynamicmime.common.Constants.MIME_ANY;
+import static android.dynamicmime.common.Constants.MIME_IMAGE_ANY;
+import static android.dynamicmime.common.Constants.MIME_IMAGE_JPEG;
+import static android.dynamicmime.common.Constants.MIME_IMAGE_PNG;
+import static android.dynamicmime.common.Constants.MIME_TEXT_ANY;
+import static android.dynamicmime.common.Constants.MIME_TEXT_PLAIN;
+import static android.dynamicmime.common.Constants.MIME_TEXT_XML;
+
+import android.dynamicmime.testapp.assertions.MimeGroupAssertions;
+import android.dynamicmime.testapp.commands.MimeGroupCommands;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test cases for single app and one MIME group per intent filter scenario
+ */
+@RunWith(AndroidJUnit4.class)
+public class SingleAppTest extends BaseDynamicMimeTest {
+ public SingleAppTest() {
+ super(MimeGroupCommands.testApp(context()), MimeGroupAssertions.testApp(context()));
+ }
+
+ @Test
+ public void testGroupWithExactType() {
+ setMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN);
+
+ assertMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN);
+ assertMatchedByMimeGroup(GROUP_FIRST, MIME_TEXT_ANY, MIME_ANY);
+ assertNotMatchedByMimeGroup(GROUP_FIRST, MIME_IMAGE_ANY);
+ }
+
+ @Test
+ public void testGroupWithWildcardType() {
+ setMimeGroup(GROUP_FIRST, MIME_TEXT_ANY);
+
+ assertMimeGroup(GROUP_FIRST, MIME_TEXT_ANY);
+ assertMatchedByMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN, MIME_ANY);
+ assertNotMatchedByMimeGroup(GROUP_FIRST, MIME_IMAGE_ANY);
+ }
+
+ @Test
+ public void testGroupWithAnyType() {
+ setMimeGroup(GROUP_FIRST, MIME_ANY);
+
+ assertMimeGroup(GROUP_FIRST, MIME_ANY);
+ assertMatchedByMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN, MIME_TEXT_ANY, MIME_IMAGE_ANY);
+ }
+
+ @Test
+ public void testAddSimilarTypes() {
+ setMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN);
+ addMimeTypeToGroup(GROUP_FIRST, MIME_TEXT_ANY);
+
+ assertMimeGroup(GROUP_FIRST, MIME_TEXT_ANY, MIME_TEXT_PLAIN);
+ }
+
+ @Test
+ public void testAddDifferentTypes() {
+ setMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN);
+ addMimeTypeToGroup(GROUP_FIRST, MIME_IMAGE_ANY);
+
+ assertMimeGroup(GROUP_FIRST, MIME_IMAGE_ANY, MIME_TEXT_PLAIN);
+ }
+
+ @Test
+ public void testRemoveExactType() {
+ setMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN);
+ removeMimeTypeFromGroup(GROUP_FIRST, MIME_TEXT_PLAIN);
+
+ assertMimeGroupIsEmpty(GROUP_FIRST);
+ assertNotMatchedByMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN);
+ }
+
+ @Test
+ public void testRemoveDifferentType() {
+ setMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN);
+ removeMimeTypeFromGroup(GROUP_FIRST, MIME_TEXT_ANY);
+
+ assertMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN);
+ }
+
+ @Test
+ public void testRemoveSameBaseType_keepSpecific() {
+ testKeepAndRemoveSimilarTypes(MIME_TEXT_PLAIN, MIME_TEXT_ANY);
+ }
+
+ @Test
+ public void testRemoveSameBaseType_keepWildcard() {
+ testKeepAndRemoveSimilarTypes(MIME_TEXT_ANY, MIME_TEXT_PLAIN);
+ }
+
+ @Test
+ public void testRemoveAnyType_keepSpecific() {
+ testKeepAndRemoveSimilarTypes(MIME_TEXT_PLAIN, MIME_ANY);
+ }
+
+ @Test
+ public void testRemoveAnyType_keepWildcard() {
+ testKeepAndRemoveSimilarTypes(MIME_ANY, MIME_TEXT_PLAIN);
+ }
+
+ @Test
+ public void testResetWithoutIntersection() {
+ setMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN, MIME_TEXT_XML);
+ setMimeGroup(GROUP_FIRST, MIME_IMAGE_JPEG, MIME_IMAGE_PNG);
+
+ assertMimeGroup(GROUP_FIRST, MIME_IMAGE_JPEG, MIME_IMAGE_PNG);
+ }
+
+ @Test
+ public void testResetWithIntersection() {
+ setMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN, MIME_TEXT_XML);
+ setMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN, MIME_IMAGE_PNG);
+
+ assertMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN, MIME_IMAGE_PNG);
+ }
+
+ @Test
+ public void testResetToEmpty() {
+ setMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN, MIME_TEXT_XML);
+ setMimeGroup(GROUP_FIRST, (String[]) null);
+
+ assertMimeGroupIsEmpty(GROUP_FIRST);
+ }
+
+ @Test
+ public void testClear() {
+ setMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN, MIME_IMAGE_PNG);
+ clearMimeGroup(GROUP_FIRST);
+
+ assertMimeGroupIsEmpty(GROUP_FIRST);
+ }
+
+ @Test
+ public void testUndefinedGroupIsNullAfterAdd() {
+ addMimeTypeToGroup(GROUP_UNDEFINED, MIME_TEXT_PLAIN);
+
+ assertMimeGroupIsNull(GROUP_UNDEFINED);
+ }
+
+ @Test
+ public void testUndefinedGroupIsNullAfterSet() {
+ setMimeGroup(GROUP_UNDEFINED, MIME_TEXT_PLAIN);
+
+ assertMimeGroupIsNull(GROUP_UNDEFINED);
+ }
+
+ @Test
+ public void testUndefinedGroupIsNullAfterRemove() {
+ removeMimeTypeFromGroup(GROUP_UNDEFINED, MIME_TEXT_PLAIN);
+
+ assertMimeGroupIsNull(GROUP_UNDEFINED);
+ }
+
+ @Test
+ public void testUndefinedGroupIsNullAfterClear() {
+ clearMimeGroup(GROUP_UNDEFINED);
+
+ assertMimeGroupIsNull(GROUP_UNDEFINED);
+ }
+
+ @Test
+ public void testDefinedGroupNotNullAfterRemove() {
+ setMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN);
+ removeMimeTypeFromGroup(GROUP_FIRST, MIME_TEXT_PLAIN);
+
+ assertMimeGroupIsEmpty(GROUP_FIRST);
+ }
+
+ @Test
+ public void testDefinedGroupNotNullAfterClear() {
+ setMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN);
+ clearMimeGroup(GROUP_FIRST);
+
+ assertMimeGroupIsEmpty(GROUP_FIRST);
+ }
+
+ @Test
+ public void testDefinedGroupNotNullAfterSetEmpty() {
+ setMimeGroup(GROUP_FIRST, (String[]) null);
+ clearMimeGroup(GROUP_FIRST);
+
+ assertMimeGroupIsEmpty(GROUP_FIRST);
+ }
+
+ @Test
+ public void testDefinedGroupNotNullAfterSetNull() {
+ setMimeGroup(GROUP_FIRST, (String[]) null);
+ clearMimeGroup(GROUP_FIRST);
+
+ assertMimeGroupIsEmpty(GROUP_FIRST);
+ }
+
+ @Test
+ public void testMimeGroupsIndependentAdd() {
+ addMimeTypeToGroup(GROUP_SECOND, MIME_TEXT_ANY);
+
+ assertMimeGroup(GROUP_SECOND, MIME_TEXT_ANY);
+ assertMimeGroupIsEmpty(GROUP_FIRST);
+ }
+
+ @Test
+ public void testMimeGroupsIndependentAddToBoth() {
+ addMimeTypeToGroup(GROUP_SECOND, MIME_TEXT_ANY);
+ addMimeTypeToGroup(GROUP_FIRST, MIME_IMAGE_ANY);
+
+ assertMimeGroup(GROUP_FIRST, MIME_IMAGE_ANY);
+ assertMimeGroup(GROUP_SECOND, MIME_TEXT_ANY);
+ }
+
+ @Test
+ public void testMimeGroupsIndependentRemove() {
+ setMimeGroup(GROUP_FIRST, MIME_IMAGE_ANY);
+ setMimeGroup(GROUP_SECOND, MIME_TEXT_ANY);
+
+ removeMimeTypeFromGroup(GROUP_FIRST, MIME_TEXT_ANY);
+ removeMimeTypeFromGroup(GROUP_SECOND, MIME_IMAGE_ANY);
+
+ assertMimeGroup(GROUP_FIRST, MIME_IMAGE_ANY);
+ assertMimeGroup(GROUP_SECOND, MIME_TEXT_ANY);
+ }
+
+ @Test
+ public void testMimeGroupsIndependentClear() {
+ setMimeGroup(GROUP_FIRST, MIME_IMAGE_ANY, MIME_TEXT_ANY);
+ setMimeGroup(GROUP_SECOND, MIME_TEXT_ANY);
+
+ clearMimeGroup(GROUP_FIRST);
+
+ assertMimeGroupIsEmpty(GROUP_FIRST);
+ assertMimeGroup(GROUP_SECOND, MIME_TEXT_ANY);
+ }
+
+ @Test
+ public void testMimeGroupsIndependentClearBoth() {
+ setMimeGroup(GROUP_FIRST, MIME_IMAGE_ANY, MIME_TEXT_ANY);
+ setMimeGroup(GROUP_SECOND, MIME_TEXT_ANY);
+ clearMimeGroup(GROUP_FIRST);
+ setMimeGroup(GROUP_FIRST, MIME_TEXT_ANY);
+ clearMimeGroup(GROUP_SECOND);
+
+ assertMimeGroup(GROUP_FIRST, MIME_TEXT_ANY);
+ assertMimeGroupIsEmpty(GROUP_SECOND);
+ }
+
+ @Test
+ public void testMimeGroupsIndependentSet() {
+ setMimeGroup(GROUP_FIRST, MIME_IMAGE_PNG, MIME_IMAGE_ANY);
+
+ assertMimeGroup(GROUP_FIRST, MIME_IMAGE_PNG, MIME_IMAGE_ANY);
+ assertMimeGroupIsEmpty(GROUP_SECOND);
+ }
+
+ @Test
+ public void testMimeGroupsIndependentSetBoth() {
+ setMimeGroup(GROUP_FIRST, MIME_IMAGE_PNG, MIME_IMAGE_ANY);
+ setMimeGroup(GROUP_SECOND, MIME_TEXT_PLAIN, MIME_TEXT_ANY);
+
+ assertMimeGroup(GROUP_FIRST, MIME_IMAGE_PNG, MIME_IMAGE_ANY);
+ assertMimeGroup(GROUP_SECOND, MIME_TEXT_PLAIN, MIME_TEXT_ANY);
+ }
+
+ private void testKeepAndRemoveSimilarTypes(String mimeTypeToKeep, String mimeTypeToRemove) {
+ addMimeTypeToGroup(GROUP_FIRST, mimeTypeToKeep, mimeTypeToRemove);
+ removeMimeTypeFromGroup(GROUP_FIRST, mimeTypeToRemove);
+
+ assertMimeGroup(GROUP_FIRST, mimeTypeToKeep);
+ assertMatchedByMimeGroup(GROUP_FIRST, mimeTypeToRemove);
+ }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/assertions/AppAssertionsByGroupData.java b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/assertions/AppAssertionsByGroupData.java
new file mode 100644
index 0000000..95eaa12
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/assertions/AppAssertionsByGroupData.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.dynamicmime.testapp.assertions;
+
+import android.content.Context;
+import android.dynamicmime.testapp.util.AppMimeGroups;
+import android.util.ArraySet;
+
+import java.util.Set;
+
+public class AppAssertionsByGroupData extends AssertionsByGroupData {
+ private final AppMimeGroups mAppMimeGroups;
+
+ AppAssertionsByGroupData(Context context, String targetPackage) {
+ mAppMimeGroups = AppMimeGroups.with(context, targetPackage);
+ }
+
+ @Override
+ protected Set<String> getMimeGroup(String mimeGroup) {
+ String[] mimeTypes = mAppMimeGroups.get(mimeGroup);
+ return mimeTypes != null ? new ArraySet<>(mimeTypes) : null;
+ }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/assertions/AssertionsByGroupData.java b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/assertions/AssertionsByGroupData.java
new file mode 100644
index 0000000..eb74b3d
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/assertions/AssertionsByGroupData.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.dynamicmime.testapp.assertions;
+
+import static android.dynamicmime.common.Constants.PACKAGE_HELPER_APP;
+import static android.dynamicmime.common.Constants.PACKAGE_UPDATE_APP;
+
+import android.content.Context;
+
+import org.junit.Assert;
+
+import java.util.Set;
+
+public abstract class AssertionsByGroupData extends MimeGroupAssertions {
+ @Override
+ public void assertMatchedByMimeGroup(String mimeGroup, String mimeType) {
+ // We cannot check if type is matched by group via group info from PackageManager
+ }
+
+ @Override
+ public void assertNotMatchedByMimeGroup(String mimeGroup, String mimeType) {
+ // We cannot check if type is matched by group via group info from PackageManager
+ }
+
+ @Override
+ public void assertMimeGroupInternal(String mimeGroup, Set<String> mimeTypes) {
+ Assert.assertEquals(getMimeGroup(mimeGroup), mimeTypes);
+ }
+
+ protected abstract Set<String> getMimeGroup(String mimeGroup);
+
+ public static MimeGroupAssertions testApp(Context context) {
+ return new TestAppAssertionsByGroupData(context);
+ }
+
+ public static MimeGroupAssertions helperApp(Context context) {
+ return new AppAssertionsByGroupData(context, PACKAGE_HELPER_APP);
+ }
+
+ public static MimeGroupAssertions appWithUpdates(Context context) {
+ return new AppAssertionsByGroupData(context, PACKAGE_UPDATE_APP);
+ }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/assertions/AssertionsByIntentResolution.java b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/assertions/AssertionsByIntentResolution.java
new file mode 100644
index 0000000..13f4d4d
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/assertions/AssertionsByIntentResolution.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.dynamicmime.testapp.assertions;
+
+import static android.dynamicmime.common.Constants.ACTIVITY_BOTH;
+import static android.dynamicmime.common.Constants.ACTIVITY_BOTH_AND_TYPE;
+import static android.dynamicmime.common.Constants.ACTIVITY_FIRST;
+import static android.dynamicmime.common.Constants.ACTIVITY_SECOND;
+import static android.dynamicmime.common.Constants.ALIAS_BOTH_GROUPS;
+import static android.dynamicmime.common.Constants.ALIAS_BOTH_GROUPS_AND_TYPE;
+import static android.dynamicmime.common.Constants.GROUP_FIRST;
+import static android.dynamicmime.common.Constants.GROUP_SECOND;
+import static android.dynamicmime.common.Constants.PACKAGE_HELPER_APP;
+import static android.dynamicmime.common.Constants.PACKAGE_TEST_APP;
+import static android.dynamicmime.common.Constants.PACKAGE_UPDATE_APP;
+import static android.dynamicmime.testapp.util.IntentsResolutionHelper.resolve;
+
+import static org.junit.Assert.assertEquals;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.util.ArraySet;
+
+import java.util.Set;
+
+public class AssertionsByIntentResolution extends MimeGroupAssertions {
+ private final Context mContext;
+ private final String mPackageName;
+
+ private AssertionsByIntentResolution(String packageName, Context context) {
+ mContext = context;
+ mPackageName = packageName;
+ }
+
+ @Override
+ public void assertMatchedByMimeGroup(String mimeGroup, String mimeType) {
+ resolve(mContext, intentSend(mimeType))
+ .assertMatched(mPackageName, activity(mimeGroup));
+ }
+
+ @Override
+ public void assertNotMatchedByMimeGroup(String mimeGroup, String mimeType) {
+ resolve(mContext, intentSend(mimeType))
+ .assertNotMatched(mPackageName, activity(mimeGroup));
+ }
+
+ @Override
+ public void assertMimeGroupInternal(String mimeGroup, Set<String> mimeTypes) {
+ if (mimeTypes == null) {
+ // We can't distinguish empty group from null group via intent resolution
+ return;
+ }
+
+ // Check MIME group types retrieved from declared intent filter
+ assertEquals("Mismatch for " + mimeGroup + " MIME types",
+ mimeTypes, getMimeGroup(mimeGroup, mimeTypes));
+
+ // Additionally we check if all types are matched by this MIME group
+ for (String mimeType: mimeTypes) {
+ assertMatchedByMimeGroup(mimeGroup, mimeType);
+ }
+ }
+
+ public static MimeGroupAssertions testApp(Context context) {
+ return new AssertionsByIntentResolution(PACKAGE_TEST_APP, context);
+ }
+
+ public static MimeGroupAssertions helperApp(Context context) {
+ return new AssertionsByIntentResolution(PACKAGE_HELPER_APP, context);
+ }
+
+ public static MimeGroupAssertions appWithUpdates(Context context) {
+ return new AssertionsByIntentResolution(PACKAGE_UPDATE_APP, context);
+ }
+
+ private Set<String> getMimeGroup(String mimeGroup, Set<String> mimeTypes) {
+ IntentFilter filter = resolve(mContext, intentGroup(mimeGroup, mimeTypes))
+ .getAny()
+ .filter;
+
+ Set<String> actualTypes = new ArraySet<>(filter.countDataTypes());
+
+ for (int i = 0; i < filter.countDataTypes(); i++) {
+ actualTypes.add(appendWildcard(filter.getDataType(i)));
+ }
+
+ return actualTypes;
+ }
+
+ private Intent intentGroup(String mimeGroup, Set<String> mimeTypes) {
+ Intent intent = new Intent(action(mimeGroup));
+
+ if (mimeTypes != null && !mimeTypes.isEmpty()) {
+ intent.setType(mimeTypes.iterator().next());
+ }
+
+ return intent;
+ }
+
+ private static Intent intentSend(String mimeType) {
+ Intent sendIntent = new Intent();
+ sendIntent.setAction(Intent.ACTION_SEND);
+ sendIntent.setType(mimeType);
+ return sendIntent;
+ }
+
+ private static String appendWildcard(String actualType) {
+ int slashpos = actualType.indexOf('/');
+
+ if (slashpos < 0) {
+ return actualType + "/*";
+ } else {
+ return actualType;
+ }
+ }
+
+ private static String activity(String mimeGroup) {
+ switch (mimeGroup) {
+ case GROUP_FIRST:
+ return ACTIVITY_FIRST;
+ case GROUP_SECOND:
+ return ACTIVITY_SECOND;
+ case ALIAS_BOTH_GROUPS:
+ return ACTIVITY_BOTH;
+ case ALIAS_BOTH_GROUPS_AND_TYPE:
+ return ACTIVITY_BOTH_AND_TYPE;
+ default:
+ throw new IllegalArgumentException("Unexpected MIME group " + mimeGroup);
+ }
+ }
+
+ private String action(String mimeGroup) {
+ return mPackageName + ".FILTER_INFO_HOOK_"+ mimeGroup;
+ }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/assertions/MimeGroupAssertions.java b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/assertions/MimeGroupAssertions.java
new file mode 100644
index 0000000..105e6eb
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/assertions/MimeGroupAssertions.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.dynamicmime.testapp.assertions;
+
+import android.content.Context;
+import android.util.ArraySet;
+
+import java.util.Collections;
+import java.util.Set;
+
+public abstract class MimeGroupAssertions {
+ public abstract void assertMatchedByMimeGroup(String mimeGroup, String mimeType);
+
+ public abstract void assertNotMatchedByMimeGroup(String mimeGroup, String mimeType);
+
+ public abstract void assertMimeGroupInternal(String mimeGroup, Set<String> mimeTypes);
+
+ public final void assertMimeGroup(String mimeGroup, String... mimeTypes) {
+ assertMimeGroupInternal(mimeGroup, new ArraySet<>(mimeTypes));
+ }
+
+ public final void assertMimeGroupIsEmpty(String mimeGroup) {
+ assertMimeGroupInternal(mimeGroup, Collections.emptySet());
+ }
+
+ public final void assertMimeGroupIsNull(String mimeGroup) {
+ assertMimeGroupInternal(mimeGroup, null);
+ }
+
+ public final void assertMatchedByMimeGroup(String mimeGroup, String... mimeTypes) {
+ for (String mimeType: mimeTypes) {
+ assertMatchedByMimeGroup(mimeGroup, mimeType);
+ }
+ }
+
+ public final void assertNotMatchedByMimeGroup(String mimeGroup, String... mimeTypes) {
+ for (String mimeType: mimeTypes) {
+ assertNotMatchedByMimeGroup(mimeGroup, mimeType);
+ }
+ }
+
+ public static MimeGroupAssertions testApp(Context context) {
+ return AssertionsByGroupData.testApp(context)
+ .mergeWith(AssertionsByIntentResolution.testApp(context));
+ }
+
+ public static MimeGroupAssertions helperApp(Context context) {
+ return AssertionsByGroupData.helperApp(context)
+ .mergeWith(AssertionsByIntentResolution.helperApp(context));
+ }
+
+ public static MimeGroupAssertions appWithUpdates(Context context) {
+ return AssertionsByGroupData.appWithUpdates(context)
+ .mergeWith(AssertionsByIntentResolution.appWithUpdates(context));
+ }
+
+ public static MimeGroupAssertions noOp() {
+ return new MimeGroupAssertions() {
+ @Override
+ public void assertMatchedByMimeGroup(String mimeGroup, String mimeType) {
+ }
+
+ @Override
+ public void assertNotMatchedByMimeGroup(String mimeGroup, String mimeType) {
+ }
+
+ @Override
+ public void assertMimeGroupInternal(String mimeGroup, Set<String> mimeTypes) {
+ }
+ };
+ }
+
+ public static MimeGroupAssertions notUsed() {
+ return new MimeGroupAssertions() {
+ @Override
+ public void assertMatchedByMimeGroup(String group, String type) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void assertNotMatchedByMimeGroup(String group, String type) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void assertMimeGroupInternal(String group, Set<String> types) {
+ throw new UnsupportedOperationException();
+ }
+ };
+ }
+
+ protected final MimeGroupAssertions mergeWith(MimeGroupAssertions other) {
+ return new MimeGroupAssertions() {
+ @Override
+ public void assertMatchedByMimeGroup(String mimeGroup, String mimeType) {
+ other.assertMatchedByMimeGroup(mimeGroup, mimeType);
+ MimeGroupAssertions.this.assertMatchedByMimeGroup(mimeGroup, mimeType);
+ }
+
+ @Override
+ public void assertNotMatchedByMimeGroup(String mimeGroup, String mimeType) {
+ other.assertNotMatchedByMimeGroup(mimeGroup, mimeType);
+ MimeGroupAssertions.this.assertNotMatchedByMimeGroup(mimeGroup, mimeType);
+ }
+
+ @Override
+ public void assertMimeGroupInternal(String mimeGroup, Set<String> mimeTypes) {
+ other.assertMimeGroupInternal(mimeGroup, mimeTypes);
+ MimeGroupAssertions.this.assertMimeGroupInternal(mimeGroup, mimeTypes);
+ }
+ };
+ }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/assertions/TestAppAssertionsByGroupData.java b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/assertions/TestAppAssertionsByGroupData.java
new file mode 100644
index 0000000..017fa44
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/assertions/TestAppAssertionsByGroupData.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.dynamicmime.testapp.assertions;
+
+import android.content.Context;
+
+import java.util.Set;
+
+class TestAppAssertionsByGroupData extends AssertionsByGroupData {
+ private final Context mContext;
+
+ TestAppAssertionsByGroupData(Context context) {
+ mContext = context;
+ }
+
+ @Override
+ protected Set<String> getMimeGroup(String mimeGroup) {
+ return mContext.getPackageManager().getMimeGroup(mimeGroup);
+ }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/commands/AppCommands.java b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/commands/AppCommands.java
new file mode 100644
index 0000000..83400f4
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/commands/AppCommands.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.dynamicmime.testapp.commands;
+
+import android.content.Context;
+import android.dynamicmime.testapp.util.AppMimeGroups;
+import android.util.ArraySet;
+
+import androidx.annotation.Nullable;
+
+import java.util.Set;
+
+public class AppCommands implements MimeGroupCommands {
+ private final AppMimeGroups mAppMimeGroups;
+
+ AppCommands(Context context, String targetPackage) {
+ mAppMimeGroups = AppMimeGroups.with(context, targetPackage);
+ }
+
+ @Override
+ public void setMimeGroup(String mimeGroup, Set<String> mimeTypes) {
+ mAppMimeGroups.set(mimeGroup, mimeTypes.toArray(new String[0]));
+ }
+
+ @Override
+ public void clearMimeGroup(String mimeGroup) {
+ mAppMimeGroups.clear(mimeGroup);
+ }
+
+ @Nullable
+ @Override
+ public Set<String> getMimeGroupInternal(String mimeGroup) {
+ return new ArraySet<>(mAppMimeGroups.get(mimeGroup));
+ }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/commands/MimeGroupCommands.java b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/commands/MimeGroupCommands.java
new file mode 100644
index 0000000..27a9cb2
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/commands/MimeGroupCommands.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.dynamicmime.testapp.commands;
+
+import static android.dynamicmime.common.Constants.GROUP_FIRST;
+import static android.dynamicmime.common.Constants.GROUP_SECOND;
+import static android.dynamicmime.common.Constants.GROUP_THIRD;
+import static android.dynamicmime.common.Constants.GROUP_UNDEFINED;
+import static android.dynamicmime.common.Constants.PACKAGE_HELPER_APP;
+import static android.dynamicmime.common.Constants.PACKAGE_PREFERRED_APP;
+import static android.dynamicmime.common.Constants.PACKAGE_UPDATE_APP;
+
+import android.content.Context;
+import android.util.ArraySet;
+
+import androidx.annotation.Nullable;
+
+import java.util.Set;
+
+public interface MimeGroupCommands {
+ void setMimeGroup(String mimeGroup, Set<String> mimeTypes);
+ void clearMimeGroup(String mimeGroup);
+
+ @Nullable
+ Set<String> getMimeGroupInternal(String mimeGroup);
+
+ default void addMimeTypeToGroup(String mimeGroup, String... mimeTypes) {
+ ArraySet<String> typesToAdd = new ArraySet<>(mimeTypes);
+
+ Set<String> newMimeTypes = new ArraySet<>(getMimeGroupInternal(mimeGroup));
+ newMimeTypes.addAll(typesToAdd);
+
+ setMimeGroup(mimeGroup, newMimeTypes);
+ }
+
+ default void removeMimeTypeFromGroup(String mimeGroup, String... mimeTypes) {
+ ArraySet<String> typesToRemove = new ArraySet<>(mimeTypes);
+
+ Set<String> newMimeTypes = new ArraySet<>(getMimeGroupInternal(mimeGroup));
+ newMimeTypes.removeAll(typesToRemove);
+
+ setMimeGroup(mimeGroup, newMimeTypes);
+ }
+
+ default void setMimeGroup(String mimeGroup, String... mimeTypes) {
+ setMimeGroup(mimeGroup, new ArraySet<>(mimeTypes));
+ }
+
+ default void clearGroups() {
+ clearMimeGroup(GROUP_FIRST);
+ clearMimeGroup(GROUP_SECOND);
+ clearMimeGroup(GROUP_THIRD);
+ clearMimeGroup(GROUP_UNDEFINED);
+ }
+
+ static MimeGroupCommands testApp(Context context) {
+ return new TestAppCommands(context);
+ }
+
+ static MimeGroupCommands helperApp(Context context) {
+ return new AppCommands(context, PACKAGE_HELPER_APP);
+ }
+
+ static MimeGroupCommands appWithUpdates(Context context) {
+ return new AppCommands(context, PACKAGE_UPDATE_APP);
+ }
+
+ static MimeGroupCommands preferredApp(Context context) {
+ return new AppCommands(context, PACKAGE_PREFERRED_APP);
+ }
+
+ static MimeGroupCommands noOp() {
+ return new MimeGroupCommands() {
+ @Override
+ public void setMimeGroup(String mimeGroup, Set<String> mimeTypes) {
+ }
+
+ @Override
+ public void clearMimeGroup(String mimeGroup) {
+ }
+
+ @Override
+ public Set<String> getMimeGroupInternal(String mimeGroup) {
+ return null;
+ }
+ };
+ }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/commands/TestAppCommands.java b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/commands/TestAppCommands.java
new file mode 100644
index 0000000..82dcb8853
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/commands/TestAppCommands.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.dynamicmime.testapp.commands;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+
+import androidx.annotation.Nullable;
+
+import java.util.Set;
+
+public class TestAppCommands implements MimeGroupCommands {
+ private PackageManager mPM;
+
+ TestAppCommands(Context context) {
+ mPM = context.getPackageManager();
+ }
+
+ @Override
+ public void setMimeGroup(String mimeGroup, Set<String> mimeTypes) {
+ mPM.setMimeGroup(mimeGroup, mimeTypes);
+ }
+
+ @Override
+ public void clearMimeGroup(String mimeGroup) {
+ mPM.clearMimeGroup(mimeGroup);
+ }
+
+ @Nullable
+ @Override
+ public Set<String> getMimeGroupInternal(String mimeGroup) {
+ return mPM.getMimeGroup(mimeGroup);
+ }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/preferred/PreferredActivitiesTest.java b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/preferred/PreferredActivitiesTest.java
new file mode 100644
index 0000000..5ef052ef
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/preferred/PreferredActivitiesTest.java
@@ -0,0 +1,323 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.dynamicmime.testapp.preferred;
+
+import static android.dynamicmime.common.Constants.ACTIVITY_BOTH;
+import static android.dynamicmime.common.Constants.ACTIVITY_FIRST;
+import static android.dynamicmime.common.Constants.APK_PREFERRED_APP;
+import static android.dynamicmime.common.Constants.GROUP_FIRST;
+import static android.dynamicmime.common.Constants.GROUP_SECOND;
+import static android.dynamicmime.common.Constants.GROUP_THIRD;
+import static android.dynamicmime.common.Constants.MIME_IMAGE_PNG;
+import static android.dynamicmime.common.Constants.MIME_TEXT_PLAIN;
+import static android.dynamicmime.common.Constants.PACKAGE_PREFERRED_APP;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import android.content.Intent;
+import android.dynamicmime.testapp.BaseDynamicMimeTest;
+import android.dynamicmime.testapp.assertions.MimeGroupAssertions;
+import android.dynamicmime.testapp.commands.MimeGroupCommands;
+import android.dynamicmime.testapp.util.Utils;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.BySelector;
+import androidx.test.uiautomator.Direction;
+import androidx.test.uiautomator.UiDevice;
+import androidx.test.uiautomator.UiObject2;
+import androidx.test.uiautomator.Until;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.TimeUnit;
+
+@RunWith(AndroidJUnit4.class)
+public class PreferredActivitiesTest extends BaseDynamicMimeTest {
+ private static final BySelector BUTTON_ALWAYS = By.res("android:id/button_always");
+ private static final BySelector RESOLVER_DIALOG = By.res("android:id/contentPanel");
+
+ private static final long TIMEOUT = TimeUnit.SECONDS.toMillis(5);
+
+ private TestStrategy mTest;
+
+ public PreferredActivitiesTest() {
+ super(MimeGroupCommands.preferredApp(context()), MimeGroupAssertions.notUsed());
+ }
+
+ @Before
+ public void setUp() {
+ Utils.installApk(APK_PREFERRED_APP);
+ }
+
+ @After
+ public void tearDown() {
+ super.tearDown();
+ Utils.uninstallApp(PACKAGE_PREFERRED_APP);
+ }
+
+ @Test
+ public void testRemoveFromGroup() {
+ setStrategyAndRun(new TestStrategy() {
+ @Override
+ public void prepareMimeGroups() {
+ addMimeTypeToGroup(GROUP_FIRST, mimeType());
+ }
+
+ @Override
+ public void changeMimeGroups() {
+ removeMimeTypeFromGroup(GROUP_FIRST, mimeType());
+ }
+
+ @Override
+ public String preferredActivity() {
+ return ACTIVITY_FIRST;
+ }
+ });
+ }
+
+ @Test
+ public void testAddToGroup() {
+ setStrategyAndRun(new TestStrategy() {
+ @Override
+ public void prepareMimeGroups() {
+ addMimeTypeToGroup(GROUP_FIRST, mimeType());
+ }
+
+ @Override
+ public void changeMimeGroups() {
+ addMimeTypeToGroup(GROUP_FIRST, MIME_IMAGE_PNG);
+ }
+
+ @Override
+ public void revertMimeGroupsChange() {
+ removeMimeTypeFromGroup(GROUP_FIRST, MIME_IMAGE_PNG);
+ }
+
+ @Override
+ public String preferredActivity() {
+ return ACTIVITY_FIRST;
+ }
+ });
+ }
+
+ @Test
+ public void testClearGroup() {
+ setStrategyAndRun(new TestStrategy() {
+ @Override
+ public void prepareMimeGroups() {
+ addMimeTypeToGroup(GROUP_FIRST, mimeType());
+ }
+
+ @Override
+ public void changeMimeGroups() {
+ clearMimeGroup(GROUP_FIRST);
+ }
+
+ @Override
+ public String preferredActivity() {
+ return ACTIVITY_FIRST;
+ }
+ });
+ }
+
+ @Test
+ public void testModifyGroupWithoutActualGroupChanges() {
+ setStrategyAndRun(new TestStrategy() {
+ @Override
+ public void prepareMimeGroups() {
+ addMimeTypeToGroup(GROUP_FIRST, mimeType());
+ }
+
+ @Override
+ public void changeMimeGroups() {
+ addMimeTypeToGroup(GROUP_FIRST, mimeType());
+ }
+
+ @Override
+ public String preferredActivity() {
+ return ACTIVITY_FIRST;
+ }
+
+ @Override
+ public boolean isActivityPreferredAfterRevert() {
+ return true;
+ }
+
+ @Override
+ public boolean isActivityPreferredAfterChange() {
+ return true;
+ }
+ });
+ }
+
+ @Test
+ public void testModifyGroupWithoutActualIntentFilterChanges() {
+ setStrategyAndRun(new TestStrategy() {
+ @Override
+ public void prepareMimeGroups() {
+ addMimeTypeToGroup(GROUP_THIRD, mimeType());
+ addMimeTypeToGroup(GROUP_SECOND, mimeType());
+ }
+
+ @Override
+ public void changeMimeGroups() {
+ removeMimeTypeFromGroup(GROUP_THIRD, mimeType());
+ }
+
+ @Override
+ public void revertMimeGroupsChange() {
+ addMimeTypeToGroup(GROUP_THIRD, mimeType());
+ }
+
+ @Override
+ public String preferredActivity() {
+ return ACTIVITY_BOTH;
+ }
+
+ @Override
+ public boolean isActivityPreferredAfterRevert() {
+ return true;
+ }
+
+ @Override
+ public boolean isActivityPreferredAfterChange() {
+ return true;
+ }
+ });
+ }
+
+ private void setStrategyAndRun(TestStrategy test) {
+ mTest = test;
+ runTest();
+ }
+
+ private void runTest() {
+ mTest.prepareMimeGroups();
+ setPreferredActivity();
+
+ mTest.changeMimeGroups();
+ checkPreferredActivityAfterChange();
+
+ mTest.revertMimeGroupsChange();
+ checkPreferredActivityAfterRevert();
+
+ getUiDevice().pressHome();
+ }
+
+ private void setPreferredActivity() {
+ triggerResolutionDialog();
+
+ verifyDialogIsShown(true);
+
+ chooseActivity("TestApp" + mTest.preferredActivity());
+ }
+
+ private void checkPreferredActivityAfterChange() {
+ checkPreferredActivity(mTest.isActivityPreferredAfterChange());
+ }
+
+ private void checkPreferredActivityAfterRevert() {
+ checkPreferredActivity(mTest.isActivityPreferredAfterRevert());
+ }
+
+ private void checkPreferredActivity(boolean hasPreferredActivity) {
+ triggerResolutionDialog();
+ verifyResolutionDialog(hasPreferredActivity);
+ }
+
+ private void triggerResolutionDialog() {
+ getUiDevice().pressHome();
+ sendIntent(mTest.mimeType());
+ }
+
+ private void verifyResolutionDialog(boolean shouldLaunchActivity) {
+ verifyDialogIsShown(!shouldLaunchActivity);
+ getUiDevice().pressBack();
+ }
+
+ private void verifyDialogIsShown(boolean shouldBeShown) {
+ UiObject2 buttonAlways = getUiDevice().wait(Until.findObject(BUTTON_ALWAYS), TIMEOUT);
+
+ if (shouldBeShown) {
+ assertNotNull(buttonAlways);
+ } else {
+ assertNull(buttonAlways);
+ }
+ }
+
+ private void chooseActivity(String label) {
+ findActivityInDialog(label).click();
+ chooseUseAlways();
+
+ getUiDevice().pressBack();
+ }
+
+ private UiObject2 findActivityInDialog(String label) {
+ getUiDevice()
+ .wait(Until.findObject(RESOLVER_DIALOG), TIMEOUT)
+ .swipe(Direction.UP, 1f);
+
+ return getUiDevice().findObject(By.text(label));
+ }
+
+ private void chooseUseAlways() {
+ getUiDevice()
+ .findObject(BUTTON_ALWAYS)
+ .click();
+ }
+
+ private interface TestStrategy {
+ void prepareMimeGroups();
+
+ void changeMimeGroups();
+
+ default void revertMimeGroupsChange() {
+ prepareMimeGroups();
+ }
+
+ default String mimeType() {
+ return MIME_TEXT_PLAIN;
+ }
+
+ String preferredActivity();
+
+ default boolean isActivityPreferredAfterChange() {
+ return false;
+ }
+
+ default boolean isActivityPreferredAfterRevert() {
+ return false;
+ }
+ }
+
+ private static UiDevice getUiDevice() {
+ return UiDevice.getInstance(instrumentation());
+ }
+
+ private static void sendIntent(String mimeType) {
+ Intent sendIntent = new Intent();
+ sendIntent.setAction(Intent.ACTION_SEND);
+ sendIntent.setType(mimeType);
+ sendIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ targetContext().startActivity(sendIntent, null);
+ }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/reboot/PostRebootComplexFilterTest.java b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/reboot/PostRebootComplexFilterTest.java
new file mode 100644
index 0000000..19f8bab
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/reboot/PostRebootComplexFilterTest.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.dynamicmime.testapp.reboot;
+
+import android.dynamicmime.testapp.ComplexFilterTest;
+import android.dynamicmime.testapp.commands.MimeGroupCommands;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.runner.RunWith;
+
+/**
+ * Post-reboot part of {@link ComplexFilterTest}
+ *
+ * {@link MimeGroupCommands} are disabled during test body execution
+ */
+@RunWith(AndroidJUnit4.class)
+public class PostRebootComplexFilterTest extends ComplexFilterTest {
+ private MimeGroupCommands mCommands;
+
+ public PostRebootComplexFilterTest() {
+ super();
+ mCommands = getCommands();
+ }
+
+ @Before
+ public void setUp() {
+ setCommands(MimeGroupCommands.noOp());
+ }
+
+ @After
+ @Override
+ public void tearDown() {
+ setCommands(mCommands);
+ super.tearDown();
+ }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/reboot/PostRebootSingleAppTest.java b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/reboot/PostRebootSingleAppTest.java
new file mode 100644
index 0000000..3e4c86d
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/reboot/PostRebootSingleAppTest.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.dynamicmime.testapp.reboot;
+
+import android.dynamicmime.testapp.SingleAppTest;
+import android.dynamicmime.testapp.commands.MimeGroupCommands;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.runner.RunWith;
+
+/**
+ * Post-reboot part of {@link SingleAppTest}
+ *
+ * {@link MimeGroupCommands} are disabled during test body execution
+ */
+@RunWith(AndroidJUnit4.class)
+public class PostRebootSingleAppTest extends SingleAppTest {
+ private MimeGroupCommands mCommands;
+
+ public PostRebootSingleAppTest() {
+ super();
+ mCommands = getCommands();
+ }
+
+ @Before
+ public void setUp() {
+ setCommands(MimeGroupCommands.noOp());
+ }
+
+ @After
+ @Override
+ public void tearDown() {
+ setCommands(mCommands);
+ super.tearDown();
+ }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/reboot/PreRebootComplexFilterTest.java b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/reboot/PreRebootComplexFilterTest.java
new file mode 100644
index 0000000..9f290e3
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/reboot/PreRebootComplexFilterTest.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.dynamicmime.testapp.reboot;
+
+import android.dynamicmime.testapp.ComplexFilterTest;
+import android.dynamicmime.testapp.assertions.MimeGroupAssertions;
+import android.dynamicmime.testapp.commands.MimeGroupCommands;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.runner.RunWith;
+
+/**
+ * Pre-reboot part of {@link ComplexFilterTest}
+ *
+ * {@link MimeGroupAssertions} are disabled
+ * {@link MimeGroupCommands} are disabled during tear down
+ */
+@RunWith(AndroidJUnit4.class)
+public class PreRebootComplexFilterTest extends ComplexFilterTest {
+ private MimeGroupCommands mCommands;
+
+ public PreRebootComplexFilterTest() {
+ super();
+ setAssertions(MimeGroupAssertions.noOp());
+ mCommands = getCommands();
+ }
+
+ @Before
+ public void setUp() {
+ setCommands(mCommands);
+ }
+
+ @After
+ @Override
+ public void tearDown() {
+ setCommands(MimeGroupCommands.noOp());
+ super.tearDown();
+ }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/reboot/PreRebootSingleAppTest.java b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/reboot/PreRebootSingleAppTest.java
new file mode 100644
index 0000000..5a65df7
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/reboot/PreRebootSingleAppTest.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.dynamicmime.testapp.reboot;
+
+import android.dynamicmime.testapp.SingleAppTest;
+import android.dynamicmime.testapp.assertions.MimeGroupAssertions;
+import android.dynamicmime.testapp.commands.MimeGroupCommands;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.runner.RunWith;
+
+/**
+ * Pre-reboot part of {@link SingleAppTest}
+ *
+ * {@link MimeGroupAssertions} are disabled
+ * {@link MimeGroupCommands} are disabled during tear down
+ */
+@RunWith(AndroidJUnit4.class)
+public class PreRebootSingleAppTest extends SingleAppTest {
+ private MimeGroupCommands mCommands;
+
+ public PreRebootSingleAppTest() {
+ super();
+ setAssertions(MimeGroupAssertions.noOp());
+ mCommands = getCommands();
+ }
+
+ @Before
+ public void setUp() {
+ setCommands(mCommands);
+ }
+
+ @After
+ @Override
+ public void tearDown() {
+ setCommands(MimeGroupCommands.noOp());
+ super.tearDown();
+ }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/update/BaseUpdateTest.java b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/update/BaseUpdateTest.java
new file mode 100644
index 0000000..43f590a
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/update/BaseUpdateTest.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.dynamicmime.testapp.update;
+
+import static android.dynamicmime.common.Constants.PACKAGE_UPDATE_APP;
+
+import android.dynamicmime.testapp.BaseDynamicMimeTest;
+import android.dynamicmime.testapp.assertions.MimeGroupAssertions;
+import android.dynamicmime.testapp.commands.MimeGroupCommands;
+import android.dynamicmime.testapp.util.Utils;
+
+import org.junit.After;
+import org.junit.Before;
+
+import java.io.IOException;
+
+abstract class BaseUpdateTest extends BaseDynamicMimeTest {
+ BaseUpdateTest() {
+ super(MimeGroupCommands.appWithUpdates(context()),
+ MimeGroupAssertions.appWithUpdates(context()));
+ }
+
+ @Before
+ public void setUp() throws IOException {
+ Utils.installApk(installApkPath());
+ }
+
+ @After
+ @Override
+ public void tearDown() {
+ super.tearDown();
+ Utils.uninstallApp(PACKAGE_UPDATE_APP);
+ }
+
+ void updateApp() {
+ Utils.updateApp(updateApkPath());
+ }
+
+ abstract String installApkPath();
+ abstract String updateApkPath();
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/update/ChangedGroupsTest.java b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/update/ChangedGroupsTest.java
new file mode 100644
index 0000000..b209699
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/update/ChangedGroupsTest.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.dynamicmime.testapp.update;
+
+import static android.dynamicmime.common.Constants.APK_FIRST_GROUP;
+import static android.dynamicmime.common.Constants.APK_SECOND_GROUP;
+import static android.dynamicmime.common.Constants.GROUP_FIRST;
+import static android.dynamicmime.common.Constants.GROUP_SECOND;
+import static android.dynamicmime.common.Constants.GROUP_UNDEFINED;
+import static android.dynamicmime.common.Constants.MIME_TEXT_PLAIN;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test cases for app update with MIME groups changes
+ */
+@RunWith(AndroidJUnit4.class)
+public class ChangedGroupsTest extends BaseUpdateTest {
+ @Test
+ public void testUpdateRemoveEmptyGroup() {
+ assertMimeGroupIsEmpty(GROUP_FIRST);
+ assertMimeGroupIsNull(GROUP_SECOND);
+ assertMimeGroupIsNull(GROUP_UNDEFINED);
+
+ updateApp();
+
+ assertMimeGroupIsNull(GROUP_FIRST);
+ assertMimeGroupIsEmpty(GROUP_SECOND);
+ assertMimeGroupIsNull(GROUP_UNDEFINED);
+ }
+
+ @Test
+ public void testUpdateRemoveNonEmptyGroup() {
+ setMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN);
+
+ assertMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN);
+ assertMimeGroupIsNull(GROUP_SECOND);
+ assertMimeGroupIsNull(GROUP_UNDEFINED);
+
+ updateApp();
+
+ assertMimeGroupIsNull(GROUP_FIRST);
+ assertMimeGroupIsEmpty(GROUP_SECOND);
+ assertMimeGroupIsNull(GROUP_UNDEFINED);
+ }
+
+ @Override
+ String installApkPath() {
+ return APK_FIRST_GROUP;
+ }
+
+ @Override
+ String updateApkPath() {
+ return APK_SECOND_GROUP;
+ }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/update/SameGroupsTest.java b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/update/SameGroupsTest.java
new file mode 100644
index 0000000..0c3349a
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/update/SameGroupsTest.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.dynamicmime.testapp.update;
+
+import static android.dynamicmime.common.Constants.APK_BOTH_GROUPS;
+import static android.dynamicmime.common.Constants.GROUP_FIRST;
+import static android.dynamicmime.common.Constants.GROUP_SECOND;
+import static android.dynamicmime.common.Constants.GROUP_UNDEFINED;
+import static android.dynamicmime.common.Constants.MIME_TEXT_PLAIN;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test cases for app update, without MIME groups changes
+ */
+@RunWith(AndroidJUnit4.class)
+public class SameGroupsTest extends BaseUpdateTest {
+ @Test
+ public void testUpdate() {
+ setMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN);
+
+ updateApp();
+
+ assertMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN);
+ assertMimeGroupIsEmpty(GROUP_SECOND);
+ assertMimeGroupIsNull(GROUP_UNDEFINED);
+ }
+
+ @Override
+ String installApkPath() {
+ return APK_BOTH_GROUPS;
+ }
+
+ @Override
+ String updateApkPath() {
+ return APK_BOTH_GROUPS;
+ }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/util/AppMimeGroups.java b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/util/AppMimeGroups.java
new file mode 100644
index 0000000..b034946
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/util/AppMimeGroups.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.dynamicmime.testapp.util;
+
+import static android.dynamicmime.common.Constants.ACTION_REQUEST;
+import static android.dynamicmime.common.Constants.ACTION_RESPONSE;
+import static android.dynamicmime.common.Constants.EXTRA_GROUP;
+import static android.dynamicmime.common.Constants.EXTRA_MIMES;
+import static android.dynamicmime.common.Constants.EXTRA_REQUEST;
+import static android.dynamicmime.common.Constants.EXTRA_RESPONSE;
+import static android.dynamicmime.common.Constants.REQUEST_CLEAR;
+import static android.dynamicmime.common.Constants.REQUEST_GET;
+import static android.dynamicmime.common.Constants.REQUEST_SET;
+
+import static org.junit.Assert.assertNotNull;
+
+import android.content.Context;
+import android.content.Intent;
+
+import com.android.compatibility.common.util.BlockingBroadcastReceiver;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Class that dispatches MIME groups related commands to another app
+ * Target app should register {@link android.dynamicmime.app.AppMimeGroupsReceiver}
+ * to handler requests
+ */
+public class AppMimeGroups {
+ private final Context mContext;
+ private final String mTargetPackage;
+
+ private AppMimeGroups(Context context, String targetPackage) {
+ mContext = context;
+ mTargetPackage = targetPackage;
+ }
+
+ public static AppMimeGroups with(Context context, String targetPackage) {
+ return new AppMimeGroups(context, targetPackage);
+ }
+
+ public void set(String mimeGroup, String[] mimeTypes) {
+ sendRequestAndAwait(mimeGroup, REQUEST_SET, mimeTypes);
+ }
+
+ public void clear(String mimeGroup) {
+ sendRequestAndAwait(mimeGroup, REQUEST_CLEAR);
+ }
+
+ public String[] get(String mimeGroup) {
+ return sendRequestAndAwait(mimeGroup, REQUEST_GET)
+ .getStringArrayExtra(EXTRA_RESPONSE);
+ }
+
+ private Intent sendRequestAndAwait(String mimeGroup, int requestSet) {
+ return sendRequestAndAwait(mimeGroup, requestSet, null);
+ }
+
+ private Intent sendRequestAndAwait(String mimeGroup, int request, String[] mimeTypes) {
+ BlockingBroadcastReceiver receiver =
+ new BlockingBroadcastReceiver(mContext, ACTION_RESPONSE);
+ receiver.register();
+
+ mContext.sendBroadcast(getRequestIntent(mimeGroup, mimeTypes, request));
+
+ Intent response = receiver.awaitForBroadcast(TimeUnit.SECONDS.toMillis(5L));
+ assertNotNull(response);
+
+ mContext.unregisterReceiver(receiver);
+
+ return response;
+ }
+
+ private Intent getRequestIntent(String mimeGroup, String[] mimeTypes, int request) {
+ Intent intent = new Intent(ACTION_REQUEST);
+ intent.setPackage(mTargetPackage);
+ intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
+
+ intent.putExtra(EXTRA_GROUP, mimeGroup);
+ intent.putExtra(EXTRA_MIMES, mimeTypes);
+ intent.putExtra(EXTRA_REQUEST, request);
+
+ return intent;
+ }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/util/IntentsResolutionHelper.java b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/util/IntentsResolutionHelper.java
new file mode 100644
index 0000000..fceb48f
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/util/IntentsResolutionHelper.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.dynamicmime.testapp.util;
+
+import static android.dynamicmime.common.Constants.PACKAGE_ACTIVITIES;
+
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.Matchers.empty;
+import static org.hamcrest.Matchers.hasItem;
+import static org.junit.Assert.assertThat;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+
+import java.util.List;
+
+/**
+ * Helper class for {@link PackageManager#queryIntentActivities} results verification
+ */
+public class IntentsResolutionHelper {
+ private final List<ResolveInfo> resolvedActivities;
+ private final Intent mIntent;
+
+ public static IntentsResolutionHelper resolve(Context context, Intent intent) {
+ return new IntentsResolutionHelper(context, intent);
+ }
+
+ private IntentsResolutionHelper(Context context, Intent intent) {
+ mIntent = intent;
+ this.resolvedActivities = context.getPackageManager().queryIntentActivities(mIntent, PackageManager.GET_RESOLVED_FILTER);
+ }
+
+ public void assertMatched(String packageName, String simpleClassName) {
+ assertThat("Missing expected match for " + mIntent, resolvedActivities,
+ hasItem(activity(packageName, simpleClassName)));
+ }
+
+ public void assertNotMatched(String packageName, String simpleClassName) {
+ assertThat("Unexpected match for " + mIntent, resolvedActivities,
+ not(hasItem(activity(packageName, simpleClassName))));
+ }
+
+ public ResolveInfo getAny() {
+ assertThat("Missing match for " + mIntent, resolvedActivities, not(empty()));
+
+ return resolvedActivities
+ .stream()
+ .findAny()
+ .orElse(null);
+ }
+
+ private Matcher<ResolveInfo> activity(String packageName, String simpleClassName) {
+ return new BaseMatcher<ResolveInfo>() {
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("packageName=" + packageName + ", simpleClassName=" + simpleClassName);
+ }
+
+ @Override
+ public boolean matches(Object item) {
+ if (item == null) {
+ return false;
+ }
+ ResolveInfo info = (ResolveInfo) item;
+
+ return info.activityInfo.packageName.equals(packageName)
+ && info.activityInfo.name.equals(PACKAGE_ACTIVITIES + simpleClassName);
+ }
+ };
+ }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/util/MimeGroupOperations.java b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/util/MimeGroupOperations.java
new file mode 100644
index 0000000..ebd4cc4
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/util/MimeGroupOperations.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.dynamicmime.testapp.util;
+
+import android.dynamicmime.testapp.assertions.MimeGroupAssertions;
+import android.dynamicmime.testapp.commands.MimeGroupCommands;
+
+import androidx.annotation.Nullable;
+
+import java.util.Set;
+
+/**
+ * Base class for commands/queries related to MIME groups
+ * Allows independent implementations for commands and assertions
+ */
+public class MimeGroupOperations extends MimeGroupAssertions implements MimeGroupCommands {
+ private MimeGroupCommands mCommands;
+ private MimeGroupAssertions mAssertions;
+
+ public MimeGroupOperations(MimeGroupCommands commands, MimeGroupAssertions assertions) {
+ mCommands = commands;
+ mAssertions = assertions;
+ }
+
+ @Override
+ public final void assertMatchedByMimeGroup(String mimeGroup, String mimeType) {
+ mAssertions.assertMatchedByMimeGroup(mimeGroup, mimeType);
+ }
+
+ @Override
+ public final void assertNotMatchedByMimeGroup(String mimeGroup, String mimeType) {
+ mAssertions.assertNotMatchedByMimeGroup(mimeGroup, mimeType);
+ }
+
+ @Override
+ public final void assertMimeGroupInternal(String mimeGroup, Set<String> mimeTypes) {
+ mAssertions.assertMimeGroupInternal(mimeGroup, mimeTypes);
+ }
+
+ @Override
+ public void setMimeGroup(String mimeGroup, Set<String> mimeTypes) {
+ mCommands.setMimeGroup(mimeGroup, mimeTypes);
+ }
+
+ @Override
+ public final void clearMimeGroup(String mimeGroup) {
+ mCommands.clearMimeGroup(mimeGroup);
+ }
+
+ @Nullable
+ @Override
+ public Set<String> getMimeGroupInternal(String mimeGroup) {
+ return mCommands.getMimeGroupInternal(mimeGroup);
+ }
+
+ public MimeGroupCommands getCommands() {
+ return mCommands;
+ }
+
+ public MimeGroupAssertions getAssertions() {
+ return mAssertions;
+ }
+
+ public void setCommands(MimeGroupCommands commands) {
+ mCommands = commands;
+ }
+
+ public void setAssertions(MimeGroupAssertions assertions) {
+ mAssertions = assertions;
+ }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/util/Utils.java b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/util/Utils.java
new file mode 100644
index 0000000..fce0c98f
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/util/Utils.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.dynamicmime.testapp.util;
+
+import static org.junit.Assert.assertTrue;
+
+import android.os.ParcelFileDescriptor;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import java.io.ByteArrayOutputStream;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class Utils {
+ public static void installApk(String pathToApk) {
+ executeShellCommand("pm install -t " + pathToApk);
+ }
+
+ public static void updateApp(String pathToApk) {
+ executeShellCommand("pm install -t -r " + pathToApk);
+ }
+
+ public static void uninstallApp(String appPackage) {
+ executeShellCommand("pm uninstall " + appPackage);
+ }
+
+ private static void executeShellCommand(String command) {
+ ParcelFileDescriptor pfd = InstrumentationRegistry
+ .getInstrumentation()
+ .getUiAutomation()
+ .executeShellCommand(command);
+ InputStream is = new FileInputStream(pfd.getFileDescriptor());
+
+ try {
+ readFully(is);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static void readFully(InputStream in) throws IOException {
+ try {
+ ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+ byte[] buffer = new byte[1024];
+ int count;
+ while ((count = in.read(buffer)) != -1) {
+ bytes.write(buffer, 0, count);
+ }
+
+ String result = new String(bytes.toByteArray());
+ assertTrue(result, result.isEmpty() || result.contains("Success"));
+ } finally {
+ in.close();
+ }
+ }
+}
diff --git a/hostsidetests/stagedinstall/app/AndroidManifest.xml b/hostsidetests/stagedinstall/app/AndroidManifest.xml
index 067e1cc..bc8bb0b 100644
--- a/hostsidetests/stagedinstall/app/AndroidManifest.xml
+++ b/hostsidetests/stagedinstall/app/AndroidManifest.xml
@@ -17,7 +17,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.tests.stagedinstall" >
- <uses-permission android:name="QUERY_ALL_PACKAGES" />
+ <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
<application>
<receiver android:name="com.android.cts.install.lib.LocalIntentSender"
android:exported="true" />
diff --git a/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/StatsdCtsForegroundActivity.java b/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/StatsdCtsForegroundActivity.java
index d0a05f4..b3ab63c 100644
--- a/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/StatsdCtsForegroundActivity.java
+++ b/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/StatsdCtsForegroundActivity.java
@@ -21,6 +21,7 @@
import android.app.Activity;
import android.app.Notification;
import android.app.NotificationChannel;
+import android.app.NotificationChannelGroup;
import android.app.NotificationManager;
import android.content.Intent;
import android.graphics.Color;
@@ -44,6 +45,7 @@
public static final String ACTION_SHOW_APPLICATION_OVERLAY = "action.show_application_overlay";
public static final String ACTION_SHOW_NOTIFICATION = "action.show_notification";
public static final String ACTION_CRASH = "action.crash";
+ public static final String ACTION_CREATE_CHANNEL_GROUP = "action.create_channel_group";
public static final int SLEEP_OF_ACTION_SLEEP_WHILE_TOP = 2_000;
public static final int SLEEP_OF_ACTION_SHOW_APPLICATION_OVERLAY = 2_000;
@@ -81,6 +83,9 @@
case ACTION_CRASH:
doCrash();
break;
+ case ACTION_CREATE_CHANNEL_GROUP:
+ doCreateChannelGroup();
+ break;
default:
Log.e(TAG, "Intent had invalid action " + action);
finish();
@@ -135,15 +140,17 @@
private void doShowNotification() {
final int notificationId = R.layout.activity_main;
- final String notificationChannel = "StatsdCtsChannel";
+ final String notificationChannelId = "StatsdCtsChannel";
NotificationManager nm = getSystemService(NotificationManager.class);
- nm.createNotificationChannel(new NotificationChannel(notificationChannel, "Statsd Cts",
- NotificationManager.IMPORTANCE_DEFAULT));
+ NotificationChannel channel = new NotificationChannel(notificationChannelId, "Statsd Cts",
+ NotificationManager.IMPORTANCE_DEFAULT);
+ channel.setDescription("Statsd Cts Channel");
+ nm.createNotificationChannel(channel);
nm.notify(
notificationId,
- new Notification.Builder(this, notificationChannel)
+ new Notification.Builder(this, notificationChannelId)
.setSmallIcon(android.R.drawable.stat_notify_chat)
.setContentTitle("StatsdCts")
.setContentText("StatsdCts")
@@ -152,6 +159,15 @@
finish();
}
+ private void doCreateChannelGroup() {
+ NotificationManager nm = getSystemService(NotificationManager.class);
+ NotificationChannelGroup channelGroup = new NotificationChannelGroup("StatsdCtsGroup",
+ "Statsd Cts Group");
+ channelGroup.setDescription("StatsdCtsGroup Description");
+ nm.createNotificationChannelGroup(channelGroup);
+ finish();
+ }
+
@SuppressWarnings("ConstantOverflow")
private void doCrash() {
Log.e(TAG, "About to crash the app with 1/0 " + (long)1/0);
diff --git a/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java b/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
index 5e9e2ba..d94316d 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
@@ -48,11 +48,15 @@
import com.android.os.AtomsProto.LmkKillOccurred;
import com.android.os.AtomsProto.MediaCodecStateChanged;
import com.android.os.AtomsProto.OverlayStateChanged;
+import com.android.os.AtomsProto.PackageNotificationPreferences;
+import com.android.os.AtomsProto.PackageNotificationChannelPreferences;
+import com.android.os.AtomsProto.PackageNotificationChannelGroupPreferences;
import com.android.os.AtomsProto.PictureInPictureStateChanged;
import com.android.os.AtomsProto.ProcessMemoryHighWaterMark;
import com.android.os.AtomsProto.ProcessMemorySnapshot;
import com.android.os.AtomsProto.ProcessMemoryState;
import com.android.os.AtomsProto.ScheduledJobStateChanged;
+import com.android.os.AtomsProto.SnapshotMergeReported;
import com.android.os.AtomsProto.SyncStateChanged;
import com.android.os.AtomsProto.TestAtomReported;
import com.android.os.AtomsProto.VibratorStateChanged;
@@ -66,6 +70,7 @@
import com.google.common.collect.Range;
+import java.lang.ProcessBuilder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -120,6 +125,32 @@
assertThat(atom.getOomAdjScore()).isAtLeast(500);
}
+ public void testSnapshotMergeReported() throws Exception {
+ if (statsdDisabled()) {
+ return;
+ }
+
+ // Test is only valid for Virtual A/B devices
+ if (!"true".equals(getProperty("ro.virtual_ab.enabled"))) {
+ return;
+ }
+
+ final int atomTag = Atom.SNAPSHOT_MERGE_REPORTED_FIELD_NUMBER;
+ createAndUploadConfig(atomTag, false);
+ Thread.sleep(WAIT_TIME_SHORT);
+
+ Process process = new ProcessBuilder("snapshotctl", "--dry-run", "--report").start();
+
+ Thread.sleep(WAIT_TIME_SHORT);
+
+ List<EventMetricData> data = getEventMetricDataList();
+
+ SnapshotMergeReported atom = data.get(0).getAtom().getSnapshotMergeReported();
+ assertThat(atom.getFinalState()).isEqualTo(SnapshotMergeReported.UpdateState.MERGE_COMPLETED);
+ assertThat(atom.getDurationMillis()).isEqualTo(1234);
+ assertThat(atom.getIntermediateReboots()).isEqualTo(56);
+ }
+
public void testAppCrashOccurred() throws Exception {
if (statsdDisabled()) {
return;
@@ -1531,4 +1562,130 @@
assertThat(atom.getBytesField().getExperimentIdList()).isEmpty();
}
+ public void testNotificationPackagePreferenceExtraction() throws Exception {
+ if (statsdDisabled()) {
+ return;
+ }
+
+ StatsdConfig.Builder config = getPulledConfig();
+ addGaugeAtomWithDimensions(config,
+ Atom.PACKAGE_NOTIFICATION_PREFERENCES_FIELD_NUMBER,
+ null);
+ uploadConfig(config);
+ Thread.sleep(WAIT_TIME_SHORT);
+ runActivity("StatsdCtsForegroundActivity", "action", "action.show_notification");
+ Thread.sleep(WAIT_TIME_SHORT);
+ setAppBreadcrumbPredicate();
+ Thread.sleep(WAIT_TIME_SHORT);
+
+ List<PackageNotificationPreferences> allPreferences = new ArrayList<>();
+ for (Atom atom : getGaugeMetricDataList()){
+ if(atom.hasPackageNotificationPreferences()) {
+ allPreferences.add(atom.getPackageNotificationPreferences());
+ }
+ }
+ assertThat(allPreferences.size()).isGreaterThan(0);
+
+ boolean foundTestPackagePreferences = false;
+ int uid = getUid();
+ for (PackageNotificationPreferences pref : allPreferences) {
+ assertThat(pref.getUid()).isGreaterThan(0);
+ assertTrue(pref.hasImportance());
+ assertTrue(pref.hasVisibility());
+ assertTrue(pref.hasUserLockedFields());
+ if(pref.getUid() == uid){
+ assertThat(pref.getImportance()).isEqualTo(-1000); //UNSPECIFIED_IMPORTANCE
+ assertThat(pref.getVisibility()).isEqualTo(-1000); //UNSPECIFIED_VISIBILITY
+ foundTestPackagePreferences = true;
+ }
+ }
+ assertTrue(foundTestPackagePreferences);
+ }
+
+ public void testNotificationChannelPreferencesExtraction() throws Exception {
+ if (statsdDisabled()) {
+ return;
+ }
+
+ StatsdConfig.Builder config = getPulledConfig();
+ addGaugeAtomWithDimensions(config,
+ Atom.PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES_FIELD_NUMBER,
+ null);
+ uploadConfig(config);
+ Thread.sleep(WAIT_TIME_SHORT);
+ runActivity("StatsdCtsForegroundActivity", "action", "action.show_notification");
+ Thread.sleep(WAIT_TIME_SHORT);
+ setAppBreadcrumbPredicate();
+ Thread.sleep(WAIT_TIME_SHORT);
+
+ List<PackageNotificationChannelPreferences> allChannelPreferences = new ArrayList<>();
+ for(Atom atom : getGaugeMetricDataList()) {
+ if (atom.hasPackageNotificationChannelPreferences()) {
+ allChannelPreferences.add(atom.getPackageNotificationChannelPreferences());
+ }
+ }
+ assertThat(allChannelPreferences.size()).isGreaterThan(0);
+
+ boolean foundTestPackagePreferences = false;
+ int uid = getUid();
+ for (PackageNotificationChannelPreferences pref : allChannelPreferences) {
+ assertThat(pref.getUid()).isGreaterThan(0);
+ assertTrue(pref.hasChannelId());
+ assertTrue(pref.hasChannelName());
+ assertTrue(pref.hasDescription());
+ assertTrue(pref.hasImportance());
+ assertTrue(pref.hasUserLockedFields());
+ assertTrue(pref.hasIsDeleted());
+ if(uid == pref.getUid() && pref.getChannelId().equals("StatsdCtsChannel")) {
+ assertThat(pref.getChannelName()).isEqualTo("Statsd Cts");
+ assertThat(pref.getDescription()).isEqualTo("Statsd Cts Channel");
+ assertThat(pref.getImportance()).isEqualTo(3); // IMPORTANCE_DEFAULT
+ foundTestPackagePreferences = true;
+ }
+ }
+ assertTrue(foundTestPackagePreferences);
+ }
+
+ public void testNotificationChannelGroupPreferencesExtraction() throws Exception {
+ if (statsdDisabled()) {
+ return;
+ }
+
+ StatsdConfig.Builder config = getPulledConfig();
+ addGaugeAtomWithDimensions(config,
+ Atom.PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES_FIELD_NUMBER,
+ null);
+ uploadConfig(config);
+ Thread.sleep(WAIT_TIME_SHORT);
+ runActivity("StatsdCtsForegroundActivity", "action", "action.create_channel_group");
+ Thread.sleep(WAIT_TIME_SHORT);
+ setAppBreadcrumbPredicate();
+ Thread.sleep(WAIT_TIME_SHORT);
+
+ List<PackageNotificationChannelGroupPreferences> allGroupPreferences = new ArrayList<>();
+ for(Atom atom : getGaugeMetricDataList()) {
+ if (atom.hasPackageNotificationChannelGroupPreferences()) {
+ allGroupPreferences.add(atom.getPackageNotificationChannelGroupPreferences());
+ }
+ }
+ assertThat(allGroupPreferences.size()).isGreaterThan(0);
+
+ boolean foundTestPackagePreferences = false;
+ int uid = getUid();
+ for(PackageNotificationChannelGroupPreferences pref : allGroupPreferences) {
+ assertThat(pref.getUid()).isGreaterThan(0);
+ assertTrue(pref.hasGroupId());
+ assertTrue(pref.hasGroupName());
+ assertTrue(pref.hasDescription());
+ assertTrue(pref.hasIsBlocked());
+ assertTrue(pref.hasUserLockedFields());
+ if(uid == pref.getUid() && pref.getGroupId().equals("StatsdCtsGroup")) {
+ assertThat(pref.getGroupName()).isEqualTo("Statsd Cts Group");
+ assertThat(pref.getDescription()).isEqualTo("StatsdCtsGroup Description");
+ assertThat(pref.getIsBlocked()).isFalse();
+ foundTestPackagePreferences = true;
+ }
+ }
+ assertTrue(foundTestPackagePreferences);
+ }
}
diff --git a/hostsidetests/userspacereboot/AndroidTest.xml b/hostsidetests/userspacereboot/AndroidTest.xml
index 97f67af..5df919f 100644
--- a/hostsidetests/userspacereboot/AndroidTest.xml
+++ b/hostsidetests/userspacereboot/AndroidTest.xml
@@ -20,7 +20,7 @@
<option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
<option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
<option name="config-descriptor:metadata" key="parameter" value="not_secondary_user" />
- <test class="com.android.tradefed.testtype.HostTest" >
+ <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
<option name="class" value="com.android.cts.userspacereboot.host.UserspaceRebootHostTest" />
</test>
</configuration>
diff --git a/hostsidetests/userspacereboot/src/com/android/cts/userspacereboot/host/UserspaceRebootHostTest.java b/hostsidetests/userspacereboot/src/com/android/cts/userspacereboot/host/UserspaceRebootHostTest.java
index f953a1d..0100e6a 100644
--- a/hostsidetests/userspacereboot/src/com/android/cts/userspacereboot/host/UserspaceRebootHostTest.java
+++ b/hostsidetests/userspacereboot/src/com/android/cts/userspacereboot/host/UserspaceRebootHostTest.java
@@ -155,7 +155,7 @@
}
private void rebootUserspaceAndWaitForBootComplete() throws Exception {
- getDevice().setProperty("test.userspace_reboot.requested", "1");
+ assertThat(getDevice().setProperty("test.userspace_reboot.requested", "1")).isTrue();
getDevice().rebootUserspace();
assertWithMessage("Device did not boot withing 2 minutes").that(
getDevice().waitForBootComplete(Duration.ofMinutes(2).toMillis())).isTrue();
@@ -164,7 +164,11 @@
private void assertUserspaceRebootSucceed() throws Exception {
// If userspace reboot fails and fallback to hard reboot is triggered then
// test.userspace_reboot.requested won't be set.
- assertWithMessage("Userspace reboot failed and fallback to full reboot was triggered").that(
- getDevice().getProperty("test.userspace_reboot.requested")).isEqualTo("1");
+ final String bootReason = getDevice().getProperty("sys.boot.reason.last");
+ final boolean result = getDevice().getBooleanProperty("test.userspace_reboot.requested",
+ false);
+ assertWithMessage(
+ "Userspace reboot failed and fallback to full reboot was triggered. Boot reason: "
+ + "%s", bootReason).that(result).isTrue();
}
}
diff --git a/hostsidetests/webkit/app/src/com/android/cts/webkit/WebViewDeviceSideStartupTest.java b/hostsidetests/webkit/app/src/com/android/cts/webkit/WebViewDeviceSideStartupTest.java
index 80a8964..fae440b 100644
--- a/hostsidetests/webkit/app/src/com/android/cts/webkit/WebViewDeviceSideStartupTest.java
+++ b/hostsidetests/webkit/app/src/com/android/cts/webkit/WebViewDeviceSideStartupTest.java
@@ -174,6 +174,8 @@
.build());
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectAll()
+ // TODO(b/149790106): Fix and remove.
+ .permitIncorrectContextUse()
.penaltyLog()
.penaltyDeath()
.build());
diff --git a/hostsidetests/wifibroadcasts/src/android/wifibroadcasts/cts/WifiBroadcastsHostJUnit4Test.java b/hostsidetests/wifibroadcasts/src/android/wifibroadcasts/cts/WifiBroadcastsHostJUnit4Test.java
index 8dcc4d3..0b4882a 100644
--- a/hostsidetests/wifibroadcasts/src/android/wifibroadcasts/cts/WifiBroadcastsHostJUnit4Test.java
+++ b/hostsidetests/wifibroadcasts/src/android/wifibroadcasts/cts/WifiBroadcastsHostJUnit4Test.java
@@ -162,5 +162,7 @@
} finally {
in.close();
}
+ //Re-enable Wi-Fi as part of CTS Pre-conditions
+ device.executeShellCommand("svc wifi enable; sleep 1");
}
}
diff --git a/tests/JobScheduler/src/android/jobscheduler/cts/ConnectivityConstraintTest.java b/tests/JobScheduler/src/android/jobscheduler/cts/ConnectivityConstraintTest.java
index c0fada5..9c177cd 100644
--- a/tests/JobScheduler/src/android/jobscheduler/cts/ConnectivityConstraintTest.java
+++ b/tests/JobScheduler/src/android/jobscheduler/cts/ConnectivityConstraintTest.java
@@ -75,8 +75,7 @@
super.setUp();
mWifiManager = (WifiManager) getContext().getSystemService(Context.WIFI_SERVICE);
- mCm =
- (ConnectivityManager) getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
+ mCm = (ConnectivityManager) getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
PackageManager packageManager = mContext.getPackageManager();
mHasWifi = packageManager.hasSystemFeature(PackageManager.FEATURE_WIFI);
@@ -84,7 +83,9 @@
mBuilder =
new JobInfo.Builder(CONNECTIVITY_JOB_ID, kJobServiceComponent);
- mInitialWiFiState = mWifiManager.isWifiEnabled();
+ if (mHasWifi) {
+ mInitialWiFiState = mWifiManager.isWifiEnabled();
+ }
mInitialRestrictBackground = SystemUtil
.runShellCommand(getInstrumentation(), RESTRICT_BACKGROUND_GET_CMD)
.contains("enabled");
@@ -101,7 +102,7 @@
setDataSaverEnabled(mInitialRestrictBackground);
// Ensure that we leave WiFi in its previous state.
- if (mWifiManager.isWifiEnabled() != mInitialWiFiState) {
+ if (mHasWifi && mWifiManager.isWifiEnabled() != mInitialWiFiState) {
setWifiState(mInitialWiFiState, mCm, mWifiManager);
}
diff --git a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfoTest.java b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfoTest.java
index a59330c..0539a31 100644
--- a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfoTest.java
+++ b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfoTest.java
@@ -524,7 +524,6 @@
// labeledby: AccessibilityEndToEndTest#testLabelForReportedToAccessibility
// windowid: Not directly observable
// sourceid: Not directly observable
- // TODO(b/147393134): Need to test if we could get leashed child/parent properly.
// leashedChild: Not directly accessible
// leashedParent: Not directly accessible
// leashedParentNodeId: Not directly accessible
@@ -640,7 +639,6 @@
// labeledby (can't be performed on sealed instance, even if null)
// sourceId (not directly observable)
// windowId (not directly observable)
- // TODO(b/147393134): Need to test if we could get leashed child/parent properly.
// leashedChild (not directly observable)
// leashedParent (not directly observable)
// leashedParentNodeId (not directly observable)
diff --git a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityServiceInfoTest.java b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityServiceInfoTest.java
index ada5019..d2f7c2d 100644
--- a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityServiceInfoTest.java
+++ b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityServiceInfoTest.java
@@ -98,7 +98,7 @@
| AccessibilityServiceInfo.CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION
| AccessibilityServiceInfo.CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT);
assertEquals("foo.bar.Activity", speakingService.getSettingsActivityName());
- assertEquals(R.drawable.size_48x48, speakingService.getAnimatedImageRes());
+ assertNotNull(speakingService.loadAnimatedImage(getInstrumentation().getContext()));
assertEquals("Some description", speakingService.loadDescription(
getInstrumentation().getContext().getPackageManager()));
assertEquals("Some html description", speakingService.loadHtmlDescription(
diff --git a/tests/accessibilityservice/AndroidManifest.xml b/tests/accessibilityservice/AndroidManifest.xml
index 1dc332b..f656804 100644
--- a/tests/accessibilityservice/AndroidManifest.xml
+++ b/tests/accessibilityservice/AndroidManifest.xml
@@ -83,6 +83,7 @@
<activity
android:label="@string/accessibility_embedded_hierarchy_test_activity"
android:name=".AccessibilityEmbeddedHierarchyTest$AccessibilityEmbeddedHierarchyActivity"
+ android:theme="@android:style/Theme.Dialog"
android:screenOrientation="locked"/>
<service
diff --git a/tests/accessibilityservice/res/layout/accessibility_text_traversal_test.xml b/tests/accessibilityservice/res/layout/accessibility_text_traversal_test.xml
index 58824d4..c41c1fd 100644
--- a/tests/accessibilityservice/res/layout/accessibility_text_traversal_test.xml
+++ b/tests/accessibilityservice/res/layout/accessibility_text_traversal_test.xml
@@ -20,6 +20,7 @@
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
+ android:id="@+id/viewGroup"
>
<View
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityMagnificationTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityMagnificationTest.java
index c832368..659aaf7 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityMagnificationTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityMagnificationTest.java
@@ -313,7 +313,7 @@
final Rect boundsInScreen = new Rect();
final DisplayMetrics displayMetrics = new DisplayMetrics();
buttonNode.getBoundsInScreen(boundsInScreen);
- mInstrumentation.getContext().getDisplay().getMetrics(displayMetrics);
+ activity.getDisplay().getMetrics(displayMetrics);
final Rect displayRect = new Rect(0, 0,
displayMetrics.widthPixels, displayMetrics.heightPixels);
// The boundsInScreen of button is adjusted to outside of screen by framework,
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityTextActionTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityTextActionTest.java
index 4cc0116..ca59377 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityTextActionTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityTextActionTest.java
@@ -16,6 +16,7 @@
import static android.accessibilityservice.cts.utils.ActivityLaunchUtils.launchActivityAndWaitForItToBeOnscreen;
import static android.accessibilityservice.cts.utils.AsyncUtils.DEFAULT_TIMEOUT_MS;
+import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_RENDERING_INFO_KEY;
import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH;
import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX;
import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY;
@@ -47,7 +48,11 @@
import android.text.style.ImageSpan;
import android.text.style.ReplacementSpan;
import android.text.style.URLSpan;
+import android.util.DisplayMetrics;
+import android.util.Size;
+import android.util.TypedValue;
import android.view.View;
+import android.view.ViewGroup;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeProvider;
@@ -465,6 +470,65 @@
textView, EditorInfo.IME_ACTION_DONE, null);
}
+ @Test
+ public void testExtraRendering_textViewShouldProvideExtraDataTextSizeWhenRequested() {
+ final Bundle arg = new Bundle();
+ final DisplayMetrics displayMetrics = mActivity.getResources().getDisplayMetrics();
+ final TextView textView = mActivity.findViewById(R.id.text);
+ final String stringToSet = mActivity.getString(R.string.foo_bar_baz);
+ final int expectedWidthInPx = textView.getLayoutParams().width;
+ final int expectedHeightInPx = textView.getLayoutParams().height;
+ final float expectedTextSize = textView.getTextSize();
+ final float newTextSize = 20f;
+ final float expectedNewTextSize = (int) (0.5f + TypedValue.applyDimension(
+ TypedValue.COMPLEX_UNIT_SP, newTextSize, displayMetrics));
+ makeTextViewVisibleAndSetText(textView, stringToSet);
+
+ final AccessibilityNodeInfo info = sUiAutomation.getRootInActiveWindow()
+ .findAccessibilityNodeInfosByText(stringToSet).get(0);
+ assertTrue("Text view should offer extra data to accessibility ",
+ info.getAvailableExtraData().contains(EXTRA_DATA_RENDERING_INFO_KEY));
+
+ AccessibilityNodeInfo.ExtraRenderingInfo extraRenderingInfo;
+ assertNull(info.getExtraRenderingInfo());
+ assertTrue("Refresh failed", info.refreshWithExtraData(
+ EXTRA_DATA_RENDERING_INFO_KEY , arg));
+ assertNotNull(info.getExtraRenderingInfo());
+ extraRenderingInfo = info.getExtraRenderingInfo();
+ assertNotNull(extraRenderingInfo.getLayoutParams());
+ assertEquals(expectedWidthInPx, extraRenderingInfo.getLayoutParams().getWidth());
+ assertEquals(expectedHeightInPx, extraRenderingInfo.getLayoutParams().getHeight());
+ assertEquals(expectedTextSize, extraRenderingInfo.getTextSizeInPx(), 0f);
+ assertEquals(TypedValue.COMPLEX_UNIT_DIP, extraRenderingInfo.getTextSizeUnit());
+
+ // After changing text size
+ sInstrumentation.runOnMainSync(() ->
+ textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, newTextSize));
+ assertTrue("Refresh failed", info.refreshWithExtraData(
+ EXTRA_DATA_RENDERING_INFO_KEY, arg));
+ extraRenderingInfo = info.getExtraRenderingInfo();
+ assertEquals(expectedNewTextSize, extraRenderingInfo.getTextSizeInPx(), 0f);
+ assertEquals(TypedValue.COMPLEX_UNIT_SP, extraRenderingInfo.getTextSizeUnit());
+ }
+
+ @Test
+ public void testExtraRendering_viewGroupShouldNotProvideLayoutParamsWhenNotRequested() {
+ final AccessibilityNodeInfo info = sUiAutomation.getRootInActiveWindow()
+ .findAccessibilityNodeInfosByViewId(
+ "android.accessibilityservice.cts:id/viewGroup").get(0);
+
+ assertTrue("ViewGroup should offer extra data to accessibility",
+ info.getAvailableExtraData().contains(EXTRA_DATA_RENDERING_INFO_KEY));
+ assertNull(info.getExtraRenderingInfo());
+ assertTrue("Refresh failed", info.refreshWithExtraData(
+ EXTRA_DATA_RENDERING_INFO_KEY, new Bundle()));
+ assertNotNull(info.getExtraRenderingInfo());
+ assertNotNull(info.getExtraRenderingInfo().getLayoutParams());
+ final Size size = info.getExtraRenderingInfo().getLayoutParams();
+ assertEquals(ViewGroup.LayoutParams.MATCH_PARENT, size.getWidth());
+ assertEquals(ViewGroup.LayoutParams.WRAP_CONTENT, size.getHeight());
+ }
+
private void verifyImeActionLabel(AccessibilityNodeInfo node, String label) {
final List<AccessibilityNodeInfo.AccessibilityAction> actionList = node.getActionList();
final int indexOfActionImeEnter =
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/TouchExplorerTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/TouchExplorerTest.java
index 9042d62..bc30c7d4 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/TouchExplorerTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/TouchExplorerTest.java
@@ -68,8 +68,10 @@
import android.content.pm.PackageManager;
import android.graphics.Point;
import android.graphics.PointF;
+import android.graphics.Region;
import android.platform.test.annotations.AppModeFull;
import android.util.DisplayMetrics;
+import android.view.Display;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
@@ -353,8 +355,7 @@
dispatch(doubleTap(mTapLocation));
mHoverListener.assertNonePropagated();
mTouchListener.assertNonePropagated();
- mService.assertPropagated(
- TYPE_TOUCH_INTERACTION_START, TYPE_TOUCH_INTERACTION_END);
+ mService.assertPropagated(TYPE_TOUCH_INTERACTION_START, TYPE_TOUCH_INTERACTION_END);
mService.clearEvents();
mLongClickListener.assertNoneLongClicked();
}
@@ -470,6 +471,84 @@
ACTION_DOWN, ACTION_POINTER_DOWN, ACTION_MOVE, ACTION_POINTER_UP, ACTION_UP);
}
+ /**
+ * Test the gesture detection passthrough by performing a fast swipe in the passthrough region.
+ * It should bypass the gesture detector entirely.
+ */
+ @Test
+ @AppModeFull
+ public void testGestureDetectionPassthrough_initiatesTouchExploration() {
+ if (!mHasTouchscreen || !mScreenBigEnough) return;
+ setRightSideOfScreenGestureDetectionPassthrough();
+ // Swipe in the passthrough region. This should generate hover events.
+ dispatch(swipe(mTapLocation, add(mTapLocation, mSwipeDistance, 0)));
+ mHoverListener.assertPropagated(ACTION_HOVER_ENTER, ACTION_HOVER_MOVE, ACTION_HOVER_EXIT);
+ mService.assertPropagated(
+ TYPE_TOUCH_INTERACTION_START,
+ TYPE_TOUCH_EXPLORATION_GESTURE_START,
+ TYPE_TOUCH_EXPLORATION_GESTURE_END,
+ TYPE_TOUCH_INTERACTION_END);
+ mService.clearEvents();
+ // Swipe starting inside the passthrough region but ending outside of it. This should still
+ // behave as a passthrough interaction.
+ dispatch(swipe(mTapLocation, add(mTapLocation, -mSwipeDistance, 0)));
+ mHoverListener.assertPropagated(ACTION_HOVER_ENTER, ACTION_HOVER_MOVE, ACTION_HOVER_EXIT);
+ mService.assertPropagated(
+ TYPE_TOUCH_INTERACTION_START,
+ TYPE_TOUCH_EXPLORATION_GESTURE_START,
+ TYPE_TOUCH_EXPLORATION_GESTURE_END,
+ TYPE_TOUCH_INTERACTION_END);
+ mService.clearEvents();
+ // Swipe outside the passthrough region. This should not generate hover events.
+ dispatch(swipe(add(mTapLocation, -1, 0), add(mTapLocation, -mSwipeDistance, 0)));
+ mHoverListener.assertNonePropagated();
+ mService.assertPropagated(
+ TYPE_TOUCH_INTERACTION_START,
+ TYPE_GESTURE_DETECTION_START,
+ TYPE_GESTURE_DETECTION_END,
+ TYPE_TOUCH_INTERACTION_END);
+ mService.clearEvents();
+ // There should be no touch events in this test.
+ mTouchListener.assertNonePropagated();
+ clearPassthroughRegions();
+ }
+
+ /**
+ * Test the touch exploration passthrough by performing a fast swipe in the passthrough region.
+ * It should generate touch events.
+ */
+ @Test
+ @AppModeFull
+ public void testTouchExplorationPassthrough_sendsTouchEvents() {
+ if (!mHasTouchscreen || !mScreenBigEnough) return;
+ setRightSideOfScreenTouchExplorationPassthrough();
+ // Swipe in the passthrough region. This should generate touch events.
+ dispatch(swipe(mTapLocation, add(mTapLocation, mSwipeDistance, 0)));
+ mTouchListener.assertPropagated(ACTION_DOWN, ACTION_MOVE, ACTION_UP);
+ // We still want accessibility events to tell us when the gesture starts and ends.
+ mService.assertPropagated(
+ TYPE_TOUCH_INTERACTION_START, TYPE_TOUCH_INTERACTION_END, TYPE_VIEW_CLICKED);
+ mService.clearEvents();
+ // Swipe starting inside the passthrough region but ending outside of it. This should still
+ // behave as a passthrough interaction.
+ dispatch(swipe(mTapLocation, add(mTapLocation, -mSwipeDistance, 0)));
+ mTouchListener.assertPropagated(ACTION_DOWN, ACTION_MOVE, ACTION_UP);
+ mService.assertPropagated(
+ TYPE_TOUCH_INTERACTION_START, TYPE_TOUCH_INTERACTION_END, TYPE_VIEW_CLICKED);
+ mService.clearEvents();
+ // Swipe outside the passthrough region. This should not generate touch events.
+ dispatch(swipe(add(mTapLocation, -1, 0), add(mTapLocation, -mSwipeDistance, 0)));
+ mTouchListener.assertNonePropagated();
+ mService.assertPropagated(
+ TYPE_TOUCH_INTERACTION_START,
+ TYPE_GESTURE_DETECTION_START,
+ TYPE_GESTURE_DETECTION_END,
+ TYPE_TOUCH_INTERACTION_END);
+ // There should be no hover events in this test.
+ mHoverListener.assertNonePropagated();
+ clearPassthroughRegions();
+ }
+
public void dispatch(StrokeDescription firstStroke, StrokeDescription... rest) {
GestureDescription.Builder builder =
new GestureDescription.Builder().addStroke(firstStroke);
@@ -493,4 +572,44 @@
.performAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS);
});
}
+
+ private void setRightSideOfScreenGestureDetectionPassthrough() {
+ Region region = getRightSideOfScreenRegion();
+ mService.runOnServiceSync(
+ () -> {
+ mService.setGestureDetectionPassthroughRegion(Display.DEFAULT_DISPLAY, region);
+ });
+ }
+
+ private void setRightSideOfScreenTouchExplorationPassthrough() {
+ Region region = getRightSideOfScreenRegion();
+ mService.runOnServiceSync(
+ () -> {
+ mService.setTouchExplorationPassthroughRegion(Display.DEFAULT_DISPLAY, region);
+ });
+ }
+
+ private void clearPassthroughRegions() {
+ mService.runOnServiceSync(
+ () -> {
+ mService.setGestureDetectionPassthroughRegion(
+ Display.DEFAULT_DISPLAY, new Region());
+ mService.setTouchExplorationPassthroughRegion(
+ Display.DEFAULT_DISPLAY, new Region());
+ });
+ }
+
+ private Region getRightSideOfScreenRegion() {
+ WindowManager windowManager =
+ (WindowManager)
+ mInstrumentation.getContext().getSystemService(Context.WINDOW_SERVICE);
+ final DisplayMetrics metrics = new DisplayMetrics();
+ windowManager.getDefaultDisplay().getRealMetrics(metrics);
+ int top = 0;
+ int left = metrics.widthPixels / 2;
+ int right = metrics.widthPixels;
+ int bottom = metrics.heightPixels;
+ Region region = new Region(left, top, right, bottom);
+ return region;
+ }
}
diff --git a/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java b/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java
index dc3c57e..c1a02ab 100644
--- a/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java
+++ b/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java
@@ -145,55 +145,58 @@
}
}
- public void testSetSecurityLoggingEnabled_failIfNotDeviceOwner() {
+ public void testSetSecurityLoggingEnabled_failIfNotOrganizationOwnedProfileOwner() {
if (!mDeviceAdmin) {
- Log.w(TAG, "Skipping testSetSecurityLoggingEnabled_failIfNotDeviceOwner");
+ Log.w(TAG, "Skipping testSetSecurityLoggingEnabled_"
+ + "failIfNotOrganizationOwnedProfileOwner");
return;
}
try {
mDevicePolicyManager.setSecurityLoggingEnabled(mComponent, true);
fail("did not throw expected SecurityException");
} catch (SecurityException e) {
- assertDeviceOwnerMessage(e.getMessage());
+ assertOrganizationOwnedProfileOwnerMessage(e.getMessage());
}
}
- public void testIsSecurityLoggingEnabled_failIfNotDeviceOwner() {
+ public void testIsSecurityLoggingEnabled_failIfNotOrganizationOwnedProfileOwner() {
if (!mDeviceAdmin) {
- Log.w(TAG, "Skipping testIsSecurityLoggingEnabled_failIfNotDeviceOwner");
+ Log.w(TAG, "Skipping testIsSecurityLoggingEnabled_"
+ + "failIfNotOrganizationOwnedProfileOwner");
return;
}
try {
mDevicePolicyManager.isSecurityLoggingEnabled(mComponent);
fail("did not throw expected SecurityException");
} catch (SecurityException e) {
- assertDeviceOwnerMessage(e.getMessage());
+ assertOrganizationOwnedProfileOwnerMessage(e.getMessage());
}
}
- public void testRetrieveSecurityLogs_failIfNotDeviceOwner() {
+ public void testRetrieveSecurityLogs_failIfNotOrganizationOwnedProfileOwner() {
if (!mDeviceAdmin) {
- Log.w(TAG, "Skipping testRetrieveSecurityLogs_failIfNotDeviceOwner");
+ Log.w(TAG, "Skipping testRetrieveSecurityLogs_failIfNotOrganizationOwnedProfileOwner");
return;
}
try {
mDevicePolicyManager.retrieveSecurityLogs(mComponent);
fail("did not throw expected SecurityException");
} catch (SecurityException e) {
- assertDeviceOwnerMessage(e.getMessage());
+ assertOrganizationOwnedProfileOwnerMessage(e.getMessage());
}
}
- public void testRetrievePreRebootSecurityLogs_failIfNotDeviceOwner() {
+ public void testRetrievePreRebootSecurityLogs_failIfNotOrganizationOwnedProfileOwner() {
if (!mDeviceAdmin) {
- Log.w(TAG, "Skipping testRetrievePreRebootSecurityLogs_failIfNotDeviceOwner");
+ Log.w(TAG, "Skipping testRetrievePreRebootSecurityLogs_"
+ + "failIfNotOrganizationOwnedProfileOwner");
return;
}
try {
mDevicePolicyManager.retrievePreRebootSecurityLogs(mComponent);
fail("did not throw expected SecurityException");
} catch (SecurityException e) {
- assertDeviceOwnerMessage(e.getMessage());
+ assertOrganizationOwnedProfileOwnerMessage(e.getMessage());
}
}
@@ -287,20 +290,6 @@
}
}
- public void testRequestSetLocationProviderEnabled_failIfNotDeviceOwner() {
- if (!mDeviceAdmin) {
- Log.w(TAG, "Skipping testRequestSetLocationProviderEnabled_failIfNotDeviceOwner");
- return;
- }
- try {
- mDevicePolicyManager.requestSetLocationProviderAllowed(mComponent, "test_provider",
- true);
- fail("did not throw expected SecurityException");
- } catch (SecurityException e) {
- assertDeviceOwnerMessage(e.getMessage());
- }
- }
-
public void testSetGlobalSetting_failIfNotDeviceOwner() {
if (!mDeviceAdmin) {
Log.w(TAG, "Skipping testSetGlobalSetting_failIfNotDeviceOwner");
diff --git a/tests/app/src/android/app/cts/NotificationManagerTest.java b/tests/app/src/android/app/cts/NotificationManagerTest.java
index 6115dfb..2c9ff16 100644
--- a/tests/app/src/android/app/cts/NotificationManagerTest.java
+++ b/tests/app/src/android/app/cts/NotificationManagerTest.java
@@ -731,10 +731,6 @@
}
public void testConsolidatedNotificationPolicy() throws Exception {
- if (mActivityManager.isLowRamDevice()) {
- return;
- }
-
final int originalFilter = mNotificationManager.getCurrentInterruptionFilter();
Policy origPolicy = mNotificationManager.getNotificationPolicy();
try {
@@ -794,10 +790,6 @@
}
public void testConsolidatedNotificationPolicyMultiRules() throws Exception {
- if (mActivityManager.isLowRamDevice()) {
- return;
- }
-
final int originalFilter = mNotificationManager.getCurrentInterruptionFilter();
Policy origPolicy = mNotificationManager.getNotificationPolicy();
try {
@@ -863,10 +855,6 @@
}
public void testPostPCanToggleAlarmsMediaSystemTest() throws Exception {
- if (mActivityManager.isLowRamDevice()) {
- return;
- }
-
toggleNotificationPolicyAccess(mContext.getPackageName(),
InstrumentationRegistry.getInstrumentation(), true);
@@ -897,10 +885,6 @@
}
public void testPostRCanToggleConversationsTest() throws Exception {
- if (mActivityManager.isLowRamDevice()) {
- return;
- }
-
toggleNotificationPolicyAccess(mContext.getPackageName(),
InstrumentationRegistry.getInstrumentation(), true);
@@ -1267,10 +1251,6 @@
}
public void testSuspendPackage() throws Exception {
- if (mActivityManager.isLowRamDevice() && !mPackageManager.hasSystemFeature(FEATURE_WATCH)) {
- return;
- }
-
toggleListenerAccess(TestNotificationListener.getId(),
InstrumentationRegistry.getInstrumentation(), true);
Thread.sleep(500); // wait for listener to be allowed
@@ -1313,10 +1293,6 @@
}
public void testSuspendedPackageSendsNotification() throws Exception {
- if (mActivityManager.isLowRamDevice() && !mPackageManager.hasSystemFeature(FEATURE_WATCH)) {
- return;
- }
-
toggleListenerAccess(TestNotificationListener.getId(),
InstrumentationRegistry.getInstrumentation(), true);
Thread.sleep(500); // wait for listener to be allowed
@@ -1405,10 +1381,6 @@
}
public void testShowBadging_ranking() throws Exception {
- if (mActivityManager.isLowRamDevice() && !mPackageManager.hasSystemFeature(FEATURE_WATCH)) {
- return;
- }
-
final int originalBadging = Settings.Secure.getInt(
mContext.getContentResolver(), Settings.Secure.NOTIFICATION_BADGING);
@@ -1461,10 +1433,6 @@
}
public void testGetSuppressedVisualEffectsOff_ranking() throws Exception {
- if (mActivityManager.isLowRamDevice() && !mPackageManager.hasSystemFeature(FEATURE_WATCH)) {
- return;
- }
-
toggleListenerAccess(TestNotificationListener.getId(),
InstrumentationRegistry.getInstrumentation(), true);
Thread.sleep(500); // wait for listener to be allowed
@@ -1491,10 +1459,6 @@
}
public void testGetSuppressedVisualEffects_ranking() throws Exception {
- if (mActivityManager.isLowRamDevice() && !mPackageManager.hasSystemFeature(FEATURE_WATCH)) {
- return;
- }
-
final int originalFilter = mNotificationManager.getCurrentInterruptionFilter();
NotificationManager.Policy origPolicy = mNotificationManager.getNotificationPolicy();
try {
@@ -1547,10 +1511,6 @@
}
public void testKeyChannelGroupOverrideImportanceExplanation_ranking() throws Exception {
- if (mActivityManager.isLowRamDevice() && !mPackageManager.hasSystemFeature(FEATURE_WATCH)) {
- return;
- }
-
toggleListenerAccess(TestNotificationListener.getId(),
InstrumentationRegistry.getInstrumentation(), true);
Thread.sleep(500); // wait for listener to be allowed
@@ -1952,10 +1912,6 @@
}
public void testTotalSilenceOnlyMuteStreams() throws Exception {
- if (mActivityManager.isLowRamDevice()) {
- return;
- }
-
final int originalFilter = mNotificationManager.getCurrentInterruptionFilter();
Policy origPolicy = mNotificationManager.getNotificationPolicy();
try {
@@ -1997,10 +1953,6 @@
}
public void testAlarmsOnlyMuteStreams() throws Exception {
- if (mActivityManager.isLowRamDevice()) {
- return;
- }
-
final int originalFilter = mNotificationManager.getCurrentInterruptionFilter();
Policy origPolicy = mNotificationManager.getNotificationPolicy();
try {
@@ -2042,10 +1994,6 @@
}
public void testAddAutomaticZenRule_configActivity() throws Exception {
- if (mActivityManager.isLowRamDevice()) {
- return;
- }
-
toggleNotificationPolicyAccess(mContext.getPackageName(),
InstrumentationRegistry.getInstrumentation(), true);
@@ -2058,10 +2006,6 @@
}
public void testUpdateAutomaticZenRule_configActivity() throws Exception {
- if (mActivityManager.isLowRamDevice()) {
- return;
- }
-
toggleNotificationPolicyAccess(mContext.getPackageName(),
InstrumentationRegistry.getInstrumentation(), true);
@@ -2076,10 +2020,6 @@
}
public void testRemoveAutomaticZenRule_configActivity() throws Exception {
- if (mActivityManager.isLowRamDevice()) {
- return;
- }
-
toggleNotificationPolicyAccess(mContext.getPackageName(),
InstrumentationRegistry.getInstrumentation(), true);
@@ -2095,10 +2035,6 @@
}
public void testSetAutomaticZenRuleState() throws Exception {
- if (mActivityManager.isLowRamDevice()) {
- return;
- }
-
toggleNotificationPolicyAccess(mContext.getPackageName(),
InstrumentationRegistry.getInstrumentation(), true);
@@ -2118,10 +2054,6 @@
}
public void testSetAutomaticZenRuleState_turnOff() throws Exception {
- if (mActivityManager.isLowRamDevice()) {
- return;
- }
-
toggleNotificationPolicyAccess(mContext.getPackageName(),
InstrumentationRegistry.getInstrumentation(), true);
@@ -2150,10 +2082,6 @@
}
public void testSetAutomaticZenRuleState_deletedRule() throws Exception {
- if (mActivityManager.isLowRamDevice()) {
- return;
- }
-
toggleNotificationPolicyAccess(mContext.getPackageName(),
InstrumentationRegistry.getInstrumentation(), true);
@@ -2178,10 +2106,6 @@
}
public void testSetAutomaticZenRuleState_multipleRules() throws Exception {
- if (mActivityManager.isLowRamDevice()) {
- return;
- }
-
toggleNotificationPolicyAccess(mContext.getPackageName(),
InstrumentationRegistry.getInstrumentation(), true);
@@ -2215,9 +2139,6 @@
}
public void testSetNotificationPolicy_P_setOldFields() throws Exception {
- if (mActivityManager.isLowRamDevice()) {
- return;
- }
toggleNotificationPolicyAccess(mContext.getPackageName(),
InstrumentationRegistry.getInstrumentation(), true);
Policy origPolicy = mNotificationManager.getNotificationPolicy();
@@ -2240,9 +2161,6 @@
}
public void testSetNotificationPolicy_P_setNewFields() throws Exception {
- if (mActivityManager.isLowRamDevice()) {
- return;
- }
toggleNotificationPolicyAccess(mContext.getPackageName(),
InstrumentationRegistry.getInstrumentation(), true);
Policy origPolicy = mNotificationManager.getNotificationPolicy();
@@ -2265,9 +2183,6 @@
}
public void testSetNotificationPolicy_P_setOldNewFields() throws Exception {
- if (mActivityManager.isLowRamDevice()) {
- return;
- }
toggleNotificationPolicyAccess(mContext.getPackageName(),
InstrumentationRegistry.getInstrumentation(), true);
Policy origPolicy = mNotificationManager.getNotificationPolicy();
@@ -2401,10 +2316,6 @@
public void testNotificationDelegate_cannotCancelNotificationsPostedByDelegator()
throws Exception {
- if (mActivityManager.isLowRamDevice() && !mPackageManager.hasSystemFeature(FEATURE_WATCH)) {
- return;
- }
-
toggleListenerAccess(TestNotificationListener.getId(),
InstrumentationRegistry.getInstrumentation(), true);
Thread.sleep(500); // wait for listener to be allowed
@@ -2555,10 +2466,6 @@
}
public void testShouldHideSilentStatusIcons() throws Exception {
- if (mActivityManager.isLowRamDevice() && !mPackageManager.hasSystemFeature(FEATURE_WATCH)) {
- return;
- }
-
try {
mNotificationManager.shouldHideSilentStatusBarIcons();
fail("Non-privileged apps should not get this information");
@@ -2573,10 +2480,6 @@
}
public void testMatchesCallFilter() throws Exception {
- if (mActivityManager.isLowRamDevice()) {
- return;
- }
-
// allow all callers
toggleNotificationPolicyAccess(mContext.getPackageName(),
InstrumentationRegistry.getInstrumentation(), true);
@@ -2634,10 +2537,6 @@
}
public void testNotificationListener_setNotificationsShown() throws Exception {
- if (mActivityManager.isLowRamDevice() && !mPackageManager.hasSystemFeature(FEATURE_WATCH)) {
- return;
- }
-
toggleListenerAccess(TestNotificationListener.getId(),
InstrumentationRegistry.getInstrumentation(), true);
Thread.sleep(500); // wait for listener to be allowed
@@ -2667,10 +2566,6 @@
}
public void testNotificationListener_getNotificationChannels() throws Exception {
- if (mActivityManager.isLowRamDevice() && !mPackageManager.hasSystemFeature(FEATURE_WATCH)) {
- return;
- }
-
toggleListenerAccess(TestNotificationListener.getId(),
InstrumentationRegistry.getInstrumentation(), true);
Thread.sleep(500); // wait for listener to be allowed
@@ -2687,10 +2582,6 @@
}
public void testNotificationListener_getNotificationChannelGroups() throws Exception {
- if (mActivityManager.isLowRamDevice() && !mPackageManager.hasSystemFeature(FEATURE_WATCH)) {
- return;
- }
-
toggleListenerAccess(TestNotificationListener.getId(),
InstrumentationRegistry.getInstrumentation(), true);
Thread.sleep(500); // wait for listener to be allowed
@@ -2706,10 +2597,6 @@
}
public void testNotificationListener_updateNotificationChannel() throws Exception {
- if (mActivityManager.isLowRamDevice() && !mPackageManager.hasSystemFeature(FEATURE_WATCH)) {
- return;
- }
-
toggleListenerAccess(TestNotificationListener.getId(),
InstrumentationRegistry.getInstrumentation(), true);
Thread.sleep(500); // wait for listener to be allowed
@@ -2730,10 +2617,6 @@
}
public void testNotificationListener_getActiveNotifications() throws Exception {
- if (mActivityManager.isLowRamDevice() && !mPackageManager.hasSystemFeature(FEATURE_WATCH)) {
- return;
- }
-
toggleListenerAccess(TestNotificationListener.getId(),
InstrumentationRegistry.getInstrumentation(), true);
Thread.sleep(500); // wait for listener to be allowed
@@ -2762,10 +2645,6 @@
public void testNotificationListener_getCurrentRanking() throws Exception {
- if (mActivityManager.isLowRamDevice() && !mPackageManager.hasSystemFeature(FEATURE_WATCH)) {
- return;
- }
-
toggleListenerAccess(TestNotificationListener.getId(),
InstrumentationRegistry.getInstrumentation(), true);
Thread.sleep(500); // wait for listener to be allowed
@@ -2780,10 +2659,6 @@
}
public void testNotificationListener_cancelNotifications() throws Exception {
- if (mActivityManager.isLowRamDevice() && !mPackageManager.hasSystemFeature(FEATURE_WATCH)) {
- return;
- }
-
toggleListenerAccess(TestNotificationListener.getId(),
InstrumentationRegistry.getInstrumentation(), true);
Thread.sleep(500); // wait for listener to be allowed
diff --git a/tests/autofillservice/Android.bp b/tests/autofillservice/Android.bp
index d39b86c..7882a76 100644
--- a/tests/autofillservice/Android.bp
+++ b/tests/autofillservice/Android.bp
@@ -17,6 +17,7 @@
defaults: ["cts_defaults"],
static_libs: [
"androidx.annotation_annotation",
+ "androidx.autofill_autofill",
"androidx.test.ext.junit",
"compatibility-device-util-axt",
"ctsdeviceutillegacy-axt",
diff --git a/tests/autofillservice/AndroidManifest.xml b/tests/autofillservice/AndroidManifest.xml
index cf21314..cd52ef5 100644
--- a/tests/autofillservice/AndroidManifest.xml
+++ b/tests/autofillservice/AndroidManifest.xml
@@ -164,6 +164,18 @@
</meta-data>
</service>
<service
+ android:name=".inline.InstrumentedAutoFillServiceInlineEnabled"
+ android:label="InstrumentedAutoFillServiceInlineEnabled"
+ android:permission="android.permission.BIND_AUTOFILL_SERVICE" >
+ <intent-filter>
+ <action android:name="android.service.autofill.AutofillService" />
+ </intent-filter>
+ <meta-data
+ android:name="android.autofill"
+ android:resource="@xml/autofill_service_inline_enabled">
+ </meta-data>
+ </service>
+ <service
android:name=".NoOpAutofillService"
android:label="NoOpAutofillService"
android:permission="android.permission.BIND_AUTOFILL_SERVICE" >
diff --git a/tests/autofillservice/res/xml/autofill_service_inline_enabled.xml b/tests/autofillservice/res/xml/autofill_service_inline_enabled.xml
new file mode 100644
index 0000000..6e4f2bb
--- /dev/null
+++ b/tests/autofillservice/res/xml/autofill_service_inline_enabled.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+-->
+<autofill-service xmlns:android="http://schemas.android.com/apk/res/android"
+ android:supportsInlineSuggestions="true">
+</autofill-service>
diff --git a/tests/autofillservice/src/android/autofillservice/cts/AbstractLoginActivityTestCase.java b/tests/autofillservice/src/android/autofillservice/cts/AbstractLoginActivityTestCase.java
index e7c0cf7..e496c24 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/AbstractLoginActivityTestCase.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/AbstractLoginActivityTestCase.java
@@ -48,6 +48,13 @@
}
/**
+ * Performs a click on username.
+ */
+ protected void requestClickOnUsername() throws TimeoutException {
+ mUiBot.waitForWindowChange(() -> mActivity.onUsername(View::performClick));
+ }
+
+ /**
* Requests focus on username and expect no Window event happens.
*/
protected void requestFocusOnUsernameNoWindowChange() {
diff --git a/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java b/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java
index a5ac282..54986d6 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java
@@ -31,11 +31,16 @@
import android.content.pm.PackageManager;
import android.provider.DeviceConfig;
import android.provider.Settings;
+import android.service.autofill.InlinePresentation;
import android.util.Log;
+import android.util.Size;
import android.view.autofill.AutofillManager;
+import android.view.inline.InlinePresentationSpec;
import android.widget.RemoteViews;
import androidx.annotation.NonNull;
+import androidx.autofill.InlinePresentationBuilder;
+import androidx.test.InstrumentationRegistry;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.compatibility.common.util.DeviceConfigStateChangerRule;
@@ -44,6 +49,7 @@
import com.android.compatibility.common.util.SafeCleanerRule;
import com.android.compatibility.common.util.SettingsStateKeeperRule;
import com.android.compatibility.common.util.TestNameUtils;
+import com.android.cts.mockime.ImeSettings;
import com.android.cts.mockime.MockImeSessionRule;
import org.junit.AfterClass;
@@ -193,7 +199,10 @@
};
@ClassRule
- public static final MockImeSessionRule sMockImeSessionRule = new MockImeSessionRule();
+ public static final MockImeSessionRule sMockImeSessionRule = new MockImeSessionRule(
+ InstrumentationRegistry.getTargetContext(),
+ InstrumentationRegistry.getInstrumentation().getUiAutomation(),
+ new ImeSettings.Builder().setInlineSuggestionsEnabled(true));
protected static final RequiredFeatureRule sRequiredFeatureRule =
new RequiredFeatureRule(PackageManager.FEATURE_AUTOFILL);
@@ -416,6 +425,12 @@
return presentation;
}
+ protected InlinePresentation createInlinePresentation(String message) {
+ return new InlinePresentation(new InlinePresentationBuilder(message).build(),
+ new InlinePresentationSpec.Builder(new Size(100, 100), new Size(400, 100))
+ .build(), /* pinned= */ false);
+ }
+
@NonNull
protected AutofillManager getAutofillManager() {
return mContext.getSystemService(AutofillManager.class);
diff --git a/tests/autofillservice/src/android/autofillservice/cts/CannedFillResponse.java b/tests/autofillservice/src/android/autofillservice/cts/CannedFillResponse.java
index 5523e7a3..cd099d7 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/CannedFillResponse.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/CannedFillResponse.java
@@ -27,6 +27,7 @@
import android.service.autofill.FillCallback;
import android.service.autofill.FillContext;
import android.service.autofill.FillResponse;
+import android.service.autofill.InlinePresentation;
import android.service.autofill.SaveInfo;
import android.service.autofill.UserData;
import android.util.Log;
@@ -75,6 +76,7 @@
private final CharSequence mSaveDescription;
private final Bundle mExtras;
private final RemoteViews mPresentation;
+ private final InlinePresentation mInlinePresentation;
private final RemoteViews mHeader;
private final RemoteViews mFooter;
private final IntentSender mAuthentication;
@@ -106,6 +108,7 @@
mSaveType = builder.mSaveType;
mExtras = builder.mExtras;
mPresentation = builder.mPresentation;
+ mInlinePresentation = builder.mInlinePresentation;
mHeader = builder.mHeader;
mFooter = builder.mFooter;
mAuthentication = builder.mAuthentication;
@@ -227,7 +230,7 @@
}
if (mAuthenticationIds != null) {
builder.setAuthentication(getAutofillIds(nodeResolver, mAuthenticationIds),
- mAuthentication, mPresentation);
+ mAuthentication, mPresentation, mInlinePresentation);
}
if (mDisableDuration > 0) {
builder.disableAutofill(mDisableDuration);
@@ -278,6 +281,7 @@
+ ", failureMessage=" + mFailureMessage
+ ", saveDescription=" + mSaveDescription
+ ", hasPresentation=" + (mPresentation != null)
+ + ", hasInlinePresentation=" + (mInlinePresentation != null)
+ ", hasHeader=" + (mHeader != null)
+ ", hasFooter=" + (mFooter != null)
+ ", hasAuthentication=" + (mAuthentication != null)
@@ -313,6 +317,7 @@
public int mSaveType = -1;
private Bundle mExtras;
private RemoteViews mPresentation;
+ private InlinePresentation mInlinePresentation;
private RemoteViews mFooter;
private RemoteViews mHeader;
private IntentSender mAuthentication;
@@ -400,6 +405,14 @@
}
/**
+ * Sets the view to present the response in the UI.
+ */
+ public Builder setInlinePresentation(InlinePresentation inlinePresentation) {
+ mInlinePresentation = inlinePresentation;
+ return this;
+ }
+
+ /**
* Sets the authentication intent.
*/
public Builder setAuthentication(IntentSender authentication, String... ids) {
@@ -558,16 +571,20 @@
public static class CannedDataset {
private final Map<String, AutofillValue> mFieldValues;
private final Map<String, RemoteViews> mFieldPresentations;
+ private final Map<String, InlinePresentation> mFieldInlinePresentations;
private final Map<String, Pair<Boolean, Pattern>> mFieldFilters;
private final RemoteViews mPresentation;
+ private final InlinePresentation mInlinePresentation;
private final IntentSender mAuthentication;
private final String mId;
private CannedDataset(Builder builder) {
mFieldValues = builder.mFieldValues;
mFieldPresentations = builder.mFieldPresentations;
+ mFieldInlinePresentations = builder.mFieldInlinePresentations;
mFieldFilters = builder.mFieldFilters;
mPresentation = builder.mPresentation;
+ mInlinePresentation = builder.mInlinePresentation;
mAuthentication = builder.mAuthentication;
mId = builder.mId;
}
@@ -576,9 +593,13 @@
* Creates a new dataset, replacing the field ids by the real ids from the assist structure.
*/
Dataset asDataset(Function<String, ViewNode> nodeResolver) {
- final Dataset.Builder builder = (mPresentation == null)
- ? new Dataset.Builder()
- : new Dataset.Builder(mPresentation);
+ final Dataset.Builder builder = mPresentation != null
+ ? mInlinePresentation == null
+ ? new Dataset.Builder(mPresentation)
+ : new Dataset.Builder(mPresentation, mInlinePresentation)
+ : mInlinePresentation == null
+ ? new Dataset.Builder()
+ : new Dataset.Builder(mInlinePresentation);
if (mFieldValues != null) {
for (Map.Entry<String, AutofillValue> entry : mFieldValues.entrySet()) {
@@ -590,18 +611,34 @@
final AutofillId autofillId = node.getAutofillId();
final AutofillValue value = entry.getValue();
final RemoteViews presentation = mFieldPresentations.get(id);
+ final InlinePresentation inlinePresentation = mFieldInlinePresentations.get(id);
final Pair<Boolean, Pattern> filter = mFieldFilters.get(id);
if (presentation != null) {
if (filter == null) {
- builder.setValue(autofillId, value, presentation);
+ if (inlinePresentation != null) {
+ builder.setValue(autofillId, value, presentation,
+ inlinePresentation);
+ } else {
+ builder.setValue(autofillId, value, presentation);
+ }
} else {
- builder.setValue(autofillId, value, filter.second, presentation);
+ if (inlinePresentation != null) {
+ builder.setValue(autofillId, value, filter.second, presentation,
+ inlinePresentation);
+ } else {
+ builder.setValue(autofillId, value, filter.second, presentation);
+ }
}
} else {
- if (filter == null) {
- builder.setValue(autofillId, value);
+ if (inlinePresentation != null) {
+ builder.setInlinePresentation(autofillId, value,
+ filter != null ? filter.second : null, inlinePresentation);
} else {
- builder.setValue(autofillId, value, filter.second);
+ if (filter == null) {
+ builder.setValue(autofillId, value);
+ } else {
+ builder.setValue(autofillId, value, filter.second);
+ }
}
}
}
@@ -613,7 +650,9 @@
@Override
public String toString() {
return "CannedDataset " + mId + " : [hasPresentation=" + (mPresentation != null)
+ + ", hasInlinePresentation=" + (mInlinePresentation != null)
+ ", fieldPresentations=" + (mFieldPresentations)
+ + ", fieldInlinePresentations=" + (mFieldInlinePresentations)
+ ", hasAuthentication=" + (mAuthentication != null)
+ ", fieldValues=" + mFieldValues
+ ", fieldFilters=" + mFieldFilters + "]";
@@ -622,9 +661,12 @@
public static class Builder {
private final Map<String, AutofillValue> mFieldValues = new HashMap<>();
private final Map<String, RemoteViews> mFieldPresentations = new HashMap<>();
+ private final Map<String, InlinePresentation> mFieldInlinePresentations =
+ new HashMap<>();
private final Map<String, Pair<Boolean, Pattern>> mFieldFilters = new HashMap<>();
private RemoteViews mPresentation;
+ private InlinePresentation mInlinePresentation;
private IntentSender mAuthentication;
private String mId;
@@ -749,6 +791,35 @@
}
/**
+ * 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.
+ * For example, {@link InstrumentedAutoFillService.Replier} resolves the id based on
+ * {@link IdMode}.
+ */
+ public Builder setField(String id, String text, RemoteViews presentation,
+ InlinePresentation inlinePresentation) {
+ setField(id, text);
+ mFieldPresentations.put(id, presentation);
+ mFieldInlinePresentations.put(id, inlinePresentation);
+ 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.
+ * For example, {@link InstrumentedAutoFillService.Replier} resolves the id based on
+ * {@link IdMode}.
+ */
+ public Builder setField(String id, String text, RemoteViews presentation,
+ InlinePresentation inlinePresentation, Pattern filter) {
+ setField(id, text, presentation, inlinePresentation);
+ mFieldFilters.put(id, new Pair<>(true, filter));
+ return this;
+ }
+
+ /**
* Sets the view to present the response in the UI.
*/
public Builder setPresentation(RemoteViews presentation) {
@@ -757,6 +828,14 @@
}
/**
+ * Sets the view to present the response in the UI.
+ */
+ public Builder setInlinePresentation(InlinePresentation inlinePresentation) {
+ mInlinePresentation = inlinePresentation;
+ return this;
+ }
+
+ /**
* Sets the authentication intent.
*/
public Builder setAuthentication(IntentSender authentication) {
@@ -772,6 +851,9 @@
return this;
}
+ /**
+ * Builds the canned dataset.
+ */
public CannedDataset build() {
return new CannedDataset(this);
}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/InlinePresentationTest.java b/tests/autofillservice/src/android/autofillservice/cts/InlinePresentationTest.java
new file mode 100644
index 0000000..e50b772
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/InlinePresentationTest.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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 com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.assertThrows;
+
+import android.app.slice.Slice;
+import android.app.slice.SliceSpec;
+import android.net.Uri;
+import android.os.Parcel;
+import android.service.autofill.InlinePresentation;
+import android.util.Size;
+import android.view.inline.InlinePresentationSpec;
+
+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 InlinePresentationTest {
+
+ @Test
+ public void testNullInlinePresentationSpecsThrowsException() {
+ assertThrows(NullPointerException.class,
+ () -> createInlinePresentation(/* createSlice */true, /* createSpec */ false));
+ }
+
+ @Test
+ public void testNullSliceThrowsException() {
+ assertThrows(NullPointerException.class,
+ () -> createInlinePresentation(/* createSlice */false, /* createSpec */ true));
+ }
+
+ @Test
+ public void testInlinePresentationValues() {
+ InlinePresentation presentation =
+ createInlinePresentation(/* createSlice */true, /* createSpec */ true);
+
+ assertThat(presentation.isPinned()).isFalse();
+ assertThat(presentation.getInlinePresentationSpec()).isNotNull();
+ assertThat(presentation.getSlice()).isNotNull();
+ assertThat(presentation.getSlice().getItems().size()).isEqualTo(0);
+ }
+
+ @Test
+ public void testtInlinePresentationParcelizeDeparcelize() {
+ InlinePresentation presentation =
+ createInlinePresentation(/* createSlice */true, /* createSpec */ true);
+
+ Parcel p = Parcel.obtain();
+ presentation.writeToParcel(p, 0);
+ p.setDataPosition(0);
+
+ InlinePresentation targetPresentation = InlinePresentation.CREATOR.createFromParcel(p);
+ p.recycle();
+
+ assertThat(targetPresentation.isPinned()).isEqualTo(presentation.isPinned());
+ assertThat(targetPresentation.getInlinePresentationSpec()).isEqualTo(
+ presentation.getInlinePresentationSpec());
+ assertThat(targetPresentation.getSlice().getUri()).isEqualTo(
+ presentation.getSlice().getUri());
+ assertThat(targetPresentation.getSlice().getSpec()).isEqualTo(
+ presentation.getSlice().getSpec());
+ }
+
+ private InlinePresentation createInlinePresentation(boolean createSlice, boolean createSpec) {
+ Slice slice = createSlice ? new Slice.Builder(Uri.parse("testuri"),
+ new SliceSpec("type", 1)).build() : null;
+ InlinePresentationSpec spec = createSpec ? new InlinePresentationSpec.Builder(
+ new Size(100, 100), new Size(400, 100)).build() : null;
+ return new InlinePresentation(slice, spec, /* pined */ false);
+ }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/Timeouts.java b/tests/autofillservice/src/android/autofillservice/cts/Timeouts.java
index d64aa41..a4799c8 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/Timeouts.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/Timeouts.java
@@ -26,7 +26,7 @@
private static final long ONE_TIMEOUT_TO_RULE_THEN_ALL_MS = 20_000;
private static final long ONE_NAPTIME_TO_RULE_THEN_ALL_MS = 2_000;
- static final long MOCK_IME_TIMEOUT_MS = 5_000;
+ public static final long MOCK_IME_TIMEOUT_MS = 5_000;
/**
* Timeout until framework binds / unbinds from service.
diff --git a/tests/autofillservice/src/android/autofillservice/cts/UiBot.java b/tests/autofillservice/src/android/autofillservice/cts/UiBot.java
index 9287e71..136bcba 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/UiBot.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/UiBot.java
@@ -70,6 +70,7 @@
import com.android.compatibility.common.util.RetryableException;
import com.android.compatibility.common.util.Timeout;
+import com.android.cts.mockime.MockIme;
import java.io.File;
import java.io.FileInputStream;
@@ -94,6 +95,8 @@
private static final String RESOURCE_ID_SAVE_BUTTON_NO = "autofill_save_no";
private static final String RESOURCE_ID_SAVE_BUTTON_YES = "autofill_save_yes";
private static final String RESOURCE_ID_OVERFLOW = "overflow";
+ //TODO: Change magic constant
+ private static final String RESOURCE_ID_SUGGESTION_STRIP = "message";
private static final String RESOURCE_STRING_SAVE_TITLE = "autofill_save_title";
private static final String RESOURCE_STRING_SAVE_TITLE_WITH_TYPE =
@@ -132,6 +135,8 @@
private static final BySelector SAVE_UI_SELECTOR = By.res("android", RESOURCE_ID_SAVE_SNACKBAR);
private static final BySelector DATASET_HEADER_SELECTOR =
By.res("android", RESOURCE_ID_DATASET_HEADER);
+ private static final BySelector SUGGESTION_STRIP_SELECTOR =
+ By.res("android", RESOURCE_ID_SUGGESTION_STRIP);
// TODO: figure out a more reliable solution that does not depend on SystemUI resources.
private static final String SPLIT_WINDOW_DIVIDER_ID =
@@ -258,6 +263,14 @@
}
/**
+ * Asserts the suggestion strip was never shown.
+ */
+ public void assertNoSuggestionStripEver() throws Exception {
+ assertNeverShown("suggestion strip", SUGGESTION_STRIP_SELECTOR,
+ DATASET_PICKER_NOT_SHOWN_NAPTIME_MS);
+ }
+
+ /**
* Asserts the dataset chooser is shown and contains exactly the given datasets.
*
* @return the dataset picker object.
@@ -332,6 +345,32 @@
}
/**
+ * Asserts the suggestion strip on the {@link MockIme} is shown and contains the given number
+ * of child suggestions.
+ *
+ * @param childrenCount the expected number of children.
+ *
+ * @return the suggestion strip object
+ */
+ public UiObject2 assertSuggestionStrip(int childrenCount) throws Exception {
+ final UiObject2 strip = findSuggestionStrip(UI_TIMEOUT);
+ assertThat(strip.getChildCount()).isEqualTo(childrenCount);
+ return strip;
+ }
+
+ /**
+ * Selects the suggestion in the {@link MockIme}'s suggestion strip at the given index.
+ *
+ * @param index the index of the suggestion to select.
+ */
+ public void selectSuggestion(int index) throws Exception {
+ final UiObject2 strip = findSuggestionStrip(UI_TIMEOUT);
+ assertThat(index).isAtLeast(0);
+ assertThat(index).isLessThan(strip.getChildCount());
+ strip.getChildren().get(index).click();
+ }
+
+ /**
* Gets the text of this object children.
*/
public List<String> getChildrenAsText(UiObject2 object) {
@@ -1037,6 +1076,10 @@
return picker;
}
+ private UiObject2 findSuggestionStrip(Timeout timeout) throws Exception {
+ return waitForObject(SUGGESTION_STRIP_SELECTOR, timeout);
+ }
+
/**
* Asserts a given object has the expected accessibility title.
*/
diff --git a/tests/autofillservice/src/android/autofillservice/cts/inline/InlineLoginActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/inline/InlineLoginActivityTest.java
new file mode 100644
index 0000000..39139eb
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/inline/InlineLoginActivityTest.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.inline;
+
+import static android.autofillservice.cts.Helper.ID_PASSWORD;
+import static android.autofillservice.cts.Helper.ID_USERNAME;
+import static android.autofillservice.cts.Helper.assertTextIsSanitized;
+import static android.autofillservice.cts.Helper.findAutofillIdByResourceId;
+import static android.autofillservice.cts.Helper.findNodeByResourceId;
+import static android.autofillservice.cts.Helper.getContext;
+import static android.autofillservice.cts.InstrumentedAutoFillService.waitUntilDisconnected;
+import static android.autofillservice.cts.Timeouts.MOCK_IME_TIMEOUT_MS;
+import static android.autofillservice.cts.inline.InstrumentedAutoFillServiceInlineEnabled.SERVICE_NAME;
+
+import static com.android.cts.mockime.ImeEventStreamTestUtils.editorMatcher;
+import static com.android.cts.mockime.ImeEventStreamTestUtils.expectBindInput;
+import static com.android.cts.mockime.ImeEventStreamTestUtils.expectEvent;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.junit.Assume.assumeTrue;
+
+import android.autofillservice.cts.AbstractLoginActivityTestCase;
+import android.autofillservice.cts.CannedFillResponse;
+import android.autofillservice.cts.Helper;
+import android.autofillservice.cts.InstrumentedAutoFillService;
+import android.os.Process;
+import android.service.autofill.FillContext;
+
+import com.android.compatibility.common.util.RetryableException;
+import com.android.cts.mockime.ImeEventStream;
+import com.android.cts.mockime.MockImeSession;
+
+import org.junit.Test;
+
+import java.util.concurrent.TimeoutException;
+
+public class InlineLoginActivityTest extends AbstractLoginActivityTestCase {
+
+ private static final String TAG = "InlineLoginActivityTest";
+
+ @Override
+ protected void enableService() {
+ Helper.enableAutofillService(getContext(), SERVICE_NAME);
+ }
+
+ @Test
+ public void testAutofill_noDataset() throws Exception {
+ // Set service.
+ enableService();
+
+ final MockImeSession mockImeSession = sMockImeSessionRule.getMockImeSession();
+ assumeTrue("MockIME not available", mockImeSession != null);
+
+ sReplier.addResponse(CannedFillResponse.NO_RESPONSE);
+
+ final ImeEventStream stream = mockImeSession.openEventStream();
+ mockImeSession.callRequestShowSelf(0);
+
+ // Wait until the MockIme gets bound to the TestActivity.
+ expectBindInput(stream, Process.myPid(), MOCK_IME_TIMEOUT_MS);
+
+ // Trigger auto-fill.
+ requestFocusOnUsername();
+ expectEvent(stream, editorMatcher("onStartInput", mActivity.getUsername().getId()),
+ MOCK_IME_TIMEOUT_MS);
+
+ sReplier.getNextFillRequest();
+
+ mUiBot.assertNoSuggestionStripEver();
+ mUiBot.assertNoDatasetsEver();
+
+ waitUntilDisconnected();
+ }
+
+ @Test
+ public void testAutofill_oneDataset() throws Exception {
+ testBasicLoginAutofill(/* numDatasets= */ 1, /* selectedDatasetIndex= */ 0);
+ }
+
+ @Test
+ public void testAutofill_twoDatasets_selectFirstDataset() throws Exception {
+ testBasicLoginAutofill(/* numDatasets= */ 2, /* selectedDatasetIndex= */ 0);
+
+ }
+
+ @Test
+ public void testAutofill_twoDatasets_selectSecondDataset() throws Exception {
+ testBasicLoginAutofill(/* numDatasets= */ 2, /* selectedDatasetIndex= */ 1);
+ }
+
+ private void testBasicLoginAutofill(int numDatasets, int selectedDatasetIndex)
+ throws Exception {
+ // Set service.
+ enableService();
+
+ final MockImeSession mockImeSession = sMockImeSessionRule.getMockImeSession();
+ assumeTrue("MockIME not available", mockImeSession != null);
+
+ final CannedFillResponse.Builder builder = new CannedFillResponse.Builder();
+ for (int i = 0; i < numDatasets; i++) {
+ builder.addDataset(new CannedFillResponse.CannedDataset.Builder()
+ .setField(ID_USERNAME, "dude" + i)
+ .setField(ID_PASSWORD, "sweet" + i)
+ .setPresentation(createPresentation("The Dude" + i))
+ .setInlinePresentation(createInlinePresentation("The Dude" + i))
+ .build());
+ }
+
+ sReplier.addResponse(builder.build());
+ mActivity.expectAutoFill("dude" + selectedDatasetIndex, "sweet" + selectedDatasetIndex);
+
+ // Dynamically set password to make sure it's sanitized.
+ mActivity.onPassword((v) -> v.setText("I AM GROOT"));
+
+ final ImeEventStream stream = mockImeSession.openEventStream();
+ mockImeSession.callRequestShowSelf(0);
+
+ // Wait until the MockIme gets bound to the TestActivity.
+ expectBindInput(stream, Process.myPid(), MOCK_IME_TIMEOUT_MS);
+
+ // Trigger auto-fill.
+ requestFocusOnUsername();
+ expectEvent(stream, editorMatcher("onStartInput", mActivity.getUsername().getId()),
+ MOCK_IME_TIMEOUT_MS);
+
+ //TODO: extServices bug cause test to fail first time, retry if suggestion strip missing.
+ try {
+ expectEvent(stream, event -> "onSuggestionViewUpdated".equals(event.getEventName()),
+ MOCK_IME_TIMEOUT_MS);
+ } catch (TimeoutException e) {
+ sReplier.getNextFillRequest();
+ throw new RetryableException("Retry inline test");
+ }
+
+ mUiBot.assertSuggestionStrip(numDatasets);
+ mUiBot.assertNoDatasetsEver();
+
+ mUiBot.selectSuggestion(selectedDatasetIndex);
+
+ // Check the results.
+ mActivity.assertAutoFilled();
+
+ // Make sure input was sanitized.
+ final InstrumentedAutoFillService.FillRequest request = sReplier.getNextFillRequest();
+ assertWithMessage("CancelationSignal is null").that(request.cancellationSignal).isNotNull();
+ assertTextIsSanitized(request.structure, ID_PASSWORD);
+ 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(
+ findNodeByResourceId(request.structure, ID_USERNAME).isFocused()).isTrue();
+ assertWithMessage("Password node is focused").that(
+ findNodeByResourceId(request.structure, ID_PASSWORD).isFocused()).isFalse();
+ }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/inline/InstrumentedAutoFillServiceInlineEnabled.java b/tests/autofillservice/src/android/autofillservice/cts/inline/InstrumentedAutoFillServiceInlineEnabled.java
new file mode 100644
index 0000000..a931c53
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/inline/InstrumentedAutoFillServiceInlineEnabled.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.inline;
+
+import android.autofillservice.cts.InstrumentedAutoFillService;
+import android.service.autofill.AutofillService;
+
+/**
+ * Implementation of {@link AutofillService} that has inline suggestions support enabled.
+ */
+public class InstrumentedAutoFillServiceInlineEnabled extends InstrumentedAutoFillService {
+ @SuppressWarnings("hiding")
+ static final String SERVICE_PACKAGE = "android.autofillservice.cts";
+ @SuppressWarnings("hiding")
+ static final String SERVICE_CLASS = "InstrumentedAutoFillServiceInlineEnabled";
+ @SuppressWarnings("hiding")
+ static final String SERVICE_NAME = SERVICE_PACKAGE + "/.inline." + SERVICE_CLASS;
+
+ public InstrumentedAutoFillServiceInlineEnabled() {
+ sInstance.set(this);
+ sServiceLabel = SERVICE_CLASS;
+ }
+}
diff --git a/tests/camera/AndroidManifest.xml b/tests/camera/AndroidManifest.xml
index f311b99..85221d3 100644
--- a/tests/camera/AndroidManifest.xml
+++ b/tests/camera/AndroidManifest.xml
@@ -25,6 +25,7 @@
<uses-permission android:name="android.permission.REORDER_TASKS" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
+ <uses-sdk android:targetSdkVersion="28" />
<application android:largeHeap="true">
<uses-library android:name="android.test.runner" />
diff --git a/tests/camera/libctscamera2jni/native-camera-jni.cpp b/tests/camera/libctscamera2jni/native-camera-jni.cpp
index 9dcc34f..31218db 100644
--- a/tests/camera/libctscamera2jni/native-camera-jni.cpp
+++ b/tests/camera/libctscamera2jni/native-camera-jni.cpp
@@ -939,18 +939,28 @@
}
camera_status_t initWithErrorLog() {
+ return initWithErrorLog(nullptr /*env*/, nullptr /*jOverrideCameraId*/);
+ }
+
+ camera_status_t initWithErrorLog(JNIEnv* env, jstring jOverrideCameraId) {
camera_status_t ret = ACameraManager_getCameraIdList(
mCameraManager, &mCameraIdList);
if (ret != ACAMERA_OK) {
LOG_ERROR(errorString, "Get camera id list failed: ret %d", ret);
return ret;
}
+
+ if (env != nullptr && jOverrideCameraId != nullptr) {
+ mOverrideCameraId = env->GetStringUTFChars(jOverrideCameraId, 0);
+ }
ret = ACameraManager_registerAvailabilityCallback(mCameraManager, &mServiceCb);
if (ret != ACAMERA_OK) {
LOG_ERROR(errorString, "Register availability callback failed: ret %d", ret);
return ret;
}
mMgrInited = true;
+ mJOverrideCameraId = jOverrideCameraId;
+ mJNIEnv = env;
return ACAMERA_OK;
}
@@ -971,6 +981,12 @@
mCameraIdList = nullptr;
}
mMgrInited = false;
+ if (mOverrideCameraId != nullptr && mJNIEnv != nullptr) {
+ mJNIEnv->ReleaseStringUTFChars(mJOverrideCameraId, mOverrideCameraId);
+ mOverrideCameraId = nullptr;
+ mJOverrideCameraId = nullptr;
+ mJNIEnv = nullptr;
+ }
return ACAMERA_OK;
}
@@ -978,6 +994,9 @@
if (!mMgrInited || !mCameraIdList) {
return -1;
}
+ if (mOverrideCameraId != nullptr) {
+ return 1;
+ }
return mCameraIdList->numCameras;
}
@@ -985,6 +1004,13 @@
if (!mMgrInited || !mCameraIdList || idx < 0 || idx >= mCameraIdList->numCameras) {
return nullptr;
}
+ if (mOverrideCameraId != nullptr) {
+ if (idx >= 1) {
+ return nullptr;
+ } else {
+ return mOverrideCameraId;
+ }
+ }
return mCameraIdList->cameraIds[idx];
}
@@ -993,10 +1019,18 @@
if (!mMgrInited || !mCameraIdList || idx < 0 || idx >= mCameraIdList->numCameras) {
return nullptr;
}
+ const char* cameraId = mCameraIdList->cameraIds[idx];
+ if (mOverrideCameraId != nullptr) {
+ if (idx >= 1) {
+ return nullptr;
+ } else {
+ cameraId = mOverrideCameraId;
+ }
+ }
ACameraMetadata* chars;
camera_status_t ret = ACameraManager_getCameraCharacteristics(
- mCameraManager, mCameraIdList->cameraIds[idx], &chars);
+ mCameraManager, cameraId, &chars);
if (ret != ACAMERA_OK) {
LOG_ERROR(errorString, "Get camera characteristics failed: ret %d", ret);
return nullptr;
@@ -1618,6 +1652,9 @@
ACameraOutputTarget* mReqPreviewOutput = nullptr;
ACameraOutputTarget* mReqImgReaderOutput = nullptr;
const char* mCameraId;
+ JNIEnv* mJNIEnv = nullptr;
+ jstring mJOverrideCameraId;
+ const char* mOverrideCameraId = nullptr;
bool mMgrInited = false; // cameraId, serviceListener
bool mImgReaderInited = false;
@@ -2014,13 +2051,13 @@
extern "C" jboolean
Java_android_hardware_camera2_cts_NativeCameraDeviceTest_\
testCameraDeviceOpenAndCloseNative(
- JNIEnv* env, jclass /*clazz*/) {
+ JNIEnv* env, jclass /*clazz*/, jstring jOverrideCameraId) {
ALOGV("%s", __FUNCTION__);
int numCameras = 0;
bool pass = false;
PreviewTestCase testCase;
- camera_status_t ret = testCase.initWithErrorLog();
+ camera_status_t ret = testCase.initWithErrorLog(env, jOverrideCameraId);
if (ret != ACAMERA_OK) {
// Don't log error here. testcase did it
goto cleanup;
@@ -2084,7 +2121,7 @@
extern "C" jboolean
Java_android_hardware_camera2_cts_NativeCameraDeviceTest_\
testCameraDeviceCreateCaptureRequestNative(
- JNIEnv* env, jclass /*clazz*/) {
+ JNIEnv* env, jclass /*clazz*/, jstring jOverrideCameraId) {
ALOGV("%s", __FUNCTION__);
bool pass = false;
ACameraManager* mgr = ACameraManager_create();
@@ -2095,9 +2132,18 @@
camera_status_t ret = ACameraManager_getCameraIdList(mgr, &cameraIdList);
int numCameras = cameraIdList->numCameras;
+ const char* overrideCameraId = nullptr;
+ if (jOverrideCameraId != nullptr) {
+ overrideCameraId = env->GetStringUTFChars(jOverrideCameraId, nullptr);
+ }
+
for (int i = 0; i < numCameras; i++) {
CameraDeviceListener deviceListener;
const char* cameraId = cameraIdList->cameraIds[i];
+ if (overrideCameraId != nullptr && strcmp(overrideCameraId, cameraId)) {
+ // Skip other cameras if overriding camera id to be tested.
+ continue;
+ }
ACameraDevice_StateCallbacks deviceCb {
&deviceListener,
CameraDeviceListener::onDisconnected,
@@ -2322,13 +2368,14 @@
extern "C" jboolean
Java_android_hardware_camera2_cts_NativeCameraDeviceTest_\
testCameraDeviceSessionOpenAndCloseNative(
- JNIEnv* env, jclass /*clazz*/, jobject jPreviewSurface) {
+ JNIEnv* env, jclass /*clazz*/, jobject jPreviewSurface,
+ jstring jOverrideCameraId) {
ALOGV("%s", __FUNCTION__);
int numCameras = 0;
bool pass = false;
PreviewTestCase testCase;
- camera_status_t ret = testCase.initWithErrorLog();
+ camera_status_t ret = testCase.initWithErrorLog(env, jOverrideCameraId);
if (ret != ACAMERA_OK) {
// Don't log error here. testcase did it
goto cleanup;
@@ -2460,7 +2507,8 @@
extern "C" jboolean
Java_android_hardware_camera2_cts_NativeCameraDeviceTest_\
testCameraDeviceSharedOutputUpdate(
- JNIEnv* env, jclass /*clazz*/, jobject jPreviewSurface, jobject jSharedSurface) {
+ JNIEnv* env, jclass /*clazz*/, jobject jPreviewSurface, jobject jSharedSurface,
+ jstring jOverrideCameraId) {
ALOGV("%s", __FUNCTION__);
int numCameras = 0;
bool pass = false;
@@ -2477,7 +2525,7 @@
uint32_t timeoutSec = 1;
uint32_t runPreviewSec = 2;
- camera_status_t ret = testCase.initWithErrorLog();
+ camera_status_t ret = testCase.initWithErrorLog(env, jOverrideCameraId);
if (ret != ACAMERA_OK) {
// Don't log error here. testcase did it
goto cleanup;
@@ -2729,13 +2777,14 @@
extern "C" jboolean
Java_android_hardware_camera2_cts_NativeCameraDeviceTest_\
testCameraDeviceSimplePreviewNative(
- JNIEnv* env, jclass /*clazz*/, jobject jPreviewSurface) {
+ JNIEnv* env, jclass /*clazz*/, jobject jPreviewSurface,
+ jstring jOverrideCameraId) {
ALOGV("%s", __FUNCTION__);
int numCameras = 0;
bool pass = false;
PreviewTestCase testCase;
- camera_status_t ret = testCase.initWithErrorLog();
+ camera_status_t ret = testCase.initWithErrorLog(env, jOverrideCameraId);
if (ret != ACAMERA_OK) {
// Don't log error here. testcase did it
goto cleanup;
@@ -2837,7 +2886,8 @@
extern "C" jboolean
Java_android_hardware_camera2_cts_NativeCameraDeviceTest_\
testCameraDevicePreviewWithSessionParametersNative(
- JNIEnv* env, jclass /*clazz*/, jobject jPreviewSurface) {
+ JNIEnv* env, jclass /*clazz*/, jobject jPreviewSurface,
+ jstring jOverrideCameraId) {
ALOGV("%s", __FUNCTION__);
int numCameras = 0;
bool pass = false;
@@ -2845,7 +2895,7 @@
ACameraMetadata* chars = nullptr;
PreviewTestCase testCase;
- camera_status_t ret = testCase.initWithErrorLog();
+ camera_status_t ret = testCase.initWithErrorLog(env, jOverrideCameraId);
if (ret != ACAMERA_OK) {
// Don't log error here. testcase did it
goto cleanup;
@@ -2965,7 +3015,8 @@
}
bool nativeCameraDeviceLogicalPhysicalStreaming(
- JNIEnv* env, jobject jPreviewSurface, bool usePhysicalSettings) {
+ JNIEnv* env, jobject jPreviewSurface, bool usePhysicalSettings,
+ jstring jOverrideCameraId) {
const int NUM_TEST_IMAGES = 10;
const int TEST_WIDTH = 640;
const int TEST_HEIGHT = 480;
@@ -2981,7 +3032,7 @@
uint32_t timeoutSec = 1;
uint32_t runPreviewSec = 2;
- camera_status_t ret = testCase.initWithErrorLog();
+ camera_status_t ret = testCase.initWithErrorLog(env, jOverrideCameraId);
if (ret != ACAMERA_OK) {
// Don't log error here. testcase did it
goto cleanup;
@@ -3244,22 +3295,26 @@
extern "C" jboolean
Java_android_hardware_camera2_cts_NativeCameraDeviceTest_\
testCameraDeviceLogicalPhysicalStreamingNative(
- JNIEnv* env, jclass /*clazz*/, jobject jPreviewSurface) {
+ JNIEnv* env, jclass /*clazz*/, jobject jPreviewSurface,
+ jstring jOverrideCameraId) {
return nativeCameraDeviceLogicalPhysicalStreaming(env,
- jPreviewSurface, false /*usePhysicalSettings*/);
+ jPreviewSurface, false /*usePhysicalSettings*/,
+ jOverrideCameraId);
}
extern "C" jboolean
Java_android_hardware_camera2_cts_NativeCameraDeviceTest_\
testCameraDeviceLogicalPhysicalSettingsNative(
- JNIEnv* env, jclass /*clazz*/, jobject jPreviewSurface) {
+ JNIEnv* env, jclass /*clazz*/, jobject jPreviewSurface,
+ jstring jOverrideCameraId) {
return nativeCameraDeviceLogicalPhysicalStreaming(env,
- jPreviewSurface, true /*usePhysicalSettings*/);
+ jPreviewSurface, true /*usePhysicalSettings*/,
+ jOverrideCameraId);
}
-
bool nativeImageReaderTestBase(
- JNIEnv* env, jstring jOutPath, jint format, AImageReader_ImageCallback cb) {
+ JNIEnv* env, jstring jOutPath, jint format, AImageReader_ImageCallback cb,
+ jstring jOverrideCameraId) {
const int NUM_TEST_IMAGES = 10;
const int TEST_WIDTH = 640;
const int TEST_HEIGHT = 480;
@@ -3275,7 +3330,7 @@
ALOGI("%s: out path is %s", __FUNCTION__, outPath);
}
- camera_status_t ret = testCase.initWithErrorLog();
+ camera_status_t ret = testCase.initWithErrorLog(env, jOverrideCameraId);
if (ret != ACAMERA_OK) {
// Don't log error here. testcase did it
goto cleanup;
@@ -3523,7 +3578,7 @@
// 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) {
+testCameraDeviceCaptureFailureNative(JNIEnv* env, jclass /*clazz*/, jstring jOverrideCameraId) {
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
@@ -3536,7 +3591,7 @@
uint32_t timeoutSec = 10; // It is important to keep this timeout bigger than the framework
// timeout
- camera_status_t ret = testCase.initWithErrorLog();
+ camera_status_t ret = testCase.initWithErrorLog(env, jOverrideCameraId);
if (ret != ACAMERA_OK) {
// Don't log error here. testcase did it
goto exit;
@@ -3697,46 +3752,51 @@
extern "C" jboolean
Java_android_hardware_camera2_cts_NativeImageReaderTest_\
testJpegNative(
- JNIEnv* env, jclass /*clazz*/, jstring jOutPath) {
+ JNIEnv* env, jclass /*clazz*/, jstring jOutPath,
+ jstring jOverrideCameraId) {
ALOGV("%s", __FUNCTION__);
return nativeImageReaderTestBase(env, jOutPath, AIMAGE_FORMAT_JPEG,
- ImageReaderListener::validateImageCb);
+ ImageReaderListener::validateImageCb, jOverrideCameraId);
}
extern "C" jboolean
Java_android_hardware_camera2_cts_NativeImageReaderTest_\
testY8Native(
- JNIEnv* env, jclass /*clazz*/, jstring jOutPath) {
+ JNIEnv* env, jclass /*clazz*/, jstring jOutPath,
+ jstring jOverrideCameraId) {
ALOGV("%s", __FUNCTION__);
return nativeImageReaderTestBase(env, jOutPath, AIMAGE_FORMAT_Y8,
- ImageReaderListener::validateImageCb);
+ ImageReaderListener::validateImageCb, jOverrideCameraId);
}
extern "C" jboolean
Java_android_hardware_camera2_cts_NativeImageReaderTest_\
testHeicNative(
- JNIEnv* env, jclass /*clazz*/, jstring jOutPath) {
+ JNIEnv* env, jclass /*clazz*/, jstring jOutPath,
+ jstring jOverrideCameraId) {
ALOGV("%s", __FUNCTION__);
return nativeImageReaderTestBase(env, jOutPath, AIMAGE_FORMAT_HEIC,
- ImageReaderListener::validateImageCb);
+ ImageReaderListener::validateImageCb, jOverrideCameraId);
}
extern "C" jboolean
Java_android_hardware_camera2_cts_NativeImageReaderTest_\
testDepthJpegNative(
- JNIEnv* env, jclass /*clazz*/, jstring jOutPath) {
+ JNIEnv* env, jclass /*clazz*/, jstring jOutPath,
+ jstring jOverrideCameraId) {
ALOGV("%s", __FUNCTION__);
return nativeImageReaderTestBase(env, jOutPath, AIMAGE_FORMAT_DEPTH_JPEG,
- ImageReaderListener::validateImageCb);
+ ImageReaderListener::validateImageCb, jOverrideCameraId);
}
extern "C" jboolean
Java_android_hardware_camera2_cts_NativeImageReaderTest_\
testImageReaderCloseAcquiredImagesNative(
- JNIEnv* env, jclass /*clazz*/) {
+ JNIEnv* env, jclass /*clazz*/,
+ jstring jOverrideCameraId) {
ALOGV("%s", __FUNCTION__);
return nativeImageReaderTestBase(env, nullptr, AIMAGE_FORMAT_JPEG,
- ImageReaderListener::acquireImageCb);
+ ImageReaderListener::acquireImageCb, jOverrideCameraId);
}
template <>
@@ -3858,7 +3918,8 @@
extern "C" jboolean
Java_android_hardware_camera2_cts_NativeStillCaptureTest_\
testStillCaptureNative(
- JNIEnv* env, jclass /*clazz*/, jstring jOutPath, jobject jPreviewSurface) {
+ JNIEnv* env, jclass /*clazz*/, jstring jOutPath, jobject jPreviewSurface,
+ jstring jOverrideCameraId) {
ALOGV("%s", __FUNCTION__);
const int NUM_TEST_IMAGES = 10;
const int TEST_WIDTH = 640;
@@ -3872,7 +3933,7 @@
const char* outPath = env->GetStringUTFChars(jOutPath, nullptr);
ALOGI("%s: out path is %s", __FUNCTION__, outPath);
- camera_status_t ret = testCase.initWithErrorLog();
+ camera_status_t ret = testCase.initWithErrorLog(env, jOverrideCameraId);
if (ret != ACAMERA_OK) {
// Don't log error here. testcase did it
goto cleanup;
diff --git a/tests/camera/src/android/hardware/camera2/cts/CameraManagerTest.java b/tests/camera/src/android/hardware/camera2/cts/CameraManagerTest.java
index 8f9f44b..19223b0 100644
--- a/tests/camera/src/android/hardware/camera2/cts/CameraManagerTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/CameraManagerTest.java
@@ -16,16 +16,13 @@
package android.hardware.camera2.cts;
-import static org.mockito.Mockito.*;
-import static org.mockito.AdditionalMatchers.not;
-import static org.mockito.AdditionalMatchers.and;
import static junit.framework.Assert.*;
-import android.app.ActivityManager;
+import static org.mockito.Mockito.*;
+
import android.app.Instrumentation;
import android.app.NotificationManager;
import android.app.UiAutomation;
-import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCharacteristics;
@@ -40,17 +37,17 @@
import android.os.Handler;
import android.os.HandlerThread;
import android.os.ParcelFileDescriptor;
-import android.test.AndroidTestCase;
import android.util.Log;
import android.util.Pair;
+
import androidx.test.InstrumentationRegistry;
import com.android.compatibility.common.util.PropertyUtil;
import com.android.ex.camera2.blocking.BlockingStateCallback;
-import org.junit.runners.Parameterized;
-import org.junit.runner.RunWith;
import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
import org.mockito.ArgumentCaptor;
import org.mockito.InOrder;
@@ -61,9 +58,9 @@
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
+import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
-import java.util.Set;
/**
* <p>Basic test for CameraManager class.</p>
@@ -575,6 +572,10 @@
*/
@Test
public void testCameraManagerListenerCallbacks() throws Exception {
+ if (mOverrideCameraId != null) {
+ // Testing is done for individual camera. Skip.
+ return;
+ }
testCameraManagerListenerCallbacks(/*useExecutor*/ false);
testCameraManagerListenerCallbacks(/*useExecutor*/ true);
}
@@ -809,14 +810,6 @@
Log.i(TAG, "No cameras present, skipping test");
return;
}
-
- ActivityManager am = mContext.getSystemService(ActivityManager.class);
-
- // Go devices do not support all interrupt filtering functionality
- if (am.isLowRamDevice()) {
- return;
- }
-
// Allow the test package to adjust notification policy
toggleNotificationPolicyAccess(mContext.getPackageName(),
InstrumentationRegistry.getInstrumentation(), true);
diff --git a/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java b/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
index 30dba3e..770f3e4 100644
--- a/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
@@ -2428,6 +2428,10 @@
// Skip test, http://b/141496896
return;
}
+ if (mOverrideCameraId != null) {
+ // A single camera is being tested. Skip test.
+ return;
+ }
int legacyDeviceCount = Camera.getNumberOfCameras();
assertTrue("More legacy devices: " + legacyDeviceCount + " compared to Camera2 devices: " +
mCharacteristics.size(), legacyDeviceCount <= mCharacteristics.size());
diff --git a/tests/camera/src/android/hardware/camera2/cts/FastBasicsTest.java b/tests/camera/src/android/hardware/camera2/cts/FastBasicsTest.java
index fc09e51..dc34087 100644
--- a/tests/camera/src/android/hardware/camera2/cts/FastBasicsTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/FastBasicsTest.java
@@ -25,6 +25,7 @@
import android.graphics.ImageFormat;
import android.graphics.SurfaceTexture;
import android.hardware.Camera;
+import android.hardware.cts.helpers.CameraUtils;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CaptureRequest;
@@ -193,7 +194,8 @@
@Presubmit
@Test
public void testCamera1() throws Exception {
- for (int i = 0; i < Camera.getNumberOfCameras(); i++) {
+ int[] cameraIds = CameraUtils.deriveCameraIdsUnderTest();
+ for (int i : cameraIds) {
Camera camera = null;
try {
Log.i(TAG, "Testing android.hardware.Camera API for camera device " + i);
diff --git a/tests/camera/src/android/hardware/camera2/cts/NativeCameraDeviceTest.java b/tests/camera/src/android/hardware/camera2/cts/NativeCameraDeviceTest.java
index 207c3e3..ff1d790 100644
--- a/tests/camera/src/android/hardware/camera2/cts/NativeCameraDeviceTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/NativeCameraDeviceTest.java
@@ -47,13 +47,13 @@
@Test
public void testCameraDeviceOpenAndClose() {
assertTrue("testCameraDeviceOpenAndClose fail, see log for details",
- testCameraDeviceOpenAndCloseNative());
+ testCameraDeviceOpenAndCloseNative(mOverrideCameraId));
}
@Test
public void testCameraDeviceCreateCaptureRequest() {
assertTrue("testCameraDeviceCreateCaptureRequest fail, see log for details",
- testCameraDeviceCreateCaptureRequestNative());
+ testCameraDeviceCreateCaptureRequestNative(mOverrideCameraId));
}
@Test
@@ -61,7 +61,7 @@
// Init preview surface to a guaranteed working size
updatePreviewSurface(new Size(640, 480));
assertTrue("testCameraDeviceSessionOpenAndClose fail, see log for details",
- testCameraDeviceSessionOpenAndCloseNative(mPreviewSurface));
+ testCameraDeviceSessionOpenAndCloseNative(mPreviewSurface, mOverrideCameraId));
}
@Test
@@ -69,7 +69,7 @@
// Init preview surface to a guaranteed working size
updatePreviewSurface(new Size(640, 480));
assertTrue("testCameraDeviceSimplePreview fail, see log for details",
- testCameraDeviceSimplePreviewNative(mPreviewSurface));
+ testCameraDeviceSimplePreviewNative(mPreviewSurface, mOverrideCameraId));
}
@Test
@@ -77,7 +77,8 @@
// Init preview surface to a guaranteed working size
updatePreviewSurface(new Size(640, 480));
assertTrue("testCameraDevicePreviewWithSessionParameters fail, see log for details",
- testCameraDevicePreviewWithSessionParametersNative(mPreviewSurface));
+ testCameraDevicePreviewWithSessionParametersNative(mPreviewSurface,
+ mOverrideCameraId));
}
@Test
@@ -89,7 +90,8 @@
outputTexture.setDefaultBufferSize(previewSize.getWidth(), previewSize.getHeight());
Surface outputSurface = new Surface(outputTexture);
assertTrue("testCameraDeviceSharedOutputUpdate fail, see log for details",
- testCameraDeviceSharedOutputUpdate(mPreviewSurface, outputSurface));
+ testCameraDeviceSharedOutputUpdate(mPreviewSurface, outputSurface,
+ mOverrideCameraId));
}
@Test
@@ -101,7 +103,7 @@
outputTexture.setDefaultBufferSize(previewSize.getWidth(), previewSize.getHeight());
Surface outputSurface = new Surface(outputTexture);
assertTrue("testCameraDeviceLogicalPhysicalStreaming fail, see log for details",
- testCameraDeviceLogicalPhysicalStreamingNative(mPreviewSurface));
+ testCameraDeviceLogicalPhysicalStreamingNative(mPreviewSurface, mOverrideCameraId));
}
@Test
@@ -113,24 +115,30 @@
outputTexture.setDefaultBufferSize(previewSize.getWidth(), previewSize.getHeight());
Surface outputSurface = new Surface(outputTexture);
assertTrue("testCameraDeviceLogicalPhysicalSettings fail, see log for details",
- testCameraDeviceLogicalPhysicalSettingsNative(mPreviewSurface));
+ testCameraDeviceLogicalPhysicalSettingsNative(mPreviewSurface, mOverrideCameraId));
}
@Test
public void testCameraDeviceCaptureFailure() {
assertTrue("testCameraDeviceCaptureFailure fail, see log for details",
- testCameraDeviceCaptureFailureNative());
+ testCameraDeviceCaptureFailureNative(mOverrideCameraId));
}
- private static native boolean testCameraDeviceOpenAndCloseNative();
- private static native boolean testCameraDeviceCreateCaptureRequestNative();
- private static native boolean testCameraDeviceSessionOpenAndCloseNative(Surface preview);
- private static native boolean testCameraDeviceSimplePreviewNative(Surface preview);
+ private static native boolean testCameraDeviceOpenAndCloseNative(String overrideCameraId);
+ private static native boolean testCameraDeviceCreateCaptureRequestNative(
+ String overrideCameraId);
+ private static native boolean testCameraDeviceSessionOpenAndCloseNative(Surface preview,
+ String overrideCameraId);
+ private static native boolean testCameraDeviceSimplePreviewNative(Surface preview,
+ String overrideCameraId);
private static native boolean testCameraDevicePreviewWithSessionParametersNative(
- Surface preview);
- 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();
+ Surface preview, String overrideCameraId);
+ private static native boolean testCameraDeviceSharedOutputUpdate(Surface src, Surface dst,
+ String overrideCameraId);
+ private static native boolean testCameraDeviceLogicalPhysicalStreamingNative(Surface preview,
+ String overrideCameraId);
+ private static native boolean testCameraDeviceLogicalPhysicalSettingsNative(Surface preview,
+ String overrideCameraId);
+ private static native boolean testCameraDeviceCaptureFailureNative(String overrideCameraId);
}
diff --git a/tests/camera/src/android/hardware/camera2/cts/NativeImageReaderTest.java b/tests/camera/src/android/hardware/camera2/cts/NativeImageReaderTest.java
index 493e670..ef9a57c 100644
--- a/tests/camera/src/android/hardware/camera2/cts/NativeImageReaderTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/NativeImageReaderTest.java
@@ -43,36 +43,36 @@
@Test
public void testJpeg() {
assertTrue("testJpeg fail, see log for details",
- testJpegNative(mDebugFileNameBase));
+ testJpegNative(mDebugFileNameBase, mOverrideCameraId));
}
@Test
public void testY8() {
assertTrue("testY8 fail, see log for details",
- testY8Native(mDebugFileNameBase));
+ testY8Native(mDebugFileNameBase, mOverrideCameraId));
}
@Test
public void testHeic() {
assertTrue("testHeic fail, see log for details",
- testHeicNative(mDebugFileNameBase));
+ testHeicNative(mDebugFileNameBase, mOverrideCameraId));
}
@Test
public void testDepthJpeg() {
assertTrue("testDepthJpeg fail, see log for details",
- testDepthJpegNative(mDebugFileNameBase));
+ testDepthJpegNative(mDebugFileNameBase, mOverrideCameraId));
}
@Test
public void testImageReaderCloseAcquiredImages() {
assertTrue("testImageReaderClose fail, see log for details",
- testImageReaderCloseAcquiredImagesNative());
+ testImageReaderCloseAcquiredImagesNative(mOverrideCameraId));
}
- private static native boolean testJpegNative(String filePath);
- private static native boolean testY8Native(String filePath);
- private static native boolean testHeicNative(String filePath);
- private static native boolean testDepthJpegNative(String filePath);
- private static native boolean testImageReaderCloseAcquiredImagesNative();
+ private static native boolean testJpegNative(String filePath, String overrideCameraId);
+ private static native boolean testY8Native(String filePath, String overrideCameraId);
+ private static native boolean testHeicNative(String filePath, String overrideCameraId);
+ private static native boolean testDepthJpegNative(String filePath, String overrideCameraId);
+ private static native boolean testImageReaderCloseAcquiredImagesNative(String overrideCameraId);
}
diff --git a/tests/camera/src/android/hardware/camera2/cts/NativeStillCaptureTest.java b/tests/camera/src/android/hardware/camera2/cts/NativeStillCaptureTest.java
index a778f7d..cfefd62 100644
--- a/tests/camera/src/android/hardware/camera2/cts/NativeStillCaptureTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/NativeStillCaptureTest.java
@@ -48,9 +48,9 @@
// Init preview surface to a guaranteed working size
updatePreviewSurface(new Size(640, 480));
assertTrue("testStillCapture fail, see log for details",
- testStillCaptureNative(mDebugFileNameBase, mPreviewSurface));
+ testStillCaptureNative(mDebugFileNameBase, mPreviewSurface, mOverrideCameraId));
}
private static native boolean testStillCaptureNative(
- String filePath, Surface previewSurface);
+ String filePath, Surface previewSurface, String overrideCameraId);
}
diff --git a/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2AndroidTestRule.java b/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2AndroidTestRule.java
index 91f6b80..13ce89a 100644
--- a/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2AndroidTestRule.java
+++ b/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2AndroidTestRule.java
@@ -38,12 +38,15 @@
import android.media.Image;
import android.media.Image.Plane;
import android.media.ImageReader;
+import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Log;
import android.view.Surface;
import android.view.WindowManager;
+import androidx.test.InstrumentationRegistry;
+
import com.android.ex.camera2.blocking.BlockingSessionCallback;
import com.android.ex.camera2.blocking.BlockingStateCallback;
@@ -87,6 +90,10 @@
private WindowManager mWindowManager;
private Context mContext;
+ private static final String CAMERA_ID_INSTR_ARG_KEY = "camera-id";
+ private static final Bundle mBundle = InstrumentationRegistry.getArguments();
+ private static final String mOverrideCameraId = mBundle.getString(CAMERA_ID_INSTR_ARG_KEY);
+
public Camera2AndroidTestRule(Context context) {
mContext = context;
}
@@ -175,6 +182,20 @@
return mCollector;
}
+ private String[] deriveCameraIdsUnderTest() throws Exception {
+ String[] idsUnderTest = mCameraManager.getCameraIdListNoLazy();
+ assertNotNull("Camera ids shouldn't be null", idsUnderTest);
+ if (mOverrideCameraId != null) {
+ if (Arrays.asList(idsUnderTest).contains(mOverrideCameraId)) {
+ idsUnderTest = new String[]{mOverrideCameraId};
+ } else {
+ idsUnderTest = new String[]{};
+ }
+ }
+
+ return idsUnderTest;
+ }
+
/**
* Set up the camera2 test case required environments, including CameraManager,
* HandlerThread, Camera IDs, and CameraStateCallback etc.
@@ -193,8 +214,7 @@
*/
System.setProperty("dexmaker.dexcache", getContext().getCacheDir().toString());
- mCameraIdsUnderTest = mCameraManager.getCameraIdListNoLazy();
- assertNotNull("Camera ids shouldn't be null", mCameraIdsUnderTest);
+ mCameraIdsUnderTest = deriveCameraIdsUnderTest();
mHandlerThread = new HandlerThread(TAG);
mHandlerThread.start();
mHandler = new Handler(mHandlerThread.getLooper());
@@ -239,8 +259,7 @@
Log.v(TAG, "Tear down...");
if (mCameraManager != null) {
try {
- String[] cameraIdsPostTest = mCameraManager.getCameraIdListNoLazy();
- assertNotNull("Camera ids shouldn't be null", cameraIdsPostTest);
+ String[] cameraIdsPostTest = deriveCameraIdsUnderTest();
Log.i(TAG, "Camera ids in setup:" + Arrays.toString(mCameraIdsUnderTest));
Log.i(TAG, "Camera ids in tearDown:" + Arrays.toString(cameraIdsPostTest));
assertTrue(
@@ -710,4 +729,4 @@
// Expected.
}
}
-}
\ No newline at end of file
+}
diff --git a/tests/camera/src/android/hardware/cts/CameraGLTest.java b/tests/camera/src/android/hardware/cts/CameraGLTest.java
index c83f074..a9e82ef 100644
--- a/tests/camera/src/android/hardware/cts/CameraGLTest.java
+++ b/tests/camera/src/android/hardware/cts/CameraGLTest.java
@@ -78,6 +78,7 @@
private Looper mLooper = null;
private final ConditionVariable mSurfaceTextureDone = new ConditionVariable();
private final ConditionVariable mPreviewDone = new ConditionVariable();
+ private int[] mCameraIds;
Camera mCamera;
boolean mIsExternalCamera;
@@ -105,6 +106,8 @@
GLSurfaceViewCtsActivity ctsActivity = mActivityRule.getActivity();
// Store a link to the view so we can redraw it when needed
mGLView = ctsActivity.getView();
+
+ mCameraIds = CameraUtils.deriveCameraIdsUnderTest();
}
@After
@@ -324,8 +327,7 @@
wl.acquire();
try {
/* Run the requested test per camera */
- int nCameras = Camera.getNumberOfCameras();
- for (int id = 0; id < nCameras; id++) {
+ for (int id : mCameraIds) {
Log.v(TAG, "Camera id=" + id);
test.run(id);
}
diff --git a/tests/camera/src/android/hardware/cts/CameraTest.java b/tests/camera/src/android/hardware/cts/CameraTest.java
index 47ffca6..c8330f7 100644
--- a/tests/camera/src/android/hardware/cts/CameraTest.java
+++ b/tests/camera/src/android/hardware/cts/CameraTest.java
@@ -124,6 +124,7 @@
private final ConditionVariable mPreviewDone = new ConditionVariable();
private final ConditionVariable mFocusDone = new ConditionVariable();
private final ConditionVariable mSnapshotDone = new ConditionVariable();
+ private int[] mCameraIds;
Camera mCamera;
boolean mIsExternalCamera;
@@ -134,6 +135,7 @@
@Before
public void setUp() throws Exception {
+ mCameraIds = CameraUtils.deriveCameraIdsUnderTest();
}
@After
@@ -408,8 +410,7 @@
@UiThreadTest
@Test
public void testTakePicture() throws Exception {
- int nCameras = Camera.getNumberOfCameras();
- for (int id = 0; id < nCameras; id++) {
+ for (int id : mCameraIds) {
Log.v(TAG, "Camera id=" + id);
initializeMessageLooper(id);
mCamera.startPreview();
@@ -454,8 +455,7 @@
@UiThreadTest
@Test
public void testPreviewCallback() throws Exception {
- int nCameras = Camera.getNumberOfCameras();
- for (int id = 0; id < nCameras; id++) {
+ for (int id : mCameraIds) {
Log.v(TAG, "Camera id=" + id);
testPreviewCallbackByCamera(id);
}
@@ -509,8 +509,7 @@
@UiThreadTest
@Test
public void testStabilizationOneShotPreviewCallback() throws Exception {
- int nCameras = Camera.getNumberOfCameras();
- for (int id = 0; id < nCameras; id++) {
+ for (int id : mCameraIds) {
Log.v(TAG, "Camera id=" + id);
testStabilizationOneShotPreviewCallbackByCamera(id);
}
@@ -535,8 +534,7 @@
@UiThreadTest
@Test
public void testSetOneShotPreviewCallback() throws Exception {
- int nCameras = Camera.getNumberOfCameras();
- for (int id = 0; id < nCameras; id++) {
+ for (int id : mCameraIds) {
Log.v(TAG, "Camera id=" + id);
testSetOneShotPreviewCallbackByCamera(id);
}
@@ -559,8 +557,7 @@
@UiThreadTest
@Test
public void testSetPreviewDisplay() throws Exception {
- int nCameras = Camera.getNumberOfCameras();
- for (int id = 0; id < nCameras; id++) {
+ for (int id : mCameraIds) {
Log.v(TAG, "Camera id=" + id);
testSetPreviewDisplayByCamera(id);
}
@@ -603,8 +600,7 @@
@UiThreadTest
@Test
public void testDisplayOrientation() throws Exception {
- int nCameras = Camera.getNumberOfCameras();
- for (int id = 0; id < nCameras; id++) {
+ for (int id : mCameraIds) {
Log.v(TAG, "Camera id=" + id);
testDisplayOrientationByCamera(id);
}
@@ -642,8 +638,7 @@
@UiThreadTest
@Test
public void testParameters() throws Exception {
- int nCameras = Camera.getNumberOfCameras();
- for (int id = 0; id < nCameras; id++) {
+ for (int id : mCameraIds) {
Log.v(TAG, "Camera id=" + id);
testParametersByCamera(id);
}
@@ -866,8 +861,7 @@
@UiThreadTest
@Test
public void testJpegThumbnailSize() throws Exception {
- int nCameras = Camera.getNumberOfCameras();
- for (int id = 0; id < nCameras; id++) {
+ for (int id : mCameraIds) {
Log.v(TAG, "Camera id=" + id);
initializeMessageLooper(id);
testJpegThumbnailSizeByCamera(false, 0, 0);
@@ -940,8 +934,7 @@
@UiThreadTest
@Test
public void testJpegExif() throws Exception {
- int nCameras = Camera.getNumberOfCameras();
- for (int id = 0; id < nCameras; id++) {
+ for (int id : mCameraIds) {
Log.v(TAG, "Camera id=" + id);
initializeMessageLooper(id);
testJpegExifByCamera(false);
@@ -1233,8 +1226,7 @@
@UiThreadTest
@Test
public void testLockUnlock() throws Exception {
- int nCameras = Camera.getNumberOfCameras();
- for (int id = 0; id < nCameras; id++) {
+ for (int id : mCameraIds) {
Log.v(TAG, "Camera id=" + id);
testLockUnlockByCamera(id);
}
@@ -1426,8 +1418,7 @@
@UiThreadTest
@Test
public void testPreviewCallbackWithBuffer() throws Exception {
- int nCameras = Camera.getNumberOfCameras();
- for (int id = 0; id < nCameras; id++) {
+ for (int id : mCameraIds) {
Log.v(TAG, "Camera id=" + id);
testPreviewCallbackWithBufferByCamera(id);
}
@@ -1524,8 +1515,7 @@
@UiThreadTest
@Test
public void testImmediateZoom() throws Exception {
- int nCameras = Camera.getNumberOfCameras();
- for (int id = 0; id < nCameras; id++) {
+ for (int id : mCameraIds) {
Log.v(TAG, "Camera id=" + id);
testImmediateZoomByCamera(id);
}
@@ -1596,8 +1586,7 @@
@UiThreadTest
@Test
public void testSmoothZoom() throws Exception {
- int nCameras = Camera.getNumberOfCameras();
- for (int id = 0; id < nCameras; id++) {
+ for (int id : mCameraIds) {
Log.v(TAG, "Camera id=" + id);
testSmoothZoomByCamera(id);
}
@@ -1722,8 +1711,7 @@
@UiThreadTest
@Test
public void testFocusDistances() throws Exception {
- int nCameras = Camera.getNumberOfCameras();
- for (int id = 0; id < nCameras; id++) {
+ for (int id : mCameraIds) {
Log.v(TAG, "Camera id=" + id);
testFocusDistancesByCamera(id);
}
@@ -1831,8 +1819,7 @@
@UiThreadTest
@Test
public void testCancelAutofocus() throws Exception {
- int nCameras = Camera.getNumberOfCameras();
- for (int id = 0; id < nCameras; id++) {
+ for (int id : mCameraIds) {
Log.v(TAG, "Camera id=" + id);
testCancelAutofocusByCamera(id);
}
@@ -1913,6 +1900,11 @@
@UiThreadTest
@Test
public void testMultipleCameras() throws Exception {
+ if (CameraUtils.mOverrideCameraId != null) {
+ // A single camera is being tested. Skip.
+ return;
+ }
+
int nCameras = Camera.getNumberOfCameras();
Log.v(TAG, "total " + nCameras + " cameras");
assertTrue(nCameras >= 0);
@@ -1976,8 +1968,7 @@
@UiThreadTest
@Test(timeout=60*60*1000) // timeout = 60 mins for long running tests
public void testPreviewPictureSizesCombination() throws Exception {
- int nCameras = Camera.getNumberOfCameras();
- for (int id = 0; id < nCameras; id++) {
+ for (int id : mCameraIds) {
Log.v(TAG, "Camera id=" + id);
testPreviewPictureSizesCombinationByCamera(id);
}
@@ -2093,8 +2084,7 @@
@UiThreadTest
@Test
public void testPreviewFpsRange() throws Exception {
- int nCameras = Camera.getNumberOfCameras();
- for (int id = 0; id < nCameras; id++) {
+ for (int id : mCameraIds) {
Log.v(TAG, "Camera id=" + id);
testPreviewFpsRangeByCamera(id);
}
@@ -2322,8 +2312,7 @@
@UiThreadTest
@Test
public void testSceneMode() throws Exception {
- int nCameras = Camera.getNumberOfCameras();
- for (int id = 0; id < nCameras; id++) {
+ for (int id : mCameraIds) {
Log.v(TAG, "Camera id=" + id);
testSceneModeByCamera(id);
}
@@ -2423,8 +2412,7 @@
@UiThreadTest
@Test
public void testInvalidParameters() throws Exception {
- int nCameras = Camera.getNumberOfCameras();
- for (int id = 0; id < nCameras; id++) {
+ for (int id : mCameraIds) {
Log.v(TAG, "Camera id=" + id);
testInvalidParametersByCamera(id);
}
@@ -2478,8 +2466,7 @@
@UiThreadTest
@Test
public void testGetParameterDuringFocus() throws Exception {
- int nCameras = Camera.getNumberOfCameras();
- for (int id = 0; id < nCameras; id++) {
+ for (int id : mCameraIds) {
Log.v(TAG, "Camera id=" + id);
testGetParameterDuringFocusByCamera(id);
}
@@ -2514,8 +2501,7 @@
@UiThreadTest
@Test
public void testPreviewFormats() throws Exception {
- int nCameras = Camera.getNumberOfCameras();
- for (int id = 0; id < nCameras; id++) {
+ for (int id : mCameraIds) {
Log.v(TAG, "Camera id=" + id);
testPreviewFormatsByCamera(id);
}
@@ -2539,6 +2525,11 @@
@UiThreadTest
@Test
public void testMultiCameraRelease() throws Exception {
+ if (CameraUtils.mOverrideCameraId != null) {
+ // A single camera is being tested. Skip.
+ return;
+ }
+
// Verify that multiple cameras exist, and that they can be opened at the same time
if (VERBOSE) Log.v(TAG, "testMultiCameraRelease: Checking pre-conditions.");
int nCameras = Camera.getNumberOfCameras();
@@ -2628,8 +2619,7 @@
@UiThreadTest
@Test
public void testFocusAreas() throws Exception {
- int nCameras = Camera.getNumberOfCameras();
- for (int id = 0; id < nCameras; id++) {
+ for (int id : mCameraIds) {
Log.v(TAG, "Camera id=" + id);
initializeMessageLooper(id);
@@ -2648,8 +2638,7 @@
@UiThreadTest
@Test
public void testMeteringAreas() throws Exception {
- int nCameras = Camera.getNumberOfCameras();
- for (int id = 0; id < nCameras; id++) {
+ for (int id : mCameraIds) {
Log.v(TAG, "Camera id=" + id);
initializeMessageLooper(id);
Parameters parameters = mCamera.getParameters();
@@ -2785,8 +2774,7 @@
@UiThreadTest
@Test
public void testJpegCallbackStartPreview() throws Exception {
- int nCameras = Camera.getNumberOfCameras();
- for (int id = 0; id < nCameras; id++) {
+ for (int id : mCameraIds) {
Log.v(TAG, "Camera id=" + id);
testJpegCallbackStartPreviewByCamera(id);
}
@@ -2815,8 +2803,7 @@
@UiThreadTest
@Test
public void testRecordingHint() throws Exception {
- int nCameras = Camera.getNumberOfCameras();
- for (int id = 0; id < nCameras; id++) {
+ for (int id : mCameraIds) {
Log.v(TAG, "Camera id=" + id);
testRecordingHintByCamera(id);
}
@@ -2911,8 +2898,7 @@
@UiThreadTest
@Test
public void testAutoExposureLock() throws Exception {
- int nCameras = Camera.getNumberOfCameras();
- for (int id = 0; id < nCameras; id++) {
+ for (int id : mCameraIds) {
Log.v(TAG, "Camera id=" + id);
initializeMessageLooper(id);
Parameters parameters = mCamera.getParameters();
@@ -2928,8 +2914,7 @@
@UiThreadTest
@Test
public void testAutoWhiteBalanceLock() throws Exception {
- int nCameras = Camera.getNumberOfCameras();
- for (int id = 0; id < nCameras; id++) {
+ for (int id : mCameraIds) {
Log.v(TAG, "Camera id=" + id);
initializeMessageLooper(id);
Parameters parameters = mCamera.getParameters();
@@ -2945,8 +2930,7 @@
@UiThreadTest
@Test
public void test3ALockInteraction() throws Exception {
- int nCameras = Camera.getNumberOfCameras();
- for (int id = 0; id < nCameras; id++) {
+ for (int id : mCameraIds) {
Log.v(TAG, "Camera id=" + id);
initializeMessageLooper(id);
Parameters parameters = mCamera.getParameters();
@@ -3170,8 +3154,7 @@
@UiThreadTest
@Test
public void testFaceDetection() throws Exception {
- int nCameras = Camera.getNumberOfCameras();
- for (int id = 0; id < nCameras; id++) {
+ for (int id : mCameraIds) {
Log.v(TAG, "Camera id=" + id);
testFaceDetectionByCamera(id);
}
@@ -3297,8 +3280,7 @@
@UiThreadTest
@Test(timeout=60*60*1000) // timeout = 60 mins for long running tests
public void testVideoSnapshot() throws Exception {
- int nCameras = Camera.getNumberOfCameras();
- for (int id = 0; id < nCameras; id++) {
+ for (int id : mCameraIds) {
Log.v(TAG, "Camera id=" + id);
testVideoSnapshotByCamera(id, /*recordingHint*/false);
testVideoSnapshotByCamera(id, /*recordingHint*/true);
@@ -3378,8 +3360,7 @@
@Test
public void testPreviewCallbackWithPicture() throws Exception {
- int nCameras = Camera.getNumberOfCameras();
- for (int id = 0; id < nCameras; id++) {
+ for (int id : mCameraIds) {
Log.v(TAG, "Camera id=" + id);
testPreviewCallbackWithPictureByCamera(id);
}
@@ -3438,8 +3419,7 @@
@Test
public void testEnableShutterSound() throws Exception {
- int nCameras = Camera.getNumberOfCameras();
- for (int id = 0; id < nCameras; id++) {
+ for (int id : mCameraIds) {
Log.v(TAG, "Camera id=" + id);
testEnableShutterSoundByCamera(id);
}
diff --git a/tests/camera/src/android/hardware/cts/Camera_SizeTest.java b/tests/camera/src/android/hardware/cts/Camera_SizeTest.java
index 77e75dd..785535c 100644
--- a/tests/camera/src/android/hardware/cts/Camera_SizeTest.java
+++ b/tests/camera/src/android/hardware/cts/Camera_SizeTest.java
@@ -70,7 +70,8 @@
* @see {@link android.hardware.camera2.CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL}
*/
public void testMaxAspectRatios() throws Exception {
- for (int id = 0; id < Camera.getNumberOfCameras(); ++id) {
+ int[] cameraIds = CameraUtils.deriveCameraIdsUnderTest();
+ for (int id : cameraIds) {
if (CameraUtils.isLegacyHAL(getContext(), id)) {
Camera camera = Camera.open(id);
diff --git a/tests/camera/src/android/hardware/cts/LegacyCameraPerformanceTest.java b/tests/camera/src/android/hardware/cts/LegacyCameraPerformanceTest.java
index ac3f6dd..5cc67fd 100644
--- a/tests/camera/src/android/hardware/cts/LegacyCameraPerformanceTest.java
+++ b/tests/camera/src/android/hardware/cts/LegacyCameraPerformanceTest.java
@@ -20,6 +20,7 @@
import android.graphics.SurfaceTexture;
import android.hardware.Camera;
import android.hardware.Camera.Parameters;
+import android.hardware.cts.helpers.CameraUtils;
import android.os.SystemClock;
import android.util.Log;
@@ -49,29 +50,31 @@
private Instrumentation mInstrumentation;
private CameraPerformanceTestHelper mHelper;
+ private int[] mCameraIds;
@Before
public void setUp() throws Exception {
mInstrumentation = InstrumentationRegistry.getInstrumentation();
mHelper = new CameraPerformanceTestHelper();
+ mCameraIds = CameraUtils.deriveCameraIdsUnderTest();
}
@After
public void tearDown() throws Exception {
if (mHelper.getCamera() != null) {
mHelper.getCamera().release();
-
}
}
@Test
public void testLegacyApiPerformance() throws Exception {
final int NUM_TEST_LOOPS = 10;
+ if (mCameraIds.length == 0) return;
- int nCameras = Camera.getNumberOfCameras();
- double[] avgCameraTakePictureTimes = new double[nCameras];
+ double[] avgCameraTakePictureTimes = new double[mCameraIds.length];
- for (int id = 0; id < nCameras; id++) {
+ int count = 0;
+ for (int id : mCameraIds) {
DeviceReportLog reportLog = new DeviceReportLog(REPORT_LOG_NAME,
"test_camera_takePicture");
reportLog.addValue("camera_id", id, ResultType.NEUTRAL, ResultUnit.NONE);
@@ -172,7 +175,7 @@
+ ". Max(ms): " + Stat.getMax(cameraTakePictureTimes));
}
- avgCameraTakePictureTimes[id] = Stat.getAverage(cameraTakePictureTimes);
+ avgCameraTakePictureTimes[count++] = Stat.getAverage(cameraTakePictureTimes);
reportLog.addValues("camera_open_time", cameraOpenTimes, ResultType.LOWER_BETTER,
ResultUnit.MS);
reportLog.addValues("camera_start_preview_time", startPreviewTimes,
@@ -191,7 +194,7 @@
reportLog.submit(mInstrumentation);
}
- if (nCameras != 0) {
+ if (mCameraIds.length != 0) {
DeviceReportLog reportLog = new DeviceReportLog(REPORT_LOG_NAME,
"test_camera_takepicture_average");
reportLog.setSummary("camera_takepicture_average_time_for_all_cameras",
@@ -200,4 +203,4 @@
reportLog.submit(mInstrumentation);
}
}
-}
\ No newline at end of file
+}
diff --git a/tests/camera/utils/src/android/hardware/camera2/cts/Camera2ParameterizedTestCase.java b/tests/camera/utils/src/android/hardware/camera2/cts/Camera2ParameterizedTestCase.java
index c7deb5a..6d90f2e 100644
--- a/tests/camera/utils/src/android/hardware/camera2/cts/Camera2ParameterizedTestCase.java
+++ b/tests/camera/utils/src/android/hardware/camera2/cts/Camera2ParameterizedTestCase.java
@@ -49,15 +49,13 @@
System.setProperty("dexmaker.dexcache", mContext.getCacheDir().toString());
mCameraManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
assertNotNull("Unable to get CameraManager", mCameraManager);
- mCameraIdsUnderTest =
- CameraTestUtils.getCameraIdListForTesting(mCameraManager, mAdoptShellPerm);
+ mCameraIdsUnderTest = deriveCameraIdsUnderTest();
assertNotNull("Unable to get camera ids", mCameraIdsUnderTest);
}
@Override
public void tearDown() throws Exception {
- String[] cameraIdsPostTest =
- CameraTestUtils.getCameraIdListForTesting(mCameraManager, mAdoptShellPerm);
+ String[] cameraIdsPostTest = deriveCameraIdsUnderTest();
assertNotNull("Camera ids shouldn't be null", cameraIdsPostTest);
Log.i(TAG, "Camera ids in setup:" + Arrays.toString(mCameraIdsUnderTest));
Log.i(TAG, "Camera ids in tearDown:" + Arrays.toString(cameraIdsPostTest));
@@ -67,4 +65,19 @@
mCameraIdsUnderTest.length == cameraIdsPostTest.length);
super.tearDown();
}
+
+ private String[] deriveCameraIdsUnderTest() throws Exception {
+ String[] idsUnderTest =
+ CameraTestUtils.getCameraIdListForTesting(mCameraManager, mAdoptShellPerm);
+ assertNotNull("Camera ids shouldn't be null", idsUnderTest);
+ if (mOverrideCameraId != null) {
+ if (Arrays.asList(idsUnderTest).contains(mOverrideCameraId)) {
+ idsUnderTest = new String[]{mOverrideCameraId};
+ } else {
+ idsUnderTest = new String[]{};
+ }
+ }
+
+ return idsUnderTest;
+ }
}
diff --git a/tests/camera/utils/src/android/hardware/cts/helpers/CameraParameterizedTestCase.java b/tests/camera/utils/src/android/hardware/cts/helpers/CameraParameterizedTestCase.java
index b2f4c04..047fba5 100644
--- a/tests/camera/utils/src/android/hardware/cts/helpers/CameraParameterizedTestCase.java
+++ b/tests/camera/utils/src/android/hardware/cts/helpers/CameraParameterizedTestCase.java
@@ -18,6 +18,7 @@
import android.app.UiAutomation;
import android.content.Context;
+import android.os.Bundle;
import android.util.Log;
import androidx.test.InstrumentationRegistry;
@@ -42,6 +43,10 @@
@Parameter(0)
public boolean mAdoptShellPerm;
+ private static final String CAMERA_ID_INSTR_ARG_KEY = "camera-id";
+ private static final Bundle mBundle = InstrumentationRegistry.getArguments();
+ protected static final String mOverrideCameraId = mBundle.getString(CAMERA_ID_INSTR_ARG_KEY);
+
@Parameters
public static Iterable<? extends Object> data() {
List<Boolean> adoptShellPerm = new ArrayList<Boolean>();
diff --git a/tests/camera/utils/src/android/hardware/cts/helpers/CameraUtils.java b/tests/camera/utils/src/android/hardware/cts/helpers/CameraUtils.java
index 82df26e..2370023 100644
--- a/tests/camera/utils/src/android/hardware/cts/helpers/CameraUtils.java
+++ b/tests/camera/utils/src/android/hardware/cts/helpers/CameraUtils.java
@@ -20,14 +20,22 @@
import android.hardware.Camera;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraManager;
+import android.os.Bundle;
+
+import androidx.test.InstrumentationRegistry;
import java.util.Comparator;
+import java.util.stream.IntStream;
/**
* Utility class containing helper functions for the Camera CTS tests.
*/
public class CameraUtils {
+ private static final String CAMERA_ID_INSTR_ARG_KEY = "camera-id";
+ private static final Bundle mBundle = InstrumentationRegistry.getArguments();
+ public static final String mOverrideCameraId = mBundle.getString(CAMERA_ID_INSTR_ARG_KEY);
+
/**
* Returns {@code true} if this device only supports {@code LEGACY} mode operation in the
* Camera2 API for the given camera ID.
@@ -104,4 +112,19 @@
}
}
+ public static int[] deriveCameraIdsUnderTest() throws Exception {
+ int numberOfCameras = Camera.getNumberOfCameras();
+ int[] cameraIds;
+ if (mOverrideCameraId == null) {
+ cameraIds = IntStream.range(0, numberOfCameras).toArray();
+ } else {
+ int overrideCameraId = Integer.parseInt(mOverrideCameraId);
+ if (overrideCameraId >= 0 && overrideCameraId < numberOfCameras) {
+ cameraIds = new int[]{overrideCameraId};
+ } else {
+ cameraIds = new int[]{};
+ }
+ }
+ return cameraIds;
+ }
}
diff --git a/tests/contentcaptureservice/AndroidManifest.xml b/tests/contentcaptureservice/AndroidManifest.xml
index f0d262f..3844378 100644
--- a/tests/contentcaptureservice/AndroidManifest.xml
+++ b/tests/contentcaptureservice/AndroidManifest.xml
@@ -90,6 +90,18 @@
</intent-filter>
</activity>
+ <activity android:name=".DataSharingActivity"
+ android:label="DataSharing"
+ android:taskAffinity=".DataSharingActivity"
+ android:theme="@android:style/Theme.NoTitleBar">
+ <intent-filter>
+ <!-- This intent filter is not really needed by CTS, but it makes easier to launch
+ this app during CTS development... -->
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
<receiver android:name=".SelfDestructReceiver"
android:exported="true"
android:process="android.contentcapture.cts.outside"/>
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CtsContentCaptureService.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CtsContentCaptureService.java
index 840bd03..6f462a1 100644
--- a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CtsContentCaptureService.java
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CtsContentCaptureService.java
@@ -22,8 +22,11 @@
import static com.google.common.truth.Truth.assertWithMessage;
import android.content.ComponentName;
+import android.os.ParcelFileDescriptor;
import android.service.contentcapture.ActivityEvent;
import android.service.contentcapture.ContentCaptureService;
+import android.service.contentcapture.DataShareCallback;
+import android.service.contentcapture.DataShareReadAdapter;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
@@ -32,18 +35,23 @@
import android.view.contentcapture.ContentCaptureEvent;
import android.view.contentcapture.ContentCaptureSessionId;
import android.view.contentcapture.DataRemovalRequest;
+import android.view.contentcapture.DataShareRequest;
import android.view.contentcapture.ViewNode;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.InputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
// TODO(b/123540602): if we don't move this service to a separate package, we need to handle the
// onXXXX methods in a separate thread
@@ -58,6 +66,8 @@
public static final ComponentName CONTENT_CAPTURE_SERVICE_COMPONENT_NAME =
componentNameFor(CtsContentCaptureService.class);
+ private static final Executor sExecutor = Executors.newCachedThreadPool();
+
private static int sIdCounter;
private static ServiceWatcher sServiceWatcher;
@@ -125,6 +135,24 @@
*/
private boolean mIgnoreOrphanSessionEvents;
+ /**
+ * Whether the service should accept a data share session.
+ */
+ private boolean mDataShareShouldAccept = false;
+
+ /**
+ * Bytes that were shared during the content capture
+ */
+ byte[] mDataShared = new byte[20_000];
+
+ /**
+ * The fields below represent state of the content capture data sharing session.
+ */
+ boolean mDataShareSessionStarted = false;
+ boolean mDataShareSessionFinished = false;
+ boolean mDataShareSessionSucceeded = false;
+ int mDataShareSessionErrorCode = 0;
+
@NonNull
public static ServiceWatcher setServiceWatcher() {
if (sServiceWatcher != null) {
@@ -297,6 +325,43 @@
}
@Override
+ public void onDataShareRequest(DataShareRequest request, DataShareCallback callback) {
+ if (mDataShareShouldAccept) {
+ callback.onAccept(sExecutor, new DataShareReadAdapter() {
+ @Override
+ public void onStart(ParcelFileDescriptor fd) {
+ mDataShareSessionStarted = true;
+
+ int bytesReadTotal = 0;
+ try (InputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(fd)) {
+ while (true) {
+ int bytesRead = fis.read(mDataShared, bytesReadTotal,
+ mDataShared.length - bytesReadTotal);
+ if (bytesRead == -1) {
+ break;
+ }
+ bytesReadTotal += bytesRead;
+ }
+ mDataShareSessionFinished = true;
+ mDataShareSessionSucceeded = true;
+ } catch (IOException e) {
+ // fall through. dataShareSessionSucceeded will stay false.
+ }
+ }
+
+ @Override
+ public void onError(int errorCode) {
+ mDataShareSessionFinished = true;
+ mDataShareSessionErrorCode = errorCode;
+ }
+ });
+ } else {
+ callback.onReject();
+ mDataShareSessionStarted = mDataShareSessionFinished = true;
+ }
+ }
+
+ @Override
public void onActivityEvent(ActivityEvent event) {
Log.i(TAG, "onActivityEvent(): " + event);
mActivityEvents.add(new MyActivityEvent(event));
@@ -361,6 +426,10 @@
return mOnDisconnectListener;
}
+ public void setDataSharingEnabled(boolean enabled) {
+ this.mDataShareShouldAccept = enabled;
+ }
+
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
super.dump(fd, pw, args);
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/DataSharingActivity.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/DataSharingActivity.java
new file mode 100644
index 0000000..1964abf
--- /dev/null
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/DataSharingActivity.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.contentcaptureservice.cts;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.LocusId;
+import android.contentcaptureservice.cts.CtsContentCaptureService.Session;
+import android.os.ParcelFileDescriptor;
+import android.view.contentcapture.ContentCaptureManager;
+import android.view.contentcapture.DataShareRequest;
+import android.view.contentcapture.DataShareWriteAdapter;
+
+import androidx.annotation.NonNull;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Random;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+
+public class DataSharingActivity extends AbstractContentCaptureActivity {
+
+ private static final Executor sExecutor = Executors.newCachedThreadPool();
+ private static final Random sRandom = new Random();
+
+ boolean mSessionFinished = false;
+ boolean mSessionSucceeded = false;
+ int mSessionErrorCode = 0;
+ boolean mSessionRejected = false;
+ byte[] mDataWritten = new byte[10_000];
+
+ @Override
+ public void onStart() {
+ super.onStart();
+
+ ContentCaptureManager manager =
+ getApplicationContext().getSystemService(ContentCaptureManager.class);
+
+ assertThat(manager).isNotNull();
+ assertThat(manager.isContentCaptureEnabled()).isTrue();
+
+ manager.shareData(
+ new DataShareRequest(new LocusId("cts"), "application/octet-stream"),
+ sExecutor, new DataShareWriteAdapter() {
+ @Override
+ public void onWrite(ParcelFileDescriptor destination) {
+ sRandom.nextBytes(mDataWritten);
+
+ try (OutputStream outputStream =
+ new ParcelFileDescriptor.AutoCloseOutputStream(destination)) {
+ outputStream.write(mDataWritten);
+ } catch (IOException e) {
+ // fall through, sessionSucceeded will stay false.
+ }
+
+ mSessionSucceeded = true;
+ mSessionFinished = true;
+ }
+
+ @Override
+ public void onRejected() {
+ mSessionRejected = true;
+ mSessionSucceeded = false;
+ mSessionFinished = true;
+ }
+
+ @Override
+ public void onError(int errorCode) {
+ mSessionErrorCode = errorCode;
+ mSessionSucceeded = false;
+ mSessionFinished = true;
+ }
+ });
+ }
+
+ @Override
+ public void assertDefaultEvents(@NonNull Session session) {
+ // Do nothing - this test operates with file sharing.
+ }
+}
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/DataSharingActivityTest.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/DataSharingActivityTest.java
new file mode 100644
index 0000000..4a2d4be
--- /dev/null
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/DataSharingActivityTest.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.contentcaptureservice.cts;
+
+import static com.android.compatibility.common.util.ActivitiesWatcher.ActivityLifecycle.RESUMED;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.platform.test.annotations.AppModeFull;
+
+import androidx.test.rule.ActivityTestRule;
+
+import com.android.compatibility.common.util.ActivitiesWatcher;
+import com.android.compatibility.common.util.PollingCheck;
+
+import org.junit.Test;
+
+import java.util.Arrays;
+
+@AppModeFull(reason = "BlankWithTitleActivityTest is enough")
+public class DataSharingActivityTest
+ extends AbstractContentCaptureIntegrationAutoActivityLaunchTest<DataSharingActivity> {
+
+ private static final String TAG = DataSharingActivityTest.class.getSimpleName();
+
+ private static final ActivityTestRule<DataSharingActivity> sActivityRule =
+ new ActivityTestRule<>(DataSharingActivity.class, false, false);
+
+ public DataSharingActivityTest() {
+ super(DataSharingActivity.class);
+ }
+
+ @Override
+ protected ActivityTestRule<DataSharingActivity> getActivityTestRule() {
+ return sActivityRule;
+ }
+
+ @Test
+ public void testHappyPath_dataCopiedSuccessfully() throws Exception {
+ final CtsContentCaptureService service = enableService();
+ final ActivitiesWatcher.ActivityWatcher watcher = startWatcher();
+
+ service.setDataSharingEnabled(true);
+
+ DataSharingActivity activity = launchActivity();
+ watcher.waitFor(RESUMED);
+
+ PollingCheck.waitFor(() -> activity.mSessionFinished);
+ PollingCheck.waitFor(() -> service.mDataShareSessionFinished);
+
+ assertThat(activity.mSessionSucceeded).isTrue();
+ assertThat(service.mDataShareSessionSucceeded).isTrue();
+
+ assertThat(activity.mDataWritten).isEqualTo(
+ Arrays.copyOfRange(service.mDataShared, 0, activity.mDataWritten.length));
+ }
+
+ @Test
+ public void testDataSharingSessionIsRejected_propagatedToClient() throws Exception {
+ final CtsContentCaptureService service = enableService();
+ final ActivitiesWatcher.ActivityWatcher watcher = startWatcher();
+
+ service.setDataSharingEnabled(false);
+
+ DataSharingActivity activity = launchActivity();
+ watcher.waitFor(RESUMED);
+
+ PollingCheck.waitFor(() -> activity.mSessionFinished);
+ PollingCheck.waitFor(() -> service.mDataShareSessionFinished);
+
+ assertThat(activity.mSessionSucceeded).isFalse();
+ assertThat(service.mDataShareSessionSucceeded).isFalse();
+
+ assertThat(activity.mSessionRejected).isTrue();
+ }
+}
diff --git a/tests/framework/base/windowmanager/AndroidManifest.xml b/tests/framework/base/windowmanager/AndroidManifest.xml
index 0f8da29..c95ae9c 100644
--- a/tests/framework/base/windowmanager/AndroidManifest.xml
+++ b/tests/framework/base/windowmanager/AndroidManifest.xml
@@ -27,7 +27,7 @@
<uses-permission android:name="android.permission.STOP_APP_SWITCHES" />
<uses-permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<application android:label="CtsWindowManagerDeviceTestCases"
android:requestLegacyExternalStorage="true">
diff --git a/tests/framework/base/windowmanager/app/AndroidManifest.xml b/tests/framework/base/windowmanager/app/AndroidManifest.xml
index 147fb8d..f6113bf 100755
--- a/tests/framework/base/windowmanager/app/AndroidManifest.xml
+++ b/tests/framework/base/windowmanager/app/AndroidManifest.xml
@@ -551,6 +551,9 @@
android:name=".PopupMinimalPostProcessingActivity"
android:theme="@android:style/Theme.Holo.Dialog.NoActionBar"
android:exported="true" />
+ <activity
+ android:name=".PresentationActivity"
+ android:exported="true" />
</application>
</manifest>
diff --git a/tests/framework/base/windowmanager/app/src/android/server/wm/app/BroadcastReceiverActivity.java b/tests/framework/base/windowmanager/app/src/android/server/wm/app/BroadcastReceiverActivity.java
index b3ab326..26c5b3a 100644
--- a/tests/framework/base/windowmanager/app/src/android/server/wm/app/BroadcastReceiverActivity.java
+++ b/tests/framework/base/windowmanager/app/src/android/server/wm/app/BroadcastReceiverActivity.java
@@ -41,6 +41,7 @@
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
+import android.view.WindowInsets;
import java.lang.ref.WeakReference;
@@ -73,14 +74,13 @@
view.setLayoutParams(new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
view.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
- view.setOnApplyWindowInsetsListener((v, insets) -> {
- final boolean cutoutExists = (insets.getDisplayCutout() != null);
- Log.i(TAG, "cutout=" + cutoutExists);
- TestJournalProvider.putExtras(BroadcastReceiverActivity.this,
- bundle -> bundle.putBoolean(EXTRA_CUTOUT_EXISTS, cutoutExists));
- return insets;
- });
setContentView(view);
+
+ WindowInsets insets = getWindowManager().getCurrentWindowMetrics().getWindowInsets();
+ final boolean cutoutExists = (insets.getDisplayCutout() != null);
+ Log.i(TAG, "cutout=" + cutoutExists);
+ TestJournalProvider.putExtras(BroadcastReceiverActivity.this,
+ bundle -> bundle.putBoolean(EXTRA_CUTOUT_EXISTS, cutoutExists));
}
@Override
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 75972b4..300398d 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
@@ -17,8 +17,8 @@
package android.server.wm.app;
import android.content.ComponentName;
-import android.server.wm.component.ComponentsBase;
import android.server.wm.TestJournalProvider;
+import android.server.wm.component.ComponentsBase;
public class Components extends ComponentsBase {
public static final ComponentName ALT_LAUNCHING_ACTIVITY = component("AltLaunchingActivity");
@@ -73,6 +73,7 @@
public static final ComponentName NO_HISTORY_ACTIVITY = component("NoHistoryActivity");
public static final ComponentName NO_RELAUNCH_ACTIVITY = component("NoRelaunchActivity");
public static final ComponentName NON_RESIZEABLE_ACTIVITY = component("NonResizeableActivity");
+ public static final ComponentName PRESENTATION_ACTIVITY = component("PresentationActivity");
public static final ComponentName PIP_ACTIVITY = component("PipActivity");
public static final ComponentName PIP_ACTIVITY2 = component("PipActivity2");
public static final ComponentName PIP_ACTIVITY_WITH_SAME_AFFINITY =
@@ -235,8 +236,10 @@
public static final String EXTRA_FIXED_ORIENTATION = "fixed_orientation";
public static final String EXTRA_CONFIGURATION = "configuration";
public static final String EXTRA_CONFIG_ASSETS_SEQ = "config_assets_seq";
+ public static final String EXTRA_INTENT = "intent";
public static final String EXTRA_INTENTS = "intents";
public static final String EXTRA_NO_IDLE = "no_idle";
+ public static final String COMMAND_NAVIGATE_UP_TO = "navigate_up_to";
public static final String COMMAND_START_ACTIVITIES = "start_activities";
}
@@ -468,6 +471,10 @@
public static final String ACTION_TOAST_TAP_DETECTED = "toast_tap_detected";
}
+ public static class PresentationActivity {
+ public static final String KEY_DISPLAY_ID = "display_id";
+ }
+
private static ComponentName component(String className) {
return component(Components.class, className);
}
diff --git a/tests/framework/base/windowmanager/app/src/android/server/wm/app/PresentationActivity.java b/tests/framework/base/windowmanager/app/src/android/server/wm/app/PresentationActivity.java
new file mode 100644
index 0000000..fc5490e
--- /dev/null
+++ b/tests/framework/base/windowmanager/app/src/android/server/wm/app/PresentationActivity.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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 android.app.Activity;
+import android.app.Presentation;
+import android.graphics.Color;
+import android.hardware.display.DisplayManager;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.Display;
+import android.view.Gravity;
+import android.view.WindowManager;
+import android.widget.TextView;
+
+public class PresentationActivity extends Activity {
+
+ private static final String TAG = PresentationActivity.class.getSimpleName();
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ int displayId = getIntent().getExtras().getInt(
+ Components.PresentationActivity.KEY_DISPLAY_ID);
+
+ Display presentationDisplay =
+ getSystemService(DisplayManager.class).getDisplay(displayId);
+
+ createPresentationWindow(presentationDisplay);
+ }
+
+ private void createPresentationWindow(Display display) {
+ final TextView view = new TextView(this);
+ view.setText("I'm a presentation");
+ view.setGravity(Gravity.CENTER);
+ view.setBackgroundColor(Color.RED);
+
+ final Presentation presentation = new Presentation(this, display);
+ presentation.setContentView(view);
+ presentation.setTitle(getPackageName());
+ try {
+ presentation.show();
+ } catch (WindowManager.InvalidDisplayException e) {
+ Log.w(TAG, "Presentation blocked", e);
+ }
+ }
+}
diff --git a/tests/framework/base/windowmanager/app_base/src/android/server/wm/app/TestActivity.java b/tests/framework/base/windowmanager/app_base/src/android/server/wm/app/TestActivity.java
index 2afd545..5bb4fd1 100644
--- a/tests/framework/base/windowmanager/app_base/src/android/server/wm/app/TestActivity.java
+++ b/tests/framework/base/windowmanager/app_base/src/android/server/wm/app/TestActivity.java
@@ -16,9 +16,11 @@
package android.server.wm.app;
+import static android.server.wm.app.Components.TestActivity.COMMAND_NAVIGATE_UP_TO;
import static android.server.wm.app.Components.TestActivity.EXTRA_CONFIG_ASSETS_SEQ;
import static android.server.wm.app.Components.TestActivity.EXTRA_FIXED_ORIENTATION;
import static android.server.wm.app.Components.TestActivity.EXTRA_INTENTS;
+import static android.server.wm.app.Components.TestActivity.EXTRA_INTENT;
import static android.server.wm.app.Components.TestActivity.EXTRA_NO_IDLE;
import static android.server.wm.app.Components.TestActivity.TEST_ACTIVITY_ACTION_FINISH_SELF;
import static android.server.wm.app.Components.TestActivity.COMMAND_START_ACTIVITIES;
@@ -109,6 +111,9 @@
final Parcelable[] intents = data.getParcelableArray(EXTRA_INTENTS);
startActivities(Arrays.copyOf(intents, intents.length, Intent[].class));
break;
+ case COMMAND_NAVIGATE_UP_TO:
+ navigateUpTo(data.getParcelable(EXTRA_INTENT));
+ break;
default:
super.handleCommand(command, data);
}
diff --git a/tests/framework/base/windowmanager/intent_tests/forResult/single-instance_for-result.json b/tests/framework/base/windowmanager/intent_tests/forResult/single-instance_for-result.json
new file mode 100644
index 0000000..78fe375
--- /dev/null
+++ b/tests/framework/base/windowmanager/intent_tests/forResult/single-instance_for-result.json
@@ -0,0 +1,66 @@
+{
+ "setup": {
+ "initialIntents": [
+ {
+ "flags": "FLAG_ACTIVITY_NEW_TASK",
+ "class": "android.server.wm.intent.Activities$TaskAffinity1Activity",
+ "package": "android.server.wm.cts",
+ "startForResult": false
+ }
+ ],
+ "act": [
+ {
+ "flags": "",
+ "class": "android.server.wm.intent.Activities$SingleInstanceActivity",
+ "package": "android.server.wm.cts",
+ "startForResult": true
+ }
+ ]
+ },
+ "initialState": {
+ "stacks": [
+ {
+ "tasks": [
+ {
+ "activities": [
+ {
+ "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity",
+ "state": "RESUMED"
+ }
+ ]
+ }
+ ],
+ "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity"
+ }
+ ]
+ },
+ "endState": {
+ "stacks": [
+ {
+ "tasks": [
+ {
+ "activities": [
+ {
+ "name": "android.server.wm.cts\/android.server.wm.intent.Activities$SingleInstanceActivity",
+ "state": "RESUMED"
+ }
+ ]
+ }
+ ],
+ "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$SingleInstanceActivity"
+ },
+ {
+ "tasks": [
+ {
+ "activities": [
+ {
+ "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity",
+ "state": "STOPPED"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+}
diff --git a/tests/framework/base/windowmanager/intent_tests/forResult/single-top_for-result.json b/tests/framework/base/windowmanager/intent_tests/forResult/single-top_for-result.json
new file mode 100644
index 0000000..a288a9e
--- /dev/null
+++ b/tests/framework/base/windowmanager/intent_tests/forResult/single-top_for-result.json
@@ -0,0 +1,54 @@
+{
+ "setup": {
+ "initialIntents": [
+ {
+ "flags": "FLAG_ACTIVITY_NEW_TASK",
+ "class": "android.server.wm.intent.Activities$SingleTopActivity",
+ "package": "android.server.wm.cts",
+ "startForResult": false
+ }
+ ],
+ "act": [
+ {
+ "flags": "",
+ "class": "android.server.wm.intent.Activities$SingleTopActivity",
+ "package": "android.server.wm.cts",
+ "startForResult": true
+ }
+ ]
+ },
+ "initialState": {
+ "stacks": [
+ {
+ "tasks": [
+ {
+ "activities": [
+ {
+ "name": "android.server.wm.cts\/android.server.wm.intent.Activities$SingleTopActivity",
+ "state": "RESUMED"
+ }
+ ]
+ }
+ ],
+ "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$SingleTopActivity"
+ }
+ ]
+ },
+ "endState": {
+ "stacks": [
+ {
+ "tasks": [
+ {
+ "activities": [
+ {
+ "name": "android.server.wm.cts\/android.server.wm.intent.Activities$SingleTopActivity",
+ "state": "RESUMED"
+ }
+ ]
+ }
+ ],
+ "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$SingleTopActivity"
+ }
+ ]
+ }
+}
diff --git a/tests/framework/base/windowmanager/intent_tests/forResult/test-2.json b/tests/framework/base/windowmanager/intent_tests/forResult/test-2.json
index 6e97377..a80e56b 100644
--- a/tests/framework/base/windowmanager/intent_tests/forResult/test-2.json
+++ b/tests/framework/base/windowmanager/intent_tests/forResult/test-2.json
@@ -43,10 +43,6 @@
{
"name": "android.server.wm.cts\/android.server.wm.intent.Activities$RegularActivity",
"state": "RESUMED"
- },
- {
- "name": "android.server.wm.cts\/android.server.wm.intent.Activities$RegularActivity",
- "state": "STOPPED"
}
]
}
diff --git a/tests/framework/base/windowmanager/intent_tests/forResult/test-4.json b/tests/framework/base/windowmanager/intent_tests/forResult/test-4.json
index 2ed6023..a288a9e 100644
--- a/tests/framework/base/windowmanager/intent_tests/forResult/test-4.json
+++ b/tests/framework/base/windowmanager/intent_tests/forResult/test-4.json
@@ -43,10 +43,6 @@
{
"name": "android.server.wm.cts\/android.server.wm.intent.Activities$SingleTopActivity",
"state": "RESUMED"
- },
- {
- "name": "android.server.wm.cts\/android.server.wm.intent.Activities$SingleTopActivity",
- "state": "STOPPED"
}
]
}
diff --git a/tests/framework/base/windowmanager/intent_tests/newTask/test-15.json b/tests/framework/base/windowmanager/intent_tests/newTask/test-15.json
index 33419d6..be49412 100644
--- a/tests/framework/base/windowmanager/intent_tests/newTask/test-15.json
+++ b/tests/framework/base/windowmanager/intent_tests/newTask/test-15.json
@@ -43,15 +43,23 @@
{
"name": "android.server.wm.cts\/android.server.wm.intent.Activities$RegularActivity",
"state": "RESUMED"
- },
+ }
+ ]
+ }
+ ],
+ "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$RegularActivity"
+ },
+ {
+ "tasks": [
+ {
+ "activities": [
{
"name": "android.server.wm.cts\/android.server.wm.intent.Activities$SingleInstanceActivity",
"state": "STOPPED"
}
]
}
- ],
- "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$RegularActivity"
+ ]
}
]
}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/ActivityTaskAffinityTests.java b/tests/framework/base/windowmanager/src/android/server/wm/ActivityTaskAffinityTests.java
index 3043ef3..c6baec7 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/ActivityTaskAffinityTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/ActivityTaskAffinityTests.java
@@ -33,7 +33,7 @@
/**
* Build/Install/Run:
- * atest CtsWindowManagerDeviceTestCases:ActivitySecurityTests
+ * atest CtsWindowManagerDeviceTestCases:ActivityTaskAffinityTests
*
* Tests if activities with the same taskAffinity can be in the same task.
*/
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/AmProfileTests.java b/tests/framework/base/windowmanager/src/android/server/wm/AmProfileTests.java
index 1a74ceb..a2c8a0e 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/AmProfileTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/AmProfileTests.java
@@ -30,6 +30,7 @@
import android.content.Intent;
import android.platform.test.annotations.Presubmit;
import android.server.wm.CommandSession.ActivitySession;
+import android.server.wm.CommandSession.DefaultLaunchProxy;
import android.server.wm.CommandSession.LaunchInjector;
import androidx.test.filters.FlakyTest;
@@ -147,8 +148,7 @@
/** Starts the activity with profiler. */
private ActivitySession startActivityProfiling(ComponentName activityName, boolean sampling,
boolean streaming) {
- return createManagedActivityClientSession().startActivity(new CommandSession.LaunchProxy() {
- LaunchInjector mLaunchInjector;
+ return createManagedActivityClientSession().startActivity(new DefaultLaunchProxy() {
@Override
public boolean shouldWaitForLaunched() {
@@ -157,11 +157,6 @@
}
@Override
- public void setLaunchInjector(LaunchInjector injector) {
- mLaunchInjector = injector;
- }
-
- @Override
public void execute() {
final StringBuilder builder = new StringBuilder();
builder.append(String.format("am start -n %s -W -S --start-profiler %s",
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/ConfigChangeTests.java b/tests/framework/base/windowmanager/src/android/server/wm/ConfigChangeTests.java
index f87fad9..edc6f9d 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/ConfigChangeTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/ConfigChangeTests.java
@@ -16,9 +16,9 @@
package android.server.wm;
-import static android.server.wm.WindowManagerState.STATE_RESUMED;
import static android.server.wm.StateLogger.log;
import static android.server.wm.StateLogger.logE;
+import static android.server.wm.WindowManagerState.STATE_RESUMED;
import static android.server.wm.app.Components.FONT_SCALE_ACTIVITY;
import static android.server.wm.app.Components.FONT_SCALE_NO_RELAUNCH_ACTIVITY;
import static android.server.wm.app.Components.FontScaleActivity.EXTRA_FONT_ACTIVITY_DPI;
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 983cb54..369904f 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/DisplayTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/DisplayTests.java
@@ -140,7 +140,7 @@
launchHomeActivity();
final DisplayMetricsSession displayMetricsSession =
- mObjectTracker.manage(new DisplayMetricsSession(DEFAULT_DISPLAY));
+ createManagedDisplayMetricsSession(DEFAULT_DISPLAY);
final LockScreenSession lockScreenSession = createManagedLockScreenSession();
// Read initial sizes.
@@ -189,32 +189,4 @@
return config;
}
}
-
- private static class DisplayMetricsSession implements AutoCloseable {
-
- private final ReportedDisplayMetrics mInitialDisplayMetrics;
- private final int mDisplayId;
-
- DisplayMetricsSession(int displayId) {
- mDisplayId = displayId;
- mInitialDisplayMetrics = ReportedDisplayMetrics.getDisplayMetrics(mDisplayId);
- }
-
- ReportedDisplayMetrics getInitialDisplayMetrics() {
- return mInitialDisplayMetrics;
- }
-
- ReportedDisplayMetrics getDisplayMetrics() {
- return ReportedDisplayMetrics.getDisplayMetrics(mDisplayId);
- }
-
- void overrideDisplayMetrics(final Size size, final int density) {
- mInitialDisplayMetrics.setDisplayMetrics(size, density);
- }
-
- @Override
- public void close() {
- mInitialDisplayMetrics.restoreDisplayMetrics();
- }
- }
}
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 43ddaee..de0372f 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayActivityLaunchTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayActivityLaunchTests.java
@@ -275,7 +275,7 @@
nonResizeableSession.takeCallbackHistory();
// Try to move the non-resizeable activity to the top of stack on secondary display.
- moveActivityToStack(NON_RESIZEABLE_ACTIVITY, externalFrontStackId);
+ moveActivityToStackOrOnTop(NON_RESIZEABLE_ACTIVITY, externalFrontStackId);
// Wait for a while to check that it will move.
assertTrue("Non-resizeable activity should be moved",
mWmState.waitForWithAmState(
@@ -653,10 +653,10 @@
assertEquals("Activity launched on secondary display must be resumed",
getActivityName(LAUNCHING_ACTIVITY), secondFrontStack.mResumedActivity);
mWmState.assertFocusedStack("Top stack must be on primary display", frontStackId);
- assertEquals("Top stack must only contain 1 task",
- 1, secondFrontStack.getTasks().size());
- assertEquals("Top task must only contain 1 activity",
- 1, secondFrontStack.getTasks().get(0).mActivities.size());
+ assertEquals("Second display must only contain 1 root task", 1,
+ mWmState.getDisplay(newDisplay.mId).getRootTasks().size());
+ assertEquals("Top task must only contain 1 activity", 1,
+ secondFrontStack.getActivities().size());
}
/**
@@ -719,10 +719,10 @@
getActivityName(ALT_LAUNCHING_ACTIVITY),
secondFrontStack.mResumedActivity);
mWmState.assertFocusedStack("Top stack must be on secondary display", secondFrontStackId);
- assertEquals("Top stack must only contain 1 task",
- 1, secondFrontStack.getTasks().size());
+ assertEquals("Second display must only contain 1 task",
+ 1, mWmState.getDisplay(newDisplay.mId).getRootTasks().size());
assertEquals("Top stack task must contain 2 activities",
- 2, secondFrontStack.getTasks().get(0).mActivities.size());
+ 2, secondFrontStack.getActivities().size());
}
/**
@@ -848,6 +848,7 @@
getPendingIntentActivity(TEST_ACTIVITY).send(mContext, resultCode, null /* intent */,
null /* onFinished */, null /* handler */, null /* requiredPermission */,
options.toBundle());
+ waitAndAssertActivityState(TOP_ACTIVITY, STATE_STOPPED, "Activity should be stopped");
waitAndAssertTopResumedActivity(TEST_ACTIVITY, displayContent.mId,
"Activity launched on secondary display and on top");
}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayKeyguardTests.java b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayKeyguardTests.java
index b45d621..e1df6d0 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayKeyguardTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayKeyguardTests.java
@@ -18,6 +18,7 @@
import static android.server.wm.UiDeviceUtils.pressBackButton;
import static android.server.wm.app.Components.DISMISS_KEYGUARD_ACTIVITY;
+import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
import static org.junit.Assert.assertFalse;
@@ -26,6 +27,7 @@
import android.platform.test.annotations.Presubmit;
import android.server.wm.WindowManagerState.DisplayContent;
+import android.util.Size;
import org.junit.Before;
import org.junit.Test;
@@ -70,7 +72,6 @@
/**
* Tests keyguard dialog shows on secondary display.
- * @throws Exception
*/
@Test
public void testShowKeyguardDialogOnSecondaryDisplay() {
@@ -93,8 +94,77 @@
}
/**
+ * Tests keyguard dialog should exist after secondary display changed.
+ */
+ @Test
+ public void testShowKeyguardDialogSecondaryDisplayChange() {
+ final LockScreenSession lockScreenSession = createManagedLockScreenSession();
+ final VirtualDisplaySession virtualDisplaySession = createManagedVirtualDisplaySession();
+
+ final DisplayContent publicDisplay = virtualDisplaySession
+ .setPublicDisplay(true)
+ .createDisplay();
+
+ lockScreenSession.gotoKeyguard();
+ assertTrue("KeyguardDialog must show on external public display",
+ mWmState.waitForWithAmState(
+ state -> isKeyguardOnDisplay(state, publicDisplay.mId),
+ "keyguard window to show"));
+
+ // By default, a Presentation object should be dismissed if the DisplayMetrics changed.
+ // But this rule should not apply to KeyguardPresentation.
+ virtualDisplaySession.resizeDisplay();
+ mWmState.computeState();
+ assertTrue("KeyguardDialog must show on external public display even display changed",
+ mWmState.waitForWithAmState(
+ state -> isKeyguardOnDisplay(state, publicDisplay.mId),
+ "keyguard window to show"));
+ }
+
+ /**
+ * Tests keyguard dialog should exist after default display changed.
+ */
+ @Test
+ public void testShowKeyguardDialogDefaultDisplayChange() {
+ final LockScreenSession lockScreenSession = createManagedLockScreenSession();
+ final VirtualDisplaySession virtualDisplaySession = createManagedVirtualDisplaySession();
+ final DisplayMetricsSession displayMetricsSession =
+ createManagedDisplayMetricsSession(DEFAULT_DISPLAY);
+
+ // Use simulate display instead of virtual display, because VirtualDisplayActivity will
+ // relaunch after configuration change.
+ final DisplayContent publicDisplay = virtualDisplaySession
+ .setSimulateDisplay(true)
+ .createDisplay();
+
+ lockScreenSession.gotoKeyguard();
+ assertTrue("KeyguardDialog must show on external public display",
+ mWmState.waitForWithAmState(
+ state -> isKeyguardOnDisplay(state, publicDisplay.mId),
+ "keyguard window to show"));
+
+ // Unlock then lock again, to ensure the display metrics has updated.
+ lockScreenSession.wakeUpDevice().unlockDevice();
+ // Overriding the display metrics on the default display should not affect Keyguard to show
+ // on secondary display.
+ final ReportedDisplayMetrics originalDisplayMetrics =
+ displayMetricsSession.getInitialDisplayMetrics();
+ final Size overrideSize = new Size(
+ (int) (originalDisplayMetrics.physicalSize.getWidth() * 1.5),
+ (int) (originalDisplayMetrics.physicalSize.getHeight() * 1.5));
+ final Integer overrideDensity = (int) (originalDisplayMetrics.physicalDensity * 1.1);
+ displayMetricsSession.overrideDisplayMetrics(overrideSize, overrideDensity);
+
+ lockScreenSession.gotoKeyguard();
+ assertTrue("KeyguardDialog must show on external public display",
+ mWmState.waitForWithAmState(
+ state -> isKeyguardOnDisplay(state, publicDisplay.mId),
+ "keyguard window to show"));
+
+ }
+
+ /**
* Tests keyguard dialog cannot be shown on private display.
- * @throws Exception
*/
@Test
public void testNoKeyguardDialogOnPrivateDisplay() {
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 5ba2c63..da874ab 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayPolicyTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayPolicyTests.java
@@ -389,7 +389,7 @@
pair(newDisplay.mId, TEST_ACTIVITY));
// Move activity from secondary display to primary.
- moveActivityToStack(TEST_ACTIVITY, defaultDisplayStackId);
+ moveActivityToStackOrOnTop(TEST_ACTIVITY, defaultDisplayStackId);
waitAndAssertTopResumedActivity(TEST_ACTIVITY, DEFAULT_DISPLAY,
"Moved activity must be on top");
}
@@ -734,8 +734,7 @@
private void assertTopTaskSameSurfaceSizeWithDisplay(int displayId) {
final DisplayContent display = mWmState.getDisplay(displayId);
final int stackId = mWmState.getFrontRootTaskId(displayId);
- final ActivityTask task =
- mWmState.getRootTask(stackId).mTasks.get(0);
+ final ActivityTask task = mWmState.getRootTask(stackId).getTopTask();
assertEquals("Task must have same surface width with its display",
display.getSurfaceSize(), task.getSurfaceWidth());
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 1e2e8c3..c48d80a 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplaySecurityTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplaySecurityTests.java
@@ -555,7 +555,8 @@
waitAndAssertTopResumedActivity(SECOND_ACTIVITY, newDisplay.mId,
"Top activity must be the newly launched one");
frontStack = mWmState.getRootTask(frontStackId);
- assertEquals("Secondary display must contain 1 task", 1, frontStack.getTasks().size());
+ assertEquals("Secondary display must contain 1 task", 1,
+ mWmState.getDisplay(newDisplay.mId).getRootTasks().size());
}
/**
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 8ff9745..f8f714c 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayTestBase.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayTestBase.java
@@ -56,6 +56,7 @@
import android.server.wm.CommandSession.ActivitySession;
import android.server.wm.CommandSession.ActivitySessionClient;
import android.server.wm.settings.SettingsSession;
+import android.util.DisplayMetrics;
import android.util.Pair;
import android.util.Size;
import android.view.WindowManager;
@@ -168,10 +169,12 @@
@Nullable
final Integer overrideDensity;
+ final int mDisplayId;
+
/** Get physical and override display metrics from WM for specified display. */
public static ReportedDisplayMetrics getDisplayMetrics(int displayId) {
return new ReportedDisplayMetrics(executeShellCommand(WM_SIZE + " -d " + displayId)
- + executeShellCommand(WM_DENSITY + " -d " + displayId));
+ + executeShellCommand(WM_DENSITY + " -d " + displayId), displayId);
}
void setDisplayMetrics(final Size size, final int density) {
@@ -193,11 +196,12 @@
}
private void setSize(final Size size) {
- executeShellCommand(WM_SIZE + " " + size.getWidth() + "x" + size.getHeight());
+ executeShellCommand(
+ WM_SIZE + " " + size.getWidth() + "x" + size.getHeight() + " -d " + mDisplayId);
}
private void setDensity(final int density) {
- executeShellCommand(WM_DENSITY + " " + density);
+ executeShellCommand(WM_DENSITY + " " + density + " -d " + mDisplayId);
}
/** Get display size that WM operates with. */
@@ -210,7 +214,8 @@
return overrideDensity != null ? overrideDensity : physicalDensity;
}
- private ReportedDisplayMetrics(final String lines) {
+ private ReportedDisplayMetrics(final String lines, int displayId) {
+ mDisplayId = displayId;
Matcher matcher = PHYSICAL_SIZE.matcher(lines);
assertTrue("Physical display size must be reported", matcher.find());
log(matcher.group());
@@ -241,6 +246,48 @@
}
}
+ public static class DisplayMetricsSession implements AutoCloseable {
+ private final ReportedDisplayMetrics mInitialDisplayMetrics;
+ private final int mDisplayId;
+
+ DisplayMetricsSession(int displayId) {
+ mDisplayId = displayId;
+ mInitialDisplayMetrics = ReportedDisplayMetrics.getDisplayMetrics(mDisplayId);
+ }
+
+ ReportedDisplayMetrics getInitialDisplayMetrics() {
+ return mInitialDisplayMetrics;
+ }
+
+ ReportedDisplayMetrics getDisplayMetrics() {
+ return ReportedDisplayMetrics.getDisplayMetrics(mDisplayId);
+ }
+
+ void changeDisplayMetrics(double sizeRatio, double densityRatio) {
+ final Size originalSize = mInitialDisplayMetrics.physicalSize;
+ final int density = mInitialDisplayMetrics.physicalDensity;
+
+ final Size overrideSize = new Size((int)(originalSize.getWidth() * sizeRatio),
+ (int)(originalSize.getHeight() * sizeRatio));
+ final int overrideDensity = (int)(density * densityRatio);
+ overrideDisplayMetrics(overrideSize, overrideDensity);
+ }
+
+ void overrideDisplayMetrics(final Size size, final int density) {
+ mInitialDisplayMetrics.setDisplayMetrics(size, density);
+ }
+
+ @Override
+ public void close() {
+ mInitialDisplayMetrics.restoreDisplayMetrics();
+ }
+ }
+
+ /** @see ObjectTracker#manage(AutoCloseable) */
+ protected DisplayMetricsSession createManagedDisplayMetricsSession(int displayId) {
+ return mObjectTracker.manage(new DisplayMetricsSession(displayId));
+ }
+
void waitForDisplayGone(Predicate<DisplayContent> displayPredicate) {
waitForOrFail("displays to be removed", () -> {
mWmState.computeState();
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/PinnedStackTests.java b/tests/framework/base/windowmanager/src/android/server/wm/PinnedStackTests.java
index debc7ef..998f553 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/PinnedStackTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/PinnedStackTests.java
@@ -574,7 +574,7 @@
launchActivity(PIP_ACTIVITY2, EXTRA_ENTER_PIP, "true");
final ActivityTask pinnedStack = getPinnedStack();
- assertEquals(1, pinnedStack.getTasks().size());
+ assertEquals(0, pinnedStack.getTasks().size());
assertTrue(mWmState.containsActivityInWindowingMode(
PIP_ACTIVITY2, WINDOWING_MODE_PINNED));
assertTrue(mWmState.containsActivityInWindowingMode(
@@ -1017,8 +1017,7 @@
launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true");
waitForEnterPip(PIP_ACTIVITY);
assertPinnedStackExists();
- ActivityTask stack = mWmState.getStandardStackByWindowingMode(
- WINDOWING_MODE_PINNED);
+ ActivityTask stack = mWmState.getStandardStackByWindowingMode(WINDOWING_MODE_PINNED);
int stackId = stack.mRootTaskId;
int taskId = stack.getTopTask().mTaskId;
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/PresentationTest.java b/tests/framework/base/windowmanager/src/android/server/wm/PresentationTest.java
new file mode 100644
index 0000000..77dc2c0
--- /dev/null
+++ b/tests/framework/base/windowmanager/src/android/server/wm/PresentationTest.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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 com.google.common.truth.Truth.assertThat;
+
+import android.content.Intent;
+import android.hardware.display.DisplayManager;
+import android.platform.test.annotations.Presubmit;
+import android.server.wm.app.Components;
+import android.view.Display;
+
+import org.junit.Test;
+
+import java.util.List;
+
+@Presubmit
+public class PresentationTest extends MultiDisplayTestBase {
+
+ // WindowManager.LayoutParams.TYPE_PRESENTATION
+ private static final int TYPE_PRESENTATION = 2037;
+
+ @Test
+ public void testPresentationFollowsDisplayFlag() {
+ DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
+ for (Display display : displayManager.getDisplays()) {
+ launchPresentationActivity(display.getDisplayId());
+ if ((display.getFlags() & Display.FLAG_PRESENTATION) != Display.FLAG_PRESENTATION) {
+ assertNoPresentationDisplayed();
+ } else {
+ assertPresentationOnDisplay(display.getDisplayId());
+ }
+ }
+ }
+
+ @Test
+ public void testPresentationAllowedOnPresentationDisplay() {
+ WindowManagerState.DisplayContent display =
+ createManagedVirtualDisplaySession()
+ .setPresentationDisplay(true)
+ .setPublicDisplay(true)
+ .createDisplay();
+
+ assertThat(display.getFlags() & Display.FLAG_PRESENTATION)
+ .isEqualTo(Display.FLAG_PRESENTATION);
+
+ launchPresentationActivity(display.mId);
+ assertPresentationOnDisplay(display.mId);
+ }
+
+ @Test
+ public void testPresentationBlockedOnNonPresentationDisplay() {
+ WindowManagerState.DisplayContent display =
+ createManagedVirtualDisplaySession()
+ .setPresentationDisplay(false)
+ .createDisplay();
+
+ assertThat(display.getFlags() & Display.FLAG_PRESENTATION).isEqualTo(0);
+ launchPresentationActivity(display.mId);
+ assertNoPresentationDisplayed();
+ }
+
+ private void assertNoPresentationDisplayed() {
+ final List<WindowManagerState.WindowState> presentationWindows =
+ mWmState.getWindowsByPackageName(
+ Components.PRESENTATION_ACTIVITY.getPackageName(), TYPE_PRESENTATION);
+ assertThat(presentationWindows).isEmpty();
+ }
+
+ private void assertPresentationOnDisplay(int displayId) {
+ final List<WindowManagerState.WindowState> presentationWindows =
+ mWmState.getWindowsByPackageName(
+ Components.PRESENTATION_ACTIVITY.getPackageName(), TYPE_PRESENTATION);
+ assertThat(presentationWindows).hasSize(1);
+ WindowManagerState.WindowState presentationWindowState = presentationWindows.get(0);
+ assertThat(presentationWindowState.getDisplayId()).isEqualTo(displayId);
+ }
+
+ private void launchPresentationActivity(int displayId) {
+ Intent intent = new Intent();
+ intent.setComponent(Components.PRESENTATION_ACTIVITY);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.putExtra(Components.PresentationActivity.KEY_DISPLAY_ID, displayId);
+ mContext.startActivity(intent);
+ waitAndAssertTopResumedActivity(
+ Components.PRESENTATION_ACTIVITY,
+ Display.DEFAULT_DISPLAY,
+ "Launched activity must be on top");
+ }
+}
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 3f9d440..0b711b0 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/StartActivityTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/StartActivityTests.java
@@ -22,11 +22,14 @@
import static android.server.wm.app.Components.NO_RELAUNCH_ACTIVITY;
import static android.server.wm.app.Components.TEST_ACTIVITY;
import static android.server.wm.app.Components.TRANSLUCENT_ACTIVITY;
+import static android.server.wm.app.Components.TestActivity.COMMAND_NAVIGATE_UP_TO;
+import static android.server.wm.app.Components.TestActivity.EXTRA_INTENT;
import static android.server.wm.app.Components.TestActivity.EXTRA_INTENTS;
import static android.server.wm.app.Components.TestActivity.COMMAND_START_ACTIVITIES;
import static android.server.wm.app27.Components.SDK_27_LAUNCHING_ACTIVITY;
import static android.server.wm.second.Components.SECOND_ACTIVITY;
+import static android.view.Display.DEFAULT_DISPLAY;
import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assert.assertEquals;
@@ -38,6 +41,7 @@
import android.os.Bundle;
import android.platform.test.annotations.Presubmit;
import android.server.wm.CommandSession.ActivitySession;
+import android.server.wm.intent.Activities;
import androidx.test.rule.ActivityTestRule;
@@ -144,6 +148,51 @@
}
/**
+ * Starts 3 activities A, B, C in the same task. A and B belong to current package and are not
+ * exported. C belongs to a different package with different uid. After C called
+ * {@link Activity#navigateUpTo(Intent)} with the intent of A, the activities B, C should be
+ * finished and instead of creating a new instance of A, the original A should become the top
+ * activity because the caller C in different uid cannot launch a non-exported activity.
+ */
+ @Test
+ public void testStartActivityByNavigateUpToFromDiffUid() {
+ final Intent intent1 = new Intent(mContext, Activities.RegularActivity.class);
+ final TestActivitySession<Activities.RegularActivity> activitySession1 =
+ createManagedTestActivitySession();
+ activitySession1.launchTestActivityOnDisplaySync(intent1, DEFAULT_DISPLAY);
+ final TestActivitySession<Activities.SingleTopActivity> activitySession2 =
+ createManagedTestActivitySession();
+ activitySession2.launchTestActivityOnDisplaySync(Activities.SingleTopActivity.class,
+ DEFAULT_DISPLAY);
+
+ final CommandSession.ActivitySession activitySession3 =
+ createManagedActivityClientSession().startActivity(
+ new CommandSession.DefaultLaunchProxy() {
+ @Override
+ public void execute() {
+ final Intent intent = new Intent().setComponent(TEST_ACTIVITY);
+ mLaunchInjector.setupIntent(intent);
+ activitySession2.getActivity().startActivity(intent);
+ }
+ });
+
+ final Bundle data = new Bundle();
+ data.putParcelable(EXTRA_INTENT, intent1);
+ activitySession3.sendCommand(COMMAND_NAVIGATE_UP_TO, data);
+
+ waitAndAssertTopResumedActivity(intent1.getComponent(), DEFAULT_DISPLAY,
+ "navigateUpTo should return to the first activity");
+ // Make sure the resumed first activity is the original instance.
+ assertFalse("The target of navigateUpTo should not be destroyed",
+ activitySession1.getActivity().isDestroyed());
+
+ // The activities above the first one should be destroyed.
+ mWmState.waitAndAssertActivityRemoved(
+ activitySession3.getOriginalLaunchIntent().getComponent());
+ mWmState.waitAndAssertActivityRemoved(activitySession2.getActivity().getComponentName());
+ }
+
+ /**
* Assume there are 3 activities (A1, A2, A3) with different task affinities and the same uid.
* After A1 called {@link Activity#startActivities} to start A2 (with NEW_TASK) and A3, the
* result should be 2 tasks: [A1] and [A2, A3].
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/WindowContextTests.java b/tests/framework/base/windowmanager/src/android/server/wm/WindowContextTests.java
new file mode 100644
index 0000000..e9bebce
--- /dev/null
+++ b/tests/framework/base/windowmanager/src/android/server/wm/WindowContextTests.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+
+import static org.junit.Assert.assertEquals;
+
+import android.app.Instrumentation;
+import android.content.Context;
+import android.hardware.display.DisplayManager;
+import android.platform.test.annotations.Presubmit;
+import android.util.DisplayMetrics;
+import android.view.Display;
+import android.view.View;
+import android.view.WindowManager;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.Test;
+
+@Presubmit
+public class WindowContextTests extends MultiDisplayTestBase {
+ private Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ private Context mContext = mInstrumentation.getTargetContext();
+ private DisplayManager mDisplayManager = mContext.getSystemService(DisplayManager.class);
+
+ @Test
+ public void testWindowContextConfigChanges() {
+ final WindowManagerState.DisplayContent display = createManagedVirtualDisplaySession()
+ .setSimulateDisplay(true).createDisplay();
+ final Context windowContext = createWindowContext(display.mId);
+ mInstrumentation.runOnMainSync(() -> {
+ final View view = new View(windowContext);
+ WindowManager wm = windowContext.getSystemService(WindowManager.class);
+ wm.addView(view, new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY));
+ });
+ mWmState.computeState();
+
+ final DisplayMetricsSession displayMetricsSession =
+ createManagedDisplayMetricsSession(display.mId);
+
+ displayMetricsSession.changeDisplayMetrics(1.2 /* sizeRatio */, 1.1 /* densityRatio */);
+
+ mWmState.computeState();
+
+ assertDisplayMetricsEquals(displayMetricsSession.getDisplayMetrics(),
+ windowContext.getResources().getDisplayMetrics());
+ }
+
+ private Context createWindowContext(int displayId) {
+ final Display display = mDisplayManager.getDisplay(displayId);
+ return mContext.createDisplayContext(display).createWindowContext(TYPE_APPLICATION_OVERLAY,
+ null /* options */);
+ }
+
+ private void assertDisplayMetricsEquals(ReportedDisplayMetrics expectedMetrics,
+ DisplayMetrics actualMetrics) {
+ assertEquals(expectedMetrics.getSize().getWidth(), actualMetrics.widthPixels);
+ assertEquals(expectedMetrics.getSize().getHeight(), actualMetrics.heightPixels);
+ assertEquals(expectedMetrics.getDensity(), actualMetrics.densityDpi);
+ }
+}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsControllerTests.java b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsControllerTests.java
index 4a4775f..60bc160 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsControllerTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsControllerTests.java
@@ -29,7 +29,7 @@
import android.view.View;
import android.view.WindowInsets;
import android.view.WindowInsets.Type;
-import android.view.WindowInsetsAnimationCallback;
+import android.view.WindowInsetsAnimation;
import com.android.compatibility.common.util.PollingCheck;
import com.android.compatibility.common.util.SystemUtil;
@@ -37,6 +37,8 @@
import org.junit.Ignore;
import org.junit.Test;
+import java.util.List;
+
/**
* Test whether WindowInsetsController controls window insets as expected.
*
@@ -199,22 +201,22 @@
() -> getInstrumentation().sendPointerSync(event));
}
- private static class AnimationCallback implements WindowInsetsAnimationCallback {
+ private static class AnimationCallback extends WindowInsetsAnimation.Callback {
private boolean mFinished = false;
- @Override
- public int getDispatchMode() {
- return DISPATCH_MODE_CONTINUE_ON_SUBTREE;
+ AnimationCallback() {
+ super(DISPATCH_MODE_CONTINUE_ON_SUBTREE);
}
@Override
- public WindowInsets onProgress(WindowInsets insets) {
+ public WindowInsets onProgress(WindowInsets insets,
+ List<WindowInsetsAnimation> runningAnimations) {
return insets;
}
@Override
- public void onFinish(InsetsAnimation animation) {
+ public void onEnd(WindowInsetsAnimation animation) {
synchronized (this) {
mFinished = true;
notify();
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/WindowMetricsTests.java b/tests/framework/base/windowmanager/src/android/server/wm/WindowMetricsTests.java
index 958279a..1b04e13 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/WindowMetricsTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/WindowMetricsTests.java
@@ -18,11 +18,16 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeFalse;
import android.app.Activity;
+import android.graphics.Insets;
+import android.graphics.Point;
import android.os.Bundle;
import android.platform.test.annotations.Presubmit;
import android.util.Size;
+import android.view.Display;
+import android.view.DisplayCutout;
import android.view.View;
import android.view.WindowInsets;
import android.view.WindowMetrics;
@@ -34,14 +39,23 @@
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
+/**
+ * Tests that verify the behavior of {@link WindowMetrics} and {@link android.app.WindowContext} API
+ *
+ * Build/Install/Run:
+ * atest CtsWindowManagerDeviceTestCases:WindowMetricsTests
+ */
@Presubmit
public class WindowMetricsTests extends WindowManagerTestBase {
- ActivityTestRule<MetricsActivity> mMetricsActivity =
+ private ActivityTestRule<MetricsActivity> mMetricsActivity =
new ActivityTestRule<>(MetricsActivity.class);
@Test
- public void test_metrics() {
+ public void testMetricsSanity() {
+ // TODO(b/149668895): handle device with cutout.
+ assumeFalse(hasDisplayCutout());
+
final MetricsActivity activity = mMetricsActivity.launchActivity(null);
activity.waitForLayout();
@@ -57,7 +71,58 @@
activity.mOnCreateCurrentMetrics.getWindowInsets().getStableInsets());
assertEquals(activity.mOnLayoutInsets.getDisplayCutout(),
activity.mOnCreateCurrentMetrics.getWindowInsets().getDisplayCutout());
+ }
+ @Test
+ public void testMetricsMatchesDisplay() {
+ final MetricsActivity activity = mMetricsActivity.launchActivity(null);
+ activity.waitForLayout();
+
+ final Display display = activity.getDisplay();
+
+ // Check window size
+ final Point displaySize = new Point();
+ display.getSize(displaySize);
+ final WindowMetrics windowMetrics = activity.getWindowManager().getCurrentWindowMetrics();
+ final Size size = getLegacySize(windowMetrics);
+ assertEquals("Reported display width must match window width",
+ displaySize.x, size.getWidth());
+ assertEquals("Reported display height must match window height",
+ displaySize.y, size.getHeight());
+
+ // Check max window size
+ final Point realDisplaySize = new Point();
+ display.getRealSize(realDisplaySize);
+ final WindowMetrics maxWindowMetrics = activity.getWindowManager()
+ .getMaximumWindowMetrics();
+ assertEquals("Reported real display width must match max window size",
+ realDisplaySize.x, maxWindowMetrics.getSize().getWidth());
+ assertEquals("Reported real display height must match max window size",
+ realDisplaySize.y, maxWindowMetrics.getSize().getHeight());
+ }
+
+ private static Size getLegacySize(WindowMetrics windowMetrics) {
+ WindowInsets windowInsets = windowMetrics.getWindowInsets();
+ final Insets insetsWithCutout = getProperInsetsWithCutout(windowInsets.getStableInsets(),
+ windowInsets.getDisplayCutout());
+ final int insetsInWidth = insetsWithCutout.left + insetsWithCutout.right;
+ final int insetsInHeight = insetsWithCutout.top + insetsWithCutout.bottom;
+
+ Size size = windowMetrics.getSize();
+ return new Size(size.getWidth() - insetsInWidth, size.getHeight() - insetsInHeight);
+ }
+
+ private static Insets getProperInsetsWithCutout(Insets stableInsets, DisplayCutout cutout) {
+ final Insets excludingStatusBar = Insets.of(stableInsets.left, 0,
+ stableInsets.right, stableInsets.bottom);
+ if (cutout == null) {
+ return excludingStatusBar;
+ } else {
+ final Insets unionInsetsWithCutout = Insets.max(excludingStatusBar,
+ Insets.of(cutout.getSafeInsetLeft(), cutout.getSafeInsetTop(),
+ cutout.getSafeInsetRight(), cutout.getSafeInsetBottom()));
+ return unionInsetsWithCutout;
+ }
}
public static class MetricsActivity extends Activity implements View.OnLayoutChangeListener {
@@ -78,7 +143,6 @@
getWindow().getDecorView().addOnLayoutChangeListener(this);
}
-
@Override
public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
int oldTop, int oldRight, int oldBottom) {
@@ -88,7 +152,7 @@
mLayoutLatch.countDown();
}
- public void waitForLayout() {
+ void waitForLayout() {
try {
assertTrue("timed out waiting for activity to layout",
mLayoutLatch.await(4, TimeUnit.SECONDS));
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/intent/Activities.java b/tests/framework/base/windowmanager/src/android/server/wm/intent/Activities.java
index 9ba003c..e4cb869 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/intent/Activities.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/intent/Activities.java
@@ -17,6 +17,7 @@
package android.server.wm.intent;
import android.app.Activity;
+import android.os.Bundle;
/**
* A collection of activities with various launch modes used in the intent tests.
@@ -27,60 +28,68 @@
*/
public class Activities {
- public static class TrackerActivity extends Activity {
+ private static class BaseActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setTitle(getClass().getSimpleName());
+ }
}
- public static class RegularActivity extends Activity {
+ public static class TrackerActivity extends BaseActivity {
}
- public static class SingleTopActivity extends Activity {
+ public static class RegularActivity extends BaseActivity {
}
- public static class SingleInstanceActivity extends Activity {
+ public static class SingleTopActivity extends BaseActivity {
}
- public static class SingleInstanceActivity2 extends Activity {
+ public static class SingleInstanceActivity extends BaseActivity {
}
- public static class SingleTaskActivity extends Activity {
+ public static class SingleInstanceActivity2 extends BaseActivity {
}
- public static class SingleTaskActivity2 extends Activity {
+ public static class SingleTaskActivity extends BaseActivity {
}
- public static class TaskAffinity1Activity extends Activity {
+ public static class SingleTaskActivity2 extends BaseActivity {
}
- public static class TaskAffinity1Activity2 extends Activity {
+ public static class TaskAffinity1Activity extends BaseActivity {
}
- public static class TaskAffinity2Activity extends Activity {
+ public static class TaskAffinity1Activity2 extends BaseActivity {
}
- public static class TaskAffinity3Activity extends Activity {
+ public static class TaskAffinity2Activity extends BaseActivity {
}
- public static class ClearTaskOnLaunchActivity extends Activity {
+ public static class TaskAffinity3Activity extends BaseActivity {
}
- public static class DocumentLaunchIntoActivity extends Activity {
+ public static class ClearTaskOnLaunchActivity extends BaseActivity {
}
- public static class DocumentLaunchAlwaysActivity extends Activity {
+ public static class DocumentLaunchIntoActivity extends BaseActivity {
}
- public static class DocumentLaunchNeverActivity extends Activity {
+ public static class DocumentLaunchAlwaysActivity extends BaseActivity {
}
- public static class NoHistoryActivity extends Activity {
+ public static class DocumentLaunchNeverActivity extends BaseActivity {
}
- public static class LauncherActivity extends Activity {
+ public static class NoHistoryActivity extends BaseActivity {
}
- public static class RelinquishTaskIdentityActivity extends Activity {
+ public static class LauncherActivity extends BaseActivity {
}
- public static class TaskAffinity1RelinquishTaskIdentityActivity extends Activity {
+ public static class RelinquishTaskIdentityActivity extends BaseActivity {
+ }
+
+ public static class TaskAffinity1RelinquishTaskIdentityActivity extends BaseActivity {
}
}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/intent/LaunchRunner.java b/tests/framework/base/windowmanager/src/android/server/wm/intent/LaunchRunner.java
index 9e1d67a..950047d 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/intent/LaunchRunner.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/intent/LaunchRunner.java
@@ -264,6 +264,11 @@
if (activity == null) {
return activityContext;
+ } else if (startForResult && activityContext == activity) {
+ // The result might have send back to caller activity and forced the caller activity
+ // to resumed again, before the started activity actually resumed. Just wait for idle
+ // for that case.
+ getInstrumentation().waitForIdleSync();
} else {
waitAndAssertActivityLaunched(activity, intent);
}
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 4c21f49..93ecabc 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
@@ -112,7 +112,7 @@
}
@Test
- public void testOccludingMovedBetweenStacks() throws Exception {
+ public void testOccludingOnSplitSecondaryStack() throws Exception {
// Launch first activity
final Activity firstActivity = launchActivityAndWait(FirstActivity.class);
@@ -140,21 +140,10 @@
.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK)
.launch();
waitAndAssertActivityStates(state(secondActivity, ON_STOP));
-
- // Move occluding third activity to side, it will occlude first now
- getLifecycleLog().clear();
- moveActivityToStack(getComponentName(ThirdActivity.class), primarySplitStack);
-
- waitAndAssertActivityStates(state(secondActivity, ON_RESUME),
- state(firstActivity, ON_STOP));
- LifecycleVerifier.assertEmptySequence(ThirdActivity.class, getLifecycleLog(), "moveToSide");
- LifecycleVerifier.assertRestartAndResumeSequence(SecondActivity.class, getLifecycleLog());
- LifecycleVerifier.assertSequence(FirstActivity.class, getLifecycleLog(),
- Arrays.asList(ON_PAUSE, ON_STOP), "moveToSide");
}
@Test
- public void testTranslucentMovedBetweenStacks() throws Exception {
+ public void testTranslucentOnSplitSecondaryStack() throws Exception {
// Launch first activity
final Activity firstActivity = launchActivityAndWait(FirstActivity.class);
@@ -183,17 +172,6 @@
.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK)
.launch();
waitAndAssertActivityStates(state(secondActivity, ON_PAUSE));
-
- // Move translucent activity to side, it will be on top of the first now
- getLifecycleLog().clear();
- moveActivityToStack(getComponentName(TranslucentActivity.class), primarySplitStack);
-
- waitAndAssertActivityStates(state(firstActivity, ON_PAUSE),
- state(secondActivity, ON_RESUME));
- LifecycleVerifier.assertSequence(FirstActivity.class, getLifecycleLog(),
- Arrays.asList(ON_PAUSE), "moveToSide");
- // Translucent activity can be either relaunched or preserved depending on whether the split
- // sizes match. Not verifying its lifecycle here.
}
@Test
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleTests.java b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleTests.java
index 3d31935..941b941 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleTests.java
@@ -157,7 +157,7 @@
// Move translucent activity into the stack with the first activity
getLifecycleLog().clear();
- moveActivityToStack(getComponentName(TranslucentActivity.class), firstActivityStack);
+ moveActivityToStackOrOnTop(getComponentName(TranslucentActivity.class), firstActivityStack);
// Wait for translucent activity to resume and first activity to pause
waitAndAssertActivityStates(state(translucentActivity, ON_RESUME),
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 b6fa201..b7352fe 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
@@ -60,7 +60,6 @@
import static android.server.wm.ActivityLauncher.KEY_USE_APPLICATION_CONTEXT;
import static android.server.wm.ActivityLauncher.KEY_WINDOWING_MODE;
import static android.server.wm.ActivityLauncher.launchActivityFromExtras;
-import static android.server.wm.WindowManagerState.STATE_RESUMED;
import static android.server.wm.CommandSession.KEY_FORWARD;
import static android.server.wm.ComponentNameUtils.getActivityName;
import static android.server.wm.ComponentNameUtils.getLogTag;
@@ -74,6 +73,7 @@
import static android.server.wm.UiDeviceUtils.pressUnlockButton;
import static android.server.wm.UiDeviceUtils.pressWakeupButton;
import static android.server.wm.UiDeviceUtils.waitForDeviceIdle;
+import static android.server.wm.WindowManagerState.STATE_RESUMED;
import static android.server.wm.app.Components.BROADCAST_RECEIVER_ACTIVITY;
import static android.server.wm.app.Components.BroadcastReceiverActivity.ACTION_TRIGGER_BROADCAST;
import static android.server.wm.app.Components.BroadcastReceiverActivity.EXTRA_BROADCAST_ORIENTATION;
@@ -141,6 +141,7 @@
import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.MotionEvent;
+import android.view.ViewConfiguration;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -533,11 +534,33 @@
return ComponentName.unflattenFromString(mContext.getResources().getString(resId));
}
+ /**
+ * Move around center of the specific display to ensures the display to be focused without
+ * triggering potential clicked event to impact the test environment.
+ * (e.g: Keyguard credential activated unexpectedly.)
+ *
+ * @param displayId the display ID to gain focused by inject swipe action
+ */
+ protected void moveAroundCenterSync(int displayId) {
+ final Rect bounds = mWmState.getDisplay(displayId).getDisplayRect();
+ final long downTime = SystemClock.uptimeMillis();
+ final int touchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
+ final int downX = bounds.left + bounds.width() / 2;
+ final int downY = bounds.top + bounds.height() / 2;
+ injectMotion(downTime, downTime, MotionEvent.ACTION_DOWN, downX, downY, displayId, true);
+
+ final int moveX = downX + Float.floatToIntBits(touchSlop / 2.0f);
+ final int moveY = downY + Float.floatToIntBits(touchSlop / 2.0f);
+ injectMotion(downTime, downTime, MotionEvent.ACTION_MOVE, moveX, moveY, displayId, true);
+
+ final long upTime = SystemClock.uptimeMillis();
+ injectMotion(downTime, upTime, MotionEvent.ACTION_UP, moveX, moveY, displayId, true);
+ }
+
protected void tapOnDisplaySync(int x, int y, int displayId) {
tapOnDisplay(x, y, displayId, true /* sync*/);
}
-
private void tapOnDisplay(int x, int y, int displayId, boolean sync) {
final long downTime = SystemClock.uptimeMillis();
injectMotion(downTime, downTime, MotionEvent.ACTION_DOWN, x, y, displayId, sync);
@@ -658,10 +681,19 @@
mWmState.computeState();
final List<WindowManagerState.ActivityTask> stacks = mWmState.getRootTasks();
for (WindowManagerState.ActivityTask stack : stacks) {
- for (WindowManagerState.ActivityTask task : stack.mTasks) {
- if (task.mTaskId == taskId) {
- return stack;
- }
+ if (stack.getTask(taskId) != null) {
+ return stack;
+ }
+ }
+ return null;
+ }
+
+ protected WindowManagerState.ActivityTask getRootTask(int taskId) {
+ mWmState.computeState();
+ final List<WindowManagerState.ActivityTask> rootTasks = mWmState.getRootTasks();
+ for (WindowManagerState.ActivityTask rootTask : rootTasks) {
+ if (rootTask.getTaskId() == taskId) {
+ return rootTask;
}
}
return null;
@@ -831,12 +863,25 @@
return result[0];
}
- protected void moveActivityToStack(ComponentName activityName, int stackId) {
+ /** Move activity to stack or on top of the given stack when the stack is a leak task. */
+ protected void moveActivityToStackOrOnTop(ComponentName activityName, int stackId) {
mWmState.computeState(activityName);
- final int taskId = mWmState.getTaskByActivity(activityName).mTaskId;
- SystemUtil.runWithShellPermissionIdentity(
- () -> mAtm.moveTaskToStack(taskId, stackId, true));
-
+ WindowManagerState.ActivityTask rootTask = getRootTask(stackId);
+ if (rootTask.getActivities().size() != 0) {
+ // If the root task is a 1-level task, start the activity on top of given task.
+ getLaunchActivityBuilder()
+ .setDisplayId(rootTask.mDisplayId)
+ .setWindowingMode(rootTask.getWindowingMode())
+ .setActivityType(rootTask.getActivityType())
+ .setTargetActivity(activityName)
+ .allowMultipleInstances(false)
+ .setUseInstrumentation()
+ .execute();
+ } else {
+ final int taskId = mWmState.getTaskByActivity(activityName).mTaskId;
+ SystemUtil.runWithShellPermissionIdentity(
+ () -> mAtm.moveTaskToStack(taskId, stackId, true));
+ }
mWmState.waitForValidState(new WaitForValidActivityState.Builder(activityName)
.setStackId(stackId)
.build());
@@ -1188,7 +1233,7 @@
public LockScreenSession enterAndConfirmLockCredential() {
// Ensure focus will switch to default display. Meanwhile we cannot tap on center area,
// which may tap on input credential area.
- tapOnDisplaySync(10, 10, DEFAULT_DISPLAY);
+ moveAroundCenterSync(DEFAULT_DISPLAY);
waitForDeviceIdle(3000);
SystemUtil.runWithShellPermissionIdentity(() ->
@@ -1230,7 +1275,7 @@
LockScreenSession unlockDevice() {
// Make sure the unlock button event is send to the default display.
- tapOnDisplaySync(10, 10, DEFAULT_DISPLAY);
+ moveAroundCenterSync(DEFAULT_DISPLAY);
pressUnlockButton();
return this;
@@ -2201,7 +2246,7 @@
// all tests afterward would also fail (since the leakage is always there) and fire
// unnecessary false alarms.
try {
- mWmState.assertEmptyStackOrTask();
+ mWmState.assertNoneEmptyTasks();
} catch (Throwable t) {
sStackTaskLeakFound = true;
addError(t);
diff --git a/tests/framework/base/windowmanager/util/src/android/server/wm/CommandSession.java b/tests/framework/base/windowmanager/util/src/android/server/wm/CommandSession.java
index a50e0c6c..4039a13 100644
--- a/tests/framework/base/windowmanager/util/src/android/server/wm/CommandSession.java
+++ b/tests/framework/base/windowmanager/util/src/android/server/wm/CommandSession.java
@@ -374,6 +374,20 @@
boolean shouldWaitForLaunched();
}
+ abstract static class DefaultLaunchProxy implements LaunchProxy {
+ LaunchInjector mLaunchInjector;
+
+ @Override
+ public boolean shouldWaitForLaunched() {
+ return true;
+ }
+
+ @Override
+ public void setLaunchInjector(LaunchInjector injector) {
+ mLaunchInjector = injector;
+ }
+ }
+
/** Created by test case to control testing activity that implements the session protocol. */
public static class ActivitySessionClient extends BroadcastReceiver implements AutoCloseable {
private final Context mContext;
@@ -942,7 +956,7 @@
/** Same as {@link #getConfigInfo()}, but for Application. */
private ConfigInfo getAppConfigInfo() {
final Application application = (Application) getApplicationContext();
- return new ConfigInfo(application, application.getDisplay());
+ return new ConfigInfo(application, getDisplay());
}
}
diff --git a/tests/framework/base/windowmanager/util/src/android/server/wm/WindowManagerState.java b/tests/framework/base/windowmanager/util/src/android/server/wm/WindowManagerState.java
index 793ece8..61c3a380 100644
--- a/tests/framework/base/windowmanager/util/src/android/server/wm/WindowManagerState.java
+++ b/tests/framework/base/windowmanager/util/src/android/server/wm/WindowManagerState.java
@@ -76,6 +76,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -412,11 +413,9 @@
public String getTopActivityName(int displayId) {
if (!getDisplay(displayId).mRootTasks.isEmpty()) {
final ActivityTask topStack = getDisplay(displayId).mRootTasks.get(0);
- if (!topStack.mTasks.isEmpty()) {
- final ActivityTask topTask = topStack.mTasks.get(0);
- if (!topTask.mActivities.isEmpty()) {
- return topTask.mActivities.get(0).name;
- }
+ final ActivityTask topTask = topStack.getTopTask();
+ if (!topTask.mActivities.isEmpty()) {
+ return topTask.mActivities.get(0).name;
}
}
return null;
@@ -519,7 +518,7 @@
continue;
}
if (stack.getWindowingMode() == windowingMode) {
- count += stack.mTasks.size();
+ count += stack.mTasks.isEmpty() ? 1 : stack.mTasks.size();
}
}
return count;
@@ -673,16 +672,16 @@
ActivityTask getHomeTask() {
final ActivityTask homeStack = getStackByActivityType(ACTIVITY_TYPE_HOME);
- if (homeStack != null && !homeStack.mTasks.isEmpty()) {
- return homeStack.mTasks.get(0);
+ if (homeStack != null) {
+ return homeStack.getTopTask();
}
return null;
}
private ActivityTask getRecentsTask() {
final ActivityTask recentsStack = getStackByActivityType(ACTIVITY_TYPE_RECENTS);
- if (recentsStack != null && !recentsStack.mTasks.isEmpty()) {
- return recentsStack.mTasks.get(0);
+ if (recentsStack != null) {
+ return recentsStack.getTopTask();
}
return null;
}
@@ -704,24 +703,15 @@
}
public ActivityTask getTaskByActivity(ComponentName activityName) {
- return getTaskByActivityInternal(getActivityName(activityName), WINDOWING_MODE_UNDEFINED);
+ return getTaskByActivity(activityName, WINDOWING_MODE_UNDEFINED);
}
ActivityTask getTaskByActivity(ComponentName activityName, int windowingMode) {
- return getTaskByActivityInternal(getActivityName(activityName), windowingMode);
- }
-
- private ActivityTask getTaskByActivityInternal(String fullName, int windowingMode) {
for (ActivityTask stack : mRootTasks) {
if (windowingMode == WINDOWING_MODE_UNDEFINED
|| windowingMode == stack.getWindowingMode()) {
- for (ActivityTask task : stack.mTasks) {
- for (Activity activity : task.mActivities) {
- if (activity.name.equals(fullName)) {
- return task;
- }
- }
- }
+ Activity activity = stack.getActivity(activityName);
+ if (activity != null) return activity.task;
}
}
return null;
@@ -739,21 +729,20 @@
// If activityName is null, count all activities in the task.
// Otherwise count activities that have specified name.
for (ActivityTask stack : mRootTasks) {
- for (ActivityTask task : stack.mTasks) {
- if (task.mTaskId == taskId) {
- if (activityName == null) {
- return task.mActivities.size();
- }
- final String fullName = getActivityName(activityName);
- int count = 0;
- for (Activity activity : task.mActivities) {
- if (activity.name.equals(fullName)) {
- count++;
- }
- }
- return count;
+ final ActivityTask task = stack.getTask(taskId);
+ if (task == null) continue;
+
+ if (activityName == null) {
+ return task.mActivities.size();
+ }
+ final String fullName = getActivityName(activityName);
+ int count = 0;
+ for (Activity activity : task.mActivities) {
+ if (activity.name.equals(fullName)) {
+ count++;
}
}
+ return count;
}
return 0;
}
@@ -1102,7 +1091,7 @@
mTasks.add(new ActivityTask(proto.tasks[i]));
}
for (int i = 0; i < proto.activities.length; i++) {
- mActivities.add(new Activity(proto.activities[i]));
+ mActivities.add(new Activity(proto.activities[i], this));
}
}
@@ -1130,33 +1119,37 @@
return mActivities;
}
- /**
- * @return the top task in the stack.
- */
+ /** @return the top task in the stack. */
ActivityTask getTopTask() {
- if (!mTasks.isEmpty()) {
- // NOTE: Unlike the ActivityManager internals, we dump the state from top to bottom,
- // so the indices are inverted
- return mTasks.get(0);
- }
- return null;
+ // NOTE: Unlike the WindowManager internals, we dump the state from top to bottom,
+ // so the indices are inverted
+ return getTask((t) -> true);
+ }
+
+ public String getResumedActivity() {
+ return mResumedActivity;
}
public List<ActivityTask> getTasks() {
return new ArrayList<>(mTasks);
}
- ActivityTask getTask(int taskId) {
+ ActivityTask getTask(Predicate<ActivityTask> predicate) {
for (ActivityTask task : mTasks) {
- if (taskId == task.mTaskId) {
- return task;
- }
+ if (predicate.test(task)) return task;
}
- return null;
+ return predicate.test(this) ? this : null;
}
- public String getResumedActivity() {
- return mResumedActivity;
+ ActivityTask getTask(int taskId) {
+ return getTask((t) -> t.mTaskId == taskId);
+ }
+
+ void forAllTasks(Consumer<ActivityTask> consumer) {
+ for (ActivityTask task : mTasks) {
+ consumer.accept(task);
+ }
+ consumer.accept(this);
}
Activity getActivity(Predicate<Activity> predicate) {
@@ -1188,9 +1181,11 @@
boolean frontOfTask;
int procId = -1;
public boolean translucent;
+ ActivityTask task;
- Activity(ActivityRecordProto proto) {
+ Activity(ActivityRecordProto proto, ActivityTask parent) {
super(proto.windowToken.windowContainer);
+ task = parent;
name = proto.name;
state = proto.state;
visible = proto.visible;
diff --git a/tests/framework/base/windowmanager/util/src/android/server/wm/WindowManagerStateHelper.java b/tests/framework/base/windowmanager/util/src/android/server/wm/WindowManagerStateHelper.java
index b387965..fc31bd6 100644
--- a/tests/framework/base/windowmanager/util/src/android/server/wm/WindowManagerStateHelper.java
+++ b/tests/framework/base/windowmanager/util/src/android/server/wm/WindowManagerStateHelper.java
@@ -590,17 +590,12 @@
getKeyguardControllerState().aodShowing);
}
- public void assertEmptyStackOrTask() {
+ public void assertNoneEmptyTasks() {
computeState();
- final List<ActivityTask> stacks = getRootTasks();
- for (ActivityTask stack : stacks) {
- assertWithMessage("Empty stack was found, id = " + stack.mRootTaskId)
- .that(stack.getTopTask()).isNotNull();
- final List<ActivityTask> tasks = stack.getTasks();
- for (ActivityTask task : tasks) {
- assertWithMessage("Empty task was found, id = " + task.mTaskId)
- .that(task.getActivities().size()).isGreaterThan(0);
- }
+ final List<ActivityTask> tasks = getRootTasks();
+ for (ActivityTask task : tasks) {
+ task.forAllTasks((t) -> assertWithMessage("Empty task was found, id = " + t.mTaskId)
+ .that(t.mTasks.size() + t.mActivities.size()).isGreaterThan(0));
}
}
diff --git a/tests/inputmethod/mockime/res/xml/method.xml b/tests/inputmethod/mockime/res/xml/method.xml
index 2266fba..5b3cf85 100644
--- a/tests/inputmethod/mockime/res/xml/method.xml
+++ b/tests/inputmethod/mockime/res/xml/method.xml
@@ -15,5 +15,6 @@
limitations under the License.
-->
-<input-method xmlns:android="http://schemas.android.com/apk/res/android">
+<input-method xmlns:android="http://schemas.android.com/apk/res/android"
+ android:supportsInlineSuggestions="true">
</input-method>
diff --git a/tests/inputmethod/mockime/src/com/android/cts/mockime/ImeSettings.java b/tests/inputmethod/mockime/src/com/android/cts/mockime/ImeSettings.java
index a7bf781..a9d1414 100644
--- a/tests/inputmethod/mockime/src/com/android/cts/mockime/ImeSettings.java
+++ b/tests/inputmethod/mockime/src/com/android/cts/mockime/ImeSettings.java
@@ -47,6 +47,7 @@
private static final String INPUT_VIEW_SYSTEM_UI_VISIBILITY = "InputViewSystemUiVisibility";
private static final String HARD_KEYBOARD_CONFIGURATION_BEHAVIOR_ALLOWED =
"HardKeyboardConfigurationBehaviorAllowed";
+ private static final String INLINE_SUGGESTIONS_ENABLED = "InlineSuggestionsEnabled";
@NonNull
private final PersistableBundle mBundle;
@@ -105,6 +106,10 @@
return mBundle.getBoolean(HARD_KEYBOARD_CONFIGURATION_BEHAVIOR_ALLOWED, defaultValue);
}
+ public boolean getInlineSuggestionsEnabled() {
+ return mBundle.getBoolean(INLINE_SUGGESTIONS_ENABLED);
+ }
+
static Bundle serializeToBundle(@NonNull String eventCallbackActionName,
@Nullable Builder builder) {
final Bundle result = new Bundle();
@@ -214,5 +219,16 @@
mBundle.putBoolean(HARD_KEYBOARD_CONFIGURATION_BEHAVIOR_ALLOWED, allowed);
return this;
}
+
+ /**
+ * Controls whether inline suggestions are enabled for {@link MockIme}. If enabled, a
+ * suggestion strip will be rendered at the top of the keyboard.
+ *
+ * @param enabled {@code true} when {@link MockIme} is enabled to show inline suggestions.
+ */
+ public Builder setInlineSuggestionsEnabled(boolean enabled) {
+ mBundle.putBoolean(INLINE_SUGGESTIONS_ENABLED, enabled);
+ return this;
+ }
}
}
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 590ce86..6142b9e 100644
--- a/tests/inputmethod/mockime/src/com/android/cts/mockime/MockIme.java
+++ b/tests/inputmethod/mockime/src/com/android/cts/mockime/MockIme.java
@@ -16,6 +16,8 @@
package com.android.cts.mockime;
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
import android.content.BroadcastReceiver;
@@ -25,6 +27,7 @@
import android.content.IntentFilter;
import android.content.res.Configuration;
import android.inputmethodservice.InputMethodService;
+import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
@@ -36,31 +39,43 @@
import android.os.SystemClock;
import android.text.TextUtils;
import android.util.Log;
+import android.util.Size;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.View;
+import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowInsets;
import android.view.WindowManager;
+import android.view.inline.InlinePresentationSpec;
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.InlineSuggestion;
+import android.view.inputmethod.InlineSuggestionsRequest;
+import android.view.inputmethod.InlineSuggestionsResponse;
import android.view.inputmethod.InputBinding;
import android.view.inputmethod.InputContentInfo;
import android.view.inputmethod.InputMethod;
import android.widget.LinearLayout;
-import android.widget.RelativeLayout;
+import android.widget.ScrollView;
import android.widget.TextView;
import androidx.annotation.AnyThread;
import androidx.annotation.CallSuper;
+import androidx.annotation.GuardedBy;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BooleanSupplier;
import java.util.function.Consumer;
@@ -407,23 +422,38 @@
final int defaultBackgroundColor =
getResources().getColor(android.R.color.holo_orange_dark, null);
- setBackgroundColor(mSettings.getBackgroundColor(defaultBackgroundColor));
final int mainSpacerHeight = mSettings.getInputViewHeightWithoutSystemWindowInset(
LayoutParams.WRAP_CONTENT);
{
- final RelativeLayout layout = new RelativeLayout(getContext());
+ final LinearLayout layout = new LinearLayout(getContext());
+ layout.setOrientation(LinearLayout.VERTICAL);
+
+ if (mSettings.getInlineSuggestionsEnabled()) {
+ final ScrollView scrollView = new ScrollView(getContext());
+ final LayoutParams scrollViewParams = new LayoutParams(MATCH_PARENT, 100);
+ scrollView.setLayoutParams(scrollViewParams);
+
+ sSuggestionView = new LinearLayout(getContext());
+ sSuggestionView.setBackgroundColor(0xFFEEEEEE);
+ //TODO: Change magic id
+ sSuggestionView.setId(0x0102000b);
+ scrollView.addView(sSuggestionView,
+ new LayoutParams(MATCH_PARENT, MATCH_PARENT));
+
+ layout.addView(scrollView);
+ }
+
final TextView textView = new TextView(getContext());
- final RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
- RelativeLayout.LayoutParams.MATCH_PARENT,
- RelativeLayout.LayoutParams.WRAP_CONTENT);
- params.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE);
+ final LayoutParams params = new LayoutParams(MATCH_PARENT, WRAP_CONTENT);
textView.setLayoutParams(params);
textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 20);
textView.setGravity(Gravity.CENTER);
textView.setText(getImeId());
+ textView.setBackgroundColor(mSettings.getBackgroundColor(defaultBackgroundColor));
layout.addView(textView);
- addView(layout, LayoutParams.MATCH_PARENT, mainSpacerHeight);
+
+ addView(layout, MATCH_PARENT, mainSpacerHeight);
}
final int systemUiVisibility = mSettings.getInputViewSystemUiVisibility(0);
@@ -598,6 +628,114 @@
return new ImeState(hasInputBinding, hasDummyInputConnectionConnection);
}
+ private static LinearLayout sSuggestionView;
+ @GuardedBy("this")
+ private List<View> mSuggestionViews = new ArrayList<>();
+ @GuardedBy("this")
+ private List<Size> mSuggestionViewSizes = new ArrayList<>();
+ @GuardedBy("this")
+ private boolean mSuggestionViewVisible = false;
+
+ public InlineSuggestionsRequest onCreateInlineSuggestionsRequest(Bundle uiExtras) {
+ Log.d(TAG, "onCreateInlineSuggestionsRequest() called");
+ final ArrayList<InlinePresentationSpec> presentationSpecs = new ArrayList<>();
+ presentationSpecs.add(new InlinePresentationSpec.Builder(new Size(100, 100),
+ new Size(400, 100)).build());
+ presentationSpecs.add(new InlinePresentationSpec.Builder(new Size(100, 100),
+ new Size(400, 100)).build());
+
+ return new InlineSuggestionsRequest.Builder(presentationSpecs)
+ .setMaxSuggestionCount(6)
+ .build();
+ }
+
+ @Override
+ public boolean onInlineSuggestionsResponse(InlineSuggestionsResponse response) {
+ Log.d(TAG, "onInlineSuggestionsResponse() called");
+ AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
+ onInlineSuggestionsResponseInternal(response);
+ });
+ return true;
+ }
+
+ private synchronized void updateInlineSuggestionVisibility(boolean visible, boolean force) {
+ Log.d(TAG, "updateInlineSuggestionVisibility() called, visible=" + visible + ", force="
+ + force);
+ mMainHandler.post(() -> {
+ Log.d(TAG, "updateInlineSuggestionVisibility() running");
+ if (visible == mSuggestionViewVisible && !force) {
+ return;
+ } else if (visible) {
+ sSuggestionView.removeAllViews();
+ final int size = mSuggestionViews.size();
+ for (int i = 0; i < size; i++) {
+ if(mSuggestionViews.get(i) == null) {
+ continue;
+ }
+ ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(
+ mSuggestionViewSizes.get(i).getWidth(),
+ mSuggestionViewSizes.get(i).getHeight());
+ sSuggestionView.addView(mSuggestionViews.get(i), layoutParams);
+ }
+ mSuggestionViewVisible = true;
+ } else {
+ sSuggestionView.removeAllViews();
+ mSuggestionViewVisible = false;
+ }
+ });
+ }
+
+ private void onSuggestionViewUpdated() {
+ getTracer().onSuggestionViewUpdated();
+ }
+
+ private synchronized void updateSuggestionViews(View[] suggestionViews, Size[] sizes) {
+ Log.d(TAG, "updateSuggestionViews() called on " + suggestionViews.length + " views");
+ mSuggestionViews = Arrays.asList(suggestionViews);
+ mSuggestionViewSizes = Arrays.asList(sizes);
+ final boolean visible = !mSuggestionViews.isEmpty();
+ updateInlineSuggestionVisibility(visible, true);
+ onSuggestionViewUpdated();
+ }
+
+ private void onInlineSuggestionsResponseInternal(InlineSuggestionsResponse response) {
+ if (response == null || response.getInlineSuggestions() == null) {
+ Log.w(TAG, "onInlineSuggestionsResponseInternal() null response/suggestions");
+ return;
+ }
+ final List<InlineSuggestion> inlineSuggestions = response.getInlineSuggestions();
+ final int totalSuggestionsCount = inlineSuggestions.size();
+ Log.d(TAG, "onInlineSuggestionsResponseInternal() called. Suggestion="
+ + totalSuggestionsCount);
+
+ if (totalSuggestionsCount == 0) {
+ updateSuggestionViews(new View[]{} , new Size[]{});
+ }
+
+ final AtomicInteger suggestionsCount = new AtomicInteger(totalSuggestionsCount);
+ final View[] suggestionViews = new View[totalSuggestionsCount];
+ final Size[] sizes = new Size[totalSuggestionsCount];
+
+ for (int i = 0; i < totalSuggestionsCount; i++) {
+ final int index = i;
+ InlineSuggestion inlineSuggestion = inlineSuggestions.get(index);
+ Size size = inlineSuggestion.getInfo().getPresentationSpec().getMaxSize();
+ Log.d(TAG, "Calling inflate on suggestion " + i);
+ inlineSuggestion.inflate(this, size,
+ AsyncTask.THREAD_POOL_EXECUTOR,
+ suggestionView -> {
+ Log.d(TAG, "new inline suggestion view ready");
+ if(suggestionView != null) {
+ suggestionViews[index] = suggestionView;
+ sizes[index] = size;
+ }
+ if (suggestionsCount.decrementAndGet() == 0) {
+ updateSuggestionViews(suggestionViews, sizes);
+ }
+ });
+ }
+ }
+
/**
* Event tracing helper class for {@link MockIme}.
*/
@@ -822,5 +960,9 @@
imeLayoutInfo.writeToBundle(arguments);
recordEventInternal("onInputViewLayoutChanged", runnable, arguments);
}
+
+ public void onSuggestionViewUpdated() {
+ recordEventInternal("onSuggestionViewUpdated", () -> {}, new Bundle());
+ }
}
}
diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/InlineSuggestionTest.java b/tests/inputmethod/src/android/view/inputmethod/cts/InlineSuggestionTest.java
index 50abb7a..a0cc196 100644
--- a/tests/inputmethod/src/android/view/inputmethod/cts/InlineSuggestionTest.java
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/InlineSuggestionTest.java
@@ -99,6 +99,19 @@
latch.await();
}
+ @Test
+ public void testInflateTwiceThrowsException() {
+ InlineSuggestion suggestion = createInlineSuggestion();
+ Context context = InstrumentationRegistry.getTargetContext();
+ Consumer<View> mockConsumer = mock(Consumer.class);
+
+ suggestion.inflate(context, new Size(100, 100), AsyncTask.THREAD_POOL_EXECUTOR,
+ mockConsumer);
+ assertThrows(IllegalStateException.class,
+ () -> suggestion.inflate(context, new Size(100, 100),
+ AsyncTask.THREAD_POOL_EXECUTOR, mockConsumer));
+ }
+
private InlineSuggestion createInlineSuggestion() {
InlineSuggestionInfo info = InlineSuggestionInfo.newInlineSuggestionInfo(
mInlinePresentationSpec, InlineSuggestionInfo.SOURCE_AUTOFILL, new String[]{""});
diff --git a/tests/location/location_fine/AndroidManifest.xml b/tests/location/location_fine/AndroidManifest.xml
index 01fe459..0804333 100644
--- a/tests/location/location_fine/AndroidManifest.xml
+++ b/tests/location/location_fine/AndroidManifest.xml
@@ -27,6 +27,7 @@
<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_WIFI_STATE"/>
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="android.location.cts.fine"
diff --git a/tests/location/location_fine/src/android/location/cts/fine/GnssAntennaInfoRegistrationTest.java b/tests/location/location_fine/src/android/location/cts/fine/GnssAntennaInfoRegistrationTest.java
index fa7c032..5aa2bd3 100644
--- a/tests/location/location_fine/src/android/location/cts/fine/GnssAntennaInfoRegistrationTest.java
+++ b/tests/location/location_fine/src/android/location/cts/fine/GnssAntennaInfoRegistrationTest.java
@@ -71,32 +71,30 @@
@Test
public void testGnssAntennaInfoCallbackRegistration() {
- TestGnssAntennaInfoCallback callback = new TestGnssAntennaInfoCallback();
-
// TODO(skz): check that version code is greater than R
- mManager.registerAntennaInfoCallback(Executors.newSingleThreadExecutor(), callback);
+ if(!mManager.getGnssCapabilities().hasGnssAntennaInfo()) {
+ // GnssAntennaInfo is not supported
+ return;
+ }
+
+ TestGnssAntennaInfoListener listener = new TestGnssAntennaInfoListener();
+
+ mManager.registerAntennaInfoListener(Executors.newSingleThreadExecutor(), listener);
try {
mAntennaInfoReciept.await(ANTENNA_INFO_TIMEOUT_SEC, TimeUnit.SECONDS);
} catch (InterruptedException e) {
Log.e(TAG, "Test was interrupted.");
}
- if(!callback.verifyStatus()) {
- // GnssAntennaInfo is not supported (or is taking too long)
- mManager.unregisterAntennaInfoCallback(callback);
- return;
- }
+ listener.verifyRegistration();
- callback.verifyRegistration();
-
- mManager.unregisterAntennaInfoCallback(callback);
+ mManager.unregisterAntennaInfoListener(listener);
}
- private class TestGnssAntennaInfoCallback extends GnssAntennaInfo.Callback {
+ private class TestGnssAntennaInfoListener implements GnssAntennaInfo.Listener {
private boolean receivedAntennaInfo = false;
private int numResults = 0;
- private int status = -10000;
@Override
public void onGnssAntennaInfoReceived(@NonNull List<GnssAntennaInfo> gnssAntennaInfos) {
@@ -109,15 +107,6 @@
}
}
- @Override
- public void onStatusChanged(@GnssAntennaInfoStatus int status) {
- this.status = status;
- }
-
- public boolean verifyStatus() {
- return (status == GnssAntennaInfo.Callback.STATUS_READY);
- }
-
public void verifyRegistration() {
assertTrue(receivedAntennaInfo);
assertThat(numResults).isGreaterThan(0);
diff --git a/tests/location/location_fine/src/android/location/cts/fine/GnssAntennaInfoTest.java b/tests/location/location_fine/src/android/location/cts/fine/GnssAntennaInfoTest.java
index 7805059..cbaabf7 100644
--- a/tests/location/location_fine/src/android/location/cts/fine/GnssAntennaInfoTest.java
+++ b/tests/location/location_fine/src/android/location/cts/fine/GnssAntennaInfoTest.java
@@ -18,11 +18,11 @@
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
import android.location.GnssAntennaInfo;
-import android.location.GnssAntennaInfo.PhaseCenterOffsetCoordinates;
-import android.location.GnssAntennaInfo.PhaseCenterVariationCorrections;
-import android.location.GnssAntennaInfo.SignalGainCorrections;
+import android.location.GnssAntennaInfo.PhaseCenterOffset;
+import android.location.GnssAntennaInfo.SphericalCorrections;
import android.os.Parcel;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -73,33 +73,56 @@
};
@Test
- public void testDescribeContents() {
- GnssAntennaInfo gnssAntennaInfo = createTestGnssAntennaInfo();
+ public void testFullAntennaInfoDescribeContents() {
+ GnssAntennaInfo gnssAntennaInfo = createFullTestGnssAntennaInfo();
assertEquals(0, gnssAntennaInfo.describeContents());
}
@Test
- public void testWriteToParcel() {
- GnssAntennaInfo gnssAntennaInfo = createTestGnssAntennaInfo();
+ public void testPartialAntennaInfoDescribeContents() {
+ GnssAntennaInfo gnssAntennaInfo = createPartialTestGnssAntennaInfo();
+ assertEquals(0, gnssAntennaInfo.describeContents());
+ }
+
+ @Test
+ public void testFullAntennaInfoWriteToParcel() {
+ GnssAntennaInfo gnssAntennaInfo = createFullTestGnssAntennaInfo();
Parcel parcel = Parcel.obtain();
gnssAntennaInfo.writeToParcel(parcel, 0);
parcel.setDataPosition(0);
GnssAntennaInfo newGnssAntennaInfo = GnssAntennaInfo.CREATOR.createFromParcel(parcel);
- verifyGnssAntennaInfoValuesAndGetters(newGnssAntennaInfo);
+ verifyFullGnssAntennaInfoValuesAndGetters(newGnssAntennaInfo);
parcel.recycle();
}
@Test
- public void testCreateGnssAntennaInfoAndGetValues() {
- GnssAntennaInfo gnssAntennaInfo = createTestGnssAntennaInfo();
- verifyGnssAntennaInfoValuesAndGetters(gnssAntennaInfo);
+ public void testPartialAntennaInfoWriteToParcel() {
+ GnssAntennaInfo gnssAntennaInfo = createPartialTestGnssAntennaInfo();
+ Parcel parcel = Parcel.obtain();
+ gnssAntennaInfo.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ GnssAntennaInfo newGnssAntennaInfo = GnssAntennaInfo.CREATOR.createFromParcel(parcel);
+ verifyPartialGnssAntennaInfoValuesAndGetters(newGnssAntennaInfo);
+ parcel.recycle();
}
- private static GnssAntennaInfo createTestGnssAntennaInfo() {
+ @Test
+ public void testCreateFullGnssAntennaInfoAndGetValues() {
+ GnssAntennaInfo gnssAntennaInfo = createFullTestGnssAntennaInfo();
+ verifyFullGnssAntennaInfoValuesAndGetters(gnssAntennaInfo);
+ }
+
+ @Test
+ public void testCreatePartialGnssAntennaInfoAndGetValues() {
+ GnssAntennaInfo gnssAntennaInfo = createPartialTestGnssAntennaInfo();
+ verifyPartialGnssAntennaInfoValuesAndGetters(gnssAntennaInfo);
+ }
+
+ private static GnssAntennaInfo createFullTestGnssAntennaInfo() {
double carrierFrequencyMHz = 13758.0;
- GnssAntennaInfo.PhaseCenterOffsetCoordinates phaseCenterOffsetCoordinates = new
- GnssAntennaInfo.PhaseCenterOffsetCoordinates(
+ GnssAntennaInfo.PhaseCenterOffset phaseCenterOffset = new
+ GnssAntennaInfo.PhaseCenterOffset(
4.3d,
1.4d,
2.10d,
@@ -110,75 +133,109 @@
double[][] phaseCenterVariationCorrectionsMillimeters = PHASE_CENTER_VARIATION_CORRECTIONS;
double[][] phaseCenterVariationCorrectionsUncertaintyMillimeters =
PHASE_CENTER_VARIATION_CORRECTION_UNCERTAINTIES;
- GnssAntennaInfo.PhaseCenterVariationCorrections
+ SphericalCorrections
phaseCenterVariationCorrections =
- new GnssAntennaInfo.PhaseCenterVariationCorrections(
+ new SphericalCorrections(
phaseCenterVariationCorrectionsMillimeters,
phaseCenterVariationCorrectionsUncertaintyMillimeters);
double[][] signalGainCorrectionsDbi = SIGNAL_GAIN_CORRECTIONS;
double[][] signalGainCorrectionsUncertaintyDbi = SIGNAL_GAIN_CORRECTION_UNCERTAINTIES;
- GnssAntennaInfo.SignalGainCorrections signalGainCorrections = new
- GnssAntennaInfo.SignalGainCorrections(
+ SphericalCorrections signalGainCorrections = new
+ SphericalCorrections(
signalGainCorrectionsDbi,
signalGainCorrectionsUncertaintyDbi);
- return new GnssAntennaInfo(carrierFrequencyMHz, phaseCenterOffsetCoordinates,
- phaseCenterVariationCorrections, signalGainCorrections);
+ return new GnssAntennaInfo.Builder()
+ .setCarrierFrequencyMHz(carrierFrequencyMHz)
+ .setPhaseCenterOffset(phaseCenterOffset)
+ .setPhaseCenterVariationCorrections(phaseCenterVariationCorrections)
+ .setSignalGainCorrections(signalGainCorrections)
+ .build();
}
- private static void verifyGnssAntennaInfoValuesAndGetters(GnssAntennaInfo gnssAntennaInfo) {
+ private static GnssAntennaInfo createPartialTestGnssAntennaInfo() {
+ double carrierFrequencyMHz = 13758.0;
+
+ GnssAntennaInfo.PhaseCenterOffset phaseCenterOffset = new
+ GnssAntennaInfo.PhaseCenterOffset(
+ 4.3d,
+ 1.4d,
+ 2.10d,
+ 2.1d,
+ 3.12d,
+ 0.5d);
+
+ return new GnssAntennaInfo.Builder()
+ .setCarrierFrequencyMHz(carrierFrequencyMHz)
+ .setPhaseCenterOffset(phaseCenterOffset)
+ .build();
+ }
+
+ private static void verifyPartialGnssAntennaInfoValuesAndGetters(GnssAntennaInfo gnssAntennaInfo) {
assertEquals(13758.0d, gnssAntennaInfo.getCarrierFrequencyMHz(), PRECISION);
// Phase Center Offset Tests --------------------------------------------------------
- PhaseCenterOffsetCoordinates phaseCenterOffsetCoordinates =
- gnssAntennaInfo.getPhaseCenterOffsetCoordinates();
- assertEquals(4.3d, phaseCenterOffsetCoordinates.getXCoordMillimeters(),
+ PhaseCenterOffset phaseCenterOffset =
+ gnssAntennaInfo.getPhaseCenterOffset();
+ assertEquals(4.3d, phaseCenterOffset.getXOffsetMm(),
PRECISION);
- assertEquals(1.4d, phaseCenterOffsetCoordinates.getXCoordUncertaintyMillimeters(),
+ assertEquals(1.4d, phaseCenterOffset.getXOffsetUncertaintyMm(),
PRECISION);
- assertEquals(2.10d, phaseCenterOffsetCoordinates.getYCoordMillimeters(),
+ assertEquals(2.10d, phaseCenterOffset.getYOffsetMm(),
PRECISION);
- assertEquals(2.1d, phaseCenterOffsetCoordinates.getYCoordUncertaintyMillimeters(),
+ assertEquals(2.1d, phaseCenterOffset.getYOffsetUncertaintyMm(),
PRECISION);
- assertEquals(3.12d, phaseCenterOffsetCoordinates.getZCoordMillimeters(),
+ assertEquals(3.12d, phaseCenterOffset.getZOffsetMm(),
PRECISION);
- assertEquals(0.5d, phaseCenterOffsetCoordinates.getZCoordUncertaintyMillimeters(),
+ assertEquals(0.5d, phaseCenterOffset.getZOffsetUncertaintyMm(),
PRECISION);
// Phase Center Variation Corrections Tests -----------------------------------------
- PhaseCenterVariationCorrections phaseCenterVariationCorrections =
+ assertNull(gnssAntennaInfo.getPhaseCenterVariationCorrections());
+
+ // Signal Gain Corrections Tests -----------------------------------------------------
+ assertNull(gnssAntennaInfo.getSignalGainCorrections());
+ }
+
+ private static void verifyFullGnssAntennaInfoValuesAndGetters(GnssAntennaInfo gnssAntennaInfo) {
+ assertEquals(13758.0d, gnssAntennaInfo.getCarrierFrequencyMHz(), PRECISION);
+
+ // Phase Center Offset Tests --------------------------------------------------------
+ PhaseCenterOffset phaseCenterOffset =
+ gnssAntennaInfo.getPhaseCenterOffset();
+ assertEquals(4.3d, phaseCenterOffset.getXOffsetMm(),
+ PRECISION);
+ assertEquals(1.4d, phaseCenterOffset.getXOffsetUncertaintyMm(),
+ PRECISION);
+ assertEquals(2.10d, phaseCenterOffset.getYOffsetMm(),
+ PRECISION);
+ assertEquals(2.1d, phaseCenterOffset.getYOffsetUncertaintyMm(),
+ PRECISION);
+ assertEquals(3.12d, phaseCenterOffset.getZOffsetMm(),
+ PRECISION);
+ assertEquals(0.5d, phaseCenterOffset.getZOffsetUncertaintyMm(),
+ PRECISION);
+
+ // Phase Center Variation Corrections Tests -----------------------------------------
+ SphericalCorrections phaseCenterVariationCorrections =
gnssAntennaInfo.getPhaseCenterVariationCorrections();
- assertEquals(6, phaseCenterVariationCorrections.getNumRows());
- assertEquals(6, phaseCenterVariationCorrections.getNumColumns());
assertEquals(60.0d, phaseCenterVariationCorrections.getDeltaTheta(), PRECISION);
assertEquals(36.0d, phaseCenterVariationCorrections.getDeltaPhi(), PRECISION);
assertArrayEquals(PHASE_CENTER_VARIATION_CORRECTIONS, phaseCenterVariationCorrections
- .getRawCorrectionsArray());
+ .getCorrectionsArray());
assertArrayEquals(PHASE_CENTER_VARIATION_CORRECTION_UNCERTAINTIES,
- phaseCenterVariationCorrections.getRawCorrectionUncertaintiesArray());
- assertEquals(13.46d, phaseCenterVariationCorrections
- .getPhaseCenterVariationCorrectionMillimetersAt(2, 2), PRECISION);
- assertEquals(2.23d, phaseCenterVariationCorrections
- .getPhaseCenterVariationCorrectionUncertaintyMillimetersAt(2,
- 2), PRECISION);
+ phaseCenterVariationCorrections.getCorrectionUncertaintiesArray());
// Signal Gain Corrections Tests -----------------------------------------------------
- SignalGainCorrections signalGainCorrections = gnssAntennaInfo.getSignalGainCorrections();
+ SphericalCorrections signalGainCorrections = gnssAntennaInfo.getSignalGainCorrections();
- assertEquals(6, signalGainCorrections.getNumRows());
- assertEquals(6, signalGainCorrections.getNumColumns());
assertEquals(60.0d, signalGainCorrections.getDeltaTheta(), PRECISION);
assertEquals(36.0d, signalGainCorrections.getDeltaPhi(), PRECISION);
assertArrayEquals(SIGNAL_GAIN_CORRECTIONS, signalGainCorrections
- .getRawCorrectionsArray());
+ .getCorrectionsArray());
assertArrayEquals(SIGNAL_GAIN_CORRECTION_UNCERTAINTIES,
- signalGainCorrections.getRawCorrectionUncertaintiesArray());
- assertEquals(7.92d, signalGainCorrections
- .getSignalGainCorrectionDbiAt(2, 2), PRECISION);
- assertEquals(2.10d, signalGainCorrections
- .getSignalGainCorrectionUncertaintyDbiAt(2,
- 2), PRECISION);
+ signalGainCorrections.getCorrectionUncertaintiesArray());
}
}
diff --git a/tests/location/location_fine/src/android/location/cts/fine/LocationManagerFineTest.java b/tests/location/location_fine/src/android/location/cts/fine/LocationManagerFineTest.java
index 9afc79b..9e0ed0b 100644
--- a/tests/location/location_fine/src/android/location/cts/fine/LocationManagerFineTest.java
+++ b/tests/location/location_fine/src/android/location/cts/fine/LocationManagerFineTest.java
@@ -1146,11 +1146,10 @@
@Test
public void testRegisterGnssAntennaInfoCallback() {
- GnssAntennaInfo.Callback callback = new GnssAntennaInfo.Callback() {
- };
+ GnssAntennaInfo.Listener listener = (gnssAntennaInfos) -> {};
- mManager.registerAntennaInfoCallback(Executors.newSingleThreadExecutor(), callback);
- mManager.unregisterAntennaInfoCallback(callback);
+ mManager.registerAntennaInfoListener(Executors.newSingleThreadExecutor(), listener);
+ mManager.unregisterAntennaInfoListener(listener);
}
@Test
diff --git a/tests/location/location_fine/src/android/location/cts/fine/ScanningSettingsTest.java b/tests/location/location_fine/src/android/location/cts/fine/ScanningSettingsTest.java
index 802aa2c..b7853e8 100644
--- a/tests/location/location_fine/src/android/location/cts/fine/ScanningSettingsTest.java
+++ b/tests/location/location_fine/src/android/location/cts/fine/ScanningSettingsTest.java
@@ -22,6 +22,7 @@
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.database.ContentObserver;
+import android.net.wifi.WifiManager;
import android.os.Handler;
import android.os.HandlerThread;
import android.platform.test.annotations.AppModeFull;
@@ -33,6 +34,7 @@
import android.test.AndroidTestCase;
import com.android.compatibility.common.util.CddTest;
+import com.android.compatibility.common.util.PollingCheck;
import androidx.test.platform.app.InstrumentationRegistry;
@@ -76,12 +78,35 @@
}
@CddTest(requirement = "7.4.2/C-2-1")
- public void testWifiScanningSettings() throws PackageManager.NameNotFoundException {
+ public void testWifiScanningSettings() throws Exception {
if (isTv()) {
return;
}
launchScanningSettings();
- toggleSettingAndVerify(WIFI_SCANNING_TITLE_RES, Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE);
+
+ final Resources res = mPackageManager.getResourcesForApplication(SETTINGS_PACKAGE);
+ final int resId = res.getIdentifier(WIFI_SCANNING_TITLE_RES, "string", SETTINGS_PACKAGE);
+ final UiObject2 pref = mDevice.findObject(By.text(res.getString(resId)));
+
+ final WifiManager wifiManager = mContext.getSystemService(WifiManager.class);
+
+ final boolean checked = wifiManager.isScanAlwaysAvailable();
+
+ // Click the preference to toggle the setting.
+ pref.click();
+ PollingCheck.check(
+ "Scan Always Available wasn't toggled from " + checked + " to " + !checked,
+ TIMEOUT,
+ () -> !checked == wifiManager.isScanAlwaysAvailable()
+ );
+
+ // Click the preference again to toggle the setting back.
+ pref.click();
+ PollingCheck.check(
+ "Scan Always Available wasn't toggled from " + !checked + " to " + checked,
+ TIMEOUT,
+ () -> checked == wifiManager.isScanAlwaysAvailable()
+ );
}
@CddTest(requirement = "7.4.3/C-4-1")
diff --git a/tests/media/Android.bp b/tests/media/Android.bp
index b813fb2..adcf3de 100644
--- a/tests/media/Android.bp
+++ b/tests/media/Android.bp
@@ -24,6 +24,7 @@
"android.test.runner.stubs",
"android.test.base.stubs",
],
+ jni_uses_platform_apis: true,
jni_libs: [
"libctsmediav2muxer_jni",
"libctsmediav2extractor_jni",
diff --git a/tests/media/src/android/mediav2/cts/CodecDecoderTest.java b/tests/media/src/android/mediav2/cts/CodecDecoderTest.java
index 19fe20a..f3ef46d 100644
--- a/tests/media/src/android/mediav2/cts/CodecDecoderTest.java
+++ b/tests/media/src/android/mediav2/cts/CodecDecoderTest.java
@@ -794,7 +794,6 @@
* Test Decoder by Queuing CSD separately
*/
@LargeTest
- @Ignore("TODO(b/149031058)")
@Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS)
public void testSimpleDecodeQueueCSD() throws IOException, InterruptedException {
MediaFormat format = setUpSource(mTestFile);
diff --git a/tests/media/src/android/mediav2/cts/CodecTestBase.java b/tests/media/src/android/mediav2/cts/CodecTestBase.java
index a09cf5a..739b60a 100644
--- a/tests/media/src/android/mediav2/cts/CodecTestBase.java
+++ b/tests/media/src/android/mediav2/cts/CodecTestBase.java
@@ -45,22 +45,24 @@
private static final String LOG_TAG = CodecAsyncHandler.class.getSimpleName();
private final Lock mLock = new ReentrantLock();
private final Condition mCondition = mLock.newCondition();
- private CallBackQueue mCbInputQueue;
- private CallBackQueue mCbOutputQueue;
+ private final LinkedList<Pair<Integer, MediaCodec.BufferInfo>> mCbInputQueue;
+ private final LinkedList<Pair<Integer, MediaCodec.BufferInfo>> mCbOutputQueue;
private MediaFormat mOutFormat;
private boolean mSignalledOutFormatChanged;
private volatile boolean mSignalledError;
CodecAsyncHandler() {
- mCbInputQueue = new CallBackQueue();
- mCbOutputQueue = new CallBackQueue();
+ mCbInputQueue = new LinkedList<>();
+ mCbOutputQueue = new LinkedList<>();
mSignalledError = false;
mSignalledOutFormatChanged = false;
}
void clearQueues() {
+ mLock.lock();
mCbInputQueue.clear();
mCbOutputQueue.clear();
+ mLock.unlock();
}
void resetContext() {
@@ -73,14 +75,20 @@
@Override
public void onInputBufferAvailable(@NonNull MediaCodec codec, int bufferIndex) {
assertTrue(bufferIndex >= 0);
- mCbInputQueue.push(new Pair<>(bufferIndex, (MediaCodec.BufferInfo) null));
+ mLock.lock();
+ mCbInputQueue.add(new Pair<>(bufferIndex, (MediaCodec.BufferInfo) null));
+ mCondition.signalAll();
+ mLock.unlock();
}
@Override
public void onOutputBufferAvailable(@NonNull MediaCodec codec, int bufferIndex,
@NonNull MediaCodec.BufferInfo info) {
assertTrue(bufferIndex >= 0);
- mCbOutputQueue.push(new Pair<>(bufferIndex, info));
+ mLock.lock();
+ mCbOutputQueue.add(new Pair<>(bufferIndex, info));
+ mCondition.signalAll();
+ mLock.unlock();
}
@Override
@@ -109,51 +117,52 @@
Pair<Integer, MediaCodec.BufferInfo> getInput() throws InterruptedException {
Pair<Integer, MediaCodec.BufferInfo> element = null;
+ mLock.lock();
while (!mSignalledError) {
if (mCbInputQueue.isEmpty()) {
- mLock.lock();
mCondition.await();
- mLock.unlock();
} else {
- element = mCbInputQueue.pop();
+ element = mCbInputQueue.remove(0);
break;
}
}
+ mLock.unlock();
return element;
}
Pair<Integer, MediaCodec.BufferInfo> getOutput() throws InterruptedException {
Pair<Integer, MediaCodec.BufferInfo> element = null;
+ mLock.lock();
while (!mSignalledError) {
if (mCbOutputQueue.isEmpty()) {
- mLock.lock();
mCondition.await();
- mLock.unlock();
} else {
- element = mCbOutputQueue.pop();
+ element = mCbOutputQueue.remove(0);
break;
}
}
+ mLock.unlock();
return element;
}
Pair<Integer, MediaCodec.BufferInfo> getWork() throws InterruptedException {
Pair<Integer, MediaCodec.BufferInfo> element = null;
+ mLock.lock();
while (!mSignalledError) {
if (mCbInputQueue.isEmpty() && mCbOutputQueue.isEmpty()) {
- mLock.lock();
mCondition.await();
- mLock.unlock();
- }
- if (!mCbOutputQueue.isEmpty()) {
- element = mCbOutputQueue.pop();
- break;
- }
- if (!mCbInputQueue.isEmpty()) {
- element = mCbInputQueue.pop();
- break;
+ } else {
+ if (!mCbOutputQueue.isEmpty()) {
+ element = mCbOutputQueue.remove(0);
+ break;
+ }
+ if (!mCbInputQueue.isEmpty()) {
+ element = mCbInputQueue.remove(0);
+ break;
+ }
}
}
+ mLock.unlock();
return element;
}
@@ -168,38 +177,6 @@
MediaFormat getOutputFormat() {
return mOutFormat;
}
-
- private class CallBackQueue {
- private final LinkedList<Pair<Integer, MediaCodec.BufferInfo>> mQueue = new LinkedList<>();
-
- void push(Pair<Integer, MediaCodec.BufferInfo> element) {
- mLock.lock();
- mQueue.add(element);
- mCondition.signalAll();
- mLock.unlock();
- }
-
- Pair<Integer, MediaCodec.BufferInfo> pop() {
- Pair<Integer, MediaCodec.BufferInfo> element = null;
- mLock.lock();
- if (mQueue.size() != 0) element = mQueue.remove(0);
- mLock.unlock();
- return element;
- }
-
- boolean isEmpty() {
- mLock.lock();
- boolean isEmpty = (mQueue.size() == 0);
- mLock.unlock();
- return isEmpty;
- }
-
- void clear() {
- mLock.lock();
- mQueue.clear();
- mLock.unlock();
- }
- }
}
class OutputManager {
@@ -461,8 +438,10 @@
int bufferID = element.first;
MediaCodec.BufferInfo info = element.second;
if (info != null) {
+ // <id, info> corresponds to output callback. Handle it accordingly
dequeueOutput(bufferID, info);
} else {
+ // <id, null> corresponds to input callback. Handle it accordingly
enqueueInput(bufferID);
frameCount++;
}
diff --git a/tests/rollback/AndroidManifest.xml b/tests/rollback/AndroidManifest.xml
index 3203f25..ab1ec0f 100644
--- a/tests/rollback/AndroidManifest.xml
+++ b/tests/rollback/AndroidManifest.xml
@@ -18,6 +18,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.cts.rollback" >
+ <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
<application>
<receiver android:name="com.android.cts.install.lib.LocalIntentSender"
android:exported="true" />
diff --git a/tests/sensor/src/android/hardware/cts/SensorTest.java b/tests/sensor/src/android/hardware/cts/SensorTest.java
index 80ac77d..5096b3d 100644
--- a/tests/sensor/src/android/hardware/cts/SensorTest.java
+++ b/tests/sensor/src/android/hardware/cts/SensorTest.java
@@ -250,17 +250,11 @@
sensors.get(0).isWakeUpSensor());
}
- private void hasAtLeastOneWakeupSensorOrEmpty(List<Sensor> sensors, String sensorName) {
- if (sensors == null || sensors.isEmpty()) return;
- boolean foundWakeup = false;
- for (Sensor sensor : sensors) {
- if (sensor.isWakeUpSensor()) {
- foundWakeup = true;
- break;
- }
- }
+ private void hasDefaultWakeupSensorOrEmpty(int sensorType, String sensorName) {
+ Sensor sensor = mSensorManager.getDefaultSensor(sensorType);
+ if (sensor == null) return;
- assertTrue("No wake-up " + sensorName + " sensors implemented", foundWakeup);
+ assertTrue("Default " + sensorName + " sensor is not a wake-up sensor", sensor.isWakeUpSensor());
}
// Some sensors like proximity, significant motion etc. are defined as wake-up sensors by
@@ -276,8 +270,8 @@
hasOnlyOneWakeUpSensorOrEmpty(mSensorManager.getSensorList(TYPE_GLANCE_GESTURE));
hasOnlyOneWakeUpSensorOrEmpty(mSensorManager.getSensorList(TYPE_PICK_UP_GESTURE));
- hasAtLeastOneWakeupSensorOrEmpty(mSensorManager.getSensorList(Sensor.TYPE_PROXIMITY), "proximity");
- hasAtLeastOneWakeupSensorOrEmpty(mSensorManager.getSensorList(Sensor.TYPE_HINGE_ANGLE), "hinge");
+ hasDefaultWakeupSensorOrEmpty(Sensor.TYPE_PROXIMITY, "proximity");
+ hasDefaultWakeupSensorOrEmpty(Sensor.TYPE_HINGE_ANGLE, "hinge");
}
public void testGetDefaultSensorWithWakeUpFlag() {
diff --git a/tests/signature/api-check/system-annotation/AndroidTest.xml b/tests/signature/api-check/system-annotation/AndroidTest.xml
index 691516c..816ff81 100644
--- a/tests/signature/api-check/system-annotation/AndroidTest.xml
+++ b/tests/signature/api-check/system-annotation/AndroidTest.xml
@@ -28,7 +28,7 @@
<option name="runner" value="repackaged.android.test.InstrumentationTestRunner" />
<option name="class" value="android.signature.cts.api.AnnotationTest" />
<option name="instrumentation-arg" key="expected-api-files" value="system-current.txt,system-removed.txt,car-system-current.txt,car-system-removed.txt" />
- <option name="instrumentation-arg" key="annotation-for-exact-match" value="@android.annotation.SystemApi\(client=PRIVILEGED_APPS,\ process=ALL\)" />
+ <option name="instrumentation-arg" key="annotation-for-exact-match" value="@android.annotation.SystemApi\(client=PRIVILEGED_APPS\)" />
<option name="runtime-hint" value="30s" />
</test>
diff --git a/tests/tests/app.usage/src/android/app/usage/cts/UsageStatsTest.java b/tests/tests/app.usage/src/android/app/usage/cts/UsageStatsTest.java
index b228c49..0ca1fc9 100644
--- a/tests/tests/app.usage/src/android/app/usage/cts/UsageStatsTest.java
+++ b/tests/tests/app.usage/src/android/app/usage/cts/UsageStatsTest.java
@@ -18,6 +18,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -122,24 +123,27 @@
private static final long TIME_DIFF_THRESHOLD = 200;
private static final String CHANNEL_ID = "my_channel";
-
+ private Context mContext;
private UiDevice mUiDevice;
private UsageStatsManager mUsageStatsManager;
+ private KeyguardManager mKeyguardManager;
private String mTargetPackage;
private String mCachedUsageSourceSetting;
@Before
public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getInstrumentation().getContext();
mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
- mUsageStatsManager = (UsageStatsManager) InstrumentationRegistry.getInstrumentation()
- .getContext().getSystemService(Context.USAGE_STATS_SERVICE);
- mTargetPackage = InstrumentationRegistry.getContext().getPackageName();
+ mUsageStatsManager = (UsageStatsManager) mContext.getSystemService(
+ Context.USAGE_STATS_SERVICE);
+ mKeyguardManager = mContext.getSystemService(KeyguardManager.class);
+ mTargetPackage = mContext.getPackageName();
+
assumeTrue("App Standby not enabled on device", AppStandbyUtils.isAppStandbyEnabled());
setAppOpsMode("allow");
mCachedUsageSourceSetting = getUsageSourceSetting();
}
-
@After
public void cleanUp() throws Exception {
if (mCachedUsageSourceSetting != null &&
@@ -158,33 +162,27 @@
}
private void setAppOpsMode(String mode) throws Exception {
- final String command = MessageFormat.format(APPOPS_SET_SHELL_COMMAND,
- InstrumentationRegistry.getContext().getPackageName(), mode);
- mUiDevice.executeShellCommand(command);
+ executeShellCmd(MessageFormat.format(APPOPS_SET_SHELL_COMMAND, mTargetPackage, mode));
}
-
private String getUsageSourceSetting() throws Exception {
- return mUiDevice.executeShellCommand(USAGE_SOURCE_GET_SHELL_COMMAND);
+ return executeShellCmd(USAGE_SOURCE_GET_SHELL_COMMAND);
}
private void setUsageSourceSetting(String usageSource) throws Exception {
if (usageSource.equals("null")) {
- mUiDevice.executeShellCommand(USAGE_SOURCE_DELETE_SHELL_COMMAND);
+ executeShellCmd(USAGE_SOURCE_DELETE_SHELL_COMMAND);
} else {
- final String command = MessageFormat.format(USAGE_SOURCE_SET_SHELL_COMMAND,
- usageSource);
- mUiDevice.executeShellCommand(command);
+ executeShellCmd(MessageFormat.format(USAGE_SOURCE_SET_SHELL_COMMAND, usageSource));
}
mUsageStatsManager.forceUsageSourceSettingRead();
}
private void launchSubActivity(Class<? extends Activity> clazz) {
- final Context context = InstrumentationRegistry.getInstrumentation().getContext();
final Intent intent = new Intent(Intent.ACTION_MAIN);
intent.setClassName(mTargetPackage, clazz.getName());
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- context.startActivity(intent);
+ mContext.startActivity(intent);
mUiDevice.wait(Until.hasObject(By.clazz(clazz)), TIMEOUT);
}
@@ -316,7 +314,7 @@
@Test
public void testStandbyBucketChangeLog() throws Exception {
final long startTime = System.currentTimeMillis();
- mUiDevice.executeShellCommand("am set-standby-bucket " + mTargetPackage + " rare");
+ setStandByBucket(mTargetPackage, "rare");
final long endTime = System.currentTimeMillis();
UsageEvents events = mUsageStatsManager.queryEvents(startTime - 1_000, endTime + 1_000);
@@ -342,7 +340,7 @@
assumeTrue("Skip GetAppStandby test: app standby is disabled.",
AppStandbyUtils.isAppStandbyEnabled());
- mUiDevice.executeShellCommand("am set-standby-bucket " + mTargetPackage + " rare");
+ setStandByBucket(mTargetPackage, "rare");
Map<String, Integer> bucketMap = mUsageStatsManager.getAppStandbyBuckets();
assertTrue("No bucket data returned", bucketMap.size() > 0);
final int bucket = bucketMap.getOrDefault(mTargetPackage, -1);
@@ -365,9 +363,9 @@
setAppOpsMode("ignore"); // To ensure permission is not required
// Time drifts of 2s are expected inside usage stats
final long start = System.currentTimeMillis() - 2_000;
- mUiDevice.executeShellCommand("am set-standby-bucket " + mTargetPackage + " rare");
+ setStandByBucket(mTargetPackage, "rare");
Thread.sleep(100);
- mUiDevice.executeShellCommand("am set-standby-bucket " + mTargetPackage + " working_set");
+ setStandByBucket(mTargetPackage, "working_set");
Thread.sleep(100);
final long end = System.currentTimeMillis() + 2_000;
final UsageEvents events = mUsageStatsManager.queryEventsForSelf(start, end);
@@ -537,20 +535,20 @@
assertTrue(stats.isEmpty());
}
- private void generateAndSendNotification(Context context) throws Exception {
+ private void generateAndSendNotification() throws Exception {
final NotificationManager mNotificationManager =
- (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
+ (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
final NotificationChannel mChannel = new NotificationChannel(CHANNEL_ID, "Channel",
NotificationManager.IMPORTANCE_DEFAULT);
// Configure the notification channel.
mChannel.setDescription("Test channel");
mNotificationManager.createNotificationChannel(mChannel);
final Notification.Builder mBuilder =
- new Notification.Builder(context, CHANNEL_ID)
+ new Notification.Builder(mContext, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_notification)
.setContentTitle("My notification")
.setContentText("Hello World!");
- final PendingIntent pi = PendingIntent.getActivity(context, 1,
+ final PendingIntent pi = PendingIntent.getActivity(mContext, 1,
new Intent(Settings.ACTION_SETTINGS), 0);
mBuilder.setContentIntent(pi);
mNotificationManager.notify(1, mBuilder.build());
@@ -561,15 +559,15 @@
@Test
public void testNotificationSeen() throws Exception {
final long startTime = System.currentTimeMillis();
- final Context context = InstrumentationRegistry.getContext();
// Skip the test for wearable devices and televisions; neither has a notification shade.
assumeFalse("Test cannot run on a watch- notification shade is not shown",
- context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH));
+ mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH));
assumeFalse("Test cannot run on a television- notifications are not shown",
- context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK_ONLY));
+ mContext.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_LEANBACK_ONLY));
- generateAndSendNotification(context);
+ generateAndSendNotification();
long endTime = System.currentTimeMillis();
UsageEvents events = queryEventsAsShell(startTime, endTime);
@@ -606,15 +604,15 @@
@Test
public void testNotificationInterruptionEventsObfuscation() throws Exception {
final long startTime = System.currentTimeMillis();
- final Context context = InstrumentationRegistry.getContext();
// Skip the test for wearable devices and televisions; neither has a notification shade.
assumeFalse("Test cannot run on a watch- notification shade is not shown",
- context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH));
+ mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH));
assumeFalse("Test cannot run on a television- notifications are not shown",
- context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK_ONLY));
+ mContext.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_LEANBACK_ONLY));
- generateAndSendNotification(context);
+ generateAndSendNotification();
final long endTime = System.currentTimeMillis();
final UsageEvents obfuscatedEvents = mUsageStatsManager.queryEvents(startTime, endTime);
@@ -667,7 +665,7 @@
mUsageStatsManager.getAppStandbyBucket(mTargetPackage));
// User force shouldn't have to deal with the timeout.
- mUiDevice.executeShellCommand("am set-standby-bucket " + mTargetPackage + " restricted");
+ setStandByBucket(mTargetPackage, "restricted");
assertEquals("User was unable to force an ACTIVE app down into RESTRICTED bucket",
UsageStatsManager.STANDBY_BUCKET_RESTRICTED,
mUsageStatsManager.getAppStandbyBucket(mTargetPackage));
@@ -678,7 +676,7 @@
@AppModeFull(reason = "Test APK Activity not found when installed as an instant app")
@Test
public void testUserLaunchRemovesFromRestricted() throws IOException {
- mUiDevice.executeShellCommand("am set-standby-bucket " + mTargetPackage + " restricted");
+ setStandByBucket(mTargetPackage, "restricted");
assertEquals("User was unable to force an app into RESTRICTED bucket",
UsageStatsManager.STANDBY_BUCKET_RESTRICTED,
mUsageStatsManager.getAppStandbyBucket(mTargetPackage));
@@ -691,7 +689,7 @@
@Test
public void testIsAppInactive_Charging() throws Exception {
- mUiDevice.executeShellCommand("am set-standby-bucket " + TEST_APP_PKG + " rare");
+ setStandByBucket(TEST_APP_PKG, "rare");
try {
BatteryUtils.runDumpsysBatteryUnplug();
@@ -707,17 +705,17 @@
}
}
- static final int[] INTERACTIVE_EVENTS = new int[] {
+ private static final int[] INTERACTIVE_EVENTS = new int[] {
Event.SCREEN_INTERACTIVE,
Event.SCREEN_NON_INTERACTIVE
};
- static final int[] KEYGUARD_EVENTS = new int[] {
+ private static final int[] KEYGUARD_EVENTS = new int[] {
Event.KEYGUARD_SHOWN,
Event.KEYGUARD_HIDDEN
};
- static final int[] ALL_EVENTS = new int[] {
+ private static final int[] ALL_EVENTS = new int[] {
Event.SCREEN_INTERACTIVE,
Event.SCREEN_NON_INTERACTIVE,
Event.KEYGUARD_SHOWN,
@@ -803,19 +801,9 @@
final AggrEventData nonInteractive = new AggrEventData("Non-interactive");
final AggrEventData keyguardShown = new AggrEventData("Keyguard shown");
final AggrEventData keyguardHidden = new AggrEventData("Keyguard hidden");
- int interactiveCount;
- long interactiveDuration;
- long interactiveLastEventTime;
- int nonInteractiveCount;
- long nonInteractiveDuration;
- long nonInteractiveLastEventTime;
- int keyguardShownCount;
- long keyguardShownDuration;
- int keyguardHiddenCount;
- long keyguardHiddenDuration;
}
- private SparseArray<AggrAllEventsData> getAggrEventData(long beforeTime) {
+ private SparseArray<AggrAllEventsData> getAggrEventData() {
final long endTime = System.currentTimeMillis();
final SparseLongArray intervalLengths = new SparseLongArray();
@@ -954,49 +942,35 @@
@AppModeFull(reason = "No usage events access in instant apps")
@Test
public void testInteractiveEvents() throws Exception {
- final KeyguardManager kmgr = InstrumentationRegistry.getInstrumentation()
- .getContext().getSystemService(KeyguardManager.class);
-
// We need to start out with the screen on.
- if (!mUiDevice.isScreenOn()) {
- pressWakeUp();
- SystemClock.sleep(1000);
- }
-
- // Also want to start out with the keyguard dismissed.
- if (kmgr.isKeyguardLocked()) {
- final long startTime = getEvents(KEYGUARD_EVENTS, 0, null) + 1;
- mUiDevice.executeShellCommand("wm dismiss-keyguard");
- ArrayList<Event> events = waitForEventCount(KEYGUARD_EVENTS, startTime, 1);
- assertEquals(Event.KEYGUARD_HIDDEN, events.get(0).getEventType());
- SystemClock.sleep(500);
- }
+ mUiDevice.wakeUp();
+ dismissKeyguard(); // also want to start out with the keyguard dismissed.
try {
ArrayList<Event> events;
// Determine time to start looking for events.
final long startTime = getEvents(ALL_EVENTS, 0, null) + 1;
- SparseArray<AggrAllEventsData> baseAggr = getAggrEventData(0);
+ SparseArray<AggrAllEventsData> baseAggr = getAggrEventData();
// First test -- put device to sleep and make sure we see this event.
- pressSleep();
+ mUiDevice.sleep();
// Do we have one event, going in to non-interactive mode?
events = waitForEventCount(INTERACTIVE_EVENTS, startTime, 1);
assertEquals(Event.SCREEN_NON_INTERACTIVE, events.get(0).getEventType());
- SparseArray<AggrAllEventsData> offAggr = getAggrEventData(startTime);
+ SparseArray<AggrAllEventsData> offAggr = getAggrEventData();
verifyAggrInteractiveEventData(baseAggr, offAggr, true, false);
// Next test -- turn screen on and make sure we have a second event.
// XXX need to wait a bit so we don't accidentally trigger double-power
// to launch camera. (SHOULD FIX HOW WE WAKEUP / SLEEP TO NOT USE POWER KEY)
SystemClock.sleep(500);
- pressWakeUp();
+ mUiDevice.wakeUp();
events = waitForEventCount(INTERACTIVE_EVENTS, startTime, 2);
assertEquals(Event.SCREEN_NON_INTERACTIVE, events.get(0).getEventType());
assertEquals(Event.SCREEN_INTERACTIVE, events.get(1).getEventType());
- SparseArray<AggrAllEventsData> onAggr = getAggrEventData(startTime);
+ SparseArray<AggrAllEventsData> onAggr = getAggrEventData();
verifyAggrInteractiveEventData(offAggr, onAggr, false, true);
// If the device is doing a lock screen, verify that we are also seeing the
@@ -1005,25 +979,25 @@
// the screen back on (at which point it must be shown).
// XXX CTS seems to be preventing the keyguard from showing, so this path is
// never being tested.
- if (kmgr.isKeyguardLocked()) {
+ if (mKeyguardManager.isKeyguardLocked()) {
events = waitForEventCount(KEYGUARD_EVENTS, startTime, 1);
assertEquals(Event.KEYGUARD_SHOWN, events.get(0).getEventType());
- SparseArray<AggrAllEventsData> shownAggr = getAggrEventData(startTime);
+ SparseArray<AggrAllEventsData> shownAggr = getAggrEventData();
verifyAggrKeyguardEventData(offAggr, shownAggr, true, false);
// Now dismiss the keyguard and verify the resulting events.
- mUiDevice.executeShellCommand("wm dismiss-keyguard");
+ executeShellCmd("wm dismiss-keyguard");
events = waitForEventCount(KEYGUARD_EVENTS, startTime, 2);
assertEquals(Event.KEYGUARD_SHOWN, events.get(0).getEventType());
assertEquals(Event.KEYGUARD_HIDDEN, events.get(1).getEventType());
- SparseArray<AggrAllEventsData> hiddenAggr = getAggrEventData(startTime);
+ SparseArray<AggrAllEventsData> hiddenAggr = getAggrEventData();
verifyAggrKeyguardEventData(shownAggr, hiddenAggr, false, true);
}
} finally {
// Dismiss keyguard to get device back in its normal state.
- pressWakeUp();
- mUiDevice.executeShellCommand("wm dismiss-keyguard");
+ mUiDevice.wakeUp();
+ executeShellCmd("wm dismiss-keyguard");
}
}
@@ -1032,14 +1006,14 @@
final String fakePackageName = "android.fake.package.name";
final int defaultValue = -1;
- mUiDevice.executeShellCommand("am set-standby-bucket " + fakePackageName + " rare");
+ setStandByBucket(fakePackageName, "rare");
// Verify the above does not add a new entry to the App Standby bucket map
Map<String, Integer> bucketMap = mUsageStatsManager.getAppStandbyBuckets();
int bucket = bucketMap.getOrDefault(fakePackageName, defaultValue);
assertFalse("Meaningful bucket value " + bucket + " returned for " + fakePackageName
+ " after set-standby-bucket", bucket > 0);
- mUiDevice.executeShellCommand("am get-standby-bucket " + fakePackageName);
+ executeShellCmd("am get-standby-bucket " + fakePackageName);
// Verify the above does not add a new entry to the App Standby bucket map
bucketMap = mUsageStatsManager.getAppStandbyBuckets();
bucket = bucketMap.getOrDefault(fakePackageName, defaultValue);
@@ -1109,12 +1083,11 @@
// This test start a foreground service then stop it. The event list should have one
// FOREGROUND_SERVICE_START and one FOREGROUND_SERVICE_STOP event.
final long startTime = System.currentTimeMillis();
- final Context context = InstrumentationRegistry.getInstrumentation().getContext();
- context.startService(new Intent(context, TestService.class));
+ mContext.startService(new Intent(mContext, TestService.class));
mUiDevice.wait(Until.hasObject(By.clazz(TestService.class)), TIMEOUT);
final long sleepTime = 500;
SystemClock.sleep(sleepTime);
- context.stopService(new Intent(context, TestService.class));
+ mContext.stopService(new Intent(mContext, TestService.class));
mUiDevice.wait(Until.gone(By.clazz(TestService.class)), TIMEOUT);
final long endTime = System.currentTimeMillis();
UsageEvents events = mUsageStatsManager.queryEvents(startTime, endTime);
@@ -1161,17 +1134,8 @@
@AppModeFull(reason = "No usage events access in instant apps")
@Test
public void testTaskRootEventField() throws Exception {
- final KeyguardManager kmgr = InstrumentationRegistry.getInstrumentation()
- .getContext().getSystemService(KeyguardManager.class);
mUiDevice.wakeUp();
- // Also want to start out with the keyguard dismissed.
- if (kmgr.isKeyguardLocked()) {
- final long startTime = getEvents(KEYGUARD_EVENTS, 0, null) + 1;
- mUiDevice.executeShellCommand("wm dismiss-keyguard");
- ArrayList<Event> events = waitForEventCount(KEYGUARD_EVENTS, startTime, 1);
- assertEquals(Event.KEYGUARD_HIDDEN, events.get(0).getEventType());
- SystemClock.sleep(500);
- }
+ dismissKeyguard(); // also want to start out with the keyguard dismissed.
final long startTime = System.currentTimeMillis();
launchSubActivity(TaskRootActivity.class);
@@ -1195,29 +1159,18 @@
@AppModeFull(reason = "No usage events access in instant apps")
@Test
public void testUsageSourceAttribution() throws Exception {
- final KeyguardManager kmgr = InstrumentationRegistry.getInstrumentation()
- .getContext().getSystemService(KeyguardManager.class);
mUiDevice.wakeUp();
- // Also want to start out with the keyguard dismissed.
- if (kmgr.isKeyguardLocked()) {
- final long startTime = getEvents(KEYGUARD_EVENTS, 0, null) + 1;
- mUiDevice.executeShellCommand("wm dismiss-keyguard");
- ArrayList<Event> events = waitForEventCount(KEYGUARD_EVENTS, startTime, 1);
- assertEquals(Event.KEYGUARD_HIDDEN, events.get(0).getEventType());
- SystemClock.sleep(500);
- }
-
+ dismissKeyguard(); // also want to start out with the keyguard dismissed.
mUiDevice.pressHome();
- setUsageSourceSetting(Integer.toString(mUsageStatsManager.USAGE_SOURCE_CURRENT_ACTIVITY));
+ setUsageSourceSetting(Integer.toString(UsageStatsManager.USAGE_SOURCE_CURRENT_ACTIVITY));
launchSubActivity(TaskRootActivity.class);
// Usage should be attributed to the test app package
assertAppOrTokenUsed(TaskRootActivity.TEST_APP_PKG, true);
mUiDevice.pressHome();
- setUsageSourceSetting(Integer.toString(
- mUsageStatsManager.USAGE_SOURCE_TASK_ROOT_ACTIVITY));
+ setUsageSourceSetting(Integer.toString(UsageStatsManager.USAGE_SOURCE_TASK_ROOT_ACTIVITY));
launchSubActivity(TaskRootActivity.class);
// Usage should be attributed to this package
assertAppOrTokenUsed(mTargetPackage, true);
@@ -1248,13 +1201,12 @@
int stops = 0;
// Only look at events belongs to mTargetPackage.
- ArrayList<UsageEvents.Event> eventList = new ArrayList<>();
while (events.hasNextEvent()) {
final UsageEvents.Event event = new UsageEvents.Event();
assertTrue(events.getNextEvent(event));
// There should be no events with this packages name
- assertFalse("Instant app package name found in usage event list",
- mTargetPackage.equals(event.getPackageName()));
+ assertNotEquals("Instant app package name found in usage event list",
+ mTargetPackage, event.getPackageName());
// Look for the obfuscated instant app string instead
if(UsageEvents.INSTANT_APP_PACKAGE_NAME.equals(event.getPackageName())) {
@@ -1279,28 +1231,17 @@
@AppModeFull(reason = "No usage events access in instant apps")
@Test
public void testSuddenDestroy() throws Exception {
- final Context context = InstrumentationRegistry.getInstrumentation().getContext();
- final KeyguardManager kmgr = InstrumentationRegistry.getInstrumentation()
- .getContext().getSystemService(KeyguardManager.class);
mUiDevice.wakeUp();
- // Also want to start out with the keyguard dismissed.
- if (kmgr.isKeyguardLocked()) {
- final long startTime = getEvents(KEYGUARD_EVENTS, 0, null) + 1;
- mUiDevice.executeShellCommand("wm dismiss-keyguard");
- ArrayList<Event> events = waitForEventCount(KEYGUARD_EVENTS, startTime, 1);
- assertEquals(Event.KEYGUARD_HIDDEN, events.get(0).getEventType());
- SystemClock.sleep(500);
- }
-
+ dismissKeyguard(); // also want to start out with the keyguard dismissed.
mUiDevice.pressHome();
final long startTime = System.currentTimeMillis();
- final ActivityManager mAm = context.getSystemService(ActivityManager.class);
+ final ActivityManager mAm = mContext.getSystemService(ActivityManager.class);
Intent intent = new Intent();
intent.setClassName(TEST_APP_PKG, TEST_APP_CLASS);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- context.startActivity(intent);
+ mContext.startActivity(intent);
mUiDevice.wait(Until.hasObject(By.clazz(TEST_APP_PKG, TEST_APP_CLASS)), TIMEOUT);
SystemClock.sleep(500);
@@ -1352,13 +1293,12 @@
}
private void startAndDestroyActivityWithLocus() {
- final Context context = InstrumentationRegistry.getInstrumentation().getContext();
- final ActivityManager mAm = context.getSystemService(ActivityManager.class);
+ final ActivityManager mAm = mContext.getSystemService(ActivityManager.class);
Intent intent = new Intent();
intent.setClassName(TEST_APP_PKG, TEST_APP_CLASS_LOCUS);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- context.startActivity(intent);
+ mContext.startActivity(intent);
mUiDevice.wait(Until.hasObject(By.clazz(TEST_APP_PKG, TEST_APP_CLASS_LOCUS)), TIMEOUT);
SystemClock.sleep(500);
@@ -1387,22 +1327,13 @@
}
}
- private void pressWakeUp() {
- mUiDevice.pressKeyCode(KeyEvent.KEYCODE_WAKEUP);
- }
-
- private void pressSleep() {
- mUiDevice.pressKeyCode(KeyEvent.KEYCODE_SLEEP);
- }
-
/**
* Assert on an app or token's usage state.
* @param entity name of the app or token
* @param expected expected usage state, true for in use, false for not in use
*/
private void assertAppOrTokenUsed(String entity, boolean expected) throws IOException {
- final String activeUsages =
- mUiDevice.executeShellCommand("dumpsys usagestats apptimelimit actives");
+ final String activeUsages = executeShellCmd("dumpsys usagestats apptimelimit actives");
final String[] actives = activeUsages.split("\n");
boolean found = false;
@@ -1421,6 +1352,24 @@
}
}
+ private void dismissKeyguard() throws Exception {
+ if (mKeyguardManager.isKeyguardLocked()) {
+ final long startTime = getEvents(KEYGUARD_EVENTS, 0, null) + 1;
+ executeShellCmd("wm dismiss-keyguard");
+ final ArrayList<Event> events = waitForEventCount(KEYGUARD_EVENTS, startTime, 1);
+ assertEquals(Event.KEYGUARD_HIDDEN, events.get(0).getEventType());
+ SystemClock.sleep(500);
+ }
+ }
+
+ private void setStandByBucket(String packageName, String bucket) throws IOException {
+ executeShellCmd("am set-standby-bucket " + packageName + " " + bucket);
+ }
+
+ private String executeShellCmd(String command) throws IOException {
+ return mUiDevice.executeShellCommand(command);
+ }
+
private UsageEvents queryEventsAsShell(long start, long end) {
return SystemUtil.runWithShellPermissionIdentity(() ->
mUsageStatsManager.queryEvents(start, end));
diff --git a/tests/tests/carrierapi/src/android/carrierapi/cts/CarrierApiTest.java b/tests/tests/carrierapi/src/android/carrierapi/cts/CarrierApiTest.java
index 929eae0..2ce1d39 100644
--- a/tests/tests/carrierapi/src/android/carrierapi/cts/CarrierApiTest.java
+++ b/tests/tests/carrierapi/src/android/carrierapi/cts/CarrierApiTest.java
@@ -131,6 +131,7 @@
private static final String ALPHA_TAG_B = "tagB";
private static final String NUMBER_A = "1234567890";
private static final String NUMBER_B = "0987654321";
+ private static final String TESTING_PLMN = "12345";
private static final int DSDS_PHONE_COUNT = 2;
@@ -485,6 +486,7 @@
mTelephonyManager.getVoiceMailAlphaTag();
mTelephonyManager.getForbiddenPlmns();
mTelephonyManager.getServiceState();
+ mTelephonyManager.getManualNetworkSelectionPlmn();
mTelephonyManager.setForbiddenPlmns(new ArrayList<String>());
} catch (SecurityException e) {
failMessage();
@@ -553,6 +555,19 @@
static final int CARRIER_PRIVILEGE_LISTENERS =
READ_PHONE_STATE_LISTENERS | READ_PRECISE_PHONE_STATE_LISTENERS;
+ public void testGetManualNetworkSelectionPlmnPersisted() throws Exception {
+ if (!hasCellular) return;
+
+ try {
+ mTelephonyManager.setNetworkSelectionModeManual(
+ TESTING_PLMN/* operatorNumeric */, true /* persistSelection */);
+ String plmn = mTelephonyManager.getManualNetworkSelectionPlmn();
+ assertEquals(TESTING_PLMN, plmn);
+ } finally {
+ mTelephonyManager.setNetworkSelectionModeAutomatic();
+ }
+ }
+
public void testPhoneStateListener() throws Exception {
if (!hasCellular) return;
PhoneStateListener psl = new PhoneStateListener((Runnable r) -> { });
@@ -563,6 +578,29 @@
}
}
+ public void testIsManualNetworkSelectionAllowed() throws Exception {
+ if (!hasCellular) return;
+
+ try {
+ assertTrue(mTelephonyManager.isManualNetworkSelectionAllowed());
+ } catch (SecurityException e) {
+ failMessage();
+ }
+ }
+
+ public void testGetNetworkSelectionMode() throws Exception {
+ if (!hasCellular) return;
+
+ try {
+ ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager,
+ (tm) -> tm.setNetworkSelectionModeAutomatic());
+ int networkMode = mTelephonyManager.getNetworkSelectionMode();
+ assertEquals(TelephonyManager.NETWORK_SELECTION_MODE_AUTO, networkMode);
+ } catch (SecurityException e) {
+ failMessage();
+ }
+ }
+
public void testSubscriptionInfoChangeListener() throws Exception {
if (!hasCellular) return;
final AtomicReference<SecurityException> error = new AtomicReference<>();
diff --git a/tests/tests/content/OWNERS b/tests/tests/content/OWNERS
new file mode 100644
index 0000000..12c955a
--- /dev/null
+++ b/tests/tests/content/OWNERS
@@ -0,0 +1,5 @@
+# Bug component: 36137
+toddke@google.com
+patb@google.com
+schfan@google.com
+alexbuy@google.com
diff --git a/tests/tests/content/pm/SecureFrp/AndroidManifest.xml b/tests/tests/content/pm/SecureFrp/AndroidManifest.xml
index f1b5184..9e45d4a 100644
--- a/tests/tests/content/pm/SecureFrp/AndroidManifest.xml
+++ b/tests/tests/content/pm/SecureFrp/AndroidManifest.xml
@@ -18,6 +18,7 @@
package="com.android.tests.securefrpinstall" >
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
+ <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
<application>
<receiver android:name="com.android.cts.install.lib.LocalIntentSender"
diff --git a/tests/tests/content/res/xml/intentfilter.xml b/tests/tests/content/res/xml/intentfilter.xml
index 7ed3a53..9041513 100644
--- a/tests/tests/content/res/xml/intentfilter.xml
+++ b/tests/tests/content/res/xml/intentfilter.xml
@@ -20,6 +20,8 @@
<action name="testAction"/>
<cat name="testCategory" />
<type name="vnd.android.cursor.dir/person"/>
+ <staticType name="text/plain"/>
+ <group name="testMimeGroup"/>
<scheme name="testScheme"/>
<auth host="testHost" port="80"/>
<path name="testPath" literal="test"/>
diff --git a/tests/tests/content/src/android/content/cts/IntentFilterTest.java b/tests/tests/content/src/android/content/cts/IntentFilterTest.java
index 80f47f9..6248044 100644
--- a/tests/tests/content/src/android/content/cts/IntentFilterTest.java
+++ b/tests/tests/content/src/android/content/cts/IntentFilterTest.java
@@ -20,18 +20,6 @@
import static android.os.PatternMatcher.PATTERN_PREFIX;
import static android.os.PatternMatcher.PATTERN_SIMPLE_GLOB;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Set;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Intent;
@@ -52,14 +40,28 @@
import com.android.internal.util.FastXmlSerializer;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
public class IntentFilterTest extends AndroidTestCase {
private IntentFilter mIntentFilter;
private static final String ACTION = "testAction";
private static final String CATEGORY = "testCategory";
- private static final String DATA_TYPE = "vnd.android.cursor.dir/person";
+ private static final String DATA_DYNAMIC_TYPE = "type/dynamic";
+ private static final String DATA_STATIC_TYPE = "vnd.android.cursor.dir/person";
private static final String DATA_SCHEME = "testDataSchemes.";
+ private static final String MIME_GROUP = "mime_group";
private static final String SSP = "testSsp";
private static final String HOST = "testHost";
private static final int PORT = 80;
@@ -80,11 +82,11 @@
filter = new IntentFilter(ACTION);
verifyContent(filter, ACTION, null);
- final IntentFilter actionTypeFilter = new IntentFilter(ACTION, DATA_TYPE);
- verifyContent(actionTypeFilter, ACTION, DATA_TYPE);
+ final IntentFilter actionTypeFilter = new IntentFilter(ACTION, DATA_STATIC_TYPE);
+ verifyContent(actionTypeFilter, ACTION, DATA_STATIC_TYPE);
filter = new IntentFilter(actionTypeFilter);
- verifyContent(filter, ACTION, DATA_TYPE);
+ verifyContent(filter, ACTION, DATA_STATIC_TYPE);
final String dataType = "testdataType";
try {
@@ -110,8 +112,10 @@
if (dataType != null) {
assertEquals(1, filter.countDataTypes());
assertEquals(dataType, filter.getDataType(0));
+ assertEquals(1, filter.countStaticDataTypes());
} else {
assertEquals(0, filter.countDataTypes());
+ assertEquals(0, filter.countStaticDataTypes());
}
}
@@ -215,6 +219,125 @@
null), });
}
+ public void testDynamicMimeTypes() {
+ IntentFilter filter = new Match()
+ .addDynamicMimeTypes(new String[] { "which1/what1" });
+ checkMatches(filter, new MatchCondition[] {
+ new MatchCondition(IntentFilter.NO_MATCH_TYPE, null, null, null, null),
+ new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "which1/what1",
+ null),
+ new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "which1/*", null),
+ new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "*/*", null),
+ new MatchCondition(IntentFilter.NO_MATCH_TYPE, null, null, "which2/what2", null),
+ new MatchCondition(IntentFilter.NO_MATCH_TYPE, null, null, "which2/*", null),
+ new MatchCondition(IntentFilter.NO_MATCH_TYPE, null, null, "which1/what2", null),
+ });
+
+ filter = new Match()
+ .addDynamicMimeTypes(new String[] { "which1/what1", "which2/what2" });
+ checkMatches(filter, new MatchCondition[] {
+ new MatchCondition(IntentFilter.NO_MATCH_TYPE, null, null, null, null),
+ new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "which1/what1",
+ null),
+ new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "which1/*", null),
+ new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "*/*", null),
+ new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "which2/what2",
+ null),
+ new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "which2/*", null),
+ new MatchCondition(IntentFilter.NO_MATCH_TYPE, null, null, "which1/what2", null),
+ new MatchCondition(IntentFilter.NO_MATCH_TYPE, null, null, "which3/what3", null),
+ });
+
+ filter = new Match()
+ .addDynamicMimeTypes(new String[] { "which1/*" });
+ checkMatches(filter, new MatchCondition[] {
+ new MatchCondition(IntentFilter.NO_MATCH_TYPE, null, null, null, null),
+ new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "which1/what1",
+ null),
+ new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "which1/*", null),
+ new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "*/*", null),
+ new MatchCondition(IntentFilter.NO_MATCH_TYPE, null, null, "which2/what2", null),
+ new MatchCondition(IntentFilter.NO_MATCH_TYPE, null, null, "which2/*", null),
+ new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "which1/what2",
+ null),
+ new MatchCondition(IntentFilter.NO_MATCH_TYPE, null, null, "which3/what3", null),
+ });
+
+ filter = new Match()
+ .addDynamicMimeTypes(new String[] { "*/*" });
+ checkMatches(filter, new MatchCondition[] {
+ new MatchCondition(IntentFilter.NO_MATCH_TYPE, null, null, null, null),
+ new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "which1/what1",
+ null),
+ new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "which1/*", null),
+ new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "*/*", null),
+ new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "which2/what2",
+ null),
+ new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "which2/*", null),
+ new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "which1/what2",
+ null),
+ new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "which3/what3",
+ null), });
+ }
+
+ public void testClearDynamicMimeTypesWithStaticType() {
+ IntentFilter filter = new Match()
+ .addMimeTypes(new String[] {"which1/what1"})
+ .addDynamicMimeTypes(new String[] { "which2/what2" });
+
+ checkMatches(filter, new MatchCondition[] {
+ new MatchCondition(IntentFilter.NO_MATCH_TYPE, null, null, null, null),
+ new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "which1/what1",
+ null),
+ new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "which1/*", null),
+ new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "*/*", null),
+ new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "which2/what2",
+ null),
+ new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "which2/*", null),
+ new MatchCondition(IntentFilter.NO_MATCH_TYPE, null, null, "which1/what2", null),
+ new MatchCondition(IntentFilter.NO_MATCH_TYPE, null, null, "which3/what3", null),
+ });
+
+ filter.clearDynamicDataTypes();
+
+ checkMatches(filter, new MatchCondition[] {
+ new MatchCondition(IntentFilter.NO_MATCH_TYPE, null, null, null, null),
+ new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "which1/what1",
+ null),
+ new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "which1/*", null),
+ new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "*/*", null),
+ new MatchCondition(IntentFilter.NO_MATCH_TYPE, null, null, "which2/what2", null),
+ new MatchCondition(IntentFilter.NO_MATCH_TYPE, null, null, "which2/*", null),
+ new MatchCondition(IntentFilter.NO_MATCH_TYPE, null, null, "which1/what2", null),
+ });
+ }
+
+ public void testClearDynamicMimeTypesWithAction() {
+ IntentFilter filter = new Match()
+ .addActions(new String[] {"action1"})
+ .addDynamicMimeTypes(new String[] { "which1/what1" });
+
+ checkMatches(filter, new MatchCondition[] {
+ new MatchCondition(IntentFilter.NO_MATCH_TYPE, null, null, null, null),
+ new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "which1/what1",
+ null),
+ new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "which1/*", null),
+ new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "*/*", null),
+ new MatchCondition(IntentFilter.NO_MATCH_TYPE, null, null, "which2/what2", null),
+ new MatchCondition(IntentFilter.NO_MATCH_TYPE, null, null, "which2/*", null),
+ new MatchCondition(IntentFilter.NO_MATCH_TYPE, null, null, "which1/what2", null),
+ });
+
+ filter.clearDynamicDataTypes();
+
+ checkMatches(filter, new MatchCondition[] {
+ new MatchCondition(IntentFilter.MATCH_CATEGORY_EMPTY, null, null, null, null),
+ new MatchCondition(IntentFilter.MATCH_CATEGORY_EMPTY, "action1", null, null, null),
+ new MatchCondition(IntentFilter.NO_MATCH_TYPE, "action1", null, "which2/what2",
+ null),
+ });
+ }
+
public void testAccessPriority() {
final int expected = 1;
mIntentFilter.setPriority(expected);
@@ -255,9 +378,9 @@
}
public void testCreate() {
- IntentFilter filter = IntentFilter.create(ACTION, DATA_TYPE);
+ IntentFilter filter = IntentFilter.create(ACTION, DATA_STATIC_TYPE);
assertNotNull(filter);
- verifyContent(filter, ACTION, DATA_TYPE);
+ verifyContent(filter, ACTION, DATA_STATIC_TYPE);
}
@@ -492,18 +615,83 @@
public void testDataTypes() throws MalformedMimeTypeException {
for (int i = 0; i < 10; i++) {
- mIntentFilter.addDataType(DATA_TYPE + i);
+ mIntentFilter.addDataType(DATA_STATIC_TYPE + i);
}
assertEquals(10, mIntentFilter.countDataTypes());
+ assertEquals(10, mIntentFilter.countStaticDataTypes());
final Iterator<String> iter = mIntentFilter.typesIterator();
+ String actual;
+ int i = 0;
+ while (iter.hasNext()) {
+ actual = iter.next();
+ assertEquals(DATA_STATIC_TYPE + i, actual);
+ assertEquals(DATA_STATIC_TYPE + i, mIntentFilter.getDataType(i));
+ assertTrue(mIntentFilter.hasDataType(DATA_STATIC_TYPE + i));
+ assertFalse(mIntentFilter.hasDataType(DATA_STATIC_TYPE + i + 10));
+ i++;
+ }
+ }
+
+ public void testDynamicDataTypes() throws MalformedMimeTypeException {
+ for (int i = 0; i < 10; i++) {
+ mIntentFilter.addDynamicDataType(DATA_DYNAMIC_TYPE + i);
+ }
+ assertEquals(10, mIntentFilter.countDataTypes());
+ assertEquals(0, mIntentFilter.countStaticDataTypes());
+
+ final Iterator<String> iter = mIntentFilter.typesIterator();
+ String actual;
+ int i = 0;
+ while (iter.hasNext()) {
+ actual = iter.next();
+ assertEquals(DATA_DYNAMIC_TYPE + i, actual);
+ assertEquals(DATA_DYNAMIC_TYPE + i, mIntentFilter.getDataType(i));
+ assertTrue(mIntentFilter.hasDataType(DATA_DYNAMIC_TYPE + i));
+ assertFalse(mIntentFilter.hasDataType(DATA_DYNAMIC_TYPE + i + 10));
+ i++;
+ }
+ }
+
+ public void testClearDynamicDataTypes() throws MalformedMimeTypeException {
+ for (int i = 0; i < 10; i++) {
+ mIntentFilter.addDataType(DATA_STATIC_TYPE + i);
+ mIntentFilter.addDynamicDataType(DATA_DYNAMIC_TYPE + i);
+ }
+ assertEquals(20, mIntentFilter.countDataTypes());
+ assertEquals(10, mIntentFilter.countStaticDataTypes());
+
+ mIntentFilter.clearDynamicDataTypes();
+
+ assertEquals(10, mIntentFilter.countDataTypes());
+ assertEquals(10, mIntentFilter.countStaticDataTypes());
+
+ final Iterator<String> iter = mIntentFilter.typesIterator();
+ String actual;
+ int i = 0;
+ while (iter.hasNext()) {
+ actual = iter.next();
+ assertEquals(DATA_STATIC_TYPE + i, actual);
+ assertEquals(DATA_STATIC_TYPE + i, mIntentFilter.getDataType(i));
+ assertTrue(mIntentFilter.hasDataType(DATA_STATIC_TYPE + i));
+ assertFalse(mIntentFilter.hasDataType(DATA_DYNAMIC_TYPE + i));
+ i++;
+ }
+ }
+
+ public void testMimeGroups() {
+ for (int i = 0; i < 10; i++) {
+ mIntentFilter.addMimeGroup(MIME_GROUP + i);
+ }
+ assertEquals(10, mIntentFilter.countMimeGroups());
+ final Iterator<String> iter = mIntentFilter.mimeGroupsIterator();
String actual = null;
int i = 0;
while (iter.hasNext()) {
actual = iter.next();
- assertEquals(DATA_TYPE + i, actual);
- assertEquals(DATA_TYPE + i, mIntentFilter.getDataType(i));
- assertTrue(mIntentFilter.hasDataType(DATA_TYPE + i));
- assertFalse(mIntentFilter.hasDataType(DATA_TYPE + i + 10));
+ assertEquals(MIME_GROUP + i, actual);
+ assertEquals(MIME_GROUP + i, mIntentFilter.getMimeGroup(i));
+ assertTrue(mIntentFilter.hasMimeGroup(MIME_GROUP + i));
+ assertFalse(mIntentFilter.hasMimeGroup(MIME_GROUP + i + 10));
i++;
}
}
@@ -514,22 +702,23 @@
assertEquals(expected, mIntentFilter.matchData(null, DATA_SCHEME, null));
assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.matchData(null, DATA_SCHEME, URI));
- assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.matchData(DATA_TYPE, DATA_SCHEME,
- URI));
+ assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.matchData(DATA_STATIC_TYPE,
+ DATA_SCHEME, URI));
mIntentFilter.addDataScheme(DATA_SCHEME);
- assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.matchData(DATA_TYPE,
+ assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.matchData(DATA_STATIC_TYPE,
"mDataSchemestest", URI));
- assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.matchData(DATA_TYPE, "", URI));
+ assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.matchData(DATA_STATIC_TYPE, "",
+ URI));
expected = IntentFilter.MATCH_CATEGORY_SCHEME + IntentFilter.MATCH_ADJUSTMENT_NORMAL;
assertEquals(expected, mIntentFilter.matchData(null, DATA_SCHEME, URI));
- assertEquals(IntentFilter.NO_MATCH_TYPE, mIntentFilter.matchData(DATA_TYPE, DATA_SCHEME,
- URI));
+ assertEquals(IntentFilter.NO_MATCH_TYPE, mIntentFilter.matchData(DATA_STATIC_TYPE,
+ DATA_SCHEME, URI));
- mIntentFilter.addDataType(DATA_TYPE);
+ mIntentFilter.addDataType(DATA_STATIC_TYPE);
assertEquals(IntentFilter.MATCH_CATEGORY_TYPE + IntentFilter.MATCH_ADJUSTMENT_NORMAL,
- mIntentFilter.matchData(DATA_TYPE, DATA_SCHEME, URI));
+ mIntentFilter.matchData(DATA_STATIC_TYPE, DATA_SCHEME, URI));
mIntentFilter.addDataAuthority(HOST, String.valueOf(PORT));
assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.matchData(null, DATA_SCHEME, URI));
@@ -584,7 +773,9 @@
mIntentFilter.addDataAuthority(HOST, String.valueOf(PORT));
mIntentFilter.addDataPath(DATA_PATH, 1);
mIntentFilter.addDataScheme(DATA_SCHEME);
- mIntentFilter.addDataType(DATA_TYPE);
+ mIntentFilter.addDataType(DATA_STATIC_TYPE);
+ mIntentFilter.addDynamicDataType(DATA_DYNAMIC_TYPE);
+ mIntentFilter.addMimeGroup(MIME_GROUP);
mIntentFilter.writeToXml(xml);
xml.flush();
final XmlPullParser parser = Xml.newPullParser();
@@ -594,7 +785,9 @@
intentFilter.readFromXml(parser);
assertEquals(ACTION, intentFilter.getAction(0));
assertEquals(CATEGORY, intentFilter.getCategory(0));
- assertEquals(DATA_TYPE, intentFilter.getDataType(0));
+ assertTrue(intentFilter.hasExactStaticDataType(DATA_STATIC_TYPE));
+ assertTrue(intentFilter.hasExactDynamicDataType(DATA_DYNAMIC_TYPE));
+ assertEquals(MIME_GROUP, intentFilter.getMimeGroup(0));
assertEquals(DATA_SCHEME, intentFilter.getDataScheme(0));
assertEquals(DATA_PATH, intentFilter.getDataPath(0).getPath());
assertEquals(HOST, intentFilter.getDataAuthority(0).getHost());
@@ -655,7 +848,11 @@
assertEquals("testAction", mIntentFilter.getAction(0));
assertEquals("testCategory", mIntentFilter.getCategory(0));
- assertEquals("vnd.android.cursor.dir/person", mIntentFilter.getDataType(0));
+
+ assertTrue(mIntentFilter.hasExactDynamicDataType("vnd.android.cursor.dir/person"));
+ assertTrue(mIntentFilter.hasExactStaticDataType("text/plain"));
+
+ assertEquals("testMimeGroup", mIntentFilter.getMimeGroup(0));
assertEquals("testScheme", mIntentFilter.getDataScheme(0));
assertEquals("testHost", mIntentFilter.getDataAuthority(0).getHost());
assertEquals(80, mIntentFilter.getDataAuthority(0).getPort());
@@ -781,10 +978,10 @@
assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.match(resolver, intent, true, null));
mIntentFilter.addDataAuthority(HOST, String.valueOf(PORT));
assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.match(resolver, intent, true, null));
- intent.setType(DATA_TYPE);
+ intent.setType(DATA_STATIC_TYPE);
assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.match(resolver, intent, true, null));
- mIntentFilter.addDataType(DATA_TYPE);
+ mIntentFilter.addDataType(DATA_STATIC_TYPE);
assertEquals(IntentFilter.MATCH_CATEGORY_TYPE + IntentFilter.MATCH_ADJUSTMENT_NORMAL,
mIntentFilter.match(resolver, intent, true, null));
@@ -797,7 +994,7 @@
assertEquals(IntentFilter.MATCH_CATEGORY_TYPE + IntentFilter.MATCH_ADJUSTMENT_NORMAL,
mIntentFilter.match(resolver, intent, true, null));
- intent.setDataAndType(uri, DATA_TYPE);
+ intent.setDataAndType(uri, DATA_STATIC_TYPE);
assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.match(resolver, intent, true, null));
}
@@ -815,18 +1012,18 @@
assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.matchData(null, DATA_SCHEME, URI));
- assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.match(ACTION, DATA_TYPE,
+ assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.match(ACTION, DATA_STATIC_TYPE,
DATA_SCHEME, URI, null, null));
mIntentFilter.addDataScheme(DATA_SCHEME);
- assertEquals(IntentFilter.NO_MATCH_TYPE, mIntentFilter.match(ACTION, DATA_TYPE,
+ assertEquals(IntentFilter.NO_MATCH_TYPE, mIntentFilter.match(ACTION, DATA_STATIC_TYPE,
DATA_SCHEME, URI, null, null));
- assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.match(ACTION, DATA_TYPE, "", URI,
- null, null));
- mIntentFilter.addDataType(DATA_TYPE);
+ assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.match(ACTION, DATA_STATIC_TYPE, "",
+ URI, null, null));
+ mIntentFilter.addDataType(DATA_STATIC_TYPE);
assertEquals(IntentFilter.MATCH_CATEGORY_TYPE + IntentFilter.MATCH_ADJUSTMENT_NORMAL,
- mIntentFilter.match(ACTION, DATA_TYPE, DATA_SCHEME, URI, null, null));
+ mIntentFilter.match(ACTION, DATA_STATIC_TYPE, DATA_SCHEME, URI, null, null));
assertEquals(IntentFilter.NO_MATCH_TYPE, mIntentFilter.match(ACTION, null, DATA_SCHEME,
URI, null, null));
@@ -835,29 +1032,29 @@
URI, cat, null));
cat.add(CATEGORY);
- assertEquals(IntentFilter.NO_MATCH_CATEGORY, mIntentFilter.match(ACTION, DATA_TYPE,
+ assertEquals(IntentFilter.NO_MATCH_CATEGORY, mIntentFilter.match(ACTION, DATA_STATIC_TYPE,
DATA_SCHEME, URI, cat, null));
cat = new HashSet<String>();
mIntentFilter.addDataAuthority(HOST, String.valueOf(PORT));
assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.match(ACTION, null, DATA_SCHEME,
URI, null, null));
- assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.match(ACTION, DATA_TYPE,
+ assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.match(ACTION, DATA_STATIC_TYPE,
DATA_SCHEME, URI, null, null));
final Uri uri = Uri.parse(DATA_SCHEME + "://" + HOST + ":" + PORT);
mIntentFilter.addDataPath(DATA_PATH, PatternMatcher.PATTERN_LITERAL);
- assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.match(ACTION, DATA_TYPE,
+ assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.match(ACTION, DATA_STATIC_TYPE,
DATA_SCHEME, uri, null, null));
- assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.match(ACTION, DATA_TYPE,
+ assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.match(ACTION, DATA_STATIC_TYPE,
DATA_SCHEME, URI, null, null));
- assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.match(ACTION, DATA_TYPE,
+ assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.match(ACTION, DATA_STATIC_TYPE,
DATA_SCHEME, URI, cat, null));
cat.add(CATEGORY);
- assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.match(ACTION, DATA_TYPE,
+ assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.match(ACTION, DATA_STATIC_TYPE,
DATA_SCHEME, URI, cat, null));
mIntentFilter.addCategory(CATEGORY);
- assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.match(ACTION, DATA_TYPE,
+ assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.match(ACTION, DATA_STATIC_TYPE,
DATA_SCHEME, URI, cat, null));
}
@@ -867,7 +1064,9 @@
mIntentFilter.addDataAuthority(HOST, String.valueOf(PORT));
mIntentFilter.addDataPath(DATA_PATH, 1);
mIntentFilter.addDataScheme(DATA_SCHEME);
- mIntentFilter.addDataType(DATA_TYPE);
+ mIntentFilter.addDataType(DATA_STATIC_TYPE);
+ mIntentFilter.addDynamicDataType(DATA_DYNAMIC_TYPE);
+ mIntentFilter.addMimeGroup(MIME_GROUP);
Parcel parcel = Parcel.obtain();
mIntentFilter.writeToParcel(parcel, 1);
parcel.setDataPosition(0);
@@ -880,6 +1079,11 @@
target.getDataAuthority(0).getPort());
assertEquals(mIntentFilter.getDataPath(0).getPath(), target.getDataPath(0).getPath());
assertEquals(mIntentFilter.getDataScheme(0), target.getDataScheme(0));
+ assertEquals(mIntentFilter.getDataType(0), target.getDataType(0));
+ assertEquals(mIntentFilter.getDataType(1), target.getDataType(1));
+ assertEquals(mIntentFilter.countStaticDataTypes(), target.countStaticDataTypes());
+ assertEquals(mIntentFilter.countDataTypes(), target.countDataTypes());
+ assertEquals(mIntentFilter.getMimeGroup(0), target.getMimeGroup(0));
}
public void testAddDataType() throws MalformedMimeTypeException {
@@ -890,17 +1094,18 @@
// expected
}
- mIntentFilter.addDataType(DATA_TYPE);
- assertEquals(DATA_TYPE, mIntentFilter.getDataType(0));
+ mIntentFilter.addDataType(DATA_STATIC_TYPE);
+ assertEquals(DATA_STATIC_TYPE, mIntentFilter.getDataType(0));
}
private static class Match extends IntentFilter {
+ Match() {
+ }
+
Match(String[] actions, String[] categories, String[] mimeTypes, String[] schemes,
String[] authorities, String[] ports) {
if (actions != null) {
- for (int i = 0; i < actions.length; i++) {
- addAction(actions[i]);
- }
+ addActions(actions);
}
if (categories != null) {
for (int i = 0; i < categories.length; i++) {
@@ -948,6 +1153,35 @@
}
}
}
+
+ Match addDynamicMimeTypes(String[] dynamicMimeTypes) {
+ for (int i = 0; i < dynamicMimeTypes.length; i++) {
+ try {
+ addDynamicDataType(dynamicMimeTypes[i]);
+ } catch (IntentFilter.MalformedMimeTypeException e) {
+ throw new RuntimeException("Bad mime type", e);
+ }
+ }
+ return this;
+ }
+
+ Match addMimeTypes(String[] mimeTypes) {
+ for (int i = 0; i < mimeTypes.length; i++) {
+ try {
+ addDataType(mimeTypes[i]);
+ } catch (IntentFilter.MalformedMimeTypeException e) {
+ throw new RuntimeException("Bad mime type", e);
+ }
+ }
+ return this;
+ }
+
+ Match addActions(String[] actions) {
+ for (int i = 0; i < actions.length; i++) {
+ addAction(actions[i]);
+ }
+ return this;
+ }
}
private static class MatchCondition {
@@ -1179,7 +1413,7 @@
public void testDump() throws MalformedMimeTypeException {
TestPrinter printer = new TestPrinter();
String prefix = "TestIntentFilter";
- IntentFilter filter = new IntentFilter(ACTION, DATA_TYPE);
+ IntentFilter filter = new IntentFilter(ACTION, DATA_STATIC_TYPE);
filter.dump(printer, prefix);
assertTrue(printer.isPrintlnCalled);
}
diff --git a/tests/tests/content/src/android/content/om/cts/OverlayInfoTest.java b/tests/tests/content/src/android/content/om/cts/OverlayInfoTest.java
index d2e1998..3eed1b2 100644
--- a/tests/tests/content/src/android/content/om/cts/OverlayInfoTest.java
+++ b/tests/tests/content/src/android/content/om/cts/OverlayInfoTest.java
@@ -108,6 +108,6 @@
assertEquals(info.state, copyInfo.state);
assertEquals(info.userId, copyInfo.userId);
assertEquals(info.priority, copyInfo.priority);
- assertEquals(info.isStatic, copyInfo.isStatic);
+ assertEquals(info.isMutable, copyInfo.isMutable);
}
}
\ No newline at end of file
diff --git a/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandTest.java b/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandTest.java
index a78069c..36ba232 100644
--- a/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandTest.java
+++ b/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandTest.java
@@ -74,8 +74,7 @@
@Parameters
public static Iterable<Object> initParameters() {
- return Arrays.asList(DATA_LOADER_TYPE_NONE, DATA_LOADER_TYPE_STREAMING,
- DATA_LOADER_TYPE_INCREMENTAL);
+ return Arrays.asList(DATA_LOADER_TYPE_NONE);
}
private boolean mStreaming = false;
diff --git a/tests/tests/display/src/android/display/cts/DisplayTest.java b/tests/tests/display/src/android/display/cts/DisplayTest.java
index d6c1cc4..48dddbd 100644
--- a/tests/tests/display/src/android/display/cts/DisplayTest.java
+++ b/tests/tests/display/src/android/display/cts/DisplayTest.java
@@ -16,6 +16,8 @@
package android.display.cts;
+import static android.view.Display.DEFAULT_DISPLAY;
+
import static org.junit.Assert.*;
import android.app.Activity;
@@ -88,6 +90,7 @@
private UiModeManager mUiModeManager;
private Context mContext;
private ColorSpace[] mSupportedWideGamuts;
+ private Display mDefaultDisplay;
// To test display mode switches.
private TestPresentation mPresentation;
@@ -106,7 +109,8 @@
mDisplayManager = (DisplayManager)mContext.getSystemService(Context.DISPLAY_SERVICE);
mWindowManager = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
mUiModeManager = (UiModeManager)mContext.getSystemService(Context.UI_MODE_SERVICE);
- mSupportedWideGamuts = mContext.getDisplay().getSupportedWideColorGamut();
+ mDefaultDisplay = mDisplayManager.getDisplay(DEFAULT_DISPLAY);
+ mSupportedWideGamuts = mDefaultDisplay.getSupportedWideColorGamut();
}
@After
@@ -171,7 +175,7 @@
boolean hasDefaultDisplay = false;
boolean hasSecondaryDisplay = false;
for (Display display : displays) {
- if (display.getDisplayId() == Display.DEFAULT_DISPLAY) {
+ if (display.getDisplayId() == DEFAULT_DISPLAY) {
hasDefaultDisplay = true;
}
if (isSecondaryDisplay(display)) {
@@ -188,7 +192,7 @@
@Presubmit
@Test
public void testDefaultDisplay() {
- assertEquals(Display.DEFAULT_DISPLAY, mWindowManager.getDefaultDisplay().getDisplayId());
+ assertEquals(DEFAULT_DISPLAY, mWindowManager.getDefaultDisplay().getDisplayId());
}
/**
@@ -196,7 +200,7 @@
*/
@Test
public void testDefaultDisplayHdrCapability() {
- Display display = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY);
+ Display display = mDisplayManager.getDisplay(DEFAULT_DISPLAY);
HdrCapabilities cap = display.getHdrCapabilities();
int[] hdrTypes = cap.getSupportedHdrTypes();
for (int type : hdrTypes) {
@@ -219,7 +223,7 @@
public void testSecondaryDisplay() {
Display display = getSecondaryDisplay(mDisplayManager.getDisplays());
assertNotNull(display);
- assertTrue(Display.DEFAULT_DISPLAY != display.getDisplayId());
+ assertTrue(DEFAULT_DISPLAY != display.getDisplayId());
}
/**
@@ -384,10 +388,9 @@
*/
@Test
public void testGetPreferredWideGamutColorSpace() {
- final Display defaultDisplay = mWindowManager.getDefaultDisplay();
- final ColorSpace colorSpace = defaultDisplay.getPreferredWideGamutColorSpace();
+ final ColorSpace colorSpace = mDefaultDisplay.getPreferredWideGamutColorSpace();
- if (defaultDisplay.isWideColorGamut()) {
+ if (mDefaultDisplay.isWideColorGamut()) {
assertFalse(colorSpace.isSrgb());
assertTrue(colorSpace.isWideGamut());
} else {
@@ -430,7 +433,7 @@
final ColorSpace displayP3 = ColorSpace.get(ColorSpace.Named.DISPLAY_P3);
final ColorSpace dciP3 = ColorSpace.get(ColorSpace.Named.DCI_P3);
final List<ColorSpace> list = Arrays.asList(mSupportedWideGamuts);
- final boolean supportsWideGamut = mContext.getDisplay().isWideColorGamut()
+ final boolean supportsWideGamut = mDefaultDisplay.isWideColorGamut()
&& mSupportedWideGamuts.length > 0;
final boolean supportsP3 = list.contains(displayP3) || list.contains(dciP3);
assertEquals(supportsWideGamut, supportsP3);
diff --git a/tests/tests/dynamic_linker/Android.bp b/tests/tests/dynamic_linker/Android.bp
index 76b6603..ee88e21 100644
--- a/tests/tests/dynamic_linker/Android.bp
+++ b/tests/tests/dynamic_linker/Android.bp
@@ -14,6 +14,7 @@
cc_test_library {
name: "libdynamiclinker_native_lib_a",
+ sdk_version: "current",
srcs: ["native_lib_a.cpp"],
cflags: [
"-Wall",
@@ -27,6 +28,7 @@
cc_test_library {
name: "libdynamiclinker_native_lib_b",
+ sdk_version: "current",
srcs: ["native_lib_b.cpp"],
cflags: [
"-Wall",
diff --git a/tests/tests/graphics/AndroidManifest.xml b/tests/tests/graphics/AndroidManifest.xml
index 98c392a..2e53417 100644
--- a/tests/tests/graphics/AndroidManifest.xml
+++ b/tests/tests/graphics/AndroidManifest.xml
@@ -53,6 +53,10 @@
<activity android:name="android.graphics.drawable.cts.DrawableStubActivity"
android:theme="@style/WhiteBackgroundNoWindowAnimation"
android:screenOrientation="locked"/>
+ <activity android:name="android.graphics.drawable.cts.AnimatedImageActivity"
+ android:theme="@style/WhiteBackgroundNoWindowAnimation"
+ android:screenOrientation="locked">
+ </activity>
<provider
android:name=".EmptyProvider"
android:exported="true"
diff --git a/tests/tests/graphics/jni/Android.bp b/tests/tests/graphics/jni/Android.bp
index 87105a2..7327949 100644
--- a/tests/tests/graphics/jni/Android.bp
+++ b/tests/tests/graphics/jni/Android.bp
@@ -38,6 +38,7 @@
"VulkanPreTransformTestHelpers.cpp",
"VulkanTestHelpers.cpp",
],
+ include_dirs: ["system/core/include"],
cflags: [
"-Wall",
"-Werror",
diff --git a/tests/tests/graphics/jni/android_graphics_cts_FrameRateCtsActivity.cpp b/tests/tests/graphics/jni/android_graphics_cts_FrameRateCtsActivity.cpp
index 1361e3d..2b4bef6 100644
--- a/tests/tests/graphics/jni/android_graphics_cts_FrameRateCtsActivity.cpp
+++ b/tests/tests/graphics/jni/android_graphics_cts_FrameRateCtsActivity.cpp
@@ -17,60 +17,232 @@
#define LOG_TAG "FrameRateCtsActivity"
+#include <android/hardware_buffer.h>
+#include <android/hardware_buffer_jni.h>
+#include <android/log.h>
#include <android/native_window.h>
#include <android/native_window_jni.h>
+#include <android/rect.h>
#include <android/surface_control.h>
#include <jni.h>
+#include <utils/Errors.h>
#include <array>
+#include <string>
+
+#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
+
+using namespace android;
namespace {
-jint nativeWindowSetFrameRate(JNIEnv* env, jclass, jobject jSurface, jfloat frameRate) {
+class Buffer {
+public:
+ Buffer(int width, int height, int rgbaColor) {
+ AHardwareBuffer_Desc desc;
+ memset(&desc, 0, sizeof(desc));
+ desc.width = width;
+ desc.height = height;
+ desc.layers = 1;
+ desc.format = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM;
+ desc.usage =
+ AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY | AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE;
+ int rc = AHardwareBuffer_allocate(&desc, &mBuffer);
+ if (rc < 0 || mBuffer == nullptr) {
+ ALOGE("AHardwareBuffer_allocate failed: %s (%d)", strerror(-rc), -rc);
+ return;
+ }
+ int8_t* buf = nullptr;
+ int32_t bytesPerPixel = 0;
+ int32_t bytesPerStride = 0;
+ std::string lockFunctionName = "AHardwareBuffer_lockAndGetInfo";
+ rc = AHardwareBuffer_lockAndGetInfo(mBuffer, AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY,
+ /*fence=*/-1,
+ /*rect=*/nullptr, reinterpret_cast<void**>(&buf),
+ &bytesPerPixel, &bytesPerStride);
+ if (rc == INVALID_OPERATION) {
+ // Older versions of gralloc don't implement AHardwareBuffer_lockAndGetInfo(). Fall back
+ // to AHardwareBuffer_lock().
+ lockFunctionName = "AHardwareBuffer_lock";
+ bytesPerPixel = 4;
+ bytesPerStride = width * bytesPerPixel;
+ rc = AHardwareBuffer_lock(mBuffer, AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY,
+ /*fence=*/-1,
+ /*rect=*/nullptr, reinterpret_cast<void**>(&buf));
+ }
+ if (rc < 0 || buf == nullptr) {
+ ALOGE("%s failed: %s (%d)", lockFunctionName.c_str(), strerror(-rc), -rc);
+ AHardwareBuffer_release(mBuffer);
+ mBuffer = nullptr;
+ return;
+ }
+
+ // There's a bug where Qualcomm returns pixels per stride instead of bytes per stride. See
+ // b/149601846.
+ if (bytesPerStride < width * bytesPerPixel) {
+ bytesPerStride *= bytesPerPixel;
+ }
+
+ int8_t* rgbaBytes = reinterpret_cast<int8_t*>(&rgbaColor);
+ for (int row = 0; row < height; row++) {
+ int8_t* ptr = buf + row * bytesPerStride;
+ for (int col = 0; col < width; col++, ptr += bytesPerPixel) {
+ ptr[0] = rgbaBytes[0];
+ ptr[1] = rgbaBytes[1];
+ ptr[2] = rgbaBytes[2];
+ ptr[3] = rgbaBytes[3];
+ }
+ }
+
+ rc = AHardwareBuffer_unlock(mBuffer, /*fence=*/nullptr);
+ if (rc < 0) {
+ ALOGE("AHardwareBuffer_unlock failed: %s (%d)", strerror(-rc), -rc);
+ AHardwareBuffer_release(mBuffer);
+ mBuffer = nullptr;
+ return;
+ }
+ }
+
+ ~Buffer() {
+ if (mBuffer) {
+ AHardwareBuffer_release(mBuffer);
+ }
+ }
+
+ bool isValid() const { return mBuffer != nullptr; }
+ AHardwareBuffer* getBuffer() const { return mBuffer; }
+
+private:
+ AHardwareBuffer* mBuffer = nullptr;
+};
+
+class Surface {
+public:
+ Surface(ANativeWindow* parentWindow, const std::string& name, int left, int top, int right,
+ int bottom) {
+ mSurface = ASurfaceControl_createFromWindow(parentWindow, name.c_str());
+ if (mSurface == nullptr) {
+ return;
+ }
+
+ mWidth = right - left;
+ mHeight = bottom - top;
+ ARect source{0, 0, mWidth, mHeight};
+ ARect dest{left, top, right, bottom};
+ ASurfaceTransaction* transaction = ASurfaceTransaction_create();
+ ASurfaceTransaction_setGeometry(transaction, mSurface, source, dest,
+ ANATIVEWINDOW_TRANSFORM_IDENTITY);
+ ASurfaceTransaction_apply(transaction);
+ ASurfaceTransaction_delete(transaction);
+ }
+
+ ~Surface() {
+ ASurfaceTransaction* transaction = ASurfaceTransaction_create();
+ ASurfaceTransaction_reparent(transaction, mSurface, nullptr);
+ ASurfaceTransaction_apply(transaction);
+ ASurfaceTransaction_delete(transaction);
+ ASurfaceControl_release(mSurface);
+ }
+
+ bool isValid() const { return mSurface != nullptr; }
+ ASurfaceControl* getSurfaceControl() const { return mSurface; }
+ int getWidth() const { return mWidth; }
+ int getHeight() const { return mHeight; }
+
+private:
+ ASurfaceControl* mSurface = nullptr;
+ int mWidth = 0;
+ int mHeight = 0;
+};
+
+jint nativeWindowSetFrameRate(JNIEnv* env, jclass, jobject jSurface, jfloat frameRate,
+ jint compatibility) {
ANativeWindow* window = nullptr;
if (jSurface) {
window = ANativeWindow_fromSurface(env, jSurface);
}
- return ANativeWindow_setFrameRate(window, frameRate);
+ return ANativeWindow_setFrameRate(window, frameRate, compatibility);
}
-jlong surfaceControlCreate(JNIEnv* env, jclass, jobject jParentSurface) {
- ANativeWindow* window = nullptr;
- if (jParentSurface) {
- window = ANativeWindow_fromSurface(env, jParentSurface);
- }
- if (!window) {
+jlong surfaceControlCreate(JNIEnv* env, jclass, jobject jParentSurface, jstring jName, jint left,
+ jint top, jint right, jint bottom) {
+ if (!jParentSurface || !jName) {
return 0;
}
- return reinterpret_cast<jlong>(
- ASurfaceControl_createFromWindow(window, "SetFrameRateTestSurface"));
+ ANativeWindow* parentWindow = ANativeWindow_fromSurface(env, jParentSurface);
+ if (!parentWindow) {
+ return 0;
+ }
+
+ const char* name = env->GetStringUTFChars(jName, nullptr);
+ std::string strName = name;
+ env->ReleaseStringUTFChars(jName, name);
+
+ Surface* surface = new Surface(parentWindow, strName, left, top, right, bottom);
+ if (!surface->isValid()) {
+ delete surface;
+ return 0;
+ }
+
+ return reinterpret_cast<jlong>(surface);
}
void surfaceControlDestroy(JNIEnv*, jclass, jlong surfaceControlLong) {
if (surfaceControlLong == 0) {
return;
}
- ASurfaceControl* surfaceControl = reinterpret_cast<ASurfaceControl*>(surfaceControlLong);
- ASurfaceTransaction* transaction = ASurfaceTransaction_create();
- ASurfaceTransaction_reparent(transaction, surfaceControl, nullptr);
- ASurfaceTransaction_apply(transaction);
- ASurfaceTransaction_delete(transaction);
- ASurfaceControl_release(surfaceControl);
+ delete reinterpret_cast<Surface*>(surfaceControlLong);
}
-void surfaceControlSetFrameRate(JNIEnv*, jclass, jlong surfaceControlLong, jfloat frameRate) {
- ASurfaceControl* surfaceControl = reinterpret_cast<ASurfaceControl*>(surfaceControlLong);
+void surfaceControlSetFrameRate(JNIEnv*, jclass, jlong surfaceControlLong, jfloat frameRate,
+ int compatibility) {
+ ASurfaceControl* surfaceControl =
+ reinterpret_cast<Surface*>(surfaceControlLong)->getSurfaceControl();
ASurfaceTransaction* transaction = ASurfaceTransaction_create();
- ASurfaceTransaction_setFrameRate(transaction, surfaceControl, frameRate);
+ ASurfaceTransaction_setFrameRate(transaction, surfaceControl, frameRate, compatibility);
ASurfaceTransaction_apply(transaction);
ASurfaceTransaction_delete(transaction);
}
-const std::array<JNINativeMethod, 4> JNI_METHODS = {{
- {"nativeWindowSetFrameRate", "(Landroid/view/Surface;F)I", (void*)nativeWindowSetFrameRate},
- {"nativeSurfaceControlCreate", "(Landroid/view/Surface;)J", (void*)surfaceControlCreate},
+void surfaceControlSetVisibility(JNIEnv*, jclass, jlong surfaceControlLong, jboolean visible) {
+ ASurfaceControl* surfaceControl =
+ reinterpret_cast<Surface*>(surfaceControlLong)->getSurfaceControl();
+ ASurfaceTransaction* transaction = ASurfaceTransaction_create();
+ ASurfaceTransaction_setVisibility(transaction, surfaceControl,
+ visible == JNI_TRUE ? ASURFACE_TRANSACTION_VISIBILITY_SHOW
+ : ASURFACE_TRANSACTION_VISIBILITY_HIDE);
+ ASurfaceTransaction_apply(transaction);
+ ASurfaceTransaction_delete(transaction);
+}
+
+jboolean surfaceControlPostBuffer(JNIEnv*, jclass, jlong surfaceControlLong, jint argbColor) {
+ Surface* surface = reinterpret_cast<Surface*>(surfaceControlLong);
+ ASurfaceControl* surfaceControl = surface->getSurfaceControl();
+ // Android's Color.* values are represented as ARGB. Convert to RGBA.
+ int rgbaColor = ((argbColor >> 16) & 0xff) | ((argbColor >> 8) & 0xff) |
+ ((argbColor >> 0) & 0xff) | ((argbColor >> 24) & 0xff);
+
+ Buffer buffer(surface->getWidth(), surface->getHeight(), rgbaColor);
+ if (!buffer.isValid()) {
+ return JNI_FALSE;
+ }
+
+ ASurfaceTransaction* transaction = ASurfaceTransaction_create();
+ ASurfaceTransaction_setBuffer(transaction, surfaceControl, buffer.getBuffer());
+ ASurfaceTransaction_apply(transaction);
+ ASurfaceTransaction_delete(transaction);
+ return JNI_TRUE;
+}
+
+const std::array<JNINativeMethod, 6> JNI_METHODS = {{
+ {"nativeWindowSetFrameRate", "(Landroid/view/Surface;FI)I",
+ (void*)nativeWindowSetFrameRate},
+ {"nativeSurfaceControlCreate", "(Landroid/view/Surface;Ljava/lang/String;IIII)J",
+ (void*)surfaceControlCreate},
{"nativeSurfaceControlDestroy", "(J)V", (void*)surfaceControlDestroy},
- {"nativeSurfaceControlSetFrameRate", "(JF)V", (void*)surfaceControlSetFrameRate},
+ {"nativeSurfaceControlSetFrameRate", "(JFI)V", (void*)surfaceControlSetFrameRate},
+ {"nativeSurfaceControlSetVisibility", "(JZ)V", (void*)surfaceControlSetVisibility},
+ {"nativeSurfaceControlPostBuffer", "(JI)Z", (void*)surfaceControlPostBuffer},
}};
} // namespace
diff --git a/tests/tests/graphics/src/android/graphics/cts/FrameRateCtsActivity.java b/tests/tests/graphics/src/android/graphics/cts/FrameRateCtsActivity.java
index 37a91a4..2cef070 100644
--- a/tests/tests/graphics/src/android/graphics/cts/FrameRateCtsActivity.java
+++ b/tests/tests/graphics/src/android/graphics/cts/FrameRateCtsActivity.java
@@ -16,11 +16,14 @@
package android.graphics.cts;
+import static android.system.OsConstants.EINVAL;
+
import static org.junit.Assert.assertTrue;
import android.app.Activity;
import android.graphics.Canvas;
import android.graphics.Color;
+import android.graphics.Rect;
import android.hardware.display.DisplayManager;
import android.os.Bundle;
import android.os.Handler;
@@ -48,6 +51,7 @@
private static final long WAIT_FOR_SURFACE_TIMEOUT_SECONDS = 3;
private static final long FRAME_RATE_SWITCH_GRACE_PERIOD_SECONDS = 1;
private static final long STABLE_FRAME_RATE_WAIT_SECONDS = 1;
+ private static final long POST_BUFFER_INTERVAL_MILLIS = 500;
private DisplayManager mDisplayManager;
private SurfaceView mSurfaceView;
@@ -104,6 +108,179 @@
public void onDisplayRemoved(int displayId) {}
};
+ public enum Api {
+ SURFACE("Surface"),
+ ANATIVE_WINDOW("ANativeWindow"),
+ SURFACE_CONTROL("SurfaceControl"),
+ NATIVE_SURFACE_CONTROL("ASurfaceControl");
+
+ private final String mName;
+ Api(String name) {
+ mName = name;
+ }
+
+ public String toString() {
+ return mName;
+ }
+ }
+
+ private static class TestSurface {
+ private Api mApi;
+ private String mName;
+ private SurfaceControl mSurfaceControl;
+ private Surface mSurface;
+ private long mNativeSurfaceControl;
+ private int mColor;
+ private boolean mLastBufferPostTimeValid;
+ private long mLastBufferPostTime;
+
+ TestSurface(Api api, SurfaceControl parentSurfaceControl, Surface parentSurface,
+ String name, Rect destFrame, boolean visible, int color) {
+ mApi = api;
+ mName = name;
+ mColor = color;
+
+ if (mApi == Api.SURFACE || mApi == Api.ANATIVE_WINDOW || mApi == Api.SURFACE_CONTROL) {
+ assertTrue("No parent surface", parentSurfaceControl != null);
+ mSurfaceControl = new SurfaceControl.Builder()
+ .setParent(parentSurfaceControl)
+ .setName(mName)
+ .setBufferSize(destFrame.right - destFrame.left,
+ destFrame.bottom - destFrame.top)
+ .build();
+ SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
+ try {
+ transaction.setGeometry(mSurfaceControl, null, destFrame, Surface.ROTATION_0)
+ .apply();
+ } finally {
+ transaction.close();
+ }
+ mSurface = new Surface(mSurfaceControl);
+ } else if (mApi == Api.NATIVE_SURFACE_CONTROL) {
+ assertTrue("No parent surface", parentSurface != null);
+ mNativeSurfaceControl = nativeSurfaceControlCreate(parentSurface, mName,
+ destFrame.left, destFrame.top, destFrame.right, destFrame.bottom);
+ assertTrue("Failed to create a native SurfaceControl", mNativeSurfaceControl != 0);
+ }
+
+ setVisibility(visible);
+ postBuffer();
+ }
+
+ public int setFrameRate(float frameRate, int compatibility) {
+ Log.i(TAG,
+ String.format("Setting frame rate for %s: fps=%.0f compatibility=%s", mName,
+ frameRate, frameRateCompatibilityToString(compatibility)));
+
+ int rc = 0;
+ if (mApi == Api.SURFACE) {
+ mSurface.setFrameRate(frameRate, compatibility);
+ } else if (mApi == Api.ANATIVE_WINDOW) {
+ rc = nativeWindowSetFrameRate(mSurface, frameRate, compatibility);
+ } else if (mApi == Api.SURFACE_CONTROL) {
+ SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
+ try {
+ transaction.setFrameRate(mSurfaceControl, frameRate, compatibility).apply();
+ } finally {
+ transaction.close();
+ }
+ } else if (mApi == Api.NATIVE_SURFACE_CONTROL) {
+ nativeSurfaceControlSetFrameRate(mNativeSurfaceControl, frameRate, compatibility);
+ }
+ return rc;
+ }
+
+ public void setInvalidFrameRate(float frameRate, int compatibility) {
+ if (mApi == Api.SURFACE) {
+ boolean caughtIllegalArgException = false;
+ try {
+ setFrameRate(frameRate, compatibility);
+ } catch (IllegalArgumentException exc) {
+ caughtIllegalArgException = true;
+ }
+ assertTrue("Expected an IllegalArgumentException from invalid call to"
+ + " Surface.setFrameRate()",
+ caughtIllegalArgException);
+ } else {
+ int rc = setFrameRate(frameRate, compatibility);
+ if (mApi == Api.ANATIVE_WINDOW) {
+ assertTrue("Expected -EINVAL return value from invalid call to"
+ + " ANativeWindow_setFrameRate()",
+ rc == -EINVAL);
+ }
+ }
+ }
+
+ public void setVisibility(boolean visible) {
+ Log.i(TAG,
+ String.format("Setting visibility for %s: %s", mName,
+ visible ? "visible" : "hidden"));
+ if (mApi == Api.SURFACE || mApi == Api.ANATIVE_WINDOW || mApi == Api.SURFACE_CONTROL) {
+ SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
+ try {
+ transaction.setVisibility(mSurfaceControl, visible).apply();
+ } finally {
+ transaction.close();
+ }
+ } else if (mApi == Api.NATIVE_SURFACE_CONTROL) {
+ nativeSurfaceControlSetVisibility(mNativeSurfaceControl, visible);
+ }
+ }
+
+ public void postBuffer() {
+ mLastBufferPostTimeValid = true;
+ mLastBufferPostTime = System.nanoTime();
+ if (mApi == Api.SURFACE || mApi == Api.ANATIVE_WINDOW || mApi == Api.SURFACE_CONTROL) {
+ Canvas canvas = mSurface.lockHardwareCanvas();
+ canvas.drawColor(mColor);
+ mSurface.unlockCanvasAndPost(canvas);
+ } else if (mApi == Api.NATIVE_SURFACE_CONTROL) {
+ assertTrue("Posting a buffer failed",
+ nativeSurfaceControlPostBuffer(mNativeSurfaceControl, mColor));
+ }
+ }
+
+ public long getLastBufferPostTime() {
+ assertTrue("No buffer posted yet", mLastBufferPostTimeValid);
+ return mLastBufferPostTime;
+ }
+
+ public void release() {
+ if (mSurface != null) {
+ mSurface.release();
+ mSurface = null;
+ }
+ if (mSurfaceControl != null) {
+ mSurfaceControl.release();
+ mSurfaceControl = null;
+ }
+ if (mNativeSurfaceControl != 0) {
+ nativeSurfaceControlDestroy(mNativeSurfaceControl);
+ mNativeSurfaceControl = 0;
+ }
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ release();
+ } finally {
+ super.finalize();
+ }
+ }
+ }
+
+ private static String frameRateCompatibilityToString(int compatibility) {
+ switch (compatibility) {
+ case Surface.FRAME_RATE_COMPATIBILITY_DEFAULT:
+ return "default";
+ case Surface.FRAME_RATE_COMPATIBILITY_FIXED_SOURCE:
+ return "fixed_source";
+ default:
+ return "invalid(" + compatibility + ")";
+ }
+ }
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -150,6 +327,29 @@
return uniqueFrameRates;
}
+ private boolean isFrameRateMultiple(float higherFrameRate, float lowerFrameRate) {
+ float multiple = higherFrameRate / lowerFrameRate;
+ int roundedMultiple = Math.round(multiple);
+ return roundedMultiple > 0
+ && Math.abs(roundedMultiple * lowerFrameRate - higherFrameRate) <= 0.1f;
+ }
+
+ // Returns two device-supported frame rates that aren't multiples of each other, or null if no
+ // such incompatible frame rates are available. This is useful for testing behavior where we
+ // have layers with conflicting frame rates.
+ private float[] getIncompatibleFrameRates() {
+ ArrayList<Float> frameRates = getFrameRatesToTest();
+ for (int i = 0; i < frameRates.size(); i++) {
+ for (int j = i + 1; j < frameRates.size(); j++) {
+ if (!isFrameRateMultiple(Math.max(frameRates.get(i), frameRates.get(j)),
+ Math.min(frameRates.get(i), frameRates.get(j)))) {
+ return new float[] {frameRates.get(i), frameRates.get(j)};
+ }
+ }
+ }
+ return null;
+ }
+
private void waitForSurface() throws InterruptedException {
if (mSurface == null) {
Log.i(TAG, "Waiting for surface");
@@ -164,60 +364,52 @@
}
}
- private boolean isDeviceFrameRateCompatibleWithAppRequest(
- float deviceFrameRate, float appRequestedFrameRate) {
- float multiple = deviceFrameRate / appRequestedFrameRate;
- int roundedMultiple = Math.round(multiple);
- return roundedMultiple > 0
- && Math.abs(roundedMultiple * appRequestedFrameRate - deviceFrameRate) <= 0.1f;
- }
-
- private void postBuffer() {
- Canvas canvas = mSurfaceView.getHolder().lockHardwareCanvas();
- canvas.drawColor(Color.BLUE);
- mSurfaceView.getHolder().unlockCanvasAndPost(canvas);
- }
-
- private void setFrameRateSurface(float frameRate) {
- Log.i(TAG, String.format("Setting frame rate to %.0f", frameRate));
- mSurface.setFrameRate(frameRate);
- postBuffer();
- }
-
- private void setFrameRateANativeWindow(float frameRate) {
- Log.i(TAG, String.format("Setting frame rate to %.0f", frameRate));
- nativeWindowSetFrameRate(mSurface, frameRate);
- postBuffer();
- }
-
- private void setFrameRateSurfaceControl(float frameRate) {
- Log.i(TAG, String.format("Setting frame rate to %.0f", frameRate));
- SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
- transaction.setFrameRate(mSurfaceView.getSurfaceControl(), frameRate).apply();
- transaction.close();
- postBuffer();
- }
-
- private void setFrameRateNativeSurfaceControl(long surfaceControl, float frameRate) {
- Log.i(TAG, String.format("Setting frame rate to %.0f", frameRate));
- nativeSurfaceControlSetFrameRate(surfaceControl, frameRate);
- }
-
- private void verifyCompatibleAndStableFrameRate(float appRequestedFrameRate)
+ // Returns true if we reached waitUntilNanos, false if some other event occurred.
+ private boolean waitForEvents(long waitUntilNanos, ArrayList<TestSurface> surfaces)
throws InterruptedException {
+ mFrameRateChangedEvents.clear();
+ long nowNanos = System.nanoTime();
+ while (nowNanos < waitUntilNanos) {
+ long surfacePostTime = Long.MAX_VALUE;
+ for (TestSurface surface : surfaces) {
+ surfacePostTime = Math.min(surfacePostTime,
+ surface.getLastBufferPostTime()
+ + (POST_BUFFER_INTERVAL_MILLIS * 1_000_000L));
+ }
+ long timeoutNs = Math.min(waitUntilNanos, surfacePostTime) - nowNanos;
+ long timeoutMs = timeoutNs / 1_000_000L;
+ int remainderNs = (int) (timeoutNs % 1_000_000L);
+ // Don't call wait(0, 0) - it blocks indefinitely.
+ if (timeoutMs > 0 || remainderNs > 0) {
+ mLock.wait(timeoutMs, remainderNs);
+ }
+ nowNanos = System.nanoTime();
+ assertTrue("Lost the surface", mSurface != null);
+ if (!mFrameRateChangedEvents.isEmpty()) {
+ return false;
+ }
+ if (nowNanos >= surfacePostTime) {
+ for (TestSurface surface : surfaces) {
+ surface.postBuffer();
+ }
+ }
+ }
+ return true;
+ }
+
+ private void verifyCompatibleAndStableFrameRate(float appRequestedFrameRate,
+ ArrayList<TestSurface> surfaces) throws InterruptedException {
Log.i(TAG, "Verifying compatible and stable frame rate");
long nowNanos = System.nanoTime();
long gracePeriodEndTimeNanos =
nowNanos + FRAME_RATE_SWITCH_GRACE_PERIOD_SECONDS * 1_000_000_000L;
while (true) {
// Wait until we switch to a compatible frame rate.
- while (!isDeviceFrameRateCompatibleWithAppRequest(
- mDeviceFrameRate, appRequestedFrameRate)
- && gracePeriodEndTimeNanos > nowNanos) {
- mLock.wait((gracePeriodEndTimeNanos - nowNanos) / 1_000_000);
- nowNanos = System.nanoTime();
- assertTrue("Lost the surface", mSurface != null);
+ while (!isFrameRateMultiple(mDeviceFrameRate, appRequestedFrameRate)
+ && !waitForEvents(gracePeriodEndTimeNanos, surfaces)) {
+ // Empty
}
+ nowNanos = System.nanoTime();
// TODO(b/148033900): Remove the if and error log below, and replace it with this
// assertTrue() call, once we have a way to ignore display manager policy.
@@ -241,95 +433,142 @@
// that frame rate.
long endTimeNanos = nowNanos + STABLE_FRAME_RATE_WAIT_SECONDS * 1_000_000_000L;
while (endTimeNanos > nowNanos) {
- mFrameRateChangedEvents.clear();
- mLock.wait((endTimeNanos - nowNanos) / 1_000_000);
- nowNanos = System.nanoTime();
- assertTrue("Lost the surface", mSurface != null);
- if (!mFrameRateChangedEvents.isEmpty()) {
- break;
- }
- if (nowNanos >= endTimeNanos) {
+ if (waitForEvents(endTimeNanos, surfaces)) {
Log.i(TAG, String.format("Stable frame rate %.0f verified", mDeviceFrameRate));
return;
}
+ nowNanos = System.nanoTime();
+ if (!mFrameRateChangedEvents.isEmpty()) {
+ break;
+ }
}
}
}
- public void testSurfaceSetFrameRate() throws InterruptedException {
+ private void testExactFrameRateMatch(Api api) throws InterruptedException {
+ Log.i(TAG, String.format("Testing %s exact frame rate match", api));
ArrayList<Float> frameRatesToTest = getFrameRatesToTest();
- Log.i(TAG, "Testing Surface.setFrameRate()");
- synchronized (mLock) {
- waitForSurface();
- for (float frameRate : frameRatesToTest) {
- setFrameRateSurface(frameRate);
- verifyCompatibleAndStableFrameRate(frameRate);
- }
- setFrameRateSurface(-100.f);
- Thread.sleep(1000);
- setFrameRateSurface(0.f);
- }
- Log.i(TAG, "Done testing Surface.setFrameRate()");
- }
-
- public void testANativeWindowSetFrameRate() throws InterruptedException {
- ArrayList<Float> frameRatesToTest = getFrameRatesToTest();
- Log.i(TAG, "Testing ANativeWindow_setFrameRate()");
- synchronized (mLock) {
- waitForSurface();
- for (float frameRate : frameRatesToTest) {
- setFrameRateANativeWindow(frameRate);
- verifyCompatibleAndStableFrameRate(frameRate);
- }
- setFrameRateANativeWindow(-100.f);
- Thread.sleep(1000);
- setFrameRateANativeWindow(0.f);
- }
- Log.i(TAG, "Done testing ANativeWindow_setFrameRate()");
- }
-
- public void testSurfaceControlSetFrameRate() throws InterruptedException {
- ArrayList<Float> frameRatesToTest = getFrameRatesToTest();
- Log.i(TAG, "Testing SurfaceControl.Transaction.setFrameRate()");
- synchronized (mLock) {
- waitForSurface();
- for (float frameRate : frameRatesToTest) {
- setFrameRateSurfaceControl(frameRate);
- verifyCompatibleAndStableFrameRate(frameRate);
- }
- setFrameRateSurfaceControl(-100.f);
- Thread.sleep(1000);
- setFrameRateSurfaceControl(0.f);
- }
- Log.i(TAG, "Done testing SurfaceControl.Transaction.setFrameRate()");
- }
-
- public void testNativeSurfaceControlSetFrameRate() throws InterruptedException {
- ArrayList<Float> frameRatesToTest = getFrameRatesToTest();
- Log.i(TAG, "Testing ASurfaceTransaction_setFrameRate()");
- long nativeSurfaceControl = 0;
+ TestSurface surface = null;
try {
synchronized (mLock) {
waitForSurface();
- nativeSurfaceControl = nativeSurfaceControlCreate(mSurface);
- assertTrue("Failed to create a native SurfaceControl", nativeSurfaceControl != 0);
+ surface = new TestSurface(api, mSurfaceView.getSurfaceControl(), mSurface,
+ "testSurface", mSurfaceView.getHolder().getSurfaceFrame(),
+ /*visible=*/true, Color.RED);
+ ArrayList<TestSurface> surfaces = new ArrayList<>();
+ surfaces.add(surface);
for (float frameRate : frameRatesToTest) {
- setFrameRateNativeSurfaceControl(nativeSurfaceControl, frameRate);
- verifyCompatibleAndStableFrameRate(frameRate);
+ surface.setFrameRate(frameRate, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT);
+ verifyCompatibleAndStableFrameRate(frameRate, surfaces);
}
- setFrameRateNativeSurfaceControl(nativeSurfaceControl, -100.f);
- Thread.sleep(1000);
- setFrameRateNativeSurfaceControl(nativeSurfaceControl, 0.f);
+ surface.setFrameRate(0.f, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT);
}
} finally {
- nativeSurfaceControlDestroy(nativeSurfaceControl);
+ if (surface != null) {
+ surface.release();
+ }
}
- Log.i(TAG, "Done testing ASurfaceTransaction_setFrameRate()");
}
- private static native int nativeWindowSetFrameRate(Surface surface, float frameRate);
- private static native long nativeSurfaceControlCreate(Surface parentSurface);
+ public void testExactFrameRateMatch() throws InterruptedException {
+ for (Api api : Api.values()) {
+ testExactFrameRateMatch(api);
+ }
+ }
+
+ private void testFixedSource(Api api) throws InterruptedException {
+ Log.i(TAG, String.format("Testing %s fixed_source behavior", api));
+ float[] incompatibleFrameRates = getIncompatibleFrameRates();
+ if (incompatibleFrameRates == null) {
+ Log.i(TAG, "No incompatible frame rates to use for testing fixed_source behavior");
+ return;
+ }
+
+ float frameRateA = incompatibleFrameRates[0];
+ float frameRateB = incompatibleFrameRates[1];
+ Log.i(TAG,
+ String.format("Testing with incompatible frame rates: surfaceA=%.0f surfaceB=%.0f",
+ frameRateA, frameRateB));
+ synchronized (mLock) {
+ waitForSurface();
+ TestSurface surfaceA = null;
+ TestSurface surfaceB = null;
+ try {
+ int width = mSurfaceView.getHolder().getSurfaceFrame().width();
+ int height = mSurfaceView.getHolder().getSurfaceFrame().height() / 2;
+ Rect destFrameA =
+ new Rect(/*left=*/0, /*top=*/0, /*right=*/width, /*bottom=*/height);
+ surfaceA = new TestSurface(api, mSurfaceView.getSurfaceControl(), mSurface,
+ "surfaceA", destFrameA, /*visible=*/true, Color.RED);
+ Rect destFrameB = new Rect(
+ /*left=*/0, /*top=*/height, /*right=*/width, /*bottom=*/height * 2);
+ surfaceB = new TestSurface(api, mSurfaceView.getSurfaceControl(), mSurface,
+ "surfaceB", destFrameB, /*visible=*/false, Color.GREEN);
+
+ surfaceA.setFrameRate(frameRateA, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT);
+ surfaceB.setFrameRate(frameRateB, Surface.FRAME_RATE_COMPATIBILITY_FIXED_SOURCE);
+
+ ArrayList<TestSurface> surfaces = new ArrayList<>();
+ surfaces.add(surfaceA);
+ surfaces.add(surfaceB);
+
+ verifyCompatibleAndStableFrameRate(frameRateA, surfaces);
+ surfaceB.setVisibility(true);
+ verifyCompatibleAndStableFrameRate(frameRateB, surfaces);
+ } finally {
+ if (surfaceA != null) {
+ surfaceA.release();
+ }
+ if (surfaceB != null) {
+ surfaceB.release();
+ }
+ }
+ }
+ }
+
+ public void testFixedSource() throws InterruptedException {
+ for (Api api : Api.values()) {
+ testFixedSource(api);
+ }
+ }
+
+ private void testInvalidParams(Api api) throws InterruptedException {
+ Log.i(TAG, String.format("Testing %s invalid params behavior", api));
+ TestSurface surface = null;
+ try {
+ synchronized (mLock) {
+ waitForSurface();
+ surface = new TestSurface(api, mSurfaceView.getSurfaceControl(), mSurface,
+ "testSurface", mSurfaceView.getHolder().getSurfaceFrame(),
+ /*visible=*/true, Color.RED);
+ surface.setInvalidFrameRate(-100.f, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT);
+ surface.setInvalidFrameRate(
+ Float.POSITIVE_INFINITY, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT);
+ surface.setInvalidFrameRate(Float.NaN, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT);
+ surface.setInvalidFrameRate(0.f, -10);
+ surface.setInvalidFrameRate(0.f, 50);
+ }
+ } finally {
+ if (surface != null) {
+ surface.release();
+ }
+ }
+ }
+
+ public void testInvalidParams() throws InterruptedException {
+ for (Api api : Api.values()) {
+ testInvalidParams(api);
+ }
+ }
+
+ private static native int nativeWindowSetFrameRate(
+ Surface surface, float frameRate, int compatibility);
+ private static native long nativeSurfaceControlCreate(
+ Surface parentSurface, String name, int left, int top, int right, int bottom);
private static native void nativeSurfaceControlDestroy(long surfaceControl);
private static native void nativeSurfaceControlSetFrameRate(
- long surfaceControl, float frameRate);
+ long surfaceControl, float frameRate, int compatibility);
+ private static native void nativeSurfaceControlSetVisibility(
+ long surfaceControl, boolean visible);
+ private static native boolean nativeSurfaceControlPostBuffer(long surfaceControl, int color);
}
diff --git a/tests/tests/graphics/src/android/graphics/cts/SetFrameRateTest.java b/tests/tests/graphics/src/android/graphics/cts/SetFrameRateTest.java
index 65dcfcd..ec7ef2d 100644
--- a/tests/tests/graphics/src/android/graphics/cts/SetFrameRateTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/SetFrameRateTest.java
@@ -32,11 +32,20 @@
new ActivityTestRule<>(FrameRateCtsActivity.class);
@Test
- public void testSetFrameRate() throws InterruptedException {
+ public void testExactFrameRateMatch() throws InterruptedException {
FrameRateCtsActivity activity = mActivityRule.getActivity();
- activity.testSurfaceSetFrameRate();
- activity.testANativeWindowSetFrameRate();
- activity.testSurfaceControlSetFrameRate();
- activity.testNativeSurfaceControlSetFrameRate();
+ activity.testExactFrameRateMatch();
+ }
+
+ @Test
+ public void testFixedSource() throws InterruptedException {
+ FrameRateCtsActivity activity = mActivityRule.getActivity();
+ activity.testFixedSource();
+ }
+
+ @Test
+ public void testInvalidParams() throws InterruptedException {
+ FrameRateCtsActivity activity = mActivityRule.getActivity();
+ activity.testInvalidParams();
}
}
diff --git a/tests/tests/graphics/src/android/graphics/cts/Utils.java b/tests/tests/graphics/src/android/graphics/cts/Utils.java
index 85b5b40..c558e89 100644
--- a/tests/tests/graphics/src/android/graphics/cts/Utils.java
+++ b/tests/tests/graphics/src/android/graphics/cts/Utils.java
@@ -40,7 +40,7 @@
return getResources().openRawResource(resId);
}
- static Uri getAsResourceUri(int resId) {
+ public static Uri getAsResourceUri(int resId) {
Resources res = getResources();
return new Uri.Builder()
.scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/AnimatedImageActivity.java b/tests/tests/graphics/src/android/graphics/drawable/cts/AnimatedImageActivity.java
new file mode 100644
index 0000000..c1524f8
--- /dev/null
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/AnimatedImageActivity.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.graphics.drawable.cts;
+
+import android.app.Activity;
+import android.graphics.cts.R;
+import android.os.Bundle;
+import android.view.WindowManager;
+
+/**
+ * Simple Activity with an ImageView for inserting an AnimatedImageDrawable in
+ * AnimatedImageDrawableTest.
+ */
+public final class AnimatedImageActivity extends Activity {
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.animated_image_layout);
+
+ // AnimatedImageDrawable only animates if the screen is on, so ensure that the screen stays
+ // on for the duration of the test.
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+ }
+}
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/AnimatedImageDrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/AnimatedImageDrawableTest.java
index ee3347d..453f1ad 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/AnimatedImageDrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/AnimatedImageDrawableTest.java
@@ -36,6 +36,7 @@
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.cts.R;
+import android.graphics.cts.Utils;
import android.graphics.drawable.AnimatedImageDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
@@ -47,8 +48,9 @@
import androidx.test.rule.ActivityTestRule;
import com.android.compatibility.common.util.BitmapUtils;
+import com.android.compatibility.common.util.PollingCheck;
+import com.android.compatibility.common.util.WidgetTestUtils;
-import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -66,8 +68,7 @@
@RunWith(JUnitParamsRunner.class)
public class AnimatedImageDrawableTest {
- private Resources mRes;
- private ContentResolver mContentResolver;
+ private ImageView mImageView;
private static final int RES_ID = R.drawable.animated;
private static final int WIDTH = 278;
@@ -75,27 +76,24 @@
private static final int NUM_FRAMES = 4;
private static final int FRAME_DURATION = 250; // in milliseconds
private static final int DURATION = NUM_FRAMES * FRAME_DURATION;
- private static final int LAYOUT = R.layout.animated_image_layout;
- private static final int IMAGE_ID = R.id.animated_image;
+
@Rule
- public ActivityTestRule<DrawableStubActivity> mActivityRule =
- new ActivityTestRule<DrawableStubActivity>(DrawableStubActivity.class);
+ public ActivityTestRule<AnimatedImageActivity> mActivityRule =
+ new ActivityTestRule<AnimatedImageActivity>(AnimatedImageActivity.class);
private Activity mActivity;
- private Uri getAsResourceUri(int resId) {
- return new Uri.Builder()
- .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
- .authority(mRes.getResourcePackageName(resId))
- .appendPath(mRes.getResourceTypeName(resId))
- .appendPath(mRes.getResourceEntryName(resId))
- .build();
+ private Resources getResources() {
+ return InstrumentationRegistry.getTargetContext().getResources();
}
- @Before
- public void setup() {
- mRes = InstrumentationRegistry.getTargetContext().getResources();
- mContentResolver = InstrumentationRegistry.getTargetContext().getContentResolver();
+ private ContentResolver getContentResolver() {
+ return InstrumentationRegistry.getTargetContext().getContentResolver();
+ }
+
+ private void setupActivity() {
mActivity = mActivityRule.getActivity();
+ PollingCheck.waitFor(mActivity::hasWindowFocus);
+ mImageView = mActivity.findViewById(R.id.animated_image);
}
@Test
@@ -105,8 +103,9 @@
@Test
public void testMutate() {
- AnimatedImageDrawable aid1 = (AnimatedImageDrawable) mRes.getDrawable(R.drawable.animated);
- AnimatedImageDrawable aid2 = (AnimatedImageDrawable) mRes.getDrawable(R.drawable.animated);
+ Resources res = getResources();
+ AnimatedImageDrawable aid1 = (AnimatedImageDrawable) res.getDrawable(R.drawable.animated);
+ AnimatedImageDrawable aid2 = (AnimatedImageDrawable) res.getDrawable(R.drawable.animated);
final int originalAlpha = aid1.getAlpha();
assertEquals(255, originalAlpha);
@@ -117,15 +116,15 @@
aid1.setAlpha(100);
assertEquals(originalAlpha, aid2.getAlpha());
} finally {
- mRes.getDrawable(R.drawable.animated).setAlpha(originalAlpha);
+ res.getDrawable(R.drawable.animated).setAlpha(originalAlpha);
}
}
private AnimatedImageDrawable createFromImageDecoder(int resId) {
Uri uri = null;
try {
- uri = getAsResourceUri(resId);
- ImageDecoder.Source source = ImageDecoder.createSource(mContentResolver, uri);
+ uri = Utils.getAsResourceUri(resId);
+ ImageDecoder.Source source = ImageDecoder.createSource(getContentResolver(), uri);
Drawable drawable = ImageDecoder.decodeDrawable(source);
assertTrue(drawable instanceof AnimatedImageDrawable);
return (AnimatedImageDrawable) drawable;
@@ -176,6 +175,7 @@
@Test
public void testRegisterCallback() throws Throwable {
+ setupActivity();
AnimatedImageDrawable drawable = createFromImageDecoder(R.drawable.animated);
mActivityRule.runOnUiThread(() -> {
@@ -191,6 +191,7 @@
@Test
public void testClearCallbacks() throws Throwable {
+ setupActivity();
AnimatedImageDrawable drawable = createFromImageDecoder(R.drawable.animated);
Callback[] callbacks = new Callback[] {
@@ -218,26 +219,14 @@
}
}
- /**
- * Helper for attaching drawable to the view system.
- *
- * Necessary for the drawable to animate.
- *
- * Must be called from UI thread.
- */
- private void setContentView(AnimatedImageDrawable drawable) {
- mActivity.setContentView(LAYOUT);
- ImageView imageView = (ImageView) mActivity.findViewById(IMAGE_ID);
- imageView.setImageDrawable(drawable);
- }
-
@Test
public void testUnregisterCallback() throws Throwable {
+ setupActivity();
AnimatedImageDrawable drawable = createFromImageDecoder(R.drawable.animated);
Callback cb = new Callback(drawable);
- mActivityRule.runOnUiThread(() -> {
- setContentView(drawable);
+ WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mImageView, () -> {
+ mImageView.setImageDrawable(drawable);
drawable.registerAnimationCallback(cb);
assertTrue(drawable.unregisterAnimationCallback(cb));
@@ -255,14 +244,15 @@
@Test
@FlakyTest (bugId = 120280954)
public void testLifeCycle() throws Throwable {
+ setupActivity();
AnimatedImageDrawable drawable = createFromImageDecoder(RES_ID);
// Only run the animation one time.
drawable.setRepeatCount(0);
Callback cb = new Callback(drawable);
- mActivityRule.runOnUiThread(() -> {
- setContentView(drawable);
+ WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mImageView, () -> {
+ mImageView.setImageDrawable(drawable);
drawable.registerAnimationCallback(cb);
});
@@ -271,13 +261,15 @@
cb.assertStarted(false);
cb.assertEnded(false);
- mActivityRule.runOnUiThread(() -> {
+ WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mImageView, () -> {
drawable.start();
assertTrue(drawable.isRunning());
});
cb.waitForStart();
cb.assertStarted(true);
+ // FIXME: Now that it seems the reason for the flakiness has been solved (b/129400990),
+ // reduce this extra duration workaround.
// Extra time, to wait for the message to post.
cb.waitForEnd(DURATION * 20);
cb.assertEnded(true);
@@ -286,6 +278,7 @@
@Test
public void testLifeCycleSoftware() throws Throwable {
+ setupActivity();
AnimatedImageDrawable drawable = createFromImageDecoder(RES_ID);
Bitmap bm = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(),
@@ -332,16 +325,19 @@
@Test
@FlakyTest (bugId = 72737527)
public void testAddCallbackAfterStart() throws Throwable {
+ setupActivity();
AnimatedImageDrawable drawable = createFromImageDecoder(RES_ID);
Callback cb = new Callback(drawable);
- mActivityRule.runOnUiThread(() -> {
- setContentView(drawable);
+ WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mImageView, () -> {
+ mImageView.setImageDrawable(drawable);
drawable.setRepeatCount(0);
drawable.start();
drawable.registerAnimationCallback(cb);
});
+ // FIXME: Now that it seems the reason for the flakiness has been solved (b/129400990),
+ // reduce this extra duration workaround.
// Add extra duration to wait for the message posted by the end of the
// animation. This should help fix flakiness.
cb.waitForEnd(DURATION * 10);
@@ -350,10 +346,11 @@
@Test
public void testStop() throws Throwable {
+ setupActivity();
AnimatedImageDrawable drawable = createFromImageDecoder(RES_ID);
Callback cb = new Callback(drawable);
- mActivityRule.runOnUiThread(() -> {
- setContentView(drawable);
+ WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mImageView, () -> {
+ mImageView.setImageDrawable(drawable);
drawable.registerAnimationCallback(cb);
@@ -377,42 +374,45 @@
@Test
@FlakyTest (bugId = 72737527)
- public void testRepeatCounts() throws Throwable {
- for (int repeatCount : new int[] { 3, 5, 7, 16 }) {
- AnimatedImageDrawable drawable = createFromImageDecoder(RES_ID);
- assertEquals(AnimatedImageDrawable.REPEAT_INFINITE, drawable.getRepeatCount());
+ @Parameters({ "3", "5", "7", "16" })
+ public void testRepeatCounts(int repeatCount) throws Throwable {
+ setupActivity();
+ AnimatedImageDrawable drawable = createFromImageDecoder(RES_ID);
+ assertEquals(AnimatedImageDrawable.REPEAT_INFINITE, drawable.getRepeatCount());
- Callback cb = new Callback(drawable);
- mActivityRule.runOnUiThread(() -> {
- setContentView(drawable);
+ Callback cb = new Callback(drawable);
+ WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mImageView, () -> {
+ mImageView.setImageDrawable(drawable);
- drawable.registerAnimationCallback(cb);
- drawable.setRepeatCount(repeatCount);
- assertEquals(repeatCount, drawable.getRepeatCount());
- drawable.start();
- });
+ drawable.registerAnimationCallback(cb);
+ drawable.setRepeatCount(repeatCount);
+ assertEquals(repeatCount, drawable.getRepeatCount());
+ drawable.start();
+ });
- cb.waitForStart();
- cb.assertStarted(true);
+ cb.waitForStart();
+ cb.assertStarted(true);
- // The animation runs repeatCount + 1 total times.
- cb.waitForEnd(DURATION * repeatCount);
- cb.assertEnded(false);
+ // The animation runs repeatCount + 1 total times.
+ cb.waitForEnd(DURATION * repeatCount);
+ cb.assertEnded(false);
- cb.waitForEnd(DURATION * 20);
- cb.assertEnded(true);
+ // FIXME: Now that it seems the reason for the flakiness has been solved (b/129400990),
+ // reduce this extra duration workaround.
+ cb.waitForEnd(DURATION * 20);
+ cb.assertEnded(true);
- drawable.setRepeatCount(AnimatedImageDrawable.REPEAT_INFINITE);
- assertEquals(AnimatedImageDrawable.REPEAT_INFINITE, drawable.getRepeatCount());
- }
+ drawable.setRepeatCount(AnimatedImageDrawable.REPEAT_INFINITE);
+ assertEquals(AnimatedImageDrawable.REPEAT_INFINITE, drawable.getRepeatCount());
}
@Test
public void testRepeatCountInfinite() throws Throwable {
+ setupActivity();
AnimatedImageDrawable drawable = createFromImageDecoder(RES_ID);
Callback cb = new Callback(drawable);
- mActivityRule.runOnUiThread(() -> {
- setContentView(drawable);
+ WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mImageView, () -> {
+ mImageView.setImageDrawable(drawable);
drawable.registerAnimationCallback(cb);
drawable.setRepeatCount(AnimatedImageDrawable.REPEAT_INFINITE);
@@ -522,8 +522,8 @@
AnimatedImageDrawable testDrawable = null;
Uri uri = null;
try {
- uri = getAsResourceUri(RES_ID);
- ImageDecoder.Source source = ImageDecoder.createSource(mContentResolver, uri);
+ uri = Utils.getAsResourceUri(RES_ID);
+ ImageDecoder.Source source = ImageDecoder.createSource(getContentResolver(), uri);
Drawable dr = ImageDecoder.decodeDrawable(source, (decoder, info, src) -> {
decoder.setPostProcessor((canvas) -> {
canvas.drawRect(rectCreator.apply(canvas.getWidth(),
@@ -550,32 +550,36 @@
@Test
public void testCreateFromXml() throws XmlPullParserException, IOException {
- XmlPullParser parser = mRes.getXml(R.drawable.animatedimagedrawable_tag);
- Drawable drawable = Drawable.createFromXml(mRes, parser);
+ Resources res = getResources();
+ XmlPullParser parser = res.getXml(R.drawable.animatedimagedrawable_tag);
+ Drawable drawable = Drawable.createFromXml(res, parser);
assertNotNull(drawable);
assertTrue(drawable instanceof AnimatedImageDrawable);
}
@Test
public void testCreateFromXmlClass() throws XmlPullParserException, IOException {
- XmlPullParser parser = mRes.getXml(R.drawable.animatedimagedrawable);
- Drawable drawable = Drawable.createFromXml(mRes, parser);
+ Resources res = getResources();
+ XmlPullParser parser = res.getXml(R.drawable.animatedimagedrawable);
+ Drawable drawable = Drawable.createFromXml(res, parser);
assertNotNull(drawable);
assertTrue(drawable instanceof AnimatedImageDrawable);
}
@Test
public void testCreateFromXmlClassAttribute() throws XmlPullParserException, IOException {
- XmlPullParser parser = mRes.getXml(R.drawable.animatedimagedrawable_class);
- Drawable drawable = Drawable.createFromXml(mRes, parser);
+ Resources res = getResources();
+ XmlPullParser parser = res.getXml(R.drawable.animatedimagedrawable_class);
+ Drawable drawable = Drawable.createFromXml(res, parser);
assertNotNull(drawable);
assertTrue(drawable instanceof AnimatedImageDrawable);
}
@Test(expected=XmlPullParserException.class)
public void testMissingSrcInflate() throws XmlPullParserException, IOException {
- XmlPullParser parser = mRes.getXml(R.drawable.animatedimagedrawable_nosrc);
- Drawable drawable = Drawable.createFromXml(mRes, parser);
+ Resources res = getResources();
+ XmlPullParser parser = res.getXml(R.drawable.animatedimagedrawable_nosrc);
+ Drawable drawable = Drawable.createFromXml(res, parser);
}
@Test
@@ -600,8 +604,9 @@
}
private AnimatedImageDrawable parseXml(int resId) throws XmlPullParserException, IOException {
- XmlPullParser parser = mRes.getXml(resId);
- Drawable drawable = Drawable.createFromXml(mRes, parser);
+ Resources res = getResources();
+ XmlPullParser parser = res.getXml(resId);
+ Drawable drawable = Drawable.createFromXml(res, parser);
assertNotNull(drawable);
assertTrue(drawable instanceof AnimatedImageDrawable);
return (AnimatedImageDrawable) drawable;
@@ -666,8 +671,9 @@
@Test
public void testRepeatCountFromXml() throws XmlPullParserException, IOException {
- XmlPullParser parser = mRes.getXml(R.drawable.animatedimagedrawable_loop_count);
- Drawable drawable = Drawable.createFromXml(mRes, parser);
+ Resources res = getResources();
+ XmlPullParser parser = res.getXml(R.drawable.animatedimagedrawable_loop_count);
+ Drawable drawable = Drawable.createFromXml(res, parser);
assertNotNull(drawable);
assertTrue(drawable instanceof AnimatedImageDrawable);
@@ -678,15 +684,16 @@
@Test
public void testInfiniteRepeatCountFromXml() throws XmlPullParserException, IOException {
// This image has an encoded repeat count of 1. Verify that.
- Drawable drawable = mRes.getDrawable(R.drawable.animated_one_loop);
+ Resources res = getResources();
+ Drawable drawable = res.getDrawable(R.drawable.animated_one_loop);
assertNotNull(drawable);
assertTrue(drawable instanceof AnimatedImageDrawable);
AnimatedImageDrawable aid = (AnimatedImageDrawable) drawable;
assertEquals(1, aid.getRepeatCount());
// This layout uses the same image and overrides the repeat count to infinity.
- XmlPullParser parser = mRes.getXml(R.drawable.animatedimagedrawable_loop_count_infinite);
- drawable = Drawable.createFromXml(mRes, parser);
+ XmlPullParser parser = res.getXml(R.drawable.animatedimagedrawable_loop_count_infinite);
+ drawable = Drawable.createFromXml(res, parser);
assertNotNull(drawable);
assertTrue(drawable instanceof AnimatedImageDrawable);
@@ -697,8 +704,9 @@
// Verify that decoding on the AnimatedImageThread works.
private void decodeInBackground(AnimatedImageDrawable drawable) throws Throwable {
final Callback cb = new Callback(drawable);
- mActivityRule.runOnUiThread(() -> {
- setContentView(drawable);
+ WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mImageView, () -> {
+ mImageView.setImageDrawable(drawable);
+
drawable.registerAnimationCallback(cb);
drawable.start();
});
@@ -713,9 +721,11 @@
@Test
public void testInputStream() throws Throwable {
- try (InputStream in = mRes.openRawResource(R.drawable.animated)) {
+ setupActivity();
+ Resources res = getResources();
+ try (InputStream in = res.openRawResource(R.drawable.animated)) {
ImageDecoder.Source src =
- ImageDecoder.createSource(mRes, in, Bitmap.DENSITY_NONE);
+ ImageDecoder.createSource(res, in, Bitmap.DENSITY_NONE);
AnimatedImageDrawable drawable =
(AnimatedImageDrawable) ImageDecoder.decodeDrawable(src);
decodeInBackground(drawable);
@@ -725,7 +735,7 @@
private byte[] getAsByteArray() {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
- try (InputStream in = mRes.openRawResource(RES_ID)) {
+ try (InputStream in = getResources().openRawResource(RES_ID)) {
byte[] buf = new byte[4096];
int bytesRead;
while ((bytesRead = in.read(buf)) != -1) {
@@ -758,6 +768,7 @@
@Test
public void testByteBuffer() throws Throwable {
+ setupActivity();
// Natively, this tests ByteArrayStream.
byte[] array = getAsByteArray();
ByteBuffer byteBuffer = ByteBuffer.wrap(array);
@@ -767,6 +778,7 @@
@Test
public void testReadOnlyByteBuffer() throws Throwable {
+ setupActivity();
// Natively, this tests ByteBufferStream.
byte[] array = getAsByteArray();
ByteBuffer byteBuffer = ByteBuffer.wrap(array).asReadOnlyBuffer();
@@ -776,6 +788,7 @@
@Test
public void testDirectByteBuffer() throws Throwable {
+ setupActivity();
ByteBuffer byteBuffer = getAsDirectByteBuffer();
final AnimatedImageDrawable drawable = createFromByteBuffer(byteBuffer);
decodeInBackground(drawable);
diff --git a/tests/tests/hardware/res/raw/asus_gamepad_register.json b/tests/tests/hardware/res/raw/asus_gamepad_register.json
index dd422a4..64cf5e4 100644
--- a/tests/tests/hardware/res/raw/asus_gamepad_register.json
+++ b/tests/tests/hardware/res/raw/asus_gamepad_register.json
@@ -4,6 +4,7 @@
"name": "Asus Gamepad (Test)",
"vid": 0x0b05,
"pid": 0x4500,
+ "bus": "bluetooth",
"descriptor": [0x05, 0x01, 0x09, 0x05, 0xa1, 0x01, 0x85, 0x01, 0x05, 0x09, 0x0a, 0x01, 0x00,
0x0a, 0x02, 0x00, 0x0a, 0x04, 0x00, 0x0a, 0x05, 0x00, 0x0a, 0x07, 0x00, 0x0a, 0x08, 0x00,
0x0a, 0x0e, 0x00, 0x0a, 0x0f, 0x00, 0x0a, 0x0d, 0x00, 0x05, 0x0c, 0x0a, 0x24, 0x02, 0x0a,
diff --git a/tests/tests/hardware/res/raw/microsoft_designer_keyboard_register.json b/tests/tests/hardware/res/raw/microsoft_designer_keyboard_register.json
index a426740..263b14d 100644
--- a/tests/tests/hardware/res/raw/microsoft_designer_keyboard_register.json
+++ b/tests/tests/hardware/res/raw/microsoft_designer_keyboard_register.json
@@ -4,6 +4,7 @@
"name": "Designer Keyboard (Test)",
"vid": 0x045e,
"pid": 0x0806,
+ "bus": "bluetooth",
"descriptor": [
0x06, 0xbc, 0xff, 0x09, 0x88, 0xa1, 0x01, 0x85, 0x22, 0x06, 0x00, 0xff,
0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x13, 0x0a, 0x0a, 0xfa,
diff --git a/tests/tests/hardware/res/raw/microsoft_sculpttouch_register.json b/tests/tests/hardware/res/raw/microsoft_sculpttouch_register.json
index a9fd5ab..8ee16b4 100644
--- a/tests/tests/hardware/res/raw/microsoft_sculpttouch_register.json
+++ b/tests/tests/hardware/res/raw/microsoft_sculpttouch_register.json
@@ -4,6 +4,7 @@
"name": "Microsoft Sculpt Touch Mouse (Test)",
"vid": 0x045e,
"pid": 0x077c,
+ "bus": "bluetooth",
"descriptor": [
0x05, 0x01, 0x09, 0x02, 0xa1, 0x01, 0x05, 0x01, 0x09, 0x02, 0xa1, 0x02,
0x85, 0x1a, 0x09, 0x01, 0xa1, 0x00, 0x05, 0x09, 0x19, 0x01, 0x29, 0x05,
diff --git a/tests/tests/hardware/res/raw/microsoft_xboxones_register.json b/tests/tests/hardware/res/raw/microsoft_xboxones_register.json
index c1757e8..d789297 100755
--- a/tests/tests/hardware/res/raw/microsoft_xboxones_register.json
+++ b/tests/tests/hardware/res/raw/microsoft_xboxones_register.json
@@ -1,28 +1,31 @@
{
- "id": 1,
- "command": "register",
- "name": "Xbox One S (Bluetooth Test)",
- "vid": 0x045e,
- "pid": 0x02fd,
- "descriptor": [0x05, 0x01, 0x09, 0x05, 0xa1, 0x01, 0x85, 0x01, 0x09, 0x01, 0xa1, 0x00, 0x09, 0x30,
- 0x09, 0x31, 0x15, 0x00, 0x27, 0xff, 0xff, 0x00, 0x00, 0x95, 0x02, 0x75, 0x10, 0x81, 0x02, 0xc0,
- 0x09, 0x01, 0xa1, 0x00, 0x09, 0x32, 0x09, 0x35, 0x15, 0x00, 0x27, 0xff, 0xff, 0x00, 0x00, 0x95,
- 0x02, 0x75, 0x10, 0x81, 0x02, 0xc0, 0x05, 0x02, 0x09, 0xc5, 0x15, 0x00, 0x26, 0xff, 0x03, 0x95,
- 0x01, 0x75, 0x0a, 0x81, 0x02, 0x15, 0x00, 0x25, 0x00, 0x75, 0x06, 0x95, 0x01, 0x81, 0x03, 0x05,
- 0x02, 0x09, 0xc4, 0x15, 0x00, 0x26, 0xff, 0x03, 0x95, 0x01, 0x75, 0x0a, 0x81, 0x02, 0x15, 0x00,
- 0x25, 0x00, 0x75, 0x06, 0x95, 0x01, 0x81, 0x03, 0x05, 0x01, 0x09, 0x39, 0x15, 0x01, 0x25, 0x08,
- 0x35, 0x00, 0x46, 0x3b, 0x01, 0x66, 0x14, 0x00, 0x75, 0x04, 0x95, 0x01, 0x81, 0x42, 0x75, 0x04,
- 0x95, 0x01, 0x15, 0x00, 0x25, 0x00, 0x35, 0x00, 0x45, 0x00, 0x65, 0x00, 0x81, 0x03, 0x05, 0x09,
- 0x19, 0x01, 0x29, 0x0f, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x0f, 0x81, 0x02, 0x15, 0x00,
- 0x25, 0x00, 0x75, 0x01, 0x95, 0x01, 0x81, 0x03, 0x05, 0x0c, 0x0a, 0x24, 0x02, 0x15, 0x00, 0x25,
- 0x01, 0x95, 0x01, 0x75, 0x01, 0x81, 0x02, 0x15, 0x00, 0x25, 0x00, 0x75, 0x07, 0x95, 0x01, 0x81,
- 0x03, 0x05, 0x0c, 0x09, 0x01, 0x85, 0x02, 0xa1, 0x01, 0x05, 0x0c, 0x0a, 0x23, 0x02, 0x15, 0x00,
- 0x25, 0x01, 0x95, 0x01, 0x75, 0x01, 0x81, 0x02, 0x15, 0x00, 0x25, 0x00, 0x75, 0x07, 0x95, 0x01,
- 0x81, 0x03, 0xc0, 0x05, 0x0f, 0x09, 0x21, 0x85, 0x03, 0xa1, 0x02, 0x09, 0x97, 0x15, 0x00, 0x25,
- 0x01, 0x75, 0x04, 0x95, 0x01, 0x91, 0x02, 0x15, 0x00, 0x25, 0x00, 0x75, 0x04, 0x95, 0x01, 0x91,
- 0x03, 0x09, 0x70, 0x15, 0x00, 0x25, 0x64, 0x75, 0x08, 0x95, 0x04, 0x91, 0x02, 0x09, 0x50, 0x66,
- 0x01, 0x10, 0x55, 0x0e, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x01, 0x91, 0x02, 0x09,
- 0xa7, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x01, 0x91, 0x02, 0x65, 0x00, 0x55, 0x00,
- 0x09, 0x7c, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x01, 0x91, 0x02, 0xc0, 0x05, 0x06,
- 0x09, 0x20, 0x85, 0x04, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x01, 0x81, 0x02, 0xc0]
- }
+ "id": 1,
+ "command": "register",
+ "name": "Xbox One S (Bluetooth Test)",
+ "vid": 0x045e,
+ "pid": 0x02fd,
+ "bus": "bluetooth",
+ "descriptor": [
+ 0x05, 0x01, 0x09, 0x05, 0xa1, 0x01, 0x85, 0x01, 0x09, 0x01, 0xa1, 0x00, 0x09, 0x30,
+ 0x09, 0x31, 0x15, 0x00, 0x27, 0xff, 0xff, 0x00, 0x00, 0x95, 0x02, 0x75, 0x10, 0x81, 0x02, 0xc0,
+ 0x09, 0x01, 0xa1, 0x00, 0x09, 0x32, 0x09, 0x35, 0x15, 0x00, 0x27, 0xff, 0xff, 0x00, 0x00, 0x95,
+ 0x02, 0x75, 0x10, 0x81, 0x02, 0xc0, 0x05, 0x02, 0x09, 0xc5, 0x15, 0x00, 0x26, 0xff, 0x03, 0x95,
+ 0x01, 0x75, 0x0a, 0x81, 0x02, 0x15, 0x00, 0x25, 0x00, 0x75, 0x06, 0x95, 0x01, 0x81, 0x03, 0x05,
+ 0x02, 0x09, 0xc4, 0x15, 0x00, 0x26, 0xff, 0x03, 0x95, 0x01, 0x75, 0x0a, 0x81, 0x02, 0x15, 0x00,
+ 0x25, 0x00, 0x75, 0x06, 0x95, 0x01, 0x81, 0x03, 0x05, 0x01, 0x09, 0x39, 0x15, 0x01, 0x25, 0x08,
+ 0x35, 0x00, 0x46, 0x3b, 0x01, 0x66, 0x14, 0x00, 0x75, 0x04, 0x95, 0x01, 0x81, 0x42, 0x75, 0x04,
+ 0x95, 0x01, 0x15, 0x00, 0x25, 0x00, 0x35, 0x00, 0x45, 0x00, 0x65, 0x00, 0x81, 0x03, 0x05, 0x09,
+ 0x19, 0x01, 0x29, 0x0f, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x0f, 0x81, 0x02, 0x15, 0x00,
+ 0x25, 0x00, 0x75, 0x01, 0x95, 0x01, 0x81, 0x03, 0x05, 0x0c, 0x0a, 0x24, 0x02, 0x15, 0x00, 0x25,
+ 0x01, 0x95, 0x01, 0x75, 0x01, 0x81, 0x02, 0x15, 0x00, 0x25, 0x00, 0x75, 0x07, 0x95, 0x01, 0x81,
+ 0x03, 0x05, 0x0c, 0x09, 0x01, 0x85, 0x02, 0xa1, 0x01, 0x05, 0x0c, 0x0a, 0x23, 0x02, 0x15, 0x00,
+ 0x25, 0x01, 0x95, 0x01, 0x75, 0x01, 0x81, 0x02, 0x15, 0x00, 0x25, 0x00, 0x75, 0x07, 0x95, 0x01,
+ 0x81, 0x03, 0xc0, 0x05, 0x0f, 0x09, 0x21, 0x85, 0x03, 0xa1, 0x02, 0x09, 0x97, 0x15, 0x00, 0x25,
+ 0x01, 0x75, 0x04, 0x95, 0x01, 0x91, 0x02, 0x15, 0x00, 0x25, 0x00, 0x75, 0x04, 0x95, 0x01, 0x91,
+ 0x03, 0x09, 0x70, 0x15, 0x00, 0x25, 0x64, 0x75, 0x08, 0x95, 0x04, 0x91, 0x02, 0x09, 0x50, 0x66,
+ 0x01, 0x10, 0x55, 0x0e, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x01, 0x91, 0x02, 0x09,
+ 0xa7, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x01, 0x91, 0x02, 0x65, 0x00, 0x55, 0x00,
+ 0x09, 0x7c, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x01, 0x91, 0x02, 0xc0, 0x05, 0x06,
+ 0x09, 0x20, 0x85, 0x04, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x01, 0x81, 0x02, 0xc0
+ ]
+}
diff --git a/tests/tests/hardware/res/raw/razer_serval_register.json b/tests/tests/hardware/res/raw/razer_serval_register.json
index 3d8bb96..ab27177 100644
--- a/tests/tests/hardware/res/raw/razer_serval_register.json
+++ b/tests/tests/hardware/res/raw/razer_serval_register.json
@@ -4,6 +4,7 @@
"name": "Razer Serval (Test)",
"vid": 0x1532,
"pid": 0x0900,
+ "bus": "bluetooth",
"descriptor": [
0x05, 0x01, 0x09, 0x05, 0xa1, 0x01, 0xa1, 0x02, 0x85, 0x01, 0x75, 0x08, 0x95, 0x04,
0x15, 0x00, 0x26, 0xff, 0x00, 0x09, 0x30, 0x09, 0x31, 0x09, 0x32, 0x09, 0x35, 0x81,
diff --git a/tests/tests/hardware/res/raw/sony_dualshock3_usb_keyeventtests.json b/tests/tests/hardware/res/raw/sony_dualshock3_usb_keyeventtests.json
new file mode 100644
index 0000000..fd152c2
--- /dev/null
+++ b/tests/tests/hardware/res/raw/sony_dualshock3_usb_keyeventtests.json
@@ -0,0 +1,325 @@
+[
+ {
+ "name": "Press BUTTON_A",
+ "reports": [
+ [0x01, 0x00, 0x00, 0x40, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+ 0x02, 0x33, 0x01, 0xeb],
+ [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+ 0x02, 0x33, 0x01, 0xeb]
+ ],
+ "source": "KEYBOARD | GAMEPAD",
+ "events": [
+ {"action": "DOWN", "keycode": "BUTTON_A"},
+ {"action": "UP", "keycode": "BUTTON_A"}
+ ]
+ },
+
+ {
+ "name": "Press BUTTON_B",
+ "reports": [
+ [0x01, 0x00, 0x00, 0x20, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+ 0x02, 0x33, 0x01, 0xeb],
+ [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+ 0x02, 0x33, 0x01, 0xeb]
+ ],
+ "source": "KEYBOARD | GAMEPAD",
+ "events": [
+ {"action": "DOWN", "keycode": "BUTTON_B"},
+ {"action": "UP", "keycode": "BUTTON_B"}
+ ]
+ },
+
+ {
+ "name": "Press BUTTON_X",
+ "reports": [
+ [0x01, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+ 0x02, 0x33, 0x01, 0xeb],
+ [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+ 0x02, 0x33, 0x01, 0xeb]
+ ],
+ "source": "KEYBOARD | GAMEPAD",
+ "events": [
+ {"action": "DOWN", "keycode": "BUTTON_X"},
+ {"action": "UP", "keycode": "BUTTON_X"}
+ ]
+ },
+
+ {
+ "name": "Press BUTTON_Y",
+ "reports": [
+ [0x01, 0x00, 0x00, 0x10, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+ 0x02, 0x33, 0x01, 0xeb],
+ [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+ 0x02, 0x33, 0x01, 0xeb]
+ ],
+ "source": "KEYBOARD | GAMEPAD",
+ "events": [
+ {"action": "DOWN", "keycode": "BUTTON_Y"},
+ {"action": "UP", "keycode": "BUTTON_Y"}
+ ]
+ },
+
+ {
+ "name": "Press BUTTON_L1",
+ "reports": [
+ [0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+ 0x02, 0x33, 0x01, 0xeb],
+ [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+ 0x02, 0x33, 0x01, 0xeb]
+ ],
+ "source": "KEYBOARD | GAMEPAD",
+ "events": [
+ {"action": "DOWN", "keycode": "BUTTON_L1"},
+ {"action": "UP", "keycode": "BUTTON_L1"}
+ ]
+ },
+
+ {
+ "name": "Press BUTTON_R1",
+ "reports": [
+ [0x01, 0x00, 0x00, 0x08, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+ 0x02, 0x33, 0x01, 0xeb],
+ [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+ 0x02, 0x33, 0x01, 0xeb]
+ ],
+ "source": "KEYBOARD | GAMEPAD",
+ "events": [
+ {"action": "DOWN", "keycode": "BUTTON_R1"},
+ {"action": "UP", "keycode": "BUTTON_R1"}
+ ]
+ },
+
+ {
+ "name": "Press BUTTON_L2",
+ "reports": [
+ [0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+ 0x02, 0x33, 0x01, 0xeb],
+ [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+ 0x02, 0x33, 0x01, 0xeb]
+ ],
+ "source": "KEYBOARD | GAMEPAD",
+ "events": [
+ {"action": "DOWN", "keycode": "BUTTON_L2"},
+ {"action": "UP", "keycode": "BUTTON_L2"}
+ ]
+ },
+
+ {
+ "name": "Press BUTTON_R2",
+ "reports": [
+ [0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+ 0x02, 0x33, 0x01, 0xeb],
+ [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+ 0x02, 0x33, 0x01, 0xeb]
+ ],
+ "source": "KEYBOARD | GAMEPAD",
+ "events": [
+ {"action": "DOWN", "keycode": "BUTTON_R2"},
+ {"action": "UP", "keycode": "BUTTON_R2"}
+ ]
+ },
+
+ {
+ "name": "Press BUTTON_L3",
+ "reports": [
+ [0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+ 0x02, 0x33, 0x01, 0xeb],
+ [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+ 0x02, 0x33, 0x01, 0xeb]
+ ],
+ "source": "KEYBOARD | GAMEPAD",
+ "events": [
+ {"action": "DOWN", "keycode": "BUTTON_THUMBL"},
+ {"action": "UP", "keycode": "BUTTON_THUMBL"}
+ ]
+ },
+
+ {
+ "name": "Press BUTTON_R3",
+ "reports": [
+ [0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+ 0x02, 0x33, 0x01, 0xeb],
+ [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+ 0x02, 0x33, 0x01, 0xeb]
+ ],
+ "source": "KEYBOARD | GAMEPAD",
+ "events": [
+ {"action": "DOWN", "keycode": "BUTTON_THUMBR"},
+ {"action": "UP", "keycode": "BUTTON_THUMBR"}
+ ]
+ },
+
+ {
+ "name": "Press BUTTON_SELECT",
+ "reports": [
+ [0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+ 0x02, 0x33, 0x01, 0xeb],
+ [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+ 0x02, 0x33, 0x01, 0xeb]
+ ],
+ "source": "KEYBOARD | GAMEPAD",
+ "events": [
+ {"action": "DOWN", "keycode": "BUTTON_SELECT"},
+ {"action": "UP", "keycode": "BUTTON_SELECT"}
+ ]
+ },
+
+ {
+ "name": "Press BUTTON_START",
+ "reports": [
+ [0x01, 0x00, 0x08, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+ 0x02, 0x33, 0x01, 0xeb],
+ [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+ 0x02, 0x33, 0x01, 0xeb]
+ ],
+ "source": "KEYBOARD | GAMEPAD",
+ "events": [
+ {"action": "DOWN", "keycode": "BUTTON_START"},
+ {"action": "UP", "keycode": "BUTTON_START"}
+ ]
+ },
+
+ {
+ "name": "Press BUTTON_PS",
+ "reports": [
+ [0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+ 0x02, 0x33, 0x01, 0xeb],
+ [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+ 0x02, 0x33, 0x01, 0xeb]
+ ],
+ "source": "KEYBOARD | GAMEPAD",
+ "events": [
+ {"action": "DOWN", "keycode": "BUTTON_MODE"},
+ {"action": "UP", "keycode": "BUTTON_MODE"}
+ ]
+ },
+
+ {
+ "name": "Press left DPAD key",
+ "reports": [
+ [0x01, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+ 0x02, 0x33, 0x01, 0xeb],
+ [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+ 0x02, 0x33, 0x01, 0xeb]
+ ],
+ "source": "KEYBOARD | GAMEPAD",
+ "events": [
+ {"action": "DOWN", "keycode": "KEYCODE_DPAD_LEFT"},
+ {"action": "UP", "keycode": "KEYCODE_DPAD_LEFT"}
+ ]
+ },
+
+ {
+ "name": "Press right DPAD key",
+ "reports": [
+ [0x01, 0x00, 0x20, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+ 0x02, 0x33, 0x01, 0xeb],
+ [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+ 0x02, 0x33, 0x01, 0xeb]
+ ],
+ "source": "KEYBOARD | GAMEPAD",
+ "events": [
+ {"action": "DOWN", "keycode": "KEYCODE_DPAD_RIGHT"},
+ {"action": "UP", "keycode": "KEYCODE_DPAD_RIGHT"}
+ ]
+ },
+
+ {
+ "name": "Press up DPAD key",
+ "reports": [
+ [0x01, 0x00, 0x10, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+ 0x02, 0x33, 0x01, 0xeb],
+ [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+ 0x02, 0x33, 0x01, 0xeb]
+ ],
+ "source": "KEYBOARD | GAMEPAD",
+ "events": [
+ {"action": "DOWN", "keycode": "KEYCODE_DPAD_UP"},
+ {"action": "UP", "keycode": "KEYCODE_DPAD_UP"}
+ ]
+ },
+
+ {
+ "name": "Press down DPAD key",
+ "reports": [
+ [0x01, 0x00, 0x40, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+ 0x02, 0x33, 0x01, 0xeb],
+ [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+ 0x02, 0x33, 0x01, 0xeb]
+ ],
+ "source": "KEYBOARD | GAMEPAD",
+ "events": [
+ {"action": "DOWN", "keycode": "KEYCODE_DPAD_DOWN"},
+ {"action": "UP", "keycode": "KEYCODE_DPAD_DOWN"}
+ ]
+ }
+]
+
diff --git a/tests/tests/hardware/res/raw/sony_dualshock3_usb_motioneventtests.json b/tests/tests/hardware/res/raw/sony_dualshock3_usb_motioneventtests.json
new file mode 100644
index 0000000..bae0f94
--- /dev/null
+++ b/tests/tests/hardware/res/raw/sony_dualshock3_usb_motioneventtests.json
@@ -0,0 +1,257 @@
+[
+ {
+ "name": "Sanity check - should not produce any events",
+ "reports": [
+ [0x11, 0xc0, 0x00, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00,
+ 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c,
+ 0x2e, 0x80, 0xf2]
+ ],
+ "source": "JOYSTICK",
+ "events": [
+ ]
+ },
+
+ {
+ "name": "Left stick - press left",
+ "reports": [
+ [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+ 0x02, 0x33, 0x01, 0xeb],
+ [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+ 0x02, 0x33, 0x01, 0xeb],
+ [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+ 0x02, 0x33, 0x01, 0xeb]
+ ],
+ "source": "JOYSTICK",
+ "events": [
+ {"action": "MOVE", "axes": {"AXIS_X": -0.5}},
+ {"action": "MOVE", "axes": {"AXIS_X": -1}},
+ {"action": "MOVE", "axes": {"AXIS_X": 0}}
+ ]
+ },
+
+ {
+ "name": "Left stick - press right",
+ "reports": [
+ [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+ 0x02, 0x33, 0x01, 0xeb],
+ [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+ 0x02, 0x33, 0x01, 0xeb],
+ [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+ 0x02, 0x33, 0x01, 0xeb]
+ ],
+ "source": "JOYSTICK",
+ "events": [
+ {"action": "MOVE", "axes": {"AXIS_X": 0.51}},
+ {"action": "MOVE", "axes": {"AXIS_X": 1}},
+ {"action": "MOVE", "axes": {"AXIS_X": 0}}
+ ]
+ },
+
+ {
+ "name": "Left stick - press up",
+ "reports": [
+ [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x40, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+ 0x02, 0x33, 0x01, 0xeb],
+ [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+ 0x02, 0x33, 0x01, 0xeb],
+ [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+ 0x02, 0x33, 0x01, 0xeb]
+ ],
+ "source": "JOYSTICK",
+ "events": [
+ {"action": "MOVE", "axes": {"AXIS_Y": -0.5}},
+ {"action": "MOVE", "axes": {"AXIS_Y": -1}},
+ {"action": "MOVE", "axes": {"AXIS_Y": 0}}
+ ]
+ },
+
+ {
+ "name": "Left stick - press down",
+ "reports": [
+ [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc0, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+ 0x02, 0x33, 0x01, 0xeb],
+ [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xff, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+ 0x02, 0x33, 0x01, 0xeb],
+ [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+ 0x02, 0x33, 0x01, 0xeb]
+ ],
+ "source": "JOYSTICK",
+ "events": [
+ {"action": "MOVE", "axes": {"AXIS_Y": 0.51}},
+ {"action": "MOVE", "axes": {"AXIS_Y": 1}},
+ {"action": "MOVE", "axes": {"AXIS_Y": 0}}
+ ]
+ },
+
+ {
+ "name": "Right stick - press left",
+ "reports": [
+ [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x40, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+ 0x02, 0x33, 0x01, 0xeb],
+ [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+ 0x02, 0x33, 0x01, 0xeb],
+ [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+ 0x02, 0x33, 0x01, 0xeb]
+ ],
+ "source": "JOYSTICK",
+ "events": [
+ {"action": "MOVE", "axes": {"AXIS_Z": -0.5}},
+ {"action": "MOVE", "axes": {"AXIS_Z": -1}},
+ {"action": "MOVE", "axes": {"AXIS_Z": 0}}
+ ]
+ },
+
+ {
+ "name": "Right stick - press right",
+ "reports": [
+ [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+ 0x02, 0x33, 0x01, 0xeb],
+ [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+ 0x02, 0x33, 0x01, 0xeb],
+ [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+ 0x02, 0x33, 0x01, 0xeb]
+ ],
+ "source": "JOYSTICK",
+ "events": [
+ {"action": "MOVE", "axes": {"AXIS_Z": 0.51}},
+ {"action": "MOVE", "axes": {"AXIS_Z": 1}},
+ {"action": "MOVE", "axes": {"AXIS_Z": 0}}
+ ]
+ },
+
+ {
+ "name": "Right stick - press up",
+ "reports": [
+ [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+ 0x02, 0x33, 0x01, 0xeb],
+ [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+ 0x02, 0x33, 0x01, 0xeb],
+ [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+ 0x02, 0x33, 0x01, 0xeb]
+ ],
+ "source": "JOYSTICK",
+ "events": [
+ {"action": "MOVE", "axes": {"AXIS_RZ": -0.5}},
+ {"action": "MOVE", "axes": {"AXIS_RZ": -1}},
+ {"action": "MOVE", "axes": {"AXIS_RZ": 0}}
+ ]
+ },
+
+ {
+ "name": "Right stick - press down",
+ "reports": [
+ [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+ 0x02, 0x33, 0x01, 0xeb],
+ [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+ 0x02, 0x33, 0x01, 0xeb],
+ [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+ 0x02, 0x33, 0x01, 0xeb]
+ ],
+ "source": "JOYSTICK",
+ "events": [
+ {"action": "MOVE", "axes": {"AXIS_RZ": 0.51}},
+ {"action": "MOVE", "axes": {"AXIS_RZ": 1}},
+ {"action": "MOVE", "axes": {"AXIS_RZ": 0}}
+ ]
+ },
+
+ {
+ "name": "Left trigger - quick press",
+ "reports": [
+ [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+ 0x02, 0x33, 0x01, 0xeb],
+ [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+ 0x02, 0x33, 0x01, 0xeb],
+ [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+ 0x02, 0x33, 0x01, 0xeb]
+ ],
+ "source": "JOYSTICK",
+ "events": [
+ {"action": "MOVE", "axes": {"AXIS_LTRIGGER": 0.5, "AXIS_BRAKE": 0.5}},
+ {"action": "MOVE", "axes": {"AXIS_LTRIGGER": 1.0, "AXIS_BRAKE": 1.0}},
+ {"action": "MOVE", "axes": {"AXIS_LTRIGGER": 0, "AXIS_BRAKE": 0}}
+ ]
+ },
+
+ {
+ "name": "Right trigger - quick press",
+ "reports": [
+ [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+ 0x02, 0x33, 0x01, 0xeb],
+ [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+ 0x02, 0x33, 0x01, 0xeb],
+ [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+ 0x02, 0x33, 0x01, 0xeb]
+ ],
+ "source": "JOYSTICK",
+ "events": [
+ {"action": "MOVE", "axes": {"AXIS_RTRIGGER": 0.5, "AXIS_GAS": 0.5}},
+ {"action": "MOVE", "axes": {"AXIS_RTRIGGER": 1.0, "AXIS_GAS": 1.0}},
+ {"action": "MOVE", "axes": {"AXIS_RTRIGGER": 0, "AXIS_GAS": 0}}
+ ]
+ }
+]
+
diff --git a/tests/tests/hardware/res/raw/sony_dualshock3_usb_register.json b/tests/tests/hardware/res/raw/sony_dualshock3_usb_register.json
new file mode 100644
index 0000000..3d22120
--- /dev/null
+++ b/tests/tests/hardware/res/raw/sony_dualshock3_usb_register.json
@@ -0,0 +1,30 @@
+{
+ "id": 1,
+ "command": "register",
+ "name": "Sony DS3 (model CECHZC2U)(USB Test)",
+ "vid": 0x054c,
+ "pid": 0x0268,
+ "bus": "usb",
+ "descriptor": [0x05, 0x01, 0x09, 0x04, 0xa1, 0x01, 0xa1, 0x02, 0x85, 0x01, 0x75, 0x08, 0x95, 0x01,
+ 0x15, 0x00, 0x26, 0xff, 0x00, 0x81, 0x03, 0x75, 0x01, 0x95, 0x13, 0x15, 0x00, 0x25, 0x01, 0x35,
+ 0x00, 0x45, 0x01, 0x05, 0x09, 0x19, 0x01, 0x29, 0x13, 0x81, 0x02, 0x75, 0x01, 0x95, 0x0d, 0x06,
+ 0x00, 0xff, 0x81, 0x03, 0x15, 0x00, 0x26, 0xff, 0x00, 0x05, 0x01, 0x09, 0x01, 0xa1, 0x00, 0x75,
+ 0x08, 0x95, 0x04, 0x35, 0x00, 0x46, 0xff, 0x00, 0x09, 0x30, 0x09, 0x31, 0x09, 0x32, 0x09, 0x35,
+ 0x81, 0x02, 0xc0, 0x05, 0x01, 0x75, 0x08, 0x95, 0x27, 0x09, 0x01, 0x81, 0x02, 0x75, 0x08, 0x95,
+ 0x30, 0x09, 0x01, 0x91, 0x02, 0x75, 0x08, 0x95, 0x30, 0x09, 0x01, 0xb1, 0x02, 0xc0, 0xa1, 0x02,
+ 0x85, 0x02, 0x75, 0x08, 0x95, 0x30, 0x09, 0x01, 0xb1, 0x02, 0xc0, 0xa1, 0x02, 0x85, 0xee, 0x75,
+ 0x08, 0x95, 0x30, 0x09, 0x01, 0xb1, 0x02, 0xc0, 0xa1, 0x02, 0x85, 0xef, 0x75, 0x08, 0x95, 0x30,
+ 0x09, 0x01, 0xb1, 0x02, 0xc0, 0xc0],
+ "feature_reports": [
+ {
+ "id": 0xf2,
+ "data": [0xf2, 0xff, 0xff, 0x00, 0x08, 0xa9, 0x5a, 0x15, 0x4a, 0xfe, 0x00, 0x03, 0x50, 0x81,
+ 0xd8, 0x01, 0x8a]
+ },
+ {
+ "id": 0xf5,
+ "data": [0x01, 0x00, 0xc8, 0xbc, 0xc8, 0xcf, 0x6f, 0xb8]
+ }
+ ]
+}
+
diff --git a/tests/tests/hardware/res/raw/sony_dualshock4_keyeventtests.json b/tests/tests/hardware/res/raw/sony_dualshock4_bluetooth_keyeventtests.json
similarity index 100%
rename from tests/tests/hardware/res/raw/sony_dualshock4_keyeventtests.json
rename to tests/tests/hardware/res/raw/sony_dualshock4_bluetooth_keyeventtests.json
diff --git a/tests/tests/hardware/res/raw/sony_dualshock4_motioneventtests.json b/tests/tests/hardware/res/raw/sony_dualshock4_bluetooth_motioneventtests.json
similarity index 100%
rename from tests/tests/hardware/res/raw/sony_dualshock4_motioneventtests.json
rename to tests/tests/hardware/res/raw/sony_dualshock4_bluetooth_motioneventtests.json
diff --git a/tests/tests/hardware/res/raw/sony_dualshock4_bluetooth_register.json b/tests/tests/hardware/res/raw/sony_dualshock4_bluetooth_register.json
new file mode 100644
index 0000000..4c49453
--- /dev/null
+++ b/tests/tests/hardware/res/raw/sony_dualshock4_bluetooth_register.json
@@ -0,0 +1,46 @@
+{
+ "id": 1,
+ "command": "register",
+ "name": "Sony DS4 (model CUH-ZCT1U)(Bluetooth Test)",
+ "vid": 0x054c,
+ "pid": 0x05c4,
+ "bus": "bluetooth",
+ "descriptor": [0x05, 0x01, 0x09, 0x05, 0xa1, 0x01, 0x85, 0x01, 0x09, 0x30, 0x09, 0x31, 0x09, 0x32,
+ 0x09, 0x35, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x04, 0x81, 0x02, 0x09, 0x39, 0x15,
+ 0x00, 0x25, 0x07, 0x75, 0x04, 0x95, 0x01, 0x81, 0x42, 0x05, 0x09, 0x19, 0x01, 0x29, 0x0e, 0x15,
+ 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x0e, 0x81, 0x02, 0x75, 0x06, 0x95, 0x01, 0x81, 0x01, 0x05,
+ 0x01, 0x09, 0x33, 0x09, 0x34, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x02, 0x81, 0x02,
+ 0x06, 0x04, 0xff, 0x85, 0x02, 0x09, 0x24, 0x95, 0x24, 0xb1, 0x02, 0x85, 0xa3, 0x09, 0x25, 0x95,
+ 0x30, 0xb1, 0x02, 0x85, 0x05, 0x09, 0x26, 0x95, 0x28, 0xb1, 0x02, 0x85, 0x06, 0x09, 0x27, 0x95,
+ 0x34, 0xb1, 0x02, 0x85, 0x07, 0x09, 0x28, 0x95, 0x30, 0xb1, 0x02, 0x85, 0x08, 0x09, 0x29, 0x95,
+ 0x2f, 0xb1, 0x02, 0x06, 0x03, 0xff, 0x85, 0x03, 0x09, 0x21, 0x95, 0x26, 0xb1, 0x02, 0x85, 0x04,
+ 0x09, 0x22, 0x95, 0x2e, 0xb1, 0x02, 0x85, 0xf0, 0x09, 0x47, 0x95, 0x3f, 0xb1, 0x02, 0x85, 0xf1,
+ 0x09, 0x48, 0x95, 0x3f, 0xb1, 0x02, 0x85, 0xf2, 0x09, 0x49, 0x95, 0x0f, 0xb1, 0x02, 0x06, 0x00,
+ 0xff, 0x85, 0x11, 0x09, 0x20, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x4d, 0x81, 0x02,
+ 0x09, 0x21, 0x91, 0x02, 0x85, 0x12, 0x09, 0x22, 0x95, 0x8d, 0x81, 0x02, 0x09, 0x23, 0x91, 0x02,
+ 0x85, 0x13, 0x09, 0x24, 0x95, 0xcd, 0x81, 0x02, 0x09, 0x25, 0x91, 0x02, 0x85, 0x14, 0x09, 0x26,
+ 0x96, 0x0d, 0x01, 0x81, 0x02, 0x09, 0x27, 0x91, 0x02, 0x85, 0x15, 0x09, 0x28, 0x96, 0x4d, 0x01,
+ 0x81, 0x02, 0x09, 0x29, 0x91, 0x02, 0x85, 0x16, 0x09, 0x2a, 0x96, 0x8d, 0x01, 0x81, 0x02, 0x09,
+ 0x2b, 0x91, 0x02, 0x85, 0x17, 0x09, 0x2c, 0x96, 0xcd, 0x01, 0x81, 0x02, 0x09, 0x2d, 0x91, 0x02,
+ 0x85, 0x18, 0x09, 0x2e, 0x96, 0x0d, 0x02, 0x81, 0x02, 0x09, 0x2f, 0x91, 0x02, 0x85, 0x19, 0x09,
+ 0x30, 0x96, 0x22, 0x02, 0x81, 0x02, 0x09, 0x31, 0x91, 0x02, 0x06, 0x80, 0xff, 0x85, 0x82, 0x09,
+ 0x22, 0x95, 0x3f, 0xb1, 0x02, 0x85, 0x83, 0x09, 0x23, 0xb1, 0x02, 0x85, 0x84, 0x09, 0x24, 0xb1,
+ 0x02, 0x85, 0x90, 0x09, 0x30, 0xb1, 0x02, 0x85, 0x91, 0x09, 0x31, 0xb1, 0x02, 0x85, 0x92, 0x09,
+ 0x32, 0xb1, 0x02, 0x85, 0x93, 0x09, 0x33, 0xb1, 0x02, 0x85, 0xa0, 0x09, 0x40, 0xb1, 0x02, 0x85,
+ 0xa4, 0x09, 0x44, 0xb1, 0x02, 0xc0],
+ "feature_reports": [
+ {
+ "id": 0x05,
+ "data": [0x05, 0x1e, 0x00, 0x05, 0x00, 0xe2, 0xff, 0xf2, 0x22, 0xbe, 0x22, 0x8d, 0x22, 0x4f,
+ 0xdd, 0x4d, 0xdd, 0x39, 0xdd, 0x1c, 0x02, 0x1c, 0x02, 0xe3, 0x1f, 0x8b, 0xdf, 0x8c, 0x1e,
+ 0xb4, 0xde, 0x30, 0x20, 0x71, 0xe0, 0x10, 0x00, 0xca, 0xfc, 0x64, 0x4d]
+ },
+ {
+ "id": 0xa3,
+ "data": [0xa3, 0x41, 0x70, 0x72, 0x20, 0x20, 0x38, 0x20, 0x32, 0x30, 0x31, 0x34, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x30, 0x39, 0x3a, 0x34, 0x36, 0x3a, 0x30, 0x36, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x43, 0x03, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05,
+ 0x00, 0x00, 0x80, 0x03, 0x00]
+ }
+ ]
+}
diff --git a/tests/tests/hardware/res/raw/sony_dualshock4_usb_keyeventtests.json b/tests/tests/hardware/res/raw/sony_dualshock4_usb_keyeventtests.json
new file mode 100644
index 0000000..17ab0c9
--- /dev/null
+++ b/tests/tests/hardware/res/raw/sony_dualshock4_usb_keyeventtests.json
@@ -0,0 +1,275 @@
+[
+ {
+ "name": "Press BUTTON_A",
+ "reports": [
+ [0x01, 0x80, 0x80, 0x80, 0x80, 0x28, 0x00, 0x8c, 0x00, 0x00, 0xfb, 0x8c, 0x10, 0x0e, 0x00,
+ 0x06, 0x00, 0xe6, 0xff, 0x09, 0x00, 0x87, 0x1d, 0x61, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x00],
+ [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+ 0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x00]
+ ],
+ "source": "KEYBOARD | GAMEPAD",
+ "events": [
+ {"action": "DOWN", "keycode": "BUTTON_A"},
+ {"action": "UP", "keycode": "BUTTON_A"}
+ ]
+ },
+
+ {
+ "name": "Press BUTTON_B",
+ "reports": [
+ [0x01, 0x80, 0x80, 0x80, 0x80, 0x48, 0x00, 0x5c, 0x00, 0x00, 0xf0, 0xd8, 0x12, 0x0f, 0x00,
+ 0x05, 0x00, 0xe6, 0xff, 0x68, 0x00, 0xf8, 0x1d, 0xc4, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x00],
+ [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+ 0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x00]
+ ],
+ "source": "KEYBOARD | GAMEPAD",
+ "events": [
+ {"action": "DOWN", "keycode": "BUTTON_B"},
+ {"action": "UP", "keycode": "BUTTON_B"}
+ ]
+ },
+
+ {
+ "name": "Press BUTTON_X",
+ "reports": [
+ [0x01, 0x80, 0x80, 0x80, 0x80, 0x18, 0x00, 0x68, 0x00, 0x00, 0xfe, 0x2f, 0x12, 0x0f, 0x00,
+ 0x04, 0x00, 0xe5, 0xff, 0x36, 0x00, 0xd3, 0x1d, 0xdb, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x00],
+ [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+ 0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x00]
+ ],
+ "source": "KEYBOARD | GAMEPAD",
+ "events": [
+ {"action": "DOWN", "keycode": "BUTTON_X"},
+ {"action": "UP", "keycode": "BUTTON_X"}
+ ]
+ },
+
+ {
+ "name": "Press BUTTON_Y",
+ "reports": [
+ [0x01, 0x80, 0x80, 0x80, 0x80, 0x88, 0x00, 0x58, 0x00, 0x00, 0x1f, 0x91, 0x12, 0x0e, 0x00,
+ 0x06, 0x00, 0xe4, 0xff, 0x41, 0x00, 0x30, 0x1e, 0xed, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1b, 0x00, 0x00, 0x01, 0x37, 0x81, 0x3e, 0x07, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x00],
+ [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+ 0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x00]
+ ],
+ "source": "KEYBOARD | GAMEPAD",
+ "events": [
+ {"action": "DOWN", "keycode": "BUTTON_Y"},
+ {"action": "UP", "keycode": "BUTTON_Y"}
+ ]
+ },
+
+ {
+ "name": "Press BUTTON_L1",
+ "reports": [
+ [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x01, 0xf0, 0x00, 0x00, 0xe9, 0x57, 0x12, 0x14, 0x00,
+ 0xf0, 0xff, 0xf3, 0xff, 0xbc, 0xfb, 0x58, 0x1d, 0xc4, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1b, 0x00, 0x00, 0x01, 0x37, 0x81, 0x3e, 0x07, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x00],
+ [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+ 0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x00]
+ ],
+ "source": "KEYBOARD | GAMEPAD",
+ "events": [
+ {"action": "DOWN", "keycode": "BUTTON_L1"},
+ {"action": "UP", "keycode": "BUTTON_L1"}
+ ]
+ },
+
+ {
+ "name": "Press BUTTON_R1",
+ "reports": [
+ [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x02, 0xfc, 0x00, 0x00, 0x2f, 0x36, 0x12, 0x0e, 0x00,
+ 0x02, 0x00, 0xe3, 0xff, 0x50, 0x00, 0xfe, 0x1d, 0xec, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1b, 0x00, 0x00, 0x01, 0x37, 0x81, 0x3e, 0x07, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x00],
+ [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+ 0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x00]
+ ],
+ "source": "KEYBOARD | GAMEPAD",
+ "events": [
+ {"action": "DOWN", "keycode": "BUTTON_R1"},
+ {"action": "UP", "keycode": "BUTTON_R1"}
+ ]
+ },
+
+ {
+ "name": "Press BUTTON_L2",
+ "reports": [
+ [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x04, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+ 0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x00],
+ [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+ 0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x00]
+ ],
+ "source": "KEYBOARD | GAMEPAD",
+ "events": [
+ {"action": "DOWN", "keycode": "BUTTON_L2"},
+ {"action": "UP", "keycode": "BUTTON_L2"}
+ ]
+ },
+
+ {
+ "name": "Press BUTTON_R2",
+ "reports": [
+ [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x08, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+ 0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x00],
+ [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+ 0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x00]
+ ],
+ "source": "KEYBOARD | GAMEPAD",
+ "events": [
+ {"action": "DOWN", "keycode": "BUTTON_R2"},
+ {"action": "UP", "keycode": "BUTTON_R2"}
+ ]
+ },
+
+ {
+ "name": "Press BUTTON_L3",
+ "reports": [
+ [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x40, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+ 0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x00],
+ [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+ 0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x00]
+ ],
+ "source": "KEYBOARD | GAMEPAD",
+ "events": [
+ {"action": "DOWN", "keycode": "BUTTON_THUMBL"},
+ {"action": "UP", "keycode": "BUTTON_THUMBL"}
+ ]
+ },
+
+ {
+ "name": "Press BUTTON_R3",
+ "reports": [
+ [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x80, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+ 0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x00],
+ [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+ 0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x00]
+ ],
+ "source": "KEYBOARD | GAMEPAD",
+ "events": [
+ {"action": "DOWN", "keycode": "BUTTON_THUMBR"},
+ {"action": "UP", "keycode": "BUTTON_THUMBR"}
+ ]
+ },
+
+ {
+ "name": "Press BUTTON_SHARE",
+ "reports": [
+ [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x10, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+ 0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x00],
+ [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+ 0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x00]
+ ],
+ "source": "KEYBOARD | GAMEPAD",
+ "events": [
+ {"action": "DOWN", "keycode": "BUTTON_SELECT"},
+ {"action": "UP", "keycode": "BUTTON_SELECT"}
+ ]
+ },
+
+ {
+ "name": "Press BUTTON_OPTIONS",
+ "reports": [
+ [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x20, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+ 0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x00],
+ [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+ 0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x00]
+ ],
+ "source": "KEYBOARD | GAMEPAD",
+ "events": [
+ {"action": "DOWN", "keycode": "BUTTON_START"},
+ {"action": "UP", "keycode": "BUTTON_START"}
+ ]
+ },
+
+ {
+ "name": "Press BUTTON_PS",
+ "reports": [
+ [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x11, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+ 0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x00],
+ [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+ 0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x00]
+ ],
+ "source": "KEYBOARD | GAMEPAD",
+ "events": [
+ {"action": "DOWN", "keycode": "BUTTON_MODE"},
+ {"action": "UP", "keycode": "BUTTON_MODE"}
+ ]
+ }
+
+]
diff --git a/tests/tests/hardware/res/raw/sony_dualshock4_usb_motioneventtests.json b/tests/tests/hardware/res/raw/sony_dualshock4_usb_motioneventtests.json
new file mode 100644
index 0000000..f0be94e
--- /dev/null
+++ b/tests/tests/hardware/res/raw/sony_dualshock4_usb_motioneventtests.json
@@ -0,0 +1,370 @@
+[
+ {
+ "name": "Sanity check - should not produce any events",
+ "reports": [
+ [0x11, 0xc0, 0x00, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00,
+ 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c,
+ 0x2e, 0x80, 0xf2]
+ ],
+ "source": "JOYSTICK",
+ "events": [
+ ]
+ },
+
+ {
+ "name": "Press left DPAD key",
+ "reports": [
+ [0x01, 0x80, 0x80, 0x80, 0x80, 0x06, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+ 0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x00],
+ [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+ 0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x00]
+ ],
+ "source": "JOYSTICK",
+ "events": [
+ {"action": "MOVE", "axes": {"AXIS_HAT_X": -1}},
+ {"action": "MOVE", "axes": {"AXIS_HAT_X": 0}}
+ ]
+ },
+
+ {
+ "name": "Press right DPAD key",
+ "reports": [
+ [0x01, 0x80, 0x80, 0x80, 0x80, 0x02, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+ 0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x00],
+ [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+ 0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x00]
+ ],
+ "source": "JOYSTICK",
+ "events": [
+ {"action": "MOVE", "axes": {"AXIS_HAT_X": 1}},
+ {"action": "MOVE", "axes": {"AXIS_HAT_X": 0}}
+ ]
+ },
+
+ {
+ "name": "Press up DPAD key",
+ "reports": [
+ [0x01, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+ 0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x00],
+ [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+ 0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x00]
+ ],
+ "source": "JOYSTICK",
+ "events": [
+ {"action": "MOVE", "axes": {"AXIS_HAT_Y": -1}},
+ {"action": "MOVE", "axes": {"AXIS_HAT_Y": 0}}
+ ]
+ },
+
+ {
+ "name": "Press down DPAD key",
+ "reports": [
+ [0x01, 0x80, 0x80, 0x80, 0x80, 0x04, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+ 0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x00],
+ [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+ 0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x00]
+ ],
+ "source": "JOYSTICK",
+ "events": [
+ {"action": "MOVE", "axes": {"AXIS_HAT_Y": 1}},
+ {"action": "MOVE", "axes": {"AXIS_HAT_Y": 0}}
+ ]
+ },
+
+ {
+ "name": "Left stick - press left",
+ "reports": [
+ [0x01, 0x40, 0x80, 0x80, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+ 0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x00],
+ [0x01, 0x00, 0x80, 0x80, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+ 0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x00],
+ [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+ 0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x00]
+ ],
+ "source": "JOYSTICK",
+ "events": [
+ {"action": "MOVE", "axes": {"AXIS_X": -0.5}},
+ {"action": "MOVE", "axes": {"AXIS_X": -1}},
+ {"action": "MOVE", "axes": {"AXIS_X": 0}}
+ ]
+ },
+
+ {
+ "name": "Left stick - press right",
+ "reports": [
+ [0x01, 0xc0, 0x80, 0x80, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+ 0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x00],
+ [0x01, 0xff, 0x80, 0x80, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+ 0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x00],
+ [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+ 0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x00]
+ ],
+ "source": "JOYSTICK",
+ "events": [
+ {"action": "MOVE", "axes": {"AXIS_X": 0.51}},
+ {"action": "MOVE", "axes": {"AXIS_X": 1}},
+ {"action": "MOVE", "axes": {"AXIS_X": 0}}
+ ]
+ },
+
+ {
+ "name": "Left stick - press up",
+ "reports": [
+ [0x01, 0x80, 0x40, 0x80, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+ 0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x00],
+ [0x01, 0x80, 0x00, 0x80, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+ 0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x00],
+ [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+ 0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x00]
+ ],
+ "source": "JOYSTICK",
+ "events": [
+ {"action": "MOVE", "axes": {"AXIS_Y": -0.5}},
+ {"action": "MOVE", "axes": {"AXIS_Y": -1}},
+ {"action": "MOVE", "axes": {"AXIS_Y": 0}}
+ ]
+ },
+
+ {
+ "name": "Left stick - press down",
+ "reports": [
+ [0x01, 0x80, 0xc0, 0x80, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+ 0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x00],
+ [0x01, 0x80, 0xff, 0x80, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+ 0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x00],
+ [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+ 0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x00]
+ ],
+ "source": "JOYSTICK",
+ "events": [
+ {"action": "MOVE", "axes": {"AXIS_Y": 0.51}},
+ {"action": "MOVE", "axes": {"AXIS_Y": 1}},
+ {"action": "MOVE", "axes": {"AXIS_Y": 0}}
+ ]
+ },
+
+ {
+ "name": "Right stick - press left",
+ "reports": [
+ [0x01, 0x80, 0x80, 0x40, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+ 0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x00],
+ [0x01, 0x80, 0x80, 0x00, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+ 0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x00],
+ [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+ 0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x00]
+ ],
+ "source": "JOYSTICK",
+ "events": [
+ {"action": "MOVE", "axes": {"AXIS_Z": -0.5}},
+ {"action": "MOVE", "axes": {"AXIS_Z": -1}},
+ {"action": "MOVE", "axes": {"AXIS_Z": 0}}
+ ]
+ },
+
+ {
+ "name": "Right stick - press right",
+ "reports": [
+ [0x01, 0x80, 0x80, 0xc0, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+ 0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x00],
+ [0x01, 0x80, 0x80, 0xff, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+ 0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x00],
+ [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+ 0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x00]
+ ],
+ "source": "JOYSTICK",
+ "events": [
+ {"action": "MOVE", "axes": {"AXIS_Z": 0.51}},
+ {"action": "MOVE", "axes": {"AXIS_Z": 1}},
+ {"action": "MOVE", "axes": {"AXIS_Z": 0}}
+ ]
+ },
+
+ {
+ "name": "Right stick - press up",
+ "reports": [
+ [0x01, 0x80, 0x80, 0x80, 0x40, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+ 0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x00],
+ [0x01, 0x80, 0x80, 0x80, 0x00, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+ 0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x00],
+ [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+ 0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x00]
+ ],
+ "source": "JOYSTICK",
+ "events": [
+ {"action": "MOVE", "axes": {"AXIS_RZ": -0.5}},
+ {"action": "MOVE", "axes": {"AXIS_RZ": -1}},
+ {"action": "MOVE", "axes": {"AXIS_RZ": 0}}
+ ]
+ },
+
+ {
+ "name": "Right stick - press down",
+ "reports": [
+ [0x01, 0x80, 0x80, 0x80, 0xc0, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+ 0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x00],
+ [0x01, 0x80, 0x80, 0x80, 0xff, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+ 0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x00],
+ [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+ 0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x00]
+ ],
+ "source": "JOYSTICK",
+ "events": [
+ {"action": "MOVE", "axes": {"AXIS_RZ": 0.51}},
+ {"action": "MOVE", "axes": {"AXIS_RZ": 1}},
+ {"action": "MOVE", "axes": {"AXIS_RZ": 0}}
+ ]
+ },
+
+ {
+ "name": "Left trigger - quick press",
+ "reports": [
+ [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x10, 0x80, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+ 0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x00],
+ [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x10, 0xff, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+ 0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x00],
+ [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+ 0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x00]
+ ],
+ "source": "JOYSTICK",
+ "events": [
+ {"action": "MOVE", "axes": {"AXIS_LTRIGGER": 0.5, "AXIS_BRAKE": 0.5}},
+ {"action": "MOVE", "axes": {"AXIS_LTRIGGER": 1.0, "AXIS_BRAKE": 1.0}},
+ {"action": "MOVE", "axes": {"AXIS_LTRIGGER": 0, "AXIS_BRAKE": 0}}
+ ]
+ },
+
+ {
+ "name": "Right trigger - quick press",
+ "reports": [
+ [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x10, 0x00, 0x80, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+ 0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x00],
+ [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x10, 0x00, 0xff, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+ 0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x00],
+ [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+ 0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x00]
+ ],
+ "source": "JOYSTICK",
+ "events": [
+ {"action": "MOVE", "axes": {"AXIS_RTRIGGER": 0.5, "AXIS_GAS": 0.5}},
+ {"action": "MOVE", "axes": {"AXIS_RTRIGGER": 1.0, "AXIS_GAS": 1.0}},
+ {"action": "MOVE", "axes": {"AXIS_RTRIGGER": 0, "AXIS_GAS": 0}}
+ ]
+ }
+]
diff --git a/tests/tests/hardware/res/raw/sony_dualshock4_usb_register.json b/tests/tests/hardware/res/raw/sony_dualshock4_usb_register.json
new file mode 100644
index 0000000..5654c50
--- /dev/null
+++ b/tests/tests/hardware/res/raw/sony_dualshock4_usb_register.json
@@ -0,0 +1,58 @@
+{
+ "id": 1,
+ "command": "register",
+ "name": "Sony DS4 (model CUH-ZCT1U)(USB Test)",
+ "vid": 0x054c,
+ "pid": 0x05c4,
+ "bus": "usb",
+ "descriptor": [0x05, 0x01, 0x09, 0x05, 0xa1, 0x01, 0x85, 0x01, 0x09, 0x30, 0x09, 0x31, 0x09, 0x32,
+ 0x09, 0x35, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x04, 0x81, 0x02, 0x09, 0x39, 0x15,
+ 0x00, 0x25, 0x07, 0x35, 0x00, 0x46, 0x3b, 0x01, 0x65, 0x14, 0x75, 0x04, 0x95, 0x01, 0x81, 0x42,
+ 0x65, 0x00, 0x05, 0x09, 0x19, 0x01, 0x29, 0x0e, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x0e,
+ 0x81, 0x02, 0x06, 0x00, 0xff, 0x09, 0x20, 0x75, 0x06, 0x95, 0x01, 0x15, 0x00, 0x25, 0x7f, 0x81,
+ 0x02, 0x05, 0x01, 0x09, 0x33, 0x09, 0x34, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x02,
+ 0x81, 0x02, 0x06, 0x00, 0xff, 0x09, 0x21, 0x95, 0x36, 0x81, 0x02, 0x85, 0x05, 0x09, 0x22, 0x95,
+ 0x1f, 0x91, 0x02, 0x85, 0x04, 0x09, 0x23, 0x95, 0x24, 0xb1, 0x02, 0x85, 0x02, 0x09, 0x24, 0x95,
+ 0x24, 0xb1, 0x02, 0x85, 0x08, 0x09, 0x25, 0x95, 0x03, 0xb1, 0x02, 0x85, 0x10, 0x09, 0x26, 0x95,
+ 0x04, 0xb1, 0x02, 0x85, 0x11, 0x09, 0x27, 0x95, 0x02, 0xb1, 0x02, 0x85, 0x12, 0x06, 0x02, 0xff,
+ 0x09, 0x21, 0x95, 0x0f, 0xb1, 0x02, 0x85, 0x13, 0x09, 0x22, 0x95, 0x16, 0xb1, 0x02, 0x85, 0x14,
+ 0x06, 0x05, 0xff, 0x09, 0x20, 0x95, 0x10, 0xb1, 0x02, 0x85, 0x15, 0x09, 0x21, 0x95, 0x2c, 0xb1,
+ 0x02, 0x06, 0x80, 0xff, 0x85, 0x80, 0x09, 0x20, 0x95, 0x06, 0xb1, 0x02, 0x85, 0x81, 0x09, 0x21,
+ 0x95, 0x06, 0xb1, 0x02, 0x85, 0x82, 0x09, 0x22, 0x95, 0x05, 0xb1, 0x02, 0x85, 0x83, 0x09, 0x23,
+ 0x95, 0x01, 0xb1, 0x02, 0x85, 0x84, 0x09, 0x24, 0x95, 0x04, 0xb1, 0x02, 0x85, 0x85, 0x09, 0x25,
+ 0x95, 0x06, 0xb1, 0x02, 0x85, 0x86, 0x09, 0x26, 0x95, 0x06, 0xb1, 0x02, 0x85, 0x87, 0x09, 0x27,
+ 0x95, 0x23, 0xb1, 0x02, 0x85, 0x88, 0x09, 0x28, 0x95, 0x22, 0xb1, 0x02, 0x85, 0x89, 0x09, 0x29,
+ 0x95, 0x02, 0xb1, 0x02, 0x85, 0x90, 0x09, 0x30, 0x95, 0x05, 0xb1, 0x02, 0x85, 0x91, 0x09, 0x31,
+ 0x95, 0x03, 0xb1, 0x02, 0x85, 0x92, 0x09, 0x32, 0x95, 0x03, 0xb1, 0x02, 0x85, 0x93, 0x09, 0x33,
+ 0x95, 0x0c, 0xb1, 0x02, 0x85, 0xa0, 0x09, 0x40, 0x95, 0x06, 0xb1, 0x02, 0x85, 0xa1, 0x09, 0x41,
+ 0x95, 0x01, 0xb1, 0x02, 0x85, 0xa2, 0x09, 0x42, 0x95, 0x01, 0xb1, 0x02, 0x85, 0xa3, 0x09, 0x43,
+ 0x95, 0x30, 0xb1, 0x02, 0x85, 0xa4, 0x09, 0x44, 0x95, 0x0d, 0xb1, 0x02, 0x85, 0xa5, 0x09, 0x45,
+ 0x95, 0x15, 0xb1, 0x02, 0x85, 0xa6, 0x09, 0x46, 0x95, 0x15, 0xb1, 0x02, 0x85, 0xf0, 0x09, 0x47,
+ 0x95, 0x3f, 0xb1, 0x02, 0x85, 0xf1, 0x09, 0x48, 0x95, 0x3f, 0xb1, 0x02, 0x85, 0xf2, 0x09, 0x49,
+ 0x95, 0x0f, 0xb1, 0x02, 0x85, 0xa7, 0x09, 0x4a, 0x95, 0x01, 0xb1, 0x02, 0x85, 0xa8, 0x09, 0x4b,
+ 0x95, 0x01, 0xb1, 0x02, 0x85, 0xa9, 0x09, 0x4c, 0x95, 0x08, 0xb1, 0x02, 0x85, 0xaa, 0x09, 0x4e,
+ 0x95, 0x01, 0xb1, 0x02, 0x85, 0xab, 0x09, 0x4f, 0x95, 0x39, 0xb1, 0x02, 0x85, 0xac, 0x09, 0x50,
+ 0x95, 0x39, 0xb1, 0x02, 0x85, 0xad, 0x09, 0x51, 0x95, 0x0b, 0xb1, 0x02, 0x85, 0xae, 0x09, 0x52,
+ 0x95, 0x01, 0xb1, 0x02, 0x85, 0xaf, 0x09, 0x53, 0x95, 0x02, 0xb1, 0x02, 0x85, 0xb0, 0x09, 0x54,
+ 0x95, 0x3f, 0xb1, 0x02, 0x85, 0xb1, 0x09, 0x55, 0x95, 0x02, 0xb1, 0x02, 0x85, 0xb2, 0x09, 0x56,
+ 0x95, 0x02, 0xb1, 0x02, 0xc0],
+ "feature_reports": [
+ {
+ "id": 0x02,
+ "data": [0x02, 0xf8, 0xff, 0xfd, 0xff, 0xf9, 0xff, 0xdf, 0x21, 0xf1, 0xdd, 0x95, 0x22, 0x67,
+ 0xdd, 0xf2, 0x23, 0x1c, 0xdc, 0x1c, 0x02, 0x1c, 0x02, 0xaa, 0x1f, 0x56, 0xe0, 0xf7, 0x20,
+ 0x08, 0xdf, 0x0a, 0x20, 0xf7, 0xdf, 0x06, 0x00]
+ },
+ {
+ "id": 0xa3,
+ "data": [0xa3, 0x41, 0x70, 0x72, 0x20, 0x20, 0x38, 0x20, 0x32, 0x30, 0x31, 0x34, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x30, 0x39, 0x3a, 0x34, 0x36, 0x3a, 0x30, 0x36, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x43, 0x03, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05,
+ 0x00, 0x00, 0x80, 0x03, 0x00]
+ },
+ {
+ "id": 0x81,
+ "data": [0x81, 0x62, 0x97, 0xc9, 0x00, 0x00, 0x00]
+ }
+ ]
+}
diff --git a/tests/tests/hardware/res/raw/sony_dualshock4_register.json b/tests/tests/hardware/res/raw/sony_dualshock4pro_bluetooth_register.json
similarity index 96%
rename from tests/tests/hardware/res/raw/sony_dualshock4_register.json
rename to tests/tests/hardware/res/raw/sony_dualshock4pro_bluetooth_register.json
index cd33b9e..d4fa66d 100644
--- a/tests/tests/hardware/res/raw/sony_dualshock4_register.json
+++ b/tests/tests/hardware/res/raw/sony_dualshock4pro_bluetooth_register.json
@@ -1,9 +1,10 @@
{
"id": 1,
"command": "register",
- "name": "Sony DS4 Joystick (Test)",
+ "name": "Sony DS4 Pro (model CUH-ZCT2U)(Bluetooth Test)",
"vid": 0x054c,
"pid": 0x09cc,
+ "bus": "bluetooth",
"descriptor": [0x05, 0x01, 0x09, 0x05, 0xa1, 0x01, 0x85, 0x01, 0x09, 0x30, 0x09, 0x31, 0x09, 0x32,
0x09, 0x35, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x04, 0x81, 0x02, 0x09, 0x39, 0x15,
0x00, 0x25, 0x07, 0x75, 0x04, 0x95, 0x01, 0x81, 0x42, 0x05, 0x09, 0x19, 0x01, 0x29, 0x0e, 0x15,
@@ -33,7 +34,7 @@
0x46, 0xb1, 0x02, 0x85, 0xb3, 0x09, 0x45, 0xb1, 0x02, 0x85, 0xb4, 0x09, 0x46, 0xb1, 0x02, 0xc0],
"feature_reports": [
{
- "id": 5,
+ "id": 0x05,
"data": [0x05, 0x1e, 0x00, 0x05, 0x00, 0xe2, 0xff, 0xf2, 0x22, 0xbe, 0x22, 0x8d, 0x22, 0x4f,
0xdd, 0x4d, 0xdd, 0x39, 0xdd, 0x1c, 0x02, 0x1c, 0x02, 0xe3, 0x1f, 0x8b, 0xdf, 0x8c, 0x1e,
0xb4, 0xde, 0x30, 0x20, 0x71, 0xe0, 0x10, 0x00, 0xca, 0xfc, 0x64, 0x4d]
diff --git a/tests/tests/hardware/res/raw/sony_dualshock4pro_usb_register.json b/tests/tests/hardware/res/raw/sony_dualshock4pro_usb_register.json
new file mode 100644
index 0000000..4514fd4
--- /dev/null
+++ b/tests/tests/hardware/res/raw/sony_dualshock4pro_usb_register.json
@@ -0,0 +1,59 @@
+{
+ "id": 1,
+ "command": "register",
+ "name": "Sony DS4 Pro (model CUH-ZCT2U)(USB Test)",
+ "vid": 0x054c,
+ "pid": 0x09cc,
+ "bus": "usb",
+ "descriptor": [0x05, 0x01, 0x09, 0x05, 0xa1, 0x01, 0x85, 0x01, 0x09, 0x30, 0x09, 0x31, 0x09, 0x32,
+ 0x09, 0x35, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x04, 0x81, 0x02, 0x09, 0x39, 0x15,
+ 0x00, 0x25, 0x07, 0x35, 0x00, 0x46, 0x3b, 0x01, 0x65, 0x14, 0x75, 0x04, 0x95, 0x01, 0x81, 0x42,
+ 0x65, 0x00, 0x05, 0x09, 0x19, 0x01, 0x29, 0x0e, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x0e,
+ 0x81, 0x02, 0x06, 0x00, 0xff, 0x09, 0x20, 0x75, 0x06, 0x95, 0x01, 0x15, 0x00, 0x25, 0x7f, 0x81,
+ 0x02, 0x05, 0x01, 0x09, 0x33, 0x09, 0x34, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x02,
+ 0x81, 0x02, 0x06, 0x00, 0xff, 0x09, 0x21, 0x95, 0x36, 0x81, 0x02, 0x85, 0x05, 0x09, 0x22, 0x95,
+ 0x1f, 0x91, 0x02, 0x85, 0x04, 0x09, 0x23, 0x95, 0x24, 0xb1, 0x02, 0x85, 0x02, 0x09, 0x24, 0x95,
+ 0x24, 0xb1, 0x02, 0x85, 0x08, 0x09, 0x25, 0x95, 0x03, 0xb1, 0x02, 0x85, 0x10, 0x09, 0x26, 0x95,
+ 0x04, 0xb1, 0x02, 0x85, 0x11, 0x09, 0x27, 0x95, 0x02, 0xb1, 0x02, 0x85, 0x12, 0x06, 0x02, 0xff,
+ 0x09, 0x21, 0x95, 0x0f, 0xb1, 0x02, 0x85, 0x13, 0x09, 0x22, 0x95, 0x16, 0xb1, 0x02, 0x85, 0x14,
+ 0x06, 0x05, 0xff, 0x09, 0x20, 0x95, 0x10, 0xb1, 0x02, 0x85, 0x15, 0x09, 0x21, 0x95, 0x2c, 0xb1,
+ 0x02, 0x06, 0x80, 0xff, 0x85, 0x80, 0x09, 0x20, 0x95, 0x06, 0xb1, 0x02, 0x85, 0x81, 0x09, 0x21,
+ 0x95, 0x06, 0xb1, 0x02, 0x85, 0x82, 0x09, 0x22, 0x95, 0x05, 0xb1, 0x02, 0x85, 0x83, 0x09, 0x23,
+ 0x95, 0x01, 0xb1, 0x02, 0x85, 0x84, 0x09, 0x24, 0x95, 0x04, 0xb1, 0x02, 0x85, 0x85, 0x09, 0x25,
+ 0x95, 0x06, 0xb1, 0x02, 0x85, 0x86, 0x09, 0x26, 0x95, 0x06, 0xb1, 0x02, 0x85, 0x87, 0x09, 0x27,
+ 0x95, 0x23, 0xb1, 0x02, 0x85, 0x88, 0x09, 0x28, 0x95, 0x22, 0xb1, 0x02, 0x85, 0x89, 0x09, 0x29,
+ 0x95, 0x02, 0xb1, 0x02, 0x85, 0x90, 0x09, 0x30, 0x95, 0x05, 0xb1, 0x02, 0x85, 0x91, 0x09, 0x31,
+ 0x95, 0x03, 0xb1, 0x02, 0x85, 0x92, 0x09, 0x32, 0x95, 0x03, 0xb1, 0x02, 0x85, 0x93, 0x09, 0x33,
+ 0x95, 0x0c, 0xb1, 0x02, 0x85, 0xa0, 0x09, 0x40, 0x95, 0x06, 0xb1, 0x02, 0x85, 0xa1, 0x09, 0x41,
+ 0x95, 0x01, 0xb1, 0x02, 0x85, 0xa2, 0x09, 0x42, 0x95, 0x01, 0xb1, 0x02, 0x85, 0xa3, 0x09, 0x43,
+ 0x95, 0x30, 0xb1, 0x02, 0x85, 0xa4, 0x09, 0x44, 0x95, 0x0d, 0xb1, 0x02, 0x85, 0xa5, 0x09, 0x45,
+ 0x95, 0x15, 0xb1, 0x02, 0x85, 0xa6, 0x09, 0x46, 0x95, 0x15, 0xb1, 0x02, 0x85, 0xf0, 0x09, 0x47,
+ 0x95, 0x3f, 0xb1, 0x02, 0x85, 0xf1, 0x09, 0x48, 0x95, 0x3f, 0xb1, 0x02, 0x85, 0xf2, 0x09, 0x49,
+ 0x95, 0x0f, 0xb1, 0x02, 0x85, 0xa7, 0x09, 0x4a, 0x95, 0x01, 0xb1, 0x02, 0x85, 0xa8, 0x09, 0x4b,
+ 0x95, 0x01, 0xb1, 0x02, 0x85, 0xa9, 0x09, 0x4c, 0x95, 0x08, 0xb1, 0x02, 0x85, 0xaa, 0x09, 0x4e,
+ 0x95, 0x01, 0xb1, 0x02, 0x85, 0xab, 0x09, 0x4f, 0x95, 0x39, 0xb1, 0x02, 0x85, 0xac, 0x09, 0x50,
+ 0x95, 0x39, 0xb1, 0x02, 0x85, 0xad, 0x09, 0x51, 0x95, 0x0b, 0xb1, 0x02, 0x85, 0xae, 0x09, 0x52,
+ 0x95, 0x01, 0xb1, 0x02, 0x85, 0xaf, 0x09, 0x53, 0x95, 0x02, 0xb1, 0x02, 0x85, 0xb0, 0x09, 0x54,
+ 0x95, 0x3f, 0xb1, 0x02, 0x85, 0xb1, 0x09, 0x55, 0x95, 0x02, 0xb1, 0x02, 0x85, 0xb2, 0x09, 0x56,
+ 0x95, 0x02, 0xb1, 0x02, 0x85, 0xe0, 0x09, 0x57, 0x95, 0x02, 0xb1, 0x02, 0x85, 0xb3, 0x09, 0x55,
+ 0x95, 0x3f, 0xb1, 0x02, 0x85, 0xb4, 0x09, 0x55, 0x95, 0x3f, 0xb1, 0x02, 0xc0],
+ "feature_reports": [
+ {
+ "id": 0x02,
+ "data": [0x02, 0x1e, 0x00, 0x05, 0x00, 0xe2, 0xff, 0xf2, 0x22, 0x4f, 0xdd, 0x4d, 0xdd, 0xbe,
+ 0x22, 0x8d, 0x22, 0x39, 0xdd, 0x1c, 0x02, 0x1c, 0x02, 0xe3, 0x1f, 0x8b, 0xdf, 0x8c, 0x1e,
+ 0xb4, 0xde, 0x30, 0x20, 0x71, 0xe0, 0x10, 0x00]
+ },
+ {
+ "id": 0xa3,
+ "data": [0xa3, 0x41, 0x70, 0x72, 0x20, 0x20, 0x38, 0x20, 0x32, 0x30, 0x31, 0x34, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x30, 0x39, 0x3a, 0x34, 0x36, 0x3a, 0x30, 0x36, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x43, 0x03, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05,
+ 0x00, 0x00, 0x80, 0x03, 0x00]
+ },
+ {
+ "id": 0x81,
+ "data": [0x81, 0x62, 0x97, 0xc9, 0x00, 0x00, 0x00]
+ }
+ ]
+}
diff --git a/tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualshock4Test.java b/tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualshock3UsbTest.java
similarity index 69%
copy from tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualshock4Test.java
copy to tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualshock3UsbTest.java
index 409c84f..e65fa7a 100644
--- a/tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualshock4Test.java
+++ b/tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualshock3UsbTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 The Android Open Source Project
+ * Copyright 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,19 +26,20 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class SonyDualshock4Test extends InputTestCase {
+public class SonyDualshock3UsbTest extends InputTestCase {
- public SonyDualshock4Test() {
- super(R.raw.sony_dualshock4_register);
+ // Simulates the behavior of PlayStation DualShock3 gamepad (model CECHZC2U)
+ public SonyDualshock3UsbTest() {
+ super(R.raw.sony_dualshock3_usb_register);
}
@Test
public void testAllKeys() {
- testInputEvents(R.raw.sony_dualshock4_keyeventtests);
+ testInputEvents(R.raw.sony_dualshock3_usb_keyeventtests);
}
@Test
public void testAllMotions() {
- testInputEvents(R.raw.sony_dualshock4_motioneventtests);
+ testInputEvents(R.raw.sony_dualshock3_usb_motioneventtests);
}
}
diff --git a/tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualshock4Test.java b/tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualshock4BluetoothTest.java
similarity index 68%
copy from tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualshock4Test.java
copy to tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualshock4BluetoothTest.java
index 409c84f..ca36a68 100644
--- a/tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualshock4Test.java
+++ b/tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualshock4BluetoothTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 The Android Open Source Project
+ * Copyright 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,19 +26,20 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class SonyDualshock4Test extends InputTestCase {
+public class SonyDualshock4BluetoothTest extends InputTestCase {
- public SonyDualshock4Test() {
- super(R.raw.sony_dualshock4_register);
+ // Simulates the behavior of PlayStation DualShock4 gamepad (model CUH-ZCT1U)
+ public SonyDualshock4BluetoothTest() {
+ super(R.raw.sony_dualshock4_bluetooth_register);
}
@Test
public void testAllKeys() {
- testInputEvents(R.raw.sony_dualshock4_keyeventtests);
+ testInputEvents(R.raw.sony_dualshock4_bluetooth_keyeventtests);
}
@Test
public void testAllMotions() {
- testInputEvents(R.raw.sony_dualshock4_motioneventtests);
+ testInputEvents(R.raw.sony_dualshock4_bluetooth_motioneventtests);
}
}
diff --git a/tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualshock4Test.java b/tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualshock4ProBluetoothTest.java
similarity index 70%
rename from tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualshock4Test.java
rename to tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualshock4ProBluetoothTest.java
index 409c84f..c5f761f 100644
--- a/tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualshock4Test.java
+++ b/tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualshock4ProBluetoothTest.java
@@ -26,19 +26,20 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class SonyDualshock4Test extends InputTestCase {
+public class SonyDualshock4ProBluetoothTest extends InputTestCase {
- public SonyDualshock4Test() {
- super(R.raw.sony_dualshock4_register);
+ // Simulates the behavior of PlayStation DualShock4 Pro gamepad (model CUH-ZCT2U)
+ public SonyDualshock4ProBluetoothTest() {
+ super(R.raw.sony_dualshock4pro_bluetooth_register);
}
@Test
public void testAllKeys() {
- testInputEvents(R.raw.sony_dualshock4_keyeventtests);
+ testInputEvents(R.raw.sony_dualshock4_bluetooth_keyeventtests);
}
@Test
public void testAllMotions() {
- testInputEvents(R.raw.sony_dualshock4_motioneventtests);
+ testInputEvents(R.raw.sony_dualshock4_bluetooth_motioneventtests);
}
}
diff --git a/tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualshock4Test.java b/tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualshock4ProUsbTest.java
similarity index 68%
copy from tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualshock4Test.java
copy to tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualshock4ProUsbTest.java
index 409c84f..8e967ff 100644
--- a/tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualshock4Test.java
+++ b/tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualshock4ProUsbTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 The Android Open Source Project
+ * Copyright 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,19 +26,20 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class SonyDualshock4Test extends InputTestCase {
+public class SonyDualshock4ProUsbTest extends InputTestCase {
- public SonyDualshock4Test() {
- super(R.raw.sony_dualshock4_register);
+ // Simulates the behavior of PlayStation DualShock4 Pro gamepad (model CUH-ZCT2U)
+ public SonyDualshock4ProUsbTest() {
+ super(R.raw.sony_dualshock4pro_usb_register);
}
@Test
public void testAllKeys() {
- testInputEvents(R.raw.sony_dualshock4_keyeventtests);
+ testInputEvents(R.raw.sony_dualshock4_usb_keyeventtests);
}
@Test
public void testAllMotions() {
- testInputEvents(R.raw.sony_dualshock4_motioneventtests);
+ testInputEvents(R.raw.sony_dualshock4_usb_motioneventtests);
}
}
diff --git a/tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualshock4Test.java b/tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualshock4UsbTest.java
similarity index 69%
copy from tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualshock4Test.java
copy to tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualshock4UsbTest.java
index 409c84f..062dced 100644
--- a/tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualshock4Test.java
+++ b/tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualshock4UsbTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 The Android Open Source Project
+ * Copyright 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,19 +26,20 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class SonyDualshock4Test extends InputTestCase {
+public class SonyDualshock4UsbTest extends InputTestCase {
- public SonyDualshock4Test() {
- super(R.raw.sony_dualshock4_register);
+ // Simulates the behavior of PlayStation DualShock4 gamepad (model CUH-ZCT1U)
+ public SonyDualshock4UsbTest() {
+ super(R.raw.sony_dualshock4_usb_register);
}
@Test
public void testAllKeys() {
- testInputEvents(R.raw.sony_dualshock4_keyeventtests);
+ testInputEvents(R.raw.sony_dualshock4_usb_keyeventtests);
}
@Test
public void testAllMotions() {
- testInputEvents(R.raw.sony_dualshock4_motioneventtests);
+ testInputEvents(R.raw.sony_dualshock4_usb_motioneventtests);
}
}
diff --git a/tests/tests/identity/AndroidTest.xml b/tests/tests/identity/AndroidTest.xml
index 25e1dfe..6c08b5e 100644
--- a/tests/tests/identity/AndroidTest.xml
+++ b/tests/tests/identity/AndroidTest.xml
@@ -18,6 +18,7 @@
<option name="config-descriptor:metadata" key="component" value="security" />
<option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
<option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
+ <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="CtsIdentityTestCases.apk" />
diff --git a/tests/tests/identity/src/android/security/identity/cts/AttestationTest.java b/tests/tests/identity/src/android/security/identity/cts/AttestationTest.java
new file mode 100644
index 0000000..34b0cf2
--- /dev/null
+++ b/tests/tests/identity/src/android/security/identity/cts/AttestationTest.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.identity.cts;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+
+import android.security.identity.IdentityCredential;
+import android.security.identity.IdentityCredentialStore;
+import androidx.test.InstrumentationRegistry;
+
+import org.junit.Test;
+import org.junit.Ignore;
+
+import com.google.common.primitives.Bytes;
+
+import java.security.SecureRandom;
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+import java.util.Collection;
+
+import java.util.Optional;
+
+public class AttestationTest {
+ private static final String TAG = "AttestationTest";
+
+ // The subset of Keymaster tags we care about. See
+ //
+ // https://source.android.com/security/keystore/tags
+ //
+ // for a list of known tags.
+ //
+ public static final int KM_TAG_ATTESTATION_APPLICATION_ID = 709;
+ public static final int KM_TAG_IDENTITY_CREDENTIAL_KEY = 721;
+
+ @Ignore("Not ready until SW implementation produces correct attestations")
+ @Test
+ public void attestationTest() throws Exception {
+ Context appContext = InstrumentationRegistry.getTargetContext();
+ IdentityCredentialStore store = IdentityCredentialStore.getInstance(appContext);
+
+ // Use a random challenge of length between 16 and 32.
+ SecureRandom random = new SecureRandom();
+ int challengeLength = 16 + random.nextInt(16);
+ byte[] challenge = new byte[challengeLength];
+ random.nextBytes(challenge);
+
+ String credentialName = "test";
+
+ store.deleteCredentialByName(credentialName);
+ Collection<X509Certificate> certChain = ProvisioningTest.createCredentialWithChallenge(
+ store, credentialName, challenge);
+ store.deleteCredentialByName("test");
+
+ // Check that each certificate in the chain isn't expired and also that it's signed by the
+ // next one.
+ assertTrue(verifyCertificateChain(certChain));
+
+ // Parse the attestation record... Identity Credential uses standard KeyMaster-style
+ // attestation with only a few differences:
+ //
+ // - Tag::IDENTITY_CREDENTIAL_KEY (boolean) must be set to identify the key as an Identity
+ // Credential key.
+ //
+ // - the KeyMaster version and security-level should be set to that of Identity Credential
+ //
+ // In this test we only test for these two things and a) the attestationApplicationId
+ // matches the calling application; and b) the given challenge matches what was sent.
+ //
+ // We could test for all the other expected attestation records but currently we assume
+ // this is already tested by Keymaster/Android Keystore tests since the TA is expected to
+ // re-use the same infrastructure.
+ //
+ X509Certificate cert = certChain.iterator().next();
+ ParsedAttestationRecord record = new ParsedAttestationRecord(cert);
+
+ // Need at least attestation version 3 and attestations to be done in either the TEE
+ // or in a StrongBox.
+ assertTrue(record.getAttestationVersion() >= 3);
+
+ // Also per the IC HAL, the keymasterSecurityLevel field is used as the securityLevel of
+ // the IC TA and this must be either TEE or in a StrongBox... except we specifically
+ // allow our software implementation to report Software here.
+ //
+ // Since we cannot get the implementation name or author at this layer, we can't test for
+ // it. This can be tested for in the VTS test, however.
+
+ // As per the IC HAL, the keymasterVersion field should be the version of the Identity
+ // Credential HAL - 1.0 - and this is encoded as major*10 + minor. This field is used by
+ // Keymaster which is known to report integers less than or equal to 4 (for KM up to 4.0)
+ // and integers greater or equal than 41 (for KM starting with 4.1).
+ //
+ // Since we won't get to version 4.0 of the IC HAL for a while, let's also check that a KM
+ // version isn't errornously returned.
+ assertTrue(record.getKeymasterVersion() >= 10);
+ assertTrue(record.getKeymasterVersion() < 40);
+
+ // Check that the challenge we passed in, is in fact in the attestation record.
+ assertArrayEquals(challenge, record.getAttestationChallenge());
+
+ // Tag::ATTESTATION_APPLICATION_ID is used to identify the set of possible applications of
+ // which one has initiated a key attestation. This is complicated ASN.1 which we don't want
+ // to parse and we don't need to [1].. we can however easily check that our applicationId
+ // is appearing as a substring somewhere in this blob.
+ //
+ // [1] : and the point of this test isn't to verify that attestations are done correctly,
+ // that's tested elsewhere in e.g. KM and Android Keystore.
+ //
+ Optional<byte[]> attestationApplicationId =
+ record.getSoftwareAuthorizationByteString(KM_TAG_ATTESTATION_APPLICATION_ID);
+ assertTrue(attestationApplicationId.isPresent());
+ String appId = appContext.getPackageName();
+ assertTrue(Bytes.indexOf(attestationApplicationId.get(), appId.getBytes()) != -1);
+
+ // Tag::IDENTITY_CREDENTIAL_KEY is used in attestations produced by the Identity Credential
+ // HAL when that HAL attests to Credential Keys.
+ boolean isIdentityCredentialKey =
+ record.getTeeAuthorizationBoolean(KM_TAG_IDENTITY_CREDENTIAL_KEY);
+ assertTrue(isIdentityCredentialKey);
+ }
+
+ // This only verifies each cert hasn't expired and signed by the next one.
+ private static boolean verifyCertificateChain(Collection<X509Certificate> certChain) {
+ X509Certificate[] certs = new X509Certificate[certChain.size()];
+ certs = certChain.toArray(certs);
+ X509Certificate parent = certs[certs.length - 1];
+ for (int i = certs.length - 1; i >= 0; i--) {
+ X509Certificate cert = certs[i];
+ try {
+ cert.checkValidity();
+ cert.verify(parent.getPublicKey());
+ } catch (Exception e) {
+ e.printStackTrace();
+ return false;
+ }
+ parent = cert;
+ }
+ return true;
+ }
+}
diff --git a/tests/tests/identity/src/android/security/identity/cts/ParsedAttestationRecord.java b/tests/tests/identity/src/android/security/identity/cts/ParsedAttestationRecord.java
new file mode 100644
index 0000000..e4297d7
--- /dev/null
+++ b/tests/tests/identity/src/android/security/identity/cts/ParsedAttestationRecord.java
@@ -0,0 +1,219 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.identity.cts;
+
+import java.io.IOException;
+import java.security.cert.X509Certificate;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Set;
+import java.util.Optional;
+
+import org.bouncycastle.asn1.ASN1InputStream;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1Boolean;
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1Enumerated;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Set;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DEROctetString;
+
+// This code is loosely based on https://github.com/google/android-key-attestation
+
+class ParsedAttestationRecord {
+ private static final String KEY_DESCRIPTION_OID = "1.3.6.1.4.1.11129.2.1.17";
+
+ enum SecurityLevel {
+ SOFTWARE,
+ TRUSTED_ENVIRONMENT,
+ STRONG_BOX
+ }
+
+ private static final int ATTESTATION_VERSION_INDEX = 0;
+ private static final int ATTESTATION_SECURITY_LEVEL_INDEX = 1;
+ private static final int KEYMASTER_VERSION_INDEX = 2;
+ private static final int KEYMASTER_SECURITY_LEVEL_INDEX = 3;
+ private static final int ATTESTATION_CHALLENGE_INDEX = 4;
+ private static final int UNIQUE_ID_INDEX = 5;
+ private static final int SW_ENFORCED_INDEX = 6;
+ private static final int TEE_ENFORCED_INDEX = 7;
+
+ // Some security values. The complete list is in this AOSP file:
+ // hardware/libhardware/include/hardware/keymaster_defs.h
+ private static final int KM_SECURITY_LEVEL_SOFTWARE = 0;
+ private static final int KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT = 1;
+ private static final int KM_SECURITY_LEVEL_STRONG_BOX = 2;
+
+ private int attestationVersion;
+ private SecurityLevel attestationSecurityLevel;
+ private int keymasterVersion;
+ private SecurityLevel keymasterSecurityLevel;
+ private byte[] attestationChallenge;
+ private byte[] uniqueId;
+
+ private Map<Integer, ASN1Primitive> softwareEnforcedAuthorizations;
+ private Map<Integer, ASN1Primitive> teeEnforcedAuthorizations;
+
+ public int getAttestationVersion() {
+ return attestationVersion;
+ }
+
+ public SecurityLevel getAttestationSecurityLevel() {
+ return attestationSecurityLevel;
+ }
+
+ public int getKeymasterVersion() {
+ return keymasterVersion;
+ }
+
+ public SecurityLevel getKeymasterSecurityLevel() {
+ return attestationSecurityLevel;
+ }
+
+ public byte[] getAttestationChallenge() {
+ return attestationChallenge;
+ }
+
+ public byte[] getUniqueId() {
+ return uniqueId;
+ }
+
+ public Set<Integer> getSoftwareEnforcedAuthorizationTags() {
+ return softwareEnforcedAuthorizations.keySet();
+ }
+
+ public Set<Integer> getTeeEnforcedAuthorizationTags() {
+ return teeEnforcedAuthorizations.keySet();
+ }
+
+ private static ASN1Primitive findAuthorizationListEntry(
+ Map<Integer, ASN1Primitive> authorizationMap, int tag) {
+ return authorizationMap.getOrDefault(tag, null);
+ }
+
+ public Optional<Integer> getSoftwareAuthorizationInteger(int tag) {
+ ASN1Primitive entry = findAuthorizationListEntry(softwareEnforcedAuthorizations, tag);
+ return Optional.ofNullable(entry).map(ParsedAttestationRecord::getIntegerFromAsn1);
+ }
+
+ public Optional<Integer> getTeeAuthorizationInteger(int tag) {
+ ASN1Primitive entry = findAuthorizationListEntry(teeEnforcedAuthorizations, tag);
+ return Optional.ofNullable(entry).map(ParsedAttestationRecord::getIntegerFromAsn1);
+ }
+ public boolean getSoftwareAuthorizationBoolean(int tag) {
+ ASN1Primitive entry = findAuthorizationListEntry(softwareEnforcedAuthorizations, tag);
+ return entry != null;
+ }
+
+ public boolean getTeeAuthorizationBoolean(int tag) {
+ ASN1Primitive entry = findAuthorizationListEntry(teeEnforcedAuthorizations, tag);
+ return entry != null;
+ }
+
+ public Optional<byte[]> getSoftwareAuthorizationByteString(int tag) {
+ ASN1OctetString entry = (ASN1OctetString) findAuthorizationListEntry(softwareEnforcedAuthorizations, tag);
+ return Optional.ofNullable(entry).map(ASN1OctetString::getOctets);
+ }
+
+ public Optional<byte[]> getTeeAuthorizationByteString(int tag) {
+ ASN1OctetString entry = (ASN1OctetString) findAuthorizationListEntry(teeEnforcedAuthorizations, tag);
+ return Optional.ofNullable(entry).map(ASN1OctetString::getOctets);
+ }
+
+ private static boolean getBooleanFromAsn1(ASN1Encodable asn1Value) {
+ if (asn1Value instanceof ASN1Boolean) {
+ return ((ASN1Boolean) asn1Value).isTrue();
+ } else {
+ throw new RuntimeException(
+ "Boolean value expected; found " + asn1Value.getClass().getName() + " instead.");
+ }
+ }
+
+ private static int getIntegerFromAsn1(ASN1Encodable asn1Value) {
+ if (asn1Value instanceof ASN1Integer) {
+ return ((ASN1Integer) asn1Value).getValue().intValue();
+ } else if (asn1Value instanceof ASN1Enumerated) {
+ return ((ASN1Enumerated) asn1Value).getValue().intValue();
+ } else {
+ throw new IllegalArgumentException(
+ "Integer value expected; found " + asn1Value.getClass().getName() + " instead.");
+ }
+ }
+
+ private static Map<Integer, ASN1Primitive> getAuthorizationMap(
+ ASN1Encodable[] authorizationList) {
+ Map<Integer, ASN1Primitive> authorizationMap = new HashMap<>();
+ for (ASN1Encodable entry : authorizationList) {
+ ASN1TaggedObject taggedEntry = (ASN1TaggedObject) entry;
+ authorizationMap.put(taggedEntry.getTagNo(), taggedEntry.getObject());
+ }
+ return authorizationMap;
+ }
+
+ public ParsedAttestationRecord(X509Certificate cert) throws IOException {
+ byte[] attestationExtensionBytes = cert.getExtensionValue(KEY_DESCRIPTION_OID);
+ if (attestationExtensionBytes == null || attestationExtensionBytes.length == 0) {
+ throw new IllegalArgumentException("Couldn't find keystore attestation extension.");
+ }
+
+ ASN1Sequence seq;
+ try (ASN1InputStream asn1InputStream = new ASN1InputStream(attestationExtensionBytes)) {
+ // The extension contains one object, a sequence, in the
+ // Distinguished Encoding Rules (DER)-encoded form. Get the DER
+ // bytes.
+ byte[] derSequenceBytes = ((ASN1OctetString) asn1InputStream.readObject()).getOctets();
+ // Decode the bytes as an ASN1 sequence object.
+ try (ASN1InputStream seqInputStream = new ASN1InputStream(derSequenceBytes)) {
+ seq = (ASN1Sequence) seqInputStream.readObject();
+ }
+ }
+
+ this.attestationVersion = getIntegerFromAsn1(seq.getObjectAt(ATTESTATION_VERSION_INDEX));
+ this.attestationSecurityLevel =
+ securityLevelToEnum(getIntegerFromAsn1(
+ seq.getObjectAt(ATTESTATION_SECURITY_LEVEL_INDEX)));
+ this.keymasterVersion = getIntegerFromAsn1(seq.getObjectAt(KEYMASTER_VERSION_INDEX));
+ this.keymasterSecurityLevel = securityLevelToEnum(
+ getIntegerFromAsn1(seq.getObjectAt(KEYMASTER_SECURITY_LEVEL_INDEX)));
+ this.attestationChallenge =
+ ((ASN1OctetString) seq.getObjectAt(ATTESTATION_CHALLENGE_INDEX)).getOctets();
+ this.uniqueId = ((ASN1OctetString) seq.getObjectAt(UNIQUE_ID_INDEX)).getOctets();
+
+ this.softwareEnforcedAuthorizations = getAuthorizationMap(
+ ((ASN1Sequence) seq.getObjectAt(SW_ENFORCED_INDEX)).toArray());
+
+ this.teeEnforcedAuthorizations = getAuthorizationMap(
+ ((ASN1Sequence) seq.getObjectAt(TEE_ENFORCED_INDEX)).toArray());
+ }
+
+ private static SecurityLevel securityLevelToEnum(int securityLevel) {
+ switch (securityLevel) {
+ case KM_SECURITY_LEVEL_SOFTWARE:
+ return SecurityLevel.SOFTWARE;
+ case KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT:
+ return SecurityLevel.TRUSTED_ENVIRONMENT;
+ case KM_SECURITY_LEVEL_STRONG_BOX:
+ return SecurityLevel.STRONG_BOX;
+ default:
+ throw new IllegalArgumentException("Invalid security level.");
+ }
+ }
+}
diff --git a/tests/tests/identity/src/android/security/identity/cts/ProvisioningTest.java b/tests/tests/identity/src/android/security/identity/cts/ProvisioningTest.java
index 8269234..48cc405 100644
--- a/tests/tests/identity/src/android/security/identity/cts/ProvisioningTest.java
+++ b/tests/tests/identity/src/android/security/identity/cts/ProvisioningTest.java
@@ -107,11 +107,17 @@
static Collection<X509Certificate> createCredential(IdentityCredentialStore store,
String credentialName) throws IdentityCredentialException {
+ return createCredentialWithChallenge(store, credentialName, "SomeChallenge".getBytes());
+ }
+
+ static Collection<X509Certificate> createCredentialWithChallenge(IdentityCredentialStore store,
+ String credentialName,
+ byte[] challenge) throws IdentityCredentialException {
WritableIdentityCredential wc = null;
wc = store.createCredential(credentialName, "org.iso.18013-5.2019.mdl");
Collection<X509Certificate> certificateChain =
- wc.getCredentialKeyCertificateChain("SomeChallenge".getBytes());
+ wc.getCredentialKeyCertificateChain(challenge);
// TODO: inspect cert-chain
// Profile 0 (no authentication)
diff --git a/tests/tests/keystore/src/android/keystore/cts/DesCipherPerformanceTest.java b/tests/tests/keystore/src/android/keystore/cts/DesCipherPerformanceTest.java
index 5d9d7aa..527dee5 100644
--- a/tests/tests/keystore/src/android/keystore/cts/DesCipherPerformanceTest.java
+++ b/tests/tests/keystore/src/android/keystore/cts/DesCipherPerformanceTest.java
@@ -18,8 +18,6 @@
import android.security.keystore.KeyProperties;
-import org.junit.Test;
-
import java.security.AlgorithmParameters;
import javax.crypto.Cipher;
@@ -31,18 +29,30 @@
final int[] TEST_MESSAGE_SIZES = {1 << 6, 1 << 10, 1 << 17};
public void testDESede_CBC_NoPadding() throws Exception {
+ if (!TestUtils.supports3DES()) {
+ return;
+ }
testDesCipher("DESede/CBC/NoPadding", SUPPORTED_DES_KEY_SIZES, TEST_MESSAGE_SIZES);
}
public void testDESede_CBC_PKCS7Padding() throws Exception {
+ if (!TestUtils.supports3DES()) {
+ return;
+ }
testDesCipher("DESede/CBC/PKCS7Padding", SUPPORTED_DES_KEY_SIZES, TEST_MESSAGE_SIZES);
}
public void testDESede_ECB_NoPadding() throws Exception {
+ if (!TestUtils.supports3DES()) {
+ return;
+ }
testDesCipher("DESede/ECB/NoPadding", SUPPORTED_DES_KEY_SIZES, TEST_MESSAGE_SIZES);
}
public void testDESede_ECB_PKCS7Padding() throws Exception {
+ if (!TestUtils.supports3DES()) {
+ return;
+ }
testDesCipher("DESede/ECB/PKCS7Padding", SUPPORTED_DES_KEY_SIZES, TEST_MESSAGE_SIZES);
}
diff --git a/tests/tests/keystore/src/android/keystore/cts/DesKeyGenPerformanceTest.java b/tests/tests/keystore/src/android/keystore/cts/DesKeyGenPerformanceTest.java
index 195d573..0d9d627 100644
--- a/tests/tests/keystore/src/android/keystore/cts/DesKeyGenPerformanceTest.java
+++ b/tests/tests/keystore/src/android/keystore/cts/DesKeyGenPerformanceTest.java
@@ -18,13 +18,14 @@
import android.security.keystore.KeyProperties;
-import org.junit.Test;
-
public class DesKeyGenPerformanceTest extends PerformanceTestBase {
final int[] SUPPORTED_DES_KEY_SIZES = {168};
public void testDesKeyGen() throws Exception {
+ if (!TestUtils.supports3DES()) {
+ return;
+ }
for (int keySize : SUPPORTED_DES_KEY_SIZES) {
measure(
new KeystoreSecretKeyGenMeasurable(
diff --git a/tests/tests/keystore/src/android/keystore/cts/ImportWrappedKeyTest.java b/tests/tests/keystore/src/android/keystore/cts/ImportWrappedKeyTest.java
index ba94982..e967438 100644
--- a/tests/tests/keystore/src/android/keystore/cts/ImportWrappedKeyTest.java
+++ b/tests/tests/keystore/src/android/keystore/cts/ImportWrappedKeyTest.java
@@ -406,10 +406,9 @@
private KeyPair genKeyPair(String alias, boolean isStrongBoxBacked) throws Exception {
KeyPairGenerator kpg =
KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA, "AndroidKeyStore");
-
kpg.initialize(
new KeyGenParameterSpec.Builder(alias, KeyProperties.PURPOSE_WRAP_KEY)
- .setDigests(KeyProperties.DIGEST_SHA512, KeyProperties.DIGEST_SHA256)
+ .setDigests(KeyProperties.DIGEST_SHA256)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_OAEP)
.setBlockModes(KeyProperties.BLOCK_MODE_ECB)
.setIsStrongBoxBacked(isStrongBoxBacked)
diff --git a/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java b/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java
index 55fecfe..46592ca 100644
--- a/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java
+++ b/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java
@@ -234,14 +234,16 @@
KeyGenParameterSpec.Builder builder =
new KeyGenParameterSpec.Builder(keystoreAlias, PURPOSE_SIGN)
.setAlgorithmParameterSpec(new ECGenParameterSpec("secp256r1"))
- .setDigests(DIGEST_NONE, DIGEST_SHA256, DIGEST_SHA512)
.setAttestationChallenge(new byte[128])
.setKeyValidityStart(now)
.setKeyValidityForOriginationEnd(originationEnd)
.setKeyValidityForConsumptionEnd(consumptionEnd);
if (TestUtils.hasStrongBox(getContext())) {
+ builder.setDigests(DIGEST_NONE, DIGEST_SHA256);
builder.setIsStrongBoxBacked(true);
+ } else {
+ builder.setDigests(DIGEST_NONE, DIGEST_SHA256, DIGEST_SHA512);
}
generateKeyPair(KEY_ALGORITHM_EC, builder.build());
@@ -251,7 +253,7 @@
try {
Certificate certificates[] = keyStore.getCertificateChain(keystoreAlias);
- verifyCertificateChain(certificates);
+ verifyCertificateChain(certificates, TestUtils.hasStrongBox(getContext()));
X509Certificate attestationCert = (X509Certificate) certificates[0];
checkDeviceLocked(new Attestation(attestationCert));
@@ -392,7 +394,10 @@
.setKeyValidityForConsumptionEnd(consumptionEnd);
if (TestUtils.hasStrongBox(getContext())) {
+ builder.setDigests(DIGEST_NONE, DIGEST_SHA256);
builder.setIsStrongBoxBacked(true);
+ } else {
+ builder.setDigests(DIGEST_NONE, DIGEST_SHA256, DIGEST_SHA512);
}
generateKeyPair(KEY_ALGORITHM_RSA, builder.build());
@@ -402,7 +407,7 @@
try {
Certificate certificates[] = keyStore.getCertificateChain(keystoreAlias);
- verifyCertificateChain(certificates);
+ verifyCertificateChain(certificates, TestUtils.hasStrongBox(getContext()));
X509Certificate attestationCert = (X509Certificate) certificates[0];
checkDeviceLocked(new Attestation(attestationCert));
@@ -504,7 +509,7 @@
try {
Certificate certificates[] = keyStore.getCertificateChain(keystoreAlias);
- verifyCertificateChain(certificates);
+ verifyCertificateChain(certificates, false /* expectStrongBox */);
X509Certificate attestationCert = (X509Certificate) certificates[0];
Attestation attestation = new Attestation(attestationCert);
@@ -558,7 +563,7 @@
try {
Certificate certificates[] = keyStore.getCertificateChain(keystoreAlias);
- verifyCertificateChain(certificates);
+ verifyCertificateChain(certificates, false /* expectStrongBox */);
X509Certificate attestationCert = (X509Certificate) certificates[0];
Attestation attestation = new Attestation(attestationCert);
@@ -1076,7 +1081,7 @@
keyPairGenerator.generateKeyPair();
}
- private void verifyCertificateChain(Certificate[] certChain)
+ private void verifyCertificateChain(Certificate[] certChain, boolean expectStrongBox)
throws GeneralSecurityException {
assertNotNull(certChain);
for (int i = 1; i < certChain.length; ++i) {
@@ -1107,14 +1112,15 @@
assertEquals(signedCertSubject, new X500Name("CN=Android Keystore Key"));
} else {
// Only strongbox implementations should have strongbox in the subject line
- assertFalse(x509CurrCert.getSubjectDN()
- .getName()
- .toLowerCase()
- .contains("strongbox"));
+ assertEquals(expectStrongBox, x509CurrCert.getSubjectDN()
+ .getName()
+ .toLowerCase()
+ .contains("strongbox"));
}
} catch (InvalidKeyException | CertificateException | NoSuchAlgorithmException
| NoSuchProviderException | SignatureException e) {
- throw new GeneralSecurityException("Failed to verify certificate "
+ throw new GeneralSecurityException("Using StrongBox: " + expectStrongBox + "\n"
+ + "Failed to verify certificate "
+ certChain[i - 1] + " with public key " + certChain[i].getPublicKey(), e);
}
}
diff --git a/tests/tests/keystore/src/android/keystore/cts/KeyPairGeneratorTest.java b/tests/tests/keystore/src/android/keystore/cts/KeyPairGeneratorTest.java
index 6353fed..40970f5 100644
--- a/tests/tests/keystore/src/android/keystore/cts/KeyPairGeneratorTest.java
+++ b/tests/tests/keystore/src/android/keystore/cts/KeyPairGeneratorTest.java
@@ -66,6 +66,7 @@
import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.X509ExtendedKeyManager;
@@ -1812,11 +1813,23 @@
}
@Override
+ public String chooseEngineClientAlias(String[] keyType, Principal[] issuers,
+ SSLEngine engine) {
+ return "fake";
+ }
+
+ @Override
public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) {
return "fake";
}
@Override
+ public String chooseEngineServerAlias(String keyType, Principal[] issuers,
+ SSLEngine engine) {
+ return "fake";
+ }
+
+ @Override
public X509Certificate[] getCertificateChain(String alias) {
return chain;
}
diff --git a/tests/tests/keystore/src/android/keystore/cts/TestUtils.java b/tests/tests/keystore/src/android/keystore/cts/TestUtils.java
index 23886be..dcfcc85 100644
--- a/tests/tests/keystore/src/android/keystore/cts/TestUtils.java
+++ b/tests/tests/keystore/src/android/keystore/cts/TestUtils.java
@@ -827,36 +827,37 @@
}
static int getMaxSupportedPlaintextInputSizeBytes(String transformation, int keySizeBits) {
+ String encryptionPadding = getCipherEncryptionPadding(transformation);
+ int modulusSizeBytes = (keySizeBits + 7) / 8;
+ if (KeyProperties.ENCRYPTION_PADDING_NONE.equalsIgnoreCase(encryptionPadding)) {
+ return modulusSizeBytes - 1;
+ } else if (KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1.equalsIgnoreCase(
+ encryptionPadding)) {
+ return modulusSizeBytes - 11;
+ } else if (KeyProperties.ENCRYPTION_PADDING_RSA_OAEP.equalsIgnoreCase(
+ encryptionPadding)) {
+ String digest = getCipherDigest(transformation);
+ int digestOutputSizeBytes = (getDigestOutputSizeBits(digest) + 7) / 8;
+ return modulusSizeBytes - 2 * digestOutputSizeBytes - 2;
+ } else {
+ throw new IllegalArgumentException(
+ "Unsupported encryption padding scheme: " + encryptionPadding);
+ }
+
+ }
+
+ static int getMaxSupportedPlaintextInputSizeBytes(String transformation, Key key) {
String keyAlgorithm = getCipherKeyAlgorithm(transformation);
if (KeyProperties.KEY_ALGORITHM_AES.equalsIgnoreCase(keyAlgorithm)
|| KeyProperties.KEY_ALGORITHM_3DES.equalsIgnoreCase(keyAlgorithm)) {
return Integer.MAX_VALUE;
} else if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(keyAlgorithm)) {
- String encryptionPadding = getCipherEncryptionPadding(transformation);
- int modulusSizeBytes = (keySizeBits + 7) / 8;
- if (KeyProperties.ENCRYPTION_PADDING_NONE.equalsIgnoreCase(encryptionPadding)) {
- return modulusSizeBytes - 1;
- } else if (KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1.equalsIgnoreCase(
- encryptionPadding)) {
- return modulusSizeBytes - 11;
- } else if (KeyProperties.ENCRYPTION_PADDING_RSA_OAEP.equalsIgnoreCase(
- encryptionPadding)) {
- String digest = getCipherDigest(transformation);
- int digestOutputSizeBytes = (getDigestOutputSizeBits(digest) + 7) / 8;
- return modulusSizeBytes - 2 * digestOutputSizeBytes - 2;
- } else {
- throw new IllegalArgumentException(
- "Unsupported encryption padding scheme: " + encryptionPadding);
- }
+ return getMaxSupportedPlaintextInputSizeBytes(transformation, getKeySizeBits(key));
} else {
throw new IllegalArgumentException("Unsupported key algorithm: " + keyAlgorithm);
}
}
- static int getMaxSupportedPlaintextInputSizeBytes(String transformation, Key key) {
- return getMaxSupportedPlaintextInputSizeBytes(transformation, getKeySizeBits(key));
- }
-
static int getDigestOutputSizeBits(String digest) {
if (KeyProperties.DIGEST_NONE.equals(digest)) {
return -1;
diff --git a/tests/tests/libthermalndk/Android.bp b/tests/tests/libthermalndk/Android.bp
new file mode 100644
index 0000000..2b7d126
--- /dev/null
+++ b/tests/tests/libthermalndk/Android.bp
@@ -0,0 +1,38 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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: "CtsThermalTestCases",
+ defaults: ["cts_defaults"],
+ compile_multilib: "both",
+ static_libs: [
+ "compatibility-device-util-axt",
+ "ctstestrunner-axt",
+ ],
+ libs: [
+ "android.test.runner.stubs",
+ "android.test.base.stubs",
+ ],
+ jni_libs: [
+ "libctsthermal_jni",
+ ],
+ srcs: ["src/**/*.java"],
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "vts",
+ "general-tests",
+ ],
+ sdk_version: "test_current",
+}
diff --git a/tests/tests/libthermalndk/AndroidManifest.xml b/tests/tests/libthermalndk/AndroidManifest.xml
new file mode 100644
index 0000000..eb6eb22
--- /dev/null
+++ b/tests/tests/libthermalndk/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.thermal.cts">
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.thermal.cts"
+ android:label="CTS Thermal tests of android.thermal" >
+ <meta-data
+ android:name="listener"
+ android:value="com.android.cts.runner.CtsTestRunListener" />
+ </instrumentation>
+</manifest>
diff --git a/tests/tests/libthermalndk/AndroidTest.xml b/tests/tests/libthermalndk/AndroidTest.xml
new file mode 100644
index 0000000..e4ab0ac
--- /dev/null
+++ b/tests/tests/libthermalndk/AndroidTest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Config for CTS Thermal test cases">
+ <option name="test-suite-tag" value="cts" />
+ <option name="config-descriptor:metadata" key="component" value="framework" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
+ <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="CtsThermalTestCases.apk" />
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="android.thermal.cts" />
+ </test>
+</configuration>
diff --git a/tests/tests/libthermalndk/OWNERS b/tests/tests/libthermalndk/OWNERS
new file mode 100644
index 0000000..1c617ec
--- /dev/null
+++ b/tests/tests/libthermalndk/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 46788
+michaelwr@google.com
+lzye@google.com
diff --git a/tests/tests/libthermalndk/jni/Android.bp b/tests/tests/libthermalndk/jni/Android.bp
new file mode 100644
index 0000000..9f95e7f
--- /dev/null
+++ b/tests/tests/libthermalndk/jni/Android.bp
@@ -0,0 +1,31 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_test_library {
+ name: "libctsthermal_jni",
+ srcs: [
+ "NativeThermalTest.cpp",
+ ],
+ shared_libs: [
+ "libandroid",
+ "liblog",
+ "libbase",
+ ],
+ stl: "libc++_static",
+ cflags: [
+ "-Werror",
+ "-Wall",
+ ],
+ gtest: false,
+}
diff --git a/tests/tests/libthermalndk/jni/NativeThermalTest.cpp b/tests/tests/libthermalndk/jni/NativeThermalTest.cpp
new file mode 100644
index 0000000..913a8ae
--- /dev/null
+++ b/tests/tests/libthermalndk/jni/NativeThermalTest.cpp
@@ -0,0 +1,286 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "NativeThermalTest"
+
+#include <condition_variable>
+#include <jni.h>
+#include <mutex>
+#include <optional>
+#include <thread>
+#include <inttypes.h>
+#include <time.h>
+#include <unistd.h>
+#include <vector>
+
+#include <android/thermal.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android-base/thread_annotations.h>
+#include <log/log.h>
+#include <sys/stat.h>
+#include <utils/Errors.h>
+
+using namespace android;
+using namespace std::chrono_literals;
+using android::base::StringPrintf;
+
+struct AThermalTestContext {
+ AThermalManager *mThermalMgr;
+ std::mutex mMutex;
+ std::condition_variable mCv;
+ std::vector<AThermalStatus> mListenerStatus GUARDED_BY(mMutex);
+};
+
+static jclass gNativeThermalTest_class;
+static jmethodID gNativeThermalTest_thermalOverrideMethodID;
+
+int onStatusChange(void *data, AThermalStatus status) {
+ AThermalTestContext *ctx = static_cast<AThermalTestContext *>(data);
+ if (ctx == nullptr) {
+ return BAD_VALUE;
+ } else {
+ std::lock_guard<std::mutex> guard(ctx->mMutex);
+ ctx->mListenerStatus.push_back(status);
+ ctx->mCv.notify_all();
+ }
+ return OK;
+}
+
+static inline void setThermalStatusOverride(JNIEnv* env, jobject obj, int32_t level) {
+ env->CallVoidMethod(obj, gNativeThermalTest_thermalOverrideMethodID, level);
+}
+
+static inline jstring returnJString(JNIEnv *env, std::optional<std::string> result) {
+ if (result.has_value()) {
+ return env->NewStringUTF(result.value().c_str());
+ } else {
+ return env->NewStringUTF("");
+ }
+}
+
+static std::optional<std::string> testGetCurrentThermalStatus(
+ JNIEnv *env, jobject obj, int32_t level) {
+ AThermalTestContext ctx;
+
+ ctx.mThermalMgr = AThermal_acquireManager();
+ if (ctx.mThermalMgr == nullptr) {
+ return "AThermal_acquireManager failed";
+ }
+
+ setThermalStatusOverride(env, obj, level);
+ AThermalStatus thermalStatus = AThermal_getCurrentThermalStatus(ctx.mThermalMgr);
+ if (thermalStatus == ATHERMAL_STATUS_ERROR) {
+ return "getCurrentThermalStatus returns ATHERMAL_STATUS_ERROR";
+ }
+ // Verify the current thermal status is same as override
+ if (thermalStatus != static_cast<AThermalStatus>(level)) {
+ return StringPrintf("getCurrentThermalStatus %" PRId32 " != override %" PRId32 ".",
+ thermalStatus, level);
+ }
+
+ AThermal_releaseManager(ctx.mThermalMgr);
+ return std::nullopt;
+}
+
+static jstring nativeTestGetCurrentThermalStatus(JNIEnv *env, jobject obj, jint level) {
+ return returnJString(env, testGetCurrentThermalStatus(env, obj, static_cast<int32_t>(level)));
+}
+
+static std::optional<std::string> testRegisterThermalStatusListener(JNIEnv *env, jobject obj) {
+ AThermalTestContext ctx;
+ std::unique_lock<std::mutex> lock(ctx.mMutex);
+
+ ctx.mThermalMgr = AThermal_acquireManager();
+ if (ctx.mThermalMgr == nullptr) {
+ return "AThermal_acquireManager failed";
+ }
+
+ // Register a listener with valid callback
+ int ret = AThermal_registerThermalStatusListener(ctx.mThermalMgr, onStatusChange, &ctx);
+ if (ret != 0) {
+ return StringPrintf("AThermal_registerThermalStatusListener failed: %s",
+ strerror(ret));
+ }
+
+ // Expect the callback after registration
+ if (ctx.mCv.wait_for(lock, 1s) == std::cv_status::timeout) {
+ return "Listener callback should be called after registration";
+ }
+
+ // Verify the current thermal status is same as listener callback
+ auto thermalStatus = AThermal_getCurrentThermalStatus(ctx.mThermalMgr);
+ auto listenerStatus = ctx.mListenerStatus.back();
+ if (thermalStatus != listenerStatus) {
+ return StringPrintf("thermalStatus %" PRId32 " != Listener status %" PRId32 ".",
+ thermalStatus, listenerStatus);
+ }
+
+ // Change override level and verify the listener callback
+ for (int32_t level = ATHERMAL_STATUS_LIGHT; level <= ATHERMAL_STATUS_SHUTDOWN; level++) {
+ setThermalStatusOverride(env, obj, level);
+ if (ctx.mCv.wait_for(lock, 1s) == std::cv_status::timeout) {
+ return StringPrintf("Listener callback timeout at level %" PRId32, level);
+ }
+ auto overrideStatus = static_cast<AThermalStatus>(level);
+ auto listenerStatus = ctx.mListenerStatus.back();
+ if (listenerStatus != overrideStatus) {
+ return StringPrintf("Listener thermalStatus%" PRId32 " != override %" PRId32 ".",
+ listenerStatus, overrideStatus);
+ }
+ }
+
+ // Unregister listener
+ ret = AThermal_unregisterThermalStatusListener(ctx.mThermalMgr, onStatusChange, &ctx);
+ if (ret != 0) {
+ return StringPrintf("AThermal_unregisterThermalStatusListener failed: %s",
+ strerror(ret));
+ }
+
+ AThermal_releaseManager(ctx.mThermalMgr);
+ return std::nullopt;
+}
+
+static jstring nativeTestRegisterThermalStatusListener(JNIEnv *env, jobject obj) {
+ return returnJString(env, testRegisterThermalStatusListener(env, obj));
+}
+
+static std::optional<std::string> testThermalStatusRegisterNullListener() {
+ AThermalTestContext ctx;
+
+ ctx.mThermalMgr = AThermal_acquireManager();
+ if (ctx.mThermalMgr == nullptr) {
+ return StringPrintf("AThermal_acquireManager failed");
+ }
+
+ // Register a listener with null callback
+ int ret = AThermal_registerThermalStatusListener(ctx.mThermalMgr, nullptr, &ctx);
+ if (ret != EINVAL) {
+ return "AThermal_registerThermalStatusListener should fail with null callback";
+ }
+
+ // Register a listener with null data
+ ret = AThermal_registerThermalStatusListener(ctx.mThermalMgr, onStatusChange, &ctx);
+ if (ret != 0) {
+ return StringPrintf("AThermal_registerThermalStatusListener failed: %s",
+ strerror(ret));
+ }
+
+ // Unregister listener with null callback and null data
+ ret = AThermal_unregisterThermalStatusListener(ctx.mThermalMgr, nullptr, nullptr);
+ if (ret != EINVAL) {
+ return "AThermal_unregisterThermalStatusListener should fail with null listener";
+ }
+
+ AThermal_releaseManager(ctx.mThermalMgr);
+ return std::nullopt;
+}
+
+static jstring nativeTestThermalStatusRegisterNullListener(JNIEnv *env, jobject) {
+ return returnJString(env, testThermalStatusRegisterNullListener());
+}
+
+static std::optional<std::string> testThermalStatusListenerDoubleRegistration
+ (JNIEnv *env, jobject obj) {
+ AThermalTestContext ctx;
+ std::unique_lock<std::mutex> lock(ctx.mMutex);
+
+ ctx.mThermalMgr = AThermal_acquireManager();
+ if (ctx.mThermalMgr == nullptr) {
+ return "AThermal_acquireManager failed";
+ }
+
+ // Register a listener with valid callback
+ int ret = AThermal_registerThermalStatusListener(ctx.mThermalMgr, onStatusChange, &ctx);
+ if (ret != 0) {
+ return StringPrintf("AThermal_registerThermalStatusListener failed: %s",
+ strerror(ret));
+ }
+
+ // Register the listener again with same callback and data
+ ret = AThermal_registerThermalStatusListener(ctx.mThermalMgr, onStatusChange, &ctx);
+ if (ret != EINVAL) {
+ return "Register should fail as listener already registered";
+ }
+
+ // Register a listener with same callback but null data
+ ret = AThermal_registerThermalStatusListener(ctx.mThermalMgr, onStatusChange, nullptr);
+ if (ret != 0) {
+ return StringPrintf("Register listener with null data failed: %s", strerror(ret));
+ }
+
+ // Expect listener callback
+ if (ctx.mCv.wait_for(lock, 1s) == std::cv_status::timeout) {
+ return "Thermal listener callback timeout";
+ }
+
+ // Unregister listener
+ ret = AThermal_unregisterThermalStatusListener(ctx.mThermalMgr, onStatusChange, &ctx);
+ if (ret != 0) {
+ return StringPrintf("AThermal_unregisterThermalStatusListener failed: %s",
+ strerror(ret));
+ }
+
+ for (int32_t level = ATHERMAL_STATUS_LIGHT; level <= ATHERMAL_STATUS_SHUTDOWN; level++) {
+ setThermalStatusOverride(env, obj, level);
+ // Expect no listener callback
+ if (ctx.mCv.wait_for(lock, 1s) != std::cv_status::timeout) {
+ return "Thermal listener got callback after unregister.";
+ }
+ }
+
+ // Unregister listener already unregistered
+ ret = AThermal_unregisterThermalStatusListener(ctx.mThermalMgr, onStatusChange, &ctx);
+ if (ret != EINVAL) {
+ return "Unregister should fail with listener already unregistered";
+ }
+
+ AThermal_releaseManager(ctx.mThermalMgr);
+ return std::nullopt;
+}
+
+static jstring nativeTestThermalStatusListenerDoubleRegistration(JNIEnv *env, jobject obj) {
+ return returnJString(env, testThermalStatusListenerDoubleRegistration(env, obj));
+}
+
+extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void*) {
+ JNIEnv* env;
+ const JNINativeMethod methodTable[] = {
+ {"nativeTestGetCurrentThermalStatus", "(I)Ljava/lang/String;",
+ (void*)nativeTestGetCurrentThermalStatus},
+ {"nativeTestRegisterThermalStatusListener", "()Ljava/lang/String;",
+ (void*)nativeTestRegisterThermalStatusListener},
+ {"nativeTestThermalStatusRegisterNullListener", "()Ljava/lang/String;",
+ (void*)nativeTestThermalStatusRegisterNullListener},
+ {"nativeTestThermalStatusListenerDoubleRegistration", "()Ljava/lang/String;",
+ (void*)nativeTestThermalStatusListenerDoubleRegistration},
+ };
+ if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
+ return JNI_ERR;
+ }
+ gNativeThermalTest_class = env->FindClass("android/thermal/cts/NativeThermalTest");
+ gNativeThermalTest_thermalOverrideMethodID =
+ env->GetMethodID(gNativeThermalTest_class, "setOverrideStatus", "(I)V");
+ if (gNativeThermalTest_thermalOverrideMethodID == nullptr) {
+ return JNI_ERR;
+ }
+ if (env->RegisterNatives(gNativeThermalTest_class, methodTable,
+ sizeof(methodTable) / sizeof(JNINativeMethod)) != JNI_OK) {
+ return JNI_ERR;
+ }
+ return JNI_VERSION_1_6;
+}
diff --git a/tests/tests/libthermalndk/src/android/thermal/cts/NativeThermalTest.java b/tests/tests/libthermalndk/src/android/thermal/cts/NativeThermalTest.java
new file mode 100644
index 0000000..ab6d518
--- /dev/null
+++ b/tests/tests/libthermalndk/src/android/thermal/cts/NativeThermalTest.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.thermal.cts;
+
+import android.content.Context;
+import android.os.PowerManager;
+import android.support.test.uiautomator.UiDevice;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+import com.google.common.base.Strings;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.runner.RunWith;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+
+import static org.junit.Assert.fail;
+
+/**
+ * Tests native thermal API for get current thermal status, register and unregister
+ * thermal status listeners.
+ */
+@RunWith(AndroidJUnit4.class)
+public class NativeThermalTest {
+ private UiDevice mUiDevice;
+ private Executor mExec = Executors.newSingleThreadExecutor();
+
+ private native String nativeTestGetCurrentThermalStatus(int level);
+ private native String nativeTestRegisterThermalStatusListener();
+ private native String nativeTestThermalStatusRegisterNullListener();
+ private native String nativeTestThermalStatusListenerDoubleRegistration();
+
+ @Before
+ public void setUp() throws Exception {
+ mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mUiDevice.executeShellCommand("cmd thermalservice reset");
+ }
+
+ /**
+ * Helper function to set override status
+ */
+ public void setOverrideStatus (int level) throws Exception {
+ mUiDevice.executeShellCommand("cmd thermalservice override-status " + level);
+ }
+
+ /**
+ * Confirm that we can get thermal status.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testGetCurrentThermalStatus() throws Exception {
+ for (int level = PowerManager.THERMAL_STATUS_NONE;
+ level < PowerManager.THERMAL_STATUS_SHUTDOWN; level++) {
+ final String failureMessage = nativeTestGetCurrentThermalStatus(level);
+ if (!Strings.isNullOrEmpty(failureMessage)) {
+ fail(failureMessage);
+ }
+ }
+ }
+
+ /**
+ * Confirm that we can register thermal status listener and get callback.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testRegisterThermalStatusListener() throws Exception {
+ final String failureMessage = nativeTestRegisterThermalStatusListener();
+ if (!Strings.isNullOrEmpty(failureMessage)) {
+ fail(failureMessage);
+ }
+ }
+
+ /**
+ * Confirm that register null thermal status listener fails with error.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testThermalStatusRegisterNullListener() throws Exception {
+ final String failureMessage = nativeTestThermalStatusRegisterNullListener();
+ if (!Strings.isNullOrEmpty(failureMessage)) {
+ fail(failureMessage);
+ }
+ }
+
+ /**
+ * Confirm that double register and unregister same listener fails with error.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testThermalStatusListenerDoubleRegistration() throws Exception {
+ final String failureMessage = nativeTestThermalStatusListenerDoubleRegistration();
+ if (!Strings.isNullOrEmpty(failureMessage)) {
+ fail(failureMessage);
+ }
+ }
+
+ static {
+ System.loadLibrary("ctsthermal_jni");
+ }
+}
diff --git a/tests/tests/match_flags/Android.bp b/tests/tests/match_flags/Android.bp
new file mode 100644
index 0000000..7debb8a
--- /dev/null
+++ b/tests/tests/match_flags/Android.bp
@@ -0,0 +1,32 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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: "CtsMatchFlagTestCases",
+ defaults: ["cts_defaults"],
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "vts",
+ "general-tests",
+ ],
+ static_libs: [
+ "ctstestrunner-axt",
+ "compatibility-device-util-axt",
+ "androidx.test.ext.junit",
+ ],
+
+ srcs: ["src/**/*.java"],
+ sdk_version: "test_current",
+}
diff --git a/tests/tests/match_flags/AndroidManifest.xml b/tests/tests/match_flags/AndroidManifest.xml
new file mode 100644
index 0000000..1b6cb42
--- /dev/null
+++ b/tests/tests/match_flags/AndroidManifest.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT 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.matchflags.cts">
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.matchflags.cts"
+ android:label="CTS tests for match flags">
+ <meta-data android:name="listener"
+ android:value="com.android.cts.runner.CtsTestRunListener" />
+ </instrumentation>
+</manifest>
+
diff --git a/tests/tests/match_flags/AndroidTest.xml b/tests/tests/match_flags/AndroidTest.xml
new file mode 100644
index 0000000..a5fadab
--- /dev/null
+++ b/tests/tests/match_flags/AndroidTest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT 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 match flag CTS test cases">
+ <option name="test-suite-tag" value="cts" />
+ <option name="config-descriptor:metadata" key="component" value="framework" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
+ <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="CtsMatchFlagTestCases.apk" />
+ <option name="test-file-name" value="CtsMatchFlagsUniqueAndSharedUri.apk" />
+ <option name="test-file-name" value="CtsMatchFlagsSharedUri.apk" />
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="android.matchflags.cts" />
+ <option name="runtime-hint" value="12m30s" />
+ </test>
+</configuration>
diff --git a/tests/tests/match_flags/OWNERS b/tests/tests/match_flags/OWNERS
new file mode 100644
index 0000000..8a44fb2
--- /dev/null
+++ b/tests/tests/match_flags/OWNERS
@@ -0,0 +1,5 @@
+# Bug component: 36137
+patb@google.com
+toddke@google.com
+chiuwinson@google.com
+rtmitchell@google.com
diff --git a/tests/tests/match_flags/app/a/Android.bp b/tests/tests/match_flags/app/a/Android.bp
new file mode 100644
index 0000000..9a5db56
--- /dev/null
+++ b/tests/tests/match_flags/app/a/Android.bp
@@ -0,0 +1,27 @@
+// Copyright (C) 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+android_test_helper_app {
+ name: "CtsMatchFlagsUniqueAndSharedUri",
+ manifest: "AndroidManifest.xml",
+ defaults: ["cts_support_defaults"],
+ srcs: ["src/**/*.java"],
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "vts",
+ "general-tests",
+ ],
+ sdk_version: "test_current",
+}
\ No newline at end of file
diff --git a/tests/tests/match_flags/app/a/AndroidManifest.xml b/tests/tests/match_flags/app/a/AndroidManifest.xml
new file mode 100644
index 0000000..8ad54de
--- /dev/null
+++ b/tests/tests/match_flags/app/a/AndroidManifest.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT 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.matchflags.app.uniqueandshared">
+ <application>
+ <uses-library android:name="android.test.runner"/>
+ <activity android:name="android.matchflags.cts.app.TestActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.VIEW"/>
+ <category android:name="android.intent.category.BROWSABLE"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ <data android:scheme="https"
+ android:host="unique-5gle2bs6woovjn8xabwyb3js01xl0ducci3gd3fpe622h48lyg.com"/>
+ </intent-filter>
+ <intent-filter>
+ <action android:name="android.matchflags.app.UNIQUE_ACTION"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ </intent-filter>
+ <intent-filter>
+ <action android:name="android.matchflags.app.SHARED_ACTION"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/tests/tests/match_flags/app/a/src/android/matchflags/cts/app/TestActivity.java b/tests/tests/match_flags/app/a/src/android/matchflags/cts/app/TestActivity.java
new file mode 100644
index 0000000..e9046eb
--- /dev/null
+++ b/tests/tests/match_flags/app/a/src/android/matchflags/cts/app/TestActivity.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.matchflags.cts.app;
+
+import static android.content.Intent.EXTRA_RETURN_RESULT;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.os.RemoteCallback;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.SparseArray;
+
+import java.util.Random;
+
+public class TestActivity extends Activity {
+}
\ No newline at end of file
diff --git a/tests/tests/match_flags/app/b/Android.bp b/tests/tests/match_flags/app/b/Android.bp
new file mode 100644
index 0000000..8c49047
--- /dev/null
+++ b/tests/tests/match_flags/app/b/Android.bp
@@ -0,0 +1,27 @@
+// Copyright (C) 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+android_test_helper_app {
+ name: "CtsMatchFlagsSharedUri",
+ manifest: "AndroidManifest.xml",
+ defaults: ["cts_support_defaults"],
+ srcs: ["src/**/*.java"],
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "vts",
+ "general-tests",
+ ],
+ sdk_version: "test_current",
+}
\ No newline at end of file
diff --git a/tests/tests/match_flags/app/b/AndroidManifest.xml b/tests/tests/match_flags/app/b/AndroidManifest.xml
new file mode 100644
index 0000000..6d2c9d0
--- /dev/null
+++ b/tests/tests/match_flags/app/b/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT 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.matchflags.app.shared">
+ <application>
+ <uses-library android:name="android.test.runner"/>
+ <activity android:name="android.matchflags.cts.app.TestActivity">
+ <intent-filter>
+ <action android:name="android.matchflags.app.SHARED_ACTION"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/tests/tests/match_flags/app/b/src/android/matchflags/cts/app/TestActivity.java b/tests/tests/match_flags/app/b/src/android/matchflags/cts/app/TestActivity.java
new file mode 100644
index 0000000..e9046eb
--- /dev/null
+++ b/tests/tests/match_flags/app/b/src/android/matchflags/cts/app/TestActivity.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.matchflags.cts.app;
+
+import static android.content.Intent.EXTRA_RETURN_RESULT;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.os.RemoteCallback;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.SparseArray;
+
+import java.util.Random;
+
+public class TestActivity extends Activity {
+}
\ No newline at end of file
diff --git a/tests/tests/match_flags/src/android/matchflags/cts/MatchFlagTests.java b/tests/tests/match_flags/src/android/matchflags/cts/MatchFlagTests.java
new file mode 100644
index 0000000..da7e212
--- /dev/null
+++ b/tests/tests/match_flags/src/android/matchflags/cts/MatchFlagTests.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.matchflags.cts;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+
+import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.ConditionVariable;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.RemoteCallback;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.compatibility.common.util.SystemUtil;
+
+import org.hamcrest.core.IsNull;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestName;
+import org.junit.runner.RunWith;
+
+import java.util.Objects;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicReference;
+
+@RunWith(AndroidJUnit4.class)
+public class MatchFlagTests {
+
+ private static final String ONLY_BROWSER_URI =
+ "https://nohandler-02xgpcssu1v7xvpek0skc905glnyu7ihjtza3eufox0mauqyri.com";
+ private static final String UNIQUE_URI =
+ "https://unique-5gle2bs6woovjn8xabwyb3js01xl0ducci3gd3fpe622h48lyg.com";
+ @Rule
+ public TestName name = new TestName();
+
+ @BeforeClass
+ public static void setup() {
+
+ }
+
+ @Test
+ public void startNoBrowserIntentWithNoMatchingApps() throws Exception {
+ Intent onlyBrowserIntent = new Intent(Intent.ACTION_VIEW)
+ .addCategory(Intent.CATEGORY_BROWSABLE)
+ .setData(Uri.parse(ONLY_BROWSER_URI));
+
+ startActivity(onlyBrowserIntent);
+
+ Intent noBrowserWithBrowserOnlyIntent = new Intent(onlyBrowserIntent)
+ .addFlags(Intent.FLAG_ACTIVITY_REQUIRE_NON_BROWSER);
+
+ try {
+ startActivity(noBrowserWithBrowserOnlyIntent);
+ fail("Should not have started a browser-only intent with NON_BROWSER flag set");
+ } catch (ActivityNotFoundException e) {
+ // hooray!
+ }
+ }
+
+ @Test
+ public void startRequireDefaultWithNoDefault() throws Exception {
+ Intent sharedIntent = new Intent("android.matchflags.app.SHARED_ACTION");
+
+ startActivity(sharedIntent);
+
+ Intent sharedIntentRequireDefault = new Intent(sharedIntent)
+ .addFlags(Intent.FLAG_ACTIVITY_REQUIRE_DEFAULT);
+
+ try {
+ startActivity(sharedIntentRequireDefault);
+ fail("Should have started intent with no default when default required");
+ } catch (ActivityNotFoundException e) {
+ // hooray!
+ }
+ }
+
+ @Test
+ public void startRequireDefaultWithSingleMatch() throws Exception {
+ Intent uniqueIntent = new Intent("android.matchflags.app.UNIQUE_ACTION")
+ .addFlags(Intent.FLAG_ACTIVITY_REQUIRE_DEFAULT);
+ startActivity(uniqueIntent);
+ }
+
+ @Test
+ public void startNoBrowserRequireDefault() throws Exception {
+ Intent uniqueUriIntent = new Intent(Intent.ACTION_VIEW)
+ .addCategory(Intent.CATEGORY_BROWSABLE)
+ .setData(Uri.parse(UNIQUE_URI));
+
+ startActivity(uniqueUriIntent);
+
+ Intent uniqueUriIntentNoBrowserRequireDefault = new Intent(uniqueUriIntent)
+ .addFlags(Intent.FLAG_ACTIVITY_REQUIRE_NON_BROWSER)
+ .addFlags(Intent.FLAG_ACTIVITY_REQUIRE_DEFAULT);
+
+ startActivity(uniqueUriIntentNoBrowserRequireDefault);
+ }
+
+ private static void startActivity(Intent onlyBrowserIntent) {
+ InstrumentationRegistry.getInstrumentation().getContext().startActivity(
+ onlyBrowserIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
+ }
+
+}
diff --git a/tests/tests/media/AndroidManifest.xml b/tests/tests/media/AndroidManifest.xml
index a7727b1..1bec10d 100644
--- a/tests/tests/media/AndroidManifest.xml
+++ b/tests/tests/media/AndroidManifest.xml
@@ -143,7 +143,7 @@
android:foregroundServiceType="mediaProjection"
android:enabled="true">
</service>
- <service android:name=".SampleMediaRoute2ProviderService"
+ <service android:name=".StubMediaRoute2ProviderService"
android:exported="true">
<intent-filter>
<action android:name="android.media.MediaRoute2ProviderService" />
diff --git a/tests/tests/media/src/android/media/cts/AudioManagerTest.java b/tests/tests/media/src/android/media/cts/AudioManagerTest.java
index 90f517a..7e475d1 100644
--- a/tests/tests/media/src/android/media/cts/AudioManagerTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioManagerTest.java
@@ -37,7 +37,6 @@
import static android.media.AudioManager.VIBRATE_TYPE_RINGER;
import static android.provider.Settings.System.SOUND_EFFECTS_ENABLED;
-import android.app.ActivityManager;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.BroadcastReceiver;
@@ -114,8 +113,6 @@
mIsSingleVolume = mContext.getResources().getBoolean(
Resources.getSystem().getIdentifier("config_single_volume", "bool", "android"));
mSkipRingerTests = mUseFixedVolume || mIsTelevision || mIsSingleVolume;
- ActivityManager am = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
- mSupportNotificationPolicyAccess = !am.isLowRamDevice();
// Store the original volumes that that they can be recovered in tearDown().
final int[] streamTypes = {
@@ -133,19 +130,18 @@
mOriginalStreamVolumes.put(streamType, mAudioManager.getStreamVolume(streamType));
}
- if (mSupportNotificationPolicyAccess) {
- try {
- Utils.toggleNotificationPolicyAccess(
- mContext.getPackageName(), getInstrumentation(), true);
- mOriginalNotificationPolicy = mNm.getNotificationPolicy();
- mOriginalZen = mNm.getCurrentInterruptionFilter();
- } finally {
- setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_ALL);
- Utils.toggleNotificationPolicyAccess(
- mContext.getPackageName(), getInstrumentation(), false);
- }
+ try {
+ Utils.toggleNotificationPolicyAccess(
+ mContext.getPackageName(), getInstrumentation(), true);
+ mOriginalNotificationPolicy = mNm.getNotificationPolicy();
+ mOriginalZen = mNm.getCurrentInterruptionFilter();
+ } finally {
+ setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_ALL);
+ Utils.toggleNotificationPolicyAccess(
+ mContext.getPackageName(), getInstrumentation(), false);
}
+
// Check original mirchrophone mute/unmute status
mDoNotCheckUnmute = false;
if (mAudioManager.isMicrophoneMute()) {
@@ -159,9 +155,6 @@
@Override
protected void tearDown() throws Exception {
- if (!mSupportNotificationPolicyAccess) {
- return;
- }
try {
Utils.toggleNotificationPolicyAccess(
mContext.getPackageName(), getInstrumentation(), true);
@@ -303,9 +296,6 @@
}
public void testCheckingZenModeBlockDoesNotRequireNotificationPolicyAccess() throws Exception {
- if (!mSupportNotificationPolicyAccess) {
- return;
- }
try {
// set zen mode to priority only, so playSoundEffect will check notification policy
Utils.toggleNotificationPolicyAccess(mContext.getPackageName(), getInstrumentation(),
@@ -396,7 +386,7 @@
}
public void testVibrateNotification() throws Exception {
- if (mUseFixedVolume || !mHasVibrator || !mSupportNotificationPolicyAccess) {
+ if (mUseFixedVolume || !mHasVibrator) {
return;
}
Utils.toggleNotificationPolicyAccess(
@@ -459,7 +449,7 @@
}
public void testVibrateRinger() throws Exception {
- if (mUseFixedVolume || !mHasVibrator || !mSupportNotificationPolicyAccess) {
+ if (mUseFixedVolume || !mHasVibrator) {
return;
}
Utils.toggleNotificationPolicyAccess(
@@ -523,9 +513,6 @@
}
public void testAccessRingMode() throws Exception {
- if (!mSupportNotificationPolicyAccess) {
- return;
- }
Utils.toggleNotificationPolicyAccess(
mContext.getPackageName(), getInstrumentation(), true);
mAudioManager.setRingerMode(RINGER_MODE_NORMAL);
@@ -550,7 +537,7 @@
}
public void testSetRingerModePolicyAccess() throws Exception {
- if (mSkipRingerTests || !mSupportNotificationPolicyAccess) {
+ if (mSkipRingerTests) {
return;
}
// Apps without policy access cannot change silent -> normal or silent -> vibrate.
@@ -613,9 +600,6 @@
}
public void testVolume() throws Exception {
- if (!mSupportNotificationPolicyAccess) {
- return;
- }
Utils.toggleNotificationPolicyAccess(
mContext.getPackageName(), getInstrumentation(), true);
int volume, volumeDelta;
@@ -838,7 +822,7 @@
}
public void testMuteDndAffectedStreams() throws Exception {
- if (mSkipRingerTests || !mSupportNotificationPolicyAccess) {
+ if (mSkipRingerTests) {
return;
}
int[] streams = { AudioManager.STREAM_RING };
@@ -912,7 +896,7 @@
}
public void testMuteDndUnaffectedStreams() throws Exception {
- if (mSkipRingerTests || !mSupportNotificationPolicyAccess) {
+ if (mSkipRingerTests) {
return;
}
int[] streams = {
@@ -1006,7 +990,7 @@
}
public void testAdjustVolumeInTotalSilenceMode() throws Exception {
- if (mSkipRingerTests || !mSupportNotificationPolicyAccess) {
+ if (mSkipRingerTests) {
return;
}
try {
@@ -1027,7 +1011,7 @@
}
public void testAdjustVolumeInAlarmsOnlyMode() throws Exception {
- if (mSkipRingerTests || !mSupportNotificationPolicyAccess) {
+ if (mSkipRingerTests) {
return;
}
try {
@@ -1052,7 +1036,7 @@
}
public void testSetStreamVolumeInTotalSilenceMode() throws Exception {
- if (mSkipRingerTests || !mSupportNotificationPolicyAccess) {
+ if (mSkipRingerTests) {
return;
}
try {
@@ -1076,7 +1060,7 @@
}
public void testSetStreamVolumeInAlarmsOnlyMode() throws Exception {
- if (mSkipRingerTests || !mSupportNotificationPolicyAccess) {
+ if (mSkipRingerTests) {
return;
}
try {
@@ -1098,7 +1082,7 @@
}
public void testSetStreamVolumeInPriorityOnlyMode() throws Exception {
- if (mSkipRingerTests || !mSupportNotificationPolicyAccess) {
+ if (mSkipRingerTests) {
return;
}
Utils.toggleNotificationPolicyAccess(
@@ -1136,7 +1120,7 @@
}
public void testAdjustVolumeInPriorityOnly() throws Exception {
- if (mSkipRingerTests || !mSupportNotificationPolicyAccess) {
+ if (mSkipRingerTests) {
return;
}
@@ -1181,7 +1165,7 @@
}
public void testPriorityOnlyMuteAll() throws Exception {
- if (mSkipRingerTests || !mSupportNotificationPolicyAccess) {
+ if (mSkipRingerTests) {
return;
}
@@ -1216,7 +1200,7 @@
}
public void testPriorityOnlyMediaAllowed() throws Exception {
- if (mSkipRingerTests || !mSupportNotificationPolicyAccess) {
+ if (mSkipRingerTests) {
return;
}
Utils.toggleNotificationPolicyAccess(
@@ -1251,7 +1235,7 @@
}
public void testPriorityOnlySystemAllowed() throws Exception {
- if (mSkipRingerTests || !mSupportNotificationPolicyAccess) {
+ if (mSkipRingerTests) {
return;
}
@@ -1285,7 +1269,7 @@
}
public void testPriorityOnlySystemDisallowedWithRingerMuted() throws Exception {
- if (mSkipRingerTests || !mSupportNotificationPolicyAccess) {
+ if (mSkipRingerTests) {
return;
}
@@ -1322,7 +1306,7 @@
}
public void testPriorityOnlyAlarmsAllowed() throws Exception {
- if (mSkipRingerTests || !mSupportNotificationPolicyAccess) {
+ if (mSkipRingerTests) {
return;
}
@@ -1358,7 +1342,7 @@
}
public void testPriorityOnlyRingerAllowed() throws Exception {
- if (mSkipRingerTests || !mSupportNotificationPolicyAccess) {
+ if (mSkipRingerTests) {
return;
}
@@ -1394,7 +1378,7 @@
public void testPriorityOnlyChannelsCanBypassDnd() throws Exception {
final String NOTIFICATION_CHANNEL_ID = "test_id";
- if (mSkipRingerTests || !mSupportNotificationPolicyAccess) {
+ if (mSkipRingerTests) {
return;
}
diff --git a/tests/tests/media/src/android/media/cts/AudioPlaybackCaptureTest.java b/tests/tests/media/src/android/media/cts/AudioPlaybackCaptureTest.java
index ee23442..7b00c9a 100644
--- a/tests/tests/media/src/android/media/cts/AudioPlaybackCaptureTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioPlaybackCaptureTest.java
@@ -42,7 +42,6 @@
import android.media.projection.MediaProjection;
import android.os.Handler;
import android.os.Looper;
-import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
import androidx.test.rule.ActivityTestRule;
@@ -78,7 +77,6 @@
private AudioManager mAudioManager;
private boolean mPlaybackBeforeCapture;
private int mUid; //< UID of this test
- private int mUserId; // userId of this test
private AudioPlaybackCaptureActivity mActivity;
private MediaProjection mMediaProjection;
@Rule
@@ -90,8 +88,6 @@
public @AttributeUsage int[] excludeUsages;
public int[] matchingUids;
public int[] excludeUids;
- public int[] matchingUserIds;
- public int[] excludeUserIds;
private AudioPlaybackCaptureConfiguration build(MediaProjection projection)
throws Exception {
AudioPlaybackCaptureConfiguration.Builder apccBuilder =
@@ -117,16 +113,6 @@
apccBuilder.excludeUid(uid);
}
}
- if (matchingUserIds != null) {
- for (int userId : matchingUserIds) {
- apccBuilder.addMatchingUserId(userId);
- }
- }
- if (excludeUserIds != null) {
- for (int userId : excludeUserIds) {
- apccBuilder.excludeUserId(userId);
- }
- }
AudioPlaybackCaptureConfiguration config = apccBuilder.build();
assertCorreclyBuilt(config);
return config;
@@ -137,8 +123,6 @@
assertEqualNullIsEmpty("excludeUsages", excludeUsages, config.getExcludeUsages());
assertEqualNullIsEmpty("matchingUids", matchingUids, config.getMatchingUids());
assertEqualNullIsEmpty("excludeUids", excludeUids, config.getExcludeUids());
- assertEqualNullIsEmpty("matchingUserIds", matchingUserIds, config.getMatchingUserIds());
- assertEqualNullIsEmpty("excludeUserIds", excludeUserIds, config.getExcludeUserIds());
}
private void assertEqualNullIsEmpty(String msg, int[] expected, int[] found) {
@@ -158,8 +142,6 @@
mActivity = mActivityRule.getActivity();
mAudioManager = mActivity.getSystemService(AudioManager.class);
mUid = mActivity.getApplicationInfo().uid;
- mUserId = UserHandle.getUserHandleForUid(mActivity.getApplicationInfo().uid)
- .getIdentifier();
mMediaProjection = mActivity.waitForMediaProjection();
}
@@ -339,13 +321,6 @@
testPlaybackCapture(OPT_IN, AudioAttributes.USAGE_UNKNOWN, EXPECT_SILENCE);
}
- @Test(expected = IllegalStateException.class)
- public void testCombineUserId() throws Exception {
- mAPCTestConfig.matchingUserIds = new int[]{ mUserId };
- mAPCTestConfig.excludeUserIds = new int[]{ mUserId + 1 };
- testPlaybackCapture(OPT_IN, AudioAttributes.USAGE_UNKNOWN, EXPECT_SILENCE);
- }
-
@Test
public void testCaptureMatchingAllowedUsage() throws Exception {
for (int usage : ALLOWED_USAGES) {
@@ -396,17 +371,6 @@
}
@Test
- public void testCaptureMatchingUserId() throws Exception {
- mAPCTestConfig.matchingUserIds = new int[]{ mUserId };
- testPlaybackCapture(OPT_IN, AudioAttributes.USAGE_GAME, EXPECT_DATA);
- testPlaybackCapture(OPT_OUT, AudioAttributes.USAGE_GAME, EXPECT_SILENCE);
- testPlaybackCapture(OPT_IN, AudioAttributes.USAGE_VOICE_COMMUNICATION, EXPECT_SILENCE);
-
- mAPCTestConfig.matchingUserIds = new int[]{ mUserId + 1 };
- testPlaybackCapture(OPT_IN, AudioAttributes.USAGE_GAME, EXPECT_SILENCE);
- }
-
- @Test
public void testCaptureExcludeUid() throws Exception {
mAPCTestConfig.excludeUids = new int[]{ 0 };
testPlaybackCapture(OPT_IN, AudioAttributes.USAGE_GAME, EXPECT_DATA);
@@ -417,17 +381,6 @@
testPlaybackCapture(OPT_IN, AudioAttributes.USAGE_GAME, EXPECT_SILENCE);
}
- @Test
- public void testCaptureExcludeUserId() throws Exception {
- mAPCTestConfig.excludeUserIds = new int[]{ mUserId + 1 };
- testPlaybackCapture(OPT_IN, AudioAttributes.USAGE_GAME, EXPECT_DATA);
- testPlaybackCapture(OPT_OUT, AudioAttributes.USAGE_UNKNOWN, EXPECT_SILENCE);
- testPlaybackCapture(OPT_IN, AudioAttributes.USAGE_VOICE_COMMUNICATION, EXPECT_SILENCE);
-
- mAPCTestConfig.excludeUserIds = new int[]{ mUserId };
- testPlaybackCapture(OPT_IN, AudioAttributes.USAGE_GAME, EXPECT_SILENCE);
- }
-
@Test(expected = UnsupportedOperationException.class)
public void testStoppedMediaProjection() throws Exception {
mMediaProjection.stop();
diff --git a/tests/tests/media/src/android/media/cts/CodecState.java b/tests/tests/media/src/android/media/cts/CodecState.java
index 5f1d322..2384704 100644
--- a/tests/tests/media/src/android/media/cts/CodecState.java
+++ b/tests/tests/media/src/android/media/cts/CodecState.java
@@ -19,6 +19,7 @@
import android.media.MediaCodec;
import android.media.MediaExtractor;
import android.media.MediaFormat;
+import android.os.Handler;
import android.util.Log;
import android.view.Surface;
import java.nio.ByteBuffer;
@@ -50,6 +51,7 @@
private MediaFormat mFormat;
private MediaFormat mOutputFormat;
private NonBlockingAudioTrack mAudioTrack;
+ private OnFrameRenderedListener mOnFrameRenderedListener;
/**
* Manages audio and video playback using MediaCodec and AudioTrack.
@@ -84,6 +86,11 @@
String mime = mFormat.getString(MediaFormat.KEY_MIME);
Log.d(TAG, "CodecState::CodecState " + mime);
mIsAudio = mime.startsWith("audio/");
+
+ if (mTunneled && !mIsAudio) {
+ mOnFrameRenderedListener = new OnFrameRenderedListener();
+ codec.setOnFrameRenderedListener(mOnFrameRenderedListener, new Handler());
+ }
}
public void release() {
@@ -100,6 +107,11 @@
mAvailableOutputBufferIndices = null;
mAvailableOutputBufferInfos = null;
+ if (mOnFrameRenderedListener != null) {
+ mCodec.setOnFrameRenderedListener(null, null);
+ mOnFrameRenderedListener = null;
+ }
+
mCodec.release();
mCodec = null;
@@ -218,11 +230,6 @@
Log.d(TAG, "sampleSize: " + sampleSize + " trackIndex:" + trackIndex +
" sampleTime:" + sampleTime + " sampleFlags:" + sampleFlags);
mSawInputEOS = true;
- // FIX-ME: in tunneled mode we currently use input EOS as output EOS indicator
- // we should use stream duration
- if (mTunneled && !mIsAudio) {
- mSawOutputEOS = true;
- }
return false;
}
@@ -231,9 +238,6 @@
mSampleBaseTimeUs = sampleTime;
}
sampleTime -= mSampleBaseTimeUs;
- // FIX-ME: in tunneled mode we currently use input buffer time
- // as video presentation time. This is not accurate and should be fixed
- mPresentationTimeUs = sampleTime;
}
if ((sampleFlags & MediaExtractor.SAMPLE_FLAG_ENCRYPTED) != 0) {
@@ -255,11 +259,6 @@
Log.d(TAG, "saw input EOS on track " + mTrackIndex);
mSawInputEOS = true;
- // FIX-ME: in tunneled mode we currently use input EOS as output EOS indicator
- // we should use stream duration
- if (mTunneled && !mIsAudio) {
- mSawOutputEOS = true;
- }
mCodec.queueInputBuffer(
index, 0 /* offset */, 0 /* sampleSize */,
@@ -375,6 +374,23 @@
}
}
+ /** Callback called by the renderer in tunneling mode. */
+ private class OnFrameRenderedListener implements MediaCodec.OnFrameRenderedListener {
+ private static final long TUNNELING_EOS_PRESENTATION_TIME_US = Long.MAX_VALUE;
+
+ @Override
+ public void onFrameRendered(MediaCodec codec, long presentationTimeUs, long nanoTime) {
+ if (this != mOnFrameRenderedListener) {
+ return; // stale event
+ }
+ if (presentationTimeUs == TUNNELING_EOS_PRESENTATION_TIME_US) {
+ mSawOutputEOS = true;
+ } else {
+ mPresentationTimeUs = presentationTimeUs;
+ }
+ }
+ }
+
public long getAudioTimeUs() {
if (mAudioTrack == null) {
return 0;
diff --git a/tests/tests/media/src/android/media/cts/MediaCasTest.java b/tests/tests/media/src/android/media/cts/MediaCasTest.java
index f5e5027..27a8879 100644
--- a/tests/tests/media/src/android/media/cts/MediaCasTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaCasTest.java
@@ -252,7 +252,7 @@
MediaDescrambler descrambler = null;
try {
- mediaCas = new MediaCas(sClearKeySystemId, "TIS_Session_1",
+ mediaCas = new MediaCas(getContext(), sClearKeySystemId, "TIS_Session_1",
android.media.tv.TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE);
descrambler = new MediaDescrambler(sClearKeySystemId);
@@ -266,9 +266,7 @@
fail("Can't open session for program");
}
- if (!PropertyUtil.isVendorApiLevelNewerThan(API_LEVEL_BEFORE_CAS_SESSION + 1)) {
- Log.d(TAG, "Session Id = " + Arrays.toString(session.getSessionId()));
- }
+ Log.d(TAG, "Session Id = " + Arrays.toString(session.getSessionId()));
session.setPrivateData(pvtData);
diff --git a/tests/tests/media/src/android/media/cts/MediaCodecBlockModelTest.java b/tests/tests/media/src/android/media/cts/MediaCodecBlockModelTest.java
index 27f9ddc..7123d9c 100644
--- a/tests/tests/media/src/android/media/cts/MediaCodecBlockModelTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaCodecBlockModelTest.java
@@ -173,12 +173,16 @@
mExtractor.advance();
mSignaledEos = mExtractor.getSampleTrackIndex() == -1
|| timestampUs >= mLastBufferTimestampUs;
- codec.getQueueRequest(index).setLinearBlock(
- input.block,
- input.offset,
- written,
- timestampUs,
- mSignaledEos ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0).queue();
+ codec.getQueueRequest(index)
+ .setLinearBlock(
+ input.block,
+ input.offset,
+ written,
+ null /* cryptoInfo */)
+ .setPresentationTimeUs(timestampUs)
+ .setFlags(
+ mSignaledEos ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0)
+ .queue();
input.offset += written;
}
diff --git a/tests/tests/media/src/android/media/cts/MediaCodecTest.java b/tests/tests/media/src/android/media/cts/MediaCodecTest.java
index 90125d0..193345a 100644
--- a/tests/tests/media/src/android/media/cts/MediaCodecTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaCodecTest.java
@@ -2532,4 +2532,104 @@
}
}
}
+
+ /**
+ * Test if flushing early in the playback does not prevent client from getting the
+ * latest configuration. Empirically, this happens most often when the
+ * codec is flushed after the first buffer is queued, so this test walks
+ * through the scenario.
+ */
+ public void testFlushAfterFirstBuffer() throws Exception {
+ for (int i = 0; i < 100; ++i) {
+ doFlushAfterFirstBuffer();
+ }
+ }
+
+ private void doFlushAfterFirstBuffer() throws Exception {
+ MediaExtractor extractor = null;
+ MediaCodec codec = null;
+
+ try {
+ MediaFormat newFormat = null;
+ extractor = getMediaExtractorForMimeType(
+ R.raw.noise_2ch_48khz_aot29_dr_sbr_sig2_mp4, "audio/");
+ int trackIndex = extractor.getSampleTrackIndex();
+ MediaFormat format = extractor.getTrackFormat(trackIndex);
+ codec = createCodecByType(
+ format.getString(MediaFormat.KEY_MIME), false /* isEncoder */);
+ codec.configure(format, null, null, 0);
+ codec.start();
+ int firstInputIndex = codec.dequeueInputBuffer(0);
+ while (firstInputIndex == MediaCodec.INFO_TRY_AGAIN_LATER) {
+ firstInputIndex = codec.dequeueInputBuffer(5000);
+ }
+ assertTrue(firstInputIndex >= 0);
+ extractor.readSampleData(codec.getInputBuffer(firstInputIndex), 0);
+ codec.queueInputBuffer(
+ firstInputIndex, 0, Math.toIntExact(extractor.getSampleSize()),
+ extractor.getSampleTime(), extractor.getSampleFlags());
+ // Don't advance, so the first buffer will be read again after flush
+ codec.flush();
+ ByteBuffer csd = format.getByteBuffer("csd-0");
+ MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
+ // We don't need to decode many frames
+ int numFrames = 10;
+ boolean eos = false;
+ while (!eos) {
+ if (numFrames > 0) {
+ int inputIndex = codec.dequeueInputBuffer(0);
+ if (inputIndex == MediaCodec.INFO_TRY_AGAIN_LATER) {
+ inputIndex = codec.dequeueInputBuffer(5000);
+ }
+ if (inputIndex >= 0) {
+ ByteBuffer inputBuffer = codec.getInputBuffer(inputIndex);
+ if (csd != null) {
+ inputBuffer.clear();
+ inputBuffer.put(csd);
+ codec.queueInputBuffer(
+ inputIndex, 0, inputBuffer.position(), 0,
+ MediaCodec.BUFFER_FLAG_CODEC_CONFIG);
+ csd = null;
+ } else {
+ int size = extractor.readSampleData(inputBuffer, 0);
+ if (size <= 0) {
+ break;
+ }
+ int flags = extractor.getSampleFlags();
+ --numFrames;
+ if (numFrames <= 0) {
+ flags |= MediaCodec.BUFFER_FLAG_END_OF_STREAM;
+ }
+ codec.queueInputBuffer(
+ inputIndex, 0, size, extractor.getSampleTime(), flags);
+ extractor.advance();
+ }
+ }
+ }
+
+ int outputIndex = codec.dequeueOutputBuffer(bufferInfo, 0);
+ if (outputIndex == MediaCodec.INFO_TRY_AGAIN_LATER) {
+ outputIndex = codec.dequeueOutputBuffer(bufferInfo, 5000);
+ }
+ if (outputIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
+ newFormat = codec.getOutputFormat();
+ } else if (outputIndex >= 0) {
+ if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+ eos = true;
+ }
+ codec.releaseOutputBuffer(outputIndex, false);
+ }
+ }
+ assertNotNull(newFormat);
+ assertEquals(48000, newFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE));
+ } finally {
+ if (extractor != null) {
+ extractor.release();
+ }
+ if (codec != null) {
+ codec.stop();
+ codec.release();
+ }
+ }
+ }
}
diff --git a/tests/tests/media/src/android/media/cts/MediaMetadataRetrieverTest.java b/tests/tests/media/src/android/media/cts/MediaMetadataRetrieverTest.java
index 05a3970..e5bf714 100644
--- a/tests/tests/media/src/android/media/cts/MediaMetadataRetrieverTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaMetadataRetrieverTest.java
@@ -45,11 +45,14 @@
import androidx.test.filters.SmallTest;
import com.android.compatibility.common.util.MediaUtils;
+import java.io.Closeable;
import java.io.File;
+import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
+import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -62,6 +65,7 @@
public class MediaMetadataRetrieverTest extends AndroidTestCase {
private static final String TAG = "MediaMetadataRetrieverTest";
private static final boolean SAVE_BITMAP_OUTPUT = false;
+ private static final String TEST_MEDIA_FILE = "retriever_test.3gp";
protected Resources mResources;
protected MediaMetadataRetriever mRetriever;
@@ -93,6 +97,10 @@
protected void tearDown() throws Exception {
super.tearDown();
mRetriever.release();
+ File file = new File(Environment.getExternalStorageDirectory(), TEST_MEDIA_FILE);
+ if (file.exists()) {
+ file.delete();
+ }
}
protected void setDataSourceFd(int resid) {
@@ -368,27 +376,23 @@
}
public void testSetDataSourcePath() {
- File outputFile = new File(Environment.getExternalStorageDirectory(), "retriever_test.3gp");
+ copyMeidaFile();
+ File file = new File(Environment.getExternalStorageDirectory(), TEST_MEDIA_FILE);
try {
- recordMedia(outputFile);
- mRetriever.setDataSource(outputFile.getAbsolutePath());
+ mRetriever.setDataSource(file.getAbsolutePath());
} catch (Exception ex) {
fail("Failed setting data source with path, caught exception:" + ex);
- } finally {
- outputFile.delete();
}
}
public void testSetDataSourceUri() {
- File outputFile = new File(Environment.getExternalStorageDirectory(), "retriever_test.3gp");
+ copyMeidaFile();
+ File file = new File(Environment.getExternalStorageDirectory(), TEST_MEDIA_FILE);
try {
- recordMedia(outputFile);
- Uri uri = Uri.parse(outputFile.getAbsolutePath());
+ Uri uri = Uri.parse(file.getAbsolutePath());
mRetriever.setDataSource(getContext(), uri);
} catch (Exception ex) {
fail("Failed setting data source with Uri, caught exception:" + ex);
- } finally {
- outputFile.delete();
}
}
@@ -1005,20 +1009,42 @@
}
}
- private void recordMedia(File outputFile) throws Exception {
- MediaRecorder mr = new MediaRecorder();
+ private void copyMeidaFile() {
+ InputStream inputStream = null;
+ FileOutputStream outputStream = null;
+ String outputPath = new File(
+ Environment.getExternalStorageDirectory(), TEST_MEDIA_FILE).getAbsolutePath();
try {
- mr.setAudioSource(MediaRecorder.AudioSource.MIC);
- mr.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
- mr.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
- mr.setOutputFile(outputFile.getAbsolutePath());
+ inputStream = getContext().getResources().openRawResource(R.raw.testvideo);
+ outputStream = new FileOutputStream(outputPath);
+ copy(inputStream, outputStream);
+ } catch (Exception e) {
- mr.prepare();
- mr.start();
- Thread.sleep(SLEEP_TIME);
- mr.stop();
- } finally {
- mr.release();
+ }finally {
+ closeQuietly(inputStream);
+ closeQuietly(outputStream);
+ }
+ }
+
+ private int copy(InputStream in, OutputStream out) throws IOException {
+ int total = 0;
+ byte[] buffer = new byte[8192];
+ int c;
+ while ((c = in.read(buffer)) != -1) {
+ total += c;
+ out.write(buffer, 0, c);
+ }
+ return total;
+ }
+
+ private void closeQuietly(Closeable closeable) {
+ if (closeable != null) {
+ try {
+ closeable.close();
+ } catch (RuntimeException rethrown) {
+ throw rethrown;
+ } catch (Exception ignored) {
+ }
}
}
diff --git a/tests/tests/media/src/android/media/cts/MediaPlayerTest.java b/tests/tests/media/src/android/media/cts/MediaPlayerTest.java
index b9d4873..3e547bb 100644
--- a/tests/tests/media/src/android/media/cts/MediaPlayerTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaPlayerTest.java
@@ -15,7 +15,6 @@
*/
package android.media.cts;
-import android.app.ActivityManager;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.AssetFileDescriptor;
@@ -827,8 +826,7 @@
int oldRingerMode = Integer.MIN_VALUE;
int oldVolume = Integer.MIN_VALUE;
try {
- if (am.getRingerMode() != AudioManager.RINGER_MODE_NORMAL
- && !ActivityManager.isLowRamDeviceStatic()) {
+ if (am.getRingerMode() != AudioManager.RINGER_MODE_NORMAL) {
Utils.toggleNotificationPolicyAccess(
mContext.getPackageName(), getInstrumentation(), true /* on */);
}
@@ -932,10 +930,8 @@
if (oldVolume != Integer.MIN_VALUE) {
am.setStreamVolume(AudioManager.STREAM_MUSIC, oldVolume, 0);
}
- if (!ActivityManager.isLowRamDeviceStatic()) {
- Utils.toggleNotificationPolicyAccess(
- mContext.getPackageName(), getInstrumentation(), false /* on == false */);
- }
+ Utils.toggleNotificationPolicyAccess(
+ mContext.getPackageName(), getInstrumentation(), false /* on == false */);
}
}
diff --git a/tests/tests/media/src/android/media/cts/MediaRoute2ProviderServiceTest.java b/tests/tests/media/src/android/media/cts/MediaRoute2ProviderServiceTest.java
index 12332bc..773aede 100644
--- a/tests/tests/media/src/android/media/cts/MediaRoute2ProviderServiceTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaRoute2ProviderServiceTest.java
@@ -17,12 +17,12 @@
package android.media.cts;
import static android.media.cts.MediaRouter2Test.releaseControllers;
-import static android.media.cts.SampleMediaRoute2ProviderService.FEATURE_SAMPLE;
-import static android.media.cts.SampleMediaRoute2ProviderService.FEATURE_SPECIAL;
-import static android.media.cts.SampleMediaRoute2ProviderService.ROUTE_ID1;
-import static android.media.cts.SampleMediaRoute2ProviderService.ROUTE_ID2;
-import static android.media.cts.SampleMediaRoute2ProviderService.ROUTE_ID4_TO_SELECT_AND_DESELECT;
-import static android.media.cts.SampleMediaRoute2ProviderService.ROUTE_ID5_TO_TRANSFER_TO;
+import static android.media.cts.StubMediaRoute2ProviderService.FEATURE_SAMPLE;
+import static android.media.cts.StubMediaRoute2ProviderService.FEATURE_SPECIAL;
+import static android.media.cts.StubMediaRoute2ProviderService.ROUTE_ID1;
+import static android.media.cts.StubMediaRoute2ProviderService.ROUTE_ID2;
+import static android.media.cts.StubMediaRoute2ProviderService.ROUTE_ID4_TO_SELECT_AND_DESELECT;
+import static android.media.cts.StubMediaRoute2ProviderService.ROUTE_ID5_TO_TRANSFER_TO;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -34,12 +34,13 @@
import android.media.MediaRoute2Info;
import android.media.MediaRoute2ProviderService;
import android.media.MediaRouter2;
+import android.media.MediaRouter2.ControllerCallback;
import android.media.MediaRouter2.RouteCallback;
import android.media.MediaRouter2.RoutingController;
-import android.media.MediaRouter2.RoutingControllerCallback;
+import android.media.MediaRouter2.TransferCallback;
import android.media.RouteDiscoveryPreference;
import android.media.RoutingSessionInfo;
-import android.media.cts.SampleMediaRoute2ProviderService.Proxy;
+import android.media.cts.StubMediaRoute2ProviderService.Proxy;
import android.os.Bundle;
import android.platform.test.annotations.AppModeFull;
import android.platform.test.annotations.LargeTest;
@@ -47,6 +48,8 @@
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
+import com.android.compatibility.common.util.PollingCheck;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -63,14 +66,14 @@
import java.util.concurrent.TimeUnit;
@RunWith(AndroidJUnit4.class)
-@AppModeFull(reason = "The system should be able to bind to SampleMediaRoute2ProviderService")
+@AppModeFull(reason = "The system should be able to bind to StubMediaRoute2ProviderService")
@LargeTest
public class MediaRoute2ProviderServiceTest {
private static final String TAG = "MR2ProviderServiceTest";
Context mContext;
private MediaRouter2 mRouter2;
private Executor mExecutor;
- private SampleMediaRoute2ProviderService mServiceInstance;
+ private StubMediaRoute2ProviderService mService;
private static final int TIMEOUT_MS = 5000;
@@ -86,61 +89,68 @@
mContext = InstrumentationRegistry.getTargetContext();
mRouter2 = MediaRouter2.getInstance(mContext);
mExecutor = Executors.newSingleThreadExecutor();
- mServiceInstance = SampleMediaRoute2ProviderService.getInstance();
+
+ new PollingCheck(TIMEOUT_MS) {
+ @Override
+ protected boolean check() {
+ StubMediaRoute2ProviderService service =
+ StubMediaRoute2ProviderService.getInstance();
+ if (service != null) {
+ mService = service;
+ return true;
+ }
+ return false;
+ }
+ }.run();
}
@After
public void tearDown() throws Exception {
- if (mServiceInstance != null) {
- mServiceInstance.clear();
- mServiceInstance = null;
+ if (mService != null) {
+ mService.clear();
+ mService = null;
}
}
@Test
public void testGetSessionInfoAndGetAllSessionInfo() {
- SampleMediaRoute2ProviderService service = mServiceInstance;
- assertNotNull(service);
- assertEquals(0, service.getAllSessionInfo().size());
+ assertEquals(0, mService.getAllSessionInfo().size());
// Add a session
RoutingSessionInfo sessionInfo1 = new RoutingSessionInfo.Builder(
SESSION_ID_1, "" /* clientPackageName */)
.addSelectedRoute(ROUTE_ID1)
.build();
- service.notifySessionCreated(sessionInfo1, MediaRoute2ProviderService.REQUEST_ID_UNKNOWN);
- assertEquals(1, service.getAllSessionInfo().size());
- assertEquals(sessionInfo1, service.getAllSessionInfo().get(0));
- assertEquals(sessionInfo1, service.getSessionInfo(SESSION_ID_1));
+ mService.notifySessionCreated(sessionInfo1, MediaRoute2ProviderService.REQUEST_ID_NONE);
+ assertEquals(1, mService.getAllSessionInfo().size());
+ assertEquals(sessionInfo1, mService.getAllSessionInfo().get(0));
+ assertEquals(sessionInfo1, mService.getSessionInfo(SESSION_ID_1));
// Add another session
RoutingSessionInfo sessionInfo2 = new RoutingSessionInfo.Builder(
SESSION_ID_2, "" /* clientPackageName */)
.addSelectedRoute(ROUTE_ID2)
.build();
- service.notifySessionCreated(
- sessionInfo2, MediaRoute2ProviderService.REQUEST_ID_UNKNOWN);
- assertEquals(2, service.getAllSessionInfo().size());
- assertEquals(sessionInfo2, service.getSessionInfo(SESSION_ID_2));
+ mService.notifySessionCreated(
+ sessionInfo2, MediaRoute2ProviderService.REQUEST_ID_NONE);
+ assertEquals(2, mService.getAllSessionInfo().size());
+ assertEquals(sessionInfo2, mService.getSessionInfo(SESSION_ID_2));
// Remove the first session
- service.notifySessionReleased(SESSION_ID_1);
- assertNull(service.getSessionInfo(SESSION_ID_1));
- assertEquals(1, service.getAllSessionInfo().size());
- assertEquals(sessionInfo2, service.getAllSessionInfo().get(0));
- assertEquals(sessionInfo2, service.getSessionInfo(SESSION_ID_2));
+ mService.notifySessionReleased(SESSION_ID_1);
+ assertNull(mService.getSessionInfo(SESSION_ID_1));
+ assertEquals(1, mService.getAllSessionInfo().size());
+ assertEquals(sessionInfo2, mService.getAllSessionInfo().get(0));
+ assertEquals(sessionInfo2, mService.getSessionInfo(SESSION_ID_2));
// Remove the remaining session
- service.notifySessionReleased(SESSION_ID_2);
- assertEquals(0, service.getAllSessionInfo().size());
- assertNull(service.getSessionInfo(SESSION_ID_2));
+ mService.notifySessionReleased(SESSION_ID_2);
+ assertEquals(0, mService.getAllSessionInfo().size());
+ assertNull(mService.getSessionInfo(SESSION_ID_2));
}
@Test
public void testNotifyRoutesInvokesMediaRouter2RouteCallback() throws Exception {
- SampleMediaRoute2ProviderService service = mServiceInstance;
- assertNotNull(service);
-
final String routeId0 = "routeId0";
final String routeName0 = "routeName0";
final String routeId1 = "routeId1";
@@ -235,7 +245,7 @@
mRouter2.registerRouteCallback(mExecutor, routeCallback,
new RouteDiscoveryPreference.Builder(features, true).build());
try {
- service.notifyRoutes(routes);
+ mService.notifyRoutes(routes);
assertTrue(onRoutesAddedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
// Change the connection state of route2 in order to invoke onRoutesChanged()
@@ -243,12 +253,12 @@
.setConnectionState(newConnectionState)
.build();
routes.set(1, newRoute2);
- service.notifyRoutes(routes);
+ mService.notifyRoutes(routes);
assertTrue(onRoutesChangedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
// Now remove all the routes
routes.clear();
- service.notifyRoutes(routes);
+ mService.notifyRoutes(routes);
assertTrue(onRoutesRemovedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
} finally {
mRouter2.unregisterRouteCallback(routeCallback);
@@ -257,10 +267,8 @@
@Test
public void testSessionRelatedCallbacks() throws Exception {
- SampleMediaRoute2ProviderService service = mServiceInstance;
- assertNotNull(service);
- service.initializeRoutes();
- service.publishRoutes();
+ mService.initializeRoutes();
+ mService.publishRoutes();
List<String> featuresSample = Collections.singletonList(FEATURE_SAMPLE);
Map<String, MediaRoute2Info> routes = waitAndGetRoutes(featuresSample);
@@ -280,7 +288,7 @@
// Now test all session-related callbacks.
setProxy(new Proxy() {
@Override
- public void onCreateSession(String packageName, String routeId, long requestId,
+ public void onCreateSession(long requestId, String packageName, String routeId,
Bundle sessionHints) {
assertEquals(mContext.getPackageName(), packageName);
assertEquals(ROUTE_ID1, routeId);
@@ -294,88 +302,89 @@
.addSelectableRoute(ROUTE_ID4_TO_SELECT_AND_DESELECT)
.addTransferableRoute(ROUTE_ID5_TO_TRANSFER_TO)
.build();
- service.notifySessionCreated(info, requestId);
+ mService.notifySessionCreated(info, requestId);
onCreateSessionLatch.countDown();
}
@Override
- public void onSelectRoute(String sessionId, String routeId) {
+ public void onSelectRoute(long requestId, String sessionId, String routeId) {
assertEquals(SESSION_ID_1, sessionId);
assertEquals(ROUTE_ID4_TO_SELECT_AND_DESELECT, routeId);
- RoutingSessionInfo oldInfo = service.getSessionInfo(SESSION_ID_1);
+ RoutingSessionInfo oldInfo = mService.getSessionInfo(SESSION_ID_1);
RoutingSessionInfo newInfo = new RoutingSessionInfo.Builder(oldInfo)
.addSelectedRoute(ROUTE_ID4_TO_SELECT_AND_DESELECT)
.removeSelectableRoute(ROUTE_ID4_TO_SELECT_AND_DESELECT)
.addDeselectableRoute(ROUTE_ID4_TO_SELECT_AND_DESELECT)
.build();
- service.notifySessionUpdated(newInfo);
+ mService.notifySessionUpdated(newInfo);
onSelectRouteLatch.countDown();
}
@Override
- public void onDeselectRoute(String sessionId, String routeId) {
+ public void onDeselectRoute(long requestId, String sessionId, String routeId) {
assertEquals(SESSION_ID_1, sessionId);
assertEquals(ROUTE_ID4_TO_SELECT_AND_DESELECT, routeId);
- RoutingSessionInfo oldInfo = service.getSessionInfo(SESSION_ID_1);
+ RoutingSessionInfo oldInfo = mService.getSessionInfo(SESSION_ID_1);
RoutingSessionInfo newInfo = new RoutingSessionInfo.Builder(oldInfo)
.removeSelectedRoute(ROUTE_ID4_TO_SELECT_AND_DESELECT)
.addSelectableRoute(ROUTE_ID4_TO_SELECT_AND_DESELECT)
.removeDeselectableRoute(ROUTE_ID4_TO_SELECT_AND_DESELECT)
.build();
- service.notifySessionUpdated(newInfo);
+ mService.notifySessionUpdated(newInfo);
onDeselectRouteLatch.countDown();
}
@Override
- public void onTransferToRoute(String sessionId, String routeId) {
+ public void onTransferToRoute(long requestId, String sessionId, String routeId) {
assertEquals(SESSION_ID_1, sessionId);
assertEquals(ROUTE_ID5_TO_TRANSFER_TO, routeId);
- RoutingSessionInfo oldInfo = service.getSessionInfo(SESSION_ID_1);
+ RoutingSessionInfo oldInfo = mService.getSessionInfo(SESSION_ID_1);
RoutingSessionInfo newInfo = new RoutingSessionInfo.Builder(oldInfo)
.clearDeselectableRoutes()
.clearSelectedRoutes()
.clearDeselectableRoutes()
.addSelectedRoute(ROUTE_ID5_TO_TRANSFER_TO)
.build();
- service.notifySessionUpdated(newInfo);
+ mService.notifySessionUpdated(newInfo);
onTransferToRouteLatch.countDown();
}
@Override
- public void onReleaseSession(String sessionId) {
+ public void onReleaseSession(long requestId, String sessionId) {
assertEquals(SESSION_ID_1, sessionId);
- service.notifySessionReleased(sessionId);
+ mService.notifySessionReleased(sessionId);
onReleaseSessionLatch.countDown();
}
});
-
CountDownLatch onControllerCreatedLatch = new CountDownLatch(1);
CountDownLatch onControllerUpdatedForSelectLatch = new CountDownLatch(1);
CountDownLatch onControllerUpdatedForDeselectLatch = new CountDownLatch(1);
CountDownLatch onControllerUpdatedForTransferLatch = new CountDownLatch(1);
List<RoutingController> controllers = new ArrayList<>();
- RoutingControllerCallback controllerCallback = new RoutingControllerCallback() {
+ TransferCallback transferCallback = new TransferCallback() {
@Override
- public void onControllerCreated(RoutingController controller) {
- // TODO: Make RoutingController#getOriginalId() as @TestApi and use it.
- if (ROUTE_ID1.equals(controller.getSelectedRoutes().get(0).getOriginalId())) {
- controllers.add(controller);
+ public void onTransferred(RoutingController oldController,
+ RoutingController newController) {
+ if (newController != null && SESSION_ID_1.equals(newController.getOriginalId())) {
+ controllers.add(newController);
onControllerCreatedLatch.countDown();
}
}
+ };
+ ControllerCallback controllerCallback = new ControllerCallback() {
@Override
public void onControllerUpdated(RoutingController controller) {
List<MediaRoute2Info> selectedRoutes = controller.getSelectedRoutes();
if (onControllerUpdatedForSelectLatch.getCount() > 0) {
if (selectedRoutes.size() == 2
&& ROUTE_ID4_TO_SELECT_AND_DESELECT.equals(
- selectedRoutes.get(1).getOriginalId())) {
+ selectedRoutes.get(1).getOriginalId())) {
onControllerUpdatedForSelectLatch.countDown();
}
} else if (onControllerUpdatedForDeselectLatch.getCount() > 0) {
@@ -386,21 +395,22 @@
} else if (onControllerUpdatedForTransferLatch.getCount() > 0) {
if (selectedRoutes.size() == 1
&& ROUTE_ID5_TO_TRANSFER_TO.equals(
- selectedRoutes.get(0).getOriginalId())) {
+ selectedRoutes.get(0).getOriginalId())) {
onControllerUpdatedForTransferLatch.countDown();
}
}
}
};
- // TODO: Remove this once the MediaRouter2 becomes always connected to the service.
+ // TODO: Remove this once the MediaRouter2 becomes always connected to the mService.
RouteCallback dummyCallback = new RouteCallback();
try {
mRouter2.registerRouteCallback(mExecutor, dummyCallback,
new RouteDiscoveryPreference.Builder(new ArrayList<>(), true).build());
+ mRouter2.registerTransferCallback(mExecutor, transferCallback);
mRouter2.registerControllerCallback(mExecutor, controllerCallback);
- mRouter2.requestCreateController(routeToCreateSession);
+ mRouter2.transferTo(routeToCreateSession);
assertTrue(onCreateSessionLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
assertTrue(onControllerCreatedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
assertFalse(controllers.isEmpty());
@@ -416,7 +426,7 @@
assertTrue(onControllerUpdatedForDeselectLatch.await(
TIMEOUT_MS, TimeUnit.MILLISECONDS));
- controller.transferToRoute(routes.get(ROUTE_ID5_TO_TRANSFER_TO));
+ mRouter2.transferTo(routes.get(ROUTE_ID5_TO_TRANSFER_TO));
assertTrue(onTransferToRouteLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
assertTrue(onControllerUpdatedForTransferLatch.await(
TIMEOUT_MS, TimeUnit.MILLISECONDS));
@@ -425,6 +435,7 @@
assertTrue(onReleaseSessionLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
} finally {
mRouter2.unregisterRouteCallback(dummyCallback);
+ mRouter2.unregisterTransferCallback(transferCallback);
mRouter2.unregisterControllerCallback(controllerCallback);
mRouter2.setOnGetControllerHintsListener(null);
releaseControllers(mRouter2.getControllers());
@@ -433,10 +444,8 @@
@Test
public void testNotifySessionReleased() throws Exception {
- SampleMediaRoute2ProviderService service = mServiceInstance;
- assertNotNull(service);
- service.initializeRoutes();
- service.publishRoutes();
+ mService.initializeRoutes();
+ mService.publishRoutes();
List<String> featuresSample = Collections.singletonList(FEATURE_SAMPLE);
Map<String, MediaRoute2Info> routes = waitAndGetRoutes(featuresSample);
@@ -446,7 +455,7 @@
CountDownLatch onCreateSessionLatch = new CountDownLatch(1);
setProxy(new Proxy() {
@Override
- public void onCreateSession(String packageName, String routeId, long requestId,
+ public void onCreateSession(long requestId, String packageName, String routeId,
Bundle sessionHints) {
assertEquals(mContext.getPackageName(), packageName);
assertEquals(ROUTE_ID1, routeId);
@@ -458,7 +467,7 @@
.addSelectableRoute(ROUTE_ID4_TO_SELECT_AND_DESELECT)
.addTransferableRoute(ROUTE_ID5_TO_TRANSFER_TO)
.build();
- service.notifySessionCreated(info, requestId);
+ mService.notifySessionCreated(info, requestId);
onCreateSessionLatch.countDown();
}
});
@@ -468,42 +477,42 @@
CountDownLatch onControllerReleasedLatch = new CountDownLatch(1);
List<RoutingController> controllers = new ArrayList<>();
- RoutingControllerCallback controllerCallback = new RoutingControllerCallback() {
+ TransferCallback transferCallback = new TransferCallback() {
@Override
- public void onControllerCreated(RoutingController controller) {
- // TODO: Make RoutingController#getOriginalId() as @TestApi and use it.
- if (ROUTE_ID1.equals(controller.getSelectedRoutes().get(0).getOriginalId())) {
- controllers.add(controller);
- onControllerCreatedLatch.countDown();
- }
- }
-
- @Override
- public void onControllerReleased(RoutingController controller) {
- if (ROUTE_ID1.equals(controller.getSelectedRoutes().get(0).getOriginalId())) {
- assertTrue(controller.isReleased());
- onControllerReleasedLatch.countDown();
+ public void onTransferred(RoutingController oldController,
+ RoutingController newController) {
+ if (newController != null) {
+ if (SESSION_ID_1.equals(newController.getOriginalId())) {
+ controllers.add(newController);
+ onControllerCreatedLatch.countDown();
+ }
+ } else {
+ // newController == null means that the oldController is released
+ if (SESSION_ID_1.equals(oldController.getOriginalId())) {
+ assertTrue(oldController.isReleased());
+ onControllerReleasedLatch.countDown();
+ }
}
}
};
- // TODO: Remove this once the MediaRouter2 becomes always connected to the service.
+ // TODO: Remove this once the MediaRouter2 becomes always connected to the mService.
RouteCallback dummyCallback = new RouteCallback();
try {
mRouter2.registerRouteCallback(mExecutor, dummyCallback,
new RouteDiscoveryPreference.Builder(new ArrayList<>(), true).build());
- mRouter2.registerControllerCallback(mExecutor, controllerCallback);
+ mRouter2.registerTransferCallback(mExecutor, transferCallback);
- mRouter2.requestCreateController(routeToCreateSession);
+ mRouter2.transferTo(routeToCreateSession);
assertTrue(onCreateSessionLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
assertTrue(onControllerCreatedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
assertFalse(controllers.isEmpty());
- service.notifySessionReleased(SESSION_ID_1);
+ mService.notifySessionReleased(SESSION_ID_1);
assertTrue(onControllerReleasedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
} finally {
mRouter2.unregisterRouteCallback(dummyCallback);
- mRouter2.unregisterControllerCallback(controllerCallback);
+ mRouter2.unregisterTransferCallback(transferCallback);
releaseControllers(mRouter2.getControllers());
}
}
@@ -548,10 +557,10 @@
}
}
- void setProxy(SampleMediaRoute2ProviderService.Proxy proxy) {
- SampleMediaRoute2ProviderService instance = SampleMediaRoute2ProviderService.getInstance();
- if (instance != null) {
- instance.setProxy(proxy);
+ void setProxy(StubMediaRoute2ProviderService.Proxy proxy) {
+ StubMediaRoute2ProviderService service = mService;
+ if (service != null) {
+ service.setProxy(proxy);
}
}
diff --git a/tests/tests/media/src/android/media/cts/MediaRouter2Test.java b/tests/tests/media/src/android/media/cts/MediaRouter2Test.java
index 95c5104..0c87a2a 100644
--- a/tests/tests/media/src/android/media/cts/MediaRouter2Test.java
+++ b/tests/tests/media/src/android/media/cts/MediaRouter2Test.java
@@ -18,14 +18,14 @@
import static android.content.Context.AUDIO_SERVICE;
import static android.media.MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE;
-import static android.media.cts.SampleMediaRoute2ProviderService.FEATURES_SPECIAL;
-import static android.media.cts.SampleMediaRoute2ProviderService.FEATURE_SAMPLE;
-import static android.media.cts.SampleMediaRoute2ProviderService.ROUTE_ID1;
-import static android.media.cts.SampleMediaRoute2ProviderService.ROUTE_ID2;
-import static android.media.cts.SampleMediaRoute2ProviderService.ROUTE_ID3_SESSION_CREATION_FAILED;
-import static android.media.cts.SampleMediaRoute2ProviderService.ROUTE_ID4_TO_SELECT_AND_DESELECT;
-import static android.media.cts.SampleMediaRoute2ProviderService.ROUTE_ID5_TO_TRANSFER_TO;
-import static android.media.cts.SampleMediaRoute2ProviderService.ROUTE_ID_SPECIAL_FEATURE;
+import static android.media.cts.StubMediaRoute2ProviderService.FEATURES_SPECIAL;
+import static android.media.cts.StubMediaRoute2ProviderService.FEATURE_SAMPLE;
+import static android.media.cts.StubMediaRoute2ProviderService.ROUTE_ID1;
+import static android.media.cts.StubMediaRoute2ProviderService.ROUTE_ID2;
+import static android.media.cts.StubMediaRoute2ProviderService.ROUTE_ID3_SESSION_CREATION_FAILED;
+import static android.media.cts.StubMediaRoute2ProviderService.ROUTE_ID4_TO_SELECT_AND_DESELECT;
+import static android.media.cts.StubMediaRoute2ProviderService.ROUTE_ID5_TO_TRANSFER_TO;
+import static android.media.cts.StubMediaRoute2ProviderService.ROUTE_ID_SPECIAL_FEATURE;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -40,10 +40,11 @@
import android.media.AudioManager;
import android.media.MediaRoute2Info;
import android.media.MediaRouter2;
+import android.media.MediaRouter2.ControllerCallback;
import android.media.MediaRouter2.OnGetControllerHintsListener;
import android.media.MediaRouter2.RouteCallback;
import android.media.MediaRouter2.RoutingController;
-import android.media.MediaRouter2.RoutingControllerCallback;
+import android.media.MediaRouter2.TransferCallback;
import android.media.RouteDiscoveryPreference;
import android.os.Bundle;
import android.platform.test.annotations.AppModeFull;
@@ -53,6 +54,8 @@
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
+import com.android.compatibility.common.util.PollingCheck;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -67,10 +70,9 @@
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
-import java.util.stream.Collectors;
@RunWith(AndroidJUnit4.class)
-@AppModeFull(reason = "The system should be able to bind to SampleMediaRoute2ProviderService")
+@AppModeFull(reason = "The system should be able to bind to StubMediaRoute2ProviderService")
@LargeTest
public class MediaRouter2Test {
private static final String TAG = "MR2Test";
@@ -78,7 +80,7 @@
private MediaRouter2 mRouter2;
private Executor mExecutor;
private AudioManager mAudioManager;
- private SampleMediaRoute2ProviderService mServiceInstance;
+ private StubMediaRoute2ProviderService mService;
private static final int TIMEOUT_MS = 5000;
private static final int WAIT_MS = 2000;
@@ -95,18 +97,27 @@
mExecutor = Executors.newSingleThreadExecutor();
mAudioManager = (AudioManager) mContext.getSystemService(AUDIO_SERVICE);
- mServiceInstance = SampleMediaRoute2ProviderService.getInstance();
- if (mServiceInstance != null) {
- mServiceInstance.initializeRoutes();
- mServiceInstance.publishRoutes();
- }
+ new PollingCheck(TIMEOUT_MS) {
+ @Override
+ protected boolean check() {
+ StubMediaRoute2ProviderService service =
+ StubMediaRoute2ProviderService.getInstance();
+ if (service != null) {
+ mService = service;
+ return true;
+ }
+ return false;
+ }
+ }.run();
+ mService.initializeRoutes();
+ mService.publishRoutes();
}
@After
public void tearDown() throws Exception {
- if (mServiceInstance != null) {
- mServiceInstance.clear();
- mServiceInstance = null;
+ if (mService != null) {
+ mService.clear();
+ mService = null;
}
}
@@ -134,45 +145,28 @@
}
@Test
- public void testRegisterControllerCallbackWithInvalidArguments() {
+ public void testRegisterTransferCallbackWithInvalidArguments() {
Executor executor = mExecutor;
- RoutingControllerCallback callback = new RoutingControllerCallback();
+ TransferCallback callback = new TransferCallback();
// Tests null executor
assertThrows(NullPointerException.class,
- () -> mRouter2.registerControllerCallback(null, callback));
+ () -> mRouter2.registerTransferCallback(null, callback));
// Tests null callback
assertThrows(NullPointerException.class,
- () -> mRouter2.registerControllerCallback(executor, null));
+ () -> mRouter2.registerTransferCallback(executor, null));
}
@Test
- public void testUnregisterControllerCallbackWithNullCallback() {
+ public void testUnregisterTransferCallbackWithNullCallback() {
// Tests null callback
assertThrows(NullPointerException.class,
- () -> mRouter2.unregisterControllerCallback(null));
+ () -> mRouter2.unregisterTransferCallback(null));
}
@Test
- public void testRequestCreateControllerWithNullRoute() {
- assertThrows(NullPointerException.class,
- () -> mRouter2.requestCreateController(null));
- }
-
- @Test
- public void testRequestCreateControllerWithSystemRoute() {
- List<MediaRoute2Info> systemRoutes =
- mRouter2.getRoutes().stream().filter(r -> r.isSystemRoute())
- .collect(Collectors.toList());
-
- assertFalse(systemRoutes.isEmpty());
- assertThrows(IllegalArgumentException.class,
- () -> mRouter2.requestCreateController(systemRoutes.get(0)));
- }
-
- @Test
- public void testRequestCreateControllerSuccess() throws Exception {
+ public void testTransferToSuccess() throws Exception {
final List<String> sampleRouteFeature = new ArrayList<>();
sampleRouteFeature.add(FEATURE_SAMPLE);
@@ -185,18 +179,20 @@
final List<RoutingController> controllers = new ArrayList<>();
// Create session with this route
- RoutingControllerCallback controllerCallback = new RoutingControllerCallback() {
+ TransferCallback controllerCallback = new TransferCallback() {
@Override
- public void onControllerCreated(RoutingController controller) {
- assertNotNull(controller);
- assertTrue(createRouteMap(controller.getSelectedRoutes()).containsKey(
- ROUTE_ID1));
- controllers.add(controller);
- successLatch.countDown();
+ public void onTransferred(RoutingController oldController,
+ RoutingController newController) {
+ if (newController != null) {
+ assertTrue(createRouteMap(newController.getSelectedRoutes()).containsKey(
+ ROUTE_ID1));
+ controllers.add(newController);
+ successLatch.countDown();
+ }
}
@Override
- public void onControllerCreationFailed(MediaRoute2Info requestedRoute) {
+ public void onTransferFailed(MediaRoute2Info requestedRoute) {
failureLatch.countDown();
}
};
@@ -206,8 +202,8 @@
mRouter2.registerRouteCallback(mExecutor, routeCallback, EMPTY_DISCOVERY_PREFERENCE);
try {
- mRouter2.registerControllerCallback(mExecutor, controllerCallback);
- mRouter2.requestCreateController(route);
+ mRouter2.registerTransferCallback(mExecutor, controllerCallback);
+ mRouter2.transferTo(route);
assertTrue(successLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
// onSessionCreationFailed should not be called.
@@ -215,12 +211,12 @@
} finally {
releaseControllers(controllers);
mRouter2.unregisterRouteCallback(routeCallback);
- mRouter2.unregisterControllerCallback(controllerCallback);
+ mRouter2.unregisterTransferCallback(controllerCallback);
}
}
@Test
- public void testRequestCreateControllerFailure() throws Exception {
+ public void testTransferToFailure() throws Exception {
final List<String> sampleRouteType = new ArrayList<>();
sampleRouteType.add(FEATURE_SAMPLE);
@@ -233,15 +229,18 @@
final List<RoutingController> controllers = new ArrayList<>();
// Create session with this route
- RoutingControllerCallback controllerCallback = new RoutingControllerCallback() {
+ TransferCallback transferCallback = new TransferCallback() {
@Override
- public void onControllerCreated(RoutingController controller) {
- controllers.add(controller);
- successLatch.countDown();
+ public void onTransferred(RoutingController oldController,
+ RoutingController newController) {
+ if (newController != null) {
+ controllers.add(newController);
+ successLatch.countDown();
+ }
}
@Override
- public void onControllerCreationFailed(MediaRoute2Info requestedRoute) {
+ public void onTransferFailed(MediaRoute2Info requestedRoute) {
assertEquals(route, requestedRoute);
failureLatch.countDown();
}
@@ -252,38 +251,46 @@
mRouter2.registerRouteCallback(mExecutor, routeCallback, EMPTY_DISCOVERY_PREFERENCE);
try {
- mRouter2.registerControllerCallback(mExecutor, controllerCallback);
- mRouter2.requestCreateController(route);
+ mRouter2.registerTransferCallback(mExecutor, transferCallback);
+ mRouter2.transferTo(route);
assertTrue(failureLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
- // onSessionCreated should not be called.
+ // onTransferred should not be called.
assertFalse(successLatch.await(WAIT_MS, TimeUnit.MILLISECONDS));
} finally {
releaseControllers(controllers);
mRouter2.unregisterRouteCallback(routeCallback);
- mRouter2.unregisterControllerCallback(controllerCallback);
+ mRouter2.unregisterTransferCallback(transferCallback);
}
}
@Test
- public void testRequestCreateControllerMultipleSessions() throws Exception {
+ public void testTransferToTwice() throws Exception {
final List<String> sampleRouteType = new ArrayList<>();
sampleRouteType.add(FEATURE_SAMPLE);
- final CountDownLatch successLatch = new CountDownLatch(2);
+ final CountDownLatch successLatch1 = new CountDownLatch(1);
+ final CountDownLatch successLatch2 = new CountDownLatch(1);
final CountDownLatch failureLatch = new CountDownLatch(1);
final List<RoutingController> createdControllers = new ArrayList<>();
// Create session with this route
- RoutingControllerCallback controllerCallback = new RoutingControllerCallback() {
+ TransferCallback transferCallback = new TransferCallback() {
@Override
- public void onControllerCreated(RoutingController controller) {
- createdControllers.add(controller);
- successLatch.countDown();
+ public void onTransferred(RoutingController oldController,
+ RoutingController newController) {
+ if (newController != null) {
+ createdControllers.add(newController);
+ if (successLatch1.getCount() > 0) {
+ successLatch1.countDown();
+ } else {
+ successLatch2.countDown();
+ }
+ }
}
@Override
- public void onControllerCreationFailed(MediaRoute2Info requestedRoute) {
+ public void onTransferFailed(MediaRoute2Info requestedRoute) {
failureLatch.countDown();
}
};
@@ -299,12 +306,13 @@
mRouter2.registerRouteCallback(mExecutor, routeCallback, EMPTY_DISCOVERY_PREFERENCE);
try {
- mRouter2.registerControllerCallback(mExecutor, controllerCallback);
- mRouter2.requestCreateController(route1);
- mRouter2.requestCreateController(route2);
- assertTrue(successLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ mRouter2.registerTransferCallback(mExecutor, transferCallback);
+ mRouter2.transferTo(route1);
+ assertTrue(successLatch1.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ mRouter2.transferTo(route2);
+ assertTrue(successLatch2.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
- // onSessionCreationFailed should not be called.
+ // onTransferFailed should not be called.
assertFalse(failureLatch.await(WAIT_MS, TimeUnit.MILLISECONDS));
// Created controllers should have proper info
@@ -312,6 +320,9 @@
RoutingController controller1 = createdControllers.get(0);
RoutingController controller2 = createdControllers.get(1);
+ // The first controller is expected to be released.
+ assertTrue(controller1.isReleased());
+
assertNotEquals(controller1.getId(), controller2.getId());
assertTrue(createRouteMap(controller1.getSelectedRoutes()).containsKey(
ROUTE_ID1));
@@ -321,7 +332,7 @@
} finally {
releaseControllers(createdControllers);
mRouter2.unregisterRouteCallback(routeCallback);
- mRouter2.unregisterControllerCallback(controllerCallback);
+ mRouter2.unregisterTransferCallback(transferCallback);
}
}
@@ -334,40 +345,37 @@
MediaRoute2Info route = routes.get(ROUTE_ID1);
assertNotNull(route);
- final Bundle createSessionHints = new Bundle();
- createSessionHints.putString(TEST_KEY, TEST_VALUE);
- final OnGetControllerHintsListener listener = new OnGetControllerHintsListener() {
- @Override
- public Bundle onGetControllerHints(MediaRoute2Info route) {
- return createSessionHints;
- }
- };
+ final Bundle controllerHints = new Bundle();
+ controllerHints.putString(TEST_KEY, TEST_VALUE);
+ final OnGetControllerHintsListener listener = route1 -> controllerHints;
final CountDownLatch successLatch = new CountDownLatch(1);
final CountDownLatch failureLatch = new CountDownLatch(1);
final List<RoutingController> controllers = new ArrayList<>();
// Create session with this route
- RoutingControllerCallback controllerCallback = new RoutingControllerCallback() {
+ TransferCallback transferCallback = new TransferCallback() {
@Override
- public void onControllerCreated(RoutingController controller) {
- assertNotNull(controller);
- assertTrue(createRouteMap(controller.getSelectedRoutes()).containsKey(
- ROUTE_ID1));
+ public void onTransferred(RoutingController oldController,
+ RoutingController newController) {
+ if (newController != null) {
+ assertTrue(createRouteMap(newController.getSelectedRoutes()).containsKey(
+ ROUTE_ID1));
- // The SampleMediaRoute2ProviderService supposed to set control hints
- // with the given creationSessionHints.
- Bundle controlHints = controller.getControlHints();
- assertNotNull(controlHints);
- assertTrue(controlHints.containsKey(TEST_KEY));
- assertEquals(TEST_VALUE, controlHints.getString(TEST_KEY));
+ // The StubMediaRoute2ProviderService is supposed to set control hints
+ // with the given controllerHints.
+ Bundle controlHints = newController.getControlHints();
+ assertNotNull(controlHints);
+ assertTrue(controlHints.containsKey(TEST_KEY));
+ assertEquals(TEST_VALUE, controlHints.getString(TEST_KEY));
- controllers.add(controller);
- successLatch.countDown();
+ controllers.add(newController);
+ successLatch.countDown();
+ }
}
@Override
- public void onControllerCreationFailed(MediaRoute2Info requestedRoute) {
+ public void onTransferFailed(MediaRoute2Info requestedRoute) {
failureLatch.countDown();
}
};
@@ -377,12 +385,12 @@
mRouter2.registerRouteCallback(mExecutor, routeCallback, EMPTY_DISCOVERY_PREFERENCE);
try {
- mRouter2.registerControllerCallback(mExecutor, controllerCallback);
+ mRouter2.registerTransferCallback(mExecutor, transferCallback);
- // The SampleMediaRoute2ProviderService supposed to set control hints
+ // The StubMediaRoute2ProviderService supposed to set control hints
// with the given creationSessionHints.
mRouter2.setOnGetControllerHintsListener(listener);
- mRouter2.requestCreateController(route);
+ mRouter2.transferTo(route);
assertTrue(successLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
// onSessionCreationFailed should not be called.
@@ -390,7 +398,7 @@
} finally {
releaseControllers(controllers);
mRouter2.unregisterRouteCallback(routeCallback);
- mRouter2.unregisterControllerCallback(controllerCallback);
+ mRouter2.unregisterTransferCallback(transferCallback);
}
}
@@ -409,11 +417,14 @@
List<RoutingController> controllers = new ArrayList<>();
// Create session with this route
- RoutingControllerCallback controllerCallback = new RoutingControllerCallback() {
+ TransferCallback transferCallback = new TransferCallback() {
@Override
- public void onControllerCreated(RoutingController controller) {
- controllers.add(controller);
- successLatch.countDown();
+ public void onTransferred(RoutingController oldController,
+ RoutingController newController) {
+ if (newController != null) {
+ controllers.add(newController);
+ successLatch.countDown();
+ }
}
};
@@ -422,12 +433,12 @@
try {
mRouter2.registerRouteCallback(mExecutor, routeCallback, EMPTY_DISCOVERY_PREFERENCE);
- mRouter2.registerControllerCallback(mExecutor, controllerCallback);
- mRouter2.requestCreateController(route);
+ mRouter2.registerTransferCallback(mExecutor, transferCallback);
+ mRouter2.transferTo(route);
assertTrue(successLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
} finally {
- mRouter2.unregisterControllerCallback(controllerCallback);
+ mRouter2.unregisterTransferCallback(transferCallback);
mRouter2.unregisterRouteCallback(routeCallback);
}
@@ -440,7 +451,7 @@
int maxVolume = targetController.getVolumeMax();
int targetVolume = (currentVolume == maxVolume) ? currentVolume - 1 : (currentVolume + 1);
- RoutingControllerCallback routingControllerCallback = new RoutingControllerCallback() {
+ ControllerCallback controllerCallback = new ControllerCallback() {
@Override
public void onControllerUpdated(MediaRouter2.RoutingController controller) {
if (!TextUtils.equals(targetController.getId(), controller.getId())) {
@@ -453,19 +464,19 @@
};
try {
- mRouter2.registerControllerCallback(mExecutor, routingControllerCallback);
+ mRouter2.registerControllerCallback(mExecutor, controllerCallback);
mRouter2.registerRouteCallback(mExecutor, routeCallback, EMPTY_DISCOVERY_PREFERENCE);
targetController.setVolume(targetVolume);
assertTrue(volumeChangedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
} finally {
releaseControllers(controllers);
mRouter2.unregisterRouteCallback(routeCallback);
- mRouter2.unregisterControllerCallback(routingControllerCallback);
+ mRouter2.unregisterControllerCallback(controllerCallback);
}
}
@Test
- public void testRoutingControllerCallbackIsNotCalledAfterUnregistered() throws Exception {
+ public void testTransferCallbackIsNotCalledAfterUnregistered() throws Exception {
final List<String> sampleRouteType = new ArrayList<>();
sampleRouteType.add(FEATURE_SAMPLE);
@@ -478,15 +489,18 @@
final List<RoutingController> controllers = new ArrayList<>();
// Create session with this route
- RoutingControllerCallback controllerCallback = new RoutingControllerCallback() {
+ TransferCallback transferCallback = new TransferCallback() {
@Override
- public void onControllerCreated(RoutingController controller) {
- controllers.add(controller);
- successLatch.countDown();
+ public void onTransferred(RoutingController oldController,
+ RoutingController newController) {
+ if (newController != null) {
+ controllers.add(newController);
+ successLatch.countDown();
+ }
}
@Override
- public void onControllerCreationFailed(MediaRoute2Info requestedRoute) {
+ public void onTransferFailed(MediaRoute2Info requestedRoute) {
failureLatch.countDown();
}
};
@@ -496,19 +510,19 @@
mRouter2.registerRouteCallback(mExecutor, routeCallback, EMPTY_DISCOVERY_PREFERENCE);
try {
- mRouter2.registerControllerCallback(mExecutor, controllerCallback);
- mRouter2.requestCreateController(route);
+ mRouter2.registerTransferCallback(mExecutor, transferCallback);
+ mRouter2.transferTo(route);
- // Unregisters session callback
- mRouter2.unregisterControllerCallback(controllerCallback);
+ // Unregisters transfer callback
+ mRouter2.unregisterTransferCallback(transferCallback);
- // No session callback methods should be called.
+ // No transfer callback methods should be called.
assertFalse(successLatch.await(WAIT_MS, TimeUnit.MILLISECONDS));
assertFalse(failureLatch.await(WAIT_MS, TimeUnit.MILLISECONDS));
} finally {
releaseControllers(controllers);
mRouter2.unregisterRouteCallback(routeCallback);
- mRouter2.unregisterControllerCallback(controllerCallback);
+ mRouter2.unregisterTransferCallback(transferCallback);
}
}
@@ -519,28 +533,33 @@
sampleRouteType.add(FEATURE_SAMPLE);
Map<String, MediaRoute2Info> routes = waitAndGetRoutes(sampleRouteType);
- MediaRoute2Info routeToCreateSessionWith = routes.get(ROUTE_ID1);
- assertNotNull(routeToCreateSessionWith);
+ MediaRoute2Info routeToBegin = routes.get(ROUTE_ID1);
+ assertNotNull(routeToBegin);
- final CountDownLatch onControllerCreatedLatch = new CountDownLatch(1);
+ final CountDownLatch onTransferredLatch = new CountDownLatch(1);
final CountDownLatch onControllerUpdatedLatchForSelect = new CountDownLatch(1);
final CountDownLatch onControllerUpdatedLatchForDeselect = new CountDownLatch(1);
final List<RoutingController> controllers = new ArrayList<>();
- // Create session with ROUTE_ID1
- RoutingControllerCallback controllerCallback = new RoutingControllerCallback() {
- @Override
- public void onControllerCreated(RoutingController controller) {
- assertNotNull(controller);
- assertTrue(getOriginalRouteIds(controller.getSelectedRoutes()).contains(
- ROUTE_ID1));
- controllers.add(controller);
- onControllerCreatedLatch.countDown();
- }
+ // Create session with ROUTE_ID1
+ TransferCallback transferCallback = new TransferCallback() {
+ @Override
+ public void onTransferred(RoutingController oldController,
+ RoutingController newController) {
+ if (newController != null) {
+ assertTrue(getOriginalRouteIds(newController.getSelectedRoutes()).contains(
+ ROUTE_ID1));
+ controllers.add(newController);
+ onTransferredLatch.countDown();
+ }
+ }
+ };
+
+ ControllerCallback controllerCallback = new ControllerCallback() {
@Override
public void onControllerUpdated(RoutingController controller) {
- if (onControllerCreatedLatch.getCount() != 0
+ if (onTransferredLatch.getCount() != 0
|| !TextUtils.equals(controllers.get(0).getId(), controller.getId())) {
return;
}
@@ -573,14 +592,16 @@
}
};
+
// TODO: Remove this once the MediaRouter2 becomes always connected to the service.
RouteCallback routeCallback = new RouteCallback();
mRouter2.registerRouteCallback(mExecutor, routeCallback, EMPTY_DISCOVERY_PREFERENCE);
try {
+ mRouter2.registerTransferCallback(mExecutor, transferCallback);
mRouter2.registerControllerCallback(mExecutor, controllerCallback);
- mRouter2.requestCreateController(routeToCreateSessionWith);
- assertTrue(onControllerCreatedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ mRouter2.transferTo(routeToBegin);
+ assertTrue(onTransferredLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
assertEquals(1, controllers.size());
RoutingController controller = controllers.get(0);
@@ -601,6 +622,7 @@
} finally {
releaseControllers(controllers);
mRouter2.unregisterRouteCallback(routeCallback);
+ mRouter2.unregisterTransferCallback(transferCallback);
mRouter2.unregisterControllerCallback(controllerCallback);
}
}
@@ -611,27 +633,31 @@
sampleRouteType.add(FEATURE_SAMPLE);
Map<String, MediaRoute2Info> routes = waitAndGetRoutes(sampleRouteType);
- MediaRoute2Info routeToCreateSessionWith = routes.get(ROUTE_ID1);
- assertNotNull(routeToCreateSessionWith);
+ MediaRoute2Info routeToBegin = routes.get(ROUTE_ID1);
+ assertNotNull(routeToBegin);
- final CountDownLatch onControllerCreatedLatch = new CountDownLatch(1);
+ final CountDownLatch onTransferredLatch = new CountDownLatch(1);
final CountDownLatch onControllerUpdatedLatch = new CountDownLatch(1);
final List<RoutingController> controllers = new ArrayList<>();
// Create session with ROUTE_ID1
- RoutingControllerCallback controllerCallback = new RoutingControllerCallback() {
+ TransferCallback transferCallback = new TransferCallback() {
@Override
- public void onControllerCreated(RoutingController controller) {
- assertNotNull(controller);
- assertTrue(getOriginalRouteIds(controller.getSelectedRoutes()).contains(
- ROUTE_ID1));
- controllers.add(controller);
- onControllerCreatedLatch.countDown();
+ public void onTransferred(RoutingController oldController,
+ RoutingController newController) {
+ if (newController != null) {
+ assertTrue(getOriginalRouteIds(newController.getSelectedRoutes()).contains(
+ ROUTE_ID1));
+ controllers.add(newController);
+ onTransferredLatch.countDown();
+ }
}
+ };
+ ControllerCallback controllerCallback = new ControllerCallback() {
@Override
public void onControllerUpdated(RoutingController controller) {
- if (onControllerCreatedLatch.getCount() != 0
+ if (onTransferredLatch.getCount() != 0
|| !TextUtils.equals(controllers.get(0).getId(), controller.getId())) {
return;
}
@@ -640,9 +666,6 @@
ROUTE_ID1));
assertTrue(getOriginalRouteIds(controller.getSelectedRoutes())
.contains(ROUTE_ID5_TO_TRANSFER_TO));
- assertFalse(getOriginalRouteIds(controller.getTransferableRoutes())
- .contains(ROUTE_ID5_TO_TRANSFER_TO));
-
onControllerUpdatedLatch.countDown();
}
};
@@ -652,25 +675,94 @@
mRouter2.registerRouteCallback(mExecutor, routeCallback, EMPTY_DISCOVERY_PREFERENCE);
try {
+ mRouter2.registerTransferCallback(mExecutor, transferCallback);
mRouter2.registerControllerCallback(mExecutor, controllerCallback);
- mRouter2.requestCreateController(routeToCreateSessionWith);
- assertTrue(onControllerCreatedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ mRouter2.transferTo(routeToBegin);
+ assertTrue(onTransferredLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
assertEquals(1, controllers.size());
RoutingController controller = controllers.get(0);
- assertTrue(getOriginalRouteIds(controller.getTransferableRoutes())
- .contains(ROUTE_ID5_TO_TRANSFER_TO));
// Transfer to ROUTE_ID5_TO_TRANSFER_TO
MediaRoute2Info routeToTransferTo = routes.get(ROUTE_ID5_TO_TRANSFER_TO);
assertNotNull(routeToTransferTo);
- controller.transferToRoute(routeToTransferTo);
+ mRouter2.transferTo(routeToTransferTo);
assertTrue(onControllerUpdatedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
} finally {
releaseControllers(controllers);
mRouter2.unregisterRouteCallback(routeCallback);
mRouter2.unregisterControllerCallback(controllerCallback);
+ mRouter2.unregisterTransferCallback(transferCallback);
+ }
+ }
+
+ @Test
+ public void testControllerCallbackUnregister() throws Exception {
+ final List<String> sampleRouteType = new ArrayList<>();
+ sampleRouteType.add(FEATURE_SAMPLE);
+
+ Map<String, MediaRoute2Info> routes = waitAndGetRoutes(sampleRouteType);
+ MediaRoute2Info routeToBegin = routes.get(ROUTE_ID1);
+ assertNotNull(routeToBegin);
+
+ final CountDownLatch onTransferredLatch = new CountDownLatch(1);
+ final CountDownLatch onControllerUpdatedLatch = new CountDownLatch(1);
+ final List<RoutingController> controllers = new ArrayList<>();
+
+ // Create session with ROUTE_ID1
+ TransferCallback transferCallback = new TransferCallback() {
+ @Override
+ public void onTransferred(RoutingController oldController,
+ RoutingController newController) {
+ if (newController != null) {
+ assertTrue(getOriginalRouteIds(newController.getSelectedRoutes()).contains(
+ ROUTE_ID1));
+ controllers.add(newController);
+ onTransferredLatch.countDown();
+ }
+ }
+ };
+ ControllerCallback controllerCallback = new ControllerCallback() {
+ @Override
+ public void onControllerUpdated(RoutingController controller) {
+ if (onTransferredLatch.getCount() != 0
+ || !TextUtils.equals(controllers.get(0).getId(), controller.getId())) {
+ return;
+ }
+ assertEquals(1, controller.getSelectedRoutes().size());
+ assertFalse(getOriginalRouteIds(controller.getSelectedRoutes()).contains(
+ ROUTE_ID1));
+ assertTrue(getOriginalRouteIds(controller.getSelectedRoutes())
+ .contains(ROUTE_ID5_TO_TRANSFER_TO));
+ onControllerUpdatedLatch.countDown();
+ }
+ };
+
+ // TODO: Remove this once the MediaRouter2 becomes always connected to the service.
+ RouteCallback routeCallback = new RouteCallback();
+ mRouter2.registerRouteCallback(mExecutor, routeCallback, EMPTY_DISCOVERY_PREFERENCE);
+
+ try {
+ mRouter2.registerTransferCallback(mExecutor, transferCallback);
+ mRouter2.registerControllerCallback(mExecutor, controllerCallback);
+ mRouter2.transferTo(routeToBegin);
+ assertTrue(onTransferredLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+
+ assertEquals(1, controllers.size());
+
+ // Transfer to ROUTE_ID5_TO_TRANSFER_TO
+ MediaRoute2Info routeToTransferTo = routes.get(ROUTE_ID5_TO_TRANSFER_TO);
+ assertNotNull(routeToTransferTo);
+
+ mRouter2.unregisterControllerCallback(controllerCallback);
+ mRouter2.transferTo(routeToTransferTo);
+ assertFalse(onControllerUpdatedLatch.await(WAIT_MS, TimeUnit.MILLISECONDS));
+ } finally {
+ releaseControllers(controllers);
+ mRouter2.unregisterRouteCallback(routeCallback);
+ mRouter2.unregisterControllerCallback(controllerCallback);
+ mRouter2.unregisterTransferCallback(transferCallback);
}
}
@@ -682,68 +774,68 @@
sampleRouteType.add(FEATURE_SAMPLE);
Map<String, MediaRoute2Info> routes = waitAndGetRoutes(sampleRouteType);
- MediaRoute2Info routeToCreateSessionWith = routes.get(ROUTE_ID1);
- assertNotNull(routeToCreateSessionWith);
+ MediaRoute2Info routeTransferFrom = routes.get(ROUTE_ID1);
+ assertNotNull(routeTransferFrom);
- final CountDownLatch onControllerCreatedLatch = new CountDownLatch(1);
+ final CountDownLatch onTransferredLatch = new CountDownLatch(1);
final CountDownLatch onControllerUpdatedLatch = new CountDownLatch(1);
final CountDownLatch onControllerReleasedLatch = new CountDownLatch(1);
final List<RoutingController> controllers = new ArrayList<>();
- // Create session with ROUTE_ID1
- RoutingControllerCallback controllerCallback = new RoutingControllerCallback() {
+ TransferCallback transferCallback = new TransferCallback() {
@Override
- public void onControllerCreated(RoutingController controller) {
- assertNotNull(controller);
- assertTrue(getOriginalRouteIds(controller.getSelectedRoutes()).contains(
- ROUTE_ID1));
- controllers.add(controller);
- onControllerCreatedLatch.countDown();
+ public void onTransferred(RoutingController oldController,
+ RoutingController newController) {
+ if (newController != null) {
+ assertTrue(getOriginalRouteIds(newController.getSelectedRoutes()).contains(
+ ROUTE_ID1));
+ controllers.add(newController);
+ onTransferredLatch.countDown();
+ } else {
+ if (onTransferredLatch.getCount() != 0
+ || !TextUtils.equals(
+ controllers.get(0).getId(), oldController.getId())) {
+ return;
+ }
+ onControllerReleasedLatch.countDown();
+ }
}
+ };
+ ControllerCallback controllerCallback = new ControllerCallback() {
@Override
public void onControllerUpdated(RoutingController controller) {
- if (onControllerCreatedLatch.getCount() != 0
+ if (onTransferredLatch.getCount() != 0
|| !TextUtils.equals(controllers.get(0).getId(), controller.getId())) {
return;
}
onControllerUpdatedLatch.countDown();
}
-
- @Override
- public void onControllerReleased(RoutingController controller) {
- if (onControllerCreatedLatch.getCount() != 0
- || !TextUtils.equals(controllers.get(0).getId(), controller.getId())) {
- return;
- }
- onControllerReleasedLatch.countDown();
- }
};
- // TODO: Remove this once the MediaRouter2 becomes always connected to the service.
+ // TODO: Remove this once the MediaRouter2 becomes always connected to the service.
RouteCallback routeCallback = new RouteCallback();
mRouter2.registerRouteCallback(mExecutor, routeCallback, EMPTY_DISCOVERY_PREFERENCE);
try {
+ mRouter2.registerTransferCallback(mExecutor, transferCallback);
mRouter2.registerControllerCallback(mExecutor, controllerCallback);
- mRouter2.requestCreateController(routeToCreateSessionWith);
- assertTrue(onControllerCreatedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ mRouter2.transferTo(routeTransferFrom);
+ assertTrue(onTransferredLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
assertEquals(1, controllers.size());
RoutingController controller = controllers.get(0);
- assertTrue(getOriginalRouteIds(controller.getTransferableRoutes())
- .contains(ROUTE_ID5_TO_TRANSFER_TO));
// Release controller. Future calls should be ignored.
controller.release();
- // Transfer to ROUTE_ID5_TO_TRANSFER_TO
- MediaRoute2Info routeToTransferTo = routes.get(ROUTE_ID5_TO_TRANSFER_TO);
- assertNotNull(routeToTransferTo);
+ // Select ROUTE_ID5_TO_TRANSFER_TO
+ MediaRoute2Info routeToSelect = routes.get(ROUTE_ID4_TO_SELECT_AND_DESELECT);
+ assertNotNull(routeToSelect);
// This call should be ignored.
// The onSessionInfoChanged() shouldn't be called.
- controller.transferToRoute(routeToTransferTo);
+ controller.selectRoute(routeToSelect);
assertFalse(onControllerUpdatedLatch.await(WAIT_MS, TimeUnit.MILLISECONDS));
// onControllerReleased should be called.
@@ -752,6 +844,7 @@
releaseControllers(controllers);
mRouter2.unregisterRouteCallback(routeCallback);
mRouter2.unregisterControllerCallback(controllerCallback);
+ mRouter2.unregisterTransferCallback(transferCallback);
}
}
@@ -843,12 +936,10 @@
routeCallback.onRoutesChanged(null);
routeCallback.onRoutesRemoved(null);
- MediaRouter2.RoutingControllerCallback controllerCallback =
- new MediaRouter2.RoutingControllerCallback();
- controllerCallback.onControllerCreated(null);
- controllerCallback.onControllerCreationFailed(null);
- controllerCallback.onControllerUpdated(null);
- controllerCallback.onControllerReleased(null);
+ MediaRouter2.TransferCallback transferCallback =
+ new MediaRouter2.TransferCallback();
+ transferCallback.onTransferred(null, null);
+ transferCallback.onTransferFailed(null);
OnGetControllerHintsListener listener = route -> null;
listener.onGetControllerHints(null);
diff --git a/tests/tests/media/src/android/media/cts/RingtoneManagerTest.java b/tests/tests/media/src/android/media/cts/RingtoneManagerTest.java
index 3ff68c3..f305a74 100644
--- a/tests/tests/media/src/android/media/cts/RingtoneManagerTest.java
+++ b/tests/tests/media/src/android/media/cts/RingtoneManagerTest.java
@@ -70,8 +70,7 @@
mDefaultUri = RingtoneManager.getActualDefaultRingtoneUri(mContext,
RingtoneManager.TYPE_RINGTONE);
- if (mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT
- && !ActivityManager.isLowRamDeviceStatic()) {
+ if (mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT) {
try {
Utils.toggleNotificationPolicyAccess(
mContext.getPackageName(), getInstrumentation(), true);
@@ -87,19 +86,17 @@
@Override
protected void tearDown() throws Exception {
- if (!ActivityManager.isLowRamDeviceStatic()) {
- try {
- Utils.toggleNotificationPolicyAccess(
- mContext.getPackageName(), getInstrumentation(), true);
- // restore original ringer settings
- if (mAudioManager != null) {
- mAudioManager.setStreamVolume(AudioManager.STREAM_RING, mOriginalVolume,
- AudioManager.FLAG_ALLOW_RINGER_MODES);
- }
- } finally {
- Utils.toggleNotificationPolicyAccess(
- mContext.getPackageName(), getInstrumentation(), false);
+ try {
+ Utils.toggleNotificationPolicyAccess(
+ mContext.getPackageName(), getInstrumentation(), true);
+ // restore original ringer settings
+ if (mAudioManager != null) {
+ mAudioManager.setStreamVolume(AudioManager.STREAM_RING, mOriginalVolume,
+ AudioManager.FLAG_ALLOW_RINGER_MODES);
}
+ } finally {
+ Utils.toggleNotificationPolicyAccess(
+ mContext.getPackageName(), getInstrumentation(), false);
}
RingtoneManager.setActualDefaultRingtoneUri(mContext, RingtoneManager.TYPE_RINGTONE,
mDefaultUri);
diff --git a/tests/tests/media/src/android/media/cts/RingtoneTest.java b/tests/tests/media/src/android/media/cts/RingtoneTest.java
index c7ddbd8..ba289f3 100644
--- a/tests/tests/media/src/android/media/cts/RingtoneTest.java
+++ b/tests/tests/media/src/android/media/cts/RingtoneTest.java
@@ -16,8 +16,6 @@
package android.media.cts;
-import android.app.ActivityManager;
-import android.app.UiAutomation;
import android.content.Context;
import android.content.pm.PackageManager;
import android.media.AudioAttributes;
@@ -63,7 +61,7 @@
} else if (mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_NORMAL) {
mAudioManager.setStreamVolume(AudioManager.STREAM_RING, maxVolume / 2,
AudioManager.FLAG_ALLOW_RINGER_MODES);
- } else if (!ActivityManager.isLowRamDeviceStatic()) {
+ } else {
try {
Utils.toggleNotificationPolicyAccess(
mContext.getPackageName(), getInstrumentation(), true);
@@ -101,7 +99,7 @@
if (mRingtone.isPlaying()) mRingtone.stop();
mRingtone.setStreamType(mOriginalStreamType);
}
- if (mAudioManager != null && !ActivityManager.isLowRamDeviceStatic()) {
+ if (mAudioManager != null) {
try {
Utils.toggleNotificationPolicyAccess(
mContext.getPackageName(), getInstrumentation(), true);
diff --git a/tests/tests/media/src/android/media/cts/SampleMediaRoute2ProviderService.java b/tests/tests/media/src/android/media/cts/StubMediaRoute2ProviderService.java
similarity index 88%
rename from tests/tests/media/src/android/media/cts/SampleMediaRoute2ProviderService.java
rename to tests/tests/media/src/android/media/cts/StubMediaRoute2ProviderService.java
index 6bfdc41..326adb0 100644
--- a/tests/tests/media/src/android/media/cts/SampleMediaRoute2ProviderService.java
+++ b/tests/tests/media/src/android/media/cts/StubMediaRoute2ProviderService.java
@@ -31,7 +31,6 @@
import android.os.Bundle;
import android.os.IBinder;
import android.text.TextUtils;
-import android.util.Log;
import java.lang.reflect.Method;
import java.util.ArrayList;
@@ -41,7 +40,7 @@
import javax.annotation.concurrent.GuardedBy;
-public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService {
+public class StubMediaRoute2ProviderService extends MediaRoute2ProviderService {
private static final String TAG = "SampleMR2ProviderSvc";
private static final Object sLock = new Object();
@@ -89,7 +88,7 @@
private int mNextSessionId = 1000;
@GuardedBy("sLock")
- private static SampleMediaRoute2ProviderService sInstance;
+ private static StubMediaRoute2ProviderService sInstance;
private Proxy mProxy;
public void initializeRoutes() {
@@ -140,7 +139,7 @@
mRoutes.put(variableVolumeRoute.getId(), variableVolumeRoute);
}
- public static SampleMediaRoute2ProviderService getInstance() {
+ public static StubMediaRoute2ProviderService getInstance() {
synchronized (sLock) {
return sInstance;
}
@@ -183,7 +182,7 @@
}
@Override
- public void onSetRouteVolume(String routeId, int volume) {
+ public void onSetRouteVolume(long requestId, String routeId, int volume) {
MediaRoute2Info route = mRoutes.get(routeId);
if (route == null) {
return;
@@ -196,7 +195,7 @@
}
@Override
- public void onSetSessionVolume(String sessionId, int volume) {
+ public void onSetSessionVolume(long requestId, String sessionId, int volume) {
RoutingSessionInfo sessionInfo = getSessionInfo(sessionId);
if (sessionInfo == null) {
return;
@@ -209,11 +208,11 @@
}
@Override
- public void onCreateSession(String packageName, String routeId, long requestId,
+ public void onCreateSession(long requestId, String packageName, String routeId,
@Nullable Bundle sessionHints) {
Proxy proxy = mProxy;
if (doesProxyOverridesMethod(proxy, "onCreateSession")) {
- proxy.onCreateSession(packageName, routeId, requestId, sessionHints);
+ proxy.onCreateSession(requestId, packageName, routeId, sessionHints);
return;
}
@@ -223,7 +222,7 @@
notifySessionCreationFailed(requestId);
return;
}
- maybeDeselectRoute(routeId);
+ maybeDeselectRoute(routeId, requestId);
final String sessionId = String.valueOf(mNextSessionId);
mNextSessionId++;
@@ -248,10 +247,10 @@
}
@Override
- public void onReleaseSession(String sessionId) {
+ public void onReleaseSession(long requestId, String sessionId) {
Proxy proxy = mProxy;
if (doesProxyOverridesMethod(proxy, "onReleaseSession")) {
- proxy.onReleaseSession(sessionId);
+ proxy.onReleaseSession(requestId, sessionId);
return;
}
@@ -286,10 +285,10 @@
}
@Override
- public void onSelectRoute(String sessionId, String routeId) {
+ public void onSelectRoute(long requestId, String sessionId, String routeId) {
Proxy proxy = mProxy;
if (doesProxyOverridesMethod(proxy, "onSelectRoute")) {
- proxy.onSelectRoute(sessionId, routeId);
+ proxy.onSelectRoute(requestId, sessionId, routeId);
return;
}
@@ -298,12 +297,13 @@
if (route == null || sessionInfo == null) {
return;
}
- maybeDeselectRoute(routeId);
+ maybeDeselectRoute(routeId, requestId);
mRoutes.put(routeId, new MediaRoute2Info.Builder(route)
.setClientPackageName(sessionInfo.getClientPackageName())
.build());
mRouteIdToSessionId.put(routeId, sessionId);
+ publishRoutes();
RoutingSessionInfo newSessionInfo = new RoutingSessionInfo.Builder(sessionInfo)
.addSelectedRoute(routeId)
@@ -314,10 +314,10 @@
}
@Override
- public void onDeselectRoute(String sessionId, String routeId) {
+ public void onDeselectRoute(long requestId, String sessionId, String routeId) {
Proxy proxy = mProxy;
if (doesProxyOverridesMethod(proxy, "onDeselectRoute")) {
- proxy.onDeselectRoute(sessionId, routeId);
+ proxy.onDeselectRoute(requestId, sessionId, routeId);
return;
}
@@ -333,6 +333,7 @@
mRoutes.put(routeId, new MediaRoute2Info.Builder(route)
.setClientPackageName(null)
.build());
+ publishRoutes();
if (sessionInfo.getSelectedRoutes().size() == 1) {
notifySessionReleased(sessionId);
@@ -348,10 +349,10 @@
}
@Override
- public void onTransferToRoute(String sessionId, String routeId) {
+ public void onTransferToRoute(long requestId, String sessionId, String routeId) {
Proxy proxy = mProxy;
if (doesProxyOverridesMethod(proxy, "onTransferToRoute")) {
- proxy.onTransferToRoute(sessionId, routeId);
+ proxy.onTransferToRoute(requestId, sessionId, routeId);
return;
}
@@ -387,13 +388,13 @@
publishRoutes();
}
- void maybeDeselectRoute(String routeId) {
+ void maybeDeselectRoute(String routeId, long requestId) {
if (!mRouteIdToSessionId.containsKey(routeId)) {
return;
}
String sessionId = mRouteIdToSessionId.get(routeId);
- onDeselectRoute(sessionId, routeId);
+ onDeselectRoute(requestId, sessionId, routeId);
}
void publishRoutes() {
@@ -401,12 +402,15 @@
}
public static class Proxy {
- public void onCreateSession(@NonNull String packageName, @NonNull String routeId,
- long requestId, @Nullable Bundle sessionHints) {}
- public void onReleaseSession(@NonNull String sessionId) {}
- public void onSelectRoute(@NonNull String sessionId, @NonNull String routeId) {}
- public void onDeselectRoute(@NonNull String sessionId, @NonNull String routeId) {}
- public void onTransferToRoute(@NonNull String sessionId, @NonNull String routeId) {}
+ public void onCreateSession(long requestId, @NonNull String packageName,
+ @NonNull String routeId, @Nullable Bundle sessionHints) {}
+ public void onReleaseSession(long requestId, @NonNull String sessionId) {}
+ public void onSelectRoute(long requestId, @NonNull String sessionId,
+ @NonNull String routeId) {}
+ public void onDeselectRoute(long requestId, @NonNull String sessionId,
+ @NonNull String routeId) {}
+ public void onTransferToRoute(long requestId, @NonNull String sessionId,
+ @NonNull String routeId) {}
public void onDiscoveryPreferenceChanged(RouteDiscoveryPreference preference) {}
// TODO: Handle onSetRouteVolume() && onSetSessionVolume()
}
diff --git a/tests/tests/mediaparser/AndroidTest.xml b/tests/tests/mediaparser/AndroidTest.xml
index 905e7e4..1f28389 100644
--- a/tests/tests/mediaparser/AndroidTest.xml
+++ b/tests/tests/mediaparser/AndroidTest.xml
@@ -18,6 +18,7 @@
<option name="config-descriptor:metadata" key="component" value="media" />
<option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
<option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
+ <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="CtsMediaParserTestCases.apk" />
diff --git a/tests/tests/mediaparser/src/android/media/mediaparser/cts/MediaParserTest.java b/tests/tests/mediaparser/src/android/media/mediaparser/cts/MediaParserTest.java
index 818ce18..bdc7dfb 100644
--- a/tests/tests/mediaparser/src/android/media/mediaparser/cts/MediaParserTest.java
+++ b/tests/tests/mediaparser/src/android/media/mediaparser/cts/MediaParserTest.java
@@ -364,7 +364,7 @@
mediaParser.advance(mockInput);
if (expectedExtractorName != null) {
- assertThat(expectedExtractorName).isEqualTo(mediaParser.getExtractorName());
+ assertThat(expectedExtractorName).isEqualTo(mediaParser.getParserName());
// We are only checking that the extractor is the right one.
return;
}
diff --git a/tests/tests/nativemedia/aaudio/jni/test_aaudio_callback.cpp b/tests/tests/nativemedia/aaudio/jni/test_aaudio_callback.cpp
index 813b40c..4a89d69 100644
--- a/tests/tests/nativemedia/aaudio/jni/test_aaudio_callback.cpp
+++ b/tests/tests/nativemedia/aaudio/jni/test_aaudio_callback.cpp
@@ -55,17 +55,48 @@
return latencyMillis;
}
-using CbTestParams = std::tuple<aaudio_sharing_mode_t, int32_t, aaudio_performance_mode_t>;
+using CbTestParams = std::tuple<aaudio_sharing_mode_t, int32_t,
+ aaudio_performance_mode_t, int32_t, aaudio_format_t>;
enum {
PARAM_SHARING_MODE = 0,
PARAM_FRAMES_PER_CB,
- PARAM_PERF_MODE
+ PARAM_PERF_MODE,
+ PARAM_ALLOW_MMAP,
+ PARAM_AUDIO_FORMAT
};
+enum {
+ MMAP_NOT_ALLOWED,
+ MMAP_ALLOWED,
+};
+
+static const char* allowMMapToString(int allow) {
+ switch (allow) {
+ case MMAP_NOT_ALLOWED: return "NOTMMAP";
+ case MMAP_ALLOWED:
+ default:
+ return "MMAPOK";
+ }
+}
+
+static const char* audioFormatToString(aaudio_format_t format) {
+ switch (format) {
+ case AAUDIO_FORMAT_UNSPECIFIED: return "UNSP";
+ case AAUDIO_FORMAT_PCM_I16: return "I16";
+ case AAUDIO_FORMAT_PCM_FLOAT: return "FLT";
+ default:
+ return "BAD";
+ }
+}
+
static std::string getTestName(const ::testing::TestParamInfo<CbTestParams>& info) {
- return std::string() + sharingModeToString(std::get<PARAM_SHARING_MODE>(info.param)) +
- "__" + std::to_string(std::get<PARAM_FRAMES_PER_CB>(info.param)) +
- "__" + performanceModeToString(std::get<PARAM_PERF_MODE>(info.param));
+ return std::string()
+ + sharingModeToString(std::get<PARAM_SHARING_MODE>(info.param))
+ + "__" + std::to_string(std::get<PARAM_FRAMES_PER_CB>(info.param))
+ + "__" + performanceModeToString(std::get<PARAM_PERF_MODE>(info.param))
+ + "__" + allowMMapToString(std::get<PARAM_ALLOW_MMAP>(info.param))
+ + "__" + audioFormatToString(std::get<PARAM_AUDIO_FORMAT>(info.param))
+ ;
}
template<typename T>
@@ -143,11 +174,15 @@
}
void AAudioInputStreamCallbackTest::SetUp() {
+ aaudio_policy_t originalPolicy = AAUDIO_POLICY_AUTO;
+
mSetupSuccesful = false;
if (!deviceSupportsFeature(FEATURE_RECORDING)) return;
mHelper.reset(new InputStreamBuilderHelper(
std::get<PARAM_SHARING_MODE>(GetParam()),
- std::get<PARAM_PERF_MODE>(GetParam())));
+ std::get<PARAM_PERF_MODE>(GetParam()),
+ std::get<PARAM_AUDIO_FORMAT>(GetParam()))
+ );
mHelper->initBuilder();
int32_t framesPerDataCallback = std::get<PARAM_FRAMES_PER_CB>(GetParam());
@@ -158,7 +193,23 @@
AAudioStreamBuilder_setFramesPerDataCallback(builder(), framesPerDataCallback);
}
+ // Turn off MMap if requested.
+ int allowMMap = std::get<PARAM_ALLOW_MMAP>(GetParam()) == MMAP_ALLOWED;
+ if (AAudioExtensions::getInstance().isMMapSupported()) {
+ originalPolicy = AAudioExtensions::getInstance().getMMapPolicy();
+ AAudioExtensions::getInstance().setMMapEnabled(allowMMap);
+ }
+
mHelper->createAndVerifyStream(&mSetupSuccesful);
+
+ // Restore policy for next test.
+ if (AAudioExtensions::getInstance().isMMapSupported()) {
+ AAudioExtensions::getInstance().setMMapPolicy(originalPolicy);
+ }
+ if (!allowMMap) {
+ ASSERT_FALSE(AAudioExtensions::getInstance().isMMapUsed(mHelper->stream()));
+ }
+
}
// Test Reading from an AAudioStream using a Callback
@@ -166,12 +217,12 @@
if (!mSetupSuccesful) return;
const int32_t framesPerDataCallback = std::get<PARAM_FRAMES_PER_CB>(GetParam());
- const int32_t actualFramesPerDataCallback = AAudioStream_getFramesPerDataCallback(stream());
+ const int32_t streamFramesPerDataCallback = AAudioStream_getFramesPerDataCallback(stream());
if (framesPerDataCallback != AAUDIO_UNSPECIFIED) {
- ASSERT_EQ(framesPerDataCallback, actualFramesPerDataCallback);
+ ASSERT_EQ(framesPerDataCallback, streamFramesPerDataCallback);
}
- mCbData->reset(actualFramesPerDataCallback);
+ mCbData->reset(streamFramesPerDataCallback);
mHelper->startStream();
// See b/62090113. For legacy path, the device is only known after
@@ -189,8 +240,8 @@
sleep(1);
EXPECT_EQ(oldCallbackCount, mCbData->callbackCount); // expect not advancing
- if (framesPerDataCallback != AAUDIO_UNSPECIFIED) {
- ASSERT_EQ(framesPerDataCallback, mCbData->actualFramesPerCallback);
+ if (streamFramesPerDataCallback != AAUDIO_UNSPECIFIED) {
+ ASSERT_EQ(streamFramesPerDataCallback, mCbData->actualFramesPerCallback);
}
ASSERT_EQ(AAUDIO_OK, mCbData->callbackError);
@@ -201,16 +252,42 @@
std::make_tuple(
AAUDIO_SHARING_MODE_SHARED,
AAUDIO_UNSPECIFIED,
- AAUDIO_PERFORMANCE_MODE_NONE),
- // cb buffer size: arbitrary prime number < 192
- std::make_tuple(AAUDIO_SHARING_MODE_SHARED, 109, AAUDIO_PERFORMANCE_MODE_NONE),
+ AAUDIO_PERFORMANCE_MODE_NONE,
+ MMAP_ALLOWED,
+ AAUDIO_FORMAT_UNSPECIFIED),
+ // cb buffer size: arbitrary prime number < 96
+ std::make_tuple(AAUDIO_SHARING_MODE_SHARED, 67,
+ AAUDIO_PERFORMANCE_MODE_NONE, MMAP_ALLOWED,
+ AAUDIO_FORMAT_UNSPECIFIED),
+ std::make_tuple(AAUDIO_SHARING_MODE_SHARED, 67,
+ AAUDIO_PERFORMANCE_MODE_LOW_LATENCY, MMAP_ALLOWED,
+ AAUDIO_FORMAT_UNSPECIFIED),
+ std::make_tuple(AAUDIO_SHARING_MODE_EXCLUSIVE, 67,
+ AAUDIO_PERFORMANCE_MODE_LOW_LATENCY, MMAP_ALLOWED,
+ AAUDIO_FORMAT_UNSPECIFIED),
+ std::make_tuple(AAUDIO_SHARING_MODE_SHARED, 67,
+ AAUDIO_PERFORMANCE_MODE_LOW_LATENCY, MMAP_NOT_ALLOWED,
+ AAUDIO_FORMAT_PCM_I16),
+ std::make_tuple(AAUDIO_SHARING_MODE_SHARED, 67,
+ AAUDIO_PERFORMANCE_MODE_LOW_LATENCY, MMAP_NOT_ALLOWED,
+ AAUDIO_FORMAT_PCM_FLOAT),
// cb buffer size: arbitrary prime number > 192
- std::make_tuple(AAUDIO_SHARING_MODE_SHARED, 223, AAUDIO_PERFORMANCE_MODE_NONE),
+ std::make_tuple(AAUDIO_SHARING_MODE_SHARED, 223,
+ AAUDIO_PERFORMANCE_MODE_NONE, MMAP_ALLOWED,
+ AAUDIO_FORMAT_UNSPECIFIED),
// Recording in POWER_SAVING mode isn't supported, b/62291775.
std::make_tuple(
AAUDIO_SHARING_MODE_SHARED,
AAUDIO_UNSPECIFIED,
- AAUDIO_PERFORMANCE_MODE_LOW_LATENCY)),
+ AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
+ MMAP_ALLOWED,
+ AAUDIO_FORMAT_UNSPECIFIED),
+ std::make_tuple(
+ AAUDIO_SHARING_MODE_EXCLUSIVE,
+ AAUDIO_UNSPECIFIED,
+ AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
+ MMAP_ALLOWED,
+ AAUDIO_FORMAT_UNSPECIFIED)),
&getTestName);
@@ -224,7 +301,10 @@
// Callback function that fills the audio output buffer.
aaudio_data_callback_result_t AAudioOutputStreamCallbackTest::MyDataCallbackProc(
- AAudioStream *stream, void *userData, void *audioData, int32_t numFrames) {
+ AAudioStream *stream,
+ void *userData,
+ void *audioData,
+ int32_t numFrames) {
int32_t channelCount = AAudioStream_getChannelCount(stream);
int32_t numSamples = channelCount * numFrames;
if (AAudioStream_getFormat(stream) == AAUDIO_FORMAT_PCM_I16) {
@@ -247,7 +327,9 @@
if (!deviceSupportsFeature(FEATURE_PLAYBACK)) return;
mHelper.reset(new OutputStreamBuilderHelper(
std::get<PARAM_SHARING_MODE>(GetParam()),
- std::get<PARAM_PERF_MODE>(GetParam())));
+ std::get<PARAM_PERF_MODE>(GetParam()),
+ std::get<PARAM_AUDIO_FORMAT>(GetParam()))
+ );
mHelper->initBuilder();
int32_t framesPerDataCallback = std::get<PARAM_FRAMES_PER_CB>(GetParam());
@@ -259,6 +341,7 @@
}
mHelper->createAndVerifyStream(&mSetupSuccesful);
+
}
// Test Writing to an AAudioStream using a Callback
@@ -266,15 +349,15 @@
if (!mSetupSuccesful) return;
const int32_t framesPerDataCallback = std::get<PARAM_FRAMES_PER_CB>(GetParam());
- const int32_t actualFramesPerDataCallback = AAudioStream_getFramesPerDataCallback(stream());
+ const int32_t streamFramesPerDataCallback = AAudioStream_getFramesPerDataCallback(stream());
if (framesPerDataCallback != AAUDIO_UNSPECIFIED) {
- ASSERT_EQ(framesPerDataCallback, actualFramesPerDataCallback);
+ ASSERT_EQ(framesPerDataCallback, streamFramesPerDataCallback);
}
// Start/stop more than once to see if it fails after the first time.
// Write some data and measure the rate to see if the timing is OK.
for (int loopIndex = 0; loopIndex < 2; loopIndex++) {
- mCbData->reset(actualFramesPerDataCallback);
+ mCbData->reset(streamFramesPerDataCallback);
mHelper->startStream();
// See b/62090113. For legacy path, the device is only known after
@@ -297,8 +380,8 @@
sleep(1);
EXPECT_EQ(oldCallbackCount, mCbData->callbackCount); // expect not advancing
- if (framesPerDataCallback != AAUDIO_UNSPECIFIED) {
- ASSERT_EQ(framesPerDataCallback, mCbData->actualFramesPerCallback);
+ if (streamFramesPerDataCallback != AAUDIO_UNSPECIFIED) {
+ ASSERT_EQ(streamFramesPerDataCallback, mCbData->actualFramesPerCallback);
}
EXPECT_GE(mCbData->minLatency, 1); // Absurdly low
@@ -318,17 +401,39 @@
std::make_tuple(
AAUDIO_SHARING_MODE_SHARED,
AAUDIO_UNSPECIFIED,
- AAUDIO_PERFORMANCE_MODE_NONE),
- // cb buffer size: arbitrary prime number < 192
- std::make_tuple(AAUDIO_SHARING_MODE_SHARED, 109, AAUDIO_PERFORMANCE_MODE_NONE),
+ AAUDIO_PERFORMANCE_MODE_NONE,
+ MMAP_ALLOWED,
+ AAUDIO_FORMAT_UNSPECIFIED),
+ // cb buffer size: arbitrary prime number < 96
+ std::make_tuple(AAUDIO_SHARING_MODE_SHARED, 67, AAUDIO_PERFORMANCE_MODE_NONE, MMAP_ALLOWED,
+ AAUDIO_FORMAT_UNSPECIFIED),
+ std::make_tuple(AAUDIO_SHARING_MODE_SHARED, 67, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY, MMAP_ALLOWED,
+ AAUDIO_FORMAT_UNSPECIFIED),
+ std::make_tuple(AAUDIO_SHARING_MODE_EXCLUSIVE, 67, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY, MMAP_ALLOWED,
+ AAUDIO_FORMAT_UNSPECIFIED),
+ std::make_tuple(AAUDIO_SHARING_MODE_SHARED, 67, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY, MMAP_NOT_ALLOWED,
+ AAUDIO_FORMAT_PCM_I16),
+ std::make_tuple(AAUDIO_SHARING_MODE_SHARED, 67, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY, MMAP_NOT_ALLOWED,
+ AAUDIO_FORMAT_PCM_FLOAT),
// cb buffer size: arbitrary prime number > 192
- std::make_tuple(AAUDIO_SHARING_MODE_SHARED, 223, AAUDIO_PERFORMANCE_MODE_NONE),
+ std::make_tuple(AAUDIO_SHARING_MODE_SHARED, 223, AAUDIO_PERFORMANCE_MODE_NONE, MMAP_ALLOWED,
+ AAUDIO_FORMAT_UNSPECIFIED),
std::make_tuple(
AAUDIO_SHARING_MODE_SHARED,
AAUDIO_UNSPECIFIED,
- AAUDIO_PERFORMANCE_MODE_POWER_SAVING),
+ AAUDIO_PERFORMANCE_MODE_POWER_SAVING,
+ MMAP_ALLOWED,
+ AAUDIO_FORMAT_UNSPECIFIED),
std::make_tuple(
AAUDIO_SHARING_MODE_SHARED,
AAUDIO_UNSPECIFIED,
- AAUDIO_PERFORMANCE_MODE_LOW_LATENCY)),
+ AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
+ MMAP_ALLOWED,
+ AAUDIO_FORMAT_UNSPECIFIED),
+ std::make_tuple(
+ AAUDIO_SHARING_MODE_EXCLUSIVE,
+ AAUDIO_UNSPECIFIED,
+ AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
+ MMAP_ALLOWED,
+ AAUDIO_FORMAT_UNSPECIFIED)),
&getTestName);
diff --git a/tests/tests/nativemedia/aaudio/jni/test_aaudio_mmap.cpp b/tests/tests/nativemedia/aaudio/jni/test_aaudio_mmap.cpp
index fd67644..f08e1df 100644
--- a/tests/tests/nativemedia/aaudio/jni/test_aaudio_mmap.cpp
+++ b/tests/tests/nativemedia/aaudio/jni/test_aaudio_mmap.cpp
@@ -28,43 +28,9 @@
#include "test_aaudio.h"
#include "utils.h"
-/* These definitions are from aaudio/AAudioTesting.h */
-enum {
- AAUDIO_POLICY_NEVER = 1,
- AAUDIO_POLICY_AUTO,
- AAUDIO_POLICY_ALWAYS
-};
-typedef int32_t aaudio_policy_t;
-
-static aaudio_result_t (*s_setMMapPolicy)(aaudio_policy_t policy) = nullptr;
-static aaudio_policy_t (*s_getMMapPolicy)() = nullptr;
-
-/**
- * @return integer value or -1 on error
- */
-static int getSystemPropertyInt(const char *propName, int defaultValue) {
- char valueText[PROP_VALUE_MAX] = {'\0'};
- if (__system_property_get(propName, valueText) <= 0) {
- return defaultValue;
- }
- char *endptr = nullptr;
- int value = strtol(valueText, &endptr, 10);
- if (endptr == nullptr || *endptr != '\0') {
- __android_log_print(ANDROID_LOG_ERROR, LOG_TAG,
- "getSystemPropertyInt() - non-integer value = %s", valueText);
- return -1;
- } else {
- return value;
- }
-}
-
-static int getSystemMMapPolicy() {
- return getSystemPropertyInt("aaudio.mmap_policy", AAUDIO_UNSPECIFIED);
-}
-
// Test allowed values of policy.
TEST(test_aaudio_mmap, testCurrentPolicy) {
- aaudio_policy_t policy = getSystemMMapPolicy();
+ aaudio_policy_t policy = (aaudio_policy_t) AAudioExtensions::getMMapPolicyProperty();
// It must be one of these defined enum values.
EXPECT_TRUE(policy == AAUDIO_UNSPECIFIED
@@ -78,19 +44,6 @@
EXPECT_NE(AAUDIO_POLICY_ALWAYS, policy);
}
-// Link to test functions in shared library.
-static void loadMMapTestFunctions() {
- if (s_setMMapPolicy != nullptr) return; // already loaded
-
- void *handle;
- handle = dlopen("libaaudio.so", RTLD_NOW);
- EXPECT_NE(nullptr, handle);
- s_setMMapPolicy = (int (*)(int)) dlsym(handle, "AAudio_setMMapPolicy");
- EXPECT_NE(nullptr, s_setMMapPolicy);
- s_getMMapPolicy = (int (*)()) dlsym(handle, "AAudio_getMMapPolicy");
- EXPECT_NE(nullptr, s_getMMapPolicy);
-}
-
// An application should not be able to create an MMAP stream
// by enabling MMAP when the system "aaudio.mmap_policy" says not to.
TEST(test_aaudio_mmap, testElevatingMMapPolicy) {
@@ -98,12 +51,8 @@
AAudioStreamBuilder *builder = nullptr;
AAudioStream *stream = nullptr;
- aaudio_policy_t policy = getSystemMMapPolicy();
- bool mmapAllowed = (policy == AAUDIO_POLICY_AUTO || policy == AAUDIO_POLICY_ALWAYS);
+ bool mmapAllowed = AAudioExtensions::getInstance().isMMapSupported();
if (mmapAllowed) return;
- // Try to enable MMAP when not allowed.
-
- loadMMapTestFunctions();
EXPECT_EQ(AAUDIO_OK, AAudio_createStreamBuilder(&builder));
@@ -111,10 +60,10 @@
AAudioStreamBuilder_setPerformanceMode(builder, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY);
// Force policy to create an MMAP stream or fail.
- aaudio_policy_t originalPolicy = s_getMMapPolicy();
- s_setMMapPolicy(AAUDIO_POLICY_ALWAYS); // try to enable MMAP mode
+ aaudio_policy_t originalPolicy = AAudioExtensions::getInstance().getMMapPolicy();
+ AAudioExtensions::getInstance().setMMapPolicy(AAUDIO_POLICY_ALWAYS); // try to enable MMAP mode
result = AAudioStreamBuilder_openStream(builder, &stream);
- s_setMMapPolicy(originalPolicy);
+ AAudioExtensions::getInstance().setMMapPolicy(originalPolicy);
// openStream should have failed.
EXPECT_NE(AAUDIO_OK, result);
diff --git a/tests/tests/nativemedia/aaudio/jni/utils.cpp b/tests/tests/nativemedia/aaudio/jni/utils.cpp
index d843614..a9ed984 100644
--- a/tests/tests/nativemedia/aaudio/jni/utils.cpp
+++ b/tests/tests/nativemedia/aaudio/jni/utils.cpp
@@ -149,7 +149,9 @@
ASSERT_LE(mActual.channelCount, 16); // TODO what is min/max?
mActual.dataFormat = AAudioStream_getFormat(mStream);
- ASSERT_EQ(AAUDIO_FORMAT_PCM_I16, mActual.dataFormat);
+ if (mRequested.dataFormat != AAUDIO_FORMAT_UNSPECIFIED) {
+ ASSERT_EQ(mRequested.dataFormat, mActual.dataFormat);
+ }
mActual.perfMode = AAudioStream_getPerformanceMode(mStream);
if (mRequested.perfMode != AAUDIO_PERFORMANCE_MODE_NONE
@@ -192,17 +194,20 @@
ASSERT_EQ(toState, state);
}
-
InputStreamBuilderHelper::InputStreamBuilderHelper(
- aaudio_sharing_mode_t requestedSharingMode, aaudio_performance_mode_t requestedPerfMode)
+ aaudio_sharing_mode_t requestedSharingMode,
+ aaudio_performance_mode_t requestedPerfMode,
+ aaudio_format_t requestedFormat)
: StreamBuilderHelper{AAUDIO_DIRECTION_INPUT,
- 48000, 1, AAUDIO_FORMAT_PCM_I16, requestedSharingMode, requestedPerfMode} {}
+ 48000, 1, requestedFormat, requestedSharingMode, requestedPerfMode} {}
OutputStreamBuilderHelper::OutputStreamBuilderHelper(
- aaudio_sharing_mode_t requestedSharingMode, aaudio_performance_mode_t requestedPerfMode)
+ aaudio_sharing_mode_t requestedSharingMode,
+ aaudio_performance_mode_t requestedPerfMode,
+ aaudio_format_t requestedFormat)
: StreamBuilderHelper{AAUDIO_DIRECTION_OUTPUT,
- 48000, 2, AAUDIO_FORMAT_PCM_I16, requestedSharingMode, requestedPerfMode} {}
+ 48000, 2, requestedFormat, requestedSharingMode, requestedPerfMode} {}
void OutputStreamBuilderHelper::initBuilder() {
StreamBuilderHelper::initBuilder();
@@ -215,3 +220,51 @@
ASSERT_GE(AAudioStream_getBufferCapacityInFrames(mStream), kBufferCapacityFrames);
}
}
+
+AAudioExtensions::AAudioExtensions()
+ : mMMapSupported(isPolicyEnabled(getMMapPolicyProperty()))
+ , mMMapExclusiveSupported(isPolicyEnabled(getIntegerProperty(
+ "aaudio.mmap_exclusive_policy", AAUDIO_POLICY_UNSPECIFIED))) {
+ loadLibrary();
+}
+
+int AAudioExtensions::getIntegerProperty(const char *name, int defaultValue) {
+ int result = defaultValue;
+ char valueText[PROP_VALUE_MAX] = {0};
+ if (__system_property_get(name, valueText) != 0) {
+ result = atoi(valueText);
+ }
+ return result;
+}
+
+// This should only be called once from the constructor.
+bool AAudioExtensions::loadLibrary() {
+ mLibHandle = dlopen(LIB_AAUDIO_NAME, 0);
+ if (mLibHandle == nullptr) {
+ //LOGI("%s() could not find " LIB_AAUDIO_NAME, __func__);
+ return false;
+ }
+
+ mAAudioStream_isMMap = (bool (*)(AAudioStream *stream))
+ dlsym(mLibHandle, FUNCTION_IS_MMAP);
+ if (mAAudioStream_isMMap == nullptr) {
+ //LOGI("%s() could not find " FUNCTION_IS_MMAP, __func__);
+ return false;
+ }
+
+ mAAudio_setMMapPolicy = (int32_t (*)(aaudio_policy_t policy))
+ dlsym(mLibHandle, FUNCTION_SET_MMAP_POLICY);
+ if (mAAudio_setMMapPolicy == nullptr) {
+ //LOGI("%s() could not find " FUNCTION_SET_MMAP_POLICY, __func__);
+ return false;
+ }
+
+ mAAudio_getMMapPolicy = (aaudio_policy_t (*)())
+ dlsym(mLibHandle, FUNCTION_GET_MMAP_POLICY);
+ if (mAAudio_getMMapPolicy == nullptr) {
+ //LOGI("%s() could not find " FUNCTION_GET_MMAP_POLICY, __func__);
+ return false;
+ }
+ mFunctionsLoaded = true;
+ return mFunctionsLoaded;
+}
\ No newline at end of file
diff --git a/tests/tests/nativemedia/aaudio/jni/utils.h b/tests/tests/nativemedia/aaudio/jni/utils.h
index 4211410..f14e04d 100644
--- a/tests/tests/nativemedia/aaudio/jni/utils.h
+++ b/tests/tests/nativemedia/aaudio/jni/utils.h
@@ -16,7 +16,9 @@
#ifndef CTS_MEDIA_TEST_AAUDIO_UTILS_H
#define CTS_MEDIA_TEST_AAUDIO_UTILS_H
+#include <dlfcn.h>
#include <map>
+#include <sys/system_properties.h>
#include <aaudio/AAudio.h>
@@ -88,14 +90,16 @@
public:
InputStreamBuilderHelper(
aaudio_sharing_mode_t requestedSharingMode,
- aaudio_performance_mode_t requestedPerfMode);
+ aaudio_performance_mode_t requestedPerfMode,
+ aaudio_format_t requestedFormat = AAUDIO_FORMAT_PCM_FLOAT);
};
class OutputStreamBuilderHelper : public StreamBuilderHelper {
public:
OutputStreamBuilderHelper(
aaudio_sharing_mode_t requestedSharingMode,
- aaudio_performance_mode_t requestedPerfMode);
+ aaudio_performance_mode_t requestedPerfMode,
+ aaudio_format_t requestedFormat = AAUDIO_FORMAT_PCM_I16);
void initBuilder();
void createAndVerifyStream(bool *success);
@@ -103,4 +107,91 @@
const int32_t kBufferCapacityFrames = 2000;
};
+
+#define LIB_AAUDIO_NAME "libaaudio.so"
+#define FUNCTION_IS_MMAP "AAudioStream_isMMapUsed"
+#define FUNCTION_SET_MMAP_POLICY "AAudio_setMMapPolicy"
+#define FUNCTION_GET_MMAP_POLICY "AAudio_getMMapPolicy"
+
+enum {
+ AAUDIO_POLICY_UNSPECIFIED = 0,
+/* These definitions are from aaudio/AAudioTesting.h */
+ AAUDIO_POLICY_NEVER = 1,
+ AAUDIO_POLICY_AUTO = 2,
+ AAUDIO_POLICY_ALWAYS = 3
+};
+typedef int32_t aaudio_policy_t;
+
+/**
+ * Call some AAudio test routines that are not part of the normal API.
+ */
+class AAudioExtensions {
+public:
+ AAudioExtensions();
+
+ static bool isPolicyEnabled(int32_t policy) {
+ return (policy == AAUDIO_POLICY_AUTO || policy == AAUDIO_POLICY_ALWAYS);
+ }
+
+ static AAudioExtensions &getInstance() {
+ static AAudioExtensions instance;
+ return instance;
+ }
+
+ static int getMMapPolicyProperty() {
+ return getIntegerProperty("aaudio.mmap_policy", AAUDIO_POLICY_UNSPECIFIED);
+ }
+
+ aaudio_policy_t getMMapPolicy() {
+ if (!mFunctionsLoaded) return -1;
+ return mAAudio_getMMapPolicy();
+ }
+
+ int32_t setMMapPolicy(aaudio_policy_t policy) {
+ if (!mFunctionsLoaded) return -1;
+ return mAAudio_setMMapPolicy(policy);
+ }
+
+ bool isMMapUsed(AAudioStream *aaudioStream) {
+ if (!mFunctionsLoaded) return false;
+ return mAAudioStream_isMMap(aaudioStream);
+ }
+
+ int32_t setMMapEnabled(bool enabled) {
+ return setMMapPolicy(enabled ? AAUDIO_POLICY_AUTO : AAUDIO_POLICY_NEVER);
+ }
+
+ bool isMMapEnabled() {
+ return isPolicyEnabled(mAAudio_getMMapPolicy());
+ }
+
+ bool isMMapSupported() const {
+ return mMMapSupported;
+ }
+
+ bool isMMapExclusiveSupported() const {
+ return mMMapExclusiveSupported;
+ }
+
+private:
+
+ static int getIntegerProperty(const char *name, int defaultValue);
+
+ /**
+ * Load some AAudio test functions.
+ * This should only be called once from the constructor.
+ * @return true if it succeeds
+ */
+ bool loadLibrary();
+
+ bool mFunctionsLoaded = false;
+ void *mLibHandle = nullptr;
+ bool (*mAAudioStream_isMMap)(AAudioStream *stream) = nullptr;
+ int32_t (*mAAudio_setMMapPolicy)(aaudio_policy_t policy) = nullptr;
+ aaudio_policy_t (*mAAudio_getMMapPolicy)() = nullptr;
+
+ const bool mMMapSupported;
+ const bool mMMapExclusiveSupported;
+};
+
#endif // CTS_MEDIA_TEST_AAUDIO_UTILS_H
diff --git a/tests/tests/ndef/OWNERS b/tests/tests/ndef/OWNERS
index 8ffbd10..d92b2ab 100644
--- a/tests/tests/ndef/OWNERS
+++ b/tests/tests/ndef/OWNERS
@@ -1,3 +1,5 @@
# Bug component: 48448
-rmojumder@google.com
+alisher@google.com
+jackcwyu@google.com
+georgekgchang@google.com
zachoverflow@google.com
diff --git a/tests/tests/net/src/android/net/cts/ConnectivityDiagnosticsManagerTest.java b/tests/tests/net/src/android/net/cts/ConnectivityDiagnosticsManagerTest.java
new file mode 100644
index 0000000..0a80047
--- /dev/null
+++ b/tests/tests/net/src/android/net/cts/ConnectivityDiagnosticsManagerTest.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.cts;
+
+import static android.net.ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+
+import android.content.Context;
+import android.net.ConnectivityDiagnosticsManager;
+import android.net.NetworkRequest;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.Executor;
+
+@RunWith(AndroidJUnit4.class)
+public class ConnectivityDiagnosticsManagerTest {
+ private static final Executor INLINE_EXECUTOR = x -> x.run();
+ private static final NetworkRequest DEFAULT_REQUEST = new NetworkRequest.Builder().build();
+
+ private Context mContext;
+ private ConnectivityDiagnosticsManager mCdm;
+ private ConnectivityDiagnosticsCallback mCallback;
+
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getContext();
+ mCdm = mContext.getSystemService(ConnectivityDiagnosticsManager.class);
+
+ mCallback = new ConnectivityDiagnosticsCallback() {};
+ }
+
+ @Test
+ public void testRegisterConnectivityDiagnosticsCallback() {
+ mCdm.registerConnectivityDiagnosticsCallback(DEFAULT_REQUEST, INLINE_EXECUTOR, mCallback);
+ }
+
+ @Test
+ public void testRegisterDuplicateConnectivityDiagnosticsCallback() {
+ mCdm.registerConnectivityDiagnosticsCallback(DEFAULT_REQUEST, INLINE_EXECUTOR, mCallback);
+
+ try {
+ mCdm.registerConnectivityDiagnosticsCallback(
+ DEFAULT_REQUEST, INLINE_EXECUTOR, mCallback);
+ fail("Registering the same callback twice should throw an IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ @Test
+ public void testUnregisterConnectivityDiagnosticsCallback() {
+ mCdm.registerConnectivityDiagnosticsCallback(DEFAULT_REQUEST, INLINE_EXECUTOR, mCallback);
+ mCdm.unregisterConnectivityDiagnosticsCallback(mCallback);
+ }
+
+ @Test
+ public void testUnregisterUnknownConnectivityDiagnosticsCallback() {
+ // Expected to silently ignore the unregister() call
+ mCdm.unregisterConnectivityDiagnosticsCallback(mCallback);
+ }
+}
diff --git a/tests/tests/net/src/android/net/cts/DnsResolverTest.java b/tests/tests/net/src/android/net/cts/DnsResolverTest.java
index c32a7a0..1cc49f9 100644
--- a/tests/tests/net/src/android/net/cts/DnsResolverTest.java
+++ b/tests/tests/net/src/android/net/cts/DnsResolverTest.java
@@ -86,7 +86,7 @@
static final int CANCEL_RETRY_TIMES = 5;
static final int QUERY_TIMES = 10;
static final int NXDOMAIN = 3;
- static final int PRIVATE_DNS_SETTING_TIMEOUT_MS = 2_000;
+ static final int PRIVATE_DNS_SETTING_TIMEOUT_MS = 6_000;
private ContentResolver mCR;
private ConnectivityManager mCM;
@@ -122,10 +122,15 @@
mOldDnsSpecifier = Settings.Global.getString(mCR, Settings.Global.PRIVATE_DNS_SPECIFIER);
}
- private void restorePrivateDnsSetting() {
+ private void restorePrivateDnsSetting() throws InterruptedException {
// restore private DNS setting
Settings.Global.putString(mCR, Settings.Global.PRIVATE_DNS_MODE, mOldMode);
- Settings.Global.putString(mCR, Settings.Global.PRIVATE_DNS_SPECIFIER, mOldDnsSpecifier);
+ if ("hostname".equals(mOldMode)) {
+ Settings.Global.putString(
+ mCR, Settings.Global.PRIVATE_DNS_SPECIFIER, mOldDnsSpecifier);
+ mCtsNetUtils.awaitPrivateDnsSetting("restorePrivateDnsSetting timeout",
+ mCM.getActiveNetwork(), mOldDnsSpecifier, PRIVATE_DNS_SETTING_TIMEOUT_MS, true);
+ }
}
private static String byteArrayToHexString(byte[] bytes) {
@@ -203,6 +208,7 @@
private final CancellationSignal mCancelSignal;
private int mRcode;
private DnsAnswer mDnsAnswer;
+ private String mErrorMsg = null;
VerifyCancelCallback(@NonNull String msg, @Nullable CancellationSignal cancel) {
mMsg = msg;
@@ -228,14 +234,18 @@
@Override
public void onAnswer(@NonNull byte[] answer, int rcode) {
if (mCancelSignal != null && mCancelSignal.isCanceled()) {
- fail(mMsg + " should not have returned any answers");
+ mErrorMsg = mMsg + " should not have returned any answers";
+ mLatch.countDown();
+ return;
}
mRcode = rcode;
try {
mDnsAnswer = new DnsAnswer(answer);
} catch (ParseException | DnsParseException e) {
- fail(mMsg + e.getMessage());
+ mErrorMsg = mMsg + e.getMessage();
+ mLatch.countDown();
+ return;
}
Log.d(TAG, "Reported blob: " + byteArrayToHexString(answer));
mLatch.countDown();
@@ -243,10 +253,12 @@
@Override
public void onError(@NonNull DnsResolver.DnsException error) {
- fail(mMsg + error.getMessage());
+ mErrorMsg = mMsg + error.getMessage();
+ mLatch.countDown();
}
private void assertValidAnswer() {
+ assertNull(mErrorMsg);
assertNotNull(mMsg + " No valid answer", mDnsAnswer);
assertEquals(mMsg + " Unexpected error: reported rcode" + mRcode +
" blob's rcode " + mDnsAnswer.getRcode(), mRcode, mDnsAnswer.getRcode());
@@ -402,20 +414,18 @@
public void doTestRawQueryNXDomainWithPrivateDns(Executor executor)
throws InterruptedException {
final String msg = "RawQuery " + TEST_NX_DOMAIN + " with private DNS";
-
// Enable private DNS strict mode and set server to dns.google before doing NxDomain test.
// b/144521720
Settings.Global.putString(mCR, Settings.Global.PRIVATE_DNS_MODE, "hostname");
Settings.Global.putString(mCR,
Settings.Global.PRIVATE_DNS_SPECIFIER, GOOGLE_PRIVATE_DNS_SERVER);
-
for (Network network : getTestableNetworks()) {
final Network networkForPrivateDns =
(network != null) ? network : mCM.getActiveNetwork();
assertNotNull("Can't find network to await private DNS on", networkForPrivateDns);
mCtsNetUtils.awaitPrivateDnsSetting(msg + " wait private DNS setting timeout",
networkForPrivateDns, GOOGLE_PRIVATE_DNS_SERVER,
- PRIVATE_DNS_SETTING_TIMEOUT_MS);
+ PRIVATE_DNS_SETTING_TIMEOUT_MS, true);
final VerifyCancelCallback callback = new VerifyCancelCallback(msg);
mDns.rawQuery(network, TEST_NX_DOMAIN, CLASS_IN, TYPE_AAAA, FLAG_NO_CACHE_LOOKUP,
executor, null, callback);
@@ -508,6 +518,7 @@
private final String mMsg;
private final List<InetAddress> mAnswers;
private final CancellationSignal mCancelSignal;
+ private String mErrorMsg = null;
VerifyCancelInetAddressCallback(@NonNull String msg, @Nullable CancellationSignal cancel) {
this.mMsg = msg;
@@ -541,10 +552,16 @@
return false;
}
+ public void assertNoError() {
+ assertNull(mErrorMsg);
+ }
+
@Override
public void onAnswer(@NonNull List<InetAddress> answerList, int rcode) {
if (mCancelSignal != null && mCancelSignal.isCanceled()) {
- fail(mMsg + " should not have returned any answers");
+ mErrorMsg = mMsg + " should not have returned any answers";
+ mLatch.countDown();
+ return;
}
for (InetAddress addr : answerList) {
Log.d(TAG, "Reported addr: " + addr.toString());
@@ -556,7 +573,7 @@
@Override
public void onError(@NonNull DnsResolver.DnsException error) {
- fail(mMsg + error.getMessage());
+ mErrorMsg = mMsg + error.getMessage();
}
}
@@ -601,6 +618,7 @@
assertTrue(msg + " but no answer after " + TIMEOUT_MS + "ms.",
callback.waitForAnswer());
+ callback.assertNoError();
assertTrue(msg + " returned 0 results", !callback.isAnswerEmpty());
}
}
@@ -644,6 +662,7 @@
assertTrue(msg + " but no answer after " + TIMEOUT_MS + "ms.",
callback.waitForAnswer());
+ callback.assertNoError();
assertTrue(msg + " returned 0 results", !callback.isAnswerEmpty());
assertTrue(msg + " returned Ipv6 results", !callback.hasIpv6Answer());
}
@@ -659,6 +678,7 @@
assertTrue(msg + " but no answer after " + TIMEOUT_MS + "ms.",
callback.waitForAnswer());
+ callback.assertNoError();
assertTrue(msg + " returned 0 results", !callback.isAnswerEmpty());
assertTrue(msg + " returned Ipv4 results", !callback.hasIpv4Answer());
}
@@ -671,7 +691,6 @@
Settings.Global.putString(mCR, Settings.Global.PRIVATE_DNS_MODE, "hostname");
Settings.Global.putString(mCR,
Settings.Global.PRIVATE_DNS_SPECIFIER, INVALID_PRIVATE_DNS_SERVER);
-
final String msg = "Test PrivateDnsBypass " + TEST_DOMAIN;
for (Network network : testNetworks) {
// This test cannot be ran with null network because we need to explicitly pass a
@@ -680,7 +699,7 @@
// wait for private DNS setting propagating
mCtsNetUtils.awaitPrivateDnsSetting(msg + " wait private DNS setting timeout",
- network, INVALID_PRIVATE_DNS_SERVER, PRIVATE_DNS_SETTING_TIMEOUT_MS);
+ network, INVALID_PRIVATE_DNS_SERVER, PRIVATE_DNS_SETTING_TIMEOUT_MS, false);
final CountDownLatch latch = new CountDownLatch(1);
final DnsResolver.Callback<List<InetAddress>> errorCallback =
@@ -712,6 +731,7 @@
assertTrue(msg + " bypass private DNS round. No answer after " + TIMEOUT_MS + "ms.",
callback.waitForAnswer());
+ callback.assertNoError();
assertTrue(msg + " returned 0 results", !callback.isAnswerEmpty());
// To ensure private DNS bypass still work even if passing null network.
@@ -724,6 +744,7 @@
assertTrue(msg + " with null network bypass private DNS round. No answer after " +
TIMEOUT_MS + "ms.", callbackWithNullNetwork.waitForAnswer());
+ callbackWithNullNetwork.assertNoError();
assertTrue(msg + " with null network returned 0 results",
!callbackWithNullNetwork.isAnswerEmpty());
@@ -745,6 +766,7 @@
assertTrue(msg + " but no answer after " + TIMEOUT_MS + "ms.",
callback.waitForAnswer());
+ callback.assertNoError();
assertTrue(msg + " returned 0 results", !callback.isAnswerEmpty());
assertTrue(msg + " returned " + (queryV6 ? "Ipv4" : "Ipv6") + " results",
queryV6 ? !callback.hasIpv4Answer() : !callback.hasIpv6Answer());
diff --git a/tests/tests/net/src/android/net/cts/MultinetworkApiTest.java b/tests/tests/net/src/android/net/cts/MultinetworkApiTest.java
index 766c55e..f123187 100644
--- a/tests/tests/net/src/android/net/cts/MultinetworkApiTest.java
+++ b/tests/tests/net/src/android/net/cts/MultinetworkApiTest.java
@@ -245,7 +245,7 @@
for (Network network : getTestableNetworks()) {
// Wait for private DNS setting to propagate.
mCtsNetUtils.awaitPrivateDnsSetting("NxDomain test wait private DNS setting timeout",
- network, GOOGLE_PRIVATE_DNS_SERVER, PRIVATE_DNS_SETTING_TIMEOUT_MS);
+ network, GOOGLE_PRIVATE_DNS_SERVER, PRIVATE_DNS_SETTING_TIMEOUT_MS, true);
runResNnxDomainCheck(network.getNetworkHandle());
}
} finally {
diff --git a/tests/tests/net/src/android/net/wifi/cts/WifiEnterpriseConfigTest.java b/tests/tests/net/src/android/net/wifi/cts/WifiEnterpriseConfigTest.java
index e4c4b00..45b9d97 100644
--- a/tests/tests/net/src/android/net/wifi/cts/WifiEnterpriseConfigTest.java
+++ b/tests/tests/net/src/android/net/wifi/cts/WifiEnterpriseConfigTest.java
@@ -16,17 +16,15 @@
package android.net.wifi.cts;
-import android.content.Context;
+import static com.google.common.truth.Truth.assertThat;
+
import android.content.pm.PackageManager;
import android.net.wifi.WifiEnterpriseConfig;
import android.net.wifi.WifiEnterpriseConfig.Eap;
import android.net.wifi.WifiEnterpriseConfig.Phase2;
-import android.net.wifi.WifiManager;
import android.platform.test.annotations.AppModeFull;
import android.test.AndroidTestCase;
-import com.android.compatibility.common.util.SystemUtil;
-
import java.io.ByteArrayInputStream;
import java.security.KeyFactory;
import java.security.PrivateKey;
@@ -36,9 +34,7 @@
@AppModeFull(reason = "Cannot get WifiManager in instant app mode")
public class WifiEnterpriseConfigTest extends AndroidTestCase {
- private WifiManager mWifiManager;
- private static final String SSID = "\"TestSSID\"";
private static final String IDENTITY = "identity";
private static final String PASSWORD = "password";
private static final String SUBJECT_MATCH = "subjectmatch";
@@ -47,7 +43,11 @@
private static final String PLMN = "plmn";
private static final String REALM = "realm";
private static final String ANON_IDENTITY = "anonidentity";
- private static final int ENABLE_DELAY = 10000;
+ private static final String CERTIFICATE_ALIAS1 = "certificatealias1";
+ private static final String CERTIFICATE_ALIAS2 = "certificatealias2";
+ private static final String CA_PATH = "capath";
+ private static final String CLIENT_CERTIFICATE_ALIAS = "clientcertificatealias";
+ private static final String WAPI_CERT_SUITE = "wapicertsuite";
/*
* The keys and certificates below are generated with:
@@ -684,22 +684,6 @@
PackageManager.FEATURE_WIFI);
}
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- if(!hasWifi()) {
- return;
- }
- mWifiManager = (WifiManager) mContext
- .getSystemService(Context.WIFI_SERVICE);
- assertNotNull(mWifiManager);
- SystemUtil.runShellCommand("svc wifi enable");
- Thread.sleep(ENABLE_DELAY);
- if (hasWifi()) {
- assertTrue(mWifiManager.isWifiEnabled());
- }
- }
-
public void testSettersAndGetters() throws Exception {
if (!hasWifi()) {
return;
@@ -766,6 +750,7 @@
assertTrue(testClientCertChain.length == 2);
assertTrue(testClientCertChain[0] == testClientCert);
assertTrue(testClientCertChain[1] == cert1);
+ assertSame(clientKey, config.getClientPrivateKey());
config.setSubjectMatch(SUBJECT_MATCH);
assertTrue(config.getSubjectMatch().equals(SUBJECT_MATCH));
@@ -793,4 +778,122 @@
assertTrue(stringRepresentation.contains(identity));
assertFalse(stringRepresentation.contains(password));
}
+
+ public void testGetSetCaCertificateAliases() {
+ if (!hasWifi()) {
+ return;
+ }
+ WifiEnterpriseConfig config = new WifiEnterpriseConfig();
+
+ config.setCaCertificateAliases(null);
+ assertThat(config.getCaCertificateAliases()).isNull();
+
+ config.setCaCertificateAliases(new String[]{CERTIFICATE_ALIAS1});
+ assertThat(config.getCaCertificateAliases()).isEqualTo(new String[]{CERTIFICATE_ALIAS1});
+
+ config.setCaCertificateAliases(new String[]{CERTIFICATE_ALIAS1, CERTIFICATE_ALIAS2});
+ assertThat(config.getCaCertificateAliases())
+ .isEqualTo(new String[]{CERTIFICATE_ALIAS1, CERTIFICATE_ALIAS2});
+ }
+
+ public void testGetSetCaPath() {
+ if (!hasWifi()) {
+ return;
+ }
+ WifiEnterpriseConfig config = new WifiEnterpriseConfig();
+
+ config.setCaPath("");
+ assertThat(config.getCaPath()).isEmpty();
+
+ config.setCaPath(CA_PATH);
+ assertThat(config.getCaPath()).isEqualTo(CA_PATH);
+ }
+
+ public void testGetSetClientCertificateAlias() {
+ if (!hasWifi()) {
+ return;
+ }
+ WifiEnterpriseConfig config = new WifiEnterpriseConfig();
+
+ config.setClientCertificateAlias("");
+ assertThat(config.getClientCertificateAlias()).isEmpty();
+
+ config.setClientCertificateAlias(CLIENT_CERTIFICATE_ALIAS);
+ assertThat(config.getClientCertificateAlias()).isEqualTo(CLIENT_CERTIFICATE_ALIAS);
+ }
+
+ public void testGetSetOcsp() {
+ if (!hasWifi()) {
+ return;
+ }
+ WifiEnterpriseConfig config = new WifiEnterpriseConfig();
+
+ config.setOcsp(WifiEnterpriseConfig.OCSP_NONE);
+ assertThat(config.getOcsp()).isEqualTo(WifiEnterpriseConfig.OCSP_NONE);
+
+ config.setOcsp(WifiEnterpriseConfig.OCSP_REQUIRE_ALL_NON_TRUSTED_CERTS_STATUS);
+ assertThat(config.getOcsp())
+ .isEqualTo(WifiEnterpriseConfig.OCSP_REQUIRE_ALL_NON_TRUSTED_CERTS_STATUS);
+
+ try {
+ config.setOcsp(-1);
+ fail("WifiEnterpriseConfig.setOcsp(-1) did not throw an IllegalArgumentException!");
+ } catch (IllegalArgumentException expected) {}
+ }
+
+ public void testGetSetWapiCertSuite() {
+ if (!hasWifi()) {
+ return;
+ }
+ WifiEnterpriseConfig config = new WifiEnterpriseConfig();
+
+ config.setWapiCertSuite("");
+ assertThat(config.getWapiCertSuite()).isEmpty();
+
+ config.setWapiCertSuite(WAPI_CERT_SUITE);
+ assertThat(config.getWapiCertSuite()).isEqualTo(WAPI_CERT_SUITE);
+ }
+
+ public void testIsAuthenticationSimBased() {
+ if (!hasWifi()) {
+ return;
+ }
+ WifiEnterpriseConfig config = new WifiEnterpriseConfig();
+
+ config.setEapMethod(Eap.AKA);
+ assertThat(config.isAuthenticationSimBased()).isTrue();
+
+ config.setEapMethod(Eap.PWD);
+ assertThat(config.isAuthenticationSimBased()).isFalse();
+
+ config.setEapMethod(Eap.PEAP);
+ config.setPhase2Method(Phase2.SIM);
+ assertThat(config.isAuthenticationSimBased()).isTrue();
+
+ config.setEapMethod(Eap.PEAP);
+ config.setPhase2Method(Phase2.NONE);
+ assertThat(config.isAuthenticationSimBased()).isFalse();
+ }
+
+ public void testCopyConstructor() {
+ if (!hasWifi()) {
+ return;
+ }
+ WifiEnterpriseConfig config = new WifiEnterpriseConfig();
+ config.setEapMethod(Eap.WAPI_CERT);
+ config.setWapiCertSuite(WAPI_CERT_SUITE);
+ config.setOcsp(WifiEnterpriseConfig.OCSP_REQUIRE_ALL_NON_TRUSTED_CERTS_STATUS);
+ config.setCaPath(CA_PATH);
+ config.setPassword(PASSWORD);
+ config.setRealm(REALM);
+
+ WifiEnterpriseConfig copy = new WifiEnterpriseConfig(config);
+ assertThat(copy.getEapMethod()).isEqualTo(Eap.WAPI_CERT);
+ assertThat(copy.getWapiCertSuite()).isEqualTo(WAPI_CERT_SUITE);
+ assertThat(copy.getOcsp())
+ .isEqualTo(WifiEnterpriseConfig.OCSP_REQUIRE_ALL_NON_TRUSTED_CERTS_STATUS);
+ assertThat(copy.getCaPath()).isEqualTo(CA_PATH);
+ assertThat(copy.getPassword()).isEqualTo(PASSWORD);
+ assertThat(copy.getRealm()).isEqualTo(REALM);
+ }
}
diff --git a/tests/tests/net/src/android/net/wifi/cts/WifiInfoTest.java b/tests/tests/net/src/android/net/wifi/cts/WifiInfoTest.java
index d943231..6f94fea 100644
--- a/tests/tests/net/src/android/net/wifi/cts/WifiInfoTest.java
+++ b/tests/tests/net/src/android/net/wifi/cts/WifiInfoTest.java
@@ -16,11 +16,13 @@
package android.net.wifi.cts;
+import static com.google.common.truth.Truth.assertThat;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.net.wifi.ScanResult;
import android.net.wifi.SupplicantState;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
@@ -78,13 +80,13 @@
mContext.registerReceiver(mReceiver, mIntentFilter);
mWifiManager = (WifiManager) getContext().getSystemService(Context.WIFI_SERVICE);
- assertNotNull(mWifiManager);
+ assertThat(mWifiManager).isNotNull();
mWifiLock = mWifiManager.createWifiLock(TAG);
mWifiLock.acquire();
if (!mWifiManager.isWifiEnabled())
setWifiEnabled(true);
Thread.sleep(DURATION);
- assertTrue(mWifiManager.isWifiEnabled());
+ assertThat(mWifiManager.isWifiEnabled()).isTrue();
mMySync.expectedState = STATE_NULL;
}
@@ -123,31 +125,19 @@
// skip the test if WiFi is not supported
return;
}
+
+ // wait for Wifi to be connected
+ PollingCheck.check(
+ "Wifi not connected - Please ensure there is a saved network in range of this "
+ + "device",
+ 20000,
+ () -> mWifiManager.getConnectionInfo().getNetworkId() != -1);
+
// this test case should in Wifi environment
WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
- assertNotNull(wifiInfo);
- assertNotNull(wifiInfo.toString());
- SupplicantState.isValidState(wifiInfo.getSupplicantState());
- WifiInfo.getDetailedStateOf(SupplicantState.DISCONNECTED);
- String ssid = wifiInfo.getSSID();
- if (!ssid.startsWith("0x") && !ssid.equals(WifiManager.UNKNOWN_SSID)) {
- // Non-hex string should be quoted
- assertTrue(ssid.charAt(0) == '"');
- assertTrue(ssid.charAt(ssid.length() - 1) == '"');
- }
+ testWifiInfoPropertiesWhileConnected(wifiInfo);
- wifiInfo.getBSSID();
- wifiInfo.getFrequency();
- wifiInfo.getIpAddress();
- wifiInfo.getLinkSpeed();
- wifiInfo.getPasspointFqdn();
- wifiInfo.getPasspointProviderFriendlyName();
- wifiInfo.getTxLinkSpeedMbps();
- wifiInfo.getRxLinkSpeedMbps();
- wifiInfo.getRssi();
- wifiInfo.getHiddenSSID();
- wifiInfo.getMacAddress();
setWifiEnabled(false);
PollingCheck.check("getNetworkId not -1", 20000, new Callable<Boolean>() {
@@ -166,4 +156,55 @@
});
}
+ private void testWifiInfoPropertiesWhileConnected(WifiInfo wifiInfo) {
+ assertThat(wifiInfo).isNotNull();
+ assertThat(wifiInfo.toString()).isNotNull();
+ SupplicantState.isValidState(wifiInfo.getSupplicantState());
+ WifiInfo.getDetailedStateOf(SupplicantState.DISCONNECTED);
+ String ssid = wifiInfo.getSSID();
+ if (!ssid.startsWith("0x") && !ssid.equals(WifiManager.UNKNOWN_SSID)) {
+ // Non-hex string should be quoted
+ assertThat(ssid).startsWith("\"");
+ assertThat(ssid).endsWith("\"");
+ }
+
+ assertThat(wifiInfo.getBSSID()).isNotNull();
+ assertThat(wifiInfo.getFrequency()).isGreaterThan(0);
+ assertThat(wifiInfo.getMacAddress()).isNotNull();
+
+ wifiInfo.getRssi();
+ wifiInfo.getIpAddress();
+ wifiInfo.getHiddenSSID();
+ wifiInfo.getScore();
+
+ // null for saved networks
+ assertThat(wifiInfo.getRequestingPackageName()).isNull();
+ assertThat(wifiInfo.getPasspointFqdn()).isNull();
+ assertThat(wifiInfo.getPasspointProviderFriendlyName()).isNull();
+
+ // false for saved networks
+ assertThat(wifiInfo.isEphemeral()).isFalse();
+ assertThat(wifiInfo.isOsuAp()).isFalse();
+ assertThat(wifiInfo.isPasspointAp()).isFalse();
+
+ assertThat(wifiInfo.getWifiStandard()).isAnyOf(
+ ScanResult.WIFI_STANDARD_UNKNOWN,
+ ScanResult.WIFI_STANDARD_LEGACY,
+ ScanResult.WIFI_STANDARD_11N,
+ ScanResult.WIFI_STANDARD_11AC,
+ ScanResult.WIFI_STANDARD_11AX
+ );
+
+ assertThat(wifiInfo.getLostTxPacketsPerSecond()).isAtLeast(0.0);
+ assertThat(wifiInfo.getRetriedTxPacketsPerSecond()).isAtLeast(0.0);
+ assertThat(wifiInfo.getSuccessfulRxPacketsPerSecond()).isAtLeast(0.0);
+ assertThat(wifiInfo.getSuccessfulTxPacketsPerSecond()).isAtLeast(0.0);
+
+ // Can be -1 if link speed is unknown
+ assertThat(wifiInfo.getLinkSpeed()).isAtLeast(-1);
+ assertThat(wifiInfo.getTxLinkSpeedMbps()).isAtLeast(-1);
+ assertThat(wifiInfo.getRxLinkSpeedMbps()).isAtLeast(-1);
+ assertThat(wifiInfo.getMaxSupportedTxLinkSpeedMbps()).isAtLeast(-1);
+ assertThat(wifiInfo.getMaxSupportedRxLinkSpeedMbps()).isAtLeast(-1);
+ }
}
diff --git a/tests/tests/net/util/java/android/net/cts/util/CtsNetUtils.java b/tests/tests/net/util/java/android/net/cts/util/CtsNetUtils.java
index f0c34e3..6214f89 100644
--- a/tests/tests/net/util/java/android/net/cts/util/CtsNetUtils.java
+++ b/tests/tests/net/util/java/android/net/cts/util/CtsNetUtils.java
@@ -56,6 +56,7 @@
private static final String TAG = CtsNetUtils.class.getSimpleName();
private static final int DURATION = 10000;
private static final int SOCKET_TIMEOUT_MS = 2000;
+ private static final int PRIVATE_DNS_PROBE_MS = 1_000;
public static final int HTTP_PORT = 80;
public static final String TEST_HOST = "connectivitycheck.gstatic.com";
@@ -246,12 +247,16 @@
}
public void awaitPrivateDnsSetting(@NonNull String msg, @NonNull Network network,
- @NonNull String server, int timeoutMs) throws InterruptedException {
+ @NonNull String server, int timeoutMs,
+ boolean requiresValidatedServers) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(1);
NetworkRequest request = new NetworkRequest.Builder().clearCapabilities().build();
NetworkCallback callback = new NetworkCallback() {
@Override
public void onLinkPropertiesChanged(Network n, LinkProperties lp) {
+ if (requiresValidatedServers && lp.getValidatedPrivateDnsServers().isEmpty()) {
+ return;
+ }
if (network.equals(n) && server.equals(lp.getPrivateDnsServerName())) {
latch.countDown();
}
@@ -260,6 +265,18 @@
mCm.registerNetworkCallback(request, callback);
assertTrue(msg, latch.await(timeoutMs, TimeUnit.MILLISECONDS));
mCm.unregisterNetworkCallback(callback);
+ // Wait some time for NetworkMonitor's private DNS probe to complete. If we do not do
+ // this, then the test could complete before the NetworkMonitor private DNS probe
+ // completes. This would result in tearDown disabling private DNS, and the NetworkMonitor
+ // private DNS probe getting stuck because there are no longer any private DNS servers to
+ // query. This then results in the next test not being able to change the private DNS
+ // setting within the timeout, because the NetworkMonitor thread is blocked in the
+ // private DNS probe. There is no way to know when the probe has completed: because the
+ // network is likely already validated, there is no callback that we can listen to, so
+ // just sleep.
+ if (requiresValidatedServers) {
+ Thread.sleep(PRIVATE_DNS_PROBE_MS);
+ }
}
/**
diff --git a/tests/tests/notificationlegacy/notificationlegacy20/src/android/app/notification/legacy20/cts/LegacyNotificationManager20Test.java b/tests/tests/notificationlegacy/notificationlegacy20/src/android/app/notification/legacy20/cts/LegacyNotificationManager20Test.java
index c64f07f..803f55e 100644
--- a/tests/tests/notificationlegacy/notificationlegacy20/src/android/app/notification/legacy20/cts/LegacyNotificationManager20Test.java
+++ b/tests/tests/notificationlegacy/notificationlegacy20/src/android/app/notification/legacy20/cts/LegacyNotificationManager20Test.java
@@ -16,14 +16,11 @@
package android.app.notification.legacy20.cts;
-import static android.content.pm.PackageManager.FEATURE_WATCH;
-
import static junit.framework.Assert.fail;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
-import android.app.ActivityManager;
import android.app.Instrumentation;
import android.app.Notification;
import android.app.NotificationChannel;
@@ -64,7 +61,6 @@
private PackageManager mPackageManager;
final String NOTIFICATION_CHANNEL_ID = "LegacyNotificationManagerTest";
private NotificationManager mNotificationManager;
- private ActivityManager mActivityManager;
private Context mContext;
private TestNotificationListener mListener;
@@ -78,7 +74,6 @@
Context.NOTIFICATION_SERVICE);
mNotificationManager.createNotificationChannel(new NotificationChannel(
NOTIFICATION_CHANNEL_ID, "name", NotificationManager.IMPORTANCE_DEFAULT));
- mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
mPackageManager = mContext.getPackageManager();
}
@@ -92,10 +87,6 @@
@Test
public void testNotificationListener_cancelNotifications() throws Exception {
- if (mActivityManager.isLowRamDevice() && !mPackageManager.hasSystemFeature(FEATURE_WATCH)) {
- return;
- }
-
toggleListenerAccess(TestNotificationListener.getId(),
InstrumentationRegistry.getInstrumentation(), true);
Thread.sleep(500); // wait for listener to be allowed
diff --git a/tests/tests/notificationlegacy/notificationlegacy27/src/android/app/notification/legacy/cts/ConditionProviderServiceTest.java b/tests/tests/notificationlegacy/notificationlegacy27/src/android/app/notification/legacy/cts/ConditionProviderServiceTest.java
index b999cfe..9049b3f 100644
--- a/tests/tests/notificationlegacy/notificationlegacy27/src/android/app/notification/legacy/cts/ConditionProviderServiceTest.java
+++ b/tests/tests/notificationlegacy/notificationlegacy27/src/android/app/notification/legacy/cts/ConditionProviderServiceTest.java
@@ -24,11 +24,8 @@
import static junit.framework.Assert.assertNull;
import static junit.framework.TestCase.fail;
-import static org.junit.Assume.assumeFalse;
-
import static java.lang.Thread.sleep;
-import android.app.ActivityManager;
import android.app.AutomaticZenRule;
import android.app.Instrumentation;
import android.app.NotificationManager;
@@ -62,7 +59,6 @@
private static String TAG = "CpsTest";
private NotificationManager mNm;
- private ActivityManager mActivityManager;
private Context mContext;
private ZenModeBroadcastReceiver mModeReceiver;
private IntentFilter mModeFilter;
@@ -71,9 +67,6 @@
@Before
public void setUp() throws Exception {
mContext = InstrumentationRegistry.getContext();
- mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
- assumeFalse(mActivityManager.isLowRamDevice());
-
toggleNotificationPolicyAccess(mContext.getPackageName(),
InstrumentationRegistry.getInstrumentation(), true);
LegacyConditionProviderService.requestRebind(LegacyConditionProviderService.getId());
@@ -114,10 +107,6 @@
@Test
public void testUnboundCPSMaintainsCondition_addsNewRule() throws Exception {
- if (mActivityManager.isLowRamDevice()) {
- return;
- }
-
// make sure service get bound
pollForConnection(SecondaryConditionProviderService.class, true);
@@ -147,10 +136,6 @@
@Test
public void testUnboundCPSMaintainsCondition_otherConditionChanges() throws Exception {
- if (mActivityManager.isLowRamDevice()) {
- return;
- }
-
// make sure both services get bound
pollForConnection(LegacyConditionProviderService.class, true);
pollForConnection(SecondaryConditionProviderService.class, true);
@@ -184,10 +169,6 @@
@Test
public void testUnboundCPSMaintainsCondition_otherProviderRuleChanges() throws Exception {
- if (mActivityManager.isLowRamDevice()) {
- return;
- }
-
// make sure both services get bound
pollForConnection(LegacyConditionProviderService.class, true);
pollForConnection(SecondaryConditionProviderService.class, true);
@@ -219,10 +200,6 @@
@Test
public void testRequestRebindWhenLostAccess() throws Exception {
- if (mActivityManager.isLowRamDevice()) {
- return;
- }
-
// make sure it gets bound
pollForConnection(LegacyConditionProviderService.class, true);
@@ -250,10 +227,6 @@
@Test
public void testRequestRebindWhenStillHasAccess() throws Exception {
- if (mActivityManager.isLowRamDevice()) {
- return;
- }
-
// make sure it gets bound
pollForConnection(LegacyConditionProviderService.class, true);
@@ -276,12 +249,6 @@
@Test
public void testMethodsExistAndDoNotThrow() throws Exception {
- // behavior is covered in cts verifier
-
- if (mActivityManager.isLowRamDevice()) {
- return;
- }
-
// make sure it gets bound
pollForConnection(LegacyConditionProviderService.class, true);
diff --git a/tests/tests/notificationlegacy/notificationlegacy27/src/android/app/notification/legacy/cts/LegacyNotificationManagerTest.java b/tests/tests/notificationlegacy/notificationlegacy27/src/android/app/notification/legacy/cts/LegacyNotificationManagerTest.java
index f2960a6..f5606de 100644
--- a/tests/tests/notificationlegacy/notificationlegacy27/src/android/app/notification/legacy/cts/LegacyNotificationManagerTest.java
+++ b/tests/tests/notificationlegacy/notificationlegacy27/src/android/app/notification/legacy/cts/LegacyNotificationManagerTest.java
@@ -65,7 +65,6 @@
final String NOTIFICATION_CHANNEL_ID = "LegacyNotificationManagerTest";
private NotificationManager mNotificationManager;
- private ActivityManager mActivityManager;
private Context mContext;
private SecondaryNotificationListener mSecondaryListener;
@@ -82,7 +81,6 @@
Context.NOTIFICATION_SERVICE);
mNotificationManager.createNotificationChannel(new NotificationChannel(
NOTIFICATION_CHANNEL_ID, "name", NotificationManager.IMPORTANCE_DEFAULT));
- mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
}
@After
@@ -98,9 +96,6 @@
@Test
public void testPrePCannotToggleAlarmsAndMediaTest() throws Exception {
- if (mActivityManager.isLowRamDevice()) {
- return;
- }
toggleNotificationPolicyAccess(mContext.getPackageName(),
InstrumentationRegistry.getInstrumentation(), true);
@@ -141,9 +136,6 @@
@Test
public void testSetNotificationPolicy_preP_setOldFields() throws Exception {
- if (mActivityManager.isLowRamDevice()) {
- return;
- }
toggleNotificationPolicyAccess(mContext.getPackageName(),
InstrumentationRegistry.getInstrumentation(), true);
@@ -164,9 +156,6 @@
@Test
public void testSetNotificationPolicy_preP_setNewFields() throws Exception {
- if (mActivityManager.isLowRamDevice()) {
- return;
- }
toggleNotificationPolicyAccess(mContext.getPackageName(),
InstrumentationRegistry.getInstrumentation(), true);
@@ -187,9 +176,6 @@
@Test
public void testSuspendPackage() throws Exception {
- if (mActivityManager.isLowRamDevice()) {
- return;
- }
toggleListenerAccess(TestNotificationListener.getId(),
InstrumentationRegistry.getInstrumentation(), true);
Thread.sleep(500); // wait for listener to be allowed
@@ -222,9 +208,6 @@
@Test
public void testSuspendedPackageSendNotification() throws Exception {
- if (mActivityManager.isLowRamDevice()) {
- return;
- }
toggleListenerAccess(TestNotificationListener.getId(),
InstrumentationRegistry.getInstrumentation(), true);
Thread.sleep(500); // wait for listener to be allowed
@@ -255,9 +238,6 @@
@Test
public void testResetListenerHints_singleListener() throws Exception {
- if (mActivityManager.isLowRamDevice()) {
- return;
- }
toggleListenerAccess(TestNotificationListener.getId(),
InstrumentationRegistry.getInstrumentation(), true);
Thread.sleep(500); // wait for listener to be allowed
@@ -277,9 +257,6 @@
@Test
public void testResetListenerHints_multiListener() throws Exception {
- if (mActivityManager.isLowRamDevice()) {
- return;
- }
toggleListenerAccess(TestNotificationListener.getId(),
InstrumentationRegistry.getInstrumentation(), true);
toggleListenerAccess(SecondaryNotificationListener.getId(),
@@ -307,9 +284,6 @@
@Test
public void testSetNotificationPolicy_preP_setOldNewFields() throws Exception {
- if (mActivityManager.isLowRamDevice()) {
- return;
- }
toggleNotificationPolicyAccess(mContext.getPackageName(),
InstrumentationRegistry.getInstrumentation(), true);
diff --git a/tests/tests/notificationlegacy/notificationlegacy29/src/android/app/notification/legacy29/cts/NotificationAssistantServiceTest.java b/tests/tests/notificationlegacy/notificationlegacy29/src/android/app/notification/legacy29/cts/NotificationAssistantServiceTest.java
index 1747c17..a69c595 100644
--- a/tests/tests/notificationlegacy/notificationlegacy29/src/android/app/notification/legacy29/cts/NotificationAssistantServiceTest.java
+++ b/tests/tests/notificationlegacy/notificationlegacy29/src/android/app/notification/legacy29/cts/NotificationAssistantServiceTest.java
@@ -24,7 +24,6 @@
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.fail;
-import android.app.ActivityManager;
import android.app.Instrumentation;
import android.app.Notification;
import android.app.NotificationChannel;
@@ -43,6 +42,9 @@
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
import junit.framework.Assert;
import org.junit.After;
@@ -57,9 +59,6 @@
import java.util.List;
import java.util.concurrent.TimeUnit;
-import androidx.test.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
-
@RunWith(AndroidJUnit4.class)
public class NotificationAssistantServiceTest {
@@ -71,7 +70,6 @@
private TestNotificationAssistant mNotificationAssistantService;
private TestNotificationListener mNotificationListenerService;
private NotificationManager mNotificationManager;
- private ActivityManager mActivityManager;
private StatusBarManager mStatusBarManager;
private Context mContext;
private UiAutomation mUi;
@@ -84,7 +82,6 @@
Context.NOTIFICATION_SERVICE);
mNotificationManager.createNotificationChannel(new NotificationChannel(
NOTIFICATION_CHANNEL_ID, "name", NotificationManager.IMPORTANCE_DEFAULT));
- mActivityManager = mContext.getSystemService(ActivityManager.class);
mStatusBarManager = (StatusBarManager) mContext.getSystemService(
Context.STATUS_BAR_SERVICE);
}
@@ -92,18 +89,14 @@
@After
public void tearDown() throws IOException {
if (mNotificationListenerService != null) mNotificationListenerService.resetData();
- if (!mActivityManager.isLowRamDevice()) {
- toggleListenerAccess(false);
- toggleAssistantAccess(false);
- }
+
+ toggleListenerAccess(false);
+ toggleAssistantAccess(false);
mUi.dropShellPermissionIdentity();
}
@Test
public void testOnNotificationEnqueued() throws Exception {
- if (mActivityManager.isLowRamDevice()) {
- return;
- }
toggleListenerAccess(true);
Thread.sleep(SLEEP_TIME);
@@ -138,9 +131,6 @@
@Test
public void testAdjustNotification_userSentimentKey() throws Exception {
- if (mActivityManager.isLowRamDevice()) {
- return;
- }
setUpListeners();
mUi.adoptShellPermissionIdentity("android.permission.STATUS_BAR_SERVICE");
@@ -172,9 +162,6 @@
@Test
public void testAdjustNotification_importanceKey() throws Exception {
- if (mActivityManager.isLowRamDevice()) {
- return;
- }
setUpListeners();
mUi.adoptShellPermissionIdentity("android.permission.STATUS_BAR_SERVICE");
@@ -205,9 +192,6 @@
@Test
public void testAdjustNotification_rankingScoreKey() throws Exception {
- if (mActivityManager.isLowRamDevice()) {
- return;
- }
setUpListeners();
try {
@@ -263,9 +247,6 @@
@Test
public void testAdjustNotification_smartActionKey() throws Exception {
- if (mActivityManager.isLowRamDevice()) {
- return;
- }
setUpListeners();
mUi.adoptShellPermissionIdentity("android.permission.STATUS_BAR_SERVICE");
@@ -313,9 +294,6 @@
@Test
public void testAdjustNotification_smartReplyKey() throws Exception {
- if (mActivityManager.isLowRamDevice()) {
- return;
- }
setUpListeners();
CharSequence smartReply = "Smart Reply!";
@@ -359,9 +337,6 @@
@Test
public void testAdjustNotification_importanceKey_notAllowed() throws Exception {
- if (mActivityManager.isLowRamDevice()) {
- return;
- }
setUpListeners();
mUi.adoptShellPermissionIdentity("android.permission.STATUS_BAR_SERVICE");
@@ -394,9 +369,6 @@
@Test
public void testAdjustNotification_rankingScoreKey_notAllowed() throws Exception {
- if (mActivityManager.isLowRamDevice()) {
- return;
- }
setUpListeners();
mUi.adoptShellPermissionIdentity("android.permission.STATUS_BAR_SERVICE");
@@ -442,9 +414,6 @@
@Test
public void testGetAllowedAssistantCapabilities_permission() throws Exception {
- if (mActivityManager.isLowRamDevice()) {
- return;
- }
toggleAssistantAccess(false);
try {
@@ -457,9 +426,6 @@
@Test
public void testGetAllowedAssistantCapabilities() throws Exception {
- if (mActivityManager.isLowRamDevice()) {
- return;
- }
toggleAssistantAccess(true);
Thread.sleep(SLEEP_TIME); // wait for assistant to be allowed
mNotificationAssistantService = TestNotificationAssistant.getInstance();
@@ -489,9 +455,6 @@
@Test
public void testOnActionInvoked_methodExists() throws Exception {
- if (mActivityManager.isLowRamDevice()) {
- return;
- }
setUpListeners();
final Intent intent = new Intent(Intent.ACTION_MAIN, Telephony.Threads.CONTENT_URI);
@@ -509,9 +472,6 @@
@Test
public void testOnNotificationDirectReplied_methodExists() throws Exception {
- if (mActivityManager.isLowRamDevice()) {
- return;
- }
setUpListeners();
// This method has to exist and the call cannot fail
mNotificationAssistantService.onNotificationDirectReplied("");
@@ -519,9 +479,6 @@
@Test
public void testOnNotificationExpansionChanged_methodExists() throws Exception {
- if (mActivityManager.isLowRamDevice()) {
- return;
- }
setUpListeners();
// This method has to exist and the call cannot fail
mNotificationAssistantService.onNotificationExpansionChanged("", true, true);
@@ -529,9 +486,6 @@
@Test
public void testOnNotificationVisibilityChanged() throws Exception {
- if (mActivityManager.isLowRamDevice()) {
- return;
- }
setUpListeners();
mUi.adoptShellPermissionIdentity("android.permission.EXPAND_STATUS_BAR");
@@ -560,9 +514,6 @@
@Test
public void testOnNotificationsSeen() throws Exception {
- if (mActivityManager.isLowRamDevice()) {
- return;
- }
setUpListeners();
mUi.adoptShellPermissionIdentity("android.permission.EXPAND_STATUS_BAR");
@@ -585,9 +536,6 @@
@Test
public void testOnPanelRevealedAndHidden() throws Exception {
- if (mActivityManager.isLowRamDevice()) {
- return;
- }
setUpListeners();
mUi.adoptShellPermissionIdentity("android.permission.EXPAND_STATUS_BAR");
@@ -609,9 +557,6 @@
@Test
public void testOnSuggestedReplySent_methodExists() throws Exception {
- if (mActivityManager.isLowRamDevice()) {
- return;
- }
setUpListeners();
// This method has to exist and the call cannot fail
mNotificationAssistantService.onSuggestedReplySent("", "",
diff --git a/tests/tests/notificationlegacy/notificationlegacy29/src/android/app/notification/legacy29/cts/NotificationManager29Test.java b/tests/tests/notificationlegacy/notificationlegacy29/src/android/app/notification/legacy29/cts/NotificationManager29Test.java
index 96c2b0d..5b70d19 100644
--- a/tests/tests/notificationlegacy/notificationlegacy29/src/android/app/notification/legacy29/cts/NotificationManager29Test.java
+++ b/tests/tests/notificationlegacy/notificationlegacy29/src/android/app/notification/legacy29/cts/NotificationManager29Test.java
@@ -17,7 +17,6 @@
package android.app.notification.legacy29.cts;
import static android.app.NotificationManager.Policy.CONVERSATION_SENDERS_ANYONE;
-import static android.app.NotificationManager.Policy.CONVERSATION_SENDERS_IMPORTANT;
import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_CONVERSATIONS;
import static junit.framework.Assert.assertEquals;
@@ -25,7 +24,6 @@
import static junit.framework.TestCase.assertNotNull;
import static junit.framework.TestCase.assertNull;
-import android.app.ActivityManager;
import android.app.Instrumentation;
import android.app.Notification;
import android.app.NotificationChannel;
@@ -61,7 +59,6 @@
final String NOTIFICATION_CHANNEL_ID = "LegacyNoManTest29";
private NotificationManager mNotificationManager;
private Context mContext;
- private ActivityManager mActivityManager;
@Before
public void setUp() throws Exception {
@@ -71,7 +68,6 @@
Context.NOTIFICATION_SERVICE);
mNotificationManager.createNotificationChannel(new NotificationChannel(
NOTIFICATION_CHANNEL_ID, "name", NotificationManager.IMPORTANCE_DEFAULT));
- mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
}
private void toggleNotificationPolicyAccess(String packageName,
@@ -158,9 +154,6 @@
@Test
public void testApi29CannotToggleConversationsTest() throws Exception {
- if (mActivityManager.isLowRamDevice()) {
- return;
- }
toggleNotificationPolicyAccess(mContext.getPackageName(),
InstrumentationRegistry.getInstrumentation(), true);
@@ -185,9 +178,6 @@
@Test
public void testApi29CannotToggleConversationsOffTest() throws Exception {
- if (mActivityManager.isLowRamDevice()) {
- return;
- }
toggleNotificationPolicyAccess(mContext.getPackageName(),
InstrumentationRegistry.getInstrumentation(), true);
diff --git a/tests/tests/os/AndroidManifest.xml b/tests/tests/os/AndroidManifest.xml
index 9639c0d..7c9ea67 100644
--- a/tests/tests/os/AndroidManifest.xml
+++ b/tests/tests/os/AndroidManifest.xml
@@ -47,6 +47,7 @@
<uses-permission android:name="android.permission.POWER_SAVER" />
<uses-permission android:name="android.permission.INSTALL_DYNAMIC_SYSTEM" />
<uses-permission android:name="android.permission.MANAGE_COMPANION_DEVICES" />
+ <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.os.cts.permission.TEST_GRANTED" />
<application
@@ -70,6 +71,8 @@
</intent-filter>
</activity>
+ <activity android:name="android.os.cts.SimpleTestActivity" />
+
<service
android:name="android.os.cts.ParcelFileDescriptorPeer$Red"
android:process=":red"
diff --git a/tests/tests/os/src/android/os/cts/PowerManager_ThermalTest.java b/tests/tests/os/src/android/os/cts/PowerManager_ThermalTest.java
index 13de572..932cbaf 100644
--- a/tests/tests/os/src/android/os/cts/PowerManager_ThermalTest.java
+++ b/tests/tests/os/src/android/os/cts/PowerManager_ThermalTest.java
@@ -17,6 +17,8 @@
package android.os.cts;
import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertFalse;
+import static junit.framework.TestCase.assertTrue;
import static junit.framework.TestCase.fail;
import static org.mockito.Mockito.reset;
@@ -123,4 +125,28 @@
verify(mListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
.times(1)).onThermalStatusChanged(status);
}
+
+ @Test
+ public void testGetThermalHeadroom() throws Exception {
+ float headroom = mPowerManager.getThermalHeadroom(0);
+ // If the device doesn't support thermal headroom, return early
+ if (Float.isNaN(headroom)) {
+ return;
+ }
+ assertTrue("Expected non-negative headroom", headroom >= 0.0f);
+ assertTrue("Expected reasonably small headroom", headroom < 10.0f);
+
+ // Sleep for a second before attempting to call again so as to not get rate limited
+ Thread.sleep(1000);
+ headroom = mPowerManager.getThermalHeadroom(5);
+ assertFalse("Expected data to still be available", Float.isNaN(headroom));
+ assertTrue("Expected non-negative headroom", headroom >= 0.0f);
+ assertTrue("Expected reasonably small headroom", headroom < 10.0f);
+
+ // Test rate limiting by spamming calls and ensuring that at least the last one fails
+ for (int i = 0; i < 20; ++i) {
+ headroom = mPowerManager.getThermalHeadroom(5);
+ }
+ assertTrue("Abusive calls get rate limited", Float.isNaN(headroom));
+ }
}
diff --git a/tests/tests/os/src/android/os/cts/SimpleTestActivity.java b/tests/tests/os/src/android/os/cts/SimpleTestActivity.java
new file mode 100644
index 0000000..4242315
--- /dev/null
+++ b/tests/tests/os/src/android/os/cts/SimpleTestActivity.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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 android.app.Activity;
+
+public class SimpleTestActivity extends Activity {
+}
\ No newline at end of file
diff --git a/tests/tests/os/src/android/os/cts/StrictModeTest.java b/tests/tests/os/src/android/os/cts/StrictModeTest.java
index 2ffbbb7..1a4f0ef 100644
--- a/tests/tests/os/src/android/os/cts/StrictModeTest.java
+++ b/tests/tests/os/src/android/os/cts/StrictModeTest.java
@@ -16,17 +16,23 @@
package android.os.cts;
+import static android.content.Context.WINDOW_SERVICE;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
+import android.hardware.display.DisplayManager;
import android.net.TrafficStats;
import android.net.Uri;
import android.os.IBinder;
@@ -51,6 +57,8 @@
import android.system.Os;
import android.system.OsConstants;
import android.util.Log;
+import android.view.Display;
+import android.view.WindowManager;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
@@ -610,6 +618,66 @@
}
}
+ @Test
+ public void testIncorrectContextUse_GetSystemService() throws Exception {
+ StrictMode.setVmPolicy(
+ new StrictMode.VmPolicy.Builder()
+ .detectIncorrectContextUse()
+ .penaltyLog()
+ .build());
+
+ inspectViolation(
+ () -> getContext().getApplicationContext().getSystemService(WindowManager.class),
+ info -> assertThat(info.getStackTrace()).contains(
+ "Tried to access visual service " + WINDOW_SERVICE));
+
+ final Display display = getContext().getSystemService(DisplayManager.class)
+ .getDisplay(DEFAULT_DISPLAY);
+ final Context visualContext = getContext().createDisplayContext(display)
+ .createWindowContext(TYPE_APPLICATION_OVERLAY, null /* options */);
+ assertNoViolation(() -> visualContext.getSystemService(WINDOW_SERVICE));
+
+ Intent intent = new Intent(InstrumentationRegistry.getInstrumentation().getTargetContext(),
+ SimpleTestActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ final Activity activity = InstrumentationRegistry.getInstrumentation()
+ .startActivitySync(intent);
+ assertNoViolation(() -> activity.getSystemService(WINDOW_SERVICE));
+ }
+
+ @Test
+ public void testIncorrectContextUse_GetDisplay() throws Exception {
+ StrictMode.setVmPolicy(
+ new StrictMode.VmPolicy.Builder()
+ .detectIncorrectContextUse()
+ .penaltyLog()
+ .build());
+
+ final Display display = getContext().getSystemService(DisplayManager.class)
+ .getDisplay(DEFAULT_DISPLAY);
+
+ final Context displayContext = getContext().createDisplayContext(display);
+ assertNoViolation(displayContext::getDisplay);
+
+ final Context windowContext =
+ displayContext.createWindowContext(TYPE_APPLICATION_OVERLAY, null /* options */);
+ assertNoViolation(windowContext::getDisplay);
+
+ Intent intent = new Intent(InstrumentationRegistry.getInstrumentation().getTargetContext(),
+ SimpleTestActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ final Activity activity = InstrumentationRegistry.getInstrumentation()
+ .startActivitySync(intent);
+ assertNoViolation(() -> activity.getDisplay());
+
+ try {
+ getContext().getApplicationContext().getDisplay();
+ } catch (UnsupportedOperationException e) {
+ return;
+ }
+ fail("Expected to get incorrect use exception from calling getDisplay() on Application");
+ }
+
private static void runWithRemoteServiceBound(Context context, Consumer<ISecondary> consumer)
throws ExecutionException, InterruptedException, RemoteException {
BlockingQueue<IBinder> binderHolder = new ArrayBlockingQueue<>(1);
diff --git a/tests/tests/permission/AndroidTest.xml b/tests/tests/permission/AndroidTest.xml
index a8bc1d0..e37b956 100644
--- a/tests/tests/permission/AndroidTest.xml
+++ b/tests/tests/permission/AndroidTest.xml
@@ -76,9 +76,4 @@
<option name="package" value="android.permission.cts" />
<option name="runtime-hint" value="13m" />
</test>
-
- <object type="module_controller"
- class="com.android.tradefed.testtype.suite.module.TestFailureModuleController">
- <option name="bugreportz-on-failure" value="true" />
- </object>
</configuration>
diff --git a/tests/tests/permission/telephony/OWNERS b/tests/tests/permission/telephony/OWNERS
index 423a16b..b19c963 100644
--- a/tests/tests/permission/telephony/OWNERS
+++ b/tests/tests/permission/telephony/OWNERS
@@ -1,4 +1,2 @@
set noparent
-amitmahajan@google.com
-fionaxu@google.com
-jminjie@google.com
\ No newline at end of file
+include ../../telephony/OWNERS
\ No newline at end of file
diff --git a/tests/tests/permission/telephony/src/android/permission/cts/telephony/TelephonyManagerPermissionTest.java b/tests/tests/permission/telephony/src/android/permission/cts/telephony/TelephonyManagerPermissionTest.java
index b050ee0..b13a00a 100644
--- a/tests/tests/permission/telephony/src/android/permission/cts/telephony/TelephonyManagerPermissionTest.java
+++ b/tests/tests/permission/telephony/src/android/permission/cts/telephony/TelephonyManagerPermissionTest.java
@@ -200,6 +200,38 @@
}
}
+ /**
+ * Tests that isManualNetworkSelectionAllowed requires permission
+ * Expects a security exception since the caller does not have carrier privileges.
+ */
+ @Test
+ public void testIsManualNetworkSelectionAllowedWithoutPermission() {
+ if (!mHasTelephony) {
+ return;
+ }
+ try {
+ mTelephonyManager.isManualNetworkSelectionAllowed();
+ fail("Expected SecurityException. App does not have carrier privileges.");
+ } catch (SecurityException expected) {
+ }
+ }
+
+ /**
+ * Tests that getManualNetworkSelectionPlmn requires permission
+ * Expects a security exception since the caller does not have carrier privileges.
+ */
+ @Test
+ public void testGetManualNetworkSelectionPlmnWithoutPermission() {
+ if (!mHasTelephony) {
+ return;
+ }
+ try {
+ mTelephonyManager.getManualNetworkSelectionPlmn();
+ fail("Expected SecurityException. App does not have carrier privileges.");
+ } catch (SecurityException expected) {
+ }
+ }
+
/**
* Verify that Telephony related broadcasts are protected.
*/
@@ -307,6 +339,32 @@
}
/**
+ * Tests that getNetworkSelectionMode requires permission
+ * Expects a security exception since the caller does not have carrier privileges.
+ */
+ @Test
+ public void testGetNetworkSelectionModeWithoutPermission() {
+ if (!mHasTelephony) {
+ return;
+ }
+ assertThrowsSecurityException(() -> mTelephonyManager.getNetworkSelectionMode(),
+ "Expected SecurityException. App does not have carrier privileges.");
+ }
+
+ /**
+ * Tests that setNetworkSelectionModeAutomatic requires permission
+ * Expects a security exception since the caller does not have carrier privileges.
+ */
+ @Test
+ public void testSetNetworkSelectionModeAutomaticWithoutPermission() {
+ if (!mHasTelephony) {
+ return;
+ }
+ assertThrowsSecurityException(() -> mTelephonyManager.setNetworkSelectionModeAutomatic(),
+ "Expected SecurityException. App does not have carrier privileges.");
+ }
+
+ /**
* Verify that setForbiddenPlmns requires Permission.
* <p>
* Requires Permission:
diff --git a/tests/tests/permission2/res/raw/android_manifest.xml b/tests/tests/permission2/res/raw/android_manifest.xml
index 650432f..9bbc177 100644
--- a/tests/tests/permission2/res/raw/android_manifest.xml
+++ b/tests/tests/permission2/res/raw/android_manifest.xml
@@ -1197,6 +1197,15 @@
android:description="@string/permdesc_acceptHandovers"
android:protectionLevel="dangerous" />
+ <!-- Allows an application assigned to the Dialer role to be granted access to the telephony
+ call audio streams, both TX and RX.
+ <p>Protection level: signature|appop
+ -->
+ <permission android:name="android.permission.ACCESS_CALL_AUDIO"
+ android.label="@string/permlab_accessCallAudio"
+ android:description="@string/permdesc_accessCallAudio"
+ android:protectionLevel="signature|appop" />
+
<!-- ====================================================================== -->
<!-- Permissions for accessing the device microphone -->
<!-- ====================================================================== -->
@@ -1947,6 +1956,18 @@
android:description="@string/permdesc_modifyAudioSettings"
android:protectionLevel="normal" />
+ <!-- ======================================== -->
+ <!-- Permissions for factory reset protection -->
+ <!-- ======================================== -->
+ <eat-comment />
+
+ <!-- @SystemApi Allows an application to set a factory reset protection (FRP) policy.
+ <p>Not for use by third-party applications.
+ @hide
+ -->
+ <permission android:name="android.permission.MANAGE_FACTORY_RESET_PROTECTION"
+ android:protectionLevel="signature|privileged"/>
+
<!-- ================================== -->
<!-- Permissions for accessing hardware -->
<!-- ================================== -->
@@ -2743,7 +2764,7 @@
<permission android:name="android.permission.READ_DEVICE_CONFIG"
android:protectionLevel="signature|preinstalled" />
- <!-- @SystemApi @hide Allows an application to monitor config settings access.
+ <!-- @hide Allows an application to monitor config settings access.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.MONITOR_DEVICE_CONFIG_ACCESS"
android:protectionLevel="signature"/>
diff --git a/tests/tests/provider/src/android/provider/cts/media/MediaStorePendingTest.java b/tests/tests/provider/src/android/provider/cts/media/MediaStorePendingTest.java
index 961d998..049f4aa 100644
--- a/tests/tests/provider/src/android/provider/cts/media/MediaStorePendingTest.java
+++ b/tests/tests/provider/src/android/provider/cts/media/MediaStorePendingTest.java
@@ -269,7 +269,8 @@
Environment.DIRECTORY_NOTIFICATIONS, Environment.DIRECTORY_PODCASTS,
Environment.DIRECTORY_ALARMS));
final Set<String> allowedVideo = new HashSet<>(
- Arrays.asList(Environment.DIRECTORY_MOVIES, Environment.DIRECTORY_DCIM));
+ Arrays.asList(Environment.DIRECTORY_MOVIES, Environment.DIRECTORY_DCIM,
+ Environment.DIRECTORY_PICTURES));
final Set<String> allowedImages = new HashSet<>(
Arrays.asList(Environment.DIRECTORY_PICTURES, Environment.DIRECTORY_DCIM));
final Set<String> allowedDownloads = new HashSet<>(
diff --git a/tests/tests/secure_element/access_control/OWNERS b/tests/tests/secure_element/access_control/OWNERS
index 853b7c3..6c4d2b3 100644
--- a/tests/tests/secure_element/access_control/OWNERS
+++ b/tests/tests/secure_element/access_control/OWNERS
@@ -1,6 +1,5 @@
# Bug component: 456592
-zachoverflow@google.com
+alisher@google.com
jackcwyu@google.com
-tokuda@google.com
georgekgchang@google.com
-jimmychchang@google.com
+zachoverflow@google.com
diff --git a/tests/tests/secure_element/omapi/OWNERS b/tests/tests/secure_element/omapi/OWNERS
index 853b7c3..6c4d2b3 100644
--- a/tests/tests/secure_element/omapi/OWNERS
+++ b/tests/tests/secure_element/omapi/OWNERS
@@ -1,6 +1,5 @@
# Bug component: 456592
-zachoverflow@google.com
+alisher@google.com
jackcwyu@google.com
-tokuda@google.com
georgekgchang@google.com
-jimmychchang@google.com
+zachoverflow@google.com
diff --git a/tests/tests/selinux/TEST_MAPPING b/tests/tests/selinux/TEST_MAPPING
index a8bae77..fb3c6c8 100644
--- a/tests/tests/selinux/TEST_MAPPING
+++ b/tests/tests/selinux/TEST_MAPPING
@@ -13,8 +13,10 @@
"name": "CtsSelinuxTargetSdk28TestCases"
},
{
+ "name": "CtsSelinuxTargetSdk29TestCases"
+ },
+ {
"name": "CtsSelinuxTargetSdkCurrentTestCases"
}
]
}
-
diff --git a/tests/tests/selinux/common/src/android/security/SELinuxTargetSdkTestBase.java b/tests/tests/selinux/common/src/android/security/SELinuxTargetSdkTestBase.java
index 25a343d..6ec352c 100644
--- a/tests/tests/selinux/common/src/android/security/SELinuxTargetSdkTestBase.java
+++ b/tests/tests/selinux/common/src/android/security/SELinuxTargetSdkTestBase.java
@@ -8,6 +8,8 @@
import java.util.Scanner;
import java.io.File;
import java.io.IOException;
+import java.net.NetworkInterface;
+import java.util.Collections;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
@@ -29,27 +31,6 @@
}
}
- protected static void noExecuteOnly() throws IOException {
- String[] maps = {"^[0-9a-z]+-[0-9a-z]+\\s+--xp.*\\/apex\\/com\\.android\\.runtime\\/.*",
- "^[0-9a-z]+-[0-9a-z]+\\s+--xp.*\\/system\\/.*"};
- for (String map : maps) {
- final Pattern mapsPattern = Pattern.compile(map);
- BufferedReader reader = new BufferedReader(new FileReader("/proc/self/maps"));
- String line;
- try {
- while ((line = reader.readLine()) != null) {
- Matcher m = mapsPattern.matcher(line);
- assertFalse("System provided libraries should be not be marked execute-only " +
- "for apps with targetSdkVersion<Q, but an execute-only segment was " +
- "found:\n" + line, m.matches());
- }
-
- } finally {
- reader.close();
- }
- }
- }
-
protected static String getProperty(String property)
throws IOException {
Process process = new ProcessBuilder("getprop", property).start();
@@ -78,16 +59,36 @@
}
}
- protected static void noNetlinkRouteGetlink() throws IOException {
- assertEquals("RTM_GETLINK is not allowed on a netlink route sockets. Verify that the " +
- "following patch has been applied to your kernel: " +
- "https://android-review.googlesource.com/q/I7b44ce60ad98f858c412722d41b9842f8577151f",
- 13, checkNetlinkRouteGetlink());
+ protected static void checkNetlinkRouteGetlink(boolean expectAllowed) throws IOException {
+ if (!expectAllowed) {
+ assertEquals(
+ "RTM_GETLINK is not allowed on a netlink route sockets. Verify that the"
+ + " following patch has been applied to your kernel: "
+ + "https://android-review.googlesource.com/q/I7b44ce60ad98f858c412722d41b9842f8577151f",
+ 13,
+ checkNetlinkRouteGetlink());
+ } else {
+ assertEquals(
+ "RTM_GETLINK should be allowed netlink route sockets for apps with "
+ + "targetSdkVersion <= Q",
+ -1,
+ checkNetlinkRouteGetlink());
+ }
}
- protected static void noNetlinkRouteBind() throws IOException {
- assertEquals("Bind() is not allowed on a netlink route sockets",
- 13, checkNetlinkRouteBind());
+ protected static void checkNetlinkRouteBind(boolean expectAllowed) throws IOException {
+ if (!expectAllowed) {
+ assertEquals(
+ "Bind() is not allowed on a netlink route sockets",
+ 13,
+ checkNetlinkRouteBind());
+ } else {
+ assertEquals(
+ "Bind() should succeed for netlink route sockets for apps with "
+ + "targetSdkVersion <= Q",
+ -1,
+ checkNetlinkRouteBind());
+ }
}
/**
@@ -167,6 +168,29 @@
return true;
}
+ /**
+ * Verify that apps having targetSdkVersion <= 29 are able to see MAC
+ * addresses of ethernet devices.
+ * The counterpart of this test (testing for targetSdkVersion > 29) is
+ * {@link libcore.java.net.NetworkInterfaceTest#testGetHardwareAddress_returnsNull()}.
+ */
+ protected static void checkNetworkInterface_returnsHardwareAddresses() throws Exception {
+ assertNotNull(NetworkInterface.getNetworkInterfaces());
+ for (NetworkInterface nif : Collections.list(NetworkInterface.getNetworkInterfaces())) {
+ if (isEthernet(nif.getName())) {
+ assertEquals(6, nif.getHardwareAddress().length);
+ }
+ }
+ }
+
+ /**
+ * Checks whether a network interface is an ethernet interface.
+ */
+ private static Pattern ethernetNamePattern = Pattern.compile("^(eth|wlan)[0-9]+$");
+ private static boolean isEthernet(String ifName) throws Exception {
+ return ethernetNamePattern.matcher(ifName).matches();
+ }
+
private static final native int checkNetlinkRouteGetlink();
private static final native int checkNetlinkRouteBind();
private static final native String getFileContext(String path);
diff --git a/tests/tests/selinux/selinuxEphemeral/src/android/security/SELinuxTargetSdkTest.java b/tests/tests/selinux/selinuxEphemeral/src/android/security/SELinuxTargetSdkTest.java
index 2826421..1ed366e 100644
--- a/tests/tests/selinux/selinuxEphemeral/src/android/security/SELinuxTargetSdkTest.java
+++ b/tests/tests/selinux/selinuxEphemeral/src/android/security/SELinuxTargetSdkTest.java
@@ -77,4 +77,12 @@
checkDex2oatAccess(false);
}
}
+
+ public void testNoNetlinkRouteGetlink() throws IOException {
+ checkNetlinkRouteGetlink(false);
+ }
+
+ public void testNoNetlinkRouteBind() throws IOException {
+ checkNetlinkRouteBind(false);
+ }
}
diff --git a/tests/tests/selinux/selinuxTargetSdk25/src/android/security/SELinuxTargetSdkTest.java b/tests/tests/selinux/selinuxTargetSdk25/src/android/security/SELinuxTargetSdkTest.java
index dddd3d9..ec2af2d 100644
--- a/tests/tests/selinux/selinuxTargetSdk25/src/android/security/SELinuxTargetSdkTest.java
+++ b/tests/tests/selinux/selinuxTargetSdk25/src/android/security/SELinuxTargetSdkTest.java
@@ -68,8 +68,4 @@
public void testDex2oat() throws Exception {
checkDex2oatAccess(true);
}
-
- public void testNoExecuteOnly() throws IOException {
- noExecuteOnly();
- }
}
diff --git a/tests/tests/selinux/selinuxTargetSdk27/AndroidManifest.xml b/tests/tests/selinux/selinuxTargetSdk27/AndroidManifest.xml
index d2addf8..b94757c 100644
--- a/tests/tests/selinux/selinuxTargetSdk27/AndroidManifest.xml
+++ b/tests/tests/selinux/selinuxTargetSdk27/AndroidManifest.xml
@@ -22,6 +22,10 @@
untrusted_app_27 selinux domain -->
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="27" />
+ <!-- INTERNET permission allows apps with targetSdkValue==27 to list the
+ device's network interfaces -->
+ <uses-permission android:name="android.permission.INTERNET" />
+
<application>
<uses-library android:name="android.test.runner" />
</application>
@@ -34,4 +38,3 @@
</instrumentation>
</manifest>
-
diff --git a/tests/tests/selinux/selinuxTargetSdk27/src/android/security/SELinuxTargetSdkTest.java b/tests/tests/selinux/selinuxTargetSdk27/src/android/security/SELinuxTargetSdkTest.java
index e7c0e67..a784464 100644
--- a/tests/tests/selinux/selinuxTargetSdk27/src/android/security/SELinuxTargetSdkTest.java
+++ b/tests/tests/selinux/selinuxTargetSdk27/src/android/security/SELinuxTargetSdkTest.java
@@ -65,7 +65,7 @@
checkDex2oatAccess(true);
}
- public void testNoExecuteOnly() throws IOException {
- noExecuteOnly();
+ public void testNetworkInterface() throws Exception {
+ checkNetworkInterface_returnsHardwareAddresses();
}
}
diff --git a/tests/tests/selinux/selinuxTargetSdk28/AndroidManifest.xml b/tests/tests/selinux/selinuxTargetSdk28/AndroidManifest.xml
index af86fa5..97d15c5 100644
--- a/tests/tests/selinux/selinuxTargetSdk28/AndroidManifest.xml
+++ b/tests/tests/selinux/selinuxTargetSdk28/AndroidManifest.xml
@@ -22,6 +22,10 @@
untrusted_app_28 selinux domain -->
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="28" />
+ <!-- INTERNET permission allows apps with targetSdkValue==28 to list the
+ device's network interfaces -->
+ <uses-permission android:name="android.permission.INTERNET" />
+
<application>
<uses-library android:name="android.test.runner" />
</application>
@@ -34,4 +38,3 @@
</instrumentation>
</manifest>
-
diff --git a/tests/tests/selinux/selinuxTargetSdk28/src/android/security/SELinuxTargetSdkTest.java b/tests/tests/selinux/selinuxTargetSdk28/src/android/security/SELinuxTargetSdkTest.java
index 1ab9706..880ae1a 100644
--- a/tests/tests/selinux/selinuxTargetSdk28/src/android/security/SELinuxTargetSdkTest.java
+++ b/tests/tests/selinux/selinuxTargetSdk28/src/android/security/SELinuxTargetSdkTest.java
@@ -65,7 +65,7 @@
checkDex2oatAccess(true);
}
- public void testNoExecuteOnly() throws IOException {
- noExecuteOnly();
+ public void testNetworkInterface() throws Exception {
+ checkNetworkInterface_returnsHardwareAddresses();
}
}
diff --git a/tests/tests/selinux/selinuxTargetSdk29/Android.bp b/tests/tests/selinux/selinuxTargetSdk29/Android.bp
new file mode 100644
index 0000000..bd62d00
--- /dev/null
+++ b/tests/tests/selinux/selinuxTargetSdk29/Android.bp
@@ -0,0 +1,51 @@
+//
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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: "CtsSelinuxTargetSdk29TestCases",
+ defaults: ["cts_defaults"],
+ compile_multilib: "both",
+ static_libs: [
+ "ctstestrunner-axt",
+ "compatibility-device-util-axt",
+ ],
+ libs: [
+ "android.test.base.stubs",
+ "android.test.runner.stubs",
+ ],
+ jni_libs: [
+ "libbase",
+ "libc++",
+ "libcrypto",
+ "libcts_jni",
+ "libctsselinux_jni",
+ "libnativehelper",
+ "libnativehelper_compat_libc++",
+ "libpackagelistparser",
+ "libpcre2",
+ "libselinux",
+ ],
+ srcs: [
+ "src/**/*.java",
+ "common/**/*.java",
+ ],
+ platform_apis: true,
+ test_suites: [
+ "cts",
+ "vts",
+ "general-tests",
+ ],
+ min_sdk_version: "21",
+}
diff --git a/tests/tests/selinux/selinuxTargetSdk29/AndroidManifest.xml b/tests/tests/selinux/selinuxTargetSdk29/AndroidManifest.xml
new file mode 100644
index 0000000..3e4e7c4
--- /dev/null
+++ b/tests/tests/selinux/selinuxTargetSdk29/AndroidManifest.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.selinuxtargetsdk29.cts">
+
+ <!-- This app tests that apps with targetSdkValue==29 are placed in the
+ untrusted_app_29 selinux domain -->
+ <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="29" />
+
+ <!-- INTERNET permission allows apps with targetSdkValue==29 to list the
+ device's network interfaces -->
+ <uses-permission android:name="android.permission.INTERNET" />
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.selinuxtargetsdk29.cts"
+ android:label="CTS tests for permissions enforce by selinux based on targetSdkVersion">
+ <meta-data android:name="listener"
+ android:value="com.android.cts.runner.CtsTestRunListener" />
+ </instrumentation>
+
+</manifest>
diff --git a/tests/tests/selinux/selinuxTargetSdk29/AndroidTest.xml b/tests/tests/selinux/selinuxTargetSdk29/AndroidTest.xml
new file mode 100644
index 0000000..bed0f0a
--- /dev/null
+++ b/tests/tests/selinux/selinuxTargetSdk29/AndroidTest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Config for CTS Permission Selinux 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_instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
+ <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+ <option name="not-shardable" value="true" />
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="CtsSelinuxTargetSdk29TestCases.apk" />
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="android.selinuxtargetsdk29.cts" />
+ <option name="runtime-hint" value="2m" />
+ </test>
+</configuration>
diff --git a/tests/tests/selinux/selinuxTargetSdk29/common b/tests/tests/selinux/selinuxTargetSdk29/common
new file mode 120000
index 0000000..581eb17
--- /dev/null
+++ b/tests/tests/selinux/selinuxTargetSdk29/common
@@ -0,0 +1 @@
+../common/src
\ No newline at end of file
diff --git a/tests/tests/selinux/selinuxTargetSdk29/src/android/security/SELinuxTargetSdkTest.java b/tests/tests/selinux/selinuxTargetSdk29/src/android/security/SELinuxTargetSdkTest.java
new file mode 100644
index 0000000..1f1eaaa
--- /dev/null
+++ b/tests/tests/selinux/selinuxTargetSdk29/src/android/security/SELinuxTargetSdkTest.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security;
+
+import android.test.AndroidTestCase;
+import com.android.compatibility.common.util.PropertyUtil;
+import java.io.IOException;
+
+/**
+ * Verify the selinux domain for apps running with targetSdkVersion==29
+ */
+public class SELinuxTargetSdkTest extends SELinuxTargetSdkTestBase
+{
+ /**
+ * Verify that net.dns properties may not be read
+ */
+ public void testNoDns() throws IOException {
+ noDns();
+ }
+
+ public void testDex2oat() throws Exception {
+ /*
+ * Apps with a vendor image older than Q may access the dex2oat executable through
+ * selinux policy on the vendor partition because the permission was granted in public
+ * policy for appdomain.
+ */
+ if (PropertyUtil.isVendorApiLevelNewerThan(28)) {
+ checkDex2oatAccess(false);
+ }
+ }
+
+ public void testNetlinkRouteGetlinkSucceeds() throws IOException {
+ checkNetlinkRouteGetlink(true);
+ }
+
+ public void testNetlinkRouteBindSucceeds() throws IOException {
+ checkNetlinkRouteBind(true);
+ }
+
+ public void testCanNotExecuteFromHomeDir() throws Exception {
+ assertFalse(canExecuteFromHomeDir());
+ }
+
+ /**
+ * Verify that selinux context is the expected domain based on
+ * targetSdkVersion = 29
+ */
+ public void testAppDomainContext() throws IOException {
+ String context = "u:r:untrusted_app_29:s0:c[0-9]+,c[0-9]+,c[0-9]+,c[0-9]+";
+ String msg = "Untrusted apps with targetSdkVersion 29 " +
+ "must run in the untrusted_app selinux domain and use the levelFrom=all " +
+ "selector in SELinux seapp_contexts which adds four category types " +
+ "to the app's selinux context.\n" +
+ "Example expected value: u:r:untrusted_app:s0:c89,c256,c512,c768\n" +
+ "Actual value: ";
+ appDomainContext(context, msg);
+ }
+
+ /**
+ * Verify that selinux context is the expected type based on
+ * targetSdkVersion = 29
+ */
+ public void testAppDataContext() throws Exception {
+ String context = "u:object_r:app_data_file:s0:c[0-9]+,c[0-9]+,c[0-9]+,c[0-9]+";
+ String msg = "Untrusted apps with targetSdkVersion 29 " +
+ "must use the app_data_file selinux context and use the levelFrom=all " +
+ "selector in SELinux seapp_contexts which adds four category types " +
+ "to the app_data_file context.\n" +
+ "Example expected value: u:object_r:app_data_file:s0:c89,c256,c512,c768\n" +
+ "Actual value: ";
+ appDataContext(context, msg);
+ }
+
+ public void testNetworkInterface() throws Exception {
+ checkNetworkInterface_returnsHardwareAddresses();
+ }
+}
diff --git a/tests/tests/selinux/selinuxTargetSdkCurrent/src/android/security/SELinuxTargetSdkTest.java b/tests/tests/selinux/selinuxTargetSdkCurrent/src/android/security/SELinuxTargetSdkTest.java
index 859864e..05fad31 100644
--- a/tests/tests/selinux/selinuxTargetSdkCurrent/src/android/security/SELinuxTargetSdkTest.java
+++ b/tests/tests/selinux/selinuxTargetSdkCurrent/src/android/security/SELinuxTargetSdkTest.java
@@ -34,11 +34,11 @@
}
public void testNoNetlinkRouteGetlink() throws IOException {
- noNetlinkRouteGetlink();
+ checkNetlinkRouteGetlink(false);
}
public void testNoNetlinkRouteBind() throws IOException {
- noNetlinkRouteBind();
+ checkNetlinkRouteBind(false);
}
public void testCanNotExecuteFromHomeDir() throws Exception {
diff --git a/tests/tests/slice/src/android/slice/cts/SliceBindingTest.java b/tests/tests/slice/src/android/slice/cts/SliceBindingTest.java
index 8a655cd..710ac94 100644
--- a/tests/tests/slice/src/android/slice/cts/SliceBindingTest.java
+++ b/tests/tests/slice/src/android/slice/cts/SliceBindingTest.java
@@ -28,6 +28,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.PackageManager;
import android.graphics.drawable.Icon;
import android.net.Uri;
import android.os.Bundle;
@@ -54,9 +55,13 @@
private static final Uri BASE_URI = Uri.parse("content://android.slice.cts/");
private final Context mContext = InstrumentationRegistry.getContext();
private final SliceManager mSliceManager = mContext.getSystemService(SliceManager.class);
+ private boolean isSliceDisabled = mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_SLICES_DISABLED);
@Test
public void testProcess() {
+ if (isSliceDisabled) {
+ return;
+ }
sFlag = false;
mSliceManager.bindSlice(BASE_URI.buildUpon().appendPath("set_flag").build(),
Collections.emptySet());
@@ -71,6 +76,9 @@
@Test
public void testSliceUri() {
+ if (isSliceDisabled) {
+ return;
+ }
Slice s = mSliceManager.bindSlice(BASE_URI,
Collections.emptySet());
assertEquals(BASE_URI, s.getUri());
@@ -78,6 +86,9 @@
@Test
public void testSubSlice() {
+ if (isSliceDisabled) {
+ return;
+ }
Uri uri = BASE_URI.buildUpon().appendPath("subslice").build();
Slice s = mSliceManager.bindSlice(uri, Collections.emptySet());
assertEquals(uri, s.getUri());
@@ -93,6 +104,9 @@
@Test
public void testText() {
+ if (isSliceDisabled) {
+ return;
+ }
Uri uri = BASE_URI.buildUpon().appendPath("text").build();
Slice s = mSliceManager.bindSlice(uri,
Collections.emptySet());
@@ -107,6 +121,9 @@
@Test
public void testIcon() {
+ if (isSliceDisabled) {
+ return;
+ }
Uri uri = BASE_URI.buildUpon().appendPath("icon").build();
Slice s = mSliceManager.bindSlice(uri,
Collections.emptySet());
@@ -121,6 +138,9 @@
@Test
public void testAction() {
+ if (isSliceDisabled) {
+ return;
+ }
sFlag = false;
CountDownLatch latch = new CountDownLatch(1);
BroadcastReceiver receiver = new BroadcastReceiver() {
@@ -155,6 +175,9 @@
@Test
public void testInt() {
+ if (isSliceDisabled) {
+ return;
+ }
Uri uri = BASE_URI.buildUpon().appendPath("int").build();
Slice s = mSliceManager.bindSlice(uri, Collections.emptySet());
assertEquals(uri, s.getUri());
@@ -167,6 +190,9 @@
@Test
public void testTimestamp() {
+ if (isSliceDisabled) {
+ return;
+ }
Uri uri = BASE_URI.buildUpon().appendPath("timestamp").build();
Slice s = mSliceManager.bindSlice(uri,
Collections.emptySet());
@@ -180,6 +206,9 @@
@Test
public void testHints() {
+ if (isSliceDisabled) {
+ return;
+ }
// Note this tests that hints are propagated through to the client but not that any specific
// hints have any effects.
Uri uri = BASE_URI.buildUpon().appendPath("hints").build();
@@ -194,6 +223,9 @@
@Test
public void testHasHints() {
+ if (isSliceDisabled) {
+ return;
+ }
Uri uri = BASE_URI.buildUpon().appendPath("hints").build();
Slice s = mSliceManager.bindSlice(uri, Collections.emptySet());
@@ -203,6 +235,9 @@
@Test
public void testBundle() {
+ if (isSliceDisabled) {
+ return;
+ }
Uri uri = BASE_URI.buildUpon().appendPath("bundle").build();
Slice s = mSliceManager.bindSlice(uri, Collections.emptySet());
assertEquals(uri, s.getUri());
@@ -217,6 +252,9 @@
@Test
public void testGetDescendants() {
+ if (isSliceDisabled) {
+ return;
+ }
Collection<Uri> allUris = mSliceManager.getSliceDescendants(BASE_URI);
assertEquals(SliceProvider.PATHS.length, allUris.size());
Iterator<Uri> it = allUris.iterator();
@@ -230,6 +268,9 @@
@Test
public void testGetSliceSpec() {
+ if (isSliceDisabled) {
+ return;
+ }
Uri uri = BASE_URI.buildUpon().appendPath("spec").build();
Slice s = mSliceManager.bindSlice(uri, Collections.emptySet());
assertEquals(new SliceSpec(SliceProvider.SPEC_TYPE, SliceProvider.SPEC_REV), s.getSpec());
diff --git a/tests/tests/slice/src/android/slice/cts/SliceManagerTest.java b/tests/tests/slice/src/android/slice/cts/SliceManagerTest.java
index 69d8d80..1e5c718 100644
--- a/tests/tests/slice/src/android/slice/cts/SliceManagerTest.java
+++ b/tests/tests/slice/src/android/slice/cts/SliceManagerTest.java
@@ -30,6 +30,7 @@
import android.app.slice.SliceManager;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
@@ -54,9 +55,14 @@
private static final Uri BASE_URI = Uri.parse("content://android.slice.cts.local/main");
private final Context mContext = InstrumentationRegistry.getContext();
private final SliceManager mSliceManager = mContext.getSystemService(SliceManager.class);
+ private boolean isSliceDisabled = mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_SLICES_DISABLED);
+
@Before
public void setup() {
+ if (isSliceDisabled) {
+ return;
+ }
LocalSliceProvider.sProxy = mock(SliceProvider.class);
try {
mSliceManager.unpinSlice(BASE_URI);
@@ -66,6 +72,9 @@
@After
public void teardown() throws Exception {
+ if (isSliceDisabled) {
+ return;
+ }
try {
mSliceManager.unpinSlice(BASE_URI);
} catch (Exception e) {
@@ -74,6 +83,9 @@
@Test
public void testPinSlice() throws Exception {
+ if (isSliceDisabled) {
+ return;
+ }
mSliceManager.pinSlice(BASE_URI, Collections.emptySet());
verify(LocalSliceProvider.sProxy, timeout(2000)).onSlicePinned(eq(BASE_URI));
@@ -81,6 +93,9 @@
@Test
public void testUnpinSlice() throws Exception {
+ if (isSliceDisabled) {
+ return;
+ }
mSliceManager.pinSlice(BASE_URI, Collections.emptySet());
verify(LocalSliceProvider.sProxy, timeout(2000)).onSlicePinned(eq(BASE_URI));
@@ -92,6 +107,9 @@
@Test
public void testPinList() {
+ if (isSliceDisabled) {
+ return;
+ }
Uri uri = BASE_URI;
Uri longerUri = uri.buildUpon().appendPath("something").build();
try {
@@ -111,6 +129,9 @@
@Test
public void testMapIntentToUri() {
+ if (isSliceDisabled) {
+ return;
+ }
Intent intent = new Intent("android.slice.cts.action.TEST_ACTION");
intent.setPackage("android.slice.cts");
intent.putExtra("path", "intent");
@@ -127,6 +148,9 @@
@Test
public void testOnCreatePermissionSlice() {
+ if (isSliceDisabled) {
+ return;
+ }
LocalSliceProvider.sAnswer = invocation -> {
throw new SecurityException("No slices allowed");
};
diff --git a/tests/tests/slice/src/android/slice/cts/SlicePermissionsTest.java b/tests/tests/slice/src/android/slice/cts/SlicePermissionsTest.java
index 5081943..c36e7c9 100644
--- a/tests/tests/slice/src/android/slice/cts/SlicePermissionsTest.java
+++ b/tests/tests/slice/src/android/slice/cts/SlicePermissionsTest.java
@@ -14,6 +14,7 @@
package android.slice.cts;
+import android.content.pm.PackageManager;
import static android.content.pm.PackageManager.PERMISSION_DENIED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
@@ -42,9 +43,13 @@
private int mTestUid;
private int mTestPid;
private SliceManager mSliceManager;
+ private boolean isSliceDisabled = mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_SLICES_DISABLED);
@Before
public void setup() throws NameNotFoundException {
+ if(isSliceDisabled) {
+ return;
+ }
mSliceManager = mContext.getSystemService(SliceManager.class);
mTestPkg = mContext.getPackageName();
mTestUid = mContext.getPackageManager().getPackageUid(mTestPkg, 0);
@@ -53,11 +58,17 @@
@After
public void tearDown() {
+ if (isSliceDisabled) {
+ return;
+ }
mSliceManager.revokeSlicePermission(mTestPkg, BASE_URI);
}
@Test
public void testGrant() {
+ if (isSliceDisabled) {
+ return;
+ }
assertEquals(PERMISSION_DENIED,
mSliceManager.checkSlicePermission(BASE_URI, mTestPid, mTestUid));
@@ -69,6 +80,9 @@
@Test
public void testGrantParent() {
+ if (isSliceDisabled) {
+ return;
+ }
Uri uri = BASE_URI.buildUpon()
.appendPath("something")
.build();
@@ -84,6 +98,9 @@
@Test
public void testGrantParentExpands() {
+ if (isSliceDisabled) {
+ return;
+ }
Uri uri = BASE_URI.buildUpon()
.appendPath("something")
.build();
@@ -110,6 +127,9 @@
@Test
public void testGrantChild() {
+ if (isSliceDisabled) {
+ return;
+ }
Uri uri = BASE_URI.buildUpon()
.appendPath("something")
.build();
@@ -126,6 +146,9 @@
@Test
public void testRevoke() {
+ if (isSliceDisabled) {
+ return;
+ }
assertEquals(PERMISSION_DENIED,
mSliceManager.checkSlicePermission(BASE_URI, mTestPid, mTestUid));
@@ -142,6 +165,9 @@
@Test
public void testRevokeParent() {
+ if (isSliceDisabled) {
+ return;
+ }
Uri uri = BASE_URI.buildUpon()
.appendPath("something")
.build();
@@ -162,6 +188,9 @@
@Test
public void testRevokeChild() {
+ if (isSliceDisabled) {
+ return;
+ }
Uri uri = BASE_URI.buildUpon()
.appendPath("something")
.build();
diff --git a/tests/tests/systemui/OWNERS b/tests/tests/systemui/OWNERS
index 583baa0..22ceb12 100644
--- a/tests/tests/systemui/OWNERS
+++ b/tests/tests/systemui/OWNERS
@@ -1,4 +1,4 @@
-# Bug component: 136515
+# Bug component: 181502
evanlaird@google.com
felkachang@google.com
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/CellInfoTest.java b/tests/tests/telephony/current/src/android/telephony/cts/CellInfoTest.java
index 4423ed5..734d85f 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/CellInfoTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/CellInfoTest.java
@@ -798,8 +798,16 @@
wcdma.getDbm();
int asuLevel = wcdma.getAsuLevel();
- assertTrue("getAsuLevel() out of range 0..96, 255, asuLevel=" + asuLevel,
- asuLevel == 255 || (asuLevel >= 0 && asuLevel <= 96));
+ if (wcdma.getRscp() != CellInfo.UNAVAILABLE) {
+ assertTrue("getAsuLevel() out of range 0..96, 255), asuLevel=" + asuLevel,
+ asuLevel == 255 || (asuLevel >= 0 && asuLevel <= 96));
+ } else if (wcdma.getRssi() != CellInfo.UNAVAILABLE) {
+ assertTrue("getAsuLevel() out of range 0..31, 99), asuLevel=" + asuLevel,
+ asuLevel == 99 || (asuLevel >= 0 && asuLevel <= 31));
+ } else {
+ assertTrue("getAsuLevel() out of range 0..96, 255), asuLevel=" + asuLevel,
+ asuLevel == 255);
+ }
int level = wcdma.getLevel();
assertTrue("getLevel() out of range [0,4], level=" + level, level >= 0 && level <= 4);
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 b5f3417..dcdefb9 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/SubscriptionManagerTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/SubscriptionManagerTest.java
@@ -167,7 +167,8 @@
@Test
public void testGetActiveSubscriptionInfoForIcc() throws Exception {
if (!isSupported()) return;
- SubscriptionInfo info = mSm.getActiveSubscriptionInfo(mSubId);
+ SubscriptionInfo info = ShellIdentityUtils.invokeMethodWithShellPermissions(mSm,
+ (sm) -> sm.getActiveSubscriptionInfo(mSubId));
assertNotNull(ShellIdentityUtils.invokeMethodWithShellPermissions(mSm,
(sm) -> sm.getActiveSubscriptionInfoForIcc(info.getIccId())));
}
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 4492561..de40198 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java
@@ -168,6 +168,7 @@
private static final int MIN_FPLMN_NUM = 3;
private static final String TEST_FORWARD_NUMBER = "54321";
+ private static final String TESTING_PLMN = "12345";
static {
EMERGENCY_NUMBER_SOURCE_SET = new HashSet<Integer>();
@@ -512,6 +513,11 @@
(tm) -> tm.getImei());
ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
(tm) -> tm.getImei(mTelephonyManager.getSlotIndex()));
+ ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
+ (tm) -> tm.isManualNetworkSelectionAllowed());
+ ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
+ (tm) -> tm.getManualNetworkSelectionPlmn());
+
mTelephonyManager.getPhoneCount();
mTelephonyManager.getDataEnabled();
mTelephonyManager.getNetworkSpecifier();
@@ -540,8 +546,6 @@
}
TelephonyManager.getDefaultRespondViaMessageApplication(getContext(), false);
- ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
- (tm) -> tm.getPhoneCapability());
}
@Test
@@ -1764,6 +1768,52 @@
}
/**
+ * Tests that the device properly reports the contents of ManualNetworkSelectionPlmn
+ * The setting is not persisted selection
+ */
+ @Test
+ public void testGetManualNetworkSelectionPlmnNonPersisted() {
+ if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+ return;
+ }
+
+ try {
+ ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
+ (tm) -> tm.setNetworkSelectionModeManual(
+ TESTING_PLMN/* operatorNumeric */, false /* persistSelection */));
+ String plmn = ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
+ (tm) -> tm.getManualNetworkSelectionPlmn());
+ assertEquals(TESTING_PLMN, plmn);
+ } finally {
+ ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager,
+ (tm) -> tm.setNetworkSelectionModeAutomatic());
+ }
+ }
+
+ /**
+ * Tests that the device properly reports the contents of ManualNetworkSelectionPlmn
+ * The setting is persisted selection
+ */
+ @Test
+ public void testGetManualNetworkSelectionPlmnPersisted() {
+ if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+ return;
+ }
+
+ try {
+ ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
+ (tm) -> tm.setNetworkSelectionModeManual(
+ TESTING_PLMN/* operatorNumeric */, true /* persistSelection */));
+ String plmn = ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
+ (tm) -> tm.getManualNetworkSelectionPlmn());
+ assertEquals(TESTING_PLMN, plmn);
+ } finally {
+ ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager,
+ (tm) -> tm.setNetworkSelectionModeAutomatic());
+ }
+ }
+
+ /**
* Verify that TelephonyManager.getCardIdForDefaultEuicc returns a positive value or either
* UNINITIALIZED_CARD_ID or UNSUPPORTED_CARD_ID.
*/
@@ -1819,6 +1869,27 @@
}
/**
+ * Tests that the device properly reports the contents of NetworkSelectionMode
+ */
+ @Test
+ public void testGetNetworkSelectionMode() {
+ if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+ return;
+ }
+
+ try {
+ ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager,
+ (tm) -> tm.setNetworkSelectionModeAutomatic());
+ } catch (Exception e) {
+ }
+
+ int networkMode = ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
+ (tm) -> tm.getNetworkSelectionMode());
+
+ assertEquals(TelephonyManager.NETWORK_SELECTION_MODE_AUTO, networkMode);
+ }
+
+ /**
* Tests that the device properly sets the network selection mode to automatic.
* Expects a security exception since the caller does not have carrier privileges.
*/
@@ -1855,6 +1926,19 @@
}
/**
+ * Tests that the device properly check whether selection mode was manual.
+ */
+ @Test
+ public void testIsManualNetworkSelectionAllowed() {
+ if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+ Log.d(TAG, "Skipping test that requires FEATURE_TELEPHONY");
+ return;
+ }
+ assertTrue(ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
+ (tm) -> tm.isManualNetworkSelectionAllowed()));
+ }
+
+ /**
* Construct a CallAttributes object and test getters.
*/
@Test
diff --git a/tests/tests/text/Android.bp b/tests/tests/text/Android.bp
index f3d34bf..2909777 100644
--- a/tests/tests/text/Android.bp
+++ b/tests/tests/text/Android.bp
@@ -16,6 +16,7 @@
name: "CtsTextTestCases",
defaults: ["cts_defaults"],
sdk_version: "test_current",
+ stl: "c++_shared",
srcs: [
"src/**/*.java",
@@ -38,7 +39,7 @@
jni_libs: [
"libctstext_jni",
- "libc++",
+ "libnativehelper_compat_libc++",
],
// Include both the 32 and 64 bit versions of libctstext_jni, where
// applicable.
diff --git a/tests/tests/text/jni/Android.bp b/tests/tests/text/jni/Android.bp
index 8055f40..7821605 100644
--- a/tests/tests/text/jni/Android.bp
+++ b/tests/tests/text/jni/Android.bp
@@ -14,9 +14,10 @@
cc_library {
name: "libctstext_jni",
+ sdk_version: "current",
srcs: [
"CtsTextJniOnLoad.cpp",
"android_text_format_cts_NativeTimeFunctions.cpp",
],
- static_libs: ["libnativehelper"],
+ shared_libs: ["libnativehelper_compat_libc++"],
}
diff --git a/tests/tests/view/AndroidManifest.xml b/tests/tests/view/AndroidManifest.xml
index 72d3fd1..fa5043a 100644
--- a/tests/tests/view/AndroidManifest.xml
+++ b/tests/tests/view/AndroidManifest.xml
@@ -352,7 +352,7 @@
</intent-filter>
</activity>
- <activity android:name="android.view.cts.KeyEventInterceptTestActivity">
+ <activity android:name="android.view.cts.InputEventInterceptTestActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</intent-filter>
diff --git a/tests/tests/view/src/android/view/cts/InputDeviceEnabledTest.java b/tests/tests/view/src/android/view/cts/InputDeviceEnabledTest.java
index c7f6591..fb1cb13 100644
--- a/tests/tests/view/src/android/view/cts/InputDeviceEnabledTest.java
+++ b/tests/tests/view/src/android/view/cts/InputDeviceEnabledTest.java
@@ -23,9 +23,9 @@
import android.hardware.input.InputManager;
import android.view.InputDevice;
-import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
import org.junit.Before;
import org.junit.Test;
@@ -38,12 +38,11 @@
@RunWith(AndroidJUnit4.class)
public class InputDeviceEnabledTest {
private InputManager mInputManager;
- private Instrumentation mInstrumentation;
@Before
public void setup() {
- mInstrumentation = InstrumentationRegistry.getInstrumentation();
- mInputManager = mInstrumentation.getTargetContext().getSystemService(InputManager.class);
+ Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+ mInputManager = instrumentation.getTargetContext().getSystemService(InputManager.class);
assertNotNull(mInputManager);
}
diff --git a/tests/tests/view/src/android/view/cts/InputEventInterceptTestActivity.java b/tests/tests/view/src/android/view/cts/InputEventInterceptTestActivity.java
new file mode 100644
index 0000000..e3bfe77
--- /dev/null
+++ b/tests/tests/view/src/android/view/cts/InputEventInterceptTestActivity.java
@@ -0,0 +1,47 @@
+/*
+ * 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.view.cts;
+
+import android.app.Activity;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+
+public class InputEventInterceptTestActivity extends Activity {
+ final BlockingQueue<KeyEvent> mKeyEvents = new LinkedBlockingQueue<>();
+ final BlockingQueue<MotionEvent> mMotionEvents = new LinkedBlockingQueue<>();
+
+ @Override
+ public boolean dispatchKeyEvent(KeyEvent event) {
+ mKeyEvents.add(event);
+ return true;
+ }
+
+ @Override
+ public boolean dispatchGenericMotionEvent(MotionEvent event) {
+ mMotionEvents.add(event);
+ return true;
+ }
+
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent event) {
+ mMotionEvents.add(event);
+ return true;
+ }
+}
diff --git a/tests/tests/view/src/android/view/cts/KeyEventInterceptTest.java b/tests/tests/view/src/android/view/cts/KeyEventInterceptTest.java
index 245dee8..abae9e7 100644
--- a/tests/tests/view/src/android/view/cts/KeyEventInterceptTest.java
+++ b/tests/tests/view/src/android/view/cts/KeyEventInterceptTest.java
@@ -24,10 +24,10 @@
import android.os.SystemClock;
import android.view.KeyEvent;
-import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.MediumTest;
+import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.rule.ActivityTestRule;
-import androidx.test.runner.AndroidJUnit4;
import com.android.compatibility.common.util.PollingCheck;
@@ -56,12 +56,12 @@
@MediumTest
@RunWith(AndroidJUnit4.class)
public class KeyEventInterceptTest {
- private KeyEventInterceptTestActivity mActivity;
+ private InputEventInterceptTestActivity mActivity;
private Instrumentation mInstrumentation;
@Rule
- public ActivityTestRule<KeyEventInterceptTestActivity> mActivityRule =
- new ActivityTestRule<>(KeyEventInterceptTestActivity.class);
+ public ActivityTestRule<InputEventInterceptTestActivity> mActivityRule =
+ new ActivityTestRule<>(InputEventInterceptTestActivity.class);
@Before
public void setup() {
@@ -96,10 +96,10 @@
}
private void testKeyCodeHomeShortcut(int metaState) {
- long downTime = SystemClock.uptimeMillis();
+ final long downTime = SystemClock.uptimeMillis();
injectEvent(new KeyEvent(downTime, downTime, KeyEvent.ACTION_DOWN,
KeyEvent.KEYCODE_ENTER, 0, metaState));
- injectEvent(new KeyEvent(downTime, downTime + 1, KeyEvent.ACTION_UP,
+ injectEvent(new KeyEvent(downTime, SystemClock.uptimeMillis(), KeyEvent.ACTION_UP,
KeyEvent.KEYCODE_ENTER, 0, metaState));
assertKeyNotReceived();
diff --git a/tests/tests/view/src/android/view/cts/KeyEventInterceptTestActivity.java b/tests/tests/view/src/android/view/cts/KeyEventInterceptTestActivity.java
deleted file mode 100644
index 2422a61..0000000
--- a/tests/tests/view/src/android/view/cts/KeyEventInterceptTestActivity.java
+++ /dev/null
@@ -1,33 +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.view.cts;
-
-import android.app.Activity;
-import android.view.KeyEvent;
-
-import java.util.LinkedList;
-import java.util.Queue;
-
-public class KeyEventInterceptTestActivity extends Activity {
- final Queue<KeyEvent> mKeyEvents = new LinkedList<>();
-
- @Override
- public boolean dispatchKeyEvent(KeyEvent event) {
- mKeyEvents.add(event);
- return true;
- }
-}
diff --git a/tests/tests/view/src/android/view/cts/VerifyInputEventTest.java b/tests/tests/view/src/android/view/cts/VerifyInputEventTest.java
new file mode 100644
index 0000000..fdda4e3
--- /dev/null
+++ b/tests/tests/view/src/android/view/cts/VerifyInputEventTest.java
@@ -0,0 +1,320 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.InputDevice.SOURCE_JOYSTICK;
+import static android.view.KeyEvent.FLAG_CANCELED;
+import static android.view.KeyEvent.KEYCODE_A;
+import static android.view.MotionEvent.FLAG_WINDOW_IS_OBSCURED;
+import static android.view.MotionEvent.FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.app.Instrumentation;
+import android.app.UiAutomation;
+import android.graphics.Point;
+import android.hardware.input.InputManager;
+import android.os.SystemClock;
+import android.view.InputEvent;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.VerifiedInputEvent;
+import android.view.VerifiedKeyEvent;
+import android.view.VerifiedMotionEvent;
+import android.view.View;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.rule.ActivityTestRule;
+
+import com.android.compatibility.common.util.PollingCheck;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+
+/**
+ * Test {@link android.hardware.input.InputManager#verifyInputEvent(InputEvent)} functionality.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class VerifyInputEventTest {
+ private static final int NANOS_PER_MILLISECOND = 1000000;
+ private static final float STRICT_TOLERANCE = 0;
+ private static final int INJECTED_EVENT_DEVICE_ID = -1;
+
+ private InputManager mInputManager;
+ private UiAutomation mAutomation;
+ private InputEventInterceptTestActivity mActivity;
+
+ @Rule
+ public ActivityTestRule<InputEventInterceptTestActivity> mActivityRule =
+ new ActivityTestRule<>(InputEventInterceptTestActivity.class);
+
+ @Before
+ public void setup() {
+ Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+ mInputManager = instrumentation.getTargetContext().getSystemService(InputManager.class);
+ assertNotNull(mInputManager);
+ mAutomation = instrumentation.getUiAutomation();
+ mActivity = mActivityRule.getActivity();
+ PollingCheck.waitFor(mActivity::hasWindowFocus);
+ }
+
+ @Test
+ public void testKeyEvent() {
+ final long downTime = SystemClock.uptimeMillis();
+ final int keyCode = KEYCODE_A;
+ KeyEvent downEvent = new KeyEvent(downTime, downTime,
+ KeyEvent.ACTION_DOWN, keyCode, 0 /* repeatCount */);
+ mAutomation.injectInputEvent(downEvent, true);
+ KeyEvent received = waitForKey();
+ VerifiedInputEvent verified = mInputManager.verifyInputEvent(received);
+ assertNotNull(verified);
+ compareKeys(downEvent, verified);
+
+ // Send UP event for consistency
+ KeyEvent upEvent = new KeyEvent(downTime, SystemClock.uptimeMillis(),
+ KeyEvent.ACTION_UP, keyCode, 0 /* repeatCount */);
+ mAutomation.injectInputEvent(upEvent, true);
+ waitForKey();
+ }
+
+ /**
+ * Try to verify an event that did not come from system, and therefore does not have an hmac
+ * set properly.
+ * Verification should fail.
+ */
+ @Test
+ public void testKeyEventWithoutHmac() {
+ final long downTime = SystemClock.uptimeMillis();
+ final int keyCode = KEYCODE_A;
+ KeyEvent downEvent = new KeyEvent(downTime, downTime,
+ KeyEvent.ACTION_DOWN, keyCode, 0 /* repeatCount */);
+ mAutomation.injectInputEvent(downEvent, true);
+ waitForKey(); // we will not be using the received event
+ VerifiedInputEvent verified = mInputManager.verifyInputEvent(downEvent);
+ assertNull(verified);
+
+ // Send UP event for consistency
+ KeyEvent upEvent = new KeyEvent(downTime, SystemClock.uptimeMillis(),
+ KeyEvent.ACTION_UP, keyCode, 0 /* repeatCount */);
+ mAutomation.injectInputEvent(upEvent, true);
+ waitForKey();
+ }
+
+ /**
+ * Try to verify an event that came from system, but has been tempered with.
+ * Verification should fail.
+ */
+ @Test
+ public void testTamperedKeyEvent() {
+ final long downTime = SystemClock.uptimeMillis();
+ final int keyCode = KEYCODE_A;
+ KeyEvent downEvent = new KeyEvent(downTime, downTime,
+ KeyEvent.ACTION_DOWN, keyCode, 0 /* repeatCount */);
+ mAutomation.injectInputEvent(downEvent, true);
+ KeyEvent received = waitForKey();
+ received.setSource(SOURCE_JOYSTICK); // use the received event, but modify its source
+ VerifiedInputEvent verified = mInputManager.verifyInputEvent(received);
+ assertNull(verified);
+
+ // Send UP event for consistency
+ KeyEvent upEvent = new KeyEvent(downTime, SystemClock.uptimeMillis(),
+ KeyEvent.ACTION_UP, keyCode, 0 /* repeatCount */);
+ mAutomation.injectInputEvent(upEvent, true);
+ waitForKey();
+ }
+
+ @Test
+ public void testMotionEvent() {
+ final View view = mActivity.getWindow().getDecorView();
+ final Point point = getViewCenterOnScreen(view);
+ final long downTime = SystemClock.uptimeMillis();
+ MotionEvent downEvent = MotionEvent.obtain(downTime, downTime, MotionEvent.ACTION_DOWN,
+ point.x, point.y, 0 /*metaState*/);
+ mAutomation.injectInputEvent(downEvent, true);
+ MotionEvent received = waitForMotion();
+ VerifiedInputEvent verified = mInputManager.verifyInputEvent(received);
+ assertNotNull(verified);
+
+ compareMotions(downEvent, verified);
+
+ // Send UP event for consistency
+ MotionEvent upEvent = MotionEvent.obtain(downTime, downTime, MotionEvent.ACTION_UP,
+ point.x, point.y, 0 /*metaState*/);
+ mAutomation.injectInputEvent(upEvent, true);
+ waitForMotion();
+ }
+
+ /**
+ * Try to verify an event that did not come from system, and therefore does not have an hmac
+ * set properly.
+ * Verification should fail.
+ */
+ @Test
+ public void testMotionEventWithoutHmac() {
+ final View view = mActivity.getWindow().getDecorView();
+ final Point point = getViewCenterOnScreen(view);
+ final long downTime = SystemClock.uptimeMillis();
+ MotionEvent downEvent = MotionEvent.obtain(downTime, downTime, MotionEvent.ACTION_DOWN,
+ point.x, point.y, 0 /*metaState*/);
+ mAutomation.injectInputEvent(downEvent, true);
+ waitForMotion(); // we will not be using the received event
+ VerifiedInputEvent verified = mInputManager.verifyInputEvent(downEvent);
+ assertNull(verified);
+
+ // Send UP event for consistency
+ MotionEvent upEvent = MotionEvent.obtain(downTime, downTime, MotionEvent.ACTION_UP,
+ point.x, point.y, 0 /*metaState*/);
+ mAutomation.injectInputEvent(upEvent, true);
+ waitForMotion();
+ }
+
+ /**
+ * Try to verify an event that came from system, but has been tempered with.
+ * Verification should fail.
+ */
+ @Test
+ public void testTamperedMotionEvent() {
+ final View view = mActivity.getWindow().getDecorView();
+ final Point point = getViewCenterOnScreen(view);
+ final long downTime = SystemClock.uptimeMillis();
+ MotionEvent downEvent = MotionEvent.obtain(downTime, downTime, MotionEvent.ACTION_DOWN,
+ point.x, point.y, 0 /*metaState*/);
+ mAutomation.injectInputEvent(downEvent, true);
+ MotionEvent received = waitForMotion();
+ // use the received event, by modify its action
+ received.setAction(MotionEvent.ACTION_CANCEL);
+ VerifiedInputEvent verified = mInputManager.verifyInputEvent(received);
+ assertNull(verified);
+
+ // Send UP event for consistency
+ MotionEvent upEvent = MotionEvent.obtain(downTime, downTime, MotionEvent.ACTION_UP,
+ point.x, point.y, 0 /*metaState*/);
+ mAutomation.injectInputEvent(upEvent, true);
+ waitForMotion();
+ }
+
+ private static Point getViewCenterOnScreen(View view) {
+ final int[] location = new int[2];
+ view.getLocationOnScreen(location);
+ final int width = view.getWidth();
+ final int height = view.getHeight();
+
+ return new Point(location[0] + width / 2, location[1] + height / 2);
+ }
+
+ private KeyEvent waitForKey() {
+ return mActivity.mKeyEvents.poll();
+ }
+
+ private MotionEvent waitForMotion() {
+ return mActivity.mMotionEvents.poll();
+ }
+
+ private static void compareKeys(KeyEvent keyEvent, VerifiedInputEvent verified) {
+ assertEquals(INJECTED_EVENT_DEVICE_ID, verified.getDeviceId());
+ assertEquals(keyEvent.getEventTime() * NANOS_PER_MILLISECOND,
+ verified.getEventTimeNanos());
+ assertEquals(keyEvent.getSource(), verified.getSource());
+ assertEquals(DEFAULT_DISPLAY, verified.getDisplayId());
+
+ assertTrue(verified instanceof VerifiedKeyEvent);
+ VerifiedKeyEvent verifiedKey = (VerifiedKeyEvent) verified;
+
+ assertEquals(keyEvent.getAction(), verifiedKey.getAction());
+ assertEquals(keyEvent.getDownTime() * NANOS_PER_MILLISECOND,
+ verifiedKey.getDownTimeNanos());
+ compareKeyFlags(keyEvent.getFlags(), verifiedKey);
+ assertEquals(keyEvent.getKeyCode(), verifiedKey.getKeyCode());
+ assertEquals(keyEvent.getScanCode(), verifiedKey.getScanCode());
+ assertEquals(keyEvent.getMetaState(), verifiedKey.getMetaState());
+ assertEquals(keyEvent.getRepeatCount(), verifiedKey.getRepeatCount());
+ }
+
+ private static void compareMotions(MotionEvent motionEvent, VerifiedInputEvent verified) {
+ assertEquals(INJECTED_EVENT_DEVICE_ID, verified.getDeviceId());
+ assertEquals(motionEvent.getEventTime() * NANOS_PER_MILLISECOND,
+ verified.getEventTimeNanos());
+ assertEquals(motionEvent.getSource(), verified.getSource());
+ assertEquals(DEFAULT_DISPLAY, verified.getDisplayId());
+
+ assertTrue(verified instanceof VerifiedMotionEvent);
+ VerifiedMotionEvent verifiedMotion = (VerifiedMotionEvent) verified;
+
+ assertEquals(motionEvent.getRawX(), verifiedMotion.getRawX(), STRICT_TOLERANCE);
+ assertEquals(motionEvent.getRawY(), verifiedMotion.getRawY(), STRICT_TOLERANCE);
+ assertEquals(motionEvent.getActionMasked(), verifiedMotion.getActionMasked());
+ assertEquals(motionEvent.getDownTime() * NANOS_PER_MILLISECOND,
+ verifiedMotion.getDownTimeNanos());
+ compareMotionFlags(motionEvent.getFlags(), verifiedMotion);
+ assertEquals(motionEvent.getMetaState(), verifiedMotion.getMetaState());
+ assertEquals(motionEvent.getButtonState(), verifiedMotion.getButtonState());
+ }
+
+ private static void compareKeyFlags(int expectedFlags, VerifiedKeyEvent verified) {
+ // Separately check the value of verifiable flags
+ assertFlag(expectedFlags, FLAG_CANCELED, verified);
+ // All other flags should be null, because they are not verifiable
+ for (int i = 0; i < Integer.SIZE; i++) {
+ int flag = 1 << i;
+ if (flag == FLAG_CANCELED) {
+ continue;
+ }
+ assertNull(verified.getFlag(flag));
+ }
+ }
+
+ private static void compareMotionFlags(int expectedFlags, VerifiedMotionEvent verified) {
+ // Separately check the value of verifiable flags
+ assertFlag(expectedFlags, FLAG_WINDOW_IS_OBSCURED, verified);
+ assertFlag(expectedFlags, FLAG_WINDOW_IS_PARTIALLY_OBSCURED, verified);
+ // All other flags should be null, because they are not verifiable
+ for (int i = 0; i < Integer.SIZE; i++) {
+ int flag = 1 << i;
+ if (flag == FLAG_WINDOW_IS_OBSCURED
+ || flag == FLAG_WINDOW_IS_PARTIALLY_OBSCURED) {
+ continue;
+ }
+ assertNull(verified.getFlag(flag));
+ }
+ }
+
+ private static void assertFlag(int expectedFlags, int flag, VerifiedInputEvent verified) {
+ final Boolean actual;
+ if (verified instanceof VerifiedKeyEvent) {
+ actual = ((VerifiedKeyEvent) verified).getFlag(flag);
+ } else if (verified instanceof VerifiedMotionEvent) {
+ actual = ((VerifiedMotionEvent) verified).getFlag(flag);
+ } else {
+ fail("Unknown type of VerifiedInputEvent");
+ actual = null;
+ }
+ assertNotNull(actual);
+ boolean flagValue = (expectedFlags & flag) != 0;
+ assertEquals(flagValue, actual);
+ }
+}
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java b/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java
index 5980663..7419858 100644
--- a/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java
@@ -245,23 +245,30 @@
return;
}
- assertTrue(mSettings.getAllowFileAccess());
+ // TODO(b/148840827): Uncomment default value assertion when a new version of WebView
+ // where the change happened is dropped in master.
+ // assertFalse("File access should be off by default", mSettings.getAllowFileAccess());
+ mSettings.setAllowFileAccess(true);
+ assertTrue("Explicitly setting file access to true should work",
+ mSettings.getAllowFileAccess());
String fileUrl = TestHtmlConstants.getFileUrl(TestHtmlConstants.HELLO_WORLD_URL);
mOnUiThread.loadUrlAndWaitForCompletion(fileUrl);
- assertEquals(TestHtmlConstants.HELLO_WORLD_TITLE, mOnUiThread.getTitle());
+ assertEquals("Loading files on the file system should work with file access enabled",
+ TestHtmlConstants.HELLO_WORLD_TITLE, mOnUiThread.getTitle());
fileUrl = TestHtmlConstants.getFileUrl(TestHtmlConstants.BR_TAG_URL);
mSettings.setAllowFileAccess(false);
- assertFalse(mSettings.getAllowFileAccess());
+ assertFalse("Explicitly setting file access to false should work",
+ mSettings.getAllowFileAccess());
mOnUiThread.loadUrlAndWaitForCompletion(fileUrl);
- // android_asset URLs should still be loaded when even with file access
- // disabled.
- assertEquals(TestHtmlConstants.BR_TAG_TITLE, mOnUiThread.getTitle());
+ assertEquals(
+ "android_asset URLs should still be loaded when even with file access disabled",
+ TestHtmlConstants.BR_TAG_TITLE, mOnUiThread.getTitle());
- // Files on the file system should not be loaded.
mOnUiThread.loadUrlAndWaitForCompletion(TestHtmlConstants.LOCAL_FILESYSTEM_URL);
- assertEquals(TestHtmlConstants.WEBPAGE_NOT_AVAILABLE_TITLE, mOnUiThread.getTitle());
+ assertEquals("Files on the file system should not be loaded with file access disabled",
+ TestHtmlConstants.WEBPAGE_NOT_AVAILABLE_TITLE, mOnUiThread.getTitle());
}
public void testAccessCacheMode_defaultValue() throws Throwable {
@@ -1060,6 +1067,7 @@
writeFile("target.html", target);
mSettings.setJavaScriptEnabled(true);
+ mSettings.setAllowFileAccess(true);
// disable universal access from files
mSettings.setAllowUniversalAccessFromFileURLs(false);
mSettings.setAllowFileAccessFromFileURLs(enableXHR);
diff --git a/tests/tests/widget29/AndroidTest.xml b/tests/tests/widget29/AndroidTest.xml
index 39d3911..f867df3 100644
--- a/tests/tests/widget29/AndroidTest.xml
+++ b/tests/tests/widget29/AndroidTest.xml
@@ -18,6 +18,7 @@
<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" />
+ <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="CtsWidgetTestCases29.apk" />