Merge "Test for removing a shared element during activity transition" into oc-dev
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index 1787b42..ce6c76f 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -70,17 +70,17 @@
<!-- Needed by the Audio Quality Verifier to store the sound samples that will be mailed. -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <!-- Needed for Telecom self-managed ConnectionService tests. -->
+ <uses-permission android:name="android.permission.MANAGE_OWN_CALLS" />
+
<application android:label="@string/app_name"
android:icon="@drawable/icon"
- android:backupAgent="VerifierBackupAgent"
android:debuggable="true"
android:largeHeap="true"
android:theme="@android:style/Theme.DeviceDefault">
<meta-data android:name="SuiteName" android:value="CTS_VERIFIER" />
-
- <meta-data android:name="com.google.android.backup.api_key"
- android:value="AEdPqrEAAAAIbK6ldcOzoeRtQ1u1dFVJ1A7KetRhit-a1Xa82Q" />
+
<meta-data android:name="android.telephony.HIDE_VOICEMAIL_SETTINGS_MENU"
android:value="true"/>
<uses-library android:name="android.test.runner"/>
@@ -118,6 +118,32 @@
android:value="android.software.device_admin" />
</activity>
+ <activity android:name=".admin.tapjacking.DeviceAdminTapjackingTestActivity"
+ android:label="@string/da_tapjacking_test"
+ android:configChanges="keyboardHidden|orientation|screenSize">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.cts.intent.category.MANUAL_TEST" />
+ </intent-filter>
+ <meta-data android:name="test_category" android:value="@string/test_category_device_admin" />
+ <meta-data android:name="test_required_features"
+ android:value="android.software.device_admin" />
+ </activity>
+
+ <receiver android:name=".admin.tapjacking.EmptyDeviceAdminReceiver"
+ android:permission="android.permission.BIND_DEVICE_ADMIN">
+ <meta-data android:name="android.app.device_admin"
+ android:resource="@xml/tapjacking_device_admin" />
+ <intent-filter>
+ <action android:name="android.app.action.DEVICE_ADMIN_ENABLED"/>
+ </intent-filter>
+ </receiver>
+
+ <activity
+ android:name=".admin.tapjacking.OverlayingActivity"
+ android:theme="@style/OverlayTheme"
+ android:label="Overlaying Activity"/>
+
<activity android:name=".companion.CompanionDeviceTestActivity"
android:label="@string/companion_test"
android:configChanges="keyboardHidden|orientation|screenSize">
@@ -171,25 +197,6 @@
android:value="android.software.device_admin" />
</activity>
- <activity android:name=".backup.BackupTestActivity" android:label="@string/backup_test">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.cts.intent.category.MANUAL_TEST" />
- </intent-filter>
- <meta-data android:name="test_required_features"
- android:value="android.software.backup" />
- </activity>
-
- <!-- Further work is required for this test, b/32798562 -->
- <!-- activity android:name=".backup.BackupAccessibilityTestActivity" android:label="@string/backup_accessibility_test">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.cts.intent.category.MANUAL_TEST" />
- </intent-filter>
- <meta-data android:name="test_required_features"
- android:value="android.software.backup" />
- </activity -->
-
<!-- CTS Verifier Bluetooth Test Top Screen -->
<activity
android:name=".bluetooth.BluetoothTestActivity"
@@ -1059,6 +1066,17 @@
<meta-data android:name="test_required_features" android:value="android.hardware.location.gps" />
</activity>
+ <activity android:name=".location.GnssTtffTestsActivity"
+ android:label="@string/location_gnss_ttff_test"
+ android:screenOrientation="locked">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.cts.intent.category.MANUAL_TEST"/>
+ </intent-filter>
+ <meta-data android:name="test_category" android:value="@string/test_category_hardware"/>
+ <meta-data android:name="test_required_features" android:value="android.hardware.location.gps" />
+ </activity>
+
<activity android:name=".location.GnssMeasurementWhenNoLocationTestsActivity"
android:label="@string/location_gnss_measure_no_location_test"
android:screenOrientation="locked">
@@ -3092,6 +3110,22 @@
<meta-data
android:name="test_required_features"
android:value="android.hardware.telephony"/>
+ </activity>
+
+ <activity
+ android:name=".telecom.SelfManagedIncomingCallTestActivity"
+ android:label="@string/telecom_incoming_self_mgd_test">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.cts.intent.category.MANUAL_TEST"/>
+ </intent-filter>
+
+ <meta-data
+ android:name="test_category"
+ android:value="@string/test_category_telecom"/>
+ <meta-data
+ android:name="test_required_features"
+ android:value="android.hardware.telephony"/>
</activity>
<activity
diff --git a/apps/CtsVerifier/res/drawable/border.xml b/apps/CtsVerifier/res/drawable/border.xml
new file mode 100644
index 0000000..b419618
--- /dev/null
+++ b/apps/CtsVerifier/res/drawable/border.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <corners
+ android:radius="4dp"/>
+ <stroke
+ android:width="4dp"
+ android:color="@android:color/black" />
+</shape>
diff --git a/apps/CtsVerifier/res/layout/bu_main.xml b/apps/CtsVerifier/res/layout/bu_main.xml
deleted file mode 100644
index 2289fee..0000000
--- a/apps/CtsVerifier/res/layout/bu_main.xml
+++ /dev/null
@@ -1,44 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- >
-
- <ListView android:id="@+id/android:list"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- />
-
- <TextView android:id="@id/android:empty"
- android:gravity="center"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:text="@string/bu_loading"
- />
-
- <Button android:id="@+id/generate_button"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/bu_generate"
- />
-
- <include layout="@layout/pass_fail_buttons" />
-
-</LinearLayout>
diff --git a/apps/CtsVerifier/res/layout/bua_main.xml b/apps/CtsVerifier/res/layout/bua_main.xml
deleted file mode 100644
index e2d5ef1..0000000
--- a/apps/CtsVerifier/res/layout/bua_main.xml
+++ /dev/null
@@ -1,50 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- >
-
- <ListView android:id="@+id/android:list"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- />
-
- <TextView android:id="@id/android:empty"
- android:gravity="center"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:text="@string/bu_loading"
- />
-
- <Button android:id="@+id/generate_button"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/bua_read_settings"
- />
-
- <Button android:id="@+id/show_instructions_button"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/bua_show_instructions"
- />
-
- <include layout="@layout/pass_fail_buttons" />
-
-</LinearLayout>
diff --git a/apps/CtsVerifier/res/layout/da_tapjacking_overlay_activity.xml b/apps/CtsVerifier/res/layout/da_tapjacking_overlay_activity.xml
new file mode 100644
index 0000000..5583fce
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/da_tapjacking_overlay_activity.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@drawable/border"
+ android:padding="8dp" >
+
+ <TextView android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textColor="@android:color/black"
+ android:text="@string/da_tapjacking_overlay_app_description" />
+</LinearLayout>
\ No newline at end of file
diff --git a/apps/CtsVerifier/res/layout/da_tapjacking_test_main.xml b/apps/CtsVerifier/res/layout/da_tapjacking_test_main.xml
new file mode 100644
index 0000000..2ee9ea9
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/da_tapjacking_test_main.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+ style="@style/RootLayoutPadding"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <!-- Enable device admin -->
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" >
+
+ <TextView
+ android:id="@+id/admin_tapjacking_instructions"
+ style="@style/InstructionsSmallFont"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true"
+ android:layout_alignParentTop="true"
+ android:text="@string/da_tapjacking_instructions" />
+
+ <Button
+ android:id="@+id/enable_admin_overlay_button"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true"
+ android:layout_below="@id/admin_tapjacking_instructions"
+ android:layout_marginLeft="20dip"
+ android:layout_marginRight="20dip"
+ android:text="@string/da_tapjacking_button_text" />
+ </RelativeLayout>
+
+ <include layout="@layout/pass_fail_buttons" />
+ </LinearLayout>
+
+</ScrollView>
diff --git a/apps/CtsVerifier/res/layout/telecom_self_managed_answer.xml b/apps/CtsVerifier/res/layout/telecom_self_managed_answer.xml
new file mode 100644
index 0000000..27aa55a
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/telecom_self_managed_answer.xml
@@ -0,0 +1,133 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2017 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<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="@string/telecom_incoming_self_mgd_info"/>
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/js_padding"
+ android:layout_marginBottom="@dimen/js_padding">
+
+ <ImageView
+ android:id="@+id/step_1_status"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/fs_indeterminate"
+ android:layout_marginRight="@dimen/js_padding"
+ android:layout_alignParentStart="true"
+ android:layout_alignParentTop="true" />
+ <TextView
+ android:id="@+id/step_1_instructions"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/telecom_incoming_self_mgd_step_1"
+ android:textSize="16dp"
+ android:layout_alignParentRight="true"
+ android:layout_alignParentTop="true"
+ android:layout_toRightOf="@id/step_1_status" />
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true"
+ android:layout_below="@id/step_1_instructions"
+ android:layout_marginLeft="20dip"
+ android:layout_marginRight="20dip"
+ android:layout_toRightOf="@id/step_1_status"
+ android:id="@+id/telecom_incoming_self_mgd_register_button"
+ android:text="@string/telecom_incoming_self_mgd_register_button"/>
+ </RelativeLayout>
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/js_padding"
+ android:layout_marginBottom="@dimen/js_padding">
+
+ <ImageView
+ android:id="@+id/step_2_status"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/fs_indeterminate"
+ android:layout_marginRight="@dimen/js_padding"
+ android:layout_alignParentStart="true"
+ android:layout_alignParentTop="true" />
+ <TextView
+ android:id="@+id/step_2_instructions"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/telecom_incoming_self_mgd_step_2"
+ android:textSize="16dp"
+ android:layout_alignParentRight="true"
+ android:layout_alignParentTop="true"
+ android:layout_toRightOf="@id/step_2_status" />
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true"
+ android:layout_below="@id/step_2_instructions"
+ android:layout_marginLeft="20dip"
+ android:layout_marginRight="20dip"
+ android:layout_toRightOf="@id/step_2_status"
+ android:id="@+id/telecom_incoming_self_mgd_show_ui_button"
+ android:text="@string/telecom_incoming_self_mgd_show_ui_button"/>
+ </RelativeLayout>
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/js_padding"
+ android:layout_marginBottom="@dimen/js_padding">
+
+ <ImageView
+ android:id="@+id/step_3_status"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/fs_indeterminate"
+ android:layout_marginRight="@dimen/js_padding"
+ android:layout_alignParentStart="true"
+ android:layout_alignParentTop="true" />
+ <TextView
+ android:id="@+id/step_3_instructions"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/telecom_incoming_self_mgd_step_3"
+ android:textSize="16dp"
+ android:layout_alignParentRight="true"
+ android:layout_alignParentTop="true"
+ android:layout_toRightOf="@id/step_3_status" />
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true"
+ android:layout_below="@id/step_3_instructions"
+ android:layout_marginLeft="20dip"
+ android:layout_marginRight="20dip"
+ android:layout_toRightOf="@id/step_3_status"
+ android:id="@+id/telecom_incoming_self_mgd_confirm_answer_button"
+ android:text="@string/telecom_incoming_self_mgd_confirm_answer_button"/>
+ </RelativeLayout>
+
+ <include layout="@layout/pass_fail_buttons" />
+</LinearLayout>
\ No newline at end of file
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index d9b87d16..3f4e18e 100755
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -66,71 +66,6 @@
<string name="bu_generate_error">Error occurred while generating test data...</string>
<string name="bu_settings">Settings</string>
- <!-- Strings for BackupTestActivity -->
- <string name="backup_test">Data Backup Test</string>
- <string name="backup_info">This test checks that data backup and automatic restore works
- properly. The test activity lists some preferences and files that are backed up and
- restored by the CTS Verifier. If backup and restore is working properly, these values
- should be restored after running the backup manager, uninstalling the app, and reinstalling
- the CTS Verifier.
- \n\nPress the \"Generate Test Data\" to populate these values
- and then follow the on screen instructions to finish the test.
- </string>
- <string name="bu_generate">Generate Test Data</string>
- <string name="bu_preferences">Preferences</string>
- <string name="bu_files">Files</string>
- <string name="bu_instructions">Random values for the preferences and files have been saved.
- \n\nFollow the instructions below to check that the data backup and restore works:
- \n\n1. Make sure backup and automatic restore are enabled in settings. Depending on the
- backup transport supported by the device you may need to do additional steps. For instance
- you may need to set a Google account as the backup account for the device. If you cannot
- find the corresponding setting options on your device, run \"adb shell bmgr enable true\"
- to enable the backup manager. You can check its status by executing \"adb shell bmgr
- enabled\".
- \n\n2. Run the backup manager: adb shell bmgr run
- \n\n3. Uninstall the program: adb uninstall com.android.cts.verifier
- \n\n4. Reinstall the CTS Verifier and verify that the values are still the same.
- </string>
-
- <!-- Strings for BackupAccessibilityTestActivity -->
- <string name="backup_accessibility_test">Backup Accessibility Settings Test</string>
- <string name="backup_accessibility_info">This test checks that data backup and automatic restore
- of Accessibility-related settings works properly. If backup and restore is working properly,
- these values should be restored after running the backup manager, removing your Google
- account, changing the accessibility settings values, and re-adding your Google account.
- \n\nPress \"Generate Test Data\" to generate test values for accessibility settings and then
- follow the on screen instructions to finish the test.
- </string>
- <string name="bua_settings">General Accessibility Settings</string>
- <string name="bua_settings_color_correction">Color Correction Settings</string>
- <string name="bua_settings_accessibility_services">Accessibility Service Settings</string>
- <string name="bua_settings_captions">Captions Settings</string>
- <string name="bua_settings_tts">TTS Settings</string>
- <string name="bua_settings_system">Other System Settings</string>
- <string name="bua_instructions">You will need two devices for this test.
- \n\nFollow the instructions below to check that the data backup and restore of
- accessibility-related settings works properly:
- \n\n1. Make sure backup and automatic restore are enabled in settings. If you cannot find
- the corresponding setting options on your device, run \"adb shell bmgr enable true\" to
- enable the backup manager. You can check its status by executing \"adb shell bmgr enabled\".
- You will also need to set a Google account as the backup account for the device.
- \n\n2. Press \"Read Current Values\" and note the default values for the listed settings.
- Values that are either \"0\" or \"null\" will appear in green. Note: Some default values are
- neither \"0\", nor \"null\", so you still need to pay attention to the default setting
- values that are not highlighted.
- \n\n3. Change the values of the listed settings to something other than their default value.
- \n\n4. Return to the CtsVerifier and press \"Read Current Values\". Make sure that you have
- changed all of the settings.
- \n\n5. Run the backup manager: adb shell bmgr run
- \n\n6. Factory reset data on the second device. While going through the Setup Wizard,
- restore all data from the account on your first device. When prompted, choose to restore all
- settings from your first device.
- \n\n7. Install CtsVerifier on your new device and make sure that the values read on the
- second device match the values on your first device.
- </string>
- <string name="bua_show_instructions">Show Instructions</string>
- <string name="bua_read_settings">Read Current Values</string>
-
<!-- Strings for Device Administration tests -->
<string name="da_policy_serialization_test">Policy Serialization Test</string>
<string name="da_policy_serialization_info">This test checks that a device policy is properly
@@ -144,6 +79,10 @@
can be easily uninstalled from the application details screen in a way similar to other
apps.
</string>
+ <string name="da_tapjacking_test">Device Admin Tapjacking Test</string>
+ <string name="da_tapjacking_test_info">This test checks that an activity cannot tapjack the
+ user by obscuring the device admin details while prompting the user to activate the admin.
+ </string>
<string name="car_dock_test">Car Dock Test</string>
<string name="car_dock_test_desc">This test ensures that car mode opens the app associated with
car dock when going into car mode.\n\n
@@ -199,6 +138,18 @@
</string>
<string name="da_uninstall_admin_button_text">Launch settings</string>
+ <string name="da_tapjacking_overlay_app_description">This activity attempts to tapjack the activity below.\n
+ Any security sensitive controls below should not respond to taps as long as this activity is visible.</string>
+ <string name="da_tapjacking_instructions">
+ 1. Launch the device admin add screen by pressing the button below.\n
+ 2. Wait for an overlaying transparent activity to show up obscuring the device admin details window.\n
+ 3. Attempt to activate the admin with the transparent activity still obscuring the window.
+ The button should discard any taps while showing a toast warning to the user.\n
+ 4. Return to this screen and pass the test if the device admin could not be activated while the details
+ window was being obscured.
+ </string>
+ <string name="da_tapjacking_button_text">Enable device admin</string>
+
<!-- Strings for lock bound keys test -->
<string name="sec_lock_bound_key_test">Lock Bound Keys Test</string>
<string name="sec_lock_bound_key_test_info">
@@ -604,6 +555,7 @@
<string name="location_gnss_measure_no_location_test">GNSS Measurement Before Location Test</string>
<string name="location_gnss_reg_test">GNSS Measurement Registration Test</string>
<string name="location_gnss_value_test">GNSS Measurement Values Test</string>
+ <string name="location_gnss_ttff_test">GNSS TTFF Test</string>
<string name="location_gnss_nav_msg_test">GNSS Navigation Message Test</string>
<string name="location_gnss_status_test">GNSS Status Test</string>
<string name="location_gnss_test_info">This test verifies basic GNSS behavior.
@@ -3952,4 +3904,22 @@
that audio is audible.
</string>
<string name="telecom_incoming_call_confirm_button">Confirm</string>
+ <string name="telecom_incoming_self_mgd_test"> Incoming Self-Managed Connection Test</string>
+ <string name="telecom_incoming_self_mgd_info">
+ This test verifies that incoming calls from a Self-Managed Connection Service will trigger
+ a Telecom-managed incoming call UI when there is already an ongoing call on the device.
+ </string>
+ <string name="telecom_incoming_self_mgd_step_1">
+ Click the button below to register a test self-managed ConnectionService.
+ </string>
+ <string name="telecom_incoming_self_mgd_register_button">Register Self-Managed ConnectionService</string>
+ <string name="telecom_incoming_self_mgd_step_2">
+ Click the button below to test that the system incoming call notification shows. When the
+ notification shows, "answer" the call.
+ </string>
+ <string name="telecom_incoming_self_mgd_show_ui_button">Show System Incoming UI</string>
+ <string name="telecom_incoming_self_mgd_step_3">
+ Click the button below to confirm that the incoming call was answered.
+ </string>
+ <string name="telecom_incoming_self_mgd_confirm_answer_button">Confirm Answer</string>
</resources>
diff --git a/apps/CtsVerifier/res/values/styles.xml b/apps/CtsVerifier/res/values/styles.xml
index 0e05817..64fd4fe 100644
--- a/apps/CtsVerifier/res/values/styles.xml
+++ b/apps/CtsVerifier/res/values/styles.xml
@@ -13,4 +13,9 @@
<style name="RootLayoutPadding">
<item name="android:padding">10dip</item>
</style>
+ <style name="OverlayTheme" parent="android:Theme.Dialog">
+ <item name="android:windowNoTitle">true</item>
+ <item name="android:windowIsTranslucent">true</item>
+ <item name="android:windowBackground">@android:color/transparent</item>
+ </style>
</resources>
diff --git a/apps/CtsVerifier/res/xml/tapjacking_device_admin.xml b/apps/CtsVerifier/res/xml/tapjacking_device_admin.xml
new file mode 100644
index 0000000..d884663
--- /dev/null
+++ b/apps/CtsVerifier/res/xml/tapjacking_device_admin.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<device-admin xmlns:android="http://schemas.android.com/apk/res/android">
+ <uses-policies>
+ <limit-password />
+ <watch-login />
+ <encrypted-storage />
+ <wipe-data />
+ <reset-password />
+ <disable-keyguard-features />
+ <force-lock />
+ <limit-password />
+ </uses-policies>
+</device-admin>
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/TestResultsBackupHelper.java b/apps/CtsVerifier/src/com/android/cts/verifier/TestResultsBackupHelper.java
deleted file mode 100644
index 2527d57..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/TestResultsBackupHelper.java
+++ /dev/null
@@ -1,149 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.verifier;
-
-import com.android.cts.verifier.backup.BackupTestActivity;
-
-import android.app.backup.BackupDataInputStream;
-import android.app.backup.BackupDataOutput;
-import android.app.backup.BackupHelper;
-import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.content.Context;
-import android.database.Cursor;
-import android.os.ParcelFileDescriptor;
-import android.util.Log;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
-import java.io.IOException;
-
-/** {@link BackupHelper} for the test results database. */
-class TestResultsBackupHelper implements BackupHelper {
-
- private static final String TAG = TestResultsBackupHelper.class.getSimpleName();
-
- private static final String DB_BACKUP_KEY = "db";
-
- private final Context mContext;
-
- TestResultsBackupHelper(Context context) {
- mContext = context;
- }
-
- @Override
- public void performBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
- ParcelFileDescriptor newState) {
- ContentResolver resolver = mContext.getContentResolver();
- Cursor cursor = null;
- try {
- cursor = resolver.query(TestResultsProvider.getResultContentUri(mContext),
- null, null, null, null);
- int nameIndex = cursor.getColumnIndex(TestResultsProvider.COLUMN_TEST_NAME);
- int resultIndex = cursor.getColumnIndex(TestResultsProvider.COLUMN_TEST_RESULT);
- int infoSeenIndex = cursor.getColumnIndex(TestResultsProvider.COLUMN_TEST_INFO_SEEN);
- int detailsIndex = cursor.getColumnIndex(TestResultsProvider.COLUMN_TEST_DETAILS);
- int metricsIndex = cursor.getColumnIndex(TestResultsProvider.COLUMN_TEST_METRICS);
-
- ByteArrayOutputStream byteOutput = new ByteArrayOutputStream();
- DataOutputStream dataOutput = new DataOutputStream(byteOutput);
-
- dataOutput.writeInt(cursor.getCount());
- while (cursor.moveToNext()) {
- String name = cursor.getString(nameIndex);
- int result = cursor.getInt(resultIndex);
- int infoSeen = cursor.getInt(infoSeenIndex);
- String details = cursor.getString(detailsIndex);
- byte[] metricsData = cursor.getBlob(metricsIndex);
-
- dataOutput.writeUTF(name);
- dataOutput.writeInt(result);
- dataOutput.writeInt(infoSeen);
- dataOutput.writeUTF(details != null ? details : "");
- dataOutput.writeInt(metricsData.length);
- if (metricsData.length > 0) {
- dataOutput.write(metricsData);
- }
- }
-
- byte[] rawBytes = byteOutput.toByteArray();
- data.writeEntityHeader(DB_BACKUP_KEY, rawBytes.length);
- data.writeEntityData(rawBytes, rawBytes.length);
- } catch (IOException e) {
- Log.e(TAG, "Couldn't backup test results...", e);
- failBackupTest();
- } finally {
- if (cursor != null) {
- cursor.close();
- }
- }
- }
-
- @Override
- public void restoreEntity(BackupDataInputStream data) {
- try {
- if (DB_BACKUP_KEY.equals(data.getKey())) {
- byte[] rawBytes = new byte[data.size()];
- data.read(rawBytes, 0, data.size());
-
- ByteArrayInputStream byteInput = new ByteArrayInputStream(rawBytes);
- DataInputStream dataInput = new DataInputStream(byteInput);
-
- int numRows = dataInput.readInt();
- ContentValues[] values = new ContentValues[numRows];
- for (int i = 0; i < numRows; i++) {
- String name = dataInput.readUTF();
- int result = dataInput.readInt();
- int infoSeen = dataInput.readInt();
- String details = dataInput.readUTF();
- int metricsDataSize = dataInput.readInt();
-
- values[i] = new ContentValues();
- values[i].put(TestResultsProvider.COLUMN_TEST_NAME, name);
- values[i].put(TestResultsProvider.COLUMN_TEST_RESULT, result);
- values[i].put(TestResultsProvider.COLUMN_TEST_INFO_SEEN, infoSeen);
- values[i].put(TestResultsProvider.COLUMN_TEST_DETAILS, details);
-
- if (metricsDataSize > 0) {
- byte[] metrics = new byte[metricsDataSize];
- dataInput.readFully(metrics);
- values[i].put(TestResultsProvider.COLUMN_TEST_METRICS, metrics);
- }
- }
-
- ContentResolver resolver = mContext.getContentResolver();
- resolver.bulkInsert(TestResultsProvider.getResultContentUri(mContext), values);
- } else {
- Log.e(TAG, "Skipping key: " + data.getKey());
- }
- } catch (IOException e) {
- Log.e(TAG, "Couldn't restore test results...", e);
- failBackupTest();
- }
- }
-
- private void failBackupTest() {
- TestResultsProvider.setTestResult(mContext, BackupTestActivity.class.getName(),
- TestResult.TEST_RESULT_FAILED, null /*testDetails*/, null /*testMetrics*/);
- }
-
- @Override
- public void writeNewStateDescription(ParcelFileDescriptor newState) {
- }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/VerifierBackupAgent.java b/apps/CtsVerifier/src/com/android/cts/verifier/VerifierBackupAgent.java
deleted file mode 100644
index 3c980b9..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/VerifierBackupAgent.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.verifier;
-
-import com.android.cts.verifier.backup.BackupTestActivity;
-
-import android.app.backup.BackupAgentHelper;
-
-public class VerifierBackupAgent extends BackupAgentHelper {
-
- @Override
- public void onCreate() {
- super.onCreate();
- addHelper("test-results", new TestResultsBackupHelper(this));
- addHelper("backup-test-prefs", BackupTestActivity.getSharedPreferencesBackupHelper(this));
- addHelper("backup-test-files", BackupTestActivity.getFileBackupHelper(this));
- }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/admin/tapjacking/DeviceAdminTapjackingTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/admin/tapjacking/DeviceAdminTapjackingTestActivity.java
new file mode 100644
index 0000000..6a81cba
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/admin/tapjacking/DeviceAdminTapjackingTestActivity.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.admin.tapjacking;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.SystemClock;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+/**
+ * Test that checks that device admin activate button does not allow taps when another window
+ * is obscuring the device admin details
+ */
+public class DeviceAdminTapjackingTestActivity extends PassFailButtons.Activity implements
+ View.OnClickListener {
+
+ private static final String TAG = DeviceAdminTapjackingTestActivity.class.getSimpleName();
+ private static final String ADMIN_ACTIVATED_BUNDLE_KEY = "admin_activated";
+ private static final String ACTIVITIES_FINISHED_IN_ORDER_KEY = "activities_finished_in_order";
+ private static final int REQUEST_ENABLE_ADMIN = 0;
+ private static final int REQUEST_OVERLAY_ACTIVITY = 1;
+ private static final long REMOVE_ADMIN_TIMEOUT = 5000;
+
+ private DevicePolicyManager mDevicePolicyManager;
+ private Button mAddDeviceAdminButton;
+ private boolean mAdminActivated;
+ private boolean mActivitiesFinishedInOrder;
+ private boolean mOverlayFinished;
+ private ComponentName mAdmin;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.da_tapjacking_test_main);
+ setInfoResources(R.string.da_tapjacking_test, R.string.da_tapjacking_test_info, -1);
+ setPassFailButtonClickListeners();
+
+ mAdmin = new ComponentName(this, EmptyDeviceAdminReceiver.class);
+ mDevicePolicyManager = (DevicePolicyManager) getSystemService(DEVICE_POLICY_SERVICE);
+
+ if (savedInstanceState != null) {
+ mAdminActivated = savedInstanceState.getBoolean(ADMIN_ACTIVATED_BUNDLE_KEY, false);
+ mActivitiesFinishedInOrder = savedInstanceState.getBoolean(ACTIVITIES_FINISHED_IN_ORDER_KEY, false);
+ } else if (isActiveAdminAfterTimeout()) {
+ Log.e(TAG, "Could not remove active admin. Cannot proceed with test");
+ finish();
+ }
+ mAddDeviceAdminButton = findViewById(R.id.enable_admin_overlay_button);
+ mAddDeviceAdminButton.setOnClickListener(this);
+ }
+
+ private boolean isActiveAdminAfterTimeout() {
+ final long startTime = SystemClock.uptimeMillis();
+ while (mDevicePolicyManager.isAdminActive(mAdmin)
+ && SystemClock.uptimeMillis() < startTime + REMOVE_ADMIN_TIMEOUT) {
+ try {
+ Thread.sleep(1000);
+ } catch(InterruptedException exc) {
+ }
+ }
+ return mDevicePolicyManager.isAdminActive(mAdmin);
+ }
+
+ @Override
+ public void onClick(View v) {
+ if (v == mAddDeviceAdminButton) {
+ Intent securitySettingsIntent = new Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN);
+ securitySettingsIntent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, mAdmin);
+ startActivityForResult(securitySettingsIntent, REQUEST_ENABLE_ADMIN);
+ try {
+ Thread.sleep(2000);
+ } catch (InterruptedException exc) {
+ }
+ startActivityForResult(new Intent(this, OverlayingActivity.class), REQUEST_OVERLAY_ACTIVITY);
+ }
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (requestCode == REQUEST_ENABLE_ADMIN) {
+ mActivitiesFinishedInOrder = mOverlayFinished;
+ if (resultCode == RESULT_OK) {
+ mAdminActivated = true;
+ Log.e(TAG, "Admin was activated. Restart the Test");
+ }
+ }
+ else if (requestCode == REQUEST_OVERLAY_ACTIVITY) {
+ mOverlayFinished = true;
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ updateWidgets();
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle icicle) {
+ icicle.putBoolean(ADMIN_ACTIVATED_BUNDLE_KEY, mAdminActivated);
+ icicle.putBoolean(ACTIVITIES_FINISHED_IN_ORDER_KEY, mActivitiesFinishedInOrder);
+ }
+
+ private void updateWidgets() {
+ mAddDeviceAdminButton.setEnabled(!mActivitiesFinishedInOrder && !mAdminActivated);
+ getPassButton().setEnabled(!mAdminActivated && mActivitiesFinishedInOrder);
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/admin/tapjacking/EmptyDeviceAdminReceiver.java b/apps/CtsVerifier/src/com/android/cts/verifier/admin/tapjacking/EmptyDeviceAdminReceiver.java
new file mode 100644
index 0000000..42dd9ac
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/admin/tapjacking/EmptyDeviceAdminReceiver.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.admin.tapjacking;
+
+import android.app.admin.DeviceAdminReceiver;
+import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+
+public class EmptyDeviceAdminReceiver extends DeviceAdminReceiver {
+
+ @Override
+ public void onEnabled(Context context, Intent intent) {
+ DevicePolicyManager dpm =
+ (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
+ dpm.removeActiveAdmin(new ComponentName(context, this.getClass()));
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/admin/tapjacking/OverlayingActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/admin/tapjacking/OverlayingActivity.java
new file mode 100644
index 0000000..52c7ed5
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/admin/tapjacking/OverlayingActivity.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.admin.tapjacking;
+
+import static android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
+import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.WindowManager;
+
+import com.android.cts.verifier.R;
+
+
+public class OverlayingActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.da_tapjacking_overlay_activity);
+ WindowManager.LayoutParams params = getWindow().getAttributes();
+ params.flags = FLAG_LAYOUT_NO_LIMITS | FLAG_NOT_TOUCH_MODAL | FLAG_NOT_TOUCHABLE
+ | FLAG_KEEP_SCREEN_ON;
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/backup/BackupAccessibilityTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/backup/BackupAccessibilityTestActivity.java
deleted file mode 100644
index 157a71c..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/backup/BackupAccessibilityTestActivity.java
+++ /dev/null
@@ -1,342 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.verifier.backup;
-
-import com.android.cts.verifier.PassFailButtons;
-import com.android.cts.verifier.R;
-
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.app.backup.BackupManager;
-import android.app.backup.FileBackupHelper;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.graphics.Color;
-import android.os.AsyncTask;
-import android.os.Bundle;
-import android.provider.Settings;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.ViewGroup;
-import android.view.Window;
-import android.widget.BaseAdapter;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Random;
-import java.util.Scanner;
-
-/**
- * Test for checking whether Accessibility Settings are being backed up properly. It lists the
- * values of the accessibility preferences that should get backed up and restored after running the
- * backup manager and reinstalling the CTS verifier.
- */
-public class BackupAccessibilityTestActivity extends PassFailButtons.ListActivity {
-
- private static final String TAG = BackupAccessibilityTestActivity.class.getSimpleName();
-
- private static final int INSTRUCTIONS_DIALOG_ID = 1;
-
- private static final List<String> ACCESSIBILITY_SETTINGS = new ArrayList();
- private static final List<String> COLOR_CORRECTION_SETTINGS = new ArrayList();
- private static final List<String> ACCESSIBILITY_SERVICE_SETTINGS = new ArrayList();
- private static final List<String> CAPTIONS_SETTINGS = new ArrayList();
- private static final List<String> TTS_SETTINGS = new ArrayList();
- private static final List<String> SYSTEM_SETTINGS = new ArrayList();
-
- static {
- ACCESSIBILITY_SETTINGS.add("accessibility_display_magnification_enabled");
- ACCESSIBILITY_SETTINGS.add("accessibility_autoclick_enabled");
- ACCESSIBILITY_SETTINGS.add("accessibility_autoclick_delay");
- ACCESSIBILITY_SETTINGS.add("high_text_contrast_enabled");
- ACCESSIBILITY_SETTINGS.add("incall_power_button_behavior");
- ACCESSIBILITY_SETTINGS.add(Settings.Secure.ACCESSIBILITY_SPEAK_PASSWORD);
- ACCESSIBILITY_SETTINGS.add("accessibility_large_pointer_icon");
- ACCESSIBILITY_SETTINGS.add("long_press_timeout");
- ACCESSIBILITY_SETTINGS.add(Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED);
-
- COLOR_CORRECTION_SETTINGS.add("accessibility_display_daltonizer");
- COLOR_CORRECTION_SETTINGS.add("accessibility_display_daltonizer_enabled");
-
- CAPTIONS_SETTINGS.add("accessibility_captioning_preset");
- CAPTIONS_SETTINGS.add("accessibility_captioning_enabled");
- CAPTIONS_SETTINGS.add("accessibility_captioning_locale");
- CAPTIONS_SETTINGS.add("accessibility_captioning_background_color");
- CAPTIONS_SETTINGS.add("accessibility_captioning_foreground_color");
- CAPTIONS_SETTINGS.add("accessibility_captioning_edge_type");
- CAPTIONS_SETTINGS.add("accessibility_captioning_edge_color");
- CAPTIONS_SETTINGS.add("accessibility_captioning_typeface");
- CAPTIONS_SETTINGS.add("accessibility_captioning_font_scale");
- CAPTIONS_SETTINGS.add("accessibility_captioning_window_color");
-
- TTS_SETTINGS.add(Settings.Secure.TTS_DEFAULT_RATE);
- TTS_SETTINGS.add("tts_default_locale");
-
- ACCESSIBILITY_SERVICE_SETTINGS.add(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
- ACCESSIBILITY_SERVICE_SETTINGS.add("touch_exploration_granted_accessibility_services");
- ACCESSIBILITY_SERVICE_SETTINGS.add(Settings.Secure.TOUCH_EXPLORATION_ENABLED);
-
- SYSTEM_SETTINGS.add(Settings.System.FONT_SCALE);
- SYSTEM_SETTINGS.add(Settings.System.STAY_ON_WHILE_PLUGGED_IN);
- SYSTEM_SETTINGS.add(Settings.System.SCREEN_OFF_TIMEOUT);
- SYSTEM_SETTINGS.add(Settings.System.SCREEN_BRIGHTNESS);
- SYSTEM_SETTINGS.add(Settings.System.SCREEN_BRIGHTNESS_MODE);
- SYSTEM_SETTINGS.add(Settings.System.TEXT_SHOW_PASSWORD);
- SYSTEM_SETTINGS.add(Settings.System.HAPTIC_FEEDBACK_ENABLED);
- SYSTEM_SETTINGS.add("power_sounds_enabled");
- SYSTEM_SETTINGS.add("lockscreen_sounds_enabled");
- SYSTEM_SETTINGS.add("pointer_speed");
- SYSTEM_SETTINGS.add(Settings.System.VIBRATE_WHEN_RINGING);
- SYSTEM_SETTINGS.add(Settings.System.ACCELEROMETER_ROTATION);
- }
-
- private BackupAdapter mAdapter;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
- setContentView(R.layout.bua_main);
- setPassFailButtonClickListeners();
- setInfoResources(R.string.backup_accessibility_test, R.string.backup_accessibility_info, 0);
-
- mAdapter = new BackupAdapter(this);
- setListAdapter(mAdapter);
-
- new ReadCurrentSettingsValuesTask().execute();
-
- findViewById(R.id.generate_button).setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- new ReadCurrentSettingsValuesTask().execute();
- }
- });
-
- findViewById(R.id.show_instructions_button).setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- showDialog(INSTRUCTIONS_DIALOG_ID);
- }
- });
- }
-
- class ReadCurrentSettingsValuesTask extends AsyncTask<Void, Void, List<BackupItem>> {
-
- @Override
- protected void onPreExecute() {
- super.onPreExecute();
- setProgressBarIndeterminateVisibility(true);
- }
-
- @Override
- protected List<BackupItem> doInBackground(Void... params) {
- List<BackupItem> items = new ArrayList<BackupItem>();
-
- items.add(new CategoryBackupItem(R.string.bua_settings));
- addSecureSettings(items, ACCESSIBILITY_SETTINGS);
-
- items.add(new CategoryBackupItem(R.string.bua_settings_color_correction));
- addSecureSettings(items, COLOR_CORRECTION_SETTINGS);
-
- items.add(new CategoryBackupItem(R.string.bua_settings_captions));
- addSecureSettings(items, CAPTIONS_SETTINGS);
-
- items.add(new CategoryBackupItem(R.string.bua_settings_tts));
- addSecureSettings(items, TTS_SETTINGS);
-
- items.add(new CategoryBackupItem(R.string.bua_settings_accessibility_services));
- addSecureSettings(items, ACCESSIBILITY_SERVICE_SETTINGS);
-
- items.add(new CategoryBackupItem(R.string.bua_settings_system));
- addSystemSettings(items, SYSTEM_SETTINGS);
-
- return items;
- }
-
- private void addSecureSettings(List<BackupItem> items, List<String> settings) {
- for (String setting : settings) {
- String value = Settings.Secure.getString(getContentResolver(), setting);
- items.add(new PreferenceBackupItem(setting, value));
- }
- }
-
- private void addSystemSettings(List<BackupItem> items, List<String> settings) {
- for (String setting : settings) {
- String value = Settings.System.getString(getContentResolver(), setting);
- items.add(new PreferenceBackupItem(setting, value));
- }
- }
-
- @Override
- protected void onPostExecute(List<BackupItem> result) {
- super.onPostExecute(result);
- setProgressBarIndeterminateVisibility(false);
- mAdapter.clear();
- mAdapter.addAll(result);
- }
- }
-
- @Override
- public Dialog onCreateDialog(int id, Bundle args) {
- switch (id) {
- case INSTRUCTIONS_DIALOG_ID:
- return new AlertDialog.Builder(this)
- .setIcon(android.R.drawable.ic_dialog_info)
- .setTitle(R.string.backup_accessibility_test)
- .setMessage(R.string.bua_instructions)
- .setPositiveButton(android.R.string.ok, null)
- .setNeutralButton(R.string.bu_settings, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- startActivity(new Intent(Settings.ACTION_PRIVACY_SETTINGS));
- }
- }).create();
-
- default:
- return super.onCreateDialog(id, args);
- }
- }
-
- interface BackupItem {
- int getViewType();
- View getView(LayoutInflater inflater, int position, View convertView, ViewGroup parent);
- }
-
- static class CategoryBackupItem implements BackupItem {
-
- private final int mTitleResId;
-
- CategoryBackupItem(int titleResId) {
- mTitleResId = titleResId;
- }
-
- @Override
- public int getViewType() {
- return 0;
- }
-
- @Override
- public View getView(LayoutInflater inflater, int position, View convertView,
- ViewGroup parent) {
- TextView view = (TextView) convertView;
- if (convertView == null) {
- view = (TextView) inflater.inflate(R.layout.test_category_row, parent, false);
- }
- view.setText(mTitleResId);
- view.setAllCaps(true);
- view.setTextAppearance(1); // Bold
- return view;
- }
- }
-
- static class PreferenceBackupItem implements BackupItem {
-
- private final String mName;
- private final String mValue;
-
- PreferenceBackupItem(String name, String value) {
- mName = name;
- mValue = value;
- }
-
- @Override
- public int getViewType() {
- if (mValue == null || mValue.equals("0")) {
- return 1;
- } else {
- return 2;
- }
- }
-
- @Override
- public View getView(LayoutInflater inflater, int position, View convertView,
- ViewGroup parent) {
- TextView view = (TextView) convertView;
- if (convertView == null) {
- view = (TextView) inflater.inflate(R.layout.bu_preference_row, parent, false);
- }
- view.setText(mName + " : " + mValue);
- if (mValue == null || mValue.equals("0")) {
- view.setTextColor(Color.GREEN);
- }
- return view;
- }
- }
-
- class BackupAdapter extends BaseAdapter {
-
- private final LayoutInflater mLayoutInflater;
-
- private final List<BackupItem> mItems = new ArrayList<BackupItem>();
-
- public BackupAdapter(Context context) {
- mLayoutInflater = (LayoutInflater) context.getSystemService(LAYOUT_INFLATER_SERVICE);
- }
-
- public void clear() {
- mItems.clear();
- }
-
- public void addAll(List<BackupItem> items) {
- mItems.addAll(items);
- notifyDataSetChanged();
- }
-
- @Override
- public int getCount() {
- return mItems.size();
- }
-
- @Override
- public BackupItem getItem(int position) {
- return mItems.get(position);
- }
-
- @Override
- public long getItemId(int position) {
- return position;
- }
-
- @Override
- public boolean isEnabled(int position) {
- return false;
- }
-
- @Override
- public int getViewTypeCount() {
- return 3;
- }
-
- @Override
- public int getItemViewType(int position) {
- return getItem(position).getViewType();
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- return getItem(position).getView(mLayoutInflater, position, convertView, parent);
- }
- }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/backup/BackupTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/backup/BackupTestActivity.java
deleted file mode 100644
index cccc1c2..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/backup/BackupTestActivity.java
+++ /dev/null
@@ -1,404 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.verifier.backup;
-
-import com.android.cts.verifier.PassFailButtons;
-import com.android.cts.verifier.R;
-
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.app.backup.BackupManager;
-import android.app.backup.FileBackupHelper;
-import android.app.backup.SharedPreferencesBackupHelper;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.os.AsyncTask;
-import android.os.Bundle;
-import android.provider.Settings;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.ViewGroup;
-import android.view.Window;
-import android.widget.BaseAdapter;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Random;
-import java.util.Scanner;
-
-/**
- * Test for checking whether the BackupManager is working properly. It lists the values of
- * several preferences and contents of files that should get backed up and restored after
- * running the backup manager and reinstalling the CTS verifier.
- */
-public class BackupTestActivity extends PassFailButtons.ListActivity {
-
- private static final String TAG = BackupTestActivity.class.getSimpleName();
-
- private static final int INSTRUCTIONS_DIALOG_ID = 1;
-
- private static final String TEST_PREFS_1 = "test-prefs-1";
- private static final String INT_PREF = "int-pref";
- private static final String BOOL_PREF = "bool-pref";
-
- private static final String TEST_PREFS_2 = "test-prefs-2";
- private static final String FLOAT_PREF = "float-pref";
- private static final String LONG_PREF = "long-pref";
- private static final String STRING_PREF = "string-pref";
-
- private static final String TEST_FILE_1 = "test-file-1";
- private static final String TEST_FILE_2 = "test-file-2";
-
- private BackupAdapter mAdapter;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
- setContentView(R.layout.bu_main);
- setPassFailButtonClickListeners();
- setInfoResources(R.string.backup_test, R.string.backup_info, 0);
-
- mAdapter = new BackupAdapter(this);
- setListAdapter(mAdapter);
-
- new LoadBackupItemsTask().execute();
-
- findViewById(R.id.generate_button).setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- new GenerateValuesTask().execute();
- }
- });
- }
-
- public static SharedPreferencesBackupHelper getSharedPreferencesBackupHelper(Context context) {
- return new SharedPreferencesBackupHelper(context, TEST_PREFS_1, TEST_PREFS_2);
- }
-
- public static FileBackupHelper getFileBackupHelper(Context context) {
- return new FileBackupHelper(context, TEST_FILE_1, TEST_FILE_2);
- }
-
- class LoadBackupItemsTask extends AsyncTask<Void, Void, List<BackupItem>> {
-
- @Override
- protected void onPreExecute() {
- super.onPreExecute();
- setProgressBarIndeterminateVisibility(true);
- }
-
- @Override
- protected List<BackupItem> doInBackground(Void... params) {
- List<BackupItem> items = new ArrayList<BackupItem>();
-
- items.add(new CategoryBackupItem(R.string.bu_preferences));
- loadPreferenceGroup1(items);
- loadPreferenceGroup2(items);
-
- items.add(new CategoryBackupItem(R.string.bu_files));
- loadFile(TEST_FILE_1, items);
- loadFile(TEST_FILE_2, items);
-
- return items;
- }
-
- private void loadPreferenceGroup1(List<BackupItem> items) {
- SharedPreferences prefs = getSharedPreferences(TEST_PREFS_1, MODE_PRIVATE);
-
- int intValue = prefs.getInt(INT_PREF, 0);
- items.add(new PreferenceBackupItem(TEST_PREFS_1, INT_PREF, "" + intValue));
-
- boolean boolValue = prefs.getBoolean(BOOL_PREF, false);
- items.add(new PreferenceBackupItem(TEST_PREFS_1, BOOL_PREF, "" + boolValue));
- }
-
- private void loadPreferenceGroup2(List<BackupItem> items) {
- SharedPreferences prefs = getSharedPreferences(TEST_PREFS_2, MODE_PRIVATE);
-
- float floatValue = prefs.getFloat(FLOAT_PREF, 0.0f);
- items.add(new PreferenceBackupItem(TEST_PREFS_2, FLOAT_PREF, "" + floatValue));
-
- long longValue = prefs.getLong(LONG_PREF, 0L);
- items.add(new PreferenceBackupItem(TEST_PREFS_2, LONG_PREF, "" + longValue));
-
- String stringValue = prefs.getString(STRING_PREF, null);
- items.add(new PreferenceBackupItem(TEST_PREFS_2, STRING_PREF, stringValue));
- }
-
- private void loadFile(String fileName, List<BackupItem> items) {
- StringBuilder contents = new StringBuilder();
- Scanner scanner = null;
- try {
- scanner = new Scanner(new File(getFilesDir(), fileName));
- while (scanner.hasNext()) {
- contents.append(scanner.nextLine());
- }
- scanner.close();
- } catch (FileNotFoundException e) {
- Log.e(TAG, "Couldn't find test file but this may be fine...", e);
- } finally {
- if (scanner != null) {
- scanner.close();
- }
- }
- items.add(new FileBackupItem(fileName, contents.toString()));
- }
-
- @Override
- protected void onPostExecute(List<BackupItem> result) {
- super.onPostExecute(result);
- setProgressBarIndeterminateVisibility(false);
- mAdapter.clear();
- mAdapter.addAll(result);
- }
- }
-
- class GenerateValuesTask extends AsyncTask<Void, Void, Exception> {
-
- @Override
- protected Exception doInBackground(Void... params) {
- Random random = new Random();
- generatePreferenceGroup1(random);
- generatePreferenceGroup2(random);
- try {
- generateTestFile(TEST_FILE_1, random);
- generateTestFile(TEST_FILE_2, random);
- } catch (FileNotFoundException e) {
- return e;
- }
- return null;
- }
-
- private void generatePreferenceGroup1(Random random) {
- SharedPreferences prefs = getSharedPreferences(TEST_PREFS_1, MODE_PRIVATE);
- SharedPreferences.Editor editor = prefs.edit();
- editor.putInt(INT_PREF, (random.nextInt(100) + 1));
- editor.putBoolean(BOOL_PREF, random.nextBoolean());
- editor.commit();
- }
-
- private void generatePreferenceGroup2(Random random) {
- SharedPreferences prefs = getSharedPreferences(TEST_PREFS_2, MODE_PRIVATE);
- SharedPreferences.Editor editor = prefs.edit();
- editor.putFloat(FLOAT_PREF, random.nextFloat());
- editor.putLong(LONG_PREF, random.nextLong());
- editor.putString(STRING_PREF, "Random number: " + (random.nextInt(100) + 1));
- editor.commit();
- }
-
- private void generateTestFile(String fileName, Random random)
- throws FileNotFoundException {
- File file = new File(getFilesDir(), fileName);
- PrintWriter writer = new PrintWriter(file);
- writer.write("Random number: " + (random.nextInt(100) + 1));
- writer.close();
- }
-
- @Override
- protected void onPostExecute(Exception exception) {
- super.onPostExecute(exception);
- if (exception != null) {
- Log.e(TAG, "Couldn't generate test data...", exception);
- Toast.makeText(BackupTestActivity.this, R.string.bu_generate_error,
- Toast.LENGTH_LONG).show();
- } else {
- showDialog(INSTRUCTIONS_DIALOG_ID);
-
- BackupManager backupManager = new BackupManager(BackupTestActivity.this);
- backupManager.dataChanged();
-
- new LoadBackupItemsTask().execute();
- }
- }
- }
-
- @Override
- public Dialog onCreateDialog(int id, Bundle args) {
- switch (id) {
- case INSTRUCTIONS_DIALOG_ID:
- return new AlertDialog.Builder(this)
- .setIcon(android.R.drawable.ic_dialog_info)
- .setTitle(R.string.backup_test)
- .setMessage(R.string.bu_instructions)
- .setPositiveButton(android.R.string.ok, null)
- .setNeutralButton(R.string.bu_settings, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- startActivity(new Intent(Settings.ACTION_PRIVACY_SETTINGS));
- }
- })
- .create();
-
- default:
- return super.onCreateDialog(id, args);
- }
- }
-
- interface BackupItem {
- int getViewType();
- View getView(LayoutInflater inflater, int position, View convertView, ViewGroup parent);
- }
-
- static class CategoryBackupItem implements BackupItem {
-
- private final int mTitleResId;
-
- CategoryBackupItem(int titleResId) {
- mTitleResId = titleResId;
- }
-
- @Override
- public int getViewType() {
- return 0;
- }
-
- @Override
- public View getView(LayoutInflater inflater, int position, View convertView,
- ViewGroup parent) {
- TextView view = (TextView) convertView;
- if (convertView == null) {
- view = (TextView) inflater.inflate(R.layout.test_category_row, parent, false);
- }
- view.setText(mTitleResId);
- return view;
- }
- }
-
- static class PreferenceBackupItem implements BackupItem {
-
- private final String mGroup;
-
- private final String mName;
-
- private final String mValue;
-
- PreferenceBackupItem(String group, String name, String value) {
- mGroup = group;
- mName = name;
- mValue = value;
- }
-
- @Override
- public int getViewType() {
- return 1;
- }
-
- @Override
- public View getView(LayoutInflater inflater, int position, View convertView,
- ViewGroup parent) {
- TextView view = (TextView) convertView;
- if (convertView == null) {
- view = (TextView) inflater.inflate(R.layout.bu_preference_row, parent, false);
- }
- view.setText(mGroup + "/" + mName + " : " + mValue);
- return view;
- }
- }
-
- static class FileBackupItem implements BackupItem {
-
- private final String mName;
-
- private final String mContents;
-
- FileBackupItem(String name, String contents) {
- mName = name;
- mContents = contents;
- }
-
- @Override
- public int getViewType() {
- return 2;
- }
-
- @Override
- public View getView(LayoutInflater inflater, int position, View convertView,
- ViewGroup parent) {
- TextView view = (TextView) convertView;
- if (convertView == null) {
- view = (TextView) inflater.inflate(R.layout.bu_preference_row, parent, false);
- }
- view.setText(mName + " : " + mContents);
- return view;
- }
- }
-
- class BackupAdapter extends BaseAdapter {
-
- private final LayoutInflater mLayoutInflater;
-
- private final List<BackupItem> mItems = new ArrayList<BackupItem>();
-
- public BackupAdapter(Context context) {
- mLayoutInflater = (LayoutInflater) context.getSystemService(LAYOUT_INFLATER_SERVICE);
- }
-
- public void clear() {
- mItems.clear();
- }
-
- public void addAll(List<BackupItem> items) {
- mItems.addAll(items);
- notifyDataSetChanged();
- }
-
- @Override
- public int getCount() {
- return mItems.size();
- }
-
- @Override
- public BackupItem getItem(int position) {
- return mItems.get(position);
- }
-
- @Override
- public long getItemId(int position) {
- return position;
- }
-
- @Override
- public boolean isEnabled(int position) {
- return false;
- }
-
- @Override
- public int getViewTypeCount() {
- return 3;
- }
-
- @Override
- public int getItemViewType(int position) {
- return getItem(position).getViewType();
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- return getItem(position).getView(mLayoutInflater, position, convertView, parent);
- }
- }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/location/GnssTtffTestsActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/location/GnssTtffTestsActivity.java
new file mode 100644
index 0000000..1ba925c
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/location/GnssTtffTestsActivity.java
@@ -0,0 +1,15 @@
+package com.android.cts.verifier.location;
+
+import com.android.cts.verifier.location.base.GnssCtsTestActivity;
+import android.location.cts.GnssTtffTests;
+
+/**
+ * Activity to execute CTS GnssStatusTest.
+ * It is a wrapper for {@link GnssTtffTests} running with AndroidJUnitRunner.
+ */
+
+public class GnssTtffTestsActivity extends GnssCtsTestActivity {
+ public GnssTtffTestsActivity() {
+ super(GnssTtffTests.class);
+ }
+}
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 0355cb4..23744c7 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/AttentionManagementVerifierActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/AttentionManagementVerifierActivity.java
@@ -32,7 +32,6 @@
import android.provider.ContactsContract.CommonDataKinds.Email;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.CommonDataKinds.StructuredName;
-import android.provider.Settings;
import android.service.notification.NotificationListenerService;
import android.util.Log;
import android.view.View;
@@ -101,7 +100,7 @@
tests.add(new SetModeAllTest());
tests.add(new AllInterceptsNothingTest());
tests.add(new DefaultOrderTest());
- tests.add(new PrioritytOrderTest());
+ tests.add(new PriorityOrderTest());
tests.add(new InterruptionOrderTest());
tests.add(new AmbientBitsTest());
tests.add(new LookupUriOrderTest());
@@ -113,13 +112,11 @@
private void createChannels() {
NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL_ID,
- NOTIFICATION_CHANNEL_ID, NotificationManager.IMPORTANCE_DEFAULT);
+ NOTIFICATION_CHANNEL_ID, NotificationManager.IMPORTANCE_MIN);
mNm.createNotificationChannel(channel);
NotificationChannel noisyChannel = new NotificationChannel(NOTIFICATION_CHANNEL_ID_NOISY,
NOTIFICATION_CHANNEL_ID_NOISY, NotificationManager.IMPORTANCE_HIGH);
noisyChannel.enableVibration(true);
- noisyChannel.setSound(
- Settings.System.DEFAULT_RINGTONE_URI, Notification.AUDIO_ATTRIBUTES_DEFAULT);
mNm.createNotificationChannel(noisyChannel);
}
@@ -528,7 +525,7 @@
}
// ordered by priority: B, C, A
- protected class PrioritytOrderTest extends InteractiveTestCase {
+ protected class PriorityOrderTest extends InteractiveTestCase {
@Override
View inflate(ViewGroup parent) {
return createAutoItem(parent, R.string.attention_priority_order);
@@ -585,8 +582,8 @@
@Override
void setUp() {
createChannels();
- // send B & C noisy
- sendNotifications(SEND_B | SEND_C, MODE_NONE, false, true);
+ // send B & C noisy with contact affinity
+ sendNotifications(SEND_B | SEND_C, MODE_URI, false, true);
status = READY;
// wait for then to not be recently noisy any more
delay(15000);
@@ -595,7 +592,7 @@
@Override
void test() {
if (status == READY) {
- // send A noisy
+ // send A noisy but no contact affinity
sendNotifications(SEND_A, MODE_NONE, false, true);
status = RETEST;
delay();
@@ -650,7 +647,8 @@
@Override
void setUp() {
createChannels();
- sendNotifications(MODE_NONE, true, false);
+ sendNotifications(SEND_B | SEND_C, MODE_NONE, true, true);
+ sendNotifications(SEND_A, MODE_NONE, true, false);
status = READY;
// wait for notifications to move through the system
delay();
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/MockListener.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/MockListener.java
index 8d1bb7a..d311faa 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/MockListener.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/MockListener.java
@@ -170,8 +170,10 @@
} else if (SERVICE_SNOOZE_DURATION.equals(action)) {
String tag = intent.getStringExtra(EXTRA_TAG);
String key = mNotificationKeys.get(tag);
- MockListener.this.snoozeNotification(key,
- intent.getLongExtra(EXTRA_LONG, (long) 0));
+ if (key != null) {
+ MockListener.this.snoozeNotification(key,
+ intent.getLongExtra(EXTRA_LONG, (long) 0));
+ }
} else if (SERVICE_GET_SNOOZED.equals(action)) {
mSnoozed.clear();
StatusBarNotification[] snoozed = MockListener.this.getSnoozedNotifications();
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 5bad113..20e7d1e 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/NotificationListenerVerifierActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/NotificationListenerVerifierActivity.java
@@ -84,11 +84,13 @@
tests.add(new DismissOneWithReasonTest());
tests.add(new DismissAllTest());
tests.add(new SnoozeNotificationForTimeTest());
+ tests.add(new SnoozeNotificationForTimeCancelTest());
tests.add(new GetSnoozedNotificationTest());
tests.add(new EnableHintsTest());
tests.add(new SnoozeTest());
tests.add(new UnsnoozeTest());
tests.add(new MessageBundleTest());
+ tests.add(new EnableHintsTest());
tests.add(new IsDisabledTest());
tests.add(new ServiceStoppedTest());
tests.add(new NotificationNotReceivedTest());
@@ -792,6 +794,85 @@
}
}
+ /**
+ * Posts notifications, snoozes one of them. Verifies that it is snoozed. Cancels all
+ * notifications and reposts them. Confirms that the original notification is still snoozed.
+ */
+ private class SnoozeNotificationForTimeCancelTest extends InteractiveTestCase {
+
+ final static int READY_TO_SNOOZE = 0;
+ final static int SNOOZED = 1;
+ final static int READY_TO_CHECK_FOR_SNOOZE = 2;
+ int state = -1;
+ long snoozeTime = 10000;
+ private String tag;
+ @Override
+ View inflate(ViewGroup parent) {
+ return createAutoItem(parent, R.string.nls_snooze_one_time);
+ }
+
+ @Override
+ void setUp() {
+ createChannel();
+ sendNotifications();
+ tag = mTag1;
+ status = READY;
+ state = READY_TO_SNOOZE;
+ delay();
+ }
+
+ @Override
+ void test() {
+ status = RETEST;
+ if (state == READY_TO_SNOOZE) {
+ MockListener.snoozeOneFor(mContext, tag, snoozeTime);
+ state = SNOOZED;
+ } else if (state == SNOOZED){
+ MockListener.probeListenerSnoozed(mContext,
+ new MockListener.StringListResultCatcher() {
+ @Override
+ public void accept(List<String> result) {
+ if (result != null && result.size() >= 1
+ && result.contains(tag)) {
+ // cancel and repost
+ sendNotifications();
+ state = READY_TO_CHECK_FOR_SNOOZE;
+ delay();
+ } else {
+ logFail();
+ status = FAIL;
+ next();
+ }
+ }
+ });
+ } else {
+ MockListener.probeListenerSnoozed(mContext,
+ new MockListener.StringListResultCatcher() {
+ @Override
+ public void accept(List<String> result) {
+ if (result != null && result.size() >= 1
+ && result.contains(tag)) {
+ status = PASS;
+ } else {
+ logFail();
+ status = FAIL;
+ }
+ next();
+ }
+ });
+ }
+ delay();
+ }
+
+ @Override
+ void tearDown() {
+ mNm.cancelAll();
+ deleteChannel();
+ MockListener.resetListenerData(mContext);
+ delay();
+ }
+ }
+
private class GetSnoozedNotificationTest extends InteractiveTestCase {
final static int READY_TO_SNOOZE = 0;
final static int SNOOZED = 1;
@@ -858,7 +939,7 @@
new MockListener.StringListResultCatcher() {
@Override
public void accept(List<String> result) {
- if (result != null && result.size() == 2
+ if (result != null && result.size() >= 2
&& result.contains(mTag1)
&& result.contains(mTag2)) {
status = PASS;
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/telecom/CtsConnection.java b/apps/CtsVerifier/src/com/android/cts/verifier/telecom/CtsConnection.java
index c935841..c004b73 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/telecom/CtsConnection.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/telecom/CtsConnection.java
@@ -20,12 +20,16 @@
import android.media.AudioAttributes;
import android.media.AudioManager;
import android.media.MediaPlayer;
+import android.telecom.CallAudioState;
import android.telecom.Connection;
import android.telecom.DisconnectCause;
import android.telecom.VideoProfile;
import com.android.cts.verifier.R;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
/**
* An implementation of the {@link android.telecom.Connection} class used by the
* {@link CtsConnectionService}.
@@ -51,6 +55,7 @@
private final Listener mListener;
private final MediaPlayer mMediaPlayer;
private final Context mContext;
+ private CountDownLatch mWaitForCallAudioStateChanged = new CountDownLatch(1);
public CtsConnection(Context context, boolean isIncomingCall,
Listener listener, boolean hasAudio) {
@@ -131,6 +136,20 @@
}
}
+ public void onCallAudioStateChanged(CallAudioState state) {
+ mWaitForCallAudioStateChanged.countDown();
+ mWaitForCallAudioStateChanged = new CountDownLatch(1);
+
+ }
+
+ public void waitForAudioStateChanged() {
+ try {
+ mWaitForCallAudioStateChanged.await(CtsConnectionService.TIMEOUT_MILLIS,
+ TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ }
+ }
+
private void setDisconnectedAndDestroy(DisconnectCause cause) {
setDisconnected(cause);
destroy();
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/telecom/CtsConnectionService.java b/apps/CtsVerifier/src/com/android/cts/verifier/telecom/CtsConnectionService.java
index 528c221..7cfcbf8 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/telecom/CtsConnectionService.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/telecom/CtsConnectionService.java
@@ -26,35 +26,74 @@
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
/**
* CTS Verifier ConnectionService implementation.
*/
public class CtsConnectionService extends ConnectionService {
+ static final int TIMEOUT_MILLIS = 10000;
private CtsConnection.Listener mConnectionListener =
new CtsConnection.Listener() {
@Override
void onDestroyed(CtsConnection connection) {
- mConnections.remove(connection);
+ synchronized (mConnectionsLock) {
+ mConnections.remove(connection);
+ }
}
};
private static CtsConnectionService sConnectionService;
+ private static CountDownLatch sBindingLatch = new CountDownLatch(1);
private List<CtsConnection> mConnections = new ArrayList<>();
+ private Object mConnectionsLock = new Object();
+ private CountDownLatch mConnectionLatch = new CountDownLatch(1);
public static CtsConnectionService getConnectionService() {
return sConnectionService;
}
+ public static CtsConnectionService waitForAndGetConnectionService() {
+ if (sConnectionService == null) {
+ try {
+ sBindingLatch.await(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ }
+ }
+ return sConnectionService;
+ }
+
public CtsConnectionService() throws Exception {
super();
sConnectionService = this;
+ if (sBindingLatch != null) {
+ sBindingLatch.countDown();
+ }
+ sBindingLatch = new CountDownLatch(1);
}
public List<CtsConnection> getConnections() {
- return mConnections;
+ synchronized (mConnectionsLock) {
+ return new ArrayList<CtsConnection>(mConnections);
+ }
+ }
+
+ public CtsConnection waitForAndGetConnection() {
+ try {
+ mConnectionLatch.await(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ }
+ mConnectionLatch = new CountDownLatch(1);
+ synchronized (mConnectionsLock) {
+ if (mConnections.size() > 0) {
+ return mConnections.get(0);
+ } else {
+ return null;
+ }
+ }
}
@Override
@@ -109,7 +148,12 @@
connection.putExtras(moreExtras);
connection.setVideoState(request.getVideoState());
- mConnections.add(connection);
+ synchronized (mConnectionsLock) {
+ mConnections.add(connection);
+ }
+ if (mConnectionLatch != null) {
+ mConnectionLatch.countDown();
+ }
return connection;
}
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/telecom/PhoneAccountUtils.java b/apps/CtsVerifier/src/com/android/cts/verifier/telecom/PhoneAccountUtils.java
index cdc8665..6b0b3da 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/telecom/PhoneAccountUtils.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/telecom/PhoneAccountUtils.java
@@ -57,6 +57,19 @@
.setCapabilities(PhoneAccount.CAPABILITY_SELF_MANAGED)
.build();
+ public static final String TEST_SELF_MAANGED_PHONE_ACCOUNT2_ID = "selfMgdTest2";
+ public static final String TEST_SELF_MANAGED_PHONE_ACCOUNT2_LABEL = "CTSVerifier2";
+
+ public static final PhoneAccountHandle TEST_SELF_MANAGED_PHONE_ACCOUNT_HANDLE_2 =
+ new PhoneAccountHandle(new ComponentName(
+ PassFailButtons.class.getPackage().getName(),
+ CtsConnectionService.class.getName()), TEST_SELF_MAANGED_PHONE_ACCOUNT2_ID);
+ public static final PhoneAccount TEST_SELF_MANAGED_PHONE_ACCOUNT_2 = new PhoneAccount.Builder(
+ TEST_SELF_MANAGED_PHONE_ACCOUNT_HANDLE_2, TEST_SELF_MANAGED_PHONE_ACCOUNT2_LABEL)
+ .setAddress(TEST_SELF_MANAGED_PHONE_ACCOUNT_ADDRESS)
+ .setCapabilities(PhoneAccount.CAPABILITY_SELF_MANAGED)
+ .build();
+
/**
* Registers the test phone account.
* @param context The context.
@@ -89,13 +102,25 @@
}
/**
- * Registers the test self-managed phone account.
+ * Registers the test self-managed phone accounts.
* @param context The context.
*/
public static void registerTestSelfManagedPhoneAccount(Context context) {
TelecomManager telecomManager = (TelecomManager) context.getSystemService(
Context.TELECOM_SERVICE);
telecomManager.registerPhoneAccount(TEST_SELF_MANAGED_PHONE_ACCOUNT);
+ telecomManager.registerPhoneAccount(TEST_SELF_MANAGED_PHONE_ACCOUNT_2);
+ }
+
+ /**
+ * Unregisters the test self-managed phone accounts.
+ * @param context The context.
+ */
+ public static void unRegisterTestSelfManagedPhoneAccount(Context context) {
+ TelecomManager telecomManager = (TelecomManager) context.getSystemService(
+ Context.TELECOM_SERVICE);
+ telecomManager.unregisterPhoneAccount(TEST_SELF_MANAGED_PHONE_ACCOUNT_HANDLE);
+ telecomManager.unregisterPhoneAccount(TEST_SELF_MANAGED_PHONE_ACCOUNT_HANDLE_2);
}
/**
@@ -119,4 +144,15 @@
Context.TELECOM_SERVICE);
return telecomManager.getDefaultOutgoingPhoneAccount(PhoneAccount.SCHEME_TEL);
}
+
+ /**
+ * Retrieves the test phone account, or null if not registered.
+ * @param context The context.
+ * @return The Phone Account.
+ */
+ public static PhoneAccount getSelfManagedPhoneAccount2(Context context) {
+ TelecomManager telecomManager = (TelecomManager) context.getSystemService(
+ Context.TELECOM_SERVICE);
+ return telecomManager.getPhoneAccount(TEST_SELF_MANAGED_PHONE_ACCOUNT_HANDLE_2);
+ }
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/telecom/SelfManagedIncomingCallTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/telecom/SelfManagedIncomingCallTestActivity.java
new file mode 100644
index 0000000..2b06446
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/telecom/SelfManagedIncomingCallTestActivity.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.cts.verifier.telecom;
+
+import android.content.Context;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.telecom.Connection;
+import android.telecom.PhoneAccount;
+import android.telecom.TelecomManager;
+import android.view.View;
+import android.widget.Button;
+import android.widget.ImageView;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+import java.util.List;
+
+/**
+ * This test verifies functionality associated with the Self-Managed
+ * {@link android.telecom.ConnectionService} APIs. It ensures that Telecom will show an incoming
+ * call UI when a new incoming self-managed call is added when there is already an ongoing managed
+ * call or when there is an ongoing self-managed call in another app.
+ */
+public class SelfManagedIncomingCallTestActivity extends PassFailButtons.Activity {
+ private Uri TEST_DIAL_NUMBER_1 = Uri.fromParts("tel", "6505551212", null);
+ private Uri TEST_DIAL_NUMBER_2 = Uri.fromParts("tel", "4085551212", null);
+
+ private ImageView mStep1Status;
+ private Button mRegisterPhoneAccount;
+ private ImageView mStep2Status;
+ private Button mShowUi;
+ private ImageView mStep3Status;
+ private Button mConfirm;
+
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ View view = getLayoutInflater().inflate(R.layout.telecom_self_managed_answer, null);
+ setContentView(view);
+ setInfoResources(R.string.telecom_incoming_self_mgd_test,
+ R.string.telecom_incoming_self_mgd_info, -1);
+ setPassFailButtonClickListeners();
+ getPassButton().setEnabled(false);
+
+ mStep1Status = view.findViewById(R.id.step_1_status);
+ mRegisterPhoneAccount = view.findViewById(R.id.telecom_incoming_self_mgd_register_button);
+ mRegisterPhoneAccount.setOnClickListener(v -> {
+ PhoneAccountUtils.registerTestSelfManagedPhoneAccount(this);
+ PhoneAccount account = PhoneAccountUtils.getSelfManagedPhoneAccount(this);
+ PhoneAccount account2 = PhoneAccountUtils.getSelfManagedPhoneAccount2(this);
+ if (account != null &&
+ account.isEnabled() &&
+ account.hasCapabilities(PhoneAccount.CAPABILITY_SELF_MANAGED) &&
+ account2 != null &&
+ account2.isEnabled() &&
+ account2.hasCapabilities(PhoneAccount.CAPABILITY_SELF_MANAGED)) {
+ mRegisterPhoneAccount.setEnabled(false);
+ mShowUi.setEnabled(true);
+ mStep1Status.setImageResource(R.drawable.fs_good);
+ } else {
+ mStep1Status.setImageResource(R.drawable.fs_error);
+ }
+ });
+
+ mStep2Status = view.findViewById(R.id.step_2_status);
+ mShowUi = view.findViewById(R.id.telecom_incoming_self_mgd_show_ui_button);
+ mShowUi.setOnClickListener(v -> {
+ (new AsyncTask<Void, Void, Throwable>() {
+ @Override
+ protected Throwable doInBackground(Void... params) {
+ try {
+ Bundle extras = new Bundle();
+ extras.putParcelable(TelecomManager.EXTRA_INCOMING_CALL_ADDRESS,
+ TEST_DIAL_NUMBER_1);
+ TelecomManager telecomManager =
+ (TelecomManager) getSystemService(Context.TELECOM_SERVICE);
+ if (telecomManager == null) {
+ mStep2Status.setImageResource(R.drawable.fs_error);
+ return new Throwable("Could not get telecom service.");
+ }
+ telecomManager.addNewIncomingCall(
+ PhoneAccountUtils.TEST_SELF_MANAGED_PHONE_ACCOUNT_HANDLE, extras);
+
+ CtsConnectionService ctsConnectionService =
+ CtsConnectionService.waitForAndGetConnectionService();
+ if (ctsConnectionService == null) {
+ mStep2Status.setImageResource(R.drawable.fs_error);
+ return new Throwable("Could not get connection service.");
+ }
+
+ CtsConnection connection = ctsConnectionService.waitForAndGetConnection();
+ if (connection == null) {
+ mStep2Status.setImageResource(R.drawable.fs_error);
+ return new Throwable("Could not get connection.");
+ }
+ // Wait until the connection knows its audio state changed; at this point
+ // Telecom knows about the connection and can answer.
+ connection.waitForAudioStateChanged();
+ // Make it active to simulate an answer.
+ connection.setActive();
+
+ // Place the second call. It should trigger the incoming call UX.
+ Bundle extras2 = new Bundle();
+ extras2.putParcelable(TelecomManager.EXTRA_INCOMING_CALL_ADDRESS,
+ TEST_DIAL_NUMBER_2);
+ telecomManager.addNewIncomingCall(
+ PhoneAccountUtils.TEST_SELF_MANAGED_PHONE_ACCOUNT_HANDLE_2,
+ extras2);
+
+ return null;
+ } catch (Throwable t) {
+ return t;
+ }
+ }
+
+ @Override
+ protected void onPostExecute(Throwable t) {
+ if (t == null) {
+ mStep2Status.setImageResource(R.drawable.fs_good);
+ mShowUi.setEnabled(false);
+ mConfirm.setEnabled(true);
+ } else {
+ mStep2Status.setImageResource(R.drawable.fs_error);
+ }
+ }
+ }).execute();
+
+
+ });
+
+ mStep3Status = view.findViewById(R.id.step_3_status);
+ mConfirm = view.findViewById(R.id.telecom_incoming_self_mgd_confirm_answer_button);
+ mConfirm.setOnClickListener(v -> {
+ CtsConnectionService ctsConnectionService = CtsConnectionService.getConnectionService();
+ if (ctsConnectionService == null) {
+ mStep3Status.setImageResource(R.drawable.fs_error);
+ return;
+ }
+ List<CtsConnection> connections = ctsConnectionService.getConnections();
+ if (connections.size() != 1) {
+ mStep3Status.setImageResource(R.drawable.fs_error);
+ return;
+ }
+
+ if (connections.get(0).getState() == Connection.STATE_ACTIVE) {
+ connections
+ .stream()
+ .forEach((c) -> c.onDisconnect());
+ mStep3Status.setImageResource(R.drawable.fs_good);
+ getPassButton().setEnabled(true);
+ } else {
+ mStep3Status.setImageResource(R.drawable.fs_error);
+ }
+
+ PhoneAccountUtils.unRegisterTestSelfManagedPhoneAccount(this);
+ });
+
+ mShowUi.setEnabled(false);
+ mConfirm.setEnabled(false);
+ }
+}
diff --git a/build/device_info_package.mk b/build/device_info_package.mk
index b91c264..dcc81d3 100644
--- a/build/device_info_package.mk
+++ b/build/device_info_package.mk
@@ -26,7 +26,6 @@
$(DEVICE_INFO_PACKAGE).GenericDeviceInfo \
$(DEVICE_INFO_PACKAGE).GlesStubActivity \
$(DEVICE_INFO_PACKAGE).GraphicsDeviceInfo \
- $(DEVICE_INFO_PACKAGE).LibraryDeviceInfo \
$(DEVICE_INFO_PACKAGE).LocaleDeviceInfo \
$(DEVICE_INFO_PACKAGE).MediaDeviceInfo \
$(DEVICE_INFO_PACKAGE).MemoryDeviceInfo \
@@ -34,7 +33,8 @@
$(DEVICE_INFO_PACKAGE).PropertyDeviceInfo \
$(DEVICE_INFO_PACKAGE).ScreenDeviceInfo \
$(DEVICE_INFO_PACKAGE).StorageDeviceInfo \
- $(DEVICE_INFO_PACKAGE).UserDeviceInfo
+ $(DEVICE_INFO_PACKAGE).UserDeviceInfo \
+ $(DEVICE_INFO_PACKAGE).VintfDeviceInfo
ifeq ($(DEVICE_INFO_MIN_SDK),)
DEVICE_INFO_MIN_SDK := 8
diff --git a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/LibraryDeviceInfo.java b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/LibraryDeviceInfo.java
deleted file mode 100644
index 792c88a..0000000
--- a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/LibraryDeviceInfo.java
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.compatibility.common.deviceinfo;
-
-import android.util.Log;
-
-import com.android.compatibility.common.util.DeviceInfoStore;
-
-import java.io.Closeable;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.util.Formatter;
-
-/**
- * Library device info collector.
- */
-public final class LibraryDeviceInfo extends DeviceInfo {
-
- private static final String TAG = "LibraryDeviceInfo";
- private static final int BUFFER_SIZE_BYTES = 4096;
-
- @Override
- protected void collectDeviceInfo(DeviceInfoStore store) throws Exception {
- collectSystemLibs(store);
- collectVendorLibs(store);
- collectFrameworkJars(store);
- }
-
- private void collectSystemLibs(DeviceInfoStore store) throws Exception {
- store.startArray("lib");
- collectFileDetails(store, "/system/lib", ".so");
- store.endArray();
- }
-
- private void collectVendorLibs(DeviceInfoStore store) throws Exception {
- store.startArray("vendor_lib");
- collectFileDetails(store, "/system/vendor/lib", ".so");
- store.endArray();
- }
-
- private void collectFrameworkJars(DeviceInfoStore store) throws Exception {
- store.startArray("framework_jar");
- collectFileDetails(store, "/system/framework", ".jar");
- store.endArray();
- }
-
- private void collectFileDetails(DeviceInfoStore store, String path, String suffix)
- throws Exception {
- File dir = new File(path);
- File[] files = dir.listFiles();
- if (files == null) {
- return;
- }
- for (File file : files) {
- String name = file.getName();
- if (file.isFile() && name.endsWith(suffix)) {
- String sha1 = "unknown";
- try {
- sha1 = getSha1sum(file);
- } catch (IOException e) {
- Log.e(TAG, "Failed to hash " + file + ": ", e);
- }
- store.startGroup();
- store.addResult("name", name);
- store.addResult("sha1", sha1);
- store.endGroup();
- }
- }
- }
-
- private static String getSha1sum(File file) throws IOException {
- InputStream in = null;
- try {
- in = new FileInputStream(file);
- return sha1(in);
- } finally {
- close(in);
- }
- }
-
- private static void close(Closeable s) throws IOException {
- if (s == null) {
- return;
- }
- s.close();
- }
-
- /**
- * @return the SHA-1 digest of input as a hex string
- */
- public static String sha1(InputStream input) throws IOException {
- try {
- return toHexString(digest(input, "sha1"));
- } catch (NoSuchAlgorithmException e) {
- throw new RuntimeException(e);
- }
- }
-
- private static byte[] digest(InputStream in, String algorithm)
- throws NoSuchAlgorithmException, IOException {
- MessageDigest digest = MessageDigest.getInstance(algorithm);
- byte[] buffer = new byte[BUFFER_SIZE_BYTES];
- while (true) {
- int read = in.read(buffer);
- if (read < 0) {
- break;
- }
- digest.update(buffer, 0, read);
- }
- return digest.digest();
- }
-
- private static String toHexString(byte[] buffer) {
- Formatter formatter = new Formatter();
- try {
- for (byte b : buffer) {
- formatter.format("%02X", b);
- }
- return formatter.toString();
- } finally {
- formatter.close();
- }
- }
-}
diff --git a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/VintfDeviceInfo.java b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/VintfDeviceInfo.java
new file mode 100644
index 0000000..c38628d
--- /dev/null
+++ b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/VintfDeviceInfo.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.compatibility.common.deviceinfo;
+
+import android.os.VintfObject;
+import android.os.VintfRuntimeInfo;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Map;
+
+import com.android.compatibility.common.util.DeviceInfoStore;
+
+/**
+ * VINTF device info collector.
+ */
+public final class VintfDeviceInfo extends DeviceInfo {
+
+ private static final String[] sEmptyStringArray = new String[0];
+
+ @Override
+ protected void collectDeviceInfo(DeviceInfoStore store) throws Exception {
+ store.addResult("cpu_info", VintfRuntimeInfo.getCpuInfo());
+ store.addResult("os_name", VintfRuntimeInfo.getOsName());
+ store.addResult("node_name", VintfRuntimeInfo.getNodeName());
+ store.addResult("os_release", VintfRuntimeInfo.getOsRelease());
+ store.addResult("os_version", VintfRuntimeInfo.getOsVersion());
+ store.addResult("hardware_id", VintfRuntimeInfo.getHardwareId());
+ store.addResult("kernel_version", VintfRuntimeInfo.getKernelVersion());
+ store.addResult("kernel_sepolicy_version", VintfRuntimeInfo.getKernelSepolicyVersion());
+ store.addResult("sepolicy_version", VintfObject.getSepolicyVersion());
+
+ String[] hals = VintfObject.getHalNamesAndVersions();
+ store.addListResult("hals", hals == null
+ ? Collections.emptyList() : Arrays.<String>asList(hals));
+
+ Map<String, String[]> vndks = VintfObject.getVndkSnapshots();
+ if (vndks == null) vndks = Collections.emptyMap();
+ store.startArray("vndk_snapshots");
+ for (Map.Entry<String, String[]> e : vndks.entrySet()) {
+ store.startGroup();
+ store.addResult("version", e.getKey());
+ String[] libraries = e.getValue();
+ store.addListResult("libraries", libraries == null
+ ? Collections.emptyList() : Arrays.<String>asList(libraries));
+ store.endGroup();
+ }
+ store.endArray();
+ }
+}
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/ResultReporter.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/ResultReporter.java
index ad7cf47..7ea8a36 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/ResultReporter.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/ResultReporter.java
@@ -380,6 +380,7 @@
// Forward module results to the master.
mMasterResultReporter.mergeModuleResult(mCurrentModuleResult);
mCurrentModuleResult.resetTestRuns();
+ mCurrentModuleResult.resetRuntime();
}
}
@@ -595,9 +596,11 @@
}
FileInputStream fis = null;
+ LogFile logFile = null;
try {
fis = new FileInputStream(resultFile);
- mLogSaver.saveLogData("log-result", LogDataType.XML, fis);
+ logFile = mLogSaver.saveLogData("log-result", LogDataType.XML, fis);
+ debug("Result XML URL: %s", logFile.getUrl());
} catch (IOException ioe) {
CLog.e("[%s] error saving XML with log saver", mDeviceSerial);
CLog.e(ioe);
@@ -609,7 +612,8 @@
FileInputStream zipResultStream = null;
try {
zipResultStream = new FileInputStream(zippedResults);
- mLogSaver.saveLogData("results", LogDataType.ZIP, zipResultStream);
+ logFile = mLogSaver.saveLogData("results", LogDataType.ZIP, zipResultStream);
+ debug("Result zip URL: %s", logFile.getUrl());
} finally {
StreamUtil.close(zipResultStream);
}
@@ -790,7 +794,7 @@
* Log debug to the console.
*/
private static void debug(String format, Object... args) {
- CLog.d(format, args);
+ log(LogLevel.DEBUG, format, args);
}
/**
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/targetprep/DeviceFileCollector.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/targetprep/DeviceFileCollector.java
new file mode 100644
index 0000000..0e8426f
--- /dev/null
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/targetprep/DeviceFileCollector.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.compatibility.common.tradefed.targetprep;
+
+import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
+import com.android.compatibility.common.tradefed.testtype.CompatibilityTest;
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.config.Option;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.log.LogUtil.CLog;
+
+import java.nio.file.Paths;
+import java.io.File;
+import java.io.FileNotFoundException;
+
+/**
+ * An {@link PreconditionPreparer} that collects one device file.
+ */
+public class DeviceFileCollector extends PreconditionPreparer {
+
+ @Option(name = CompatibilityTest.SKIP_DEVICE_INFO_OPTION,
+ shortName = 'd',
+ description = "Whether device info collection should be skipped")
+ private boolean mSkipDeviceInfo = false;
+
+ @Option(name= "src-file", description = "The file path to copy to the results dir")
+ private String mSrcFile;
+
+ @Option(name = "dest-file", description = "The destination file path under the result")
+ private String mDestFile;
+
+ private File mResultFile;
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void run(ITestDevice device, IBuildInfo buildInfo) {
+ if (mSkipDeviceInfo)
+ return;
+
+ createResultDir(buildInfo);
+ if (mResultFile != null && !mResultFile.isDirectory() &&
+ mSrcFile != null && !mSrcFile.isEmpty()) {
+
+ try {
+ if (device.doesFileExist(mSrcFile)) {
+ device.pullFile(mSrcFile, mResultFile);
+ } else {
+ CLog.w(String.format("File does not exist on device: \"%s\"", mSrcFile));
+ }
+ } catch (DeviceNotAvailableException e) {
+ CLog.e("Caught exception during pull.");
+ CLog.e(e);
+ }
+ }
+ }
+
+ private void createResultDir(IBuildInfo buildInfo) {
+ CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(buildInfo);
+ try {
+ File resultDir = buildHelper.getResultDir();
+ if (mDestFile == null || mDestFile.isEmpty()) {
+ mDestFile = Paths.get(mSrcFile).getFileName().toString();
+ }
+ mResultFile = Paths.get(resultDir.getAbsolutePath(), mDestFile).toFile();
+ resultDir = mResultFile.getParentFile();
+ resultDir.mkdirs();
+ if (!resultDir.isDirectory()) {
+ CLog.e("%s is not a directory", resultDir.getAbsolutePath());
+ return;
+ }
+ } catch (FileNotFoundException fnfe) {
+ fnfe.printStackTrace();
+ }
+ }
+}
diff --git a/common/util/src/com/android/compatibility/common/util/IModuleResult.java b/common/util/src/com/android/compatibility/common/util/IModuleResult.java
index 06d66c0..2ebf181 100644
--- a/common/util/src/com/android/compatibility/common/util/IModuleResult.java
+++ b/common/util/src/com/android/compatibility/common/util/IModuleResult.java
@@ -30,6 +30,8 @@
void addRuntime(long elapsedTime);
+ void resetRuntime();
+
long getRuntime();
/**
diff --git a/common/util/src/com/android/compatibility/common/util/ModuleResult.java b/common/util/src/com/android/compatibility/common/util/ModuleResult.java
index 60038cf..60500a2 100644
--- a/common/util/src/com/android/compatibility/common/util/ModuleResult.java
+++ b/common/util/src/com/android/compatibility/common/util/ModuleResult.java
@@ -191,6 +191,14 @@
* {@inheritDoc}
*/
@Override
+ public void resetRuntime() {
+ mRuntime = 0;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
public long getRuntime() {
return mRuntime;
}
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/DirectBootHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/DirectBootHostTest.java
index 3a282bf..6a6c27d 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/DirectBootHostTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/DirectBootHostTest.java
@@ -47,8 +47,13 @@
private static final String MODE_EMULATED = "emulated";
private static final String MODE_NONE = "none";
+ private static final String FEATURE_DEVICE_ADMIN = "feature:android.software.device_admin\n";
+ private static final String FEATURE_AUTOMOTIVE = "feature:android.hardware.type.automotive\n";
+
private static final long SHUTDOWN_TIME_MS = 30 * 1000;
+ private String mFeatureList = null;
+
private IAbi mAbi;
private IBuildInfo mCtsBuild;
@@ -82,6 +87,22 @@
}
/**
+ * Automotive devices MUST support native FBE.
+ */
+ public void testAutomotiveNativeFbe() throws Exception {
+ if (!isSupportedDevice()) {
+ Log.v(TAG, "Device not supported; skipping test");
+ return;
+ } else if (!isAutomotiveDevice()) {
+ Log.v(TAG, "Device not automotive; skipping test");
+ return;
+ }
+
+ assertTrue("Automotive devices must support native FBE",
+ MODE_NATIVE.equals(getFbeMode()));
+ }
+
+ /**
* If device has native FBE, verify lifecycle.
*/
public void testDirectBootNative() throws Exception {
@@ -227,9 +248,20 @@
return "1".equals(output);
}
+ private boolean hasSystemFeature(final String feature) throws Exception {
+ if (mFeatureList == null) {
+ mFeatureList = getDevice().executeShellCommand("pm list features");
+ }
+
+ return mFeatureList.contains(feature);
+ }
+
private boolean isSupportedDevice() throws Exception {
- final String featureList = getDevice().executeShellCommand("pm list features");
- return featureList.contains("feature:android.software.device_admin\n");
+ return hasSystemFeature(FEATURE_DEVICE_ADMIN);
+ }
+
+ private boolean isAutomotiveDevice() throws Exception {
+ return hasSystemFeature(FEATURE_AUTOMOTIVE);
}
private void waitForBootCompleted() throws Exception {
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/EphemeralTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/EphemeralTest.java
index 77513c5..915f147 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/EphemeralTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/EphemeralTest.java
@@ -102,20 +102,47 @@
runDeviceTests(EPHEMERAL_1_PKG, TEST_CLASS, "testStartNormal");
}
- public void testEphemeralStartExposed() throws Exception {
- runDeviceTests(EPHEMERAL_1_PKG, TEST_CLASS, "testStartExposed");
+ // each connection to an exposed component needs to run in its own test to
+ // avoid sharing state. once an instant app is exposed to a component, it's
+ // exposed until the device restarts or the instant app is removed.
+ public void testEphemeralStartExposed01() throws Exception {
+ runDeviceTests(EPHEMERAL_1_PKG, TEST_CLASS, "testStartExposed01");
+ }
+ public void testEphemeralStartExposed02() throws Exception {
+ runDeviceTests(EPHEMERAL_1_PKG, TEST_CLASS, "testStartExposed02");
+ }
+ public void testEphemeralStartExposed03() throws Exception {
+ runDeviceTests(EPHEMERAL_1_PKG, TEST_CLASS, "testStartExposed03");
+ }
+ public void testEphemeralStartExposed04() throws Exception {
+ runDeviceTests(EPHEMERAL_1_PKG, TEST_CLASS, "testStartExposed04");
+ }
+ public void testEphemeralStartExposed05() throws Exception {
+ runDeviceTests(EPHEMERAL_1_PKG, TEST_CLASS, "testStartExposed05");
+ }
+ public void testEphemeralStartExposed06() throws Exception {
+ runDeviceTests(EPHEMERAL_1_PKG, TEST_CLASS, "testStartExposed06");
+ }
+ public void testEphemeralStartExposed07() throws Exception {
+ runDeviceTests(EPHEMERAL_1_PKG, TEST_CLASS, "testStartExposed07");
+ }
+ public void testEphemeralStartExposed08() throws Exception {
+ runDeviceTests(EPHEMERAL_1_PKG, TEST_CLASS, "testStartExposed08");
+ }
+ public void testEphemeralStartExposed09() throws Exception {
+ runDeviceTests(EPHEMERAL_1_PKG, TEST_CLASS, "testStartExposed09");
+ }
+ public void testEphemeralStartExposed10() throws Exception {
+ runDeviceTests(EPHEMERAL_1_PKG, TEST_CLASS, "testStartExposed10");
}
public void testEphemeralStartEphemeral() throws Exception {
runDeviceTests(EPHEMERAL_1_PKG, TEST_CLASS, "testStartEphemeral");
}
- /*
- * Disabled pending drops of updated prebuilts
public void testExposedSystemActivities() throws Exception {
runDeviceTests(EPHEMERAL_1_PKG, TEST_CLASS, "testExposedSystemActivities");
}
- */
public void testBuildSerialUnknown() throws Exception {
runDeviceTests(EPHEMERAL_1_PKG, TEST_CLASS, "testBuildSerialUnknown");
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/StorageHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/StorageHostTest.java
index 04fbd70..f679e83 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/StorageHostTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/StorageHostTest.java
@@ -182,6 +182,8 @@
getDevice().executeShellCommand("settings put global sys_storage_cache_max_bytes 0");
try {
for (int user : mUsers) {
+ waitForIdle();
+
// Clear all other cached data to give ourselves a clean slate
getDevice().executeShellCommand("pm trim-caches 4096G");
runDeviceTests(PKG_STATS, CLASS_STATS, "testCacheClearing", user);
@@ -196,6 +198,8 @@
}
public void doFullDisk() throws Exception {
+ waitForIdle();
+
// Clear all other cached and external storage data to give ourselves a
// clean slate to test against
getDevice().executeShellCommand("pm trim-caches 4096G");
@@ -234,6 +238,14 @@
}
}
+ public void waitForIdle() throws Exception {
+ // Try getting all pending events flushed out
+ for (int i = 0; i < 5; i++) {
+ getDevice().executeShellCommand("am wait-for-broadcast-idle");
+ Thread.sleep(1000);
+ }
+ }
+
public void runDeviceTests(String packageName, String testClassName, String testMethodName,
int userId) throws DeviceNotAvailableException {
Utils.runDeviceTests(getDevice(), packageName, testClassName, testMethodName, userId);
diff --git a/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp1/src/com/android/cts/ephemeralapp1/ClientTest.java b/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp1/src/com/android/cts/ephemeralapp1/ClientTest.java
index 8f44f89..c9d0e45 100644
--- a/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp1/src/com/android/cts/ephemeralapp1/ClientTest.java
+++ b/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp1/src/com/android/cts/ephemeralapp1/ClientTest.java
@@ -97,9 +97,6 @@
ContactsContract.CommonDataKinds.Email.CONTENT_TYPE, null),
makeIntent(Intent.ACTION_PICK, null,
ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_TYPE, null),
- makeIntent(Intent.ACTION_INSERT, null, ContactsContract.Contacts.CONTENT_TYPE, null),
- // Email
- makeIntent(Intent.ACTION_SEND, null, "text/plain", Uri.parse("mailto:")),
// File Storage
makeIntent(Intent.ACTION_OPEN_DOCUMENT, Intent.CATEGORY_OPENABLE, "*/*", null),
makeIntent(Intent.ACTION_OPEN_DOCUMENT, null, "*/*", null),
@@ -108,13 +105,8 @@
makeIntent(Intent.ACTION_OPEN_DOCUMENT_TREE, null, null, null),
makeIntent(Intent.ACTION_CREATE_DOCUMENT, Intent.CATEGORY_OPENABLE, "text/plain", null),
makeIntent(Intent.ACTION_CREATE_DOCUMENT, null, "text/plain", null),
- // Phone call
- makeIntent(Intent.ACTION_DIAL, null, null, Uri.parse("tel:")),
- // SMS
- makeIntent(Intent.ACTION_SEND, null, "text/plain", Uri.parse("sms:")),
- makeIntent(Intent.ACTION_SEND, null, "text/plain", Uri.parse("smsto:")),
- // Web
- makeIntent(Intent.ACTION_VIEW, null, "text/html", Uri.parse("https://example.com")),
+ // Framework
+ makeIntent(Intent.ACTION_CHOOSER, null, null, null),
};
private BroadcastReceiver mReceiver;
@@ -150,13 +142,13 @@
is("com.android.cts.ephemeralapp1"));
assertThat(resolveInfo.get(0).activityInfo.name,
is("com.android.cts.ephemeralapp1.EphemeralActivity"));
- assertThat(resolveInfo.get(0).instantAppAvailable,
+ assertThat(resolveInfo.get(0).isInstantAppAvailable,
is(true));
assertThat(resolveInfo.get(1).activityInfo.packageName,
is("com.android.cts.normalapp"));
assertThat(resolveInfo.get(1).activityInfo.name,
is("com.android.cts.normalapp.ExposedActivity"));
- assertThat(resolveInfo.get(1).instantAppAvailable,
+ assertThat(resolveInfo.get(1).isInstantAppAvailable,
is(false));
}
@@ -173,13 +165,13 @@
is("com.android.cts.ephemeralapp1"));
assertThat(resolveInfo.get(0).activityInfo.name,
is("com.android.cts.ephemeralapp1.EphemeralActivity"));
- assertThat(resolveInfo.get(0).instantAppAvailable,
+ assertThat(resolveInfo.get(0).isInstantAppAvailable,
is(true));
assertThat(resolveInfo.get(1).activityInfo.packageName,
is("com.android.cts.normalapp"));
assertThat(resolveInfo.get(1).activityInfo.name,
is("com.android.cts.normalapp.ExposedActivity"));
- assertThat(resolveInfo.get(1).instantAppAvailable,
+ assertThat(resolveInfo.get(1).isInstantAppAvailable,
is(false));
}
@@ -196,13 +188,13 @@
is("com.android.cts.ephemeralapp1"));
assertThat(resolveInfo.get(0).activityInfo.name,
is("com.android.cts.ephemeralapp1.EphemeralActivity"));
- assertThat(resolveInfo.get(0).instantAppAvailable,
+ assertThat(resolveInfo.get(0).isInstantAppAvailable,
is(true));
assertThat(resolveInfo.get(1).activityInfo.packageName,
is("com.android.cts.normalapp"));
assertThat(resolveInfo.get(1).activityInfo.name,
is("com.android.cts.normalapp.ExposedActivity"));
- assertThat(resolveInfo.get(1).instantAppAvailable,
+ assertThat(resolveInfo.get(1).isInstantAppAvailable,
is(false));
}
@@ -222,7 +214,7 @@
is("com.android.cts.normalapp"));
assertThat(resolveInfo.get(1).serviceInfo.name,
is("com.android.cts.normalapp.ExposedService"));
- assertThat(resolveInfo.get(1).instantAppAvailable,
+ assertThat(resolveInfo.get(1).isInstantAppAvailable,
is(false));
}
@@ -276,7 +268,7 @@
is("com.android.cts.normalapp"));
assertThat(resolveInfo.get(1).providerInfo.name,
is("com.android.cts.normalapp.ExposedProvider"));
- assertThat(resolveInfo.get(1).instantAppAvailable,
+ assertThat(resolveInfo.get(1).isInstantAppAvailable,
is(false));
}
@@ -454,217 +446,224 @@
}
@Test
- public void testStartExposed() throws Exception {
+ public void testStartExposed01() throws Exception {
// start the explicitly exposed activity
- {
- final Intent startExposedIntent = new Intent(ACTION_START_EXPOSED);
- InstrumentationRegistry
- .getContext().startActivity(startExposedIntent, null /*options*/);
- final TestResult testResult = getResult();
- assertThat(testResult.getPackageName(),
- is("com.android.cts.normalapp"));
- assertThat(testResult.getComponentName(),
- is("ExposedActivity"));
- assertThat(testResult.getStatus(),
- is("PASS"));
- assertThat(testResult.getEphemeralPackageInfoExposed(),
- is(true));
- assertThat(testResult.getException(),
- is(nullValue()));
- }
+ final Intent startExposedIntent = new Intent(ACTION_START_EXPOSED);
+ InstrumentationRegistry
+ .getContext().startActivity(startExposedIntent, null /*options*/);
+ final TestResult testResult = getResult();
+ assertThat(testResult.getPackageName(),
+ is("com.android.cts.normalapp"));
+ assertThat(testResult.getComponentName(),
+ is("ExposedActivity"));
+ assertThat(testResult.getStatus(),
+ is("PASS"));
+ assertThat(testResult.getEphemeralPackageInfoExposed(),
+ is(true));
+ assertThat(testResult.getException(),
+ is(nullValue()));
+ }
+ @Test
+ public void testStartExposed02() throws Exception {
// start the explicitly exposed activity; directed package
- {
- final Intent startExposedIntent = new Intent(ACTION_START_EXPOSED);
- startExposedIntent.setPackage("com.android.cts.normalapp");
- InstrumentationRegistry
- .getContext().startActivity(startExposedIntent, null /*options*/);
- final TestResult testResult = getResult();
- assertThat(testResult.getPackageName(),
- is("com.android.cts.normalapp"));
- assertThat(testResult.getComponentName(),
- is("ExposedActivity"));
- assertThat(testResult.getStatus(),
- is("PASS"));
- assertThat(testResult.getEphemeralPackageInfoExposed(),
- is(true));
- assertThat(testResult.getException(),
- is(nullValue()));
- }
+ final Intent startExposedIntent = new Intent(ACTION_START_EXPOSED);
+ startExposedIntent.setPackage("com.android.cts.normalapp");
+ InstrumentationRegistry
+ .getContext().startActivity(startExposedIntent, null /*options*/);
+ final TestResult testResult = getResult();
+ assertThat(testResult.getPackageName(),
+ is("com.android.cts.normalapp"));
+ assertThat(testResult.getComponentName(),
+ is("ExposedActivity"));
+ assertThat(testResult.getStatus(),
+ is("PASS"));
+ assertThat(testResult.getEphemeralPackageInfoExposed(),
+ is(true));
+ assertThat(testResult.getException(),
+ is(nullValue()));
+ }
+ @Test
+ public void testStartExposed03() throws Exception {
// start the explicitly exposed activity; directed component
- {
- final Intent startExposedIntent = new Intent(ACTION_START_EXPOSED);
- startExposedIntent.setComponent(new ComponentName(
- "com.android.cts.normalapp", "com.android.cts.normalapp.ExposedActivity"));
+ final Intent startExposedIntent = new Intent(ACTION_START_EXPOSED);
+ startExposedIntent.setComponent(new ComponentName(
+ "com.android.cts.normalapp", "com.android.cts.normalapp.ExposedActivity"));
+ InstrumentationRegistry
+ .getContext().startActivity(startExposedIntent, null /*options*/);
+ final TestResult testResult = getResult();
+ assertThat(testResult.getPackageName(),
+ is("com.android.cts.normalapp"));
+ assertThat(testResult.getComponentName(),
+ is("ExposedActivity"));
+ assertThat(testResult.getStatus(),
+ is("PASS"));
+ assertThat(testResult.getEphemeralPackageInfoExposed(),
+ is(true));
+ assertThat(testResult.getException(),
+ is(nullValue()));
+ }
+
+ @Test
+ public void testStartExposed04() throws Exception {
+ // start the implicitly exposed activity; directed package
+ try {
+ final Intent startExposedIntent = new Intent(Intent.ACTION_VIEW);
+ startExposedIntent.setPackage("com.android.cts.implicitapp");
+ startExposedIntent.addCategory(Intent.CATEGORY_BROWSABLE);
+ startExposedIntent.setData(Uri.parse("https://cts.google.com/implicit"));
InstrumentationRegistry
.getContext().startActivity(startExposedIntent, null /*options*/);
- final TestResult testResult = getResult();
- assertThat(testResult.getPackageName(),
- is("com.android.cts.normalapp"));
- assertThat(testResult.getComponentName(),
- is("ExposedActivity"));
- assertThat(testResult.getStatus(),
- is("PASS"));
- assertThat(testResult.getEphemeralPackageInfoExposed(),
- is(true));
- assertThat(testResult.getException(),
- is(nullValue()));
- }
+ fail("activity started");
+ } catch (ActivityNotFoundException expected) { }
+ }
- // start the implicitly exposed activity; directed package
- {
- try {
- final Intent startExposedIntent = new Intent(Intent.ACTION_VIEW);
- startExposedIntent.setPackage("com.android.cts.implicitapp");
- startExposedIntent.addCategory(Intent.CATEGORY_BROWSABLE);
- startExposedIntent.setData(Uri.parse("https://cts.google.com/implicit"));
- InstrumentationRegistry
- .getContext().startActivity(startExposedIntent, null /*options*/);
- fail("activity started");
- } catch (ActivityNotFoundException expected) { }
- }
-
+ @Test
+ public void testStartExposed05() throws Exception {
// start the implicitly exposed activity; directed component
- {
- try {
- final Intent startExposedIntent = new Intent(Intent.ACTION_VIEW);
- startExposedIntent.setComponent(new ComponentName(
- "com.android.cts.implicitapp",
- "com.android.cts.implicitapp.ImplicitActivity"));
- startExposedIntent.addCategory(Intent.CATEGORY_BROWSABLE);
- startExposedIntent.setData(Uri.parse("https://cts.google.com/implicit"));
- InstrumentationRegistry
- .getContext().startActivity(startExposedIntent, null /*options*/);
- fail("activity started");
- } catch (ActivityNotFoundException expected) { }
- }
-
- // start the exposed service; directed package
- {
- final Intent startExposedIntent = new Intent(ACTION_START_EXPOSED);
- startExposedIntent.setPackage("com.android.cts.normalapp");
- InstrumentationRegistry.getContext().startService(startExposedIntent);
- final TestResult testResult = getResult();
- assertThat(testResult.getPackageName(),
- is("com.android.cts.normalapp"));
- assertThat(testResult.getComponentName(),
- is("ExposedService"));
- assertThat(testResult.getStatus(),
- is("PASS"));
- assertThat(testResult.getEphemeralPackageInfoExposed(),
- is(true));
- assertThat(testResult.getException(),
- is(nullValue()));
- }
-
- // start the exposed service; directed component
- {
- final Intent startExposedIntent = new Intent(ACTION_START_EXPOSED);
+ try {
+ final Intent startExposedIntent = new Intent(Intent.ACTION_VIEW);
startExposedIntent.setComponent(new ComponentName(
- "com.android.cts.normalapp", "com.android.cts.normalapp.ExposedService"));
- InstrumentationRegistry.getContext().startService(startExposedIntent);
+ "com.android.cts.implicitapp",
+ "com.android.cts.implicitapp.ImplicitActivity"));
+ startExposedIntent.addCategory(Intent.CATEGORY_BROWSABLE);
+ startExposedIntent.setData(Uri.parse("https://cts.google.com/implicit"));
+ InstrumentationRegistry
+ .getContext().startActivity(startExposedIntent, null /*options*/);
+ fail("activity started");
+ } catch (ActivityNotFoundException expected) { }
+ }
+
+ @Test
+ public void testStartExposed06() throws Exception {
+ // start the exposed service; directed package
+ final Intent startExposedIntent = new Intent(ACTION_START_EXPOSED);
+ startExposedIntent.setPackage("com.android.cts.normalapp");
+ InstrumentationRegistry.getContext().startForegroundService(startExposedIntent);
+ final TestResult testResult = getResult();
+ assertThat(testResult.getPackageName(),
+ is("com.android.cts.normalapp"));
+ assertThat(testResult.getComponentName(),
+ is("ExposedService"));
+ assertThat(testResult.getStatus(),
+ is("PASS"));
+ assertThat(testResult.getEphemeralPackageInfoExposed(),
+ is(true));
+ assertThat(testResult.getException(),
+ is(nullValue()));
+ }
+
+ @Test
+ public void testStartExposed07() throws Exception {
+ // start the exposed service; directed component
+ final Intent startExposedIntent = new Intent(ACTION_START_EXPOSED);
+ startExposedIntent.setComponent(new ComponentName(
+ "com.android.cts.normalapp", "com.android.cts.normalapp.ExposedService"));
+ InstrumentationRegistry.getContext().startForegroundService(startExposedIntent);
+ final TestResult testResult = getResult();
+ assertThat(testResult.getPackageName(),
+ is("com.android.cts.normalapp"));
+ assertThat(testResult.getComponentName(),
+ is("ExposedService"));
+ assertThat(testResult.getMethodName(),
+ is("onStartCommand"));
+ assertThat(testResult.getStatus(),
+ is("PASS"));
+ assertThat(testResult.getEphemeralPackageInfoExposed(),
+ is(true));
+ assertThat(testResult.getException(),
+ is(nullValue()));
+ }
+
+ @Test
+ public void testStartExposed08() throws Exception {
+ // bind to the exposed service; directed package
+ final Intent startExposedIntent = new Intent(ACTION_START_EXPOSED);
+ startExposedIntent.setPackage("com.android.cts.normalapp");
+ final TestServiceConnection connection = new TestServiceConnection();
+ try {
+ assertThat(InstrumentationRegistry.getContext().bindService(
+ startExposedIntent, connection, Context.BIND_AUTO_CREATE /*flags*/),
+ is(true));
final TestResult testResult = getResult();
assertThat(testResult.getPackageName(),
is("com.android.cts.normalapp"));
assertThat(testResult.getComponentName(),
is("ExposedService"));
assertThat(testResult.getMethodName(),
- is("onStartCommand"));
+ is("onBind"));
assertThat(testResult.getStatus(),
is("PASS"));
assertThat(testResult.getEphemeralPackageInfoExposed(),
is(true));
assertThat(testResult.getException(),
is(nullValue()));
+ } finally {
+ InstrumentationRegistry.getContext().unbindService(connection);
}
+ }
- // bind to the exposed service; directed package
- {
- final Intent startExposedIntent = new Intent(ACTION_START_EXPOSED);
- startExposedIntent.setPackage("com.android.cts.normalapp");
- final TestServiceConnection connection = new TestServiceConnection();
- try {
- assertThat(InstrumentationRegistry.getContext().bindService(
- startExposedIntent, connection, Context.BIND_AUTO_CREATE /*flags*/),
- is(true));
- final TestResult testResult = getResult();
- assertThat(testResult.getPackageName(),
- is("com.android.cts.normalapp"));
- assertThat(testResult.getComponentName(),
- is("ExposedService"));
- assertThat(testResult.getMethodName(),
- is("onBind"));
- assertThat(testResult.getStatus(),
- is("PASS"));
- assertThat(testResult.getEphemeralPackageInfoExposed(),
- is(true));
- assertThat(testResult.getException(),
- is(nullValue()));
- } finally {
- InstrumentationRegistry.getContext().unbindService(connection);
- }
- }
-
+ @Test
+ public void testStartExposed09() throws Exception {
// bind to the exposed service; directed component
- {
- final Intent startExposedIntent = new Intent(ACTION_START_EXPOSED);
- startExposedIntent.setComponent(new ComponentName(
- "com.android.cts.normalapp", "com.android.cts.normalapp.ExposedService"));
- final TestServiceConnection connection = new TestServiceConnection();
- try {
- assertThat(InstrumentationRegistry.getContext().bindService(
- startExposedIntent, connection, Context.BIND_AUTO_CREATE /*flags*/),
- is(true));
- final TestResult testResult = getResult();
- assertThat(testResult.getPackageName(),
- is("com.android.cts.normalapp"));
- assertThat(testResult.getComponentName(),
- is("ExposedService"));
- assertThat(testResult.getMethodName(),
- is("onBind"));
- assertThat(testResult.getStatus(),
- is("PASS"));
- assertThat(testResult.getEphemeralPackageInfoExposed(),
- is(true));
- assertThat(testResult.getException(),
- is(nullValue()));
- } finally {
- InstrumentationRegistry.getContext().unbindService(connection);
- }
- }
-
- // connect to exposed provider
- {
- final String provider = "content://com.android.cts.normalapp.exposed.provider/table";
- final Cursor testCursor = InstrumentationRegistry
- .getContext().getContentResolver().query(
- Uri.parse(provider),
- null /*projection*/,
- null /*selection*/,
- null /*selectionArgs*/,
- null /*sortOrder*/);
- assertThat(testCursor, is(notNullValue()));
- assertThat(testCursor.getCount(), is(1));
- assertThat(testCursor.getColumnCount(), is(2));
- assertThat(testCursor.moveToFirst(), is(true));
- assertThat(testCursor.getInt(0), is(1));
- assertThat(testCursor.getString(1), is("ExposedProvider"));
+ final Intent startExposedIntent = new Intent(ACTION_START_EXPOSED);
+ startExposedIntent.setComponent(new ComponentName(
+ "com.android.cts.normalapp", "com.android.cts.normalapp.ExposedService"));
+ final TestServiceConnection connection = new TestServiceConnection();
+ try {
+ assertThat(InstrumentationRegistry.getContext().bindService(
+ startExposedIntent, connection, Context.BIND_AUTO_CREATE /*flags*/),
+ is(true));
final TestResult testResult = getResult();
assertThat(testResult.getPackageName(),
is("com.android.cts.normalapp"));
assertThat(testResult.getComponentName(),
- is("ExposedProvider"));
+ is("ExposedService"));
+ assertThat(testResult.getMethodName(),
+ is("onBind"));
assertThat(testResult.getStatus(),
is("PASS"));
assertThat(testResult.getEphemeralPackageInfoExposed(),
is(true));
assertThat(testResult.getException(),
is(nullValue()));
+ } finally {
+ InstrumentationRegistry.getContext().unbindService(connection);
}
}
@Test
+ public void testStartExposed10() throws Exception {
+ // connect to exposed provider
+ final String provider = "content://com.android.cts.normalapp.exposed.provider/table";
+ final Cursor testCursor = InstrumentationRegistry
+ .getContext().getContentResolver().query(
+ Uri.parse(provider),
+ null /*projection*/,
+ null /*selection*/,
+ null /*selectionArgs*/,
+ null /*sortOrder*/);
+ assertThat(testCursor, is(notNullValue()));
+ assertThat(testCursor.getCount(), is(1));
+ assertThat(testCursor.getColumnCount(), is(2));
+ assertThat(testCursor.moveToFirst(), is(true));
+ assertThat(testCursor.getInt(0), is(1));
+ assertThat(testCursor.getString(1), is("ExposedProvider"));
+ final TestResult testResult = getResult();
+ assertThat(testResult.getPackageName(),
+ is("com.android.cts.normalapp"));
+ assertThat(testResult.getComponentName(),
+ is("ExposedProvider"));
+ assertThat(testResult.getStatus(),
+ is("PASS"));
+ assertThat(testResult.getEphemeralPackageInfoExposed(),
+ is(true));
+ assertThat(testResult.getException(),
+ is(nullValue()));
+ }
+
+ @Test
public void testStartEphemeral() throws Exception {
// start the ephemeral activity
{
diff --git a/hostsidetests/appsecurity/test-apps/EphemeralTestApp/NormalApp/src/com/android/cts/normalapp/ClientTest.java b/hostsidetests/appsecurity/test-apps/EphemeralTestApp/NormalApp/src/com/android/cts/normalapp/ClientTest.java
index 22b8c25..cd5ac0e 100644
--- a/hostsidetests/appsecurity/test-apps/EphemeralTestApp/NormalApp/src/com/android/cts/normalapp/ClientTest.java
+++ b/hostsidetests/appsecurity/test-apps/EphemeralTestApp/NormalApp/src/com/android/cts/normalapp/ClientTest.java
@@ -99,13 +99,13 @@
is("com.android.cts.normalapp"));
assertThat(resolveInfo.get(0).activityInfo.name,
is("com.android.cts.normalapp.ExposedActivity"));
- assertThat(resolveInfo.get(0).instantAppAvailable,
+ assertThat(resolveInfo.get(0).isInstantAppAvailable,
is(false));
assertThat(resolveInfo.get(1).activityInfo.packageName,
is("com.android.cts.normalapp"));
assertThat(resolveInfo.get(1).activityInfo.name,
is("com.android.cts.normalapp.NormalActivity"));
- assertThat(resolveInfo.get(1).instantAppAvailable,
+ assertThat(resolveInfo.get(1).isInstantAppAvailable,
is(false));
}
@@ -124,13 +124,13 @@
is("com.android.cts.normalapp"));
assertThat(resolveInfo.get(0).activityInfo.name,
is("com.android.cts.normalapp.ExposedActivity"));
- assertThat(resolveInfo.get(0).instantAppAvailable,
+ assertThat(resolveInfo.get(0).isInstantAppAvailable,
is(false));
assertThat(resolveInfo.get(1).activityInfo.packageName,
is("com.android.cts.normalapp"));
assertThat(resolveInfo.get(1).activityInfo.name,
is("com.android.cts.normalapp.NormalActivity"));
- assertThat(resolveInfo.get(1).instantAppAvailable,
+ assertThat(resolveInfo.get(1).isInstantAppAvailable,
is(false));
}
@@ -167,13 +167,13 @@
is("com.android.cts.normalapp"));
assertThat(resolveInfo.get(0).serviceInfo.name,
is("com.android.cts.normalapp.ExposedService"));
- assertThat(resolveInfo.get(0).instantAppAvailable,
+ assertThat(resolveInfo.get(0).isInstantAppAvailable,
is(false));
assertThat(resolveInfo.get(1).serviceInfo.packageName,
is("com.android.cts.normalapp"));
assertThat(resolveInfo.get(1).serviceInfo.name,
is("com.android.cts.normalapp.NormalService"));
- assertThat(resolveInfo.get(1).instantAppAvailable,
+ assertThat(resolveInfo.get(1).isInstantAppAvailable,
is(false));
}
@@ -192,13 +192,13 @@
is("com.android.cts.normalapp"));
assertThat(resolveInfo.get(0).serviceInfo.name,
is("com.android.cts.normalapp.ExposedService"));
- assertThat(resolveInfo.get(0).instantAppAvailable,
+ assertThat(resolveInfo.get(0).isInstantAppAvailable,
is(false));
assertThat(resolveInfo.get(1).serviceInfo.packageName,
is("com.android.cts.normalapp"));
assertThat(resolveInfo.get(1).serviceInfo.name,
is("com.android.cts.normalapp.NormalService"));
- assertThat(resolveInfo.get(1).instantAppAvailable,
+ assertThat(resolveInfo.get(1).isInstantAppAvailable,
is(false));
}
@@ -240,7 +240,7 @@
is("com.android.cts.normalapp"));
assertThat(resolveInfo.get(1).providerInfo.name,
is("com.android.cts.normalapp.NormalProvider"));
- assertThat(resolveInfo.get(1).instantAppAvailable,
+ assertThat(resolveInfo.get(1).isInstantAppAvailable,
is(false));
}
@@ -264,7 +264,7 @@
is("com.android.cts.normalapp"));
assertThat(resolveInfo.get(1).providerInfo.name,
is("com.android.cts.normalapp.NormalProvider"));
- assertThat(resolveInfo.get(1).instantAppAvailable,
+ assertThat(resolveInfo.get(1).isInstantAppAvailable,
is(false));
}
diff --git a/hostsidetests/appsecurity/test-apps/StorageApp/src/com/android/cts/storageapp/Utils.java b/hostsidetests/appsecurity/test-apps/StorageApp/src/com/android/cts/storageapp/Utils.java
index 2e43a14..bf7f958 100644
--- a/hostsidetests/appsecurity/test-apps/StorageApp/src/com/android/cts/storageapp/Utils.java
+++ b/hostsidetests/appsecurity/test-apps/StorageApp/src/com/android/cts/storageapp/Utils.java
@@ -57,6 +57,8 @@
public static final long CACHE_EXT = (11) * MB_IN_BYTES;
public static final long CACHE_ALL = CACHE_INT + CACHE_EXT; // 78MB
+ public static final long CODE_ALL = 29 * MB_IN_BYTES;
+
public static void useSpace(Context c) throws Exception {
// We use prime numbers for all values so that we can easily identify
// which file(s) are missing from broken test results.
@@ -72,6 +74,8 @@
final File subdir = makeUniqueFile(c.getCacheDir());
Os.mkdir(subdir.getAbsolutePath(), 0700);
useFallocate(makeUniqueFile(subdir), 23 * MB_IN_BYTES);
+
+ useWrite(makeUniqueFile(c.getObbDir()), 29 * MB_IN_BYTES);
}
public static void assertAtLeast(long expected, long actual) {
@@ -125,10 +129,19 @@
}
public static long getSizeManual(File dir) throws Exception {
+ return getSizeManual(dir, false);
+ }
+
+ public static long getSizeManual(File dir, boolean excludeObb) throws Exception {
long size = getAllocatedSize(dir);
for (File f : dir.listFiles()) {
if (f.isDirectory()) {
- size += getSizeManual(f);
+ if (excludeObb && f.getName().equalsIgnoreCase("obb")
+ && f.getParentFile().getName().equalsIgnoreCase("Android")) {
+ Log.d(TAG, "Ignoring OBB directory " + f);
+ } else {
+ size += getSizeManual(f, excludeObb);
+ }
} else {
size += getAllocatedSize(f);
}
diff --git a/hostsidetests/appsecurity/test-apps/StorageApp/src/com/android/cts/storageapp/UtilsReceiver.java b/hostsidetests/appsecurity/test-apps/StorageApp/src/com/android/cts/storageapp/UtilsReceiver.java
index 2845185..927968c 100644
--- a/hostsidetests/appsecurity/test-apps/StorageApp/src/com/android/cts/storageapp/UtilsReceiver.java
+++ b/hostsidetests/appsecurity/test-apps/StorageApp/src/com/android/cts/storageapp/UtilsReceiver.java
@@ -19,6 +19,7 @@
import static com.android.cts.storageapp.Utils.TAG;
import static com.android.cts.storageapp.Utils.makeUniqueFile;
import static com.android.cts.storageapp.Utils.useFallocate;
+import static com.android.cts.storageapp.Utils.useWrite;
import android.app.Activity;
import android.content.BroadcastReceiver;
@@ -30,6 +31,8 @@
import android.util.Log;
import java.io.File;
+import java.util.Objects;
+import java.util.UUID;
public class UtilsReceiver extends BroadcastReceiver {
public static final String EXTRA_FRACTION = "fraction";
@@ -52,15 +55,37 @@
long allocated = 0;
try {
+ // When shared storage is backed by internal, then pivot our cache
+ // files between the two locations to ensure clearing logic works.
+ final File intDir = context.getCacheDir();
+ final File extDir = context.getExternalCacheDir();
+ final UUID intUuid = sm.getUuidForPath(intDir);
+ final UUID extUuid = sm.getUuidForPath(extDir);
+
+ Log.d(TAG, "Found internal " + intUuid + " and external " + extUuid);
+ final boolean doPivot = Objects.equals(intUuid, extUuid);
+
final double fraction = extras.getDouble(EXTRA_FRACTION, 0);
- final long quota = sm.getCacheQuotaBytes(sm.getUuidForPath(context.getCacheDir()));
+ final long quota = sm.getCacheQuotaBytes(intUuid);
final long bytes = (long) (quota * fraction);
final long time = extras.getLong(EXTRA_TIME, System.currentTimeMillis());
+ int i = 0;
while (allocated < bytes) {
- final File f = makeUniqueFile(context.getCacheDir());
+ final File target;
+ if (doPivot) {
+ target = (i++ % 2) == 0 ? intDir : extDir;
+ } else {
+ target = intDir;
+ }
+
+ final File f = makeUniqueFile(target);
final long size = 1024 * 1024;
- useFallocate(f, size);
+ if (target == intDir) {
+ useFallocate(f, size);
+ } else {
+ useWrite(f, size);
+ }
f.setLastModified(time);
allocated += Os.stat(f.getAbsolutePath()).st_blocks * 512;
}
diff --git a/hostsidetests/appsecurity/test-apps/StorageStatsApp/Android.mk b/hostsidetests/appsecurity/test-apps/StorageStatsApp/Android.mk
index 8b6d680..abee640 100644
--- a/hostsidetests/appsecurity/test-apps/StorageStatsApp/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/StorageStatsApp/Android.mk
@@ -18,7 +18,7 @@
LOCAL_MODULE_TAGS := tests
LOCAL_SDK_VERSION := test_current
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test ub-uiautomator
LOCAL_SRC_FILES := $(call all-java-files-under, src) \
$(call all-java-files-under, ../StorageApp/src)
diff --git a/hostsidetests/appsecurity/test-apps/StorageStatsApp/src/com/android/cts/storagestatsapp/StorageStatsTest.java b/hostsidetests/appsecurity/test-apps/StorageStatsApp/src/com/android/cts/storagestatsapp/StorageStatsTest.java
index de13476..ebc2946 100644
--- a/hostsidetests/appsecurity/test-apps/StorageStatsApp/src/com/android/cts/storagestatsapp/StorageStatsTest.java
+++ b/hostsidetests/appsecurity/test-apps/StorageStatsApp/src/com/android/cts/storagestatsapp/StorageStatsTest.java
@@ -19,6 +19,7 @@
import static android.os.storage.StorageManager.UUID_DEFAULT;
import static com.android.cts.storageapp.Utils.CACHE_ALL;
+import static com.android.cts.storageapp.Utils.CODE_ALL;
import static com.android.cts.storageapp.Utils.DATA_ALL;
import static com.android.cts.storageapp.Utils.MB_IN_BYTES;
import static com.android.cts.storageapp.Utils.PKG_A;
@@ -49,6 +50,7 @@
import android.os.Environment;
import android.os.UserHandle;
import android.os.storage.StorageManager;
+import android.support.test.uiautomator.UiDevice;
import android.system.Os;
import android.system.StructUtsname;
import android.test.InstrumentationTestCase;
@@ -57,6 +59,8 @@
import com.android.cts.storageapp.UtilsReceiver;
+import junit.framework.AssertionFailedError;
+
import java.io.File;
import java.util.UUID;
import java.util.concurrent.CountDownLatch;
@@ -110,6 +114,10 @@
final StorageStats afterApp = stats.queryStatsForUid(UUID_DEFAULT, uid);
final StorageStats afterUser = stats.queryStatsForUser(UUID_DEFAULT, user);
+ final long deltaCode = CODE_ALL;
+ assertMostlyEquals(deltaCode, afterApp.getAppBytes() - beforeApp.getAppBytes());
+ assertMostlyEquals(deltaCode, afterUser.getAppBytes() - beforeUser.getAppBytes());
+
final long deltaData = DATA_ALL;
assertMostlyEquals(deltaData, afterApp.getDataBytes() - beforeApp.getDataBytes());
assertMostlyEquals(deltaData, afterUser.getDataBytes() - beforeUser.getDataBytes());
@@ -134,6 +142,16 @@
assertMostlyEquals(DATA_ALL, bs.getDataBytes());
assertMostlyEquals(CACHE_ALL, bs.getCacheBytes());
+
+ // Since OBB storage space may be shared or isolated between users,
+ // we'll accept either expected or double usage.
+ try {
+ assertMostlyEquals(CODE_ALL * 2, as.getAppBytes(), 5 * MB_IN_BYTES);
+ assertMostlyEquals(CODE_ALL * 1, bs.getAppBytes(), 5 * MB_IN_BYTES);
+ } catch (AssertionFailedError e) {
+ assertMostlyEquals(CODE_ALL * 4, as.getAppBytes(), 5 * MB_IN_BYTES);
+ assertMostlyEquals(CODE_ALL * 2, bs.getAppBytes(), 5 * MB_IN_BYTES);
+ }
}
/**
@@ -205,7 +223,7 @@
// TODO: remove this once 34723223 is fixed
logCommand("sync");
- final long manualSize = getSizeManual(Environment.getExternalStorageDirectory());
+ final long manualSize = getSizeManual(Environment.getExternalStorageDirectory(), true);
final long statsSize = stats.queryExternalStatsForUser(UUID_DEFAULT, user).getTotalBytes();
assertMostlyEquals(manualSize, statsSize);
@@ -228,9 +246,11 @@
final File filesDir = context.getFilesDir();
final UUID filesUuid = sm.getUuidForPath(filesDir);
+ final String pmUuid = filesUuid.equals(StorageManager.UUID_DEFAULT) ? "internal"
+ : filesUuid.toString();
final long beforeAllocatable = sm.getAllocatableBytes(filesUuid);
- final long beforeFree = stats.getFreeBytes(UUID_DEFAULT);
+ final long beforeFree = stats.getFreeBytes(filesUuid);
final long beforeRaw = filesDir.getUsableSpace();
Log.d(TAG, "Before raw " + beforeRaw + ", free " + beforeFree + ", allocatable "
@@ -247,16 +267,16 @@
// Apps using up some cache space shouldn't change how much we can
// allocate, or how much we think is free; but it should decrease real
// disk space.
- if (stats.isQuotaSupported(UUID_DEFAULT)) {
+ if (stats.isQuotaSupported(filesUuid)) {
assertMostlyEquals(beforeAllocatable,
sm.getAllocatableBytes(filesUuid), 10 * MB_IN_BYTES);
assertMostlyEquals(beforeFree,
- stats.getFreeBytes(UUID_DEFAULT), 10 * MB_IN_BYTES);
+ stats.getFreeBytes(filesUuid), 10 * MB_IN_BYTES);
} else {
assertMostlyEquals(beforeAllocatable - totalAllocated,
sm.getAllocatableBytes(filesUuid), 10 * MB_IN_BYTES);
assertMostlyEquals(beforeFree - totalAllocated,
- stats.getFreeBytes(UUID_DEFAULT), 10 * MB_IN_BYTES);
+ stats.getFreeBytes(filesUuid), 10 * MB_IN_BYTES);
}
assertMostlyEquals(beforeRaw - totalAllocated,
filesDir.getUsableSpace(), 10 * MB_IN_BYTES);
@@ -267,7 +287,12 @@
// Allocate some space for ourselves, which should trim away at
// over-quota app first, even though its files are newer.
final long clear1 = filesDir.getUsableSpace() + (targetB / 2);
- sm.allocateBytes(filesUuid, clear1);
+ if (stats.isQuotaSupported(filesUuid)) {
+ sm.allocateBytes(filesUuid, clear1);
+ } else {
+ UiDevice.getInstance(getInstrumentation())
+ .executeShellCommand("pm trim-caches " + clear1 + " " + pmUuid);
+ }
assertMostlyEquals(targetA, getCacheBytes(PKG_A, user));
assertMostlyEquals(targetB / 2, getCacheBytes(PKG_B, user), 2 * MB_IN_BYTES);
@@ -277,7 +302,12 @@
// they're tied for cache ratios, we expect to clear about half of the
// remaining space from each of them.
final long clear2 = filesDir.getUsableSpace() + (targetB / 2);
- sm.allocateBytes(filesUuid, clear2);
+ if (stats.isQuotaSupported(filesUuid)) {
+ sm.allocateBytes(filesUuid, clear2);
+ } else {
+ UiDevice.getInstance(getInstrumentation())
+ .executeShellCommand("pm trim-caches " + clear2 + " " + pmUuid);
+ }
assertMostlyEquals(targetA / 2, getCacheBytes(PKG_A, user), 2 * MB_IN_BYTES);
assertMostlyEquals(targetA / 2, getCacheBytes(PKG_B, user), 2 * MB_IN_BYTES);
@@ -286,6 +316,11 @@
public void testCacheBehavior() throws Exception {
final Context context = getContext();
final StorageManager sm = context.getSystemService(StorageManager.class);
+ final StorageStatsManager stats = context.getSystemService(StorageStatsManager.class);
+
+ final UUID filesUuid = sm.getUuidForPath(context.getFilesDir());
+ final String pmUuid = filesUuid.equals(StorageManager.UUID_DEFAULT) ? "internal"
+ : filesUuid.toString();
final File normal = new File(context.getCacheDir(), "normal");
final File group = new File(context.getCacheDir(), "group");
@@ -321,7 +356,12 @@
tomb.setLastModified(tombTime);
final long clear1 = group.getUsableSpace() + (8 * MB_IN_BYTES);
- sm.allocateBytes(sm.getUuidForPath(group), clear1);
+ if (stats.isQuotaSupported(filesUuid)) {
+ sm.allocateBytes(filesUuid, clear1);
+ } else {
+ UiDevice.getInstance(getInstrumentation())
+ .executeShellCommand("pm trim-caches " + clear1 + " " + pmUuid);
+ }
assertTrue(a.exists());
assertTrue(b.exists());
diff --git a/hostsidetests/backup/Android.mk b/hostsidetests/backup/Android.mk
new file mode 100644
index 0000000..3ed8e99
--- /dev/null
+++ b/hostsidetests/backup/Android.mk
@@ -0,0 +1,34 @@
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_JAVA_RESOURCE_DIRS := assets/
+
+LOCAL_MODULE_TAGS := tests
+
+# tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts
+
+LOCAL_MODULE := CtsBackupHostTestCases
+
+LOCAL_JAVA_LIBRARIES := cts-tradefed tradefed compatibility-host-util
+
+include $(BUILD_HOST_JAVA_LIBRARY)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/hostsidetests/backup/AndroidTest.xml b/hostsidetests/backup/AndroidTest.xml
new file mode 100644
index 0000000..4675ed0
--- /dev/null
+++ b/hostsidetests/backup/AndroidTest.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Config for CTS Backup host test cases">
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="CtsBackupRestoreDeviceApp.apk" />
+ </target_preparer>
+ <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
+ <option name="jar" value="CtsBackupHostTestCases.jar" />
+ </test>
+</configuration>
diff --git a/hostsidetests/backup/app/Android.mk b/hostsidetests/backup/app/Android.mk
new file mode 100644
index 0000000..248207c
--- /dev/null
+++ b/hostsidetests/backup/app/Android.mk
@@ -0,0 +1,39 @@
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+# Don't include this package in any target
+LOCAL_MODULE_TAGS := tests
+# When built, explicitly put it in the data partition.
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_DEX_PREOPT := false
+
+LOCAL_PROGUARD_ENABLED := disabled
+
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+# tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts
+
+LOCAL_PACKAGE_NAME := CtsBackupRestoreDeviceApp
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_PACKAGE)
diff --git a/hostsidetests/backup/app/AndroidManifest.xml b/hostsidetests/backup/app/AndroidManifest.xml
new file mode 100644
index 0000000..32e8e4e
--- /dev/null
+++ b/hostsidetests/backup/app/AndroidManifest.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2017 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.backup.cts.backuprestoreapp">
+
+ <application
+ android:backupAgent="CtsBackupRestoreBackupAgent">
+ <uses-library android:name="android.test.runner" />
+
+ <activity
+ android:name=".KeyValueBackupRandomDataActivity"
+ android:launchMode="singleInstance">
+ <intent-filter>
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+
+ </application>
+
+ <instrumentation
+ android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.backup.cts.backuprestoreapp" />
+
+</manifest>
diff --git a/hostsidetests/backup/app/src/android/backup/cts/backuprestoreapp/CtsBackupRestoreBackupAgent.java b/hostsidetests/backup/app/src/android/backup/cts/backuprestoreapp/CtsBackupRestoreBackupAgent.java
new file mode 100644
index 0000000..e692fdd
--- /dev/null
+++ b/hostsidetests/backup/app/src/android/backup/cts/backuprestoreapp/CtsBackupRestoreBackupAgent.java
@@ -0,0 +1,33 @@
+/*
+ * 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.backup.cts.backuprestoreapp;
+
+import android.app.backup.BackupAgentHelper;
+
+public class CtsBackupRestoreBackupAgent extends BackupAgentHelper {
+ private static final String KEY_BACKUP_TEST_PREFS_PREFIX = "backup-test-prefs";
+ private static final String KEY_BACKUP_TEST_FILES_PREFIX = "backup-test-files";
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ addHelper(KEY_BACKUP_TEST_PREFS_PREFIX,
+ KeyValueBackupRandomDataActivity.getSharedPreferencesBackupHelper(this));
+ addHelper(KEY_BACKUP_TEST_FILES_PREFIX,
+ KeyValueBackupRandomDataActivity.getFileBackupHelper(this));
+ }
+}
diff --git a/hostsidetests/backup/app/src/android/backup/cts/backuprestoreapp/KeyValueBackupRandomDataActivity.java b/hostsidetests/backup/app/src/android/backup/cts/backuprestoreapp/KeyValueBackupRandomDataActivity.java
new file mode 100644
index 0000000..92db3f3
--- /dev/null
+++ b/hostsidetests/backup/app/src/android/backup/cts/backuprestoreapp/KeyValueBackupRandomDataActivity.java
@@ -0,0 +1,228 @@
+/*
+ * 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.backup.cts.backuprestoreapp;
+
+import android.app.Activity;
+import android.app.backup.BackupManager;
+import android.app.backup.FileBackupHelper;
+import android.app.backup.SharedPreferencesBackupHelper;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.util.Log;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.PrintWriter;
+import java.util.Random;
+import java.util.Scanner;
+
+/**
+ * Test activity that reads/writes to shared preferences and files.
+ *
+ * It uses logcat messages to send the data to the host side of the test.
+ * The format of logcat messages: "DATA_PREF: VALUE".
+ * VALUES_LOADED_MESSAGE is logged after all the values.
+ *
+ * Workflow onCreate:
+ * - Read shared preferences and files
+ * - If the values are default ones:
+ * - Randomly generate new values
+ * - Save new values to shared preferences and files
+ * - Load the new values.
+ *
+ * Migrated from BackupTestActivity in former BackupTest CTS Verfifier test.
+ */
+public class KeyValueBackupRandomDataActivity extends Activity {
+ private static final String TAG = KeyValueBackupRandomDataActivity.class.getSimpleName();
+
+ private static final String TEST_PREFS_1 = "test-prefs-1";
+ private static final String INT_PREF = "int-pref";
+ private static final String BOOL_PREF = "bool-pref";
+
+ private static final String TEST_PREFS_2 = "test-prefs-2";
+ private static final String FLOAT_PREF = "float-pref";
+ private static final String LONG_PREF = "long-pref";
+ private static final String STRING_PREF = "string-pref";
+
+ private static final String TEST_FILE_1 = "test-file-1";
+ private static final String TEST_FILE_2 = "test-file-2";
+
+ private static final int DEFAULT_INT_VALUE = 0;
+ private static final boolean DEFAULT_BOOL_VALUE = false;
+ private static final float DEFAULT_FLOAT_VALUE = 0.0f;
+ private static final long DEFAULT_LONG_VALUE = 0L;
+ private static final String DEFAULT_STRING_VALUE = null;
+
+ private static final String VALUES_LOADED_MESSAGE = "ValuesLoaded";
+ private static final String EMPTY_STRING_LOG = "empty";
+
+ private boolean mDefaultValues = true;
+ private boolean mValuesWereGenerated;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ new LoadBackupItemsTask().execute();
+ }
+
+ public static SharedPreferencesBackupHelper getSharedPreferencesBackupHelper(Context context) {
+ return new SharedPreferencesBackupHelper(context, TEST_PREFS_1, TEST_PREFS_2);
+ }
+
+ public static FileBackupHelper getFileBackupHelper(Context context) {
+ return new FileBackupHelper(context, TEST_FILE_1, TEST_FILE_2);
+ }
+
+ class LoadBackupItemsTask extends AsyncTask<Void, Void, Void> {
+
+ @Override
+ protected Void doInBackground(Void... params) {
+ loadPreferenceGroup1();
+ loadPreferenceGroup2();
+ loadFile(TEST_FILE_1);
+ loadFile(TEST_FILE_2);
+ return null;
+ }
+
+ private void loadPreferenceGroup1() {
+ SharedPreferences prefs = getSharedPreferences(TEST_PREFS_1, MODE_PRIVATE);
+
+ int intValue = prefs.getInt(INT_PREF, DEFAULT_INT_VALUE);
+ Log.i(TAG, INT_PREF + ":" + intValue);
+
+ boolean boolValue = prefs.getBoolean(BOOL_PREF, DEFAULT_BOOL_VALUE);
+ Log.i(TAG, BOOL_PREF + ":" + boolValue);
+
+ mDefaultValues = mDefaultValues
+ && intValue == DEFAULT_INT_VALUE
+ && boolValue == DEFAULT_BOOL_VALUE;
+ }
+
+ private void loadPreferenceGroup2() {
+ SharedPreferences prefs = getSharedPreferences(TEST_PREFS_2, MODE_PRIVATE);
+
+ float floatValue = prefs.getFloat(FLOAT_PREF, DEFAULT_FLOAT_VALUE);
+ Log.i(TAG, FLOAT_PREF + ":" + floatValue);
+
+ long longValue = prefs.getLong(LONG_PREF, DEFAULT_LONG_VALUE);
+ Log.i(TAG, LONG_PREF + ":" + longValue);
+
+ String stringValue = prefs.getString(STRING_PREF, DEFAULT_STRING_VALUE);
+ Log.i(TAG, STRING_PREF + ":" + stringValue);
+
+ mDefaultValues = mDefaultValues
+ && floatValue == DEFAULT_FLOAT_VALUE
+ && longValue == DEFAULT_LONG_VALUE
+ && stringValue == DEFAULT_STRING_VALUE;
+ }
+
+ private void loadFile(String fileName) {
+ StringBuilder contents = new StringBuilder();
+ Scanner scanner = null;
+ try {
+ scanner = new Scanner(new File(getFilesDir(), fileName));
+ while (scanner.hasNext()) {
+ contents.append(scanner.nextLine());
+ }
+ scanner.close();
+ } catch (FileNotFoundException e) {
+ Log.i(TAG, "Couldn't find test file but this may be fine...");
+ } finally {
+ if (scanner != null) {
+ scanner.close();
+ }
+ }
+ String logString = contents.toString();
+ logString = logString.isEmpty() ? EMPTY_STRING_LOG : logString;
+ Log.i(TAG, fileName + ":" + logString);
+
+ mDefaultValues = mDefaultValues && contents.toString().isEmpty();
+ }
+
+ @Override
+ protected void onPostExecute(Void param) {
+ super.onPostExecute(param);
+
+ if (mDefaultValues && !mValuesWereGenerated) {
+ new GenerateValuesTask().execute();
+ } else {
+ Log.i(TAG, VALUES_LOADED_MESSAGE);
+ }
+ }
+ }
+
+ class GenerateValuesTask extends AsyncTask<Void, Void, Exception> {
+
+ @Override
+ protected Exception doInBackground(Void... params) {
+ Random random = new Random();
+ generatePreferenceGroup1(random);
+ generatePreferenceGroup2(random);
+ try {
+ generateTestFile(TEST_FILE_1, random);
+ generateTestFile(TEST_FILE_2, random);
+ } catch (FileNotFoundException e) {
+ return e;
+ }
+ return null;
+ }
+
+ private void generatePreferenceGroup1(Random random) {
+ SharedPreferences prefs = getSharedPreferences(TEST_PREFS_1, MODE_PRIVATE);
+ SharedPreferences.Editor editor = prefs.edit();
+ editor.putInt(INT_PREF, (random.nextInt(100) + 1));
+ editor.putBoolean(BOOL_PREF, random.nextBoolean());
+ editor.commit();
+ }
+
+ private void generatePreferenceGroup2(Random random) {
+ SharedPreferences prefs = getSharedPreferences(TEST_PREFS_2, MODE_PRIVATE);
+ SharedPreferences.Editor editor = prefs.edit();
+ editor.putFloat(FLOAT_PREF, random.nextFloat());
+ editor.putLong(LONG_PREF, random.nextLong());
+ editor.putString(STRING_PREF, "Random number " + (random.nextInt(100) + 1));
+ editor.commit();
+ }
+
+ private void generateTestFile(String fileName, Random random)
+ throws FileNotFoundException {
+ File file = new File(getFilesDir(), fileName);
+ PrintWriter writer = new PrintWriter(file);
+ writer.write("Random number " + (random.nextInt(100) + 1));
+ writer.close();
+ }
+
+ @Override
+ protected void onPostExecute(Exception exception) {
+ super.onPostExecute(exception);
+ mValuesWereGenerated = true;
+
+ if (exception != null) {
+ Log.e(TAG, "Couldn't generate test data...", exception);
+ } else {
+ BackupManager backupManager = new BackupManager(
+ KeyValueBackupRandomDataActivity.this);
+ backupManager.dataChanged();
+
+ new LoadBackupItemsTask().execute();
+ }
+ }
+ }
+}
diff --git a/hostsidetests/backup/src/android/backup/cts/BackupRestoreHostSideTest.java b/hostsidetests/backup/src/android/backup/cts/BackupRestoreHostSideTest.java
new file mode 100644
index 0000000..86cecbb
--- /dev/null
+++ b/hostsidetests/backup/src/android/backup/cts/BackupRestoreHostSideTest.java
@@ -0,0 +1,342 @@
+/*
+ * 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.backup.cts.backup;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
+
+import static org.junit.Assume.assumeTrue;
+
+import com.android.compatibility.common.tradefed.testtype.CompatibilityHostTestBase;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.log.LogUtil.CLog;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.runner.RunWith;
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Scanner;
+import java.util.concurrent.TimeUnit;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Test for checking that key/value backup and restore works correctly.
+ * It interacts with the app that generates random values and saves them in different shared
+ * preferences and files. The app uses BackupAgentHelper to do key/value backup of those values.
+ * The tests verifies that the values are restored after the app is uninstalled and reinstalled.
+ *
+ * NB: The tests uses "bmgr backupnow" for backup, which works on N+ devices.
+ */
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class BackupRestoreHostSideTest extends CompatibilityHostTestBase {
+
+ /** Value of PackageManager.FEATURE_BACKUP */
+ private static final String FEATURE_BACKUP = "android.software.backup";
+
+ private static final String LOCAL_TRANSPORT =
+ "android/com.android.internal.backup.LocalTransport";
+
+ /** The name of the APK of the app under test */
+ private static final String TEST_APP_APK = "CtsBackupRestoreDeviceApp.apk";
+
+ /** The package name of the APK */
+ private static final String PACKAGE_UNDER_TEST = "android.backup.cts.backuprestoreapp";
+
+ /** The class name of the main activity in the APK */
+ private static final String CLASS_UNDER_TEST = "KeyValueBackupRandomDataActivity";
+
+ /** The command to launch the main activity */
+ private static final String START_ACTIVITY_UNDER_TEST_COMMAND = String.format(
+ "am start -W -a android.intent.action.MAIN -n %s/%s.%s", PACKAGE_UNDER_TEST,
+ PACKAGE_UNDER_TEST,
+ CLASS_UNDER_TEST);
+
+ /** The command to clear the user data of the package */
+ private static final String CLEAR_DATA_IN_PACKAGE_UNDER_TEST_COMMAND = String.format(
+ "pm clear %s", PACKAGE_UNDER_TEST);
+
+ /**
+ * Time we wait before reading the logcat again if the message we want is not logged by the
+ * app yet.
+ */
+ private static final int SMALL_LOGCAT_DELAY_MS = 1000;
+
+ /**
+ * Message logged by the app after all the values were loaded from SharedPreferences and files.
+ */
+ private static final String VALUES_LOADED_MESSAGE = "ValuesLoaded";
+
+ /**
+ * Keys for various shared preferences and files saved/read by the app.
+ */
+ private static final String INT_PREF = "int-pref";
+ private static final String BOOL_PREF = "bool-pref";
+ private static final String FLOAT_PREF = "float-pref";
+ private static final String LONG_PREF = "long-pref";
+ private static final String STRING_PREF = "string-pref";
+ private static final String TEST_FILE_1 = "test-file-1";
+ private static final String TEST_FILE_2 = "test-file-2";
+
+ /** Number of the values saved/restored by the app (keys listed above) */
+ private static final int NUMBER_OF_VALUES = 7;
+
+ /**
+ * String equivalents of the default values of the shared preferences logged by the app.
+ * These values are logged by the app by default if it fails to generate or restore values.
+ */
+ private static final String DEFAULT_INT_STRING = Integer.toString(0);
+ private static final String DEFAULT_BOOL_STRING = Boolean.toString(false);
+ private static final String DEFAULT_FLOAT_STRING = Float.toString(0.0f);
+ private static final String DEFAULT_LONG_STRING = Long.toString(0L);
+ private static final String DEFAULT_STRING_STRING = "null";
+ private static final String DEFAULT_FILE_STRING = "empty";
+
+ private boolean mIsBackupSupported;
+ private boolean mWasBackupEnabled;
+ private String mOldTransport;
+
+ /**
+ * Map of the shared preferences/files values reported by the app.
+ * Format example: INT_PREF -> 17 (string, as found in the logcat).
+ */
+ private Map<String, String> mSavedValues;
+
+ @Before
+ public void setUp() throws DeviceNotAvailableException, Exception {
+ mIsBackupSupported = mDevice.hasFeature("feature:" + FEATURE_BACKUP);
+ assumeTrue(mIsBackupSupported);
+ // Enable backup and select local backup transport
+ assertTrue("LocalTransport should be available.", hasBackupTransport(LOCAL_TRANSPORT));
+ mWasBackupEnabled = enableBackup(true);
+ mOldTransport = setBackupTransport(LOCAL_TRANSPORT);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ if (mIsBackupSupported) {
+ setBackupTransport(mOldTransport);
+ enableBackup(mWasBackupEnabled);
+ }
+ }
+
+ @Test
+ public void testKeyValueBackupAndRestore() throws Exception {
+ // Clear app data if any
+ mDevice.executeShellCommand(CLEAR_DATA_IN_PACKAGE_UNDER_TEST_COMMAND);
+ // Clear logcat
+ mDevice.executeAdbCommand("logcat", "-c");
+ // Start the main activity of the app
+ mDevice.executeShellCommand(START_ACTIVITY_UNDER_TEST_COMMAND);
+
+ // The app will generate some random values onCreate. Save them to mSavedValues
+ saveDataValuesReportedByApp();
+
+ // If all the values are default, there is something wrong with the app
+ assertNotAllValuesAreDefault();
+
+ // Run backup
+ // TODO: make this compatible with N-, potentially by replacing 'backupnow' with 'run'.
+ String backupnowOutput = mDevice.executeShellCommand(
+ "bmgr backupnow " + PACKAGE_UNDER_TEST);
+
+ assertBackupIsSuccessful(backupnowOutput);
+
+ assertNull(uninstallPackage(PACKAGE_UNDER_TEST));
+
+ installPackage(TEST_APP_APK);
+
+ mDevice.executeAdbCommand("logcat", "-c");
+
+ // Start the reinstalled app
+ mDevice.executeShellCommand(START_ACTIVITY_UNDER_TEST_COMMAND);
+
+ // If the app data was restored successfully, the app should not generate new values and
+ // the values reported by the app should match values saved in mSavedValues
+ assertValuesAreRestored();
+ }
+
+ /**
+ * Saves the data values reported by the app in {@code mSavedValues}.
+ */
+ private void saveDataValuesReportedByApp()
+ throws InterruptedException, DeviceNotAvailableException {
+ mSavedValues = readDataValuesFromLogcat();
+ assertEquals(NUMBER_OF_VALUES, mSavedValues.size());
+ }
+
+ /**
+ * Checks that at least some values in {@code mSavedValues} are different from corresponding
+ * default values.
+ */
+ private void assertNotAllValuesAreDefault() {
+ boolean allValuesAreDefault = mSavedValues.get(INT_PREF).equals(DEFAULT_INT_STRING)
+ && mSavedValues.get(BOOL_PREF).equals(DEFAULT_BOOL_STRING)
+ && mSavedValues.get(FLOAT_PREF).equals(DEFAULT_FLOAT_STRING)
+ && mSavedValues.get(LONG_PREF).equals(DEFAULT_LONG_STRING)
+ && mSavedValues.get(STRING_PREF).equals(DEFAULT_STRING_STRING)
+ && mSavedValues.get(TEST_FILE_1).equals(DEFAULT_FILE_STRING)
+ && mSavedValues.get(TEST_FILE_2).equals(DEFAULT_FILE_STRING);
+
+ assertFalse("The values were not changed from default.", allValuesAreDefault);
+ }
+
+ /**
+ * Parsing the output of "bmgr backupnow" command and checking that the package under test
+ * was backed up successfully.
+ *
+ * Expected format: "Package android.backup.cts.backuprestoreapp with result: Success"
+ */
+ private void assertBackupIsSuccessful(String backupnowOutput) {
+ // Assert backup was successful.
+ Scanner in = new Scanner(backupnowOutput);
+ while (in.hasNextLine()) {
+ String line = in.nextLine();
+
+ if (line.contains(PACKAGE_UNDER_TEST)) {
+ String result = line.split(":")[1].trim();
+
+ assertEquals(result, "Success");
+ }
+ }
+ in.close();
+ }
+
+ /**
+ * Reads the values logged by the app and verifies that they are the same as the ones we saved
+ * in {@code mSavedValues}.
+ */
+ private void assertValuesAreRestored()
+ throws InterruptedException, DeviceNotAvailableException {
+ Map<String, String> restoredValues = readDataValuesFromLogcat();
+
+ // Iterating through mSavedValues (vs. restoredValues) keyset to make sure all of the
+ // keys are reported in restored data
+ for (String dataType : mSavedValues.keySet()) {
+ assertEquals(mSavedValues.get(dataType), restoredValues.get(dataType));
+ }
+ }
+
+ /**
+ * Reads the values that app has reported via logcat and saves them in a map.
+ *
+ * The app logs the values once they are read from shared preferences or a file.
+ * If the values are default ones (i.e., it's the first run of the application), the app then
+ * generates random values and saves them in shared preferences or a file.
+ * Finally, the app reads the values from shared preferences or a file again and logs them.
+ * We are only interested in the final (generated or restored) values.
+ * The format of the log messages is "INT_PREF:17".
+ *
+ * @return Map of the values found in logcat.
+ */
+ private Map<String, String> readDataValuesFromLogcat()
+ throws InterruptedException, DeviceNotAvailableException {
+ Map<String, String> result = new HashMap<>();
+
+ long timeout = System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(30);
+
+ // The app generates reads, generates and reads values in async tasks fired onCreate.
+ // It may take some time for all tasks to finish and for logs to appear, so we check logcat
+ // repeatedly until we read VALUES_LOADED_MESSAGE, which is the last message the app logs.
+ search:
+ while (timeout >= System.currentTimeMillis()) {
+ String logs = getLogcatForClass(CLASS_UNDER_TEST);
+
+ Scanner in = new Scanner(logs);
+ while (in.hasNextLine()) {
+ String line = in.nextLine();
+ // Filter by TAG.
+ if (line.startsWith("I/" + CLASS_UNDER_TEST)) {
+ // Get rid of the TAG.
+ String message = line.split(":", 2)[1].trim();
+
+ // VALUES_LOADED_MESSAGE is logged by the app when all the values are loaded and
+ // logged so we can stop expecting more lines at this point.
+ if (message.equals(VALUES_LOADED_MESSAGE)) {
+ break search;
+ }
+
+ // Values are logged by the app in the format "INT_PREF:17".
+ String[] values = message.split(":");
+ if (values.length == 2) {
+ result.put(values[0], values[1]);
+ }
+ }
+ }
+ in.close();
+
+ // In case the key has not been found, wait for the log to update before
+ // performing the next search.
+ Thread.sleep(SMALL_LOGCAT_DELAY_MS);
+ }
+ assertTrue("Timeout while reading the app values", timeout > System.currentTimeMillis());
+ return result;
+ }
+
+ /**
+ * Returns the logcat string with the tag {@param className} and clears everything else.
+ */
+ private String getLogcatForClass(String className) throws DeviceNotAvailableException {
+ return mDevice.executeAdbCommand("logcat", "-v", "brief", "-d",
+ className + ":I", "*:S");
+ }
+
+ // Copied over from BackupQuotaTest
+ private boolean enableBackup(boolean enable) throws Exception {
+ boolean previouslyEnabled;
+ String output = mDevice.executeShellCommand("bmgr enabled");
+ Pattern pattern = Pattern.compile("^Backup Manager currently (enabled|disabled)$");
+ Matcher matcher = pattern.matcher(output.trim());
+ if (matcher.find()) {
+ previouslyEnabled = "enabled".equals(matcher.group(1));
+ } else {
+ throw new RuntimeException("non-parsable output setting bmgr enabled: " + output);
+ }
+
+ mDevice.executeShellCommand("bmgr enable " + enable);
+ return previouslyEnabled;
+ }
+
+ // Copied over from BackupQuotaTest
+ private String setBackupTransport(String transport) throws Exception {
+ String output = mDevice.executeShellCommand("bmgr transport " + transport);
+ Pattern pattern = Pattern.compile("\\(formerly (.*)\\)$");
+ Matcher matcher = pattern.matcher(output);
+ if (matcher.find()) {
+ return matcher.group(1);
+ } else {
+ throw new RuntimeException("non-parsable output setting bmgr transport: " + output);
+ }
+ }
+
+ // Copied over from BackupQuotaTest
+ private boolean hasBackupTransport(String transport) throws Exception {
+ String output = mDevice.executeShellCommand("bmgr list transports");
+ for (String t : output.split(" ")) {
+ if (transport.equals(t.trim())) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/AutofillRestrictionsTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/AutofillRestrictionsTest.java
index 73956c6..98bf3b7 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/AutofillRestrictionsTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/AutofillRestrictionsTest.java
@@ -28,6 +28,10 @@
private static final String AUTOFILL_PACKAGE_NAME = "com.android.cts.devicepolicy.autofillapp";
private static final String AUTOFILL_ACTIVITY_NAME = AUTOFILL_PACKAGE_NAME + ".SimpleActivity";
+ // Currently, autofill_service is a cloned service, so it's only set in the default user.
+ // That might change, so we're using a guard to decide how to set it
+ private final boolean USES_CLONED_SETTINGS = true;
+
int mUserId;
@Override
@@ -76,26 +80,39 @@
}
private void enableService() throws Exception {
- runShellCommand("settings put --user %d secure %s %s default",
- mUserId, AUTOFILL_SERVICE, SERVICE_NAME);
+ if (USES_CLONED_SETTINGS) {
+ runShellCommand("settings put secure %s %s default", AUTOFILL_SERVICE, SERVICE_NAME);
+ } else {
+ runShellCommand("settings put --user %d secure %s %s default",
+ mUserId, AUTOFILL_SERVICE, SERVICE_NAME);
+ }
waitForServiceSettingSaved(SERVICE_NAME);
}
private void disableService() {
- runShellCommand("settings delete --user %d secure %s", mUserId, AUTOFILL_SERVICE);
+ if (USES_CLONED_SETTINGS) {
+ runShellCommand("settings delete secure %s", AUTOFILL_SERVICE);
+ } else {
+ runShellCommand("settings delete --user %d secure %s", mUserId, AUTOFILL_SERVICE);
+ }
}
private void waitForServiceSettingSaved(String expected) throws Exception {
String actual = null;
- // Wait up to 0.5 second until setting is saved.
+ // Wait up to 0.5 seconds until setting is saved.
for (int i = 0; i < 5; i++) {
- actual = runShellCommand("settings get --user %d secure %s", mUserId, AUTOFILL_SERVICE);
+ if (USES_CLONED_SETTINGS) {
+ actual = runShellCommand("settings get secure %s", AUTOFILL_SERVICE);
+ } else {
+ actual = runShellCommand("settings get --user %d secure %s", mUserId,
+ AUTOFILL_SERVICE);
+ }
if (expected.equals(actual)) {
return;
}
Thread.sleep(100);
}
- fail("Expected service status: " + expected
+ fail("Expected service status for user " + mUserId + ": " + expected
+ "; actual: " + actual + " after 0.5 seconds");
}
}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDeviceAdminServiceTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDeviceAdminServiceTest.java
index b8cc99d..f0b8279 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDeviceAdminServiceTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDeviceAdminServiceTest.java
@@ -105,68 +105,91 @@
}
// Install
+ CLog.i("Installing apk1...");
installAppAsUser(OWNER_APK_1, getUserId());
+
+ CLog.i("Making it a device/profile owner...");
setAsOwnerOrFail(OWNER_COMPONENT);
withRetry(() -> assertServiceBound(OWNER_SERVICE));
// Remove admin.
+ CLog.i("Removing admin...");
removeAdmin(OWNER_COMPONENT, getUserId());
withRetry(() -> assertServiceNotBound(OWNER_SERVICE));
// Overwrite -> update.
+ CLog.i("Re-installing apk1...");
installAppAsUser(OWNER_APK_1, getUserId());
+
+ CLog.i("Making it a device/profile owner...");
setAsOwnerOrFail(OWNER_COMPONENT);
withRetry(() -> assertServiceBound(OWNER_SERVICE));
+ CLog.i("Installing apk2...");
installAppAsUser(OWNER_APK_2, getUserId());
withRetry(() -> assertServiceBound(OWNER_SERVICE)); // Should still be bound.
// Service exported -> not bound.
+ CLog.i("Installing apk3...");
installAppAsUser(OWNER_APK_3, getUserId());
withRetry(() -> assertServiceNotBound(OWNER_SERVICE));
// Recover.
+ CLog.i("Installing apk2 again...");
installAppAsUser(OWNER_APK_2, getUserId());
withRetry(() -> assertServiceBound(OWNER_SERVICE));
// Multiple service found -> not bound.
+ CLog.i("Installing apk4...");
installAppAsUser(OWNER_APK_4, getUserId());
withRetry(() -> assertServiceNotBound(OWNER_SERVICE));
withRetry(() -> assertServiceNotBound(OWNER_SERVICE2));
// Disable service1 -> now there's only one service, so should be bound.
+ CLog.i("Running testDisableService1...");
executeDeviceTestMethod(".ComponentController", "testDisableService1");
withRetry(() -> assertServiceNotBound(OWNER_SERVICE));
withRetry(() -> assertServiceBound(OWNER_SERVICE2));
+ CLog.i("Running testDisableService2...");
executeDeviceTestMethod(".ComponentController", "testDisableService2");
withRetry(() -> assertServiceNotBound(OWNER_SERVICE));
withRetry(() -> assertServiceNotBound(OWNER_SERVICE2));
+ CLog.i("Running testEnableService1...");
executeDeviceTestMethod(".ComponentController", "testEnableService1");
withRetry(() -> assertServiceBound(OWNER_SERVICE));
withRetry(() -> assertServiceNotBound(OWNER_SERVICE2));
+ CLog.i("Running testEnableService2...");
executeDeviceTestMethod(".ComponentController", "testEnableService2");
withRetry(() -> assertServiceNotBound(OWNER_SERVICE));
withRetry(() -> assertServiceNotBound(OWNER_SERVICE2));
// Remove admin.
+ CLog.i("Removing admin again...");
removeAdmin(OWNER_COMPONENT, getUserId());
withRetry(() -> assertServiceNotBound(OWNER_SERVICE));
// Retry with package 1 and remove admin.
+ CLog.i("Installing apk1 again...");
installAppAsUser(OWNER_APK_1, getUserId());
+
+ CLog.i("Making it a device/profile owner again...");
setAsOwnerOrFail(OWNER_COMPONENT);
withRetry(() -> assertServiceBound(OWNER_SERVICE));
+ CLog.i("Removing admin again...");
removeAdmin(OWNER_COMPONENT, getUserId());
withRetry(() -> assertServiceNotBound(OWNER_SERVICE));
// Now install package B and make it the owner. OWNER_APK_1 still exists, but it shouldn't
// interfere.
+ CLog.i("Installing apk B...");
installAppAsUser(OWNER_APK_B, getUserId());
+
+ CLog.i("Making it a device/profile owner...");
setAsOwnerOrFail(OWNER_COMPONENT_B);
withRetry(() -> assertServiceNotBound(OWNER_SERVICE));
withRetry(() -> assertServiceBound(OWNER_SERVICE_B));
diff --git a/hostsidetests/dumpsys/src/android/dumpsys/cts/BatteryStatsDumpsysTest.java b/hostsidetests/dumpsys/src/android/dumpsys/cts/BatteryStatsDumpsysTest.java
index fd3e85d..ea00512 100644
--- a/hostsidetests/dumpsys/src/android/dumpsys/cts/BatteryStatsDumpsysTest.java
+++ b/hostsidetests/dumpsys/src/android/dumpsys/cts/BatteryStatsDumpsysTest.java
@@ -272,26 +272,29 @@
}
private void checkWakelock(String[] parts) {
- assertEquals(20, parts.length);
+ assertEquals(23, parts.length);
assertNotNull(parts[4]); // wakelock
assertInteger(parts[5]); // full totalTime
assertEquals("f", parts[6]); // full
long full_count = assertInteger(parts[7]); // full count
- assertInteger(parts[8]); // current
- assertInteger(parts[9]); // max
+ assertInteger(parts[8]); // current duration
+ assertInteger(parts[9]); // max duration
+ assertInteger(parts[10]); // total duration
- assertInteger(parts[10]); // partial totalTime
- assertEquals("p", parts[11]); // partial
- long partial_count = assertInteger(parts[12]); // partial count
- assertInteger(parts[13]); // current
- assertInteger(parts[14]); // max
+ assertInteger(parts[11]); // partial totalTime
+ assertEquals("p", parts[12]); // partial
+ long partial_count = assertInteger(parts[13]); // partial count
+ assertInteger(parts[14]); // current duration
+ assertInteger(parts[15]); // max duration
+ assertInteger(parts[16]); // total duration
- assertInteger(parts[15]); // window totalTime
- assertEquals("w", parts[16]); // window
- long window_count = assertInteger(parts[17]); // window count
- assertInteger(parts[18]); // current
- assertInteger(parts[19]); // max
+ assertInteger(parts[17]); // window totalTime
+ assertEquals("w", parts[18]); // window
+ long window_count = assertInteger(parts[19]); // window count
+ assertInteger(parts[20]); // current duration
+ assertInteger(parts[21]); // max duration
+ assertInteger(parts[22]); // total duration
// Sanity checks.
assertTrue("full wakelock count must be >= 0", full_count >= 0);
@@ -387,7 +390,7 @@
}
private void checkBattery(String[] parts) {
- assertEquals(13, parts.length);
+ assertEquals(15, parts.length);
if (!parts[4].equals("N/A")) {
assertInteger(parts[4]); // startCount
}
@@ -399,6 +402,9 @@
long bOffReal = assertInteger(parts[10]); // batteryScreenOffRealtime
long bOffUp = assertInteger(parts[11]); // batteryScreenOffUptime
long bEstCap = assertInteger(parts[12]); // batteryEstimatedCapacity
+ assertInteger(parts[13]); // minLearnedBatteryCapacity
+ assertInteger(parts[14]); // maxLearnedBatteryCapacity
+
// The device cannot be up more than there are real-world seconds.
assertTrue("batteryRealtime must be >= batteryUptime", bReal >= bUp);
assertTrue("totalRealtime must be >= totalUptime", tReal >= tUp);
@@ -612,12 +618,13 @@
}
private void checkBluetoothMisc(String[] parts) {
- assertEquals(9, parts.length);
+ assertEquals(10, parts.length);
assertInteger(parts[4]); // totalTime
assertInteger(parts[5]); // count
assertInteger(parts[6]); // countBg
assertInteger(parts[7]); // actualTime
assertInteger(parts[8]); // actualTimeBg
+ assertInteger(parts[9]); // resultsCount
}
/**
diff --git a/hostsidetests/edi/Android.mk b/hostsidetests/edi/Android.mk
new file mode 100644
index 0000000..efa093f
--- /dev/null
+++ b/hostsidetests/edi/Android.mk
@@ -0,0 +1,32 @@
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_MODULE_TAGS := tests
+
+# tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts
+
+LOCAL_MODULE := CtsEdiHostTestCases
+
+LOCAL_JAVA_LIBRARIES := cts-tradefed tradefed compatibility-host-util
+
+include $(BUILD_HOST_JAVA_LIBRARY)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
\ No newline at end of file
diff --git a/apps/CtsVerifier/res/layout/bu_preference_row.xml b/hostsidetests/edi/AndroidTest.xml
similarity index 67%
rename from apps/CtsVerifier/res/layout/bu_preference_row.xml
rename to hostsidetests/edi/AndroidTest.xml
index c37eece..94be6b6 100644
--- a/apps/CtsVerifier/res/layout/bu_preference_row.xml
+++ b/hostsidetests/edi/AndroidTest.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 The Android Open Source Project
+<!-- Copyright (C) 2017 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -13,9 +13,8 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<TextView xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textSize="18sp"
- android:padding="5dp"
- />
+<configuration description="Config for CTS EDI host test cases">
+ <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
+ <option name="jar" value="CtsEdiHostTestCases.jar" />
+ </test>
+</configuration>
\ No newline at end of file
diff --git a/hostsidetests/edi/src/android/edi/cts/LibraryDeviceInfo.java b/hostsidetests/edi/src/android/edi/cts/LibraryDeviceInfo.java
new file mode 100644
index 0000000..b9f83f4
--- /dev/null
+++ b/hostsidetests/edi/src/android/edi/cts/LibraryDeviceInfo.java
@@ -0,0 +1,88 @@
+/*
+ * 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.edi.cts;
+
+import com.android.compatibility.common.util.DeviceInfo;
+import com.android.compatibility.common.util.HostInfoStore;
+import com.android.ddmlib.FileListingService.FileEntry;
+import com.android.tradefed.device.IFileEntry;
+import com.android.tradefed.device.ITestDevice;
+
+import java.util.Arrays;
+
+public class LibraryDeviceInfo extends DeviceInfo {
+
+ private ITestDevice mDevice;
+
+ @Override
+ protected void collectDeviceInfo(HostInfoStore store) throws Exception {
+
+ mDevice = getDevice();
+
+ collectSystemLibs(store);
+ collectVendorLibs(store);
+ collectFrameworkJars(store);
+ }
+
+ private void collectSystemLibs(HostInfoStore store) throws Exception {
+ store.startArray("lib");
+ collectFileDetails(store, "/system/lib", ".so");
+ store.endArray();
+ }
+
+ private void collectVendorLibs(HostInfoStore store) throws Exception {
+ store.startArray("vendor_lib");
+ collectFileDetails(store, "/system/vendor/lib", ".so");
+ store.endArray();
+ }
+
+ private void collectFrameworkJars(HostInfoStore store) throws Exception {
+ store.startArray("framework_jar");
+ collectFileDetails(store, "/system/framework", ".jar");
+ store.endArray();
+ }
+
+ private void collectFileDetails(HostInfoStore store, String path, String suffix)
+ throws Exception {
+ IFileEntry dir = mDevice.getFileEntry(path);
+
+ if(dir == null || !dir.isDirectory()) {
+ return;
+ }
+
+ for (IFileEntry file : dir.getChildren(false)) {
+ String name = file.getName();
+ if (!file.isDirectory() && name.endsWith(suffix)) {
+ String sha1 = getSha1(file.getFullPath());
+ store.startGroup();
+ store.addResult("name", name);
+ store.addResult("sha1", sha1);
+ store.endGroup();
+ }
+ }
+ }
+
+ private String getSha1(String filePath) {
+ String sha1 = "unknown";
+ try {
+ String out = mDevice.executeShellCommand("sha1sum " + filePath);
+ sha1 = out.split(" ", 2)[0].toUpperCase();
+ } catch (Exception e) {
+ // Do nothing.
+ }
+ return sha1;
+ }
+}
\ No newline at end of file
diff --git a/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsBackgroundService.java b/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsBackgroundService.java
index 496e61a..bc28c07 100644
--- a/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsBackgroundService.java
+++ b/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsBackgroundService.java
@@ -17,6 +17,7 @@
package com.android.server.cts.device.batterystats;
import static com.android.server.cts.device.batterystats.BatteryStatsBgVsFgActions.KEY_ACTION;
+import static com.android.server.cts.device.batterystats.BatteryStatsBgVsFgActions.KEY_REQUEST_CODE;
import static com.android.server.cts.device.batterystats.BatteryStatsBgVsFgActions.doAction;
import static com.android.server.cts.device.batterystats.BatteryStatsBgVsFgActions.isAppInBackground;
@@ -45,7 +46,9 @@
Log.w(TAG, "Couldn't determine if app is in background. Proceeding with test anyway.");
}
- Log.i(TAG, "Starting action from background service");
- doAction(this, intent.getStringExtra(KEY_ACTION));
+ String action = intent.getStringExtra(KEY_ACTION);
+ String requestCode = intent.getStringExtra(KEY_REQUEST_CODE);
+ Log.i(TAG, "Starting " + action + " from background service as request " + requestCode);
+ doAction(this, action, requestCode);
}
}
diff --git a/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsBgVsFgActions.java b/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsBgVsFgActions.java
index 480ca2f..e6e810f 100644
--- a/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsBgVsFgActions.java
+++ b/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsBgVsFgActions.java
@@ -55,9 +55,10 @@
public static final String ACTION_WIFI_DOWNLOAD = "action.wifi_download";
public static final String ACTION_WIFI_UPLOAD = "action.wifi_upload";
+ public static final String KEY_REQUEST_CODE = "request_code";
/** Perform the action specified by the given action code (see constants above). */
- public static void doAction(Context ctx, String actionCode) {
+ public static void doAction(Context ctx, String actionCode, String requestCode) {
if (actionCode == null) {
Log.e(TAG, "Intent was missing action.");
return;
@@ -65,22 +66,22 @@
sleep(100);
switch(actionCode) {
case ACTION_BLE_SCAN:
- doBleScan(ctx);
+ doBleScan(ctx, requestCode);
break;
case ACTION_JOB_SCHEDULE:
- doScheduleJob(ctx);
+ doScheduleJob(ctx, requestCode);
break;
case ACTION_SYNC:
- doSync(ctx);
+ doSync(ctx, requestCode);
break;
case ACTION_WIFI_SCAN:
- doWifiScan(ctx);
+ doWifiScan(ctx, requestCode);
break;
case ACTION_WIFI_DOWNLOAD:
- doWifiDownload(ctx);
+ doWifiDownload(ctx, requestCode);
break;
case ACTION_WIFI_UPLOAD:
- doWifiUpload(ctx);
+ doWifiUpload(ctx, requestCode);
break;
default:
Log.e(TAG, "Intent had invalid action");
@@ -88,7 +89,7 @@
sleep(100);
}
- private static void doBleScan(Context ctx) {
+ private static void doBleScan(Context ctx, String requestCode) {
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (bluetoothAdapter == null) {
Log.e(TAG, "Device does not support Bluetooth");
@@ -128,9 +129,10 @@
bleScanner.startScan(null, scanSettings, scanCallback);
sleep(2_000);
bleScanner.stopScan(scanCallback);
+ tellHostActionFinished(ACTION_BLE_SCAN, requestCode);
}
- private static void doScheduleJob(Context ctx) {
+ private static void doScheduleJob(Context ctx, String requestCode) {
final ComponentName JOB_COMPONENT_NAME =
new ComponentName("com.android.server.cts.device.batterystats",
SimpleJobService.class.getName());
@@ -148,13 +150,14 @@
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
- waitForReceiver(null, 3_000, latch, null);
+ waitForReceiver(null, 60_000, latch, null);
+ tellHostActionFinished(ACTION_JOB_SCHEDULE, requestCode);
return null;
}
}.execute();
}
- private static void doSync(Context ctx) {
+ private static void doSync(Context ctx, String requestCode) {
BatteryStatsAuthenticator.removeAllAccounts(ctx);
final Account account = BatteryStatsAuthenticator.getTestAccount();
// Create the test account.
@@ -185,36 +188,59 @@
if(ctx instanceof Activity){
((Activity) ctx).finish();
}
+ tellHostActionFinished(ACTION_SYNC, requestCode);
}
}.execute();
}
- private static void doWifiScan(Context ctx) {
+ private static void doWifiScan(Context ctx, String requestCode) {
+ // Sometimes a scan was already running (from a different uid), so the first scan doesn't
+ // start when requested. Therefore, additionally wait for whatever scan is currently running
+ // to finish, then request a scan again - at least one of these two scans should be
+ // attributed to this app.
+ doWifiScanOnce(ctx);
+ doWifiScanOnce(ctx);
+ tellHostActionFinished(ACTION_WIFI_SCAN, requestCode);
+ }
+
+ private static void doWifiScanOnce(Context ctx) {
IntentFilter intentFilter = new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
CountDownLatch onReceiveLatch = new CountDownLatch(1);
BroadcastReceiver receiver = registerReceiver(ctx, onReceiveLatch, intentFilter);
ctx.getSystemService(WifiManager.class).startScan();
- waitForReceiver(ctx, 3_000, onReceiveLatch, receiver);
+ waitForReceiver(ctx, 60_000, onReceiveLatch, receiver);
}
- private static void doWifiDownload(Context ctx) {
+ private static void doWifiDownload(Context ctx, String requestCode) {
+ CountDownLatch latch = new CountDownLatch(1);
+
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
- BatteryStatsWifiTransferTests.download();
+ BatteryStatsWifiTransferTests.download(requestCode);
+ latch.countDown();
return null;
}
}.execute();
+
+ waitForReceiver(null, 60_000, latch, null);
+ tellHostActionFinished(ACTION_WIFI_DOWNLOAD, requestCode);
}
- private static void doWifiUpload(Context ctx) {
+ private static void doWifiUpload(Context ctx, String requestCode) {
+ CountDownLatch latch = new CountDownLatch(1);
+
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
BatteryStatsWifiTransferTests.upload();
+ latch.countDown();
return null;
}
}.execute();
+
+ waitForReceiver(null, 60_000, latch, null);
+ tellHostActionFinished(ACTION_WIFI_UPLOAD, requestCode);
}
/** Register receiver to determine when given action is complete. */
@@ -258,6 +284,12 @@
}
}
+ /** Communicates to hostside (via logcat) that action has completed (regardless of success). */
+ private static void tellHostActionFinished(String actionCode, String requestCode) {
+ String s = String.format("Completed performing %s for request %s", actionCode, requestCode);
+ Log.i(TAG, s);
+ }
+
/** Determines whether the package is running as a background process. */
public static boolean isAppInBackground(Context context) throws ReflectiveOperationException {
String pkgName = context.getPackageName();
diff --git a/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsForegroundActivity.java b/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsForegroundActivity.java
index c41b244..fab306d 100644
--- a/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsForegroundActivity.java
+++ b/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsForegroundActivity.java
@@ -16,10 +16,9 @@
package com.android.server.cts.device.batterystats;
-import static com.android.server.cts.device.batterystats.BatteryStatsBgVsFgActions
- .ACTION_JOB_SCHEDULE;
import static com.android.server.cts.device.batterystats.BatteryStatsBgVsFgActions.ACTION_SYNC;
import static com.android.server.cts.device.batterystats.BatteryStatsBgVsFgActions.KEY_ACTION;
+import static com.android.server.cts.device.batterystats.BatteryStatsBgVsFgActions.KEY_REQUEST_CODE;
import static com.android.server.cts.device.batterystats.BatteryStatsBgVsFgActions.doAction;
import static com.android.server.cts.device.batterystats.BatteryStatsBgVsFgActions.isAppInBackground;
@@ -53,7 +52,9 @@
}
String action = intent.getStringExtra(KEY_ACTION);
- doAction(this, action);
+ String requestCode = intent.getStringExtra(KEY_REQUEST_CODE);
+ Log.i(TAG, "Starting " + action + " from foreground activity as request " + requestCode);
+ doAction(this, action, requestCode);
// ACTION_SYNC will finish itself. Others get finished here.
if (!ACTION_SYNC.equals(action)) {
diff --git a/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsWifiTransferTests.java b/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsWifiTransferTests.java
index 1633301..aa5c018 100644
--- a/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsWifiTransferTests.java
+++ b/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsWifiTransferTests.java
@@ -15,35 +15,14 @@
*/
package com.android.server.cts.device.batterystats;
-import static org.junit.Assert.assertTrue;
-
-import android.app.IntentService;
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.PackageManager;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.runner.AndroidJUnit4;
import android.util.Log;
import java.io.BufferedInputStream;
-import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.InputStream;
-import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Arrays;
-import java.util.Random;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
public class BatteryStatsWifiTransferTests {
private static final String TAG = "BatteryStatsWifiTransferTests";
@@ -53,7 +32,7 @@
/** Server to send requests to. */
private static final String SERVER_URL = "https://developer.android.com/index.html";
- public static String download() {
+ public static String download(String requestCode) {
HttpURLConnection conn = null;
try {
URL url = new URL(SERVER_URL);
@@ -69,7 +48,7 @@
while ((count = in.read(data)) != -1) {
total += count;
}
- Log.i(TAG, Integer.toString(total));
+ Log.i(TAG, String.format("request %s d=%d", requestCode, total));
} catch (IOException e) {
Log.i(TAG, e.toString());
return "Caught exception";
diff --git a/hostsidetests/incident/apps/graphicsstatsapp/src/com/android/server/cts/device/graphicsstats/DrawFramesActivity.java b/hostsidetests/incident/apps/graphicsstatsapp/src/com/android/server/cts/device/graphicsstats/DrawFramesActivity.java
index cccd9c9..2b9e5a6 100644
--- a/hostsidetests/incident/apps/graphicsstatsapp/src/com/android/server/cts/device/graphicsstats/DrawFramesActivity.java
+++ b/hostsidetests/incident/apps/graphicsstatsapp/src/com/android/server/cts/device/graphicsstats/DrawFramesActivity.java
@@ -147,6 +147,12 @@
drawFrames(new int[frameCount]);
}
+ public void waitForReady() throws InterruptedException, TimeoutException {
+ if (!mReady.await(4, TimeUnit.SECONDS)) {
+ throw new TimeoutException();
+ }
+ }
+
public void drawFrames(final int[] framesToDraw) throws InterruptedException, TimeoutException {
if (!mReady.await(4, TimeUnit.SECONDS)) {
throw new TimeoutException();
diff --git a/hostsidetests/incident/apps/graphicsstatsapp/src/com/android/server/cts/device/graphicsstats/SimpleDrawFrameTests.java b/hostsidetests/incident/apps/graphicsstatsapp/src/com/android/server/cts/device/graphicsstats/SimpleDrawFrameTests.java
index 854649f..ff3e9eb 100644
--- a/hostsidetests/incident/apps/graphicsstatsapp/src/com/android/server/cts/device/graphicsstats/SimpleDrawFrameTests.java
+++ b/hostsidetests/incident/apps/graphicsstatsapp/src/com/android/server/cts/device/graphicsstats/SimpleDrawFrameTests.java
@@ -39,6 +39,7 @@
@Test
public void testDrawTenFrames() throws Throwable {
DrawFramesActivity activity = mActivityRule.getActivity();
+ activity.waitForReady();
assertEquals(1, activity.getRenderedFramesCount());
assertEquals(0, activity.getDroppedReportsCount());
activity.drawFrames(10);
@@ -49,6 +50,7 @@
@Test
public void testDrawJankyFrames() throws Throwable {
DrawFramesActivity activity = mActivityRule.getActivity();
+ activity.waitForReady();
assertEquals(1, activity.getRenderedFramesCount());
assertEquals(0, activity.getDroppedReportsCount());
int[] frames = new int[50];
@@ -67,6 +69,7 @@
@Test
public void testDrawDaveyFrames() throws Throwable {
DrawFramesActivity activity = mActivityRule.getActivity();
+ activity.waitForReady();
assertEquals(1, activity.getRenderedFramesCount());
assertEquals(0, activity.getDroppedReportsCount());
int[] frames = new int[40];
diff --git a/hostsidetests/incident/src/com/android/server/cts/BatteryStatsValidationTest.java b/hostsidetests/incident/src/com/android/server/cts/BatteryStatsValidationTest.java
index 758c443..1c8ac57 100644
--- a/hostsidetests/incident/src/com/android/server/cts/BatteryStatsValidationTest.java
+++ b/hostsidetests/incident/src/com/android/server/cts/BatteryStatsValidationTest.java
@@ -15,8 +15,15 @@
*/
package com.android.server.cts;
+import com.android.ddmlib.IShellOutputReceiver;
import com.android.tradefed.log.LogUtil;
+import com.google.common.base.Charsets;
+
+import java.util.Random;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
/**
* Test for "dumpsys batterystats -c
*
@@ -52,6 +59,9 @@
public static final String ACTION_WIFI_DOWNLOAD = "action.wifi_download";
public static final String ACTION_WIFI_UPLOAD = "action.wifi_upload";
+ public static final String KEY_REQUEST_CODE = "request_code";
+ public static final String BG_VS_FG_TAG = "BatteryStatsBgVsFgActions";
+
@Override
protected void setUp() throws Exception {
super.setUp();
@@ -105,8 +115,8 @@
runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".BatteryStatsWakeLockTests",
"testHoldLongWakeLock");
- assertValueRange("wl", "BSShortWakeLock", 14, (long) (500 * 0.9), 500 * 2);
- assertValueRange("wl", "BSLongWakeLock", 14, (long) (3000 * 0.9), 3000 * 2);
+ assertValueRange("wl", "BSShortWakeLock", 15, (long) (500 * 0.9), 500 * 2); // partial max duration
+ assertValueRange("wl", "BSLongWakeLock", 15, (long) (3000 * 0.9), 3000 * 2); // partial max duration
batteryOffScreenOn();
}
@@ -130,14 +140,12 @@
installPackage(DEVICE_SIDE_TEST_APK, true);
// Foreground test.
- executeForeground(ACTION_BLE_SCAN);
- Thread.sleep(2_500);
+ executeForeground(ACTION_BLE_SCAN, 30_000);
assertValueRange("blem", "", 5, 1, 1); // ble_scan_count
assertValueRange("blem", "", 6, 0, 0); // ble_scan_count_bg
// Background test.
- executeBackground(ACTION_BLE_SCAN);
- Thread.sleep(2_500);
+ executeBackground(ACTION_BLE_SCAN, 30_000);
assertValueRange("blem", "", 5, 2, 2); // ble_scan_count
assertValueRange("blem", "", 6, 1, 1); // ble_scan_count_bg
@@ -149,14 +157,12 @@
installPackage(DEVICE_SIDE_TEST_APK, true);
// Foreground test.
- executeForeground(ACTION_JOB_SCHEDULE);
- Thread.sleep(4_000);
+ executeForeground(ACTION_JOB_SCHEDULE, 60_000);
assertValueRange("jb", "", 6, 1, 1); // count
assertValueRange("jb", "", 8, 0, 0); // background_count
// Background test.
- executeBackground(ACTION_JOB_SCHEDULE);
- Thread.sleep(4_000);
+ executeBackground(ACTION_JOB_SCHEDULE, 60_000);
assertValueRange("jb", "", 6, 2, 2); // count
assertValueRange("jb", "", 8, 1, 1); // background_count
@@ -168,15 +174,13 @@
installPackage(DEVICE_SIDE_TEST_APK, true);
// Foreground test.
- executeForeground(ACTION_SYNC);
- Thread.sleep(3_000);
+ executeForeground(ACTION_SYNC, 60_000);
// Allow one or two syncs in this time frame (not just one) due to unpredictable syncs.
assertValueRange("sy", DEVICE_SIDE_SYNC_COMPONENT, 6, 1, 2); // count
assertValueRange("sy", DEVICE_SIDE_SYNC_COMPONENT, 8, 0, 0); // background_count
// Background test.
- executeBackground(ACTION_SYNC);
- Thread.sleep(3_000);
+ executeBackground(ACTION_SYNC, 60_000);
assertValueRange("sy", DEVICE_SIDE_SYNC_COMPONENT, 6, 2, 4); // count
assertValueRange("sy", DEVICE_SIDE_SYNC_COMPONENT, 8, 1, 2); // background_count
@@ -186,18 +190,23 @@
public void testWifiScans() throws Exception {
batteryOnScreenOff();
installPackage(DEVICE_SIDE_TEST_APK, true);
+ // Whitelist this app against background wifi scan throttling
+ getDevice().executeShellCommand(String.format(
+ "settings put global wifi_scan_background_throttle_package_whitelist %s",
+ DEVICE_SIDE_TEST_PACKAGE));
// Foreground count test.
- executeForeground(ACTION_WIFI_SCAN);
- Thread.sleep(4_000);
- assertValueRange("wfl", "", 7, 1, 1); // scan_count
+ executeForeground(ACTION_WIFI_SCAN, 120_000);
+ // Allow one or two scans because we try scanning twice and because we allow for the
+ // possibility that, when the test is started, a scan from a different uid was already being
+ // performed (causing the test to 'miss' a scan).
+ assertValueRange("wfl", "", 7, 1, 2); // scan_count
assertValueRange("wfl", "", 11, 0, 0); // scan_count_bg
// Background count test.
- executeBackground(ACTION_WIFI_SCAN);
- Thread.sleep(6_000);
- assertValueRange("wfl", "", 7, 2, 2); // scan_count
- assertValueRange("wfl", "", 11, 1, 1); // scan_count_bg
+ executeBackground(ACTION_WIFI_SCAN, 120_000);
+ assertValueRange("wfl", "", 7, 2, 4); // scan_count
+ assertValueRange("wfl", "", 11, 1, 2); // scan_count_bg
batteryOffScreenOn();
}
@@ -275,8 +284,8 @@
long prevBytes = getLongValue(getUid(), "nt", "", 6);
- executeForeground(ACTION_WIFI_DOWNLOAD);
- long downloadedBytes = getDownloadedBytes();
+ String requestCode = executeForeground(ACTION_WIFI_DOWNLOAD, 60_000);
+ long downloadedBytes = getDownloadedBytes(requestCode);
assertTrue(downloadedBytes > 0);
long min = prevBytes + downloadedBytes + MIN_HTTP_HEADER_BYTES;
long max = prevBytes + downloadedBytes + FUZZ; // Add some fuzzing.
@@ -285,9 +294,8 @@
// Do the background download
long prevBgBytes = getLongValue(getUid(), "nt", "", 20);
- executeBackground(ACTION_WIFI_DOWNLOAD);
- Thread.sleep(4000);
- downloadedBytes = getDownloadedBytes();
+ requestCode = executeBackground(ACTION_WIFI_DOWNLOAD, 60_000);
+ downloadedBytes = getDownloadedBytes(requestCode);
long minBg = prevBgBytes + downloadedBytes + MIN_HTTP_HEADER_BYTES;
long maxBg = prevBgBytes + downloadedBytes + FUZZ;
@@ -310,14 +318,12 @@
batteryOnScreenOff();
installPackage(DEVICE_SIDE_TEST_APK, true);
- executeForeground(ACTION_WIFI_UPLOAD);
- Thread.sleep(2000);
+ executeForeground(ACTION_WIFI_UPLOAD, 60_000);
int min = MIN_HTTP_HEADER_BYTES + (2 * 1024);
int max = min + (6 * 1024); // Add some fuzzing.
assertValueRange("nt", "", 7, min, max); // wifi_bytes_tx
- executeBackground(ACTION_WIFI_UPLOAD);
- Thread.sleep(4000);
+ executeBackground(ACTION_WIFI_UPLOAD, 60_000);
assertValueRange("nt", "", 21, min * 2, max * 2); // wifi_bytes_bg_tx
batteryOffScreenOn();
@@ -371,15 +377,39 @@
}
/**
+ * Runs a (background) service to perform the given action, and waits for
+ * the device to report that the action has finished (via a logcat message) before returning.
+ * @param actionValue one of the constants in BatteryStatsBgVsFgActions indicating the desired
+ * action to perform.
+ * @param maxTimeMs max time to wait (in ms) for action to report that it has completed.
+ * @return A string, representing a random integer, assigned to this particular request for the
+ * device to perform the given action. This value can be used to receive
+ * communications via logcat from the device about this action.
+ */
+ private String executeBackground(String actionValue, int maxTimeMs) throws Exception {
+ String requestCode = executeBackground(actionValue);
+ String searchString = getCompletedActionString(actionValue, requestCode);
+ checkLogcatForText(BG_VS_FG_TAG, searchString, maxTimeMs);
+ return requestCode;
+ }
+
+ /**
* Runs a (background) service to perform the given action.
* @param actionValue one of the constants in BatteryStatsBgVsFgActions indicating the desired
* action to perform.
+ * @return A string, representing a random integer, assigned to this particular request for the
+ * device to perform the given action. This value can be used to receive
+ * communications via logcat from the device about this action.
*/
- private void executeBackground(String actionValue) throws Exception {
+ private String executeBackground(String actionValue) throws Exception {
allowBackgroundServices();
+ String requestCode = Integer.toString(new Random().nextInt());
getDevice().executeShellCommand(String.format(
- "am startservice -n '%s' -e %s %s",
- DEVICE_SIDE_BG_SERVICE_COMPONENT, KEY_ACTION, actionValue));
+ "am startservice -n '%s' -e %s %s -e %s %s",
+ DEVICE_SIDE_BG_SERVICE_COMPONENT,
+ KEY_ACTION, actionValue,
+ KEY_REQUEST_CODE, requestCode));
+ return requestCode;
}
/** Required to successfully start a background service from adb in O. */
@@ -389,26 +419,119 @@
}
/**
+ * Runs an activity (in the foreground) to perform the given action, and waits
+ * for the device to report that the action has finished (via a logcat message) before returning.
+ * @param actionValue one of the constants in BatteryStatsBgVsFgActions indicating the desired
+ * action to perform.
+ * @param maxTimeMs max time to wait (in ms) for action to report that it has completed.
+ * @return A string, representing a random integer, assigned to this particular request for the
+ * device to perform the given action. This value can be used to receive
+ * communications via logcat from the device about this action.
+ */
+ private String executeForeground(String actionValue, int maxTimeMs) throws Exception {
+ String requestCode = executeForeground(actionValue);
+ String searchString = getCompletedActionString(actionValue, requestCode);
+ checkLogcatForText(BG_VS_FG_TAG, searchString, maxTimeMs);
+ return requestCode;
+ }
+
+ /**
* Runs an activity (in the foreground) to perform the given action.
* @param actionValue one of the constants in BatteryStatsBgVsFgActions indicating the desired
* action to perform.
+ * @return A string, representing a random integer, assigned to this particular request for the
+ * device to perform the given action. This value can be used to receive
+ * communications via logcat from the device about this action.
*/
- private void executeForeground(String actionValue) throws Exception {
+ private String executeForeground(String actionValue) throws Exception {
+ String requestCode = Integer.toString(new Random().nextInt());
getDevice().executeShellCommand(String.format(
- "am start -n '%s' -e %s %s",
- DEVICE_SIDE_FG_ACTIVITY_COMPONENT, KEY_ACTION, actionValue));
+ "am start -n '%s' -e %s %s -e %s %s",
+ DEVICE_SIDE_FG_ACTIVITY_COMPONENT,
+ KEY_ACTION, actionValue,
+ KEY_REQUEST_CODE, requestCode));
+ return requestCode;
+ }
+
+ /**
+ * The string that will be printed in the logcat when the action completes. This needs to be
+ * identical to {@link com.android.server.cts.device.batterystats.BatteryStatsBgVsFgActions#tellHostActionFinished}.
+ */
+ private String getCompletedActionString(String actionValue, String requestCode) {
+ return String.format("Completed performing %s for request %s", actionValue, requestCode);
+ }
+
+ /**
+ * Runs logcat and waits (for a maximumum of maxTimeMs) until the desired text is displayed with
+ * the given tag.
+ * Logcat is not cleared, so make sure that text is unique (won't get false hits from old data).
+ * Note that, in practice, the actual max wait time seems to be about 10s longer than maxTimeMs.
+ */
+ private void checkLogcatForText(String logcatTag, String text, int maxTimeMs) {
+ IShellOutputReceiver receiver = new IShellOutputReceiver() {
+ private final StringBuilder mOutputBuffer = new StringBuilder();
+ private final AtomicBoolean mIsCanceled = new AtomicBoolean(false);
+
+ @Override
+ public void addOutput(byte[] data, int offset, int length) {
+ if (!isCancelled()) {
+ synchronized (mOutputBuffer) {
+ String s = new String(data, offset, length, Charsets.UTF_8);
+ mOutputBuffer.append(s);
+ if (checkBufferForText()) {
+ mIsCanceled.set(true);
+ }
+ }
+ }
+ }
+
+ private boolean checkBufferForText() {
+ if (mOutputBuffer.indexOf(text) > -1) {
+ return true;
+ } else {
+ // delete all old data (except the last few chars) since they don't contain text
+ // (presumably large chunks of data will be added at a time, so this is
+ // sufficiently efficient.)
+ int newStart = mOutputBuffer.length() - text.length();
+ if (newStart > 0) {
+ mOutputBuffer.delete(0, newStart);
+ }
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isCancelled() {
+ return mIsCanceled.get();
+ }
+
+ @Override
+ public void flush() {
+ }
+ };
+
+ try {
+ // Wait for at most maxTimeMs for logcat to display the desired text.
+ getDevice().executeShellCommand(String.format("logcat -s %s -e '%s'", logcatTag, text),
+ receiver, maxTimeMs, TimeUnit.MILLISECONDS, 0);
+ } catch (com.android.tradefed.device.DeviceNotAvailableException e) {
+ System.err.println(e);
+ }
}
/**
* Returns the bytes downloaded for the wifi transfer download tests.
+ * @param requestCode the output of executeForeground() or executeBackground() to identify in
+ * the logcat the line associated with the desired download information
*/
- private long getDownloadedBytes() throws Exception {
+ private long getDownloadedBytes(String requestCode) throws Exception {
String log = getDevice().executeShellCommand(
- "logcat -d -s BatteryStatsWifiTransferTests -e '\\d+'");
+ String.format("logcat -d -s BatteryStatsWifiTransferTests -e 'request %s d=\\d+'",
+ requestCode));
String[] lines = log.split("\n");
long size = 0;
for (int i = lines.length - 1; i >= 0; i--) {
- String[] parts = lines[i].split(":");
+ String[] parts = lines[i].split("d=");
String num = parts[parts.length - 1].trim();
if (num.matches("\\d+")) {
size = Integer.parseInt(num);
diff --git a/hostsidetests/incident/src/com/android/server/cts/GraphicsStatsValidationTest.java b/hostsidetests/incident/src/com/android/server/cts/GraphicsStatsValidationTest.java
index ea64a25..2e04e80 100644
--- a/hostsidetests/incident/src/com/android/server/cts/GraphicsStatsValidationTest.java
+++ b/hostsidetests/incident/src/com/android/server/cts/GraphicsStatsValidationTest.java
@@ -88,9 +88,10 @@
assertTrue(slowUiDelta >= 30);
int missedVsyncDelta = summaryAfter.getMissedVsyncCount()
- summaryBefore.getMissedVsyncCount();
- assertEquals(10, missedVsyncDelta);
+ assertTrue(missedVsyncDelta >= 10);
+ assertTrue(missedVsyncDelta <= 11);
- int veryJankyDelta = countFramesAbove(statsAfter, 40) - countFramesAbove(statsBefore, 40);
+ int veryJankyDelta = countFramesAbove(statsAfter, 60) - countFramesAbove(statsBefore, 60);
// The 1st frame could be >40ms, but nothing after that should be
assertTrue(veryJankyDelta <= 1);
}
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
index 577f62c..d43d8aa 100644
--- a/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
@@ -817,29 +817,37 @@
}
protected void launchComponentAndAssertNetworkAccess(int type) throws Exception {
- if (type == TYPE_COMPONENT_ACTIVTIY) {
+ if (type == TYPE_COMPONENT_FOREGROUND_SERVICE) {
+ startForegroundService();
+ assertForegroundServiceNetworkAccess();
+ return;
+ } else if (type == TYPE_COMPONENT_ACTIVTIY) {
turnScreenOn();
- }
- final CountDownLatch latch = new CountDownLatch(1);
- final Intent launchIntent = getIntentForComponent(type);
- final Bundle extras = new Bundle();
- final String[] errors = new String[] {null};
- extras.putBinder(KEY_NETWORK_STATE_OBSERVER, getNewNetworkStateObserver(latch, errors));
- launchIntent.putExtras(extras);
- if (type == TYPE_COMPONENT_ACTIVTIY) {
+ final CountDownLatch latch = new CountDownLatch(1);
+ final Intent launchIntent = getIntentForComponent(type);
+ final Bundle extras = new Bundle();
+ final String[] errors = new String[]{null};
+ extras.putBinder(KEY_NETWORK_STATE_OBSERVER, getNewNetworkStateObserver(latch, errors));
+ launchIntent.putExtras(extras);
mContext.startActivity(launchIntent);
- } else if (type == TYPE_COMPONENT_FOREGROUND_SERVICE) {
- mContext.startService(launchIntent);
- }
- if (latch.await(FOREGROUND_PROC_NETWORK_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
- if (!errors[0].isEmpty()) {
- fail("Network is not available for app2 (" + mUid + "): " + errors[0]);
+ if (latch.await(FOREGROUND_PROC_NETWORK_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+ if (!errors[0].isEmpty()) {
+ fail("Network is not available for app2 (" + mUid + "): " + errors[0]);
+ }
+ } else {
+ fail("Timed out waiting for network availability status from app2 (" + mUid + ")");
}
} else {
- fail("Timed out waiting for network availability status from app2 (" + mUid + ")");
+ throw new IllegalArgumentException("Unknown type: " + type);
}
}
+ private void startForegroundService() throws Exception {
+ final Intent launchIntent = getIntentForComponent(TYPE_COMPONENT_FOREGROUND_SERVICE);
+ mContext.startForegroundService(launchIntent);
+ assertForegroundServiceState();
+ }
+
private Intent getIntentForComponent(int type) {
final Intent intent = new Intent();
if (type == TYPE_COMPONENT_ACTIVTIY) {
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/Android.mk b/hostsidetests/services/activityandwindowmanager/activitymanager/app/Android.mk
index 9abafe9..2633c0a 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/Android.mk
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/app/Android.mk
@@ -19,6 +19,9 @@
# Don't include this package in any target.
LOCAL_MODULE_TAGS := tests
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ android-support-v4 \
+
LOCAL_SRC_FILES := $(call all-java-files-under, src) \
../../../../../apps/CtsVerifier/src/com/android/cts/verifier/vr/MockVrListenerService.java
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/AndroidManifest.xml b/hostsidetests/services/activityandwindowmanager/activitymanager/app/AndroidManifest.xml
index f406735..12ba159 100755
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/AndroidManifest.xml
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/app/AndroidManifest.xml
@@ -30,6 +30,12 @@
android:supportsPictureInPicture="true"
android:exported="true"
/>
+ <activity android:name=".TestActivityWithSameAffinity"
+ android:resizeableActivity="true"
+ android:supportsPictureInPicture="true"
+ android:exported="true"
+ android:taskAffinity="nobody.but.PipActivitySameAffinity"
+ />
<activity android:name=".TranslucentTestActivity"
android:resizeableActivity="true"
android:supportsPictureInPicture="true"
@@ -103,7 +109,20 @@
android:exported="true"
android:taskAffinity="nobody.but.PipActivity2"
/>
-
+ <activity android:name=".PipOnStopActivity"
+ android:resizeableActivity="false"
+ android:supportsPictureInPicture="true"
+ android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
+ android:exported="true"
+ android:taskAffinity="nobody.but.PipOnStopActivity"
+ />
+ <activity android:name=".PipActivityWithSameAffinity"
+ android:resizeableActivity="false"
+ android:supportsPictureInPicture="true"
+ android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
+ android:exported="true"
+ android:taskAffinity="nobody.but.PipActivitySameAffinity"
+ />
<activity android:name=".AlwaysFocusablePipActivity"
android:theme="@style/Theme.Transparent"
android:resizeableActivity="false"
@@ -134,19 +153,12 @@
android:exported="true"
/>
<activity android:name=".LaunchImeWithPipActivity"
- android:resizeableActivity="false"
- android:supportsPictureInPicture="true"
- androidprv:alwaysFocusable="true"
- android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
- android:exported="true"
- android:windowSoftInputMode="stateAlwaysVisible"
- />
- <activity android:name=".PipOnStopActivity"
- android:resizeableActivity="false"
- android:supportsPictureInPicture="true"
- android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
- android:exported="true"
- android:taskAffinity="nobody.but.PipOnStopActivity"
+ android:resizeableActivity="false"
+ android:supportsPictureInPicture="true"
+ androidprv:alwaysFocusable="true"
+ android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
+ android:exported="true"
+ android:windowSoftInputMode="stateAlwaysVisible"
/>
<activity android:name=".FreeformActivity"
android:resizeableActivity="true"
@@ -345,6 +357,14 @@
android:theme="@style/SplashscreenTheme"
android:exported="true" />
+
+ <activity android:name=".SwipeRefreshActivity"
+ android:exported="true" />
+
+ <activity android:name=".NoHistoryActivity"
+ android:noHistory="true"
+ android:exported="true" />
+
<service android:name="com.android.cts.verifier.vr.MockVrListenerService"
android:exported="true"
android:enabled="true"
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/BroadcastReceiverActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/BroadcastReceiverActivity.java
index ed75f73..18f1fa8 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/BroadcastReceiverActivity.java
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/BroadcastReceiverActivity.java
@@ -77,8 +77,8 @@
getWindow().addFlags(FLAG_DISMISS_KEYGUARD);
}
if (extras.getBoolean("dismissKeyguardMethod")) {
- getSystemService(KeyguardManager.class).dismissKeyguard(
- BroadcastReceiverActivity.this, new KeyguardDismissLoggerCallback(), null);
+ getSystemService(KeyguardManager.class).requestDismissKeyguard(
+ BroadcastReceiverActivity.this, new KeyguardDismissLoggerCallback());
}
ActivityLauncher.launchActivityFromExtras(BroadcastReceiverActivity.this, extras);
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LaunchEnterPipActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LaunchEnterPipActivity.java
index 322927e..e2b4786 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LaunchEnterPipActivity.java
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LaunchEnterPipActivity.java
@@ -18,11 +18,12 @@
import android.app.Activity;
import android.graphics.Rect;
+import android.os.Bundle;
public class LaunchEnterPipActivity extends Activity {
@Override
- protected void onResume() {
- super.onResume();
+ protected void onCreate(Bundle bundle) {
+ super.onCreate(bundle);
PipActivity.launchEnterPipActivity(this);
}
}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/NoHistoryActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/NoHistoryActivity.java
new file mode 100644
index 0000000..6a84602
--- /dev/null
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/NoHistoryActivity.java
@@ -0,0 +1,29 @@
+/*
+ * 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.server.cts;
+
+/**
+ * An activity that has the noHistory flag set.
+ */
+public class NoHistoryActivity extends AbstractLifecycleLogActivity {
+ private static final String TAG = NoHistoryActivity.class.getSimpleName();
+
+ @Override
+ protected String getTag() {
+ return TAG;
+ }
+}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/PipActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/PipActivity.java
index 79d3c3f..7e50331 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/PipActivity.java
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/PipActivity.java
@@ -299,11 +299,11 @@
}
/**
- * Launches a new instance of the PipActivity that will automatically enter PiP.
+ * Launches a new instance of the PipActivity in the same task that will automatically enter
+ * PiP.
*/
static void launchEnterPipActivity(Activity caller) {
final Intent intent = new Intent(caller, PipActivity.class);
- intent.setFlags(FLAG_ACTIVITY_CLEAR_TASK | FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(EXTRA_ENTER_PIP, "true");
intent.putExtra(EXTRA_ASSERT_NO_ON_STOP_BEFORE_PIP, "true");
caller.startActivity(intent);
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/PipActivityWithSameAffinity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/PipActivityWithSameAffinity.java
new file mode 100644
index 0000000..e97dc0e
--- /dev/null
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/PipActivityWithSameAffinity.java
@@ -0,0 +1,34 @@
+/*
+ * 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.server.cts;
+
+import android.app.Activity;
+import android.app.PictureInPictureParams;
+
+/**
+ * An activity that can enter PiP with a fixed affinity to match
+ * {@link TestActivityWithSameAffinity}.
+ */
+public class PipActivityWithSameAffinity extends Activity {
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ enterPictureInPictureMode(new PictureInPictureParams.Builder().build());
+ }
+}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/SwipeRefreshActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/SwipeRefreshActivity.java
new file mode 100644
index 0000000..5f36abc
--- /dev/null
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/SwipeRefreshActivity.java
@@ -0,0 +1,40 @@
+/*
+ * 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.server.cts;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.server.cts.SwipeRefreshLayout;
+
+
+/**
+ * An activity containing a SwipeRefreshLayout which prevents activity idle.
+ */
+public class SwipeRefreshActivity extends AbstractLifecycleLogActivity {
+ private static final String TAG = SwipeRefreshActivity.class.getSimpleName();
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(new SwipeRefreshLayout(this));
+ }
+
+ @Override
+ protected String getTag() {
+ return TAG;
+ }
+}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/SwipeRefreshLayout.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/SwipeRefreshLayout.java
new file mode 100644
index 0000000..a061d9e
--- /dev/null
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/SwipeRefreshLayout.java
@@ -0,0 +1,40 @@
+/*
+ * 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.server.cts;
+
+import android.content.Context;
+import android.util.AttributeSet;
+
+/**
+ * An extension of {@link android.support.v4.widget.SwipeRefreshLayout} that calls
+ * {@link #setRefreshing} during construction, preventing activity idle.
+ */
+public class SwipeRefreshLayout extends android.support.v4.widget.SwipeRefreshLayout {
+ public SwipeRefreshLayout(Context context) {
+ super(context);
+ initialize();
+ }
+
+ public SwipeRefreshLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ initialize();
+ }
+
+ private void initialize() {
+ setRefreshing(true);
+ }
+}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TestActivityWithSameAffinity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TestActivityWithSameAffinity.java
new file mode 100644
index 0000000..d835da7
--- /dev/null
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TestActivityWithSameAffinity.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.server.cts;
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.util.Log;
+
+public class TestActivityWithSameAffinity extends TestActivity {
+
+ private static final String TAG = TestActivityWithSameAffinity.class.getSimpleName();
+
+ // Starts the activity (component name) provided by the value at the end of onCreate
+ private static final String EXTRA_START_ACTIVITY = "start_activity";
+ // Finishes the activity at the end of onResume (after EXTRA_START_ACTIVITY is handled)
+ private static final String EXTRA_FINISH_SELF_ON_RESUME = "finish_self_on_resume";
+
+ @Override
+ protected void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ // Launch a new activity if requested
+ String launchActivityComponent = getIntent().getStringExtra(EXTRA_START_ACTIVITY);
+ if (launchActivityComponent != null) {
+ Intent launchIntent = new Intent();
+ launchIntent.setComponent(ComponentName.unflattenFromString(launchActivityComponent));
+ startActivity(launchIntent);
+ }
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ // Finish self if requested
+ if (getIntent().hasExtra(EXTRA_FINISH_SELF_ON_RESUME)) {
+ finish();
+ }
+ }
+
+ @Override
+ protected String getTag() {
+ return TAG;
+ }
+}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TurnScreenOnDismissKeyguardActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TurnScreenOnDismissKeyguardActivity.java
index 9d262a7..62948b6 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TurnScreenOnDismissKeyguardActivity.java
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TurnScreenOnDismissKeyguardActivity.java
@@ -27,7 +27,7 @@
super.onCreate(savedInstanceState);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
| WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
- getSystemService(KeyguardManager.class).dismissKeyguard(this,
- new KeyguardDismissLoggerCallback(), null);
+ getSystemService(KeyguardManager.class).requestDismissKeyguard(this,
+ new KeyguardDismissLoggerCallback());
}
}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/VirtualDisplayActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/VirtualDisplayActivity.java
index 8374f7c..c04ef90 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/VirtualDisplayActivity.java
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/VirtualDisplayActivity.java
@@ -102,6 +102,9 @@
case "destroy_display":
destroyVirtualDisplays();
break;
+ case "resize_display":
+ resizeDisplay();
+ break;
}
}
@@ -195,4 +198,12 @@
@Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
}
+
+ /** Resize virtual display to half of the surface frame size. */
+ private void resizeDisplay() {
+ final VirtualDisplayEntry vd = (VirtualDisplayEntry) mVirtualDisplays.values().toArray()[0];
+ final SurfaceHolder surfaceHolder = vd.surfaceView.getHolder();
+ vd.display.resize(surfaceHolder.getSurfaceFrame().width() / 2,
+ surfaceHolder.getSurfaceFrame().height() / 2, vd.density);
+ }
}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerActivityVisibilityTests.java b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerActivityVisibilityTests.java
index d0db632..c647be4 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerActivityVisibilityTests.java
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerActivityVisibilityTests.java
@@ -39,6 +39,9 @@
private static final String DOCKED_ACTIVITY_NAME = "DockedActivity";
private static final String TURN_SCREEN_ON_ACTIVITY_NAME = "TurnScreenOnActivity";
private static final String MOVE_TASK_TO_BACK_ACTIVITY_NAME = "MoveTaskToBackActivity";
+ private static final String SWIPE_REFRESH_ACTIVITY = "SwipeRefreshActivity";
+ private static final String NOHISTORY_ACTIVITY = "NoHistoryActivity";
+
public void testTranslucentActivityOnTopOfPinnedStack() throws Exception {
if (!supportsPip()) {
@@ -250,6 +253,24 @@
// Ensure launching activity was brought forward.
mAmWmState.assertFocusedActivity("Launching Activity must be focused",
LAUNCHING_ACTIVITY);
+ }
+ /**
+ * Asserts that a nohistory activity is stopped and removed immediately after a resumed activity
+ * above becomes visible and does not idle.
+ */
+ public void testNoHistoryActivityFinishedResumedActivityNotIdle() throws Exception {
+ // Start with home on top
+ launchHomeActivity();
+
+ // Launch no history activity
+ launchActivity(NOHISTORY_ACTIVITY);
+
+ // Launch an activity with a swipe refresh layout configured to prevent idle.
+ launchActivity(SWIPE_REFRESH_ACTIVITY);
+
+ pressBackButton();
+ mAmWmState.waitForHomeActivityVisible(mDevice);
+ mAmWmState.assertHomeActivityVisible(true);
}
}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerAssistantStackTests.java b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerAssistantStackTests.java
index af42e30..21146a4 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerAssistantStackTests.java
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerAssistantStackTests.java
@@ -17,7 +17,6 @@
package android.server.cts;
import static android.server.cts.ActivityManagerState.STATE_RESUMED;
-import static android.server.cts.ActivityManagerState.STATE_STOPPED;
/**
* Build: mmma -j32 cts/hostsidetests/services
@@ -186,6 +185,34 @@
disableAssistant();
}
+ public void testLaunchIntoSameTask() throws Exception {
+ enableAssistant();
+
+ // Launch the assistant
+ launchActivity(LAUNCH_ASSISTANT_ACTIVITY_FROM_SESSION);
+ assertAssistantStackExists();
+ mAmWmState.assertVisibility(ASSISTANT_ACTIVITY, true);
+ mAmWmState.assertFocusedStack("Expected assistant stack focused", ASSISTANT_STACK_ID);
+ assertEquals(1, mAmWmState.getAmState().getStackById(ASSISTANT_STACK_ID).getTasks().size());
+ final int taskId = mAmWmState.getAmState().getTaskByActivityName(ASSISTANT_ACTIVITY)
+ .mTaskId;
+
+ // Launch a new fullscreen activity
+ launchActivity(TEST_ACTIVITY);
+ mAmWmState.assertVisibility(ASSISTANT_ACTIVITY, false);
+
+ // Launch the assistant again and ensure that it goes into the same task
+ launchActivity(LAUNCH_ASSISTANT_ACTIVITY_FROM_SESSION);
+ assertAssistantStackExists();
+ mAmWmState.assertVisibility(ASSISTANT_ACTIVITY, true);
+ mAmWmState.assertFocusedStack("Expected assistant stack focused", ASSISTANT_STACK_ID);
+ assertEquals(1, mAmWmState.getAmState().getStackById(ASSISTANT_STACK_ID).getTasks().size());
+ assertEquals(taskId,
+ mAmWmState.getAmState().getTaskByActivityName(ASSISTANT_ACTIVITY).mTaskId);
+
+ disableAssistant();
+ }
+
/**
* Asserts that the assistant stack exists.
*/
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerDisplayTests.java b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerDisplayTests.java
index 9ad9d29..dc37d9b 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerDisplayTests.java
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerDisplayTests.java
@@ -410,7 +410,7 @@
* on the primary display. It should land on the primary display and dismiss docked stack.
*/
public void testLaunchNonResizeableActivityWithSplitScreen() throws Exception {
- if (!supportsMultiDisplay()) { return; }
+ if (!supportsMultiDisplay() || !supportsSplitScreenMultiWindow()) { return; }
// Start launching activity.
launchActivityInDockStack(LAUNCHING_ACTIVITY);
@@ -540,10 +540,9 @@
if (!supportsMultiDisplay()) { return; }
// Start launching activity.
- launchActivityInDockStack(LAUNCHING_ACTIVITY);
+ launchActivity(LAUNCHING_ACTIVITY);
// Create new virtual display.
- final DisplayState newDisplay =
- new VirtualDisplayBuilder(this).setLaunchInSplitScreen(true).build();
+ final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
// Launch activity on secondary display from the app on primary display.
getLaunchActivityBuilder().setTargetActivityName(TEST_ACTIVITY_NAME)
@@ -569,28 +568,21 @@
if (!supportsMultiDisplay()) { return; }
// Start launching activity.
- launchActivityInDockStack(LAUNCHING_ACTIVITY);
- mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
-
+ launchActivity(LAUNCHING_ACTIVITY);
// Create new virtual display.
- final DisplayState newDisplay =
- new VirtualDisplayBuilder(this).setLaunchInSplitScreen(true).build();
+ final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
- mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
// Launch activity on new secondary display.
launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mDisplayId);
mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, true /* visible */);
mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
- mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
// Launch activity on primary display and check if it doesn't affect activity on secondary
// display.
getLaunchActivityBuilder().setTargetActivityName(RESIZEABLE_ACTIVITY_NAME).execute();
- mAmWmState.waitForValidState(mDevice, RESIZEABLE_ACTIVITY_NAME,
- FULLSCREEN_WORKSPACE_STACK_ID);
+ mAmWmState.waitForValidState(mDevice, RESIZEABLE_ACTIVITY_NAME);
mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, true /* visible */);
- mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
mAmWmState.assertVisibility(RESIZEABLE_ACTIVITY_NAME, true /* visible */);
}
@@ -606,46 +598,54 @@
mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
mAmWmState.assertFocusedActivity("Virtual display activity must be focused",
VIRTUAL_DISPLAY_ACTIVITY);
- mAmWmState.assertFocusedStack("Focus must remain on primary display",
- FULLSCREEN_WORKSPACE_STACK_ID);
+ final int defaultDisplayStackId = mAmWmState.getAmState().getFocusedStackId();
+ ActivityManagerState.ActivityStack focusedStack
+ = mAmWmState.getAmState().getStackById(defaultDisplayStackId);
+ assertEquals("Focus must remain on primary display", DEFAULT_DISPLAY_ID,
+ focusedStack.mDisplayId);
// Launch activity on new secondary display.
launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mDisplayId);
mAmWmState.assertFocusedActivity("Focus must be on secondary display", TEST_ACTIVITY_NAME);
- mAmWmState.assertNotFocusedStack("Focused stack must be on secondary display",
- FULLSCREEN_WORKSPACE_STACK_ID);
+ int focusedStackId = mAmWmState.getAmState().getFocusedStackId();
+ focusedStack = mAmWmState.getAmState().getStackById(focusedStackId);
+ assertEquals("Focused stack must be on secondary display",
+ newDisplay.mDisplayId, focusedStack.mDisplayId);
// Move activity from secondary display to primary.
- moveActivityToStack(TEST_ACTIVITY_NAME, FULLSCREEN_WORKSPACE_STACK_ID);
- mAmWmState.waitForFocusedStack(mDevice, FULLSCREEN_WORKSPACE_STACK_ID);
+ moveActivityToStack(TEST_ACTIVITY_NAME, defaultDisplayStackId);
+ mAmWmState.waitForFocusedStack(mDevice, defaultDisplayStackId);
mAmWmState.assertFocusedActivity("Focus must be on moved activity", TEST_ACTIVITY_NAME);
- mAmWmState.assertFocusedStack("Focus must return to primary display",
- FULLSCREEN_WORKSPACE_STACK_ID);
+ focusedStackId = mAmWmState.getAmState().getFocusedStackId();
+ focusedStack = mAmWmState.getAmState().getStackById(focusedStackId);
+ assertEquals("Focus must return to primary display", DEFAULT_DISPLAY_ID,
+ focusedStack.mDisplayId);
}
/**
* Tests launching activities on secondary display and then removing it to see if stack focus
* is moved correctly.
- * This version launches virtual display creator to fullscreen stack.
+ * This version launches virtual display creator to fullscreen stack in split-screen.
*/
@Presubmit
public void testStackFocusSwitchOnDisplayRemoved() throws Exception {
- if (!supportsMultiDisplay()) { return; }
+ if (!supportsMultiDisplay() || !supportsSplitScreenMultiWindow()) { return; }
// Start launching activity into docked stack.
launchActivityInDockStack(LAUNCHING_ACTIVITY);
mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
- tryCreatingAndRemovingDisplayWithActivity();
+ tryCreatingAndRemovingDisplayWithActivity(true /* splitScreen */,
+ FULLSCREEN_WORKSPACE_STACK_ID);
}
/**
* Tests launching activities on secondary display and then removing it to see if stack focus
* is moved correctly.
- * This version launches virtual display creator to docked stack.
+ * This version launches virtual display creator to docked stack in split-screen.
*/
public void testStackFocusSwitchOnDisplayRemoved2() throws Exception {
- if (!supportsMultiDisplay()) { return; }
+ if (!supportsMultiDisplay() || !supportsSplitScreenMultiWindow()) { return; }
// Setup split-screen.
launchActivityInDockStack(RESIZEABLE_ACTIVITY_NAME);
@@ -654,21 +654,45 @@
launchActivityInStack(LAUNCHING_ACTIVITY, FULLSCREEN_WORKSPACE_STACK_ID);
mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
- tryCreatingAndRemovingDisplayWithActivity();
+ tryCreatingAndRemovingDisplayWithActivity(true /* splitScreen */,
+ FULLSCREEN_WORKSPACE_STACK_ID);
}
/**
- * Create a virtual display to side from LaunchingActivity, launch a test activity there,
- * destroy the display and check if test activity is moved to fullscreen stack.
+ * Tests launching activities on secondary display and then removing it to see if stack focus
+ * is moved correctly.
+ * This version works without split-screen.
*/
- private void tryCreatingAndRemovingDisplayWithActivity() throws Exception {
+ public void testStackFocusSwitchOnDisplayRemoved3() throws Exception {
+ if (!supportsMultiDisplay()) { return; }
+
+ // Start an activity on default display to determine default stack.
+ launchActivity(BROADCAST_RECEIVER_ACTIVITY);
+ final int focusedStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY_ID);
+ // Finish probing activity.
+ executeShellCommand(FINISH_ACTIVITY_BROADCAST);
+
+
+ tryCreatingAndRemovingDisplayWithActivity(false /* splitScreen */, focusedStackId);
+ }
+
+ /**
+ * Create a virtual display, launch a test activity there, destroy the display and check if test
+ * activity is moved to a stack on the default display.
+ */
+ private void tryCreatingAndRemovingDisplayWithActivity(boolean splitScreen, int defaultStackId)
+ throws Exception {
// Create new virtual display.
- final DisplayState newDisplay = new VirtualDisplayBuilder(this)
- .setLaunchInSplitScreen(true)
- .setPublicDisplay(true)
- .build();
+ final VirtualDisplayBuilder builder = new VirtualDisplayBuilder(this)
+ .setPublicDisplay(true);
+ if (splitScreen) {
+ builder.setLaunchInSplitScreen(true);
+ }
+ final DisplayState newDisplay = builder.build();
mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
- mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
+ if (splitScreen) {
+ mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
+ }
// Launch activity on new secondary display.
launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mDisplayId);
@@ -679,15 +703,17 @@
// Destroy virtual display.
destroyVirtualDisplays();
- mAmWmState.waitForValidState(mDevice, TEST_ACTIVITY_NAME, FULLSCREEN_WORKSPACE_STACK_ID);
+ mAmWmState.waitForValidState(mDevice, TEST_ACTIVITY_NAME, defaultStackId);
mAmWmState.assertSanity();
mAmWmState.assertValidBounds(true /* compareTaskAndStackBounds */);
// Check if the focus is switched back to primary display.
mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, true /* visible */);
- mAmWmState.assertFocusedStack("Fullscreen stack must be focused after display removed",
- FULLSCREEN_WORKSPACE_STACK_ID);
- mAmWmState.assertFocusedActivity("Focus must be switched back to primary display",
+ mAmWmState.assertFocusedStack(
+ "Default stack on primary display must be focused after display removed",
+ defaultStackId);
+ mAmWmState.assertFocusedActivity(
+ "Focus must be switched back to activity on primary display",
TEST_ACTIVITY_NAME);
}
@@ -698,15 +724,10 @@
public void testStackFocusSwitchOnStackEmptied() throws Exception {
if (!supportsMultiDisplay()) { return; }
- // Start launching activity.
- launchActivityInDockStack(LAUNCHING_ACTIVITY);
- mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
-
// Create new virtual display.
- final DisplayState newDisplay =
- new VirtualDisplayBuilder(this).setLaunchInSplitScreen(true).build();
+ final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
- mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
+ final int focusedStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY_ID);
// Launch activity on new secondary display.
launchActivityOnDisplay(BROADCAST_RECEIVER_ACTIVITY, newDisplay.mDisplayId);
@@ -721,9 +742,8 @@
// Unlock and check if the focus is switched back to primary display.
wakeUpAndUnlockDevice();
- mAmWmState.waitForFocusedStack(mDevice, FULLSCREEN_WORKSPACE_STACK_ID);
- mAmWmState.waitForValidState(mDevice, LAUNCHING_ACTIVITY);
- mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
+ mAmWmState.waitForFocusedStack(mDevice, focusedStackId);
+ mAmWmState.waitForValidState(mDevice, VIRTUAL_DISPLAY_ACTIVITY);
mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
mAmWmState.assertFocusedActivity("Focus must be switched back to primary display",
VIRTUAL_DISPLAY_ACTIVITY);
@@ -767,16 +787,20 @@
mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
mAmWmState.assertFocusedActivity("Virtual display activity must be focused",
VIRTUAL_DISPLAY_ACTIVITY);
- mAmWmState.assertFocusedStack("Focus must remain on primary display",
- FULLSCREEN_WORKSPACE_STACK_ID);
+ final int defaultDisplayFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
+ ActivityManagerState.ActivityStack focusedStack
+ = mAmWmState.getAmState().getStackById(defaultDisplayFocusedStackId);
+ assertEquals("Focus must remain on primary display", DEFAULT_DISPLAY_ID,
+ focusedStack.mDisplayId);
// Launch activity on new secondary display.
launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mDisplayId);
mAmWmState.assertFocusedActivity("Focus must be on secondary display",
TEST_ACTIVITY_NAME);
final int externalFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
- assertTrue("Focused stack must be on secondary display",
- FULLSCREEN_WORKSPACE_STACK_ID != externalFocusedStackId);
+ focusedStack = mAmWmState.getAmState().getStackById(externalFocusedStackId);
+ assertEquals("Focused stack must be on secondary display", newDisplay.mDisplayId,
+ focusedStack.mDisplayId);
// Launch other activity with different uid and check it is launched on dynamic stack on
// secondary display.
@@ -801,8 +825,11 @@
mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
mAmWmState.assertFocusedActivity("Virtual display activity must be focused",
VIRTUAL_DISPLAY_ACTIVITY);
- mAmWmState.assertFocusedStack("Focus must remain on primary display",
- FULLSCREEN_WORKSPACE_STACK_ID);
+ final int defaultDisplayFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
+ ActivityManagerState.ActivityStack focusedStack
+ = mAmWmState.getAmState().getStackById(defaultDisplayFocusedStackId);
+ assertEquals("Focus must remain on primary display", DEFAULT_DISPLAY_ID,
+ focusedStack.mDisplayId);
// Launch activity with different uid on secondary display.
final String startCmd = "am start -n " + SECOND_PACKAGE_NAME + "/." + SECOND_ACTIVITY_NAME;
@@ -814,8 +841,9 @@
mAmWmState.assertFocusedActivity("Focus must be on newly launched app",
SECOND_PACKAGE_NAME, SECOND_ACTIVITY_NAME);
final int externalFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
- assertTrue("Focused stack must be on secondary display",
- FULLSCREEN_WORKSPACE_STACK_ID != externalFocusedStackId);
+ focusedStack = mAmWmState.getAmState().getStackById(externalFocusedStackId);
+ assertEquals("Focused stack must be on secondary display", newDisplay.mDisplayId,
+ focusedStack.mDisplayId);
// Launch another activity with third different uid from app on secondary display and check
// it is launched on secondary display.
@@ -877,8 +905,11 @@
mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
mAmWmState.assertFocusedActivity("Virtual display activity must be focused",
VIRTUAL_DISPLAY_ACTIVITY);
- mAmWmState.assertFocusedStack("Focus must remain on primary display",
- FULLSCREEN_WORKSPACE_STACK_ID);
+ final int defaultDisplayFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
+ ActivityManagerState.ActivityStack focusedStack
+ = mAmWmState.getAmState().getStackById(defaultDisplayFocusedStackId);
+ assertEquals("Focus must remain on primary display", DEFAULT_DISPLAY_ID,
+ focusedStack.mDisplayId);
// Launch other activity with different uid on secondary display.
final String startCmd = "am start -n " + SECOND_PACKAGE_NAME + "/." + SECOND_ACTIVITY_NAME;
@@ -890,8 +921,9 @@
mAmWmState.assertFocusedActivity("Focus must be on newly launched app",
SECOND_PACKAGE_NAME, SECOND_ACTIVITY_NAME);
final int externalFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
- assertTrue("Focused stack must be on secondary display",
- FULLSCREEN_WORKSPACE_STACK_ID != externalFocusedStackId);
+ focusedStack = mAmWmState.getAmState().getStackById(externalFocusedStackId);
+ assertEquals("Focused stack must be on secondary display", newDisplay.mDisplayId,
+ focusedStack.mDisplayId);
// Check that owner uid can launch its own activity on secondary display.
final String broadcastAction = componentName + ".LAUNCH_BROADCAST_ACTION";
@@ -917,16 +949,20 @@
mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
mAmWmState.assertFocusedActivity("Virtual display activity must be focused",
VIRTUAL_DISPLAY_ACTIVITY);
- mAmWmState.assertFocusedStack("Focus must remain on primary display",
- FULLSCREEN_WORKSPACE_STACK_ID);
+ final int defaultDisplayFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
+ ActivityManagerState.ActivityStack focusedStack
+ = mAmWmState.getAmState().getStackById(defaultDisplayFocusedStackId);
+ assertEquals("Focus must remain on primary display", DEFAULT_DISPLAY_ID,
+ focusedStack.mDisplayId);
// Launch activity on new secondary display.
launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mDisplayId);
mAmWmState.assertFocusedActivity("Focus must be on secondary display",
TEST_ACTIVITY_NAME);
final int externalFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
- assertTrue("Focused stack must be on secondary display",
- FULLSCREEN_WORKSPACE_STACK_ID != externalFocusedStackId);
+ focusedStack = mAmWmState.getAmState().getStackById(externalFocusedStackId);
+ assertEquals("Focused stack must be on secondary display", newDisplay.mDisplayId,
+ focusedStack.mDisplayId);
final String logSeparator = clearLogcat();
@@ -1052,15 +1088,9 @@
public void testDisplayResize() throws Exception {
if (!supportsMultiDisplay()) { return; }
- // Start launching activity.
- launchActivityInDockStack(LAUNCHING_ACTIVITY);
-
- mAmWmState.waitForValidState(mDevice, LAUNCHING_ACTIVITY, DOCKED_STACK_ID);
// Create new virtual display.
- final DisplayState newDisplay =
- new VirtualDisplayBuilder(this).setLaunchInSplitScreen(true).build();
+ final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
- mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
// Launch a resizeable activity on new secondary display.
final String initialLogSeparator = clearLogcat();
@@ -1072,14 +1102,10 @@
// Grab reported sizes and compute new with slight size change.
final ReportedSizes initialSize = getLastReportedSizesForActivity(RESIZEABLE_ACTIVITY_NAME,
initialLogSeparator);
- final Rectangle initialBounds
- = mAmWmState.getAmState().getStackById(DOCKED_STACK_ID).getBounds();
- final Rectangle newBounds = new Rectangle(initialBounds.x, initialBounds.y,
- initialBounds.width + SIZE_VALUE_SHIFT, initialBounds.height + SIZE_VALUE_SHIFT);
// Resize the docked stack, so that activity with virtual display will also be resized.
final String logSeparator = clearLogcat();
- resizeDockedStack(newBounds.width, newBounds.height, newBounds.width, newBounds.height);
+ executeShellCommand(getResizeVirtualDisplayCommand());
mAmWmState.waitForWithAmState(mDevice, amState -> {
try {
@@ -1091,16 +1117,8 @@
}
}, "Wait for the configuration change to happen and for activity to be resumed.");
- mAmWmState.computeState(mDevice, new String[] {RESIZEABLE_ACTIVITY_NAME, LAUNCHING_ACTIVITY,
+ mAmWmState.computeState(mDevice, new String[] {RESIZEABLE_ACTIVITY_NAME,
VIRTUAL_DISPLAY_ACTIVITY}, false /* compareTaskAndStackBounds */);
- mAmWmState.assertDockedTaskBounds(newBounds.width, newBounds.height,
- LAUNCHING_ACTIVITY);
- mAmWmState.assertContainsStack("Must contain docked stack", DOCKED_STACK_ID);
- mAmWmState.assertContainsStack("Must contain fullscreen stack",
- FULLSCREEN_WORKSPACE_STACK_ID);
- assertEquals(new Rectangle(0, 0, newBounds.width, newBounds.height),
- mAmWmState.getAmState().getStackById(DOCKED_STACK_ID).getBounds());
- mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true);
mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true);
mAmWmState.assertVisibility(RESIZEABLE_ACTIVITY_NAME, true);
@@ -1112,12 +1130,8 @@
logSeparator);
assertTrue(updatedSize.widthDp <= initialSize.widthDp);
assertTrue(updatedSize.heightDp <= initialSize.heightDp);
- assertTrue(updatedSize.displayWidth <= initialSize.displayWidth);
- assertTrue(updatedSize.displayHeight <= initialSize.displayHeight);
- final boolean widthUpdated = updatedSize.metricsWidth < initialSize.metricsWidth;
- final boolean heightUpdated = updatedSize.metricsHeight < initialSize.metricsHeight;
- assertTrue("Either width or height must be updated after split-screen resize",
- widthUpdated ^ heightUpdated);
+ assertTrue(updatedSize.displayWidth == initialSize.displayWidth / 2);
+ assertTrue(updatedSize.displayHeight == initialSize.displayHeight / 2);
}
/** Read the number of configuration changes sent to activity from logs. */
@@ -1174,7 +1188,7 @@
// Check that task has moved from primary display to secondary.
final int taskNumFinal = mAmWmState.getAmState().getStackById(defaultDisplayStackId)
.getTasks().size();
- mAmWmState.assertEquals("Task number in fullscreen stack must be decremented.", taskNum - 1,
+ mAmWmState.assertEquals("Task number in default stack must be decremented.", taskNum - 1,
taskNumFinal);
final int taskNumFinalOnSecondary = mAmWmState.getAmState().getStackById(frontStackId)
.getTasks().size();
@@ -1246,12 +1260,15 @@
null /* stackIds */, false /* compareTaskAndStackBounds */, componentName);
// Check that second activity gets launched on the default display
- final ActivityManagerState.ActivityStack fullscreenStack =
- mAmWmState.getAmState().getStackById(FULLSCREEN_WORKSPACE_STACK_ID);
+ final int defaultDisplayFrontStackId = mAmWmState.getAmState().getFrontStackId(
+ DEFAULT_DISPLAY_ID);
+ final ActivityManagerState.ActivityStack defaultDisplayFrontStack =
+ mAmWmState.getAmState().getStackById(defaultDisplayFrontStackId);
assertEquals("Activity launched on default display must be resumed",
- getActivityComponentName(ALT_LAUNCHING_ACTIVITY), fullscreenStack.mResumedActivity);
+ getActivityComponentName(ALT_LAUNCHING_ACTIVITY),
+ defaultDisplayFrontStack.mResumedActivity);
mAmWmState.assertFocusedStack("Focus must be on primary display",
- FULLSCREEN_WORKSPACE_STACK_ID);
+ defaultDisplayFrontStackId);
executeShellCommand("am start -n " + getActivityComponentName(LAUNCHING_ACTIVITY));
mAmWmState.waitForFocusedStack(mDevice, frontStackId);
@@ -1293,13 +1310,14 @@
mAmWmState.waitForValidState(mDevice, new String[] {TEST_ACTIVITY_NAME},
null /* stackIds */, false /* compareTaskAndStackBounds */, componentName);
- // Check that the second activity is launched onto the fullscreen stack
- final ActivityManagerState.ActivityStack fullscreenStack =
- mAmWmState.getAmState().getStackById(FULLSCREEN_WORKSPACE_STACK_ID);
+ // Check that the second activity is launched on the default display
+ final int focusedStackId = mAmWmState.getAmState().getFocusedStackId();
+ final ActivityManagerState.ActivityStack focusedStack
+ = mAmWmState.getAmState().getStackById(focusedStackId);
assertEquals("Activity launched on default display must be resumed",
- getActivityComponentName(TEST_ACTIVITY_NAME), fullscreenStack.mResumedActivity);
- mAmWmState.assertFocusedStack("Focus must be on primary display",
- FULLSCREEN_WORKSPACE_STACK_ID);
+ getActivityComponentName(TEST_ACTIVITY_NAME), focusedStack.mResumedActivity);
+ assertEquals("Focus must be on primary display", DEFAULT_DISPLAY_ID,
+ focusedStack.mDisplayId);
executeShellCommand("am broadcast -a trigger_broadcast --ez launch_activity true "
+ "--ez new_task true --es target_activity " + LAUNCHING_ACTIVITY);
@@ -1793,6 +1811,11 @@
" --es command destroy_display";
}
+ private static String getResizeVirtualDisplayCommand() {
+ return getAmStartCmd(VIRTUAL_DISPLAY_ACTIVITY) + " -f 0x20000000" +
+ " --es command resize_display";
+ }
+
/** Checks if the device supports multi-display. */
private boolean supportsMultiDisplay() throws Exception {
return hasDeviceFeature("android.software.activities_on_secondary_displays");
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerDockedStackTests.java b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerDockedStackTests.java
index d5ba74e..c7061d1 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerDockedStackTests.java
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerDockedStackTests.java
@@ -94,6 +94,33 @@
mAmWmState.assertContainsStack("Must contain docked stack.", DOCKED_STACK_ID);
}
+ public void testLaunchToSideMultiWindowCallbacks() throws Exception {
+ if (!supportsSplitScreenMultiWindow()) {
+ CLog.logAndDisplay(LogLevel.INFO, "Skipping test: no split multi-window support");
+ return;
+ }
+
+ // Launch two activities, one docked, one adjacent
+ launchActivityInDockStack(LAUNCHING_ACTIVITY);
+ mAmWmState.computeState(mDevice, new String[] {LAUNCHING_ACTIVITY});
+ getLaunchActivityBuilder().setToSide(true).execute();
+ mAmWmState.computeState(mDevice, new String[] {TEST_ACTIVITY_NAME});
+ mAmWmState.assertContainsStack(
+ "Must contain fullscreen stack.", FULLSCREEN_WORKSPACE_STACK_ID);
+ mAmWmState.assertContainsStack("Must contain docked stack.", DOCKED_STACK_ID);
+
+ // Remove the docked stack, and ensure that
+ final String logSeparator = clearLogcat();
+ removeStacks(DOCKED_STACK_ID);
+ final ActivityLifecycleCounts lifecycleCounts = new ActivityLifecycleCounts(
+ TEST_ACTIVITY_NAME, logSeparator);
+ if (lifecycleCounts.mMultiWindowModeChangedCount != 1) {
+ fail(TEST_ACTIVITY_NAME + " has received "
+ + lifecycleCounts.mMultiWindowModeChangedCount
+ + " onMultiWindowModeChanged() calls, expecting 1");
+ }
+ }
+
public void testLaunchToSideAndBringToFront() throws Exception {
if (!supportsSplitScreenMultiWindow()) {
CLog.logAndDisplay(LogLevel.INFO, "Skipping test: no split multi-window support");
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerPinnedStackTests.java b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerPinnedStackTests.java
index 65f507e..dc94ba4 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerPinnedStackTests.java
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerPinnedStackTests.java
@@ -35,11 +35,13 @@
*/
public class ActivityManagerPinnedStackTests extends ActivityManagerTestBase {
private static final String TEST_ACTIVITY = "TestActivity";
+ private static final String TEST_ACTIVITY_WITH_SAME_AFFINITY = "TestActivityWithSameAffinity";
private static final String TRANSLUCENT_TEST_ACTIVITY = "TranslucentTestActivity";
private static final String NON_RESIZEABLE_ACTIVITY = "NonResizeableActivity";
private static final String RESUME_WHILE_PAUSING_ACTIVITY = "ResumeWhilePausingActivity";
private static final String PIP_ACTIVITY = "PipActivity";
private static final String PIP_ACTIVITY2 = "PipActivity2";
+ private static final String PIP_ACTIVITY_WITH_SAME_AFFINITY = "PipActivityWithSameAffinity";
private static final String ALWAYS_FOCUSABLE_PIP_ACTIVITY = "AlwaysFocusablePipActivity";
private static final String LAUNCH_INTO_PINNED_STACK_PIP_ACTIVITY =
"LaunchIntoPinnedStackPipActivity";
@@ -805,10 +807,10 @@
+ " onStop() calls, expecting 1");
} else if (lifecycleCounts.mPictureInPictureModeChangedCount != 1) {
fail(PIP_ACTIVITY + " has received " + lifecycleCounts.mPictureInPictureModeChangedCount
- + " onMultiWindowModeChanged() calls, expecting 1");
+ + " onPictureInPictureModeChanged() calls, expecting 1");
} else if (lifecycleCounts.mMultiWindowModeChangedCount != 1) {
fail(PIP_ACTIVITY + " has received " + lifecycleCounts.mMultiWindowModeChangedCount
- + " onPictureInPictureModeChanged() calls, expecting 1");
+ + " onMultiWindowModeChanged() calls, expecting 1");
} else {
int lastStopLine = lifecycleCounts.mLastStopLineIndex;
int lastPipLine = lifecycleCounts.mLastPictureInPictureModeChangedLineIndex;
@@ -914,6 +916,111 @@
assertTrue(lifecycleCounts.mPauseCount == 0);
}
+ public void testPinnedStackWithDockedStack() throws Exception {
+ if (!supportsPip() || !supportsSplitScreenMultiWindow()) return;
+
+ launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true");
+ launchActivityInDockStack(LAUNCHING_ACTIVITY);
+ launchActivityToSide(true, false, TEST_ACTIVITY);
+ mAmWmState.assertVisibility(PIP_ACTIVITY, true);
+ mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true);
+ mAmWmState.assertVisibility(TEST_ACTIVITY, true);
+
+ // Launch the activities again to take focus and make sure nothing is hidden
+ launchActivityInDockStack(LAUNCHING_ACTIVITY);
+ mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true);
+ mAmWmState.assertVisibility(TEST_ACTIVITY, true);
+
+ launchActivityToSide(true, false, TEST_ACTIVITY);
+ mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true);
+ mAmWmState.assertVisibility(TEST_ACTIVITY, true);
+
+ // Go to recents to make sure that fullscreen stack is invisible
+ // Some devices do not support recents or implement it differently (instead of using a
+ // separate stack id or as an activity), for those cases the visibility asserts will be
+ // ignored
+ pressAppSwitchButton();
+ if (mAmWmState.waitForRecentsActivityVisible(mDevice)) {
+ mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true);
+ mAmWmState.assertVisibility(TEST_ACTIVITY, false);
+ }
+ }
+
+ public void testLaunchTaskByComponentMatchMultipleTasks() throws Exception {
+ if (!supportsPip()) return;
+
+ // Launch a fullscreen activity which will launch a PiP activity in a new task with the same
+ // affinity
+ launchActivity(TEST_ACTIVITY_WITH_SAME_AFFINITY);
+ launchActivityInStack(PIP_ACTIVITY_WITH_SAME_AFFINITY, PINNED_STACK_ID);
+ assertPinnedStackExists();
+
+ // Launch the root activity again...
+ int rootActivityTaskId = mAmWmState.getAmState().getTaskByActivityName(
+ TEST_ACTIVITY_WITH_SAME_AFFINITY).mTaskId;
+ launchHomeActivity();
+ launchActivity(TEST_ACTIVITY_WITH_SAME_AFFINITY);
+
+ // ...and ensure that the root activity task is found and reused, and that the pinned stack
+ // is unaffected
+ assertPinnedStackExists();
+ mAmWmState.assertFocusedActivity("Expected root activity focused",
+ TEST_ACTIVITY_WITH_SAME_AFFINITY);
+ assertTrue(rootActivityTaskId == mAmWmState.getAmState().getTaskByActivityName(
+ TEST_ACTIVITY_WITH_SAME_AFFINITY).mTaskId);
+ }
+
+ public void testLaunchTaskByAffinityMatchMultipleTasks() throws Exception {
+ if (!supportsPip()) return;
+
+ // Launch a fullscreen activity which will launch a PiP activity in a new task with the same
+ // affinity, and also launch another activity in the same task, while finishing itself. As
+ // a result, the task will not have a component matching the same activity as what it was
+ // started with
+ launchActivity(TEST_ACTIVITY_WITH_SAME_AFFINITY,
+ EXTRA_START_ACTIVITY, getActivityComponentName(TEST_ACTIVITY),
+ EXTRA_FINISH_SELF_ON_RESUME, "true");
+ mAmWmState.waitForValidState(mDevice, TEST_ACTIVITY, FULLSCREEN_WORKSPACE_STACK_ID);
+ launchActivityInStack(PIP_ACTIVITY_WITH_SAME_AFFINITY, PINNED_STACK_ID);
+ mAmWmState.waitForValidState(mDevice, PIP_ACTIVITY_WITH_SAME_AFFINITY, PINNED_STACK_ID);
+ assertPinnedStackExists();
+
+ // Launch the root activity again...
+ int rootActivityTaskId = mAmWmState.getAmState().getTaskByActivityName(
+ TEST_ACTIVITY).mTaskId;
+ launchHomeActivity();
+ launchActivity(TEST_ACTIVITY_WITH_SAME_AFFINITY);
+
+ // ...and ensure that even while matching purely by task affinity, the root activity task is
+ // found and reused, and that the pinned stack is unaffected
+ assertPinnedStackExists();
+ mAmWmState.assertFocusedActivity("Expected root activity focused", TEST_ACTIVITY);
+ assertTrue(rootActivityTaskId == mAmWmState.getAmState().getTaskByActivityName(
+ TEST_ACTIVITY).mTaskId);
+ }
+
+ public void testLaunchTaskByAffinityMatchSingleTask() throws Exception {
+ if (!supportsPip()) return;
+
+ // Launch an activity into the pinned stack with a fixed affinity
+ launchActivityInStack(TEST_ACTIVITY_WITH_SAME_AFFINITY, PINNED_STACK_ID,
+ EXTRA_START_ACTIVITY, getActivityComponentName(PIP_ACTIVITY),
+ EXTRA_FINISH_SELF_ON_RESUME, "true");
+ mAmWmState.waitForValidState(mDevice, PIP_ACTIVITY, PINNED_STACK_ID);
+ assertPinnedStackExists();
+
+ // Launch the root activity again, of the matching task and ensure that we expand to
+ // fullscreen
+ int activityTaskId = mAmWmState.getAmState().getTaskByActivityName(
+ PIP_ACTIVITY).mTaskId;
+ launchHomeActivity();
+ launchActivity(TEST_ACTIVITY_WITH_SAME_AFFINITY);
+ mAmWmState.waitForValidState(mDevice, PIP_ACTIVITY, FULLSCREEN_WORKSPACE_STACK_ID);
+ assertPinnedStackDoesNotExist();
+ assertTrue(activityTaskId == mAmWmState.getAmState().getTaskByActivityName(
+ PIP_ACTIVITY).mTaskId);
+ }
+
/**
* Called after the given {@param activityName} has been moved to the fullscreen stack. Ensures
* that the {@param focusedStackId} is focused, and checks the top and/or bottom tasks in the
@@ -1014,10 +1121,10 @@
+ " onConfigurationChanged() calls, expecting 1");
} else if (lifecycleCounts.mPictureInPictureModeChangedCount != 1) {
fail(activityName + " has received " + lifecycleCounts.mPictureInPictureModeChangedCount
- + " onMultiWindowModeChanged() calls, expecting 1");
+ + " onPictureInPictureModeChanged() calls, expecting 1");
} else if (lifecycleCounts.mMultiWindowModeChangedCount != 1) {
fail(activityName + " has received " + lifecycleCounts.mMultiWindowModeChangedCount
- + " onPictureInPictureModeChanged() calls, expecting 1");
+ + " onMultiWindowModeChanged() calls, expecting 1");
} else {
int lastPipLine = lifecycleCounts.mLastPictureInPictureModeChangedLineIndex;
int lastMwLine = lifecycleCounts.mLastMultiWindowModeChangedLineIndex;
diff --git a/hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/ActivityAndWindowManagersState.java b/hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/ActivityAndWindowManagersState.java
index 9f4cf6c..b73cf1e 100644
--- a/hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/ActivityAndWindowManagersState.java
+++ b/hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/ActivityAndWindowManagersState.java
@@ -214,6 +214,13 @@
"***Waiting for home activity to be visible...");
}
+ /** @return true if recents activity is visible. Devices without recents will return false */
+ boolean waitForRecentsActivityVisible(ITestDevice device) throws Exception {
+ waitForWithAmState(device, ActivityManagerState::isRecentsActivityVisible,
+ "***Waiting for recents activity to be visible...");
+ return mAmState.isRecentsActivityVisible();
+ }
+
void waitForKeyguardShowingAndNotOccluded(ITestDevice device) throws Exception {
waitForWithAmState(device, state -> state.getKeyguardControllerState().keyguardShowing
&& !state.getKeyguardControllerState().keyguardOccluded,
diff --git a/hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/ActivityManagerState.java b/hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/ActivityManagerState.java
index 56fed31..cacfe23 100644
--- a/hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/ActivityManagerState.java
+++ b/hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/ActivityManagerState.java
@@ -34,6 +34,7 @@
import java.util.regex.Matcher;
import static android.server.cts.ActivityManagerTestBase.HOME_STACK_ID;
+import static android.server.cts.ActivityManagerTestBase.RECENTS_STACK_ID;
import static android.server.cts.StateLogger.log;
import static android.server.cts.StateLogger.logE;
@@ -309,6 +310,11 @@
return homeActivity != null && homeActivity.visible;
}
+ boolean isRecentsActivityVisible() {
+ final Activity recentsActivity = getRecentsActivity();
+ return recentsActivity != null && recentsActivity.visible;
+ }
+
String getHomeActivityName() {
Activity activity = getHomeActivity();
if (activity == null) {
@@ -330,11 +336,30 @@
return null;
}
+ ActivityTask getRecentsTask() {
+ ActivityStack recentsStack = getStackById(RECENTS_STACK_ID);
+ if (recentsStack != null) {
+ for (ActivityTask task : recentsStack.mTasks) {
+ if (task.mTaskType == RECENTS_ACTIVITY_TYPE) {
+ return task;
+ }
+ }
+ return null;
+ }
+ return null;
+ }
+
private Activity getHomeActivity() {
final ActivityTask homeTask = getHomeTask();
return homeTask != null ? homeTask.mActivities.get(homeTask.mActivities.size() - 1) : null;
}
+ private Activity getRecentsActivity() {
+ final ActivityTask recentsTask = getRecentsTask();
+ return recentsTask != null ? recentsTask.mActivities.get(recentsTask.mActivities.size() - 1)
+ : null;
+ }
+
ActivityTask getTaskByActivityName(String activityName) {
return getTaskByActivityName(activityName, -1);
}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityFingerprintGestureTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityFingerprintGestureTest.java
index 5e34c4b..3eebff9 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityFingerprintGestureTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityFingerprintGestureTest.java
@@ -29,9 +29,9 @@
import org.mockito.MockitoAnnotations;
import static android.content.pm.PackageManager.FEATURE_FINGERPRINT;
+
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.mock;
@@ -76,20 +76,9 @@
}
@Test
- public void testGetFingerprintManager_returnsManagerIfFeatureAvailable() {
- if (!mIsHardwareAvailable) {
- assertNull(mFingerprintGestureController);
- return;
- }
- assertNotNull(mFingerprintGestureController);
- }
-
- @Test
public void testGestureDetectionAvailable_initialState_shouldBeAvailable() {
- if (!mIsHardwareAvailable) {
- return;
- }
- assertTrue(mFingerprintGestureController.isGestureDetectionAvailable());
+ assertEquals(mIsHardwareAvailable,
+ mFingerprintGestureController.isGestureDetectionAvailable());
}
@Test
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityTextTraversalTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityTextTraversalTest.java
index 53eec5f..6bc2969 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityTextTraversalTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityTextTraversalTest.java
@@ -3928,6 +3928,10 @@
// Set selection at the end.
final int textLength = editText.getText().length();
+ arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT, textLength);
+ arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT, textLength);
+ assertTrue(text.performAction(AccessibilityNodeInfo.ACTION_SET_SELECTION, arguments));
+
// Verify the selection position.
assertEquals(textLength, Selection.getSelectionStart(editText.getText()));
assertEquals(textLength, Selection.getSelectionEnd(editText.getText()));
diff --git a/tests/app/app/Android.mk b/tests/app/app/Android.mk
index 2e4bdf0..3c0a0c5 100644
--- a/tests/app/app/Android.mk
+++ b/tests/app/app/Android.mk
@@ -30,6 +30,7 @@
ctstestrunner \
ctstestserver \
mockito-target-minus-junit4 \
+ android-support-v4 \
legacy-android-test
LOCAL_SRC_FILES := $(call all-java-files-under, src) \
diff --git a/tests/app/src/android/app/cts/AlertWindowsTests.java b/tests/app/src/android/app/cts/AlertWindowsTests.java
index bd97727..e31c53b 100644
--- a/tests/app/src/android/app/cts/AlertWindowsTests.java
+++ b/tests/app/src/android/app/cts/AlertWindowsTests.java
@@ -131,38 +131,36 @@
public void testAlertWindowOomAdj() throws Exception {
setAlertWindowPermission(true /* allow */);
+
assertPackageImportance(IMPORTANCE_PERCEPTIBLE, IMPORTANCE_PERCEPTIBLE_PRE_26);
- assertUidImportance(IMPORTANCE_PERCEPTIBLE, IMPORTANCE_PERCEPTIBLE_PRE_26);
+
+ // TODO AM.getUidImportance() sometimes return a different value from what
+ // getPackageImportance() returns... b/37950472
+ // assertUidImportance(IMPORTANCE_PERCEPTIBLE, IMPORTANCE_PERCEPTIBLE_PRE_26);
addAlertWindow();
// Process importance should be increased to visible when the service has an alert window.
assertPackageImportance(IMPORTANCE_VISIBLE, IMPORTANCE_VISIBLE);
- assertUidImportance(IMPORTANCE_VISIBLE, IMPORTANCE_VISIBLE);
addAlertWindow();
assertPackageImportance(IMPORTANCE_VISIBLE, IMPORTANCE_VISIBLE);
- assertUidImportance(IMPORTANCE_VISIBLE, IMPORTANCE_VISIBLE);
setAlertWindowPermission(false /* allow */);
// Process importance should no longer be visible since its alert windows are not allowed to
// be visible.
assertPackageImportance(IMPORTANCE_PERCEPTIBLE, IMPORTANCE_PERCEPTIBLE_PRE_26);
- assertUidImportance(IMPORTANCE_PERCEPTIBLE, IMPORTANCE_PERCEPTIBLE_PRE_26);
setAlertWindowPermission(true /* allow */);
// They can show again so importance should be visible again.
assertPackageImportance(IMPORTANCE_VISIBLE, IMPORTANCE_VISIBLE);
- assertUidImportance(IMPORTANCE_VISIBLE, IMPORTANCE_VISIBLE);
removeAlertWindow();
assertPackageImportance(IMPORTANCE_VISIBLE, IMPORTANCE_VISIBLE);
- assertUidImportance(IMPORTANCE_VISIBLE, IMPORTANCE_VISIBLE);
removeAlertWindow();
// Process importance should no longer be visible when the service no longer as alert
// windows.
assertPackageImportance(IMPORTANCE_PERCEPTIBLE, IMPORTANCE_PERCEPTIBLE_PRE_26);
- assertUidImportance(IMPORTANCE_PERCEPTIBLE, IMPORTANCE_PERCEPTIBLE_PRE_26);
}
private void addAlertWindow() throws Exception {
diff --git a/tests/autofillservice/Android.mk b/tests/autofillservice/Android.mk
index 37605af..f4437f7 100644
--- a/tests/autofillservice/Android.mk
+++ b/tests/autofillservice/Android.mk
@@ -16,8 +16,12 @@
include $(CLEAR_VARS)
+# Don't include this package in any target.
LOCAL_MODULE_TAGS := optional
+# When built, explicitly put it in the data partition.
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
LOCAL_STATIC_JAVA_LIBRARIES := \
compatibility-device-util \
ctstestrunner \
diff --git a/tests/autofillservice/AndroidManifest.xml b/tests/autofillservice/AndroidManifest.xml
index 32bf5a5..b762071 100644
--- a/tests/autofillservice/AndroidManifest.xml
+++ b/tests/autofillservice/AndroidManifest.xml
@@ -39,7 +39,14 @@
<activity android:name=".TimePickerClockActivity" />
<activity android:name=".TimePickerSpinnerActivity" />
<activity android:name=".FatActivity" />
- <activity android:name=".VirtualContainerActivity" />
+ <activity android:name=".VirtualContainerActivity">
+ <intent-filter>
+ <!-- This intent filter is not really needed by CTS, but it maks 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>
<activity android:name=".OptionalSaveActivity" />
<activity android:name=".AllAutofillableViewsActivity" />
<activity android:name=".GridActivity" >
diff --git a/tests/autofillservice/res/layout/login_activity.xml b/tests/autofillservice/res/layout/login_activity.xml
index 96caa46..e16d1c4 100644
--- a/tests/autofillservice/res/layout/login_activity.xml
+++ b/tests/autofillservice/res/layout/login_activity.xml
@@ -88,6 +88,12 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Login" />
+
+ <Button
+ android:id="@+id/cancel"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Cancel" />
</LinearLayout>
<TextView
diff --git a/tests/autofillservice/res/layout/pre_filled_login_activity.xml b/tests/autofillservice/res/layout/pre_filled_login_activity.xml
index 3a32928..77d94dc 100644
--- a/tests/autofillservice/res/layout/pre_filled_login_activity.xml
+++ b/tests/autofillservice/res/layout/pre_filled_login_activity.xml
@@ -90,6 +90,12 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Login" />
+
+ <Button
+ android:id="@+id/cancel"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Cancel" />
</LinearLayout>
<TextView
diff --git a/tests/autofillservice/res/layout/view_attribute_test_activity.xml b/tests/autofillservice/res/layout/view_attribute_test_activity.xml
index 96c0186..d03d65c 100644
--- a/tests/autofillservice/res/layout/view_attribute_test_activity.xml
+++ b/tests/autofillservice/res/layout/view_attribute_test_activity.xml
@@ -16,17 +16,77 @@
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent" android:layout_height="match_parent"
- android:orientation="vertical" android:id="@+id/rootContainer">
-
- <TextView android:id="@+id/textViewNoHint" android:layout_width="wrap_content"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:id="@+id/rootContainer">
+ <EditText android:id="@+id/editTextNoHint"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content" />
- <TextView android:id="@+id/textViewHintCustom" android:layout_width="wrap_content"
- android:layout_height="wrap_content" android:autofillHints="@string/new_password_label" />
- <TextView android:id="@+id/textViewPassword" android:layout_width="wrap_content"
- android:layout_height="wrap_content" android:autofillHints="password" />
- <TextView android:id="@+id/textViewPhoneName" android:layout_width="wrap_content"
- android:layout_height="wrap_content" android:autofillHints=" phone, username " />
- <TextView android:id="@+id/textViewHintsFromArray" android:layout_width="wrap_content"
- android:layout_height="wrap_content" android:autofillHints="@array/cc_expiration_values" />
+
+ <EditText android:id="@+id/editTextHintCustom"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:autofillHints="@string/new_password_label" />
+
+ <EditText android:id="@+id/editTextPassword"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:autofillHints="password" />
+
+ <EditText android:id="@+id/editTextPhoneName"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:autofillHints=" phone, username " />
+
+ <EditText android:id="@+id/editTextHintsFromArray"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:autofillHints="@array/cc_expiration_values" />
+
+ <!-- Use px instead of dp to not have to deal with screen resolution -->
+ <LinearLayout android:id="@+id/outerView"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:paddingTop="2px"
+ android:paddingBottom="5px"
+ android:paddingLeft="7px"
+ android:paddingRight="3px"
+ android:orientation="vertical"
+ android:background="#F00"
+ android:importantForAutofill="yes">
+ <LinearLayout android:id="@+id/nestedView"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:paddingTop="11px"
+ android:paddingBottom="17px"
+ android:paddingLeft="19px"
+ android:paddingRight="13px"
+ android:orientation="vertical"
+ android:background="#0F0"
+ android:importantForAutofill="no">
+ <LinearLayout android:id="@+id/doubleNestedView"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:paddingTop="23px"
+ android:paddingBottom="31px"
+ android:paddingLeft="37px"
+ android:paddingRight="29px"
+ android:orientation="vertical"
+ android:background="#00F"
+ android:importantForAutofill="yes">
+ <EditText android:id="@+id/tripleNestedView"
+ android:layout_height="41px"
+ android:layout_width="43px"
+ android:background="#FF0"
+ android:importantForAutofill="yes" />
+ </LinearLayout>
+
+ <EditText android:id="@+id/secondDoubleNestedView"
+ android:layout_height="47px"
+ android:layout_width="53px"
+ android:background="#F0F"
+ android:importantForAutofill="yes" />
+ </LinearLayout>
+ </LinearLayout>
</LinearLayout>
diff --git a/tests/autofillservice/src/android/autofillservice/cts/AuthenticationActivity.java b/tests/autofillservice/src/android/autofillservice/cts/AuthenticationActivity.java
index d1595ee..3956187 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/AuthenticationActivity.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/AuthenticationActivity.java
@@ -16,32 +16,74 @@
package android.autofillservice.cts;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.app.PendingIntent;
import android.app.assist.AssistStructure;
+import android.autofillservice.cts.CannedFillResponse.CannedDataset;
+import android.content.Context;
import android.content.Intent;
+import android.content.IntentSender;
import android.os.Bundle;
import android.os.Parcelable;
+import android.util.SparseArray;
import android.view.autofill.AutofillManager;
-import static com.google.common.truth.Truth.assertWithMessage;
+import com.google.common.base.Preconditions;
/**
* This class simulates authentication at the dataset at reponse level
*/
public class AuthenticationActivity extends AbstractAutoFillActivity {
+
+ private static final String EXTRA_DATASET_ID = "dataset_id";
+
private static CannedFillResponse sResponse;
private static CannedFillResponse.CannedDataset sDataset;
private static Bundle sData;
+ private static final SparseArray<CannedDataset> sDatasets = new SparseArray<>();
+
+ static void resetStaticState() {
+ sDatasets.clear();
+ }
public static void setResponse(CannedFillResponse response) {
sResponse = response;
sDataset = null;
}
- public static void setDataset(CannedFillResponse.CannedDataset dataset) {
+ /**
+ * @deprecated should use {@link #createSender(Context, int, CannedDataset)} instead.
+ */
+ @Deprecated
+ public static void setDataset(CannedDataset dataset) {
sDataset = dataset;
sResponse = null;
}
+ /**
+ * Creates an {@link IntentSender} with the given unique id for the given dataset.
+ */
+ public static IntentSender createSender(Context context, int id,
+ CannedDataset dataset) {
+ Preconditions.checkArgument(id > 0, "id must be positive");
+ Preconditions.checkState(sDatasets.get(id) == null, "already have id");
+ sDatasets.put(id, dataset);
+ final Intent intent = new Intent(context, AuthenticationActivity.class);
+ intent.putExtra(EXTRA_DATASET_ID, id);
+ return PendingIntent.getActivity(context, id, intent, 0).getIntentSender();
+ }
+
+ /**
+ * Creates an {@link IntentSender} with the given unique id.
+ */
+ public static IntentSender createSender(Context context, int id) {
+ Preconditions.checkArgument(id > 0, "id must be positive");
+ return PendingIntent
+ .getActivity(context, id, new Intent(context, AuthenticationActivity.class), 0)
+ .getIntentSender();
+ }
+
public static Bundle getData() {
final Bundle data = sData;
sData = null;
@@ -59,9 +101,13 @@
// and the bundle
sData = getIntent().getBundleExtra(AutofillManager.EXTRA_CLIENT_STATE);
+ final CannedDataset dataset = sDatasets.get(getIntent().getIntExtra(EXTRA_DATASET_ID, 0));
final Parcelable result;
- if (sResponse != null) {
+
+ if (dataset != null) {
+ result = dataset.asDataset((id) -> Helper.findNodeByResourceId(structure, id));
+ } else if (sResponse != null) {
result = sResponse.asFillResponse((id) -> Helper.findNodeByResourceId(structure, id));
} else if (sDataset != null) {
result = sDataset.asDataset((id) -> Helper.findNodeByResourceId(structure, id));
diff --git a/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java b/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java
index d18e621..a519e04 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java
@@ -60,13 +60,18 @@
@BeforeClass
public static void setUiBot() throws Exception {
- sUiBot = new UiBot(InstrumentationRegistry.getInstrumentation(), UI_TIMEOUT_MS);
+ sUiBot = new UiBot(InstrumentationRegistry.getInstrumentation());
}
@BeforeClass
@AfterClass
public static void disableService() {
+ if (!isServiceEnabled()) return;
+
+ final OneTimeSettingsListener observer = new OneTimeSettingsListener(getContext(),
+ AUTOFILL_SERVICE);
runShellCommand("settings delete secure %s", AUTOFILL_SERVICE);
+ observer.assertCalled();
assertServiceDisabled();
}
@@ -75,6 +80,7 @@
destroyAllSessions();
sReplier.reset();
InstrumentedAutoFillService.resetStaticState();
+ AuthenticationActivity.resetStaticState();
}
@After
@@ -84,11 +90,15 @@
}
/**
- * Enables the {@link InstrumentedAutoFillService} for auto-fill for the default user.
+ * Enables the {@link InstrumentedAutoFillService} for autofill for the current user.
*/
protected void enableService() {
- runShellCommand(
- "settings put secure %s %s default", AUTOFILL_SERVICE, SERVICE_NAME);
+ if (isServiceEnabled()) return;
+
+ final OneTimeSettingsListener observer = new OneTimeSettingsListener(getContext(),
+ AUTOFILL_SERVICE);
+ runShellCommand("settings put secure %s %s default", AUTOFILL_SERVICE, SERVICE_NAME);
+ observer.assertCalled();
assertServiceEnabled();
}
@@ -134,6 +144,11 @@
return presentation;
}
+ private static boolean isServiceEnabled() {
+ final String service = runShellCommand("settings get secure %s", AUTOFILL_SERVICE);
+ return SERVICE_NAME.equals(service);
+ }
+
private static void assertServiceStatus(boolean enabled) {
final String actual = runShellCommand("settings get secure %s", AUTOFILL_SERVICE);
final String expected = enabled ? SERVICE_NAME : "null";
diff --git a/tests/autofillservice/src/android/autofillservice/cts/CannedFillResponse.java b/tests/autofillservice/src/android/autofillservice/cts/CannedFillResponse.java
index 9f5368f..7d2f7e2 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/CannedFillResponse.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/CannedFillResponse.java
@@ -17,8 +17,6 @@
import static com.google.common.truth.Truth.assertWithMessage;
-import static android.autofillservice.cts.Helper.dumpStructure;
-import static android.autofillservice.cts.Helper.findNodeByResourceId;
import static android.autofillservice.cts.Helper.getAutofillIds;
import android.app.assist.AssistStructure;
import android.app.assist.AssistStructure.ViewNode;
@@ -26,7 +24,6 @@
import android.os.Bundle;
import android.service.autofill.Dataset;
import android.service.autofill.FillCallback;
-import android.service.autofill.FillContext;
import android.service.autofill.FillResponse;
import android.service.autofill.SaveInfo;
import android.view.autofill.AutofillId;
@@ -124,10 +121,12 @@
if (mIgnoredIds != null) {
builder.setIgnoredIds(getAutofillIds(nodeResolver, mIgnoredIds));
}
+ if (mAuthenticationIds != null) {
+ builder.setAuthentication(getAutofillIds(nodeResolver, mAuthenticationIds),
+ mAuthentication, mPresentation);
+ }
return builder
.setClientState(mExtras)
- .setAuthentication(getAutofillIds(nodeResolver, mAuthenticationIds),
- mAuthentication, mPresentation)
.build();
}
@@ -215,16 +214,9 @@
/**
* Sets the authentication intent.
*/
- public Builder setAuthentication(IntentSender authentication) {
- mAuthentication = authentication;
- return this;
- }
-
- /**
- * Sets the authentication ids.
- */
- public Builder setAuthenticationIds(String... ids) {
+ public Builder setAuthentication(IntentSender authentication, String... ids) {
mAuthenticationIds = ids;
+ mAuthentication = authentication;
return this;
}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/CheckoutActivity.java b/tests/autofillservice/src/android/autofillservice/cts/CheckoutActivity.java
index b04b45f..0b8d938 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/CheckoutActivity.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/CheckoutActivity.java
@@ -28,7 +28,6 @@
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.Spinner;
-import android.widget.SpinnerAdapter;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
diff --git a/tests/autofillservice/src/android/autofillservice/cts/Helper.java b/tests/autofillservice/src/android/autofillservice/cts/Helper.java
index f1491b4..7b2e7a5 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/Helper.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/Helper.java
@@ -16,34 +16,26 @@
package android.autofillservice.cts;
-import static android.autofillservice.cts.Helper.dumpStructure;
-
import static com.google.common.truth.Truth.assertWithMessage;
import android.app.assist.AssistStructure;
import android.app.assist.AssistStructure.ViewNode;
import android.app.assist.AssistStructure.WindowNode;
import android.icu.util.Calendar;
-import android.os.Bundle;
import android.os.UserManager;
-import android.service.autofill.Dataset;
import android.service.autofill.FillContext;
-import android.service.autofill.FillResponse;
import android.support.annotation.NonNull;
import android.support.test.InstrumentationRegistry;
import android.text.TextUtils;
-import android.text.format.DateUtils;
import android.util.Log;
-import android.view.autofill.AutofillId;
-import android.view.autofill.AutofillValue;
import android.view.View;
import android.view.ViewStructure.HtmlInfo;
+import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillValue;
import com.android.compatibility.common.util.SystemUtil;
-import java.util.ArrayList;
import java.util.List;
-import java.util.Map;
import java.util.function.Function;
/**
@@ -89,6 +81,11 @@
static final long SAVE_TIMEOUT_MS = 5000;
/**
+ * Time to wait if a UI change is not expected
+ */
+ static final long NOT_SHOWING_TIMEOUT_MS = 500;
+
+ /**
* Timeout (in milliseconds) for UI operations. Typically used by {@link UiBot}.
*/
static final int UI_TIMEOUT_MS = 2000;
@@ -239,7 +236,7 @@
/**
* Gets a node given its Android resource id, or {@code null} if not found.
*/
- static ViewNode findNodeByResourceId(ArrayList<FillContext> contexts, String resourceId) {
+ static ViewNode findNodeByResourceId(List<FillContext> contexts, String resourceId) {
for (FillContext context : contexts) {
ViewNode node = findNodeByResourceId(context.getStructure(), resourceId);
if (node != null) {
@@ -311,14 +308,17 @@
final CharSequence text = node.getText();
final String resourceId = node.getIdEntry();
if (!TextUtils.isEmpty(text)) {
- throw new AssertionError("text on sanitized field " + resourceId + ": " + text);
+ throw new AssertionError("text on sanitized field " + resourceId + ": " + text);
}
assertNodeHasNoAutofillValue(node);
}
static void assertNodeHasNoAutofillValue(ViewNode node) {
final AutofillValue value = node.getAutofillValue();
- assertWithMessage("node.getAutofillValue()").that(value).isNull();
+ if (value != null) {
+ final String text = value.isText() ? value.getTextValue().toString() : "N/A";
+ throw new AssertionError("node has value: " + value + " text=" + text);
+ }
}
/**
diff --git a/tests/autofillservice/src/android/autofillservice/cts/InstrumentedAutoFillService.java b/tests/autofillservice/src/android/autofillservice/cts/InstrumentedAutoFillService.java
index 61b47af..bfe438a 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/InstrumentedAutoFillService.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/InstrumentedAutoFillService.java
@@ -38,7 +38,6 @@
import android.service.autofill.SaveCallback;
import android.util.Log;
-import java.util.ArrayList;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
@@ -149,13 +148,13 @@
*/
static final class FillRequest {
final AssistStructure structure;
- final ArrayList<FillContext> contexts;
+ final List<FillContext> contexts;
final Bundle data;
final CancellationSignal cancellationSignal;
final FillCallback callback;
final int flags;
- private FillRequest(ArrayList<FillContext> contexts, Bundle data,
+ private FillRequest(List<FillContext> contexts, Bundle data,
CancellationSignal cancellationSignal, FillCallback callback, int flags) {
this.contexts = contexts;
this.data = data;
@@ -282,7 +281,7 @@
mSaveRequests.clear();
}
- private void onFillRequest(ArrayList<FillContext> contexts, Bundle data,
+ private void onFillRequest(List<FillContext> contexts, Bundle data,
CancellationSignal cancellationSignal, FillCallback callback, int flags) {
try {
CannedFillResponse response = null;
diff --git a/tests/autofillservice/src/android/autofillservice/cts/LoginActivity.java b/tests/autofillservice/src/android/autofillservice/cts/LoginActivity.java
index 48efb32..d05728f 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/LoginActivity.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/LoginActivity.java
@@ -58,6 +58,7 @@
private TextView mOutput;
private Button mLoginButton;
private Button mSaveButton;
+ private Button mCancelButton;
private Button mClearButton;
private FillExpectation mExpectation;
@@ -81,6 +82,7 @@
mLoginButton = (Button) findViewById(R.id.login);
mSaveButton = (Button) findViewById(R.id.save);
mClearButton = (Button) findViewById(R.id.clear);
+ mCancelButton = (Button) findViewById(R.id.cancel);
mUsernameLabel = (TextView) findViewById(R.id.username_label);
mUsernameEditText = (EditText) findViewById(R.id.username);
mPasswordLabel = (TextView) findViewById(R.id.password_label);
@@ -108,6 +110,7 @@
getAutofillManager().cancel();
}
});
+ mCancelButton.setOnClickListener((OnClickListener) v -> finish());
}
protected int getContentView() {
@@ -156,7 +159,7 @@
}
/**
- * Sets the expectation for an auto-fill request, so it can be asserted through
+ * Sets the expectation for an autofill request (for all fields), so it can be asserted through
* {@link #assertAutoFilled()} later.
*/
void expectAutoFill(String username, String password) {
@@ -166,13 +169,24 @@
}
/**
+ * Sets the expectation for an autofill request (for username only), so it can be asserted
+ * through {@link #assertAutoFilled()} later.
+ */
+ void expectAutoFill(String username) {
+ mExpectation = new FillExpectation(username);
+ mUsernameEditText.addTextChangedListener(mExpectation.ccUsernameWatcher);
+ }
+
+ /**
* Asserts the activity was auto-filled with the values passed to
* {@link #expectAutoFill(String, String)}.
*/
void assertAutoFilled() throws Exception {
assertWithMessage("expectAutoFill() not called").that(mExpectation).isNotNull();
mExpectation.ccUsernameWatcher.assertAutoFilled();
- mExpectation.ccPasswordWatcher.assertAutoFilled();
+ if (mExpectation.ccPasswordWatcher != null) {
+ mExpectation.ccPasswordWatcher.assertAutoFilled();
+ }
}
/**
@@ -278,5 +292,10 @@
ccUsernameWatcher = new OneTimeTextWatcher("username", mUsernameEditText, username);
ccPasswordWatcher = new OneTimeTextWatcher("password", mPasswordEditText, password);
}
+
+ private FillExpectation(String username) {
+ ccUsernameWatcher = new OneTimeTextWatcher("username", mUsernameEditText, username);
+ ccPasswordWatcher = null;
+ }
}
}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java
index 8ce9596..b2b5d40 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java
@@ -23,6 +23,7 @@
import static android.autofillservice.cts.Helper.assertNumberOfChildren;
import static android.autofillservice.cts.Helper.assertTextAndValue;
import static android.autofillservice.cts.Helper.assertTextIsSanitized;
+import static android.autofillservice.cts.Helper.assertValue;
import static android.autofillservice.cts.Helper.eventually;
import static android.autofillservice.cts.Helper.findNodeByResourceId;
import static android.autofillservice.cts.Helper.runShellCommand;
@@ -45,6 +46,7 @@
import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_USERNAME;
import static android.text.InputType.TYPE_NULL;
import static android.text.InputType.TYPE_TEXT_VARIATION_PASSWORD;
+import static android.view.View.IMPORTANT_FOR_AUTOFILL_NO;
import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
import static com.google.common.truth.Truth.assertThat;
@@ -87,9 +89,6 @@
*/
public class LoginActivityTest extends AutoFillServiceTestCase {
- // TODO(b/37424539): remove when fixed
- private static final boolean SUPPORTS_PARTITIONED_AUTH = false;
-
@Rule
public final ActivityTestRule<LoginActivity> mActivityRule = new ActivityTestRule<LoginActivity>(
LoginActivity.class);
@@ -168,6 +167,100 @@
}
@Test
+ public void testAutoFillTwoDatasetsSameNumberOfFields() throws Exception {
+ // Set service.
+ enableService();
+
+ // Set expectations.
+ sReplier.addResponse(new CannedFillResponse.Builder()
+ .addDataset(new CannedDataset.Builder()
+ .setField(ID_USERNAME, "dude")
+ .setField(ID_PASSWORD, "sweet")
+ .setPresentation(createPresentation("The Dude"))
+ .build())
+ .addDataset(new CannedDataset.Builder()
+ .setField(ID_USERNAME, "DUDE")
+ .setField(ID_PASSWORD, "SWEET")
+ .setPresentation(createPresentation("THE DUDE"))
+ .build())
+ .build());
+ mActivity.expectAutoFill("dude", "sweet");
+
+ // Trigger auto-fill.
+ mActivity.onUsername(View::requestFocus);
+ sReplier.getNextFillRequest();
+
+ // Make sure all datasets are available...
+ sUiBot.assertDatasets("The Dude", "THE DUDE");
+
+ // ... on all fields.
+ mActivity.onPassword(View::requestFocus);
+ sUiBot.assertDatasets("The Dude", "THE DUDE");
+
+ // Auto-fill it.
+ sUiBot.selectDataset("The Dude");
+
+ // Check the results.
+ mActivity.assertAutoFilled();
+ }
+
+ @Test
+ public void testAutoFillTwoDatasetsUnevenNumberOfFieldsFillsAll() throws Exception {
+ autoFillTwoDatasetsUnevenNumberOfFieldsTest(true);
+ }
+
+ @Test
+ public void testAutoFillTwoDatasetsUnevenNumberOfFieldsFillsOne() throws Exception {
+ autoFillTwoDatasetsUnevenNumberOfFieldsTest(false);
+ }
+
+ private void autoFillTwoDatasetsUnevenNumberOfFieldsTest(boolean fillsAll) throws Exception {
+ // Set service.
+ enableService();
+
+ // Set expectations.
+ sReplier.addResponse(new CannedFillResponse.Builder()
+ .addDataset(new CannedDataset.Builder()
+ .setField(ID_USERNAME, "dude")
+ .setField(ID_PASSWORD, "sweet")
+ .setPresentation(createPresentation("The Dude"))
+ .build())
+ .addDataset(new CannedDataset.Builder()
+ .setField(ID_USERNAME, "DUDE")
+ .setPresentation(createPresentation("THE DUDE"))
+ .build())
+ .build());
+ if (fillsAll) {
+ mActivity.expectAutoFill("dude", "sweet");
+ } else {
+ mActivity.expectAutoFill("DUDE");
+ }
+
+ // Trigger auto-fill.
+ mActivity.onUsername(View::requestFocus);
+ sReplier.getNextFillRequest();
+
+ // Make sure all datasets are available on username...
+ sUiBot.assertDatasets("The Dude", "THE DUDE");
+
+ // ... but just one for password
+ mActivity.onPassword(View::requestFocus);
+ sUiBot.assertDatasets("The Dude");
+
+ // Auto-fill it.
+ mActivity.onUsername(View::requestFocus);
+ sUiBot.assertDatasets("The Dude", "THE DUDE");
+ if (fillsAll) {
+ sUiBot.selectDataset("The Dude");
+ } else {
+ sUiBot.selectDataset("THE DUDE");
+ }
+
+ // Check the results.
+ mActivity.assertAutoFilled();
+ }
+
+ @Test
public void testAutoFillWhenViewHasChildAccessibilityNodes() throws Exception {
mActivity.onUsername((v) -> v.setAccessibilityDelegate(new AccessibilityDelegate() {
@Override
@@ -358,6 +451,10 @@
// Trigger auto-fill.
mActivity.onUsername(View::requestFocus);
+
+ // Since this is a Presubmit test, wait for connection to avoid flakiness.
+ waitUntilConnected();
+
sReplier.getNextFillRequest();
// Auto-fill it.
@@ -955,7 +1052,7 @@
// Configure the service behavior
sReplier.addResponse(new CannedFillResponse.Builder()
- .setAuthentication(authentication)
+ .setAuthentication(authentication, ID_USERNAME, ID_PASSWORD)
.setPresentation(createPresentation("Tap to auth response"))
.setExtras(extras)
.build());
@@ -1019,7 +1116,6 @@
.setField(ID_PASSWORD, "sweet")
.setPresentation(createPresentation("Dataset"))
.build())
- .setAuthenticationIds(ID_USERNAME)
.build());
// Create the authentication intent
@@ -1028,7 +1124,8 @@
// Configure the service behavior
sReplier.addResponse(new CannedFillResponse.Builder()
- .setAuthentication(authentication)
+ .setAuthentication(authentication, ID_USERNAME)
+ .setIgnoreFields(ID_PASSWORD)
.setPresentation(createPresentation("Tap to auth response"))
.setExtras(extras)
.build());
@@ -1045,29 +1142,13 @@
callback.assertUiShownEvent(username);
sUiBot.assertShownByText("Tap to auth response");
- if (SUPPORTS_PARTITIONED_AUTH) {
- // Make sure UI is not show on 2nd field
- final View password = mActivity.getPassword();
- mActivity.onPassword(View::requestFocus);
- callback.assertUiHiddenEvent(username);
- sUiBot.assertNotShownByText("Tap to auth response");
- // Now tap on 1st field to show it again...
- mActivity.onUsername(View::requestFocus);
- callback.assertUiShownEvent(username);
- } else {
- // Make sure UI is show on 2nd field as well
- final View password = mActivity.getPassword();
- mActivity.onPassword(View::requestFocus);
-
- callback.assertUiHiddenEvent(username);
- callback.assertUiShownEvent(password);
- sUiBot.assertShownByText("Tap to auth response");
-
- // Now tap on 1st field to show it again...
- mActivity.onUsername(View::requestFocus);
- callback.assertUiHiddenEvent(password);
- callback.assertUiShownEvent(username);
- }
+ // Make sure UI is not show on 2nd field
+ mActivity.onPassword(View::requestFocus);
+ callback.assertUiHiddenEvent(username);
+ sUiBot.assertNotShownByText("Tap to auth response");
+ // Now tap on 1st field to show it again...
+ mActivity.onUsername(View::requestFocus);
+ callback.assertUiShownEvent(username);
// ...and select it this time
sUiBot.selectByText("Tap to auth response");
@@ -1082,7 +1163,6 @@
// Check the results.
mActivity.assertAutoFilled();
-
final Bundle data = AuthenticationActivity.getData();
assertThat(data).isNotNull();
final String extraValue = data.getString("numbers");
@@ -1090,7 +1170,44 @@
}
@Test
- public void testDatasetAuth() throws Exception {
+ public void testFillResponseAuthServiceHasNoData() throws Exception {
+ // Set service.
+ enableService();
+ final MyAutofillCallback callback = mActivity.registerCallback();
+
+ // Prepare the authenticated response
+ AuthenticationActivity.setResponse(
+ new CannedFillResponse.Builder()
+ .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_USERNAME, ID_PASSWORD)
+ .build());
+
+ // Create the authentication intent
+ final IntentSender authentication = PendingIntent.getActivity(getContext(), 0,
+ new Intent(getContext(), AuthenticationActivity.class), 0).getIntentSender();
+
+ // Configure the service behavior
+ sReplier.addResponse(new CannedFillResponse.Builder()
+ .setAuthentication(authentication, ID_USERNAME, ID_PASSWORD)
+ .setPresentation(createPresentation("Tap to auth response"))
+ .build());
+
+ // Trigger auto-fill.
+ mActivity.onUsername(View::requestFocus);
+
+ // Wait for onFill() before proceeding.
+ sReplier.getNextFillRequest();
+ final View username = mActivity.getUsername();
+ callback.assertUiShownEvent(username);
+
+ // Select the authentication dialog.
+ sUiBot.selectByText("Tap to auth response");
+ callback.assertUiHiddenEvent(username);
+ sUiBot.assertNotShownByText("Tap to auth response");
+ sUiBot.assertNoDatasets();
+ }
+
+ @Test
+ public void testDatasetAuthTwoFields() throws Exception {
// Set service.
enableService();
final MyAutofillCallback callback = mActivity.registerCallback();
@@ -1103,7 +1220,7 @@
.build());
// Create the authentication intent
- IntentSender authentication = PendingIntent.getActivity(getContext(), 0,
+ final IntentSender authentication = PendingIntent.getActivity(getContext(), 0,
new Intent(getContext(), AuthenticationActivity.class), 0).getIntentSender();
// Configure the service behavior
@@ -1137,6 +1254,131 @@
}
@Test
+ public void testDatasetAuthTwoDatasets() throws Exception {
+ // Set service.
+ enableService();
+ final MyAutofillCallback callback = mActivity.registerCallback();
+
+ // Prepare the authenticated response
+ AuthenticationActivity.setDataset(new CannedDataset.Builder()
+ .setField(ID_USERNAME, "dude")
+ .setField(ID_PASSWORD, "sweet")
+ .setPresentation(createPresentation("Dataset"))
+ .build());
+
+ // Create the authentication intents
+ final IntentSender authentication1 = PendingIntent.getActivity(getContext(), 1,
+ new Intent(getContext(), AuthenticationActivity.class), 0).getIntentSender();
+ final IntentSender authentication2 = PendingIntent.getActivity(getContext(), 2,
+ new Intent(getContext(), AuthenticationActivity.class), 0).getIntentSender();
+
+ // Configure the service behavior
+ sReplier.addResponse(new CannedFillResponse.Builder()
+ .addDataset(new CannedDataset.Builder()
+ .setField(ID_USERNAME, "dude")
+ .setField(ID_PASSWORD, "sweet")
+ .setPresentation(createPresentation("Tap to auth dataset 1"))
+ .setAuthentication(authentication1)
+ .build())
+ .addDataset(new CannedDataset.Builder()
+ .setField(ID_USERNAME, "DUDE")
+ .setField(ID_PASSWORD, "SWEET")
+ .setPresentation(createPresentation("Tap to auth dataset 2"))
+ .setAuthentication(authentication2)
+ .build())
+ .build());
+
+ // Set expectation for the activity
+ mActivity.expectAutoFill("dude", "sweet");
+
+ // Trigger auto-fill.
+ mActivity.onUsername(View::requestFocus);
+
+ // Wait for onFill() before proceeding.
+ sReplier.getNextFillRequest();
+ final View username = mActivity.getUsername();
+
+ // Authenticate
+ callback.assertUiShownEvent(username);
+ sUiBot.assertDatasets("Tap to auth dataset 1", "Tap to auth dataset 2");
+
+ sUiBot.selectDataset("Tap to auth dataset 1");
+ callback.assertUiHiddenEvent(username);
+ sUiBot.assertNoDatasets();
+
+ // Check the results.
+ mActivity.assertAutoFilled();
+ }
+
+ @Test
+ public void testDatasetAuthMixedSelectAuth() throws Exception {
+ datasetAuthMixedTest(true);
+ }
+
+ @Test
+ public void testDatasetAuthMixedSelectNonAuth() throws Exception {
+ datasetAuthMixedTest(false);
+ }
+
+ private void datasetAuthMixedTest(boolean selectAuth) throws Exception {
+ // Set service.
+ enableService();
+ final MyAutofillCallback callback = mActivity.registerCallback();
+
+ // Prepare the authenticated response
+ AuthenticationActivity.setDataset(new CannedDataset.Builder()
+ .setField(ID_USERNAME, "dude")
+ .setField(ID_PASSWORD, "sweet")
+ .setPresentation(createPresentation("Dataset"))
+ .build());
+
+ // Create the authentication intents
+ final IntentSender authentication = PendingIntent.getActivity(getContext(), 0,
+ new Intent(getContext(), AuthenticationActivity.class), 0).getIntentSender();
+
+ // Configure the service behavior
+ sReplier.addResponse(new CannedFillResponse.Builder()
+ .addDataset(new CannedDataset.Builder()
+ .setField(ID_USERNAME, "dude")
+ .setField(ID_PASSWORD, "sweet")
+ .setPresentation(createPresentation("Tap to auth dataset"))
+ .setAuthentication(authentication)
+ .build())
+ .addDataset(new CannedDataset.Builder()
+ .setField(ID_USERNAME, "DUDE")
+ .setField(ID_PASSWORD, "SWEET")
+ .setPresentation(createPresentation("What, me auth?"))
+ .build())
+ .build());
+
+ // Set expectation for the activity
+ if (selectAuth) {
+ mActivity.expectAutoFill("dude", "sweet");
+ } else {
+ mActivity.expectAutoFill("DUDE", "SWEET");
+ }
+
+ // Trigger auto-fill.
+ mActivity.onUsername(View::requestFocus);
+
+ // Wait for onFill() before proceeding.
+ sReplier.getNextFillRequest();
+ final View username = mActivity.getUsername();
+
+ // Authenticate
+ callback.assertUiShownEvent(username);
+ sUiBot.assertDatasets("Tap to auth dataset", "What, me auth?");
+
+ final String chosenOne = selectAuth ? "Tap to auth dataset" : "What, me auth?";
+ sUiBot.selectDataset(chosenOne);
+ callback.assertUiHiddenEvent(username);
+ sUiBot.assertNoDatasets();
+
+ // Check the results.
+ mActivity.assertAutoFilled();
+ }
+
+ @Test
public void testDisableSelf() throws Exception {
enableService();
@@ -1283,7 +1525,7 @@
final FillRequest fillRequest = sReplier.getNextFillRequest();
- // Assert it only has 1 root view with 9 "leaf" nodes:
+ // Assert it only has 1 root view with 10 "leaf" nodes:
// 1.text view for app title
// 2.username text label
// 3.username text field
@@ -1293,11 +1535,12 @@
// 7.clear button
// 8.save button
// 9.login button
+ // 10.cancel button
//
// But it also has an intermediate container (for username) that should be included because
// it has a resource id.
- assertNumberOfChildren(fillRequest.structure, 11);
+ assertNumberOfChildren(fillRequest.structure, 12);
// Make sure container with a resource id was included:
final ViewNode usernameContainer = findNodeByResourceId(fillRequest.structure,
@@ -1306,21 +1549,13 @@
assertThat(usernameContainer.getChildCount()).isEqualTo(2);
}
- private static final boolean BUG_36171235_FIXED = false;
-
@Test
public void testAutofillManuallyOneDataset() throws Exception {
// Set service.
enableService();
- if (BUG_36171235_FIXED)
// And activity.
- mActivity.onUsername((v) -> {
- v.setImportantForAutofill(View.IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS);
- // TODO: setting an empty text, otherwise longPress() does not
- // display the AUTOFILL context menu. Need to fix it, but it's a test case issue...
- v.setText("");
- });
+ mActivity.onUsername((v) -> v.setImportantForAutofill(IMPORTANT_FOR_AUTOFILL_NO));
// Set expectations.
sReplier.addResponse(new CannedDataset.Builder()
@@ -1330,12 +1565,7 @@
.build());
mActivity.expectAutoFill("dude", "sweet");
- // Long-press field to trigger AUTOFILL menu.
- if (BUG_36171235_FIXED) {
- sUiBot.getAutofillMenuOption(ID_USERNAME).click();
- } else {
- mActivity.onUsername((v) -> mActivity.getAutofillManager().requestAutofill(v));
- }
+ sUiBot.getAutofillMenuOption(ID_USERNAME).click();
final FillRequest fillRequest = sReplier.getNextFillRequest();
assertThat(fillRequest.flags).isEqualTo(FLAG_MANUAL_REQUEST);
@@ -1361,14 +1591,8 @@
// Set service.
enableService();
- if (BUG_36171235_FIXED)
// And activity.
- mActivity.onUsername((v) -> {
- v.setImportantForAutofill(View.IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS);
- // TODO: setting an empty text, otherwise longPress() does not display the AUTOFILL
- // context menu. Need to fix it, but it's a test case issue...
- v.setText("");
- });
+ mActivity.onUsername((v) -> v.setImportantForAutofill(IMPORTANT_FOR_AUTOFILL_NO));
// Set expectations.
sReplier.addResponse(new CannedFillResponse.Builder()
@@ -1391,11 +1615,7 @@
}
// Long-press field to trigger AUTOFILL menu.
- if (BUG_36171235_FIXED) {
- sUiBot.getAutofillMenuOption(ID_USERNAME).click();
- } else {
- mActivity.onUsername((v) -> mActivity.getAutofillManager().requestAutofill(v));
- }
+ sUiBot.getAutofillMenuOption(ID_USERNAME).click();
final FillRequest fillRequest = sReplier.getNextFillRequest();
assertThat(fillRequest.flags).isEqualTo(FLAG_MANUAL_REQUEST);
@@ -1409,6 +1629,164 @@
}
@Test
+ public void testAutofillManuallyPartialField() throws Exception {
+ // Set service.
+ enableService();
+
+ // And activity.
+ mActivity.onUsername((v) -> {
+ v.setText("dud");
+ v.setImportantForAutofill(IMPORTANT_FOR_AUTOFILL_NO);
+ });
+ mActivity.onPassword((v) -> v.setText("IamSecretMan"));
+
+ // Set expectations.
+ sReplier.addResponse(new CannedDataset.Builder()
+ .setField(ID_USERNAME, "dude")
+ .setField(ID_PASSWORD, "sweet")
+ .setPresentation(createPresentation("The Dude"))
+ .build());
+ mActivity.expectAutoFill("dude", "sweet");
+
+ sUiBot.getAutofillMenuOption(ID_USERNAME).click();
+
+ final FillRequest fillRequest = sReplier.getNextFillRequest();
+ assertThat(fillRequest.flags).isEqualTo(FLAG_MANUAL_REQUEST);
+ // Username value should be available because it triggered the manual request...
+ assertValue(fillRequest.structure, ID_USERNAME, "dud");
+ // ... but password didn't
+ assertTextIsSanitized(fillRequest.structure, ID_PASSWORD);
+
+ // Should have been automatically filled.
+ sUiBot.assertNoDatasets();
+
+ // Check the results.
+ mActivity.assertAutoFilled();
+ }
+
+ @Test
+ public void testAutofillManuallyAgainAfterAutomaticallyAutofilledBefore() throws Exception {
+ // Set service.
+ enableService();
+
+ /*
+ * 1st fill (automatic).
+ */
+ // Set expectations.
+ sReplier.addResponse(new CannedDataset.Builder()
+ .setField(ID_USERNAME, "dude")
+ .setField(ID_PASSWORD, "sweet")
+ .setPresentation(createPresentation("The Dude"))
+ .build());
+ mActivity.expectAutoFill("dude", "sweet");
+
+ // Trigger auto-fill.
+ mActivity.onUsername(View::requestFocus);
+
+ // Assert request.
+ final FillRequest fillRequest1 = sReplier.getNextFillRequest();
+ assertThat(fillRequest1.flags).isEqualTo(0);
+ assertTextIsSanitized(fillRequest1.structure, ID_USERNAME);
+ assertTextIsSanitized(fillRequest1.structure, ID_PASSWORD);
+
+ // Select it.
+ sUiBot.selectDataset("The Dude");
+
+ // Check the results.
+ mActivity.assertAutoFilled();
+
+ /*
+ * 2nd fill (manual).
+ */
+ // Set expectations.
+ sReplier.addResponse(new CannedDataset.Builder()
+ .setField(ID_USERNAME, "DUDE")
+ .setField(ID_PASSWORD, "SWEET")
+ .setPresentation(createPresentation("THE DUDE"))
+ .build());
+ mActivity.expectAutoFill("DUDE", "SWEET");
+ // Change password to make sure it's not sent to the service.
+ mActivity.onPassword((v) -> v.setText("IamSecretMan"));
+
+ // Trigger auto-fill.
+ sUiBot.getAutofillMenuOption(ID_USERNAME).click();
+
+ // Assert request.
+ final FillRequest fillRequest2 = sReplier.getNextFillRequest();
+ assertThat(fillRequest2.flags).isEqualTo(FLAG_MANUAL_REQUEST);
+ assertValue(fillRequest2.structure, ID_USERNAME, "dude");
+ assertTextIsSanitized(fillRequest2.structure, ID_PASSWORD);
+
+ // Should have been automatically filled.
+ sUiBot.assertNoDatasets();
+
+ // Check the results.
+ mActivity.assertAutoFilled();
+ }
+
+ @Test
+ public void testAutofillManuallyAgainAfterManuallyAutofilledBefore() throws Exception {
+ // Set service.
+ enableService();
+ // And activity
+ mActivity.onUsername((v) -> v.setImportantForAutofill(IMPORTANT_FOR_AUTOFILL_NO));
+
+ /*
+ * 1st fill (manual).
+ */
+ // Set expectations.
+ sReplier.addResponse(new CannedDataset.Builder()
+ .setField(ID_USERNAME, "dude")
+ .setField(ID_PASSWORD, "sweet")
+ .setPresentation(createPresentation("The Dude"))
+ .build());
+ mActivity.expectAutoFill("dude", "sweet");
+
+ // Trigger auto-fill.
+ sUiBot.getAutofillMenuOption(ID_USERNAME).click();
+
+ // Assert request.
+ final FillRequest fillRequest1 = sReplier.getNextFillRequest();
+ assertThat(fillRequest1.flags).isEqualTo(FLAG_MANUAL_REQUEST);
+ assertValue(fillRequest1.structure, ID_USERNAME, "");
+ assertTextIsSanitized(fillRequest1.structure, ID_PASSWORD);
+
+ // Should have been automatically filled.
+ sUiBot.assertNoDatasets();
+
+ // Check the results.
+ mActivity.assertAutoFilled();
+
+ /*
+ * 2nd fill (manual).
+ */
+ // Set expectations.
+ sReplier.addResponse(new CannedDataset.Builder()
+ .setField(ID_USERNAME, "DUDE")
+ .setField(ID_PASSWORD, "SWEET")
+ .setPresentation(createPresentation("THE DUDE"))
+ .build());
+ mActivity.expectAutoFill("DUDE", "SWEET");
+ // Change password to make sure it's not sent to the service.
+ mActivity.onPassword((v) -> v.setText("IamSecretMan"));
+
+ // Trigger auto-fill.
+ sUiBot.getAutofillMenuOption(ID_USERNAME).click();
+
+ // Assert request.
+ final FillRequest fillRequest2 = sReplier.getNextFillRequest();
+ assertThat(fillRequest2.flags).isEqualTo(FLAG_MANUAL_REQUEST);
+ assertValue(fillRequest2.structure, ID_USERNAME, "dude");
+ assertTextIsSanitized(fillRequest2.structure, ID_PASSWORD);
+
+ // Should have been automatically filled.
+ sUiBot.assertNoDatasets();
+
+ // Check the results.
+ mActivity.assertAutoFilled();
+ }
+
+ @Test
public void testCommitMultipleTimes() throws Throwable {
// Set service.
enableService();
@@ -1585,7 +1963,7 @@
// Set expectations.
sReplier.addResponse(new CannedFillResponse.Builder()
- .setAuthentication(sender)
+ .setAuthentication(sender, ID_USERNAME)
.setPresentation(presentation)
.build());
@@ -1678,7 +2056,7 @@
sReplier.addResponse(new CannedFillResponse.Builder().setExtras(clientState)
.setPresentation(createPresentation("authentication"))
- .setAuthentication(authentication)
+ .setAuthentication(authentication, ID_USERNAME)
.build());
// Trigger autofill.
diff --git a/tests/autofillservice/src/android/autofillservice/cts/OneTimeSettingsListener.java b/tests/autofillservice/src/android/autofillservice/cts/OneTimeSettingsListener.java
new file mode 100644
index 0000000..e723357
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/OneTimeSettingsListener.java
@@ -0,0 +1,51 @@
+package android.autofillservice.cts;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
+import android.provider.Settings;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Helper used to block tests until a secure settings value has been updated.
+ */
+final class OneTimeSettingsListener extends ContentObserver {
+ private final CountDownLatch mLatch = new CountDownLatch(1);
+ private final ContentResolver mResolver;
+ private final String mKey;
+
+ public OneTimeSettingsListener(Context context, String key) {
+ super(new Handler(Looper.getMainLooper()));
+ mKey = key;
+ mResolver = context.getContentResolver();
+ mResolver.registerContentObserver(Settings.Secure.getUriFor(key), false, this);
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ mResolver.unregisterContentObserver(this);
+ mLatch.countDown();
+ }
+
+ /**
+ * Blocks for a few seconds until it's called.
+ *
+ * @throws IllegalStateException if it's not called.
+ */
+ void assertCalled() {
+ try {
+ final boolean updated = mLatch.await(5, TimeUnit.SECONDS);
+ if (!updated) {
+ throw new IllegalStateException("Settings " + mKey + " not called in 5s");
+ }
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ throw new IllegalStateException("Interrupted", e);
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/autofillservice/src/android/autofillservice/cts/PartitionedActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/PartitionedActivityTest.java
index 19f6b80..2a3bee1 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/PartitionedActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/PartitionedActivityTest.java
@@ -35,13 +35,18 @@
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
+import android.app.PendingIntent;
import android.app.assist.AssistStructure.ViewNode;
import android.autofillservice.cts.CannedFillResponse.CannedDataset;
import android.autofillservice.cts.GridActivity.FillExpectation;
import android.autofillservice.cts.InstrumentedAutoFillService.FillRequest;
import android.autofillservice.cts.InstrumentedAutoFillService.SaveRequest;
+import android.content.Intent;
+import android.content.IntentSender;
import android.os.Bundle;
+import android.service.autofill.FillResponse;
import android.support.test.rule.ActivityTestRule;
+import android.widget.RemoteViews;
import org.junit.Before;
import org.junit.Rule;
@@ -240,7 +245,11 @@
final FillRequest fillRequest1 = sReplier.getNextFillRequest();
assertThat(fillRequest1.flags).isEqualTo(expectedFlag);
- assertTextIsSanitized(fillRequest1.structure, ID_L1C1);
+ if (manually) {
+ assertValue(fillRequest1.structure, ID_L1C1, "");
+ } else {
+ assertTextIsSanitized(fillRequest1.structure, ID_L1C1);
+ }
assertTextIsSanitized(fillRequest1.structure, ID_L1C2);
// Auto-fill it.
@@ -272,7 +281,11 @@
assertValue(fillRequest2.structure, ID_L1C1, "l1c1");
assertValue(fillRequest2.structure, ID_L1C2, "l1c2");
- assertTextIsSanitized(fillRequest2.structure, ID_L2C1);
+ if (manually) {
+ assertValue(fillRequest2.structure, ID_L2C1, "");
+ } else {
+ assertTextIsSanitized(fillRequest2.structure, ID_L2C1);
+ }
assertTextIsSanitized(fillRequest2.structure, ID_L2C2);
// Auto-fill it.
@@ -306,7 +319,11 @@
assertValue(fillRequest3.structure, ID_L1C2, "l1c2");
assertValue(fillRequest3.structure, ID_L2C1, "l2c1");
assertValue(fillRequest3.structure, ID_L2C2, "l2c2");
- assertTextIsSanitized(fillRequest3.structure, ID_L3C1);
+ if (manually) {
+ assertValue(fillRequest3.structure, ID_L3C1, "");
+ } else {
+ assertTextIsSanitized(fillRequest3.structure, ID_L3C1);
+ }
assertTextIsSanitized(fillRequest3.structure, ID_L3C2);
// Auto-fill it.
@@ -342,7 +359,11 @@
assertValue(fillRequest4.structure, ID_L2C2, "l2c2");
assertValue(fillRequest4.structure, ID_L3C1, "l3c1");
assertValue(fillRequest4.structure, ID_L3C2, "l3c2");
- assertTextIsSanitized(fillRequest4.structure, ID_L4C1);
+ if (manually) {
+ assertValue(fillRequest4.structure, ID_L4C1, "");
+ } else {
+ assertTextIsSanitized(fillRequest4.structure, ID_L4C1);
+ }
assertTextIsSanitized(fillRequest4.structure, ID_L4C2);
// Auto-fill it.
@@ -389,6 +410,7 @@
// 2nd partition - manual
// Prepare.
+ mActivity.setText(2, 1, "L2.."); // Must set before creating expectation.
final CannedFillResponse response2 = new CannedFillResponse.Builder()
.addDataset(new CannedDataset.Builder()
.setPresentation(createPresentation("Partition 2"))
@@ -408,7 +430,7 @@
assertValue(fillRequest2.structure, ID_L1C1, "l1c1");
assertValue(fillRequest2.structure, ID_L1C2, "l1c2");
- assertTextIsSanitized(fillRequest2.structure, ID_L2C1);
+ assertValue(fillRequest2.structure, ID_L2C1, "L2..");
assertTextIsSanitized(fillRequest2.structure, ID_L2C2);
// Check the results.
@@ -448,6 +470,7 @@
// 4th partition - manual
// Prepare.
+ mActivity.setText(4, 1, "L4.."); // Must set before creating expectation.
final CannedFillResponse response4 = new CannedFillResponse.Builder()
.addDataset(new CannedDataset.Builder()
.setPresentation(createPresentation("Partition 4"))
@@ -471,7 +494,7 @@
assertValue(fillRequest4.structure, ID_L2C2, "l2c2");
assertValue(fillRequest4.structure, ID_L3C1, "l3c1");
assertValue(fillRequest4.structure, ID_L3C2, "l3c2");
- assertTextIsSanitized(fillRequest4.structure, ID_L4C1);
+ assertValue(fillRequest4.structure, ID_L4C1, "L4..");
assertTextIsSanitized(fillRequest4.structure, ID_L4C2);
// Check the results.
@@ -916,4 +939,557 @@
mActivity.focusCell(2, 2);
sUiBot.assertNoDatasets();
}
+
+ /**
+ * Tests scenario where each partition has more than one dataset, but they don't overlap, i.e.,
+ * each {@link FillResponse} only contain fields within the partition.
+ */
+ @Test
+ public void testAutofillMultipleDatasetsNoOverlap() throws Exception {
+ // Set service.
+ enableService();
+
+ /**
+ * 1st partition.
+ */
+ // Set expectations.
+ final CannedFillResponse response1 = new CannedFillResponse.Builder()
+ .addDataset(new CannedDataset.Builder()
+ .setPresentation(createPresentation("P1D1"))
+ .setField(ID_L1C1, "l1c1")
+ .setField(ID_L1C2, "l1c2")
+ .build())
+ .addDataset(new CannedDataset.Builder()
+ .setPresentation(createPresentation("P1D2"))
+ .setField(ID_L1C1, "L1C1")
+ .build())
+ .build();
+ sReplier.addResponse(response1);
+ final FillExpectation expectation1 = mActivity.expectAutofill()
+ .onCell(1, 1, "l1c1")
+ .onCell(1, 2, "l1c2");
+
+ // Trigger partition.
+ mActivity.focusCell(1, 1);
+ sReplier.getNextFillRequest();
+
+
+ /**
+ * 2nd partition.
+ */
+ // Set expectations.
+ final CannedFillResponse response2 = new CannedFillResponse.Builder()
+ .addDataset(new CannedDataset.Builder()
+ .setPresentation(createPresentation("P2D1"))
+ .setField(ID_L2C1, "l2c1")
+ .setField(ID_L2C2, "l2c2")
+ .build())
+ .addDataset(new CannedDataset.Builder()
+ .setPresentation(createPresentation("P2D2"))
+ .setField(ID_L2C2, "L2C2")
+ .build())
+ .build();
+ sReplier.addResponse(response2);
+ final FillExpectation expectation2 = mActivity.expectAutofill()
+ .onCell(2, 2, "L2C2");
+
+ // Trigger partition.
+ mActivity.focusCell(2, 1);
+ sReplier.getNextFillRequest();
+
+ /**
+ * 3rd partition.
+ */
+ // Set expectations.
+ final CannedFillResponse response3 = new CannedFillResponse.Builder()
+ .addDataset(new CannedDataset.Builder()
+ .setPresentation(createPresentation("P3D1"))
+ .setField(ID_L3C1, "l3c1")
+ .setField(ID_L3C2, "l3c2")
+ .build())
+ .addDataset(new CannedDataset.Builder()
+ .setPresentation(createPresentation("P3D2"))
+ .setField(ID_L3C1, "L3C1")
+ .setField(ID_L3C2, "L3C2")
+ .build())
+ .build();
+ sReplier.addResponse(response3);
+ final FillExpectation expectation3 = mActivity.expectAutofill()
+ .onCell(3, 1, "L3C1")
+ .onCell(3, 2, "L3C2");
+
+ // Trigger partition.
+ mActivity.focusCell(3, 1);
+ sReplier.getNextFillRequest();
+
+ /**
+ * 4th partition.
+ */
+ // Set expectations.
+ final CannedFillResponse response4 = new CannedFillResponse.Builder()
+ .addDataset(new CannedDataset.Builder()
+ .setPresentation(createPresentation("P4D1"))
+ .setField(ID_L4C1, "l4c1")
+ .build())
+ .addDataset(new CannedDataset.Builder()
+ .setPresentation(createPresentation("P4D2"))
+ .setField(ID_L4C1, "L4C1")
+ .setField(ID_L4C2, "L4C2")
+ .build())
+ .build();
+ sReplier.addResponse(response4);
+ final FillExpectation expectation4 = mActivity.expectAutofill()
+ .onCell(4, 1, "l4c1");
+
+ // Trigger partition.
+ mActivity.focusCell(4, 1);
+ sReplier.getNextFillRequest();
+
+ /*
+ * Now move focus around to make sure the proper values are displayed each time.
+ */
+ mActivity.focusCell(1, 1);
+ sUiBot.assertDatasets("P1D1", "P1D2");
+ mActivity.focusCell(1, 2);
+ sUiBot.assertDatasets("P1D1");
+
+ mActivity.focusCell(2, 1);
+ sUiBot.assertDatasets("P2D1");
+ mActivity.focusCell(2, 2);
+ sUiBot.assertDatasets("P2D1", "P2D2");
+
+ mActivity.focusCell(4, 1);
+ sUiBot.assertDatasets("P4D1", "P4D2");
+ mActivity.focusCell(4, 2);
+ sUiBot.assertDatasets("P4D2");
+
+ mActivity.focusCell(3, 2);
+ sUiBot.assertDatasets("P3D1", "P3D2");
+ mActivity.focusCell(3, 1);
+ sUiBot.assertDatasets("P3D1", "P3D2");
+
+ /*
+ * Finally, autofill and check results.
+ */
+ mActivity.focusCell(4, 1);
+ sUiBot.selectDataset("P4D1");
+ expectation4.assertAutoFilled();
+
+ mActivity.focusCell(1, 1);
+ sUiBot.selectDataset("P1D1");
+ expectation1.assertAutoFilled();
+
+ mActivity.focusCell(3, 1);
+ sUiBot.selectDataset("P3D2");
+ expectation3.assertAutoFilled();
+
+ mActivity.focusCell(2, 2);
+ sUiBot.selectDataset("P2D2");
+ expectation2.assertAutoFilled();
+ }
+
+ /**
+ * Tests scenario where each partition has more than one dataset, but they overlap, i.e.,
+ * some fields are present in more than one partition.
+ *
+ * <p>Whenever a new partition defines a field previously present in another partittion, that
+ * partition will "own" that field.
+ *
+ * <p>In the end, 4th partition will one all fields in 2 datasets; and this test cases picks
+ * the first.
+ */
+ @Test
+ public void testAutofillMultipleDatasetsOverlappingPicksFirst() throws Exception {
+ autofillMultipleDatasetsOverlapping(true);
+ }
+
+ /**
+ * Tests scenario where each partition has more than one dataset, but they overlap, i.e.,
+ * some fields are present in more than one partition.
+ *
+ * <p>Whenever a new partition defines a field previously present in another partittion, that
+ * partition will "own" that field.
+ *
+ * <p>In the end, 4th partition will one all fields in 2 datasets; and this test cases picks
+ * the second.
+ */
+ @Test
+ public void testAutofillMultipleDatasetsOverlappingPicksSecond() throws Exception {
+ autofillMultipleDatasetsOverlapping(false);
+ }
+
+ private void autofillMultipleDatasetsOverlapping(boolean pickFirst) throws Exception {
+ // Set service.
+ enableService();
+
+ /**
+ * 1st partition.
+ */
+ // Set expectations.
+ final CannedFillResponse response1 = new CannedFillResponse.Builder()
+ .addDataset(new CannedDataset.Builder()
+ .setPresentation(createPresentation("P1D1"))
+ .setField(ID_L1C1, "l1c1")
+ .setField(ID_L1C2, "l1c2")
+ .build())
+ .addDataset(new CannedDataset.Builder()
+ .setPresentation(createPresentation("P1D2"))
+ .setField(ID_L1C1, "L1C1")
+ .build())
+ .build();
+ sReplier.addResponse(response1);
+
+ // Trigger partition.
+ mActivity.focusCell(1, 1);
+ sReplier.getNextFillRequest();
+
+ // Asserts proper datasets are shown on each field defined so far.
+ mActivity.focusCell(1, 1);
+ sUiBot.assertDatasets("P1D1", "P1D2");
+ mActivity.focusCell(1, 2);
+ sUiBot.assertDatasets("P1D1");
+
+ /**
+ * 2nd partition.
+ */
+ // Set expectations.
+ final CannedFillResponse response2 = new CannedFillResponse.Builder()
+ .addDataset(new CannedDataset.Builder()
+ .setPresentation(createPresentation("P2D1"))
+ .setField(ID_L1C1, "l1c1") // from previous partition
+ .setField(ID_L2C1, "l2c1")
+ .setField(ID_L2C2, "l2c2")
+ .build())
+ .addDataset(new CannedDataset.Builder()
+ .setPresentation(createPresentation("P2D2"))
+ .setField(ID_L2C2, "L2C2")
+ .build())
+ .build();
+ sReplier.addResponse(response2);
+
+ // Trigger partition.
+ mActivity.focusCell(2, 1);
+ sReplier.getNextFillRequest();
+
+ // Asserts proper datasets are shown on each field defined so far.
+ mActivity.focusCell(1, 1);
+ sUiBot.assertDatasets("P2D1"); // changed
+ mActivity.focusCell(1, 2);
+ sUiBot.assertDatasets("P1D1");
+ mActivity.focusCell(2, 1);
+ sUiBot.assertDatasets("P2D1");
+ mActivity.focusCell(2, 2);
+ sUiBot.assertDatasets("P2D1", "P2D2");
+
+ /**
+ * 3rd partition.
+ */
+ // Set expectations.
+ final CannedFillResponse response3 = new CannedFillResponse.Builder()
+ .addDataset(new CannedDataset.Builder()
+ .setPresentation(createPresentation("P3D1"))
+ .setField(ID_L1C2, "l1c2")
+ .setField(ID_L3C1, "l3c1")
+ .setField(ID_L3C2, "l3c2")
+ .build())
+ .addDataset(new CannedDataset.Builder()
+ .setPresentation(createPresentation("P3D2"))
+ .setField(ID_L2C2, "l2c2")
+ .setField(ID_L3C1, "L3C1")
+ .setField(ID_L3C2, "L3C2")
+ .build())
+ .build();
+ sReplier.addResponse(response3);
+
+ // Trigger partition.
+ mActivity.focusCell(3, 1);
+ sReplier.getNextFillRequest();
+
+ // Asserts proper datasets are shown on each field defined so far.
+ mActivity.focusCell(1, 1);
+ sUiBot.assertDatasets("P2D1");
+ mActivity.focusCell(1, 2);
+ sUiBot.assertDatasets("P3D1"); // changed
+ mActivity.focusCell(2, 1);
+ sUiBot.assertDatasets("P2D1");
+ mActivity.focusCell(2, 2);
+ sUiBot.assertDatasets("P3D2"); // changed
+ mActivity.focusCell(3, 2);
+ sUiBot.assertDatasets("P3D1", "P3D2");
+ mActivity.focusCell(3, 1);
+ sUiBot.assertDatasets("P3D1", "P3D2");
+
+ /**
+ * 4th partition.
+ */
+ // Set expectations.
+ final CannedFillResponse response4 = new CannedFillResponse.Builder()
+ .addDataset(new CannedDataset.Builder()
+ .setPresentation(createPresentation("P4D1"))
+ .setField(ID_L1C1, "l1c1")
+ .setField(ID_L1C2, "l1c2")
+ .setField(ID_L2C1, "l2c1")
+ .setField(ID_L2C2, "l2c2")
+ .setField(ID_L3C1, "l3c1")
+ .setField(ID_L3C2, "l3c2")
+ .setField(ID_L4C1, "l4c1")
+ .build())
+ .addDataset(new CannedDataset.Builder()
+ .setPresentation(createPresentation("P4D2"))
+ .setField(ID_L1C1, "L1C1")
+ .setField(ID_L1C2, "L1C2")
+ .setField(ID_L2C1, "L2C1")
+ .setField(ID_L2C2, "L2C2")
+ .setField(ID_L3C1, "L3C1")
+ .setField(ID_L3C2, "L3C2")
+ .setField(ID_L1C1, "L1C1")
+ .setField(ID_L4C1, "L4C1")
+ .setField(ID_L4C2, "L4C2")
+ .build())
+ .build();
+ sReplier.addResponse(response4);
+
+ // Trigger partition.
+ mActivity.focusCell(4, 1);
+ sReplier.getNextFillRequest();
+
+ // Asserts proper datasets are shown on each field defined so far.
+ mActivity.focusCell(1, 1);
+ sUiBot.assertDatasets("P4D1", "P4D2");
+ mActivity.focusCell(1, 2);
+ sUiBot.assertDatasets("P4D1", "P4D2");
+ mActivity.focusCell(2, 1);
+ sUiBot.assertDatasets("P4D1", "P4D2");
+ mActivity.focusCell(2, 2);
+ sUiBot.assertDatasets("P4D1", "P4D2");
+ mActivity.focusCell(3, 2);
+ sUiBot.assertDatasets("P4D1", "P4D2");
+ mActivity.focusCell(3, 1);
+ sUiBot.assertDatasets("P4D1", "P4D2");
+ mActivity.focusCell(4, 1);
+ sUiBot.assertDatasets("P4D1", "P4D2");
+ mActivity.focusCell(4, 2);
+ sUiBot.assertDatasets("P4D2");
+
+ /*
+ * Finally, autofill and check results.
+ */
+ final FillExpectation expectation = mActivity.expectAutofill();
+ final String chosenOne;
+ if (pickFirst) {
+ expectation
+ .onCell(1, 1, "l1c1")
+ .onCell(1, 2, "l1c2")
+ .onCell(2, 1, "l2c1")
+ .onCell(2, 2, "l2c2")
+ .onCell(3, 1, "l3c1")
+ .onCell(3, 2, "l3c2")
+ .onCell(4, 1, "l4c1");
+ chosenOne = "P4D1";
+ } else {
+ expectation
+ .onCell(1, 1, "L1C1")
+ .onCell(1, 2, "L1C2")
+ .onCell(2, 1, "L2C1")
+ .onCell(2, 2, "L2C2")
+ .onCell(3, 1, "L3C1")
+ .onCell(3, 2, "L3C2")
+ .onCell(4, 1, "L4C1")
+ .onCell(4, 2, "L4C2");
+ chosenOne = "P4D2";
+ }
+
+ mActivity.focusCell(4, 1);
+ sUiBot.selectDataset(chosenOne);
+ expectation.assertAutoFilled();
+ }
+
+ @Test
+ public void testAutofillMultipleAuthDatasetsInSequence() throws Exception {
+ // Set service.
+ enableService();
+
+ // TODO: current API requires these fields...
+ final RemoteViews bogusPresentation = createPresentation("Whatever man, I'm not used...");
+ final String bogusValue = "Y U REQUIRE IT?";
+
+ /**
+ * 1st partition.
+ */
+ // Set expectations.
+ final IntentSender auth11 = AuthenticationActivity.createSender(getContext(), 11,
+ new CannedDataset.Builder()
+ .setField(ID_L1C1, "l1c1")
+ .setField(ID_L1C2, "l1c2")
+ .setPresentation(bogusPresentation)
+ .build());
+ final IntentSender auth12 = AuthenticationActivity.createSender(getContext(), 12);
+ final CannedFillResponse response1 = new CannedFillResponse.Builder()
+ .addDataset(new CannedDataset.Builder()
+ .setAuthentication(auth11)
+ .setField(ID_L1C1, bogusValue)
+ .setField(ID_L1C2, bogusValue)
+ .setPresentation(createPresentation("P1D1"))
+ .build())
+ .addDataset(new CannedDataset.Builder()
+ .setAuthentication(auth12)
+ .setField(ID_L1C1, bogusValue)
+ .setPresentation(createPresentation("P1D2"))
+ .build())
+ .build();
+ sReplier.addResponse(response1);
+ final FillExpectation expectation1 = mActivity.expectAutofill()
+ .onCell(1, 1, "l1c1")
+ .onCell(1, 2, "l1c2");
+
+ // Trigger partition.
+ mActivity.focusCell(1, 1);
+ sReplier.getNextFillRequest();
+
+ // Focus around different fields in the partition.
+ sUiBot.assertDatasets("P1D1", "P1D2");
+ mActivity.focusCell(1, 2);
+ sUiBot.assertDatasets("P1D1");
+
+ // Autofill it...
+ sUiBot.selectDataset("P1D1");
+ // ... and assert result
+ expectation1.assertAutoFilled();
+
+
+ /**
+ * 2nd partition.
+ */
+ // Set expectations.
+ final IntentSender auth21 = AuthenticationActivity.createSender(getContext(), 21, null);
+ final IntentSender auth22 = AuthenticationActivity.createSender(getContext(), 22,
+ new CannedDataset.Builder()
+ .setField(ID_L2C2, "L2C2")
+ .setPresentation(bogusPresentation)
+ .build());
+ final CannedFillResponse response2 = new CannedFillResponse.Builder()
+ .addDataset(new CannedDataset.Builder()
+ .setAuthentication(auth21)
+ .setPresentation(createPresentation("P2D1"))
+ .setField(ID_L2C1, bogusValue)
+ .setField(ID_L2C2, bogusValue)
+ .build())
+ .addDataset(new CannedDataset.Builder()
+ .setAuthentication(auth22)
+ .setPresentation(createPresentation("P2D2"))
+ .setField(ID_L2C2, bogusValue)
+ .build())
+ .build();
+ sReplier.addResponse(response2);
+ final FillExpectation expectation2 = mActivity.expectAutofill()
+ .onCell(2, 2, "L2C2");
+
+
+ // Trigger partition.
+ mActivity.focusCell(2, 1);
+ sReplier.getNextFillRequest();
+
+ // Focus around different fields in the partition.
+ sUiBot.assertDatasets("P2D1");
+ mActivity.focusCell(2, 2);
+ sUiBot.assertDatasets("P2D1", "P2D2");
+
+ // Autofill it...
+ sUiBot.selectDataset("P2D2");
+ // ... and assert result
+ expectation2.assertAutoFilled();
+
+ /**
+ * 3rd partition.
+ */
+ // Set expectations.
+ final IntentSender auth31 = AuthenticationActivity.createSender(getContext(), 31,
+ new CannedDataset.Builder()
+ .setField(ID_L3C1, "l3c1")
+ .setField(ID_L3C2, "l3c2")
+ .setPresentation(bogusPresentation)
+ .build());
+ final IntentSender auth32 = AuthenticationActivity.createSender(getContext(), 32);
+ final CannedFillResponse response3 = new CannedFillResponse.Builder()
+ .addDataset(new CannedDataset.Builder()
+ .setAuthentication(auth31)
+ .setPresentation(createPresentation("P3D1"))
+ .setField(ID_L3C1, bogusValue)
+ .setField(ID_L3C2, bogusValue)
+ .build())
+ .addDataset(new CannedDataset.Builder()
+ .setAuthentication(auth32)
+ .setPresentation(createPresentation("P3D2"))
+ .setField(ID_L3C1, bogusValue)
+ .setField(ID_L3C2, bogusValue)
+ .build())
+ .build();
+ sReplier.addResponse(response3);
+ final FillExpectation expectation3 = mActivity.expectAutofill()
+ .onCell(3, 1, "l3c1")
+ .onCell(3, 2, "l3c2");
+
+ // Trigger partition.
+ mActivity.focusCell(3, 2);
+ sReplier.getNextFillRequest();
+
+ // Focus around different fields in the partition.
+ sUiBot.assertDatasets("P3D1", "P3D2");
+ mActivity.focusCell(3, 1);
+ sUiBot.assertDatasets("P3D1", "P3D2");
+
+ // Autofill it...
+ sUiBot.selectDataset("P3D1");
+ // ... and assert result
+ expectation3.assertAutoFilled();
+
+ /**
+ * 4th partition.
+ */
+ // Set expectations.
+ final IntentSender auth41 = AuthenticationActivity.createSender(getContext(), 41, null);
+ final IntentSender auth42 = AuthenticationActivity.createSender(getContext(), 42,
+ new CannedDataset.Builder()
+ .setField(ID_L4C1, "L4C1")
+ .setField(ID_L4C2, "L4C2")
+ .setPresentation(bogusPresentation)
+ .build());
+ final CannedFillResponse response4 = new CannedFillResponse.Builder()
+ .addDataset(new CannedDataset.Builder()
+ .setAuthentication(auth41)
+ .setPresentation(createPresentation("P4D1"))
+ .setField(ID_L4C1, bogusValue)
+ .build())
+ .addDataset(new CannedDataset.Builder()
+ .setAuthentication(auth42)
+ .setPresentation(createPresentation("P4D2"))
+ .setField(ID_L4C1, bogusValue)
+ .setField(ID_L4C2, bogusValue)
+ .build())
+ .build();
+ sReplier.addResponse(response4);
+ final FillExpectation expectation4 = mActivity.expectAutofill()
+ .onCell(4, 1, "L4C1")
+ .onCell(4, 2, "L4C2");
+
+ // Trigger partition.
+ mActivity.focusCell(4, 1);
+ sReplier.getNextFillRequest();
+
+ // Focus around different fields in the partition.
+ sUiBot.assertDatasets("P4D1", "P4D2");
+ mActivity.focusCell(4, 2);
+ sUiBot.assertDatasets("P4D2");
+
+ // Autofill it...
+ sUiBot.selectDataset("P4D2");
+ // ... and assert result
+ expectation4.assertAutoFilled();
+
+ }
+
+ // TODO(b/37424539): more scenarios:
+ // - overlapping instead of sequence
+ // - mixed of auth / no auth
+ // - overlapping 1st/2nd cases
}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/SessionLifecycleTest.java b/tests/autofillservice/src/android/autofillservice/cts/SessionLifecycleTest.java
index 0454277..19b7e46 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/SessionLifecycleTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/SessionLifecycleTest.java
@@ -38,12 +38,11 @@
import android.content.Intent;
import android.content.IntentSender;
import android.os.Bundle;
-import android.support.test.rule.ActivityTestRule;
+import android.os.SystemClock;
import android.view.autofill.AutofillValue;
import org.junit.After;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
/**
@@ -54,14 +53,7 @@
private static final String PASSWORD_FULL_ID = "android.autofillservice.cts:id/" + ID_PASSWORD;
private static final String LOGIN_FULL_ID = "android.autofillservice.cts:id/" + ID_LOGIN;
private static final String BUTTON_FULL_ID = "android.autofillservice.cts:id/button";
-
- /**
- * Use an activity as background so that orientation change always works (Home screen does not
- * allow rotation
- */
- @Rule
- public final ActivityTestRule<EmptyActivity> mActivityRule =
- new ActivityTestRule<>(EmptyActivity.class);
+ private static final String CANCEL_FULL_ID = "android.autofillservice.cts:id/cancel";
@Before
public void removeAllSessions() {
@@ -84,8 +76,20 @@
Helper.allowAutoRotation();
}
+ private void killOfProcessLoginActivityProcess() throws Exception {
+ // Waiting for activity to stop (stop marker appears)
+ eventually(() -> assertThat(getStoppedMarker(getContext()).exists()).isTrue());
+
+ // onStop might not be finished, hence wait more
+ SystemClock.sleep(1000);
+
+ // Kill activity that is in the background
+ runShellCommand("kill -9 %d",
+ getOutOfProcessPid("android.autofillservice.cts.outside"));
+ }
+
@Test
- public void testSessionRetainedWhileAutofilledAppIsLifecycled() throws Exception {
+ public void testDatasetAuthResponseWhileAutofilledAppIsLifecycled() throws Exception {
// Set service.
enableService();
@@ -113,7 +117,7 @@
.setExtras(extras).build());
CannedFillResponse response = new CannedFillResponse.Builder()
- .setAuthentication(authentication)
+ .setAuthentication(authentication, ID_USERNAME, ID_PASSWORD)
.setPresentation(createPresentation("authenticate"))
.build();
sReplier.addResponse(response);
@@ -125,7 +129,7 @@
sReplier.getNextFillRequest();
// Wait until authentication is shown
- sUiBot.assertShownByText("authenticate");
+ sUiBot.assertDatasets("authenticate");
// Change orientation which triggers a destroy -> create in the app as the activity
// cannot deal with such situations
@@ -135,17 +139,10 @@
getStoppedMarker(getContext()).delete();
// Authenticate
- sUiBot.selectByText("authenticate");
-
- // Waiting for activity to stop (stop marker appears)
- eventually(() -> assertThat(getStoppedMarker(getContext()).exists()).isTrue());
-
- // onStop might not be finished, hence wait more
- Thread.sleep(1000);
+ sUiBot.selectDataset("authenticate");
// Kill activity that is in the background
- runShellCommand("kill -9 %d",
- getOutOfProcessPid("android.autofillservice.cts.outside"));
+ killOfProcessLoginActivityProcess();
// Change orientation which triggers a destroy -> create in the app as the activity
// cannot deal with such situations
@@ -155,7 +152,7 @@
sUiBot.selectById(BUTTON_FULL_ID);
// Wait for dataset to be shown
- sUiBot.assertShownByText("dataset");
+ sUiBot.assertDatasets("dataset");
// Change orientation which triggers a destroy -> create in the app as the activity
// cannot deal with such situations
@@ -205,4 +202,169 @@
disableService();
}
}
+
+ @Test
+ public void testAuthCanceledWhileAutofilledAppIsLifecycled() throws Exception {
+ // Set service.
+ enableService();
+
+ try {
+ // Start activity that is autofilled in a separate process so it can be killed
+ Intent outOfProcessAcvitityStartIntent = new Intent(getContext(),
+ OutOfProcessLoginActivity.class);
+ getContext().startActivity(outOfProcessAcvitityStartIntent);
+
+ // Create the authentication intent (launching a full screen activity)
+ IntentSender authentication = PendingIntent.getActivity(getContext(), 0,
+ new Intent(getContext(), ManualAuthenticationActivity.class),
+ 0).getIntentSender();
+
+ CannedFillResponse response = new CannedFillResponse.Builder()
+ .setAuthentication(authentication, ID_USERNAME, ID_PASSWORD)
+ .setPresentation(createPresentation("authenticate"))
+ .build();
+ sReplier.addResponse(response);
+
+ // Trigger autofill on username
+ sUiBot.selectById(USERNAME_FULL_ID);
+
+ // Wait for fill request to be processed
+ sReplier.getNextFillRequest();
+
+ // Wait until authentication is shown
+ sUiBot.assertDatasets("authenticate");
+
+ // Delete stopped marker
+ getStoppedMarker(getContext()).delete();
+
+ // Authenticate
+ sUiBot.selectDataset("authenticate");
+
+ // Kill activity that is in the background
+ killOfProcessLoginActivityProcess();
+
+ // Cancel authentication activity
+ sUiBot.pressBack();
+
+ // Authentication should still be shown
+ sUiBot.assertDatasets("authenticate");
+ } finally {
+ disableService();
+ }
+ }
+
+ @Test
+ public void testDatasetVisibleWhileAutofilledAppIsLifecycled() throws Exception {
+ // Set service.
+ enableService();
+
+ try {
+ // Start activity that is autofilled in a separate process so it can be killed
+ Intent outOfProcessAcvitityStartIntent = new Intent(getContext(),
+ OutOfProcessLoginActivity.class);
+ getContext().startActivity(outOfProcessAcvitityStartIntent);
+
+ CannedFillResponse response = new CannedFillResponse.Builder()
+ .addDataset(new CannedFillResponse.CannedDataset.Builder(
+ createPresentation("dataset"))
+ .setField(ID_USERNAME, "filled").build())
+ .build();
+ sReplier.addResponse(response);
+
+ // Trigger autofill on username
+ sUiBot.selectById(USERNAME_FULL_ID);
+
+ // Wait for fill request to be processed
+ sReplier.getNextFillRequest();
+
+ // Wait until dataset is shown
+ sUiBot.assertDatasets("dataset");
+
+ // Delete stopped marker
+ getStoppedMarker(getContext()).delete();
+
+ // Start an activity on top of the autofilled activity
+ Intent intent = new Intent(getContext(), EmptyActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+ getContext().startActivity(intent);
+
+ // Kill activity that is in the background
+ killOfProcessLoginActivityProcess();
+
+ // Cancel activity on top
+ sUiBot.pressBack();
+
+ // Dataset should still be shown
+ sUiBot.assertDatasets("dataset");
+ } finally {
+ disableService();
+ }
+ }
+
+ @Test
+ public void testAutofillNestedActivitiesWhileAutofilledAppIsLifecycled() throws Exception {
+ // Set service.
+ enableService();
+
+ try {
+ // Start activity that is autofilled in a separate process so it can be killed
+ Intent outOfProcessAcvitityStartIntent = new Intent(getContext(),
+ OutOfProcessLoginActivity.class);
+ getContext().startActivity(outOfProcessAcvitityStartIntent);
+
+ // Prepare response for first activity
+ CannedFillResponse response = new CannedFillResponse.Builder()
+ .addDataset(new CannedFillResponse.CannedDataset.Builder(
+ createPresentation("dataset1"))
+ .setField(ID_USERNAME, "filled").build())
+ .build();
+ sReplier.addResponse(response);
+
+ // Trigger autofill on username
+ sUiBot.selectById(USERNAME_FULL_ID);
+
+ // Wait for fill request to be processed
+ sReplier.getNextFillRequest();
+
+ // Wait until dataset1 is shown
+ sUiBot.assertDatasets("dataset1");
+
+ // Delete stopped marker
+ getStoppedMarker(getContext()).delete();
+
+ // Prepare response for nested activity
+ response = new CannedFillResponse.Builder()
+ .addDataset(new CannedFillResponse.CannedDataset.Builder(
+ createPresentation("dataset2"))
+ .setField(ID_USERNAME, "filled").build())
+ .build();
+ sReplier.addResponse(response);
+
+ // Start nested login activity
+ Intent intent = new Intent(getContext(), LoginActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ getContext().startActivity(intent);
+
+ // Kill activity that is in the background
+ killOfProcessLoginActivityProcess();
+
+ // Trigger autofill on username in nested activity
+ sUiBot.selectById(USERNAME_FULL_ID);
+
+ // Wait for fill request to be processed
+ sReplier.getNextFillRequest();
+
+ // Wait until dataset in nested activity is shown
+ sUiBot.assertDatasets("dataset2");
+
+ // Tap "Cancel".
+ sUiBot.selectById(CANCEL_FULL_ID);
+
+ // Dataset should still be shown
+ sUiBot.assertDatasets("dataset1");
+ } finally {
+ disableService();
+ }
+ }
}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/UiBot.java b/tests/autofillservice/src/android/autofillservice/cts/UiBot.java
index fa7780a..eba7a3f 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/UiBot.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/UiBot.java
@@ -16,7 +16,9 @@
package android.autofillservice.cts;
+import static android.autofillservice.cts.Helper.NOT_SHOWING_TIMEOUT_MS;
import static android.autofillservice.cts.Helper.SAVE_TIMEOUT_MS;
+import static android.autofillservice.cts.Helper.UI_TIMEOUT_MS;
import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_ADDRESS;
import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_CREDIT_CARD;
import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_EMAIL_ADDRESS;
@@ -73,13 +75,11 @@
private static final String TAG = "AutoFillCtsUiBot";
private final UiDevice mDevice;
- private final long mTimeout;
private final String mPackageName;
private final UiAutomation mAutoman;
- UiBot(Instrumentation instrumentation, long timeout) throws Exception {
+ UiBot(Instrumentation instrumentation) throws Exception {
mDevice = UiDevice.getInstance(instrumentation);
- mTimeout = timeout;
mPackageName = instrumentation.getContext().getPackageName();
mAutoman = instrumentation.getUiAutomation();
}
@@ -88,14 +88,16 @@
* Asserts the dataset chooser is not shown.
*/
void assertNoDatasets() {
- final UiObject2 ui;
+ final UiObject2 picker;
try {
- ui = findDatasetPicker();
+ picker = findDatasetPicker(NOT_SHOWING_TIMEOUT_MS);
} catch (Throwable t) {
// Use a more elegant check than catching the expection because it's not showing...
return;
}
- throw new RetryableException("floating ui is shown: %s", ui);
+ final StringBuilder error = new StringBuilder("Should not be showing datasets, but got [ ");
+ getAllText(picker, error);
+ throw new RetryableException(error.append("]").toString());
}
/**
@@ -107,12 +109,31 @@
final UiObject2 picker = findDatasetPicker();
for (String name : names) {
final UiObject2 dataset = picker.findObject(By.text(name));
- assertWithMessage("no dataset named %s", name).that(dataset).isNotNull();
+ if (dataset == null) {
+ final StringBuilder error = new StringBuilder("no dataset named ").append(name)
+ .append(" on [ ");
+ getAllText(picker, error);
+ throw new AssertionError(error.append("]").toString()); // not retryable
+
+ }
}
return picker;
}
/**
+ * Gets the text from an object and all its descendants.
+ */
+ private static void getAllText(UiObject2 object, StringBuilder builder) {
+ final String text = object.getText();
+ if (text != null) {
+ builder.append(text).append(' ');
+ }
+ for (UiObject2 child : object.getChildren()) {
+ getAllText(child, builder);
+ }
+ }
+
+ /**
* Selects a dataset that should be visible in the floating UI.
*/
void selectDataset(String name) {
@@ -152,7 +173,12 @@
*/
public void assertNotShownByText(String text) {
final UiObject2 uiObject = mDevice.findObject(By.text(text));
- assertWithMessage(text).that(uiObject).isNull();
+ if (uiObject != null) {
+ final StringBuilder error = new StringBuilder("Should not find object with text '")
+ .append(text).append("', but found: ");
+ getAllText(uiObject, error);
+ throw new AssertionError(error.toString()); // don't retry
+ }
}
/**
@@ -201,7 +227,14 @@
* Asserts the save snackbar is showing and returns it.
*/
UiObject2 assertSaveShowing(int type) {
- return assertSaveShowing(null, type);
+ return assertSaveShowing(SAVE_TIMEOUT_MS, type);
+ }
+
+ /**
+ * Asserts the save snackbar is showing and returns it.
+ */
+ UiObject2 assertSaveShowing(long timeout, int type) {
+ return assertSaveShowing(null, timeout, type);
}
/**
@@ -216,7 +249,7 @@
*/
void assertSaveNotShowing(int type) {
try {
- assertSaveShowing(type);
+ assertSaveShowing(NOT_SHOWING_TIMEOUT_MS, type);
} catch (Throwable t) {
// TODO: use a more elegant check than catching the expection because it's not showing
// (in which case it wouldn't need a type as parameter).
@@ -250,12 +283,24 @@
}
UiObject2 assertSaveShowing(String description, int... types) {
- return assertSaveShowing(SaveInfo.NEGATIVE_BUTTON_STYLE_CANCEL, description, types);
+ return assertSaveShowing(SaveInfo.NEGATIVE_BUTTON_STYLE_CANCEL, description,
+ SAVE_TIMEOUT_MS, types);
}
- UiObject2 assertSaveShowing(int negativeButtonStyle, String description, int... types) {
+ UiObject2 assertSaveShowing(String description, long timeout, int... types) {
+ return assertSaveShowing(SaveInfo.NEGATIVE_BUTTON_STYLE_CANCEL, description, timeout,
+ types);
+ }
+
+ UiObject2 assertSaveShowing(int negativeButtonStyle, String description,
+ int... types) {
+ return assertSaveShowing(negativeButtonStyle, description, SAVE_TIMEOUT_MS, types);
+ }
+
+ UiObject2 assertSaveShowing(int negativeButtonStyle, String description, long timeout,
+ int... types) {
final UiObject2 snackbar = waitForObject(By.res("android", RESOURCE_ID_SAVE_SNACKBAR),
- SAVE_TIMEOUT_MS);
+ timeout);
final UiObject2 titleView = snackbar.findObject(By.res("android", RESOURCE_ID_SAVE_TITLE));
assertWithMessage("save title (%s)", RESOURCE_ID_SAVE_TITLE).that(titleView).isNotNull();
@@ -314,7 +359,7 @@
*/
void saveForAutofill(boolean yesDoIt, int... types) {
final UiObject2 saveSnackBar = assertSaveShowing(
- SaveInfo.NEGATIVE_BUTTON_STYLE_CANCEL,null, types);
+ SaveInfo.NEGATIVE_BUTTON_STYLE_CANCEL, null, types);
saveForAutofill(saveSnackBar, yesDoIt);
}
@@ -392,7 +437,7 @@
* @param selector {@link BySelector} that identifies the object.
*/
private UiObject2 waitForObject(BySelector selector) {
- return waitForObject(selector, mTimeout);
+ return waitForObject(selector, UI_TIMEOUT_MS);
}
/**
@@ -413,7 +458,7 @@
SystemClock.sleep(napTime);
}
throw new RetryableException("Object with selector '%s' not found in %d ms",
- selector, mTimeout);
+ selector, UI_TIMEOUT_MS);
}
/**
@@ -422,7 +467,7 @@
* @param selector {@link BySelector} that identifies the object.
*/
private List<UiObject2> waitForObjects(BySelector selector) {
- return waitForObjects(selector, mTimeout);
+ return waitForObjects(selector, UI_TIMEOUT_MS);
}
/**
@@ -443,11 +488,16 @@
SystemClock.sleep(napTime);
}
throw new RetryableException("Objects with selector '%s' not found in %d ms",
- selector, mTimeout);
+ selector, UI_TIMEOUT_MS);
}
private UiObject2 findDatasetPicker() {
- final UiObject2 picker = waitForObject(By.res("android", RESOURCE_ID_DATASET_PICKER));
+ return findDatasetPicker(UI_TIMEOUT_MS);
+ }
+
+ private UiObject2 findDatasetPicker(long timeout) {
+ final UiObject2 picker = waitForObject(By.res("android", RESOURCE_ID_DATASET_PICKER),
+ timeout);
final String expectedTitle = getString(RESOURCE_STRING_DATASET_PICKER_ACCESSIBILITY_TITLE);
assertAccessibilityTitle(picker, expectedTitle);
diff --git a/tests/autofillservice/src/android/autofillservice/cts/ViewAttributesTest.java b/tests/autofillservice/src/android/autofillservice/cts/ViewAttributesTest.java
index 763c57f..31e95f1 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/ViewAttributesTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/ViewAttributesTest.java
@@ -16,19 +16,33 @@
package android.autofillservice.cts;
+import static android.autofillservice.cts.Helper.FILL_TIMEOUT_MS;
+import static android.autofillservice.cts.Helper.eventually;
+import static android.autofillservice.cts.Helper.findNodeByResourceId;
+
import static com.google.common.truth.Truth.assertThat;
+import android.app.assist.AssistStructure;
+import android.support.annotation.IdRes;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
+import android.util.Log;
import android.view.View;
+import android.view.autofill.AutofillValue;
+import android.widget.EditText;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.function.Consumer;
+
@RunWith(AndroidJUnit4.class)
public class ViewAttributesTest extends AutoFillServiceTestCase {
+ private static final String LOG_TAG = ViewAttributesTest.class.getSimpleName();
@Rule
public final ActivityTestRule<ViewAttributesTestActivity> mActivityRule =
new ActivityTestRule<>(ViewAttributesTestActivity.class);
@@ -40,38 +54,44 @@
mActivity = mActivityRule.getActivity();
}
- @Test
- public void checkTextViewNoHint() {
- assertThat(mActivity.findViewById(R.id.textViewNoHint).getAutofillHints()).isNull();
+ @Nullable private String[] getHintsFromView(@IdRes int resId) {
+ return mActivity.findViewById(resId).getAutofillHints();
+ }
+
+ private void checkEditTextNoHint(@Nullable String[] hints) {
+ assertThat(hints).isNull();
+ }
+
+ private void checkEditTextHintCustom(@Nullable String[] hints) {
+ assertThat(hints).isEqualTo(
+ new String[] {mActivity.getString(R.string.new_password_label)});
+ }
+
+ private void checkEditTextPassword(@Nullable String[] hints) {
+ assertThat(hints).isEqualTo(new String[] {View.AUTOFILL_HINT_PASSWORD});
+ }
+
+ private void checkEditTextPhoneName(@Nullable String[] hints) {
+ assertThat(hints).isEqualTo(
+ new String[] {View.AUTOFILL_HINT_PHONE, View.AUTOFILL_HINT_USERNAME});
+ }
+
+ private void checkEditTextHintsFromArray(@Nullable String[] hints) {
+ assertThat(hints).isEqualTo(new String[] {"yesterday", "today", "tomorrow", "never"});
}
@Test
- public void checkTextViewHintCustom() {
- assertThat(mActivity.findViewById(R.id.textViewHintCustom).getAutofillHints()).isEqualTo(
- new String[]{mActivity.getString(R.string.new_password_label)});
- }
-
- @Test
- public void checkTextViewPassword() {
- assertThat(mActivity.findViewById(R.id.textViewPassword).getAutofillHints()).isEqualTo(
- new String[]{View.AUTOFILL_HINT_PASSWORD});
- }
-
- @Test
- public void checkTextViewPhoneName() {
- assertThat(mActivity.findViewById(R.id.textViewPhoneName).getAutofillHints()).isEqualTo(
- new String[]{View.AUTOFILL_HINT_PHONE, View.AUTOFILL_HINT_USERNAME});
- }
-
- @Test
- public void checkTextViewHintsFromArray() {
- assertThat(mActivity.findViewById(R.id.textViewHintsFromArray).getAutofillHints()).isEqualTo(
- new String[]{"yesterday", "today", "tomorrow", "never"});
+ public void checkViewHints() {
+ checkEditTextNoHint(getHintsFromView(R.id.editTextNoHint));
+ checkEditTextHintCustom(getHintsFromView(R.id.editTextHintCustom));
+ checkEditTextPassword(getHintsFromView(R.id.editTextPassword));
+ checkEditTextPhoneName(getHintsFromView(R.id.editTextPhoneName));
+ checkEditTextHintsFromArray(getHintsFromView(R.id.editTextHintsFromArray));
}
@Test
public void checkSetAutoFill() {
- View v = mActivity.findViewById(R.id.textViewNoHint);
+ View v = mActivity.findViewById(R.id.editTextNoHint);
v.setAutofillHints(null);
assertThat(v.getAutofillHints()).isNull();
@@ -93,4 +113,177 @@
assertThat(v.getAutofillHints()).isEqualTo(new String[]{View.AUTOFILL_HINT_PASSWORD,
View.AUTOFILL_HINT_EMAIL_ADDRESS});
}
+
+ /**
+ * Wait for autofill to be initialized and trigger autofill on a view.
+ *
+ * @param view The view to trigger the autofill on
+ *
+ * @return The {@link InstrumentedAutoFillService.FillRequest triggered}
+ */
+ private InstrumentedAutoFillService.FillRequest startAutoFill(boolean forceAutofill,
+ @NonNull View view) throws Exception {
+ if (forceAutofill) {
+ mActivity.getAutofillManager().requestAutofill(view);
+ } else {
+ mActivity.syncRunOnUiThread(() -> {
+ view.clearFocus();
+ view.requestFocus();
+ });
+ }
+
+ InstrumentedAutoFillService.waitUntilConnected();
+
+ return sReplier.getNextFillRequest();
+ }
+
+ @Nullable private String[] getHintsFromStructure(@NonNull AssistStructure structure,
+ @NonNull String resName) {
+ return findNodeByResourceId(structure, resName).getAutofillHints();
+ }
+
+ private void onAssistStructure(boolean forceAutofill, @NonNull Consumer<AssistStructure> test)
+ throws Exception {
+ EditText editTextNoHint = mActivity.findViewById(R.id.editTextNoHint);
+ mActivity.syncRunOnUiThread(() -> editTextNoHint.setVisibility(View.VISIBLE));
+
+ // Set service.
+ enableService();
+ try {
+ // Set expectations.
+ sReplier.addResponse(new CannedFillResponse.CannedDataset.Builder()
+ .setField("editTextNoHint", AutofillValue.forText("filled"))
+ .setPresentation(createPresentation("dataset"))
+ .build());
+
+ // Trigger autofill.
+ InstrumentedAutoFillService.FillRequest request = startAutoFill(forceAutofill,
+ editTextNoHint);
+
+ assertThat(request.contexts.size()).isEqualTo(1);
+ test.accept(request.contexts.get(0).getStructure());
+ } finally {
+ disableService();
+ }
+ }
+
+ @Test
+ public void checkAssistStructureHints() throws Exception {
+ onAssistStructure(false, (structure) -> {
+ // Check autofill hints analogue to #checkViewHints
+ checkEditTextNoHint(getHintsFromStructure(structure, "editTextNoHint"));
+ checkEditTextHintCustom(getHintsFromStructure(structure, "editTextHintCustom"));
+ checkEditTextPassword(getHintsFromStructure(structure, "editTextPassword"));
+ checkEditTextPhoneName(getHintsFromStructure(structure, "editTextPhoneName"));
+ checkEditTextHintsFromArray(getHintsFromStructure(structure,
+ "editTextHintsFromArray"));
+ }
+ );
+ }
+
+ @Test
+ public void checkViewLocationInAssistStructure() throws Exception {
+ onAssistStructure(false, (structure) -> {
+ // check size of outerView
+ AssistStructure.ViewNode outerView = findNodeByResourceId(structure,
+ "outerView");
+
+ // The size of the view should include all paddings and size of all children
+ assertThat(outerView.getHeight()).isEqualTo(
+ 2 // outerView.top
+ + 11 // nestedView.top
+ + 23 // doubleNestedView.top
+ + 41 // tripleNestedView.height
+ + 47 // secondDoubleNestedView.height
+ + 31 // doubleNestedView.bottom
+ + 17 // nestedView.bottom
+ + 5); // outerView.bottom
+ assertThat(outerView.getWidth()).isEqualTo(
+ 7 // outerView.left
+ + 19 // nestedView.left
+ + Math.max(37 // doubleNestedView.left
+ + 43 // tripleNestedView.width
+ + 29, // doubleNestedView.right
+ 53) // secondDoubleNestedView.width
+ + 13 // nestedView.right
+ + 3); // outerView.right
+
+
+ // The nestedView is suppressed, hence the structure should be
+ //
+ // outerView
+ // doubleNestedView left=26 top=13
+ // tripleNestedView left=37 top=23
+ // secondDoubleNestedView left=26 top=108
+
+ assertThat(outerView.getChildCount()).isEqualTo(2);
+ AssistStructure.ViewNode doubleNestedView;
+ AssistStructure.ViewNode secondDoubleNestedView;
+ if (outerView.getChildAt(0).getIdEntry().equals("doubleNestedView")) {
+ doubleNestedView = outerView.getChildAt(0);
+ secondDoubleNestedView = outerView.getChildAt(1);
+ } else {
+ secondDoubleNestedView = outerView.getChildAt(0);
+ doubleNestedView = outerView.getChildAt(1);
+ }
+ assertThat(doubleNestedView.getIdEntry()).isEqualTo("doubleNestedView");
+ assertThat(secondDoubleNestedView.getIdEntry()).isEqualTo
+ ("secondDoubleNestedView");
+
+ // The location of the doubleNestedView should include all suppressed parent's
+ // offset
+ assertThat(doubleNestedView.getLeft()).isEqualTo(
+ 7 // outerView.left
+ + 19); // nestedView.left
+ assertThat(doubleNestedView.getTop()).isEqualTo(
+ 2 // outerView.top
+ + 11); // nestedView.top
+
+ // The location of the tripleNestedView should be relative to it's parent
+ assertThat(doubleNestedView.getChildCount()).isEqualTo(1);
+ AssistStructure.ViewNode tripleNestedView = doubleNestedView.getChildAt(0);
+ assertThat(doubleNestedView.getIdEntry()).isEqualTo("doubleNestedView");
+
+ assertThat(tripleNestedView.getLeft()).isEqualTo(37); // doubleNestedView.left
+ assertThat(tripleNestedView.getTop()).isEqualTo(23); // doubleNestedView.top
+ }
+ );
+ }
+
+ @Test
+ public void checkViewLocationInAssistStructureAfterForceAutofill() throws Exception {
+ onAssistStructure(true, (structure) -> {
+ AssistStructure.ViewNode outerView = findNodeByResourceId(structure,
+ "outerView");
+
+ // The structure should be
+ //
+ // outerView
+ // nestedView left=7 top=2
+ // doubleNestedView left=19 top=11
+ // tripleNestedView left=37 top=23
+ // secondDoubleNestedView left=19 top=106
+
+ // Test only what is different from #checkViewLocationInAssistStructure
+ assertThat(outerView.getChildCount()).isEqualTo(1);
+
+ AssistStructure.ViewNode nestedView = outerView.getChildAt(0);
+ assertThat(nestedView.getIdEntry()).isEqualTo("nestedView");
+ assertThat(nestedView.getLeft()).isEqualTo(7); // outerView.left
+ assertThat(nestedView.getTop()).isEqualTo(2); // outerView.top
+
+ assertThat(nestedView.getChildCount()).isEqualTo(2);
+ AssistStructure.ViewNode doubleNestedView;
+ if (nestedView.getChildAt(0).getIdEntry().equals("doubleNestedView")) {
+ doubleNestedView = nestedView.getChildAt(0);
+ } else {
+ doubleNestedView = nestedView.getChildAt(1);
+ }
+ assertThat(doubleNestedView.getIdEntry()).isEqualTo("doubleNestedView");
+
+ assertThat(doubleNestedView.getLeft()).isEqualTo(19); // nestedView.left
+ assertThat(doubleNestedView.getTop()).isEqualTo(11); // nestedView.top
+ }
+ );
+ }
}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerActivity.java b/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerActivity.java
index cd1084d..82bf213 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerActivity.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerActivity.java
@@ -26,6 +26,7 @@
import android.autofillservice.cts.VirtualContainerView.Line.OneTimeLineWatcher;
import android.graphics.Canvas;
import android.os.Bundle;
+import android.view.autofill.AutofillManager;
/**
* A custom activity that uses {@link Canvas} to draw the following fields:
@@ -59,6 +60,13 @@
}
/**
+ * Triggers manual autofill in a given line.
+ */
+ void requestAutofill(Line line) {
+ getAutofillManager().requestAutofill(mCustomView, line.text.id, line.bounds);
+ }
+
+ /**
* Sets the expectation for an auto-fill request, so it can be asserted through
* {@link #assertAutoFilled()} later.
*/
diff --git a/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerActivityTest.java
index 739e70e..5aac510 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerActivityTest.java
@@ -34,6 +34,8 @@
import android.app.assist.AssistStructure.ViewNode;
import android.autofillservice.cts.CannedFillResponse.CannedDataset;
import android.autofillservice.cts.InstrumentedAutoFillService.FillRequest;
+import android.autofillservice.cts.VirtualContainerView.Line;
+import android.graphics.Rect;
import android.support.test.rule.ActivityTestRule;
import android.support.test.uiautomator.UiObject2;
import android.view.autofill.AutofillManager;
@@ -74,12 +76,6 @@
autofillTest(false);
}
- @Test
- public void testAutofillOverrideDispatchProvideAutofillStructure() throws Exception {
- mActivity.mCustomView.setOverrideDispatchProvideAutofillStructure(true);
- autofillTest(true);
- }
-
/**
* Tests autofilling the virtual views, using the sync / async version of ViewStructure.addChild
*/
@@ -98,6 +94,13 @@
// Trigger auto-fill.
mActivity.mUsername.changeFocus(true);
+ assertDatasetShown(mActivity.mUsername, "The Dude");
+
+ // Play around with focus to make sure picker is properly drawn.
+ mActivity.mPassword.changeFocus(true);
+ assertDatasetShown(mActivity.mPassword, "The Dude");
+ mActivity.mUsername.changeFocus(true);
+ assertDatasetShown(mActivity.mUsername, "The Dude");
// Make sure input was sanitized.
final FillRequest request = sReplier.getNextFillRequest();
@@ -148,6 +151,50 @@
}
@Test
+ public void testAutofillTwoDatasets() throws Exception {
+ // Set service.
+ enableService();
+
+ // Set expectations.
+ sReplier.addResponse(new CannedFillResponse.Builder()
+ .addDataset(new CannedDataset.Builder()
+ .setField(ID_USERNAME, "dude")
+ .setField(ID_PASSWORD, "sweet")
+ .setPresentation(createPresentation("The Dude"))
+ .build())
+ .addDataset(new CannedDataset.Builder()
+ .setField(ID_USERNAME, "DUDE")
+ .setField(ID_PASSWORD, "SWEET")
+ .setPresentation(createPresentation("THE DUDE"))
+ .build())
+ .build());
+ mActivity.expectAutoFill("DUDE", "SWEET");
+
+ // Trigger auto-fill.
+ mActivity.mUsername.changeFocus(true);
+ sReplier.getNextFillRequest();
+ assertDatasetShown(mActivity.mUsername, "The Dude", "THE DUDE");
+
+ // Play around with focus to make sure picker is properly drawn.
+ mActivity.mPassword.changeFocus(true);
+ assertDatasetShown(mActivity.mPassword, "The Dude", "THE DUDE");
+ mActivity.mUsername.changeFocus(true);
+ assertDatasetShown(mActivity.mUsername, "The Dude", "THE DUDE");
+
+ // Auto-fill it.
+ sUiBot.selectDataset("THE DUDE");
+
+ // Check the results.
+ mActivity.assertAutoFilled();
+ }
+
+ @Test
+ public void testAutofillOverrideDispatchProvideAutofillStructure() throws Exception {
+ mActivity.mCustomView.setOverrideDispatchProvideAutofillStructure(true);
+ autofillTest(true);
+ }
+
+ @Test
public void testAutofillManuallyOneDataset() throws Exception {
// Set service.
enableService();
@@ -161,8 +208,7 @@
mActivity.expectAutoFill("dude", "sweet");
// Trigger auto-fill.
- mActivity.getSystemService(AutofillManager.class).requestAutofill(
- mActivity.mCustomView, mActivity.mUsername.text.id, mActivity.mUsername.bounds);
+ mActivity.requestAutofill(mActivity.mUsername);
sReplier.getNextFillRequest();
// Should have been automatically filled.
@@ -296,4 +342,16 @@
// Assert callback was called
callback.assertUiUnavailableEvent(mActivity.mCustomView, mActivity.mUsername.text.id);
}
+
+ /**
+ * Asserts the dataset picker is properly displayed in a give line.
+ */
+ private void assertDatasetShown(Line line, String... expectedDatasets) {
+ final Rect pickerBounds = sUiBot.assertDatasets(expectedDatasets).getVisibleBounds();
+ final Rect fieldBounds = line.getAbsCoordinates();
+ assertWithMessage("vertical coordinates don't match; picker=%s, field=%s", pickerBounds,
+ fieldBounds).that(pickerBounds.top).isEqualTo(fieldBounds.bottom);
+ assertWithMessage("horizontal coordinates don't match; picker=%s, field=%s", pickerBounds,
+ fieldBounds).that(pickerBounds.left).isEqualTo(fieldBounds.left);
+ }
}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerView.java b/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerView.java
index 2102773..7c0148e 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerView.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerView.java
@@ -41,6 +41,7 @@
import android.view.autofill.AutofillValue;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -118,15 +119,24 @@
super.onDraw(canvas);
Log.d(TAG, "onDraw: " + mLines.size() + " lines; canvas:" + canvas);
- final float x = mLeftMargin;
+ float x;
float y = mTopMargin + mLineLength;
for (int i = 0; i < mLines.size(); i++) {
+ x = mLeftMargin;
final Line line = mLines.get(i);
Log.v(TAG, "Drawing '" + line + "' at " + x + "x" + y);
mTextPaint.setColor(line.focused ? mFocusedColor : mUnfocusedColor);
- final String text = line.label.text + ": [" + line.text.text + "]";
- canvas.drawText(text, x, y, mTextPaint);
- line.setBounds(x, y);
+ final String readOnlyText = line.label.text + ": [";
+ final String writeText = line.text.text + "]";
+ // Paints the label first...
+ canvas.drawText(readOnlyText, x, y, mTextPaint);
+ // ...then paints the edit text and sets the proper boundary
+ final float deltaX = mTextPaint.measureText(readOnlyText);
+ x += deltaX;
+ line.bounds.set((int) x, (int) (y - mLineLength),
+ (int) (x + mTextPaint.measureText(writeText)), (int) y);
+ Log.d(TAG, "setBounds(" + x + ", " + y + "): " + line.bounds);
+ canvas.drawText(writeText, x, y, mTextPaint);
y += mLineLength;
}
}
@@ -243,8 +253,8 @@
final Item label;
final Item text;
-
- Rect bounds;
+ // Boundaries of the text field, relative to the CustomView
+ final Rect bounds = new Rect();
private boolean focused;
@@ -253,30 +263,30 @@
this.text = new Item(this, ++nextId, textId, text, true, true);
}
- void setBounds(float x, float y) {
- int left = (int) x;
- int right = (int) (x + mTextPaint.getTextSize());
- int top = (int) y;
- int bottom = (int) (y + mTextHeight);
- if (bounds == null) {
- bounds = new Rect(left, top, right, bottom);
- } else {
- bounds.set(left, top, right, bottom);
- }
- Log.d(TAG, "setBounds(" + x + ", " + y + "): " + bounds);
- }
-
void changeFocus(boolean focused) {
- // TODO: fix bounds values
- Log.d(TAG, "changeFocus() on " + text.id + ": " + focused + " bounds: " + bounds);
this.focused = focused;
if (focused) {
- mAfm.notifyViewEntered(VirtualContainerView.this, text.id, bounds);
+ final Rect absBounds = getAbsCoordinates();
+ Log.d(TAG, "focus gained on " + text.id + "; absBounds=" + absBounds);
+ mAfm.notifyViewEntered(VirtualContainerView.this, text.id, absBounds);
} else {
+ Log.d(TAG, "focus lost on " + text.id);
mAfm.notifyViewExited(VirtualContainerView.this, text.id);
}
}
+ Rect getAbsCoordinates() {
+ // Must offset the boundaries so they're relative to the CustomView.
+ final int offset[] = new int[2];
+ getLocationOnScreen(offset);
+ final Rect absBounds = new Rect(bounds.left + offset[0],
+ bounds.top + offset[1],
+ bounds.right + offset[0], bounds.bottom + offset[1]);
+ Log.v(TAG, "getAbsCoordinates() for " + text.id + ": bounds=" + bounds
+ + " offset: " + Arrays.toString(offset) + " absBounds: " + absBounds);
+ return absBounds;
+ }
+
void setTextChangedListener(TextWatcher listener) {
text.listener = listener;
}
diff --git a/tests/expectations/knownfailures.txt b/tests/expectations/knownfailures.txt
index ba902bf..e4ca5af 100644
--- a/tests/expectations/knownfailures.txt
+++ b/tests/expectations/knownfailures.txt
@@ -296,12 +296,5 @@
"android.telecom.cts.WiredHeadsetTest"
],
bug: 26149528
-},
-{
- desciption: "Seems to be failing on X86, flaky on arm. Root cause to be investigated",
- names: [
- "android.webkit.cts.WebViewClientTest#testOnRenderProcessGone"
- ],
- bug: 37704262
}
]
diff --git a/tests/fragment/src/android/fragment/cts/FragmentTransactionTest.java b/tests/fragment/src/android/fragment/cts/FragmentTransactionTest.java
index ea713fc..2a532d7 100644
--- a/tests/fragment/src/android/fragment/cts/FragmentTransactionTest.java
+++ b/tests/fragment/src/android/fragment/cts/FragmentTransactionTest.java
@@ -40,6 +40,7 @@
import android.view.View;
import android.view.ViewGroup;
+import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -60,10 +61,27 @@
new ActivityTestRule<>(FragmentTestActivity.class);
private FragmentTestActivity mActivity;
+ private int mOnBackStackChangedTimes;
+ private FragmentManager.OnBackStackChangedListener mOnBackStackChangedListener;
@Before
public void setUp() {
mActivity = mActivityRule.getActivity();
+ mOnBackStackChangedTimes = 0;
+ mOnBackStackChangedListener = new FragmentManager.OnBackStackChangedListener() {
+ @Override
+ public void onBackStackChanged() {
+ mOnBackStackChangedTimes++;
+ }
+ };
+ mActivity.getFragmentManager().addOnBackStackChangedListener(mOnBackStackChangedListener);
+ }
+
+ @After
+ public void tearDown() {
+ mActivity.getFragmentManager()
+ .removeOnBackStackChangedListener(mOnBackStackChangedListener);
+ mOnBackStackChangedListener = null;
}
@Test
@@ -77,6 +95,7 @@
.addToBackStack(null)
.commit();
mActivity.getFragmentManager().executePendingTransactions();
+ assertEquals(1, mOnBackStackChangedTimes);
}
});
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
@@ -96,6 +115,7 @@
.addToBackStack(null)
.commit();
mActivity.getFragmentManager().executePendingTransactions();
+ assertEquals(1, mOnBackStackChangedTimes);
} catch (IllegalStateException e) {
exceptionThrown = true;
} finally {
@@ -120,6 +140,7 @@
.addToBackStack(null)
.commit();
mActivity.getFragmentManager().executePendingTransactions();
+ assertEquals(1, mOnBackStackChangedTimes);
} catch (IllegalStateException e) {
exceptionThrown = true;
} finally {
@@ -144,6 +165,7 @@
.addToBackStack(null)
.commit();
mActivity.getFragmentManager().executePendingTransactions();
+ assertEquals(1, mOnBackStackChangedTimes);
} catch (IllegalStateException e) {
exceptionThrown = true;
} finally {
@@ -168,6 +190,7 @@
.addToBackStack(null)
.commit();
mActivity.getFragmentManager().executePendingTransactions();
+ assertEquals(1, mOnBackStackChangedTimes);
} catch (IllegalStateException e) {
exceptionThrown = true;
} finally {
@@ -193,6 +216,7 @@
}
}).commit();
fm.executePendingTransactions();
+ assertEquals(0, mOnBackStackChangedTimes);
assertTrue("runOnCommit runnable never ran", ran[0]);
@@ -211,6 +235,7 @@
}
fm.executePendingTransactions();
+ assertEquals(0, mOnBackStackChangedTimes);
assertTrue("runOnCommit was allowed to be called for back stack transaction",
threw);
@@ -235,6 +260,7 @@
Collection<Fragment> fragments = fm.getFragments();
assertEquals(1, fragments.size());
assertTrue(fragments.contains(fragment));
+ assertEquals(1, mOnBackStackChangedTimes);
// Removed fragments shouldn't show
fm.beginTransaction()
@@ -243,18 +269,22 @@
.commit();
FragmentTestUtil.executePendingTransactions(mActivityRule);
assertTrue(fm.getFragments().isEmpty());
+ assertEquals(2, mOnBackStackChangedTimes);
// Now try detached fragments
FragmentTestUtil.popBackStackImmediate(mActivityRule);
+ assertEquals(3, mOnBackStackChangedTimes);
fm.beginTransaction()
.detach(fragment)
.addToBackStack(null)
.commit();
FragmentTestUtil.executePendingTransactions(mActivityRule);
assertTrue(fm.getFragments().isEmpty());
+ assertEquals(4, mOnBackStackChangedTimes);
// Now try hidden fragments
FragmentTestUtil.popBackStackImmediate(mActivityRule);
+ assertEquals(5, mOnBackStackChangedTimes);
fm.beginTransaction()
.hide(fragment)
.addToBackStack(null)
@@ -263,15 +293,18 @@
fragments = fm.getFragments();
assertEquals(1, fragments.size());
assertTrue(fragments.contains(fragment));
+ assertEquals(6, mOnBackStackChangedTimes);
// And showing it again shouldn't change anything:
FragmentTestUtil.popBackStackImmediate(mActivityRule);
fragments = fm.getFragments();
assertEquals(1, fragments.size());
assertTrue(fragments.contains(fragment));
+ assertEquals(7, mOnBackStackChangedTimes);
// Now pop back to the start state
FragmentTestUtil.popBackStackImmediate(mActivityRule);
+ assertEquals(8, mOnBackStackChangedTimes);
// We can't force concurrency, but we can do it lots of times and hope that
// we hit it.
@@ -300,6 +333,7 @@
.add(fragment1, "1")
.commit();
FragmentTestUtil.executePendingTransactions(mActivityRule);
+ assertEquals(0, mOnBackStackChangedTimes);
final FragmentManager fm = fragment1.getChildFragmentManager();
mActivity.getFragmentManager()
.beginTransaction()
@@ -308,6 +342,7 @@
FragmentTestUtil.executePendingTransactions(mActivityRule);
assertEquals(0, mActivity.getFragmentManager().getFragments().size());
assertEquals(0, fm.getFragments().size());
+ assertEquals(0, mOnBackStackChangedTimes);
// Now the fragment1's fragment manager should allow commitAllowingStateLoss
// by doing nothing since it has been detached.
@@ -317,6 +352,7 @@
.commitAllowingStateLoss();
FragmentTestUtil.executePendingTransactions(mActivityRule);
assertEquals(0, fm.getFragments().size());
+ assertEquals(0, mOnBackStackChangedTimes);
// It should also allow commitNowAllowingStateLoss by doing nothing
mActivityRule.runOnUiThread(() -> {
@@ -326,6 +362,7 @@
.commitNowAllowingStateLoss();
assertEquals(0, fm.getFragments().size());
});
+ assertEquals(0, mOnBackStackChangedTimes);
}
/**
diff --git a/tests/fragment/src/android/fragment/cts/FragmentTransitionTest.java b/tests/fragment/src/android/fragment/cts/FragmentTransitionTest.java
index 7a73046..6f63e98 100644
--- a/tests/fragment/src/android/fragment/cts/FragmentTransitionTest.java
+++ b/tests/fragment/src/android/fragment/cts/FragmentTransitionTest.java
@@ -38,6 +38,7 @@
import com.android.compatibility.common.util.transition.TargetTracking;
import com.android.compatibility.common.util.transition.TrackingTransition;
+import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -55,6 +56,8 @@
@RunWith(Parameterized.class)
public class FragmentTransitionTest {
private final boolean mReordered;
+ private int mOnBackStackChangedTimes;
+ private FragmentManager.OnBackStackChangedListener mOnBackStackChangedListener;
@Parameterized.Parameters
public static Object[] data() {
@@ -77,6 +80,20 @@
public void setup() throws Throwable {
mFragmentManager = mActivityRule.getActivity().getFragmentManager();
FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+ mOnBackStackChangedTimes = 0;
+ mOnBackStackChangedListener = new FragmentManager.OnBackStackChangedListener() {
+ @Override
+ public void onBackStackChanged() {
+ mOnBackStackChangedTimes++;
+ }
+ };
+ mFragmentManager.addOnBackStackChangedListener(mOnBackStackChangedListener);
+ }
+
+ @After
+ public void teardown() throws Throwable {
+ mFragmentManager.removeOnBackStackChangedListener(mOnBackStackChangedListener);
+ mOnBackStackChangedListener = null;
}
// Test that normal view transitions (enter, exit, reenter, return) run with
@@ -98,6 +115,7 @@
fragment.waitForTransition();
verifyAndClearTransition(fragment.exitTransition, null, green, blue);
verifyNoOtherTransitions(fragment);
+ assertEquals(2, mOnBackStackChangedTimes);
// reenter transition
FragmentTestUtil.popBackStackImmediate(mActivityRule);
@@ -106,12 +124,14 @@
final View blue2 = findBlue();
verifyAndClearTransition(fragment.reenterTransition, null, green2, blue2);
verifyNoOtherTransitions(fragment);
+ assertEquals(3, mOnBackStackChangedTimes);
// return transition
FragmentTestUtil.popBackStackImmediate(mActivityRule);
fragment.waitForTransition();
verifyAndClearTransition(fragment.returnTransition, null, green2, blue2);
verifyNoOtherTransitions(fragment);
+ assertEquals(4, mOnBackStackChangedTimes);
}
// Test that shared elements transition from one fragment to the next
@@ -125,9 +145,11 @@
fragment2.setLayoutId(R.layout.scene2);
verifyTransition(fragment1, fragment2, "blueSquare");
+ assertEquals(2, mOnBackStackChangedTimes);
// Now pop the back stack
verifyPopTransition(1, fragment2, fragment1);
+ assertEquals(3, mOnBackStackChangedTimes);
}
// Test that shared element transitions through multiple fragments work together
@@ -173,6 +195,7 @@
}
});
FragmentTestUtil.waitForExecution(mActivityRule);
+ assertEquals(2, mOnBackStackChangedTimes);
// should be a normal transition from fragment1 to fragment2
fragment1.waitForTransition();
@@ -188,6 +211,7 @@
// Pop should also do the same thing
FragmentTestUtil.popBackStackImmediate(mActivityRule);
+ assertEquals(3, mOnBackStackChangedTimes);
fragment1.waitForTransition();
fragment2.waitForTransition();
@@ -216,6 +240,7 @@
.addToBackStack(null)
.commit();
FragmentTestUtil.waitForExecution(mActivityRule);
+ assertEquals(1, mOnBackStackChangedTimes);
fragment1.waitForTransition();
final View greenSquare1 = findViewById(fragment1, R.id.greenSquare);
@@ -450,6 +475,7 @@
.addToBackStack(null)
.commit();
FragmentTestUtil.waitForExecution(mActivityRule);
+ assertEquals(2, mOnBackStackChangedTimes);
fragment1.waitForTransition();
fragment2.waitForTransition();
@@ -465,6 +491,7 @@
// Now see if it works when popped
FragmentTestUtil.popBackStackImmediate(mActivityRule);
+ assertEquals(3, mOnBackStackChangedTimes);
fragment1.waitForTransition();
fragment2.waitForTransition();
@@ -778,6 +805,7 @@
.addToBackStack(null)
.commit();
FragmentTestUtil.waitForExecution(mActivityRule);
+ assertEquals(1, mOnBackStackChangedTimes);
fragment1.waitForTransition();
final View blueSquare1 = findBlue();
final View greenSquare1 = findGreen();
@@ -833,6 +861,7 @@
private void verifyTransition(TransitionFragment from, TransitionFragment to,
String sharedElementName) throws Throwable {
+ final int startOnBackStackChanged = mOnBackStackChangedTimes;
final View startBlue = findBlue();
final View startGreen = findGreen();
final View startRed = findRed();
@@ -847,6 +876,7 @@
.commit();
FragmentTestUtil.waitForExecution(mActivityRule);
+ assertEquals(startOnBackStackChanged + 1, mOnBackStackChangedTimes);
to.waitForTransition();
final View endGreen = findGreen();
@@ -872,7 +902,8 @@
private void verifyCrossTransition(boolean swapSource,
TransitionFragment from1, TransitionFragment from2) throws Throwable {
-
+ final int startNumOnBackStackChanged = mOnBackStackChangedTimes;
+ final int changesPerOperation = mReordered ? 1 : 2;
final TransitionFragment to1 = new TransitionFragment();
to1.setLayoutId(R.layout.scene2);
final TransitionFragment to2 = new TransitionFragment();
@@ -906,6 +937,8 @@
});
FragmentTestUtil.waitForExecution(mActivityRule);
+ assertEquals(startNumOnBackStackChanged + changesPerOperation, mOnBackStackChangedTimes);
+
from1.waitForTransition();
from2.waitForTransition();
to1.waitForTransition();
@@ -937,6 +970,8 @@
mFragmentManager.popBackStack();
});
FragmentTestUtil.waitForExecution(mActivityRule);
+ assertEquals(startNumOnBackStackChanged + changesPerOperation + 1,
+ mOnBackStackChangedTimes);
from1.waitForTransition();
from2.waitForTransition();
@@ -964,6 +999,7 @@
private void verifyPopTransition(final int numPops, TransitionFragment from,
TransitionFragment to, TransitionFragment... others) throws Throwable {
+ final int startOnBackStackChanged = mOnBackStackChangedTimes;
final View startBlue = findBlue();
final View startGreen = findGreen();
final View startRed = findRed();
@@ -975,6 +1011,7 @@
}
});
FragmentTestUtil.waitForExecution(mActivityRule);
+ assertEquals(startOnBackStackChanged + 1, mOnBackStackChangedTimes);
to.waitForTransition();
final View endGreen = findGreen();
diff --git a/tests/fragment/src/android/fragment/cts/FragmentViewTests.java b/tests/fragment/src/android/fragment/cts/FragmentViewTests.java
index f0d5947..927552d 100644
--- a/tests/fragment/src/android/fragment/cts/FragmentViewTests.java
+++ b/tests/fragment/src/android/fragment/cts/FragmentViewTests.java
@@ -298,19 +298,14 @@
FragmentTestUtil.assertChildren(container);
}
- // Removing a fragment that isn't in should throw
+ // Removing a fragment that isn't in should not throw
@Test
- public void removeNothThere() throws Throwable {
+ public void removeNotThere() throws Throwable {
FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
final StrictViewFragment fragment = new StrictViewFragment();
fm.beginTransaction().remove(fragment).commit();
- try {
- FragmentTestUtil.executePendingTransactions(mActivityRule);
- fail("Removing a fragment that isn't in should throw an exception");
- } catch (Throwable t) {
- // expected
- }
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
}
// Hide a fragment and its View should be GONE. Then pop it and the View should be VISIBLE
@@ -342,7 +337,7 @@
assertEquals(View.VISIBLE, fragment.getView().getVisibility());
}
- // Hiding a hidden fragment should throw
+ // Hiding a hidden fragment should not throw
@Test
public void doubleHide() throws Throwable {
FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
@@ -353,15 +348,11 @@
.hide(fragment)
.hide(fragment)
.commit();
- try {
- FragmentTestUtil.executePendingTransactions(mActivityRule);
- fail("Hiding a hidden fragment should throw an exception");
- } catch (Throwable t) {
- // expected
- }
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ // should not throw
}
- // Hiding a non-existing fragment should throw
+ // Hiding a non-existing fragment should not throw
@Test
public void hideUnAdded() throws Throwable {
FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
@@ -370,12 +361,8 @@
fm.beginTransaction()
.hide(fragment)
.commit();
- try {
- FragmentTestUtil.executePendingTransactions(mActivityRule);
- fail("Hiding a non-existing fragment should throw an exception");
- } catch (Throwable t) {
- // expected
- }
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ // should not throw
}
// Show a hidden fragment and its View should be VISIBLE. Then pop it and the View should be
@@ -409,7 +396,7 @@
assertEquals(View.GONE, fragment.getView().getVisibility());
}
- // Showing a shown fragment should throw
+ // Showing a shown fragment should not throw
@Test
public void showShown() throws Throwable {
FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
@@ -419,15 +406,11 @@
.add(R.id.fragmentContainer, fragment)
.show(fragment)
.commit();
- try {
- FragmentTestUtil.executePendingTransactions(mActivityRule);
- fail("Showing a visible fragment should throw an exception");
- } catch (Throwable t) {
- // expected
- }
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ // should not throw
}
- // Showing a non-existing fragment should throw
+ // Showing a non-existing fragment should not throw
@Test
public void showUnAdded() throws Throwable {
FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
@@ -436,12 +419,8 @@
fm.beginTransaction()
.show(fragment)
.commit();
- try {
- FragmentTestUtil.executePendingTransactions(mActivityRule);
- fail("Showing a non-existing fragment should throw an exception");
- } catch (Throwable t) {
- // expected
- }
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ // should not throw
}
// Detaching a fragment should remove the View from the hierarchy. Then popping it should
@@ -507,7 +486,7 @@
assertEquals(View.GONE, fragment.getView().getVisibility());
}
- // Detaching a detached fragment should throw
+ // Detaching a detached fragment should not throw
@Test
public void detachDetatched() throws Throwable {
FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
@@ -518,15 +497,11 @@
.detach(fragment)
.detach(fragment)
.commit();
- try {
- FragmentTestUtil.executePendingTransactions(mActivityRule);
- fail("Detaching a detached fragment should throw an exception");
- } catch (Throwable t) {
- // expected
- }
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ // should not throw
}
- // Detaching a non-existing fragment should throw
+ // Detaching a non-existing fragment should not throw
@Test
public void detachUnAdded() throws Throwable {
FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
@@ -535,12 +510,8 @@
fm.beginTransaction()
.detach(fragment)
.commit();
- try {
- FragmentTestUtil.executePendingTransactions(mActivityRule);
- fail("Detaching a non-existing fragment should throw an exception");
- } catch (Throwable t) {
- // expected
- }
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ // should not throw
}
// Attaching a fragment should add the View back into the hierarchy. Then popping it should
@@ -608,7 +579,7 @@
assertTrue(fragment.isHidden());
}
- // Attaching an attached fragment should throw
+ // Attaching an attached fragment should not throw
@Test
public void attachAttached() throws Throwable {
FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
@@ -618,15 +589,11 @@
.add(R.id.fragmentContainer, fragment)
.attach(fragment)
.commit();
- try {
- FragmentTestUtil.executePendingTransactions(mActivityRule);
- fail("Attaching an attached fragment should throw an exception");
- } catch (Throwable t) {
- // expected
- }
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ // should not throw an exception
}
- // Attaching a non-existing fragment should throw
+ // Attaching a non-existing fragment should not throw
@Test
public void attachUnAdded() throws Throwable {
FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
@@ -635,12 +602,8 @@
fm.beginTransaction()
.attach(fragment)
.commit();
- try {
- FragmentTestUtil.executePendingTransactions(mActivityRule);
- fail("Attaching a non-existing fragment should throw an exception");
- } catch (Throwable t) {
- // expected
- }
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ // should not throw
}
// Simple replace of one fragment in a container. Popping should replace it back again
diff --git a/tests/fragment/src/android/fragment/cts/TransitionFragment.java b/tests/fragment/src/android/fragment/cts/TransitionFragment.java
index fbe2250..17363ac 100644
--- a/tests/fragment/src/android/fragment/cts/TransitionFragment.java
+++ b/tests/fragment/src/android/fragment/cts/TransitionFragment.java
@@ -71,7 +71,7 @@
}
void waitForTransition() throws InterruptedException {
- verify(mListener, within(300)).onTransitionEnd(any());
+ verify(mListener, within(500)).onTransitionEnd(any());
reset(mListener);
}
diff --git a/tests/sensor/jni/SensorTestCases.cpp b/tests/sensor/jni/SensorTestCases.cpp
index f86f262..43e07a4 100644
--- a/tests/sensor/jni/SensorTestCases.cpp
+++ b/tests/sensor/jni/SensorTestCases.cpp
@@ -135,7 +135,7 @@
// Test sensor direct report functionality
void SensorTest::testDirectReport(JNIEnv* env, int32_t sensorType, int32_t channelType, int32_t rateLevel) {
constexpr size_t kEventSize = sizeof(ASensorEvent);
- constexpr size_t kNEvent = 500;
+ constexpr size_t kNEvent = 4096; // enough to contain 1.5 * 800 * 2.2 events
constexpr size_t kMemSize = kEventSize * kNEvent;
// value check criterion
diff --git a/tests/sensor/src/android/hardware/cts/SensorDirectReportTest.java b/tests/sensor/src/android/hardware/cts/SensorDirectReportTest.java
index 6c2462f..78ac59d 100644
--- a/tests/sensor/src/android/hardware/cts/SensorDirectReportTest.java
+++ b/tests/sensor/src/android/hardware/cts/SensorDirectReportTest.java
@@ -65,7 +65,8 @@
private static final int TEST_RUN_TIME_PERIOD_MILLISEC = 5000;
private static final int ALLOWED_SENSOR_INIT_TIME_MILLISEC = 500;
private static final int SENSORS_EVENT_SIZE = 104;
- private static final int SHARED_MEMORY_SIZE = 2000 * SENSORS_EVENT_SIZE;
+ private static final int SENSORS_EVENT_COUNT = 10240; // 800Hz * 2.2 * 5 sec + extra
+ private static final int SHARED_MEMORY_SIZE = SENSORS_EVENT_COUNT * SENSORS_EVENT_SIZE;
private static final float MERCY_FACTOR = 0.1f;
private static native boolean nativeReadHardwareBuffer(HardwareBuffer hardwareBuffer,
diff --git a/tests/tests/content/res/font/broken_xmlfont.xml b/tests/tests/content/res/font/broken_xmlfont.xml
new file mode 100644
index 0000000..be30f2f
--- /dev/null
+++ b/tests/tests/content/res/font/broken_xmlfont.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<font-family xmlns:android="http://schemas.android.com/apk/res/android">
+ <font android:fontStyle="normal" android:fontWeight="400" android:font="@font/brokenfont" />
+</font-family>
diff --git a/tests/tests/content/res/font/brokenfont.ttf b/tests/tests/content/res/font/brokenfont.ttf
new file mode 100644
index 0000000..12b2713
--- /dev/null
+++ b/tests/tests/content/res/font/brokenfont.ttf
Binary files differ
diff --git a/tests/tests/content/res/font/brokenfont_source.ttx b/tests/tests/content/res/font/brokenfont_source.ttx
new file mode 100644
index 0000000..34217b4
--- /dev/null
+++ b/tests/tests/content/res/font/brokenfont_source.ttx
@@ -0,0 +1,177 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+ <GlyphOrder>
+ <GlyphID id="0" name=".notdef"/>
+ <GlyphID id="1" name="a"/>
+ </GlyphOrder>
+
+ <head>
+ <tableVersion value="1.0"/>
+ <fontRevision value="1.0"/>
+ <checkSumAdjustment value="0x640cdb2f"/>
+ <magicNumber value="0x5f0f3cf5"/>
+ <flags value="00000000 00000011"/>
+ <unitsPerEm value="1000"/>
+ <created value="Mon May 1 00:00:00 2017"/>
+ <macStyle value="00000000 00000000"/>
+ <lowestRecPPEM value="7"/>
+ <fontDirectionHint value="2"/>
+ <glyphDataFormat value="0"/>
+ </head>
+
+ <hhea>
+ <tableVersion value="1.0"/>
+ <ascent value="1000"/>
+ <descent value="-200"/>
+ <lineGap value="0"/>
+ <caretSlopeRise value="1"/>
+ <caretSlopeRun value="0"/>
+ <caretOffset value="0"/>
+ <reserved0 value="0"/>
+ <reserved1 value="0"/>
+ <reserved2 value="0"/>
+ <reserved3 value="0"/>
+ <metricDataFormat value="0"/>
+ </hhea>
+
+ <maxp>
+ <tableVersion value="0x10000"/>
+ <maxZones value="0"/>
+ <maxTwilightPoints value="0"/>
+ <maxStorage value="0"/>
+ <maxFunctionDefs value="0"/>
+ <maxInstructionDefs value="0"/>
+ <maxStackElements value="0"/>
+ <maxSizeOfInstructions value="0"/>
+ <maxComponentElements value="0"/>
+ </maxp>
+
+ <OS_2>
+ <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+ will be recalculated by the compiler -->
+ <version value="3"/>
+ <xAvgCharWidth value="594"/>
+ <usWeightClass value="400"/>
+ <usWidthClass value="5"/>
+ <fsType value="00000000 00001000"/>
+ <ySubscriptXSize value="650"/>
+ <ySubscriptYSize value="600"/>
+ <ySubscriptXOffset value="0"/>
+ <ySubscriptYOffset value="75"/>
+ <ySuperscriptXSize value="650"/>
+ <ySuperscriptYSize value="600"/>
+ <ySuperscriptXOffset value="0"/>
+ <ySuperscriptYOffset value="350"/>
+ <yStrikeoutSize value="50"/>
+ <yStrikeoutPosition value="300"/>
+ <sFamilyClass value="0"/>
+ <panose>
+ <bFamilyType value="0"/>
+ <bSerifStyle value="0"/>
+ <bWeight value="5"/>
+ <bProportion value="0"/>
+ <bContrast value="0"/>
+ <bStrokeVariation value="0"/>
+ <bArmStyle value="0"/>
+ <bLetterForm value="0"/>
+ <bMidline value="0"/>
+ <bXHeight value="0"/>
+ </panose>
+ <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+ <achVendID value="UKWN"/>
+ <fsSelection value="00000000 01000000"/>
+ <usFirstCharIndex value="32"/>
+ <usLastCharIndex value="122"/>
+ <sTypoAscender value="800"/>
+ <sTypoDescender value="-200"/>
+ <sTypoLineGap value="200"/>
+ <usWinAscent value="1000"/>
+ <usWinDescent value="200"/>
+ <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+ <sxHeight value="500"/>
+ <sCapHeight value="700"/>
+ <usDefaultChar value="0"/>
+ <usBreakChar value="32"/>
+ <usMaxContext value="0"/>
+ </OS_2>
+
+ <hmtx>
+ <mtx name=".notdef" width="500" lsb="93"/>
+ <mtx name="a" width="500" lsb="93"/>
+ </hmtx>
+
+ <cmap>
+ <tableVersion version="0"/>
+ <cmap_format_4 platformID="3" platEncID="10" language="0">
+ <map code="0x0061" name="a" />
+ </cmap_format_4>
+ </cmap>
+
+ <loca>
+ <!-- The 'loca' table will be calculated by the compiler -->
+ </loca>
+
+ <glyf>
+ <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="a" xMin="0" yMin="0" xMax="0" yMax="0" />
+ </glyf>
+
+ <name>
+ <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True">
+ Broken Font
+ </namerecord>
+ <namerecord nameID="2" platformID="1" platEncID="0" langID="0x0" unicode="True">
+ Regular
+ </namerecord>
+ <namerecord nameID="4" platformID="1" platEncID="0" langID="0x0" unicode="True">
+ Broken Font
+ </namerecord>
+ <namerecord nameID="6" platformID="1" platEncID="0" langID="0x0" unicode="True">
+ BrokenFont-Regular
+ </namerecord>
+ <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+ Broken Font
+ </namerecord>
+ <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+ Regular
+ </namerecord>
+ <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+ Broken Font
+ </namerecord>
+ <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+ BrokenFont-Regular
+ </namerecord>
+ </name>
+
+ <post>
+ <formatType value="3.0"/>
+ <italicAngle value="0.0"/>
+ <underlinePosition value="-75"/>
+ <underlineThickness value="50"/>
+ <isFixedPitch value="0"/>
+ <minMemType42 value="0"/>
+ <maxMemType42 value="0"/>
+ <minMemType1 value="0"/>
+ <maxMemType1 value="0"/>
+ </post>
+
+</ttFont>
diff --git a/tests/tests/content/res/font/invalid_xmlfont_nosource.xml b/tests/tests/content/res/font/invalid_xmlfont_nosource.xml
new file mode 100644
index 0000000..6b70222
--- /dev/null
+++ b/tests/tests/content/res/font/invalid_xmlfont_nosource.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<font-family xmlns:android="http://schemas.android.com/apk/res/android">
+ <!-- missing the android:font entry -->
+ <font android:fontStyle="normal" android:fontWeight="400" />
+</font-family>
diff --git a/tests/tests/content/res/font/sample_bold_family.xml b/tests/tests/content/res/font/sample_bold_family.xml
new file mode 100644
index 0000000..7c09f27
--- /dev/null
+++ b/tests/tests/content/res/font/sample_bold_family.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<font-family xmlns:android="http://schemas.android.com/apk/res/android">
+ <font android:font="@font/sample_bold_font" />
+</font-family>
diff --git a/tests/tests/content/res/font/sample_bold_font.ttf b/tests/tests/content/res/font/sample_bold_font.ttf
new file mode 100644
index 0000000..de4cb7e
--- /dev/null
+++ b/tests/tests/content/res/font/sample_bold_font.ttf
Binary files differ
diff --git a/tests/tests/content/res/font/sample_bold_font_source.ttx b/tests/tests/content/res/font/sample_bold_font_source.ttx
new file mode 100644
index 0000000..5fcb8ce
--- /dev/null
+++ b/tests/tests/content/res/font/sample_bold_font_source.ttx
@@ -0,0 +1,186 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+ <GlyphOrder>
+ <GlyphID id="0" name=".notdef"/>
+ <GlyphID id="1" name="a"/>
+ </GlyphOrder>
+
+ <head>
+ <tableVersion value="1.0"/>
+ <fontRevision value="1.0"/>
+ <checkSumAdjustment value="0x640cdb2f"/>
+ <magicNumber value="0x5f0f3cf5"/>
+ <flags value="00000000 00000011"/>
+ <unitsPerEm value="1000"/>
+ <created value="Fri Mar 17 07:26:00 2017"/>
+ <macStyle value="00000000 00000000"/>
+ <lowestRecPPEM value="7"/>
+ <fontDirectionHint value="2"/>
+ <glyphDataFormat value="0"/>
+ </head>
+
+ <hhea>
+ <tableVersion value="1.0"/>
+ <ascent value="1000"/>
+ <descent value="-200"/>
+ <lineGap value="0"/>
+ <caretSlopeRise value="1"/>
+ <caretSlopeRun value="0"/>
+ <caretOffset value="0"/>
+ <reserved0 value="0"/>
+ <reserved1 value="0"/>
+ <reserved2 value="0"/>
+ <reserved3 value="0"/>
+ <metricDataFormat value="0"/>
+ </hhea>
+
+ <maxp>
+ <tableVersion value="0x10000"/>
+ <maxZones value="0"/>
+ <maxTwilightPoints value="0"/>
+ <maxStorage value="0"/>
+ <maxFunctionDefs value="0"/>
+ <maxInstructionDefs value="0"/>
+ <maxStackElements value="0"/>
+ <maxSizeOfInstructions value="0"/>
+ <maxComponentElements value="0"/>
+ </maxp>
+
+ <OS_2>
+ <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+ will be recalculated by the compiler -->
+ <version value="3"/>
+ <xAvgCharWidth value="500"/>
+ <usWeightClass value="700"/>
+ <usWidthClass value="5"/>
+ <fsType value="00000000 00001000"/>
+ <ySubscriptXSize value="650"/>
+ <ySubscriptYSize value="600"/>
+ <ySubscriptXOffset value="0"/>
+ <ySubscriptYOffset value="75"/>
+ <ySuperscriptXSize value="650"/>
+ <ySuperscriptYSize value="600"/>
+ <ySuperscriptXOffset value="0"/>
+ <ySuperscriptYOffset value="350"/>
+ <yStrikeoutSize value="50"/>
+ <yStrikeoutPosition value="300"/>
+ <sFamilyClass value="0"/>
+ <panose>
+ <bFamilyType value="0"/>
+ <bSerifStyle value="0"/>
+ <bWeight value="5"/>
+ <bProportion value="0"/>
+ <bContrast value="0"/>
+ <bStrokeVariation value="0"/>
+ <bArmStyle value="0"/>
+ <bLetterForm value="0"/>
+ <bMidline value="0"/>
+ <bXHeight value="0"/>
+ </panose>
+ <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+ <achVendID value="UKWN"/>
+ <fsSelection value="00000000 01000000"/> <!-- the last bit is for italic -->
+ <usFirstCharIndex value="32"/>
+ <usLastCharIndex value="122"/>
+ <sTypoAscender value="800"/>
+ <sTypoDescender value="-200"/>
+ <sTypoLineGap value="200"/>
+ <usWinAscent value="1000"/>
+ <usWinDescent value="200"/>
+ <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+ <sxHeight value="500"/>
+ <sCapHeight value="700"/>
+ <usDefaultChar value="0"/>
+ <usBreakChar value="32"/>
+ <usMaxContext value="0"/>
+ </OS_2>
+
+ <hmtx>
+ <mtx name=".notdef" width="500" lsb="93"/>
+ <mtx name="a" width="500" lsb="93"/>
+ </hmtx>
+
+ <cmap>
+ <tableVersion version="0"/>
+ <cmap_format_4 platformID="3" platEncID="10" language="0">
+ <map code="0x0061" name="a" />
+ </cmap_format_4>
+ </cmap>
+
+ <loca>
+ <!-- The 'loca' table will be calculated by the compiler -->
+ </loca>
+
+ <glyf>
+ <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="a" xMin="0" yMin="0" xMax="0" yMax="0">
+ <!-- FreeType treats empty table as missing table. Then if glyf table is missing, FreeType
+ treats the font as old Mac font and ignores OS/2 table. Thus needs outline here. -->
+ <contour>
+ <pt x="0" y="0" on="1"/>
+ <pt x="100" y="0" on="1"/>
+ <pt x="50" y="100" on="1"/>
+ </contour>
+ <instructions><assembly></assembly></instructions>
+ </TTGlyph>
+ </glyf>
+
+ <name>
+ <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="2" platformID="1" platEncID="0" langID="0x0" unicode="True">
+ Regular
+ </namerecord>
+ <namerecord nameID="4" platformID="1" platEncID="0" langID="0x0" unicode="True">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="6" platformID="1" platEncID="0" langID="0x0" unicode="True">
+ SampleFont-Regular
+ </namerecord>
+ <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+ Regular
+ </namerecord>
+ <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+ SampleFont-Regular
+ </namerecord>
+ </name>
+
+ <post>
+ <formatType value="3.0"/>
+ <italicAngle value="0.0"/>
+ <underlinePosition value="-75"/>
+ <underlineThickness value="50"/>
+ <isFixedPitch value="0"/>
+ <minMemType42 value="0"/>
+ <maxMemType42 value="0"/>
+ <minMemType1 value="0"/>
+ <maxMemType1 value="0"/>
+ </post>
+
+</ttFont>
diff --git a/tests/tests/content/res/font/sample_bolditalic_family.xml b/tests/tests/content/res/font/sample_bolditalic_family.xml
new file mode 100644
index 0000000..e467b38
--- /dev/null
+++ b/tests/tests/content/res/font/sample_bolditalic_family.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<font-family xmlns:android="http://schemas.android.com/apk/res/android">
+ <font android:font="@font/sample_bolditalic_font" />
+</font-family>
diff --git a/tests/tests/content/res/font/sample_bolditalic_font.ttf b/tests/tests/content/res/font/sample_bolditalic_font.ttf
new file mode 100644
index 0000000..4a35061
--- /dev/null
+++ b/tests/tests/content/res/font/sample_bolditalic_font.ttf
Binary files differ
diff --git a/tests/tests/content/res/font/sample_bolditalic_font_source.ttx b/tests/tests/content/res/font/sample_bolditalic_font_source.ttx
new file mode 100644
index 0000000..37e97d8
--- /dev/null
+++ b/tests/tests/content/res/font/sample_bolditalic_font_source.ttx
@@ -0,0 +1,186 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+ <GlyphOrder>
+ <GlyphID id="0" name=".notdef"/>
+ <GlyphID id="1" name="a"/>
+ </GlyphOrder>
+
+ <head>
+ <tableVersion value="1.0"/>
+ <fontRevision value="1.0"/>
+ <checkSumAdjustment value="0x640cdb2f"/>
+ <magicNumber value="0x5f0f3cf5"/>
+ <flags value="00000000 00000011"/>
+ <unitsPerEm value="1000"/>
+ <created value="Fri Mar 17 07:26:00 2017"/>
+ <macStyle value="00000000 00000000"/>
+ <lowestRecPPEM value="7"/>
+ <fontDirectionHint value="2"/>
+ <glyphDataFormat value="0"/>
+ </head>
+
+ <hhea>
+ <tableVersion value="1.0"/>
+ <ascent value="1000"/>
+ <descent value="-200"/>
+ <lineGap value="0"/>
+ <caretSlopeRise value="1"/>
+ <caretSlopeRun value="0"/>
+ <caretOffset value="0"/>
+ <reserved0 value="0"/>
+ <reserved1 value="0"/>
+ <reserved2 value="0"/>
+ <reserved3 value="0"/>
+ <metricDataFormat value="0"/>
+ </hhea>
+
+ <maxp>
+ <tableVersion value="0x10000"/>
+ <maxZones value="0"/>
+ <maxTwilightPoints value="0"/>
+ <maxStorage value="0"/>
+ <maxFunctionDefs value="0"/>
+ <maxInstructionDefs value="0"/>
+ <maxStackElements value="0"/>
+ <maxSizeOfInstructions value="0"/>
+ <maxComponentElements value="0"/>
+ </maxp>
+
+ <OS_2>
+ <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+ will be recalculated by the compiler -->
+ <version value="3"/>
+ <xAvgCharWidth value="500"/>
+ <usWeightClass value="700"/>
+ <usWidthClass value="5"/>
+ <fsType value="00000000 00001000"/>
+ <ySubscriptXSize value="650"/>
+ <ySubscriptYSize value="600"/>
+ <ySubscriptXOffset value="0"/>
+ <ySubscriptYOffset value="75"/>
+ <ySuperscriptXSize value="650"/>
+ <ySuperscriptYSize value="600"/>
+ <ySuperscriptXOffset value="0"/>
+ <ySuperscriptYOffset value="350"/>
+ <yStrikeoutSize value="50"/>
+ <yStrikeoutPosition value="300"/>
+ <sFamilyClass value="0"/>
+ <panose>
+ <bFamilyType value="0"/>
+ <bSerifStyle value="0"/>
+ <bWeight value="5"/>
+ <bProportion value="0"/>
+ <bContrast value="0"/>
+ <bStrokeVariation value="0"/>
+ <bArmStyle value="0"/>
+ <bLetterForm value="0"/>
+ <bMidline value="0"/>
+ <bXHeight value="0"/>
+ </panose>
+ <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+ <achVendID value="UKWN"/>
+ <fsSelection value="00000000 01000001"/> <!-- the last bit is for italic -->
+ <usFirstCharIndex value="32"/>
+ <usLastCharIndex value="122"/>
+ <sTypoAscender value="800"/>
+ <sTypoDescender value="-200"/>
+ <sTypoLineGap value="200"/>
+ <usWinAscent value="1000"/>
+ <usWinDescent value="200"/>
+ <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+ <sxHeight value="500"/>
+ <sCapHeight value="700"/>
+ <usDefaultChar value="0"/>
+ <usBreakChar value="32"/>
+ <usMaxContext value="0"/>
+ </OS_2>
+
+ <hmtx>
+ <mtx name=".notdef" width="500" lsb="93"/>
+ <mtx name="a" width="500" lsb="93"/>
+ </hmtx>
+
+ <cmap>
+ <tableVersion version="0"/>
+ <cmap_format_4 platformID="3" platEncID="10" language="0">
+ <map code="0x0061" name="a" />
+ </cmap_format_4>
+ </cmap>
+
+ <loca>
+ <!-- The 'loca' table will be calculated by the compiler -->
+ </loca>
+
+ <glyf>
+ <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="a" xMin="0" yMin="0" xMax="0" yMax="0">
+ <!-- FreeType treats empty table as missing table. Then if glyf table is missing, FreeType
+ treats the font as old Mac font and ignores OS/2 table. Thus needs outline here. -->
+ <contour>
+ <pt x="0" y="0" on="1"/>
+ <pt x="100" y="0" on="1"/>
+ <pt x="50" y="100" on="1"/>
+ </contour>
+ <instructions><assembly></assembly></instructions>
+ </TTGlyph>
+ </glyf>
+
+ <name>
+ <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="2" platformID="1" platEncID="0" langID="0x0" unicode="True">
+ Regular
+ </namerecord>
+ <namerecord nameID="4" platformID="1" platEncID="0" langID="0x0" unicode="True">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="6" platformID="1" platEncID="0" langID="0x0" unicode="True">
+ SampleFont-Regular
+ </namerecord>
+ <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+ Regular
+ </namerecord>
+ <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+ SampleFont-Regular
+ </namerecord>
+ </name>
+
+ <post>
+ <formatType value="3.0"/>
+ <italicAngle value="0.0"/>
+ <underlinePosition value="-75"/>
+ <underlineThickness value="50"/>
+ <isFixedPitch value="0"/>
+ <minMemType42 value="0"/>
+ <maxMemType42 value="0"/>
+ <minMemType1 value="0"/>
+ <maxMemType1 value="0"/>
+ </post>
+
+</ttFont>
diff --git a/tests/tests/content/res/font/sample_italic_family.xml b/tests/tests/content/res/font/sample_italic_family.xml
new file mode 100644
index 0000000..18a0a04
--- /dev/null
+++ b/tests/tests/content/res/font/sample_italic_family.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<font-family xmlns:android="http://schemas.android.com/apk/res/android">
+ <font android:font="@font/sample_italic_font" />
+</font-family>
diff --git a/tests/tests/content/res/font/sample_italic_font.ttf b/tests/tests/content/res/font/sample_italic_font.ttf
new file mode 100644
index 0000000..1278e14
--- /dev/null
+++ b/tests/tests/content/res/font/sample_italic_font.ttf
Binary files differ
diff --git a/tests/tests/content/res/font/sample_italic_font_source.ttx b/tests/tests/content/res/font/sample_italic_font_source.ttx
new file mode 100644
index 0000000..870b35c
--- /dev/null
+++ b/tests/tests/content/res/font/sample_italic_font_source.ttx
@@ -0,0 +1,186 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+ <GlyphOrder>
+ <GlyphID id="0" name=".notdef"/>
+ <GlyphID id="1" name="a"/>
+ </GlyphOrder>
+
+ <head>
+ <tableVersion value="1.0"/>
+ <fontRevision value="1.0"/>
+ <checkSumAdjustment value="0x640cdb2f"/>
+ <magicNumber value="0x5f0f3cf5"/>
+ <flags value="00000000 00000011"/>
+ <unitsPerEm value="1000"/>
+ <created value="Fri Mar 17 07:26:00 2017"/>
+ <macStyle value="00000000 00000000"/>
+ <lowestRecPPEM value="7"/>
+ <fontDirectionHint value="2"/>
+ <glyphDataFormat value="0"/>
+ </head>
+
+ <hhea>
+ <tableVersion value="1.0"/>
+ <ascent value="1000"/>
+ <descent value="-200"/>
+ <lineGap value="0"/>
+ <caretSlopeRise value="1"/>
+ <caretSlopeRun value="0"/>
+ <caretOffset value="0"/>
+ <reserved0 value="0"/>
+ <reserved1 value="0"/>
+ <reserved2 value="0"/>
+ <reserved3 value="0"/>
+ <metricDataFormat value="0"/>
+ </hhea>
+
+ <maxp>
+ <tableVersion value="0x10000"/>
+ <maxZones value="0"/>
+ <maxTwilightPoints value="0"/>
+ <maxStorage value="0"/>
+ <maxFunctionDefs value="0"/>
+ <maxInstructionDefs value="0"/>
+ <maxStackElements value="0"/>
+ <maxSizeOfInstructions value="0"/>
+ <maxComponentElements value="0"/>
+ </maxp>
+
+ <OS_2>
+ <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+ will be recalculated by the compiler -->
+ <version value="3"/>
+ <xAvgCharWidth value="500"/>
+ <usWeightClass value="400"/>
+ <usWidthClass value="5"/>
+ <fsType value="00000000 00001000"/>
+ <ySubscriptXSize value="650"/>
+ <ySubscriptYSize value="600"/>
+ <ySubscriptXOffset value="0"/>
+ <ySubscriptYOffset value="75"/>
+ <ySuperscriptXSize value="650"/>
+ <ySuperscriptYSize value="600"/>
+ <ySuperscriptXOffset value="0"/>
+ <ySuperscriptYOffset value="350"/>
+ <yStrikeoutSize value="50"/>
+ <yStrikeoutPosition value="300"/>
+ <sFamilyClass value="0"/>
+ <panose>
+ <bFamilyType value="0"/>
+ <bSerifStyle value="0"/>
+ <bWeight value="5"/>
+ <bProportion value="0"/>
+ <bContrast value="0"/>
+ <bStrokeVariation value="0"/>
+ <bArmStyle value="0"/>
+ <bLetterForm value="0"/>
+ <bMidline value="0"/>
+ <bXHeight value="0"/>
+ </panose>
+ <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+ <achVendID value="UKWN"/>
+ <fsSelection value="00000000 01000001"/> <!-- the last bit is for italic -->
+ <usFirstCharIndex value="32"/>
+ <usLastCharIndex value="122"/>
+ <sTypoAscender value="800"/>
+ <sTypoDescender value="-200"/>
+ <sTypoLineGap value="200"/>
+ <usWinAscent value="1000"/>
+ <usWinDescent value="200"/>
+ <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+ <sxHeight value="500"/>
+ <sCapHeight value="700"/>
+ <usDefaultChar value="0"/>
+ <usBreakChar value="32"/>
+ <usMaxContext value="0"/>
+ </OS_2>
+
+ <hmtx>
+ <mtx name=".notdef" width="500" lsb="93"/>
+ <mtx name="a" width="500" lsb="93"/>
+ </hmtx>
+
+ <cmap>
+ <tableVersion version="0"/>
+ <cmap_format_4 platformID="3" platEncID="10" language="0">
+ <map code="0x0061" name="a" />
+ </cmap_format_4>
+ </cmap>
+
+ <loca>
+ <!-- The 'loca' table will be calculated by the compiler -->
+ </loca>
+
+ <glyf>
+ <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="a" xMin="0" yMin="0" xMax="0" yMax="0">
+ <!-- FreeType treats empty table as missing table. Then if glyf table is missing, FreeType
+ treats the font as old Mac font and ignores OS/2 table. Thus needs outline here. -->
+ <contour>
+ <pt x="0" y="0" on="1"/>
+ <pt x="100" y="0" on="1"/>
+ <pt x="50" y="100" on="1"/>
+ </contour>
+ <instructions><assembly></assembly></instructions>
+ </TTGlyph>
+ </glyf>
+
+ <name>
+ <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="2" platformID="1" platEncID="0" langID="0x0" unicode="True">
+ Regular
+ </namerecord>
+ <namerecord nameID="4" platformID="1" platEncID="0" langID="0x0" unicode="True">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="6" platformID="1" platEncID="0" langID="0x0" unicode="True">
+ SampleFont-Regular
+ </namerecord>
+ <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+ Regular
+ </namerecord>
+ <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+ SampleFont-Regular
+ </namerecord>
+ </name>
+
+ <post>
+ <formatType value="3.0"/>
+ <italicAngle value="0.0"/>
+ <underlinePosition value="-75"/>
+ <underlineThickness value="50"/>
+ <isFixedPitch value="0"/>
+ <minMemType42 value="0"/>
+ <maxMemType42 value="0"/>
+ <minMemType1 value="0"/>
+ <maxMemType1 value="0"/>
+ </post>
+
+</ttFont>
diff --git a/tests/tests/content/res/font/sample_regular_family.xml b/tests/tests/content/res/font/sample_regular_family.xml
new file mode 100644
index 0000000..7b4da4c
--- /dev/null
+++ b/tests/tests/content/res/font/sample_regular_family.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<font-family xmlns:android="http://schemas.android.com/apk/res/android">
+ <font android:font="@font/sample_regular_font" />
+</font-family>
diff --git a/tests/tests/content/res/font/sample_regular_font.ttf b/tests/tests/content/res/font/sample_regular_font.ttf
new file mode 100644
index 0000000..8e3d602
--- /dev/null
+++ b/tests/tests/content/res/font/sample_regular_font.ttf
Binary files differ
diff --git a/tests/tests/content/res/font/sample_regular_font_source.ttx b/tests/tests/content/res/font/sample_regular_font_source.ttx
new file mode 100644
index 0000000..cffee51
--- /dev/null
+++ b/tests/tests/content/res/font/sample_regular_font_source.ttx
@@ -0,0 +1,186 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+ <GlyphOrder>
+ <GlyphID id="0" name=".notdef"/>
+ <GlyphID id="1" name="a"/>
+ </GlyphOrder>
+
+ <head>
+ <tableVersion value="1.0"/>
+ <fontRevision value="1.0"/>
+ <checkSumAdjustment value="0x640cdb2f"/>
+ <magicNumber value="0x5f0f3cf5"/>
+ <flags value="00000000 00000011"/>
+ <unitsPerEm value="1000"/>
+ <created value="Fri Mar 17 07:26:00 2017"/>
+ <macStyle value="00000000 00000000"/>
+ <lowestRecPPEM value="7"/>
+ <fontDirectionHint value="2"/>
+ <glyphDataFormat value="0"/>
+ </head>
+
+ <hhea>
+ <tableVersion value="1.0"/>
+ <ascent value="1000"/>
+ <descent value="-200"/>
+ <lineGap value="0"/>
+ <caretSlopeRise value="1"/>
+ <caretSlopeRun value="0"/>
+ <caretOffset value="0"/>
+ <reserved0 value="0"/>
+ <reserved1 value="0"/>
+ <reserved2 value="0"/>
+ <reserved3 value="0"/>
+ <metricDataFormat value="0"/>
+ </hhea>
+
+ <maxp>
+ <tableVersion value="0x10000"/>
+ <maxZones value="0"/>
+ <maxTwilightPoints value="0"/>
+ <maxStorage value="0"/>
+ <maxFunctionDefs value="0"/>
+ <maxInstructionDefs value="0"/>
+ <maxStackElements value="0"/>
+ <maxSizeOfInstructions value="0"/>
+ <maxComponentElements value="0"/>
+ </maxp>
+
+ <OS_2>
+ <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+ will be recalculated by the compiler -->
+ <version value="3"/>
+ <xAvgCharWidth value="500"/>
+ <usWeightClass value="400"/>
+ <usWidthClass value="5"/>
+ <fsType value="00000000 00001000"/>
+ <ySubscriptXSize value="650"/>
+ <ySubscriptYSize value="600"/>
+ <ySubscriptXOffset value="0"/>
+ <ySubscriptYOffset value="75"/>
+ <ySuperscriptXSize value="650"/>
+ <ySuperscriptYSize value="600"/>
+ <ySuperscriptXOffset value="0"/>
+ <ySuperscriptYOffset value="350"/>
+ <yStrikeoutSize value="50"/>
+ <yStrikeoutPosition value="300"/>
+ <sFamilyClass value="0"/>
+ <panose>
+ <bFamilyType value="0"/>
+ <bSerifStyle value="0"/>
+ <bWeight value="5"/>
+ <bProportion value="0"/>
+ <bContrast value="0"/>
+ <bStrokeVariation value="0"/>
+ <bArmStyle value="0"/>
+ <bLetterForm value="0"/>
+ <bMidline value="0"/>
+ <bXHeight value="0"/>
+ </panose>
+ <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+ <achVendID value="UKWN"/>
+ <fsSelection value="00000000 01000000"/> <!-- the last bit is for italic -->
+ <usFirstCharIndex value="32"/>
+ <usLastCharIndex value="122"/>
+ <sTypoAscender value="800"/>
+ <sTypoDescender value="-200"/>
+ <sTypoLineGap value="200"/>
+ <usWinAscent value="1000"/>
+ <usWinDescent value="200"/>
+ <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+ <sxHeight value="500"/>
+ <sCapHeight value="700"/>
+ <usDefaultChar value="0"/>
+ <usBreakChar value="32"/>
+ <usMaxContext value="0"/>
+ </OS_2>
+
+ <hmtx>
+ <mtx name=".notdef" width="500" lsb="93"/>
+ <mtx name="a" width="500" lsb="93"/>
+ </hmtx>
+
+ <cmap>
+ <tableVersion version="0"/>
+ <cmap_format_4 platformID="3" platEncID="10" language="0">
+ <map code="0x0061" name="a" />
+ </cmap_format_4>
+ </cmap>
+
+ <loca>
+ <!-- The 'loca' table will be calculated by the compiler -->
+ </loca>
+
+ <glyf>
+ <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="a" xMin="0" yMin="0" xMax="0" yMax="0">
+ <!-- FreeType treats empty table as missing table. Then if glyf table is missing, FreeType
+ treats the font as old Mac font and ignores OS/2 table. Thus needs outline here. -->
+ <contour>
+ <pt x="0" y="0" on="1"/>
+ <pt x="100" y="0" on="1"/>
+ <pt x="50" y="100" on="1"/>
+ </contour>
+ <instructions><assembly></assembly></instructions>
+ </TTGlyph>
+ </glyf>
+
+ <name>
+ <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="2" platformID="1" platEncID="0" langID="0x0" unicode="True">
+ Regular
+ </namerecord>
+ <namerecord nameID="4" platformID="1" platEncID="0" langID="0x0" unicode="True">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="6" platformID="1" platEncID="0" langID="0x0" unicode="True">
+ SampleFont-Regular
+ </namerecord>
+ <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+ Regular
+ </namerecord>
+ <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+ SampleFont-Regular
+ </namerecord>
+ </name>
+
+ <post>
+ <formatType value="3.0"/>
+ <italicAngle value="0.0"/>
+ <underlinePosition value="-75"/>
+ <underlineThickness value="50"/>
+ <isFixedPitch value="0"/>
+ <minMemType42 value="0"/>
+ <maxMemType42 value="0"/>
+ <minMemType1 value="0"/>
+ <maxMemType1 value="0"/>
+ </post>
+
+</ttFont>
diff --git a/tests/tests/content/src/android/content/pm/cts/InstantAppTest.java b/tests/tests/content/src/android/content/pm/cts/InstantAppTest.java
new file mode 100644
index 0000000..81263d1
--- /dev/null
+++ b/tests/tests/content/src/android/content/pm/cts/InstantAppTest.java
@@ -0,0 +1,75 @@
+/*
+ * 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.content.pm.cts;
+
+
+import android.content.cts.MockActivity;
+
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
+import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
+
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.net.Uri;
+import android.test.AndroidTestCase;
+
+import java.io.File;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Test instant apps.
+ */
+public class InstantAppTest extends AndroidTestCase {
+ private static final String PACKAGE_MIME_TYPE = "application/vnd.android.package-archive";
+
+ private PackageManager mPackageManager;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mPackageManager = getContext().getPackageManager();
+ }
+
+ /** Ensure only one resolver is defined */
+ public void testInstantAppResolverQuery() {
+ final Intent resolverIntent = new Intent(Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE);
+ final int resolveFlags =
+ MATCH_DIRECT_BOOT_AWARE
+ | MATCH_DIRECT_BOOT_UNAWARE
+ | MATCH_SYSTEM_ONLY;
+ final List<ResolveInfo> matches =
+ mPackageManager.queryIntentServices(resolverIntent, resolveFlags);
+ assertTrue(matches == null || matches.size() <= 1);
+ }
+
+ /** Ensure only one resolver is defined */
+ public void testInstantAppInstallerQuery() {
+ final Intent intent = new Intent(Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE);
+ intent.addCategory(Intent.CATEGORY_DEFAULT);
+ intent.setDataAndType(Uri.fromFile(new File("foo.apk")), PACKAGE_MIME_TYPE);
+ final int resolveFlags =
+ MATCH_DIRECT_BOOT_AWARE
+ | MATCH_DIRECT_BOOT_UNAWARE
+ | MATCH_SYSTEM_ONLY;
+ final List<ResolveInfo> matches =
+ mPackageManager.queryIntentActivities(intent, resolveFlags);
+ assertTrue(matches == null || matches.size() <= 1);
+ }
+}
diff --git a/tests/tests/content/src/android/content/res/cts/ResourcesTest.java b/tests/tests/content/src/android/content/res/cts/ResourcesTest.java
index 89ea29b..e8fe4bf 100644
--- a/tests/tests/content/src/android/content/res/cts/ResourcesTest.java
+++ b/tests/tests/content/src/android/content/res/cts/ResourcesTest.java
@@ -814,6 +814,30 @@
} catch (RuntimeException e) {
// pass
}
+
+ try {
+ mResources.getFont(R.font.invalid_xmlfont_nosource);
+ fail();
+ } catch (RuntimeException e) {
+ // pass
+ }
+
+ }
+
+ public void testGetFont_brokenFontFiles() {
+ try {
+ mResources.getFont(R.font.brokenfont);
+ fail();
+ } catch (RuntimeException e) {
+ // pass
+ }
+
+ try {
+ mResources.getFont(R.font.broken_xmlfont);
+ fail();
+ } catch (RuntimeException e) {
+ // pass
+ }
}
public void testGetFont_fontFileIsCached() {
@@ -829,4 +853,18 @@
assertEquals(font, font2);
}
+
+ public void testGetFont_resolveByFontTable() {
+ assertEquals(Typeface.NORMAL, mResources.getFont(R.font.sample_regular_font).getStyle());
+ assertEquals(Typeface.BOLD, mResources.getFont(R.font.sample_bold_font).getStyle());
+ assertEquals(Typeface.ITALIC, mResources.getFont(R.font.sample_italic_font).getStyle());
+ assertEquals(Typeface.BOLD_ITALIC,
+ mResources.getFont(R.font.sample_bolditalic_font).getStyle());
+
+ assertEquals(Typeface.NORMAL, mResources.getFont(R.font.sample_regular_family).getStyle());
+ assertEquals(Typeface.BOLD, mResources.getFont(R.font.sample_bold_family).getStyle());
+ assertEquals(Typeface.ITALIC, mResources.getFont(R.font.sample_italic_family).getStyle());
+ assertEquals(Typeface.BOLD_ITALIC,
+ mResources.getFont(R.font.sample_bolditalic_family).getStyle());
+ }
}
diff --git a/tests/tests/graphics/Android.mk b/tests/tests/graphics/Android.mk
index 3059d71..af59cbf 100644
--- a/tests/tests/graphics/Android.mk
+++ b/tests/tests/graphics/Android.mk
@@ -40,6 +40,9 @@
# Tag this module as a cts test artifact
LOCAL_COMPATIBILITY_SUITE := cts
+# Enforce public / test api only
+LOCAL_SDK_VERSION := test_current
+
include $(BUILD_CTS_PACKAGE)
include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/tests/graphics/res/drawable/vector_icon_size_1.xml b/tests/tests/graphics/res/drawable/vector_icon_size_1.xml
new file mode 100644
index 0000000..1c57e30
--- /dev/null
+++ b/tests/tests/graphics/res/drawable/vector_icon_size_1.xml
@@ -0,0 +1,24 @@
+<!--
+ 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="7dp"
+ android:height="7dp"
+ android:viewportWidth="20.0"
+ android:viewportHeight="50.0">
+ <path
+ android:pathData="M14.285706,47.362198A50.71429,62.14286 0,0 0,1.0630035 5.5146027"
+ android:fillColor="#ff55ff"/>
+</vector>
\ No newline at end of file
diff --git a/tests/tests/graphics/res/drawable/vector_icon_size_2.xml b/tests/tests/graphics/res/drawable/vector_icon_size_2.xml
new file mode 100644
index 0000000..1069e5c
--- /dev/null
+++ b/tests/tests/graphics/res/drawable/vector_icon_size_2.xml
@@ -0,0 +1,24 @@
+<!--
+ 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="9dp"
+ android:height="9dp"
+ android:viewportWidth="20.0"
+ android:viewportHeight="50.0">
+ <path
+ android:pathData="M14.285706,47.362198A50.71429,62.14286 0,0 0,1.0630035 5.5146027"
+ android:fillColor="#ff55ff"/>
+</vector>
\ No newline at end of file
diff --git a/tests/tests/graphics/src/android/graphics/cts/BitmapTest.java b/tests/tests/graphics/src/android/graphics/cts/BitmapTest.java
index 0d43322..fc23fb2 100644
--- a/tests/tests/graphics/src/android/graphics/cts/BitmapTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/BitmapTest.java
@@ -29,9 +29,11 @@
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
+import android.graphics.ColorSpace;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.os.Parcel;
+import android.os.StrictMode;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -389,6 +391,51 @@
}
@Test
+ public void testCreateBitmap_displayMetrics_mutable() {
+ DisplayMetrics metrics =
+ InstrumentationRegistry.getTargetContext().getResources().getDisplayMetrics();
+
+ Bitmap bitmap;
+ bitmap = Bitmap.createBitmap(metrics, 10, 10, Config.ARGB_8888);
+ assertTrue(bitmap.isMutable());
+ assertEquals(metrics.densityDpi, bitmap.getDensity());
+
+ bitmap = Bitmap.createBitmap(metrics, 10, 10, Config.ARGB_8888);
+ assertTrue(bitmap.isMutable());
+ assertEquals(metrics.densityDpi, bitmap.getDensity());
+
+ bitmap = Bitmap.createBitmap(metrics, 10, 10, Config.ARGB_8888, true);
+ assertTrue(bitmap.isMutable());
+ assertEquals(metrics.densityDpi, bitmap.getDensity());
+
+ bitmap = Bitmap.createBitmap(metrics, 10, 10, Config.ARGB_8888, true, ColorSpace.get(
+ ColorSpace.Named.SRGB));
+
+ assertTrue(bitmap.isMutable());
+ assertEquals(metrics.densityDpi, bitmap.getDensity());
+
+ int[] colors = createColors(100);
+ assertNotNull(Bitmap.createBitmap(metrics, colors, 0, 10, 10, 10, Config.ARGB_8888));
+ assertNotNull(Bitmap.createBitmap(metrics, colors, 10, 10, Config.ARGB_8888));
+ }
+
+ @Test
+ public void testCreateBitmap_displayMetrics_immutable() {
+ DisplayMetrics metrics =
+ InstrumentationRegistry.getTargetContext().getResources().getDisplayMetrics();
+ int[] colors = createColors(100);
+
+ Bitmap bitmap;
+ bitmap = Bitmap.createBitmap(metrics, colors, 0, 10, 10, 10, Config.ARGB_8888);
+ assertFalse(bitmap.isMutable());
+ assertEquals(metrics.densityDpi, bitmap.getDensity());
+
+ bitmap = Bitmap.createBitmap(metrics, colors, 10, 10, Config.ARGB_8888);
+ assertFalse(bitmap.isMutable());
+ assertEquals(metrics.densityDpi, bitmap.getDensity());
+ }
+
+ @Test
public void testCreateScaledBitmap() {
mBitmap = Bitmap.createBitmap(100, 200, Config.RGB_565);
Bitmap ret = Bitmap.createScaledBitmap(mBitmap, 50, 100, false);
@@ -398,6 +445,27 @@
}
@Test
+ public void testGenerationId() {
+ Bitmap bitmap = Bitmap.createBitmap(10, 10, Config.ARGB_8888);
+ int genId = bitmap.getGenerationId();
+ assertEquals("not expected to change", genId, bitmap.getGenerationId());
+ bitmap.setDensity(bitmap.getDensity() + 4);
+ assertEquals("not expected to change", genId, bitmap.getGenerationId());
+ bitmap.getPixel(0, 0);
+ assertEquals("not expected to change", genId, bitmap.getGenerationId());
+
+ int beforeGenId = bitmap.getGenerationId();
+ bitmap.eraseColor(Color.WHITE);
+ int afterGenId = bitmap.getGenerationId();
+ assertTrue("expected to increase", afterGenId > beforeGenId);
+
+ beforeGenId = bitmap.getGenerationId();
+ bitmap.setPixel(4, 4, Color.BLUE);
+ afterGenId = bitmap.getGenerationId();
+ assertTrue("expected to increase again", afterGenId > beforeGenId);
+ }
+
+ @Test
public void testDescribeContents() {
assertEquals(0, mBitmap.describeContents());
}
@@ -1333,6 +1401,68 @@
}
@Test
+ public void testCopyHWBitmapInStrictMode() {
+ strictModeTest(()->{
+ Bitmap bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
+ Bitmap hwBitmap = bitmap.copy(Config.HARDWARE, false);
+ hwBitmap.copy(Config.ARGB_8888, false);
+ });
+ }
+
+ @Test
+ public void testCreateScaledFromHWInStrictMode() {
+ strictModeTest(()->{
+ Bitmap bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
+ Bitmap hwBitmap = bitmap.copy(Config.HARDWARE, false);
+ Bitmap.createScaledBitmap(hwBitmap, 200, 200, false);
+ });
+ }
+
+ @Test
+ public void testExtractAlphaFromHWInStrictMode() {
+ strictModeTest(()->{
+ Bitmap bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
+ Bitmap hwBitmap = bitmap.copy(Config.HARDWARE, false);
+ hwBitmap.extractAlpha();
+ });
+ }
+
+ @Test
+ public void testCompressInStrictMode() {
+ strictModeTest(()->{
+ Bitmap bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
+ bitmap.compress(CompressFormat.JPEG, 90, new ByteArrayOutputStream());
+ });
+ }
+
+ @Test
+ public void testParcelHWInStrictMode() {
+ strictModeTest(()->{
+ mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
+ Bitmap hwBitmap = mBitmap.copy(Config.HARDWARE, false);
+ hwBitmap.writeToParcel(Parcel.obtain(), 0);
+ });
+ }
+
+ @Test
+ public void testSameAsFirstHWInStrictMode() {
+ strictModeTest(()->{
+ Bitmap bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
+ Bitmap hwBitmap = bitmap.copy(Config.HARDWARE, false);
+ hwBitmap.sameAs(bitmap);
+ });
+ }
+
+ @Test
+ public void testSameAsSecondHWInStrictMode() {
+ strictModeTest(()->{
+ Bitmap bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
+ Bitmap hwBitmap = bitmap.copy(Config.HARDWARE, false);
+ bitmap.sameAs(hwBitmap);
+ });
+ }
+
+ @Test
public void testNdkAccessAfterRecycle() {
Bitmap bitmap = Bitmap.createBitmap(10, 20, Config.RGB_565);
nValidateBitmapInfo(bitmap, 10, 20, true);
@@ -1341,6 +1471,20 @@
nValidateNdkAccessAfterRecycle(bitmap);
}
+ private void strictModeTest(Runnable runnable) {
+ StrictMode.ThreadPolicy originalPolicy = StrictMode.getThreadPolicy();
+ StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
+ .detectCustomSlowCalls().penaltyDeath().build());
+ try {
+ runnable.run();
+ fail("Shouldn't reach it");
+ } catch (RuntimeException expected){
+ // expect to receive StrictModeViolation
+ } finally {
+ StrictMode.setThreadPolicy(originalPolicy);
+ }
+ }
+
private static native void nValidateBitmapInfo(Bitmap bitmap, int width, int height,
boolean is565);
private static native void nValidateNdkAccessAfterRecycle(Bitmap bitmap);
diff --git a/tests/tests/graphics/src/android/graphics/cts/CanvasTest.java b/tests/tests/graphics/src/android/graphics/cts/CanvasTest.java
index 52db21f..b36c23a 100644
--- a/tests/tests/graphics/src/android/graphics/cts/CanvasTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/CanvasTest.java
@@ -1153,6 +1153,13 @@
}
@Test
+ public void testClipOutPath() {
+ final Path p = new Path();
+ p.addRect(new RectF(5, 5, 10, 10), Direction.CW);
+ assertTrue(mCanvas.clipOutPath(p));
+ }
+
+ @Test
public void testClipInversePath() {
final Path p = new Path();
p.addRoundRect(new RectF(0, 0, 10, 10), 0.5f, 0.5f, Direction.CW);
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/AdaptiveIconDrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/AdaptiveIconDrawableTest.java
index 5ade66e..54021b9 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/AdaptiveIconDrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/AdaptiveIconDrawableTest.java
@@ -1,17 +1,18 @@
-package android.graphics.drawable;
+package android.graphics.drawable.cts;
import static org.junit.Assert.assertTrue;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
-import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
-import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Path.Direction;
import android.graphics.Rect;
import android.graphics.Region;
+import android.graphics.drawable.AdaptiveIconDrawable;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
import android.test.AndroidTestCase;
import android.util.Log;
import java.io.File;
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/AdaptiveIconMaskTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/AdaptiveIconMaskTest.java
index 5102ef9..99d1327 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/AdaptiveIconMaskTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/AdaptiveIconMaskTest.java
@@ -30,10 +30,10 @@
import android.graphics.Region.Op;
import android.graphics.drawable.AdaptiveIconDrawable;
import android.graphics.drawable.ColorDrawable;
+import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.util.Log;
-import android.util.PathParser;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -53,6 +53,8 @@
private ColorDrawable mBlueDrawable;
private ColorDrawable mRedDrawable;
private AdaptiveIconDrawable mDrawable;
+ private static final float sMaskSize = AdaptiveIconDrawable.MASK_SIZE;
+ private boolean mUseRoundIcon;
@Before
public void setup() {
@@ -60,12 +62,15 @@
mRedDrawable = new ColorDrawable(Color.RED);
mDrawable = new AdaptiveIconDrawable( mBlueDrawable, mRedDrawable);
- String path = Resources.getSystem().getString(com.android.internal.R.string.config_icon_mask);
- L("config_icon_mask: " + path);
- mMask = PathParser.createPathFromPathData(path);
+ mMask = mDrawable.getIconMask();
+
int sInset = (int) (SAFEZONE_INSET * AdaptiveIconDrawable.MASK_SIZE);
mSafeZone.addCircle(AdaptiveIconDrawable.MASK_SIZE/2, AdaptiveIconDrawable.MASK_SIZE/2,
AdaptiveIconDrawable.MASK_SIZE/2/2 - sInset, Direction.CW);
+ mUseRoundIcon = Resources.getSystem().getBoolean(
+ InstrumentationRegistry.getTargetContext().getResources().getIdentifier(
+ "config_useRoundIcon", "bool", "android"));
+ L("config_useRoundIcon:" + mUseRoundIcon);
}
@Test
@@ -75,7 +80,6 @@
// Bounds should be [100 x 100]
RectF bounds = new RectF();
mMask.computeBounds(bounds, true);
- System.out.println("MERONG:" + bounds.toShortString());
assertTrue("Mask top should be larger than or equal to 0", -DELTA <= bounds.top);
assertTrue("Mask left should be larger than or equal to 0", -DELTA <= bounds.left);
assertTrue("Mask bottom should be smaller than or equal to" +
@@ -112,7 +116,6 @@
@Test
public void testDeviceConfigMask_isConvex() {
assertNotNull(mMask);
-
assertTrue("Mask is not convex", mMask.isConvex());
}
@@ -123,4 +126,28 @@
assertEquals("Foreground layer is not the same.",
mRedDrawable, mDrawable.getForeground());
}
+
+ @Test
+ public void testDeviceConfig_iconMask_useRoundIcon() {
+ assertNotNull(mMask);
+
+ boolean circleMask = isCircle(mMask);
+ // If mask shape is circle, then mUseRoundIcon should be defined and should be true.
+ // If mask shape is not a circle
+ // mUseRoundIcon doesn't have to be defined.
+ // if mUseRoundIcon is defined, then should be false
+ assertEquals(mUseRoundIcon, circleMask);
+ }
+
+ private boolean isCircle(Path maskPath) {
+ Path circle101 = new Path();
+ circle101.addCircle(50, 50, 50.5f, Direction.CCW);
+ Path circle99 = new Path();
+ circle99.addCircle(50, 50, 49.5f, Direction.CCW);
+ circle99.op(maskPath, Path.Op.DIFFERENCE);
+ boolean fullyEnclosesSmallerCircle = circle99.isEmpty();
+ maskPath.op(circle101, Path.Op.DIFFERENCE);
+ boolean fullyEnclosedByLargerCircle = maskPath.isEmpty();
+ return fullyEnclosedByLargerCircle && fullyEnclosesSmallerCircle;
+ }
}
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/VectorDrawableSizeTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/VectorDrawableSizeTest.java
new file mode 100644
index 0000000..0b50f35
--- /dev/null
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/VectorDrawableSizeTest.java
@@ -0,0 +1,87 @@
+/*
+ * 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.graphics.drawable.cts;
+
+import static org.junit.Assert.assertEquals;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.cts.R;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.VectorDrawable;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+import java.util.Arrays;
+
+@MediumTest
+@RunWith(Parameterized.class)
+public class VectorDrawableSizeTest {
+ private Context mContext = null;
+ private Resources mResources = null;
+ private int mResId;
+ private int mDpSize;
+
+ @Parameters ( name = "{0}" )
+ public static Iterable<Object[]> data() {
+ return Arrays.asList(new Object[][]{
+ {"size_1", R.drawable.vector_icon_size_1, 7},
+ {"size_2", R.drawable.vector_icon_size_2, 9}
+ });
+ }
+
+ public VectorDrawableSizeTest(String name, int resId, int dp) throws Throwable {
+ mResId = resId;
+ mDpSize = dp;
+ }
+
+ @Before
+ public void setup() {
+ mContext = InstrumentationRegistry.getTargetContext();
+ mResources = mContext.getResources();
+
+ }
+
+ @Test
+ public void testVectorDrawableSize() throws Throwable {
+ // This test makes sure the size computation for VectorDrawable is using round, instead of
+ // rounding down.
+ final int densityDpi = mResources.getConfiguration().densityDpi;
+
+ VectorDrawable drawable = (VectorDrawable) mResources.getDrawable(mResId, null);
+ assertEquals(Math.round(mDpSize * densityDpi / 160f), drawable.getIntrinsicWidth());
+ assertEquals(Math.round(mDpSize * densityDpi / 160f), drawable.getIntrinsicHeight());
+
+ final Drawable.ConstantState constantState = drawable.getConstantState();
+
+ DrawableTestUtils.setResourcesDensity(mResources, densityDpi / 2);
+ final VectorDrawable halfDrawable =
+ (VectorDrawable) constantState.newDrawable(mResources);
+
+ assertEquals(Math.round(mDpSize * densityDpi / 320f), halfDrawable.getIntrinsicWidth());
+ assertEquals(Math.round(mDpSize * densityDpi / 320f), halfDrawable.getIntrinsicHeight());
+
+ // Reset the density
+ DrawableTestUtils.setResourcesDensity(mResources, densityDpi);
+ }
+}
diff --git a/tests/tests/jni/libjnitest/android_jni_cts_LinkerNamespacesTest.cpp b/tests/tests/jni/libjnitest/android_jni_cts_LinkerNamespacesTest.cpp
index 4721afb..ec81980 100644
--- a/tests/tests/jni/libjnitest/android_jni_cts_LinkerNamespacesTest.cpp
+++ b/tests/tests/jni/libjnitest/android_jni_cts_LinkerNamespacesTest.cpp
@@ -47,18 +47,23 @@
// This is not the complete list - just a small subset
// of the libraries that should reside in /system/lib
+// for app-compatibility reasons.
// (in addition to kSystemPublicLibraries)
static std::vector<std::string> kSystemLibraries = {
"libart.so",
"libandroid_runtime.so",
"libbinder.so",
+ "libcrypto.so",
"libcutils.so",
+ "libexpat.so",
"libgui.so",
"libmedia.so",
"libnativehelper.so",
"libstagefright.so",
+ "libsqlite.so",
"libui.so",
"libutils.so",
+ "libvorbisidec.so",
};
static bool is_directory(const std::string& path) {
@@ -70,22 +75,20 @@
return false;
}
-static bool already_loaded(const std::string& library, const std::string& err) {
- // SELinux denials for /vendor libraries may return with library not found
- if (err.find("dlopen failed: library \"" + library + "\"") == 0 &&
- (err.find("not found") != std::string::npos ||
- err.find("is not accessible for the namespace \"classloader-namespace\"") != std::string::npos)) {
- return true;
- }
-
- return false;
+static bool not_accessible(const std::string& library, const std::string& err) {
+ return err.find("dlopen failed: library \"" + library + "\"") == 0 &&
+ err.find("is not accessible for the namespace \"classloader-namespace\"") != std::string::npos;
}
-static bool wrong_arch(const std::string& err) {
+static bool not_found(const std::string& library, const std::string& err) {
+ return err == "dlopen failed: library \"" + library + "\" not found";
+}
+
+static bool wrong_arch(const std::string& library, const std::string& err) {
// https://issuetracker.google.com/37428428
// It's okay to not be able to load a library because it's for another
// architecture (typically on an x86 device, when we come across an arm library).
- return err.find("unexpected e_machine: ") != std::string::npos;
+ return err.find("dlopen failed: library \"" + library + "\" has unexpected e_machine: ") == 0;
}
static bool check_lib(const std::string& path,
@@ -114,7 +117,7 @@
} else { // (handle == nullptr && !shouldBeAccessible(path))
// Check the error message
std::string err = dlerror();
- if (!already_loaded(path, err) && !wrong_arch(err)) {
+ if (!not_accessible(path, err) && !not_found(path, err) && !wrong_arch(path, err)) {
errors->push_back("unexpected dlerror: " + err);
return false;
}
@@ -240,9 +243,9 @@
void* handle = dlopen(library.c_str(), RTLD_NOW);
if (handle == nullptr) {
std::string err = dlerror();
- // If the library is already loaded, then dlopen failing is okay.
- if (!already_loaded(library, err)) {
- errors.push_back("Mandatory system library \"" + library + "\" failed to load: " + err);
+ // The libraries should be present and produce specific dlerror when inaccessible.
+ if (!not_accessible(library, err)) {
+ errors.push_back("Mandatory system library \"" + library + "\" failed to load with unexpected error: " + err);
success = false;
}
} else {
diff --git a/tests/tests/location/AndroidManifest.xml b/tests/tests/location/AndroidManifest.xml
index f9985ba..1357a38 100644
--- a/tests/tests/location/AndroidManifest.xml
+++ b/tests/tests/location/AndroidManifest.xml
@@ -27,6 +27,10 @@
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS"/>
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+ <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />>
+ <uses-permission android:name="android.permission.INTERNET" />
+
<instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
android:targetPackage="android.location.cts"
android:label="CTS tests of android.location">
diff --git a/tests/tests/location/src/android/location/cts/GnssLocationValuesTest.java b/tests/tests/location/src/android/location/cts/GnssLocationValuesTest.java
new file mode 100644
index 0000000..5fa09c2
--- /dev/null
+++ b/tests/tests/location/src/android/location/cts/GnssLocationValuesTest.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2017 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.location.cts;
+
+import android.location.Criteria;
+import android.location.Location;
+import android.location.LocationManager;
+import android.util.Log;
+
+/**
+ * Test the {@link Location} values.
+ *
+ * Test steps:
+ * 1. Register for location updates.
+ * 2. Wait for {@link #LOCATION_TO_COLLECT_COUNT} locations.
+ * 3.1 Confirm locations have been found.
+ * 3. Get LastKnownLocation, verified all fields are in the correct range.
+ */
+public class GnssLocationValuesTest extends GnssTestCase {
+
+ private static final String TAG = "GnssLocationValuesTest";
+ private static final int LOCATION_TO_COLLECT_COUNT = 5;
+ private TestLocationListener mLocationListener;
+ private static final int LOCATION_UNCERTIANTY_MIN_YEAR = 2017;
+ private boolean extendedLocationAccuracyExpected = false;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mTestLocationManager = new TestLocationManager(getContext());
+ int gnssYearOfHardware = mTestLocationManager.getLocationManager().getGnssYearOfHardware();
+ if(gnssYearOfHardware >= LOCATION_UNCERTIANTY_MIN_YEAR) {
+ extendedLocationAccuracyExpected = true;
+ }
+ mLocationListener = new TestLocationListener(LOCATION_TO_COLLECT_COUNT);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ // Unregister listeners
+ if (mLocationListener != null) {
+ mTestLocationManager.removeLocationUpdates(mLocationListener);
+ }
+ super.tearDown();
+ }
+
+ /**
+ * Those accuracy fields are new O-features,
+ * only test them if the hardware is later than 2017
+ */
+ public void testAccuracyFields() throws Exception {
+ SoftAssert softAssert = new SoftAssert(TAG);
+ mTestLocationManager.requestLocationUpdates(mLocationListener);
+ boolean success = mLocationListener.await();
+ SoftAssert.failOrWarning(isMeasurementTestStrict(),
+ "Time elapsed without getting the GPS locations."
+ + " Possibly, the test has been run deep indoors."
+ + " Consider retrying test outdoors.",
+ success);
+
+ for (Location location : mLocationListener.getReceivedLocationList()) {
+ checkLocationAccuracyFields(softAssert, location,
+ extendedLocationAccuracyExpected);
+ }
+
+ softAssert.assertAll();
+ }
+
+ public static void checkLocationAccuracyFields(SoftAssert softAssert,
+ Location location, boolean extendedLocationAccuracyExpected) {
+ softAssert.assertTrue("All locations generated by the LocationManager "
+ + "should have a horizontal accuracy.", location.hasAccuracy());
+ if (location.hasAccuracy()) {
+ softAssert.assertTrue("Location Accuracy should be greater than 0.",
+ location.getAccuracy() > 0);
+ }
+
+ if(!extendedLocationAccuracyExpected) {
+ return;
+ }
+ Log.i(TAG, "This is a device from 2017 or later.");
+
+ softAssert.assertTrue("All GPS locations generated by the LocationManager "
+ + "must have a bearing accuracy.", location.hasBearingAccuracy());
+ if (location.hasBearingAccuracy()) {
+ softAssert.assertTrue("Bearing Accuracy should be greater than 0.",
+ location.getBearingAccuracyDegrees() > 0);
+ }
+ softAssert.assertTrue("All GPS locations generated by the LocationManager "
+ + "must have a speed accuracy.", location.hasSpeedAccuracy());
+ if (location.hasSpeedAccuracy()) {
+ softAssert.assertTrue("Speed Accuracy should be greater than 0.",
+ location.getSpeedAccuracyMetersPerSecond() > 0);
+ }
+ softAssert.assertTrue("All GPS locations generated by the LocationManager "
+ + "must have a vertical accuracy.", location.hasVerticalAccuracy());
+ if (location.hasVerticalAccuracy()) {
+ softAssert.assertTrue("Vertical Accuracy should be greater than 0.",
+ location.getVerticalAccuracyMeters() > 0);
+ }
+ }
+
+ /**
+ * Get the location info from the device
+ * check whether all fields' value make sense
+ */
+ public void testLocationRegularFields() throws Exception {
+ mTestLocationManager.requestLocationUpdates(mLocationListener);
+ boolean success = mLocationListener.await();
+ SoftAssert.failOrWarning(isMeasurementTestStrict(),
+ "Time elapsed without getting the GPS locations."
+ + " Possibly, the test has been run deep indoors."
+ + " Consider retrying test outdoors.",
+ success);
+
+ SoftAssert softAssert = new SoftAssert(TAG);
+ for (Location location : mLocationListener.getReceivedLocationList()) {
+ checkLocationRegularFields(softAssert, location);
+ }
+
+ softAssert.assertAll();
+ }
+
+ public static void checkLocationRegularFields(SoftAssert softAssert, Location location) {
+ // For the altitude: the unit is meter
+ // The lowest exposed land on Earth is at the Dead Sea shore, at -413 meters.
+ // Whilst University of Tokyo Atacama Obsevatory is on 5,640m above sea level.
+ if(location.hasAltitude()) {
+ softAssert.assertTrue("Altitude should be greater than -500 (meters).",
+ location.getAltitude() >= -500);
+ softAssert.assertTrue("Altitude should be less than 6000 (meters).",
+ location.getAltitude() < 6000);
+ }
+
+ // It is guaranteed to be in the range [0.0, 360.0] if the device has a bearing.
+ // The API will return 0.0 if there is no bearing
+ if(location.hasBearing()) {
+ softAssert.assertTrue("Bearing should be in the range of [0.0, 360.0]",
+ location.getBearing() >= 0 && location.getBearing() <= 360);
+ }
+
+ softAssert.assertTrue("ElapsedRaltimeNanos should be great than 0.",
+ location.getElapsedRealtimeNanos() > 0);
+
+ assertEquals("gps", location.getProvider());
+ assertTrue(location.getTime() > 0);
+
+ softAssert.assertTrue("Longitude should be in the range of [-180.0, 180.0] degrees",
+ location.getLongitude() >= -180 && location.getLongitude() <= 180);
+
+ softAssert.assertTrue("Latitude should be in the range of [-90.0, 90.0] degrees",
+ location.getLatitude() >= -90 && location.getLatitude() <= 90);
+
+ // For the speed, during the cts test device shouldn't move faster than 1m/s
+ if(location.hasSpeed()) {
+ softAssert.assertTrue("In the test enviorment, speed should be in the range of [0, 1] m/s",
+ location.getSpeed() >= 0 && location.getSpeed() <= 1);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/tests/tests/location/src/android/location/cts/GnssPseudorangeVerificationTest.java b/tests/tests/location/src/android/location/cts/GnssPseudorangeVerificationTest.java
new file mode 100644
index 0000000..709fb3a
--- /dev/null
+++ b/tests/tests/location/src/android/location/cts/GnssPseudorangeVerificationTest.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2017 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.location.cts;
+
+
+import android.location.GnssMeasurement;
+import android.location.GnssMeasurementsEvent;
+import android.util.Log;
+import com.android.compatibility.common.util.CddTest;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * Test computing and verifying the pseudoranges based on the raw measurements
+ * reported by the GNSS chipset
+ */
+public class GnssPseudorangeVerificationTest extends GnssTestCase {
+ private static final String TAG = "GnssPseudorangeValTest";
+ private static final int LOCATION_TO_COLLECT_COUNT = 5;
+ private static final int MIN_SATELLITES_REQUIREMENT = 4;
+ private static final double SECONDS_PER_NANO = 1.0e-9;
+
+ private TestGnssMeasurementListener mMeasurementListener;
+ private TestLocationListener mLocationListener;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ mTestLocationManager = new TestLocationManager(getContext());
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ // Unregister listeners
+ if (mLocationListener != null) {
+ mTestLocationManager.removeLocationUpdates(mLocationListener);
+ }
+ if (mMeasurementListener != null) {
+ mTestLocationManager.unregisterGnssMeasurementCallback(mMeasurementListener);
+ }
+ super.tearDown();
+ }
+
+ /**
+ * Tests that one can listen for {@link GnssMeasurementsEvent} for collection purposes.
+ * It only performs sanity checks for the measurements received.
+ * This tests uses actual data retrieved from Gnss HAL.
+ */
+ @CddTest(requirement="7.3.3")
+ public void testPseudorangeValue() throws Exception {
+ // Checks if Gnss hardware feature is present, skips test (pass) if not,
+ // and hard asserts that Location/Gnss (Provider) is turned on if is Cts Verifier.
+ if (!TestMeasurementUtil.canTestRunOnCurrentDevice(mTestLocationManager,
+ TAG, MIN_HARDWARE_YEAR_MEASUREMENTS_REQUIRED, isCtsVerifierTest())) {
+ return;
+ }
+
+ mLocationListener = new TestLocationListener(LOCATION_TO_COLLECT_COUNT);
+ mTestLocationManager.requestLocationUpdates(mLocationListener);
+
+ mMeasurementListener = new TestGnssMeasurementListener(TAG);
+ mTestLocationManager.registerGnssMeasurementCallback(mMeasurementListener);
+
+ boolean success = mLocationListener.await();
+ SoftAssert.failOrWarning(isMeasurementTestStrict(),
+ "Time elapsed without getting enough location fixes."
+ + " Possibly, the test has been run deep indoors."
+ + " Consider retrying test outdoors.",
+ success);
+
+ Log.i(TAG, "Location status received = " + mLocationListener.isLocationReceived());
+
+ if (!mMeasurementListener.verifyStatus(isMeasurementTestStrict())) {
+ // If test is strict and verifyStatus reutrns false, an assert exception happens and
+ // test fails. If test is not strict, we arrive here, and:
+ return; // exit (with pass)
+ }
+
+ List<GnssMeasurementsEvent> events = mMeasurementListener.getEvents();
+ int eventCount = events.size();
+ Log.i(TAG, "Number of GNSS measurement events received = " + eventCount);
+ SoftAssert.failOrWarning(isMeasurementTestStrict(),
+ "GnssMeasurementEvent count: expected > 0, received = " + eventCount,
+ eventCount > 0);
+
+ SoftAssert softAssert = new SoftAssert(TAG);
+
+ boolean hasEventWithEnoughMeasurements = false;
+ // we received events, so perform a quick sanity check on mandatory fields
+ for (GnssMeasurementsEvent event : events) {
+ // Verify Gnss Event mandatory fields are in required ranges
+ assertNotNull("GnssMeasurementEvent cannot be null.", event);
+
+
+ long timeInNs = event.getClock().getTimeNanos();
+ TestMeasurementUtil.assertGnssClockFields(event.getClock(), softAssert, timeInNs);
+
+ // we need at least 4 satellites to calculate the pseudorange
+ if(event.getMeasurements().size() >= MIN_SATELLITES_REQUIREMENT) {
+ validatePseudorange(event.getMeasurements(), softAssert, timeInNs);
+ hasEventWithEnoughMeasurements = true;
+ }
+ }
+
+ SoftAssert.failOrWarning(isMeasurementTestStrict(),
+ "Should have at least one GnssMeasurementEvent with at least 4"
+ + "GnssMeasurement. If failed, retry near window or outdoors?",
+ hasEventWithEnoughMeasurements);
+
+ softAssert.assertAll();
+ }
+
+ /**
+ * Uses the common reception time approach to calculate pseudorange time
+ * measurements reported by the receiver according to http://cdn.intechopen.com/pdfs-wm/27712.pdf.
+ */
+ private void validatePseudorange(Collection<GnssMeasurement> measurements,
+ SoftAssert softAssert, long timeInNs) {
+ long largestReceivedSvTimeNanos = 0;
+ for(GnssMeasurement measurement : measurements) {
+ if (largestReceivedSvTimeNanos < measurement.getReceivedSvTimeNanos()) {
+ largestReceivedSvTimeNanos = measurement.getReceivedSvTimeNanos();
+ }
+ }
+ for (GnssMeasurement measurement : measurements) {
+ double deltaiNanos = largestReceivedSvTimeNanos
+ - measurement.getReceivedSvTimeNanos();
+ double deltaiSeconds = deltaiNanos * SECONDS_PER_NANO;
+ // according to http://cdn.intechopen.com/pdfs-wm/27712.pdf
+ // the pseudorange in time is 65-83 ms, which is 18 ms range,
+ // deltaiSeconds should be in the range of [0.0, 0.018] seconds
+ softAssert.assertTrue("deltaiSeconds in Seconds." ,
+ timeInNs,
+ "0.0 <= deltaiSeconds <= 0.018",
+ String.valueOf(deltaiSeconds),
+ (deltaiSeconds >= 0.0 && deltaiSeconds <= 0.018));
+ }
+
+ }
+}
diff --git a/tests/tests/location/src/android/location/cts/GnssStatusTest.java b/tests/tests/location/src/android/location/cts/GnssStatusTest.java
index e8f3cb13..fa35438 100644
--- a/tests/tests/location/src/android/location/cts/GnssStatusTest.java
+++ b/tests/tests/location/src/android/location/cts/GnssStatusTest.java
@@ -1,6 +1,7 @@
package android.location.cts;
import android.location.GnssStatus;
+import android.util.Log;
public class GnssStatusTest extends GnssTestCase {
@@ -28,6 +29,11 @@
// Register Gps Status Listener.
TestGnssStatusCallback testGnssStatusCallback =
new TestGnssStatusCallback(TAG, STATUS_TO_COLLECT_COUNT);
+ checkGnssChange(testGnssStatusCallback);
+ }
+
+ private void checkGnssChange(TestGnssStatusCallback testGnssStatusCallback)
+ throws InterruptedException {
mTestLocationManager.registerGnssStatusCallback(testGnssStatusCallback);
TestLocationListener locationListener = new TestLocationListener(LOCATION_TO_COLLECT_COUNT);
@@ -46,4 +52,70 @@
+ " Consider retrying test outdoors.",
success);
}
+
+ /**
+ * Tests values of {@link GnssStatus}.
+ */
+ public void testGnssStatusValues() throws InterruptedException {
+ // Checks if GPS hardware feature is present, skips test (pass) if not,
+ // and hard asserts that Location/GPS (Provider) is turned on if is Cts Verifier.
+ if (!TestMeasurementUtil.canTestRunOnCurrentDevice(mTestLocationManager,
+ TAG, MIN_HARDWARE_YEAR_MEASUREMENTS_REQUIRED, isCtsVerifierTest())) {
+ return;
+ }
+ SoftAssert softAssert = new SoftAssert(TAG);
+ // Register Gps Status Listener.
+ TestGnssStatusCallback testGnssStatusCallback =
+ new TestGnssStatusCallback(TAG, STATUS_TO_COLLECT_COUNT);
+ checkGnssChange(testGnssStatusCallback);
+ validateGnssStatus(testGnssStatusCallback.getGnssStatus(), softAssert);
+ softAssert.assertAll();
+ }
+
+ /**
+ * To validate the fields in GnssStatus class, the value is got from device
+ * @param status, GnssStatus
+ * @param softAssert, customized assert class.
+ */
+ public static void validateGnssStatus(GnssStatus status, SoftAssert softAssert) {
+ int sCount = status.getSatelliteCount();
+ Log.i(TAG, "Total satellite:" + sCount);
+ // total number of satellites for all constellation is less than 200
+ softAssert.assertTrue("Satellite count test sCount : " + sCount , sCount < 200);
+ for (int i = 0; i < sCount; ++i) {
+ softAssert.assertTrue("azimuth_degrees: Azimuth in degrees: ",
+ "0.0 <= X <= 360.0",
+ String.valueOf(status.getAzimuthDegrees(i)),
+ status.getAzimuthDegrees(i) >= 0.0 && status.getAzimuthDegrees(i) <= 360.0);
+
+ if (status.hasCarrierFrequencyHz(i)) {
+ softAssert.assertTrue("carrier_frequency_hz: Carrier frequency in hz",
+ "X > 0.0",
+ String.valueOf(status.getCarrierFrequencyHz(i)),
+ status.getCarrierFrequencyHz(i) > 0.0);
+ }
+
+ softAssert.assertTrue("c_n0_dbhz: Carrier-to-noise density",
+ "0.0 >= X <= 63",
+ String.valueOf(status.getCn0DbHz(i)),
+ status.getCn0DbHz(i) >= 0.0 &&
+ status.getCn0DbHz(i) <= 63.0);
+
+ softAssert.assertTrue("elevation_degrees: Elevation in Degrees :",
+ "0.0 <= X <= 90.0",
+ String.valueOf(status.getElevationDegrees(i)),
+ status.getElevationDegrees(i) >= 0.0 && status.getElevationDegrees(i) <= 90.0);
+
+ // in validateSvidSub, it will validate ConstellationType, svid
+ // however, we don't have the event time in the current scope, pass in "-1" instead
+ TestMeasurementUtil.validateSvidSub(softAssert, null,
+ status.getConstellationType(i),status.getSvid(i));
+
+ // For those function with boolean type return, just simplly call the function
+ // to make sure those function won't crash, also increase the test coverage.
+ Log.i(TAG, "hasAlmanacData: " + status.hasAlmanacData(i));
+ Log.i(TAG, "hasEphemerisData: " + status.hasEphemerisData(i));
+ Log.i(TAG, "usedInFix: " + status.usedInFix(i));
+ }
+ }
}
diff --git a/tests/tests/location/src/android/location/cts/GnssTtffTests.java b/tests/tests/location/src/android/location/cts/GnssTtffTests.java
new file mode 100644
index 0000000..8c407d5
--- /dev/null
+++ b/tests/tests/location/src/android/location/cts/GnssTtffTests.java
@@ -0,0 +1,169 @@
+package android.location.cts;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+import com.android.compatibility.common.util.CddTest;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Tests for the ttff (time to the first fix) validating whether TTFF is
+ * below the expected thresholds in differnt scenario
+ */
+public class GnssTtffTests extends GnssTestCase {
+
+ private static final String TAG = "GnssTtffTests";
+ private static final int LOCATION_TO_COLLECT_COUNT = 1;
+ private static final int STATUS_TO_COLLECT_COUNT = 3;
+ private static final int AIDING_DATA_RESET_DELAY_SECS = 10;
+ // Threshold values
+ private static final int TTFF_WITH_WIFI_CELLUAR_WARM_TH_SECS = 7;
+ private static final int TTFF_WITH_WIFI_CELLUAR_HOT_TH_SECS = 5;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mTestLocationManager = new TestLocationManager(getContext());
+ }
+
+ /**
+ * Test the TTFF in the case where there is a network connection for both
+ * warm and hot start TTFF cases.
+ * @throws Exception
+ */
+ @CddTest(requirement="7.3.3")
+ public void testTtffWithNetwork() throws Exception {
+ checkTtffWarmWithCellularAndWifiOn();
+ checkTtffHotWithCellularAndWifiOn();
+ }
+
+ /**
+ * Test Scenario 1
+ * check whether TTFF is below the threshold on the warm start, without any network
+ * 1) Turn on wifi, turn on mobile data
+ * 2) Get GPS, check the TTFF value
+ */
+ public void checkTtffWarmWithCellularAndWifiOn() throws Exception {
+ ensureNetworkStatus();
+ SoftAssert softAssert = new SoftAssert(TAG);
+ mTestLocationManager.sendExtraCommand("delete_aiding_data");
+ Thread.sleep(TimeUnit.SECONDS.toMillis(AIDING_DATA_RESET_DELAY_SECS));
+ checkTtffByThreshold("checkTtffWarmWithCellularAndWifiOn",
+ TimeUnit.SECONDS.toMillis(TTFF_WITH_WIFI_CELLUAR_WARM_TH_SECS), softAssert);
+ softAssert.assertAll();
+ }
+
+ /**
+ * Test Scenario 2
+ * check whether TTFF is below the threhold on the hot start, without any network
+ * 1) Turn on wifi, turn on mobile data
+ * 2) Get GPS, check the TTFF value
+ */
+ public void checkTtffHotWithCellularAndWifiOn() throws Exception {
+ ensureNetworkStatus();
+ SoftAssert softAssert = new SoftAssert(TAG);
+ checkTtffByThreshold("checkTtffHotWithCellularAndWifiOn",
+ TimeUnit.SECONDS.toMillis(TTFF_WITH_WIFI_CELLUAR_HOT_TH_SECS), softAssert);
+ softAssert.assertAll();
+
+ }
+
+ /**
+ * Make sure the device has mobile data and wifi connection
+ */
+ private void ensureNetworkStatus(){
+ assertTrue("Device has to connect to Wifi to complete this test.",
+ isConnectedToWifi(getContext()));
+ ConnectivityManager connManager = getConnectivityManager(getContext());
+ NetworkInfo mobileNetworkInfo = connManager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
+ // check whether the mobile data is ON if the device has cellular capability
+ if (mobileNetworkInfo != null) {
+ TelephonyManager telephonyManager = (TelephonyManager) getContext().getApplicationContext()
+ .getSystemService(getContext().TELEPHONY_SERVICE);
+ assertTrue("Device has to have mobile data ON to complete this test.",
+ telephonyManager.isDataEnabled());
+ }
+ else {
+ Log.i(TAG, "This is a wifi only device.");
+ }
+
+ }
+
+ /*
+ * Check whether TTFF is below the threshold
+ * @param testName
+ * @param threshold, the threshold for the TTFF value
+ */
+ private void checkTtffByThreshold(String testName,
+ long threshold, SoftAssert softAssert) throws Exception {
+ TestLocationListener networkLocationListener
+ = new TestLocationListener(LOCATION_TO_COLLECT_COUNT);
+ // fetch the networklocation first to make sure the ttff is not flaky
+ mTestLocationManager.requestNetworkLocationUpdates(networkLocationListener);
+ networkLocationListener.await();
+
+ TestGnssStatusCallback testGnssStatusCallback =
+ new TestGnssStatusCallback(TAG, STATUS_TO_COLLECT_COUNT);
+ mTestLocationManager.registerGnssStatusCallback(testGnssStatusCallback);
+
+ TestLocationListener locationListener = new TestLocationListener(LOCATION_TO_COLLECT_COUNT);
+ mTestLocationManager.requestLocationUpdates(locationListener);
+
+
+ long startTimeMillis = System.currentTimeMillis();
+ boolean success = testGnssStatusCallback.awaitTtff();
+ long ttffTimeMillis = System.currentTimeMillis() - startTimeMillis;
+
+ SoftAssert.failOrWarning(isMeasurementTestStrict(),
+ "Test case:" + testName
+ + ". Threshold exceeded without getting a location."
+ + " Possibly, the test has been run deep indoors."
+ + " Consider retrying test outdoors.",
+ success);
+ mTestLocationManager.removeLocationUpdates(locationListener);
+ mTestLocationManager.unregisterGnssStatusCallback(testGnssStatusCallback);
+ softAssert.assertTrue("Test case: " + testName +", TTFF should be less than " + threshold
+ + " . In current test, TTFF value is: " + ttffTimeMillis, ttffTimeMillis < threshold);
+ }
+
+ /**
+ * Returns whether the device is currently connected to a wifi.
+ *
+ * @param context {@link Context} object
+ * @return {@code true} if connected to Wifi; {@code false} otherwise
+ */
+ public static boolean isConnectedToWifi(Context context) {
+ NetworkInfo info = getActiveNetworkInfo(context);
+ return info != null
+ && info.isConnected()
+ && info.getType() == ConnectivityManager.TYPE_WIFI;
+ }
+
+ /**
+ * Gets the active network info.
+ *
+ * @param context {@link Context} object
+ * @return {@link NetworkInfo}
+ */
+ private static NetworkInfo getActiveNetworkInfo(Context context) {
+ ConnectivityManager cm = getConnectivityManager(context);
+ if (cm != null) {
+ return cm.getActiveNetworkInfo();
+ }
+ return null;
+ }
+
+ /**
+ * Gets the connectivity manager.
+ *
+ * @param context {@link Context} object
+ * @return {@link ConnectivityManager}
+ */
+ private static ConnectivityManager getConnectivityManager(Context context) {
+ return (ConnectivityManager) context.getApplicationContext()
+ .getSystemService(Context.CONNECTIVITY_SERVICE);
+ }
+
+}
\ No newline at end of file
diff --git a/tests/tests/location/src/android/location/cts/SoftAssert.java b/tests/tests/location/src/android/location/cts/SoftAssert.java
index 4349f07..d118021 100644
--- a/tests/tests/location/src/android/location/cts/SoftAssert.java
+++ b/tests/tests/location/src/android/location/cts/SoftAssert.java
@@ -48,13 +48,17 @@
* @param actualResult actual value
* @param condition condition for test
*/
- public void assertTrue(String message, long eventTimeInNs, String expectedResult,
+ public void assertTrue(String message, Long eventTimeInNs, String expectedResult,
String actualResult, boolean condition) {
if (condition) {
Log.i(mTag, message + ", (Test: PASS, actual : " +
actualResult + ", expected: " + expectedResult + ")");
} else {
- String errorMessage = "At time = " + eventTimeInNs + " ns, " + message +
+ String errorMessage = "";
+ if (eventTimeInNs != null) {
+ errorMessage = "At time = " + eventTimeInNs + " ns, ";
+ }
+ errorMessage += message +
" (Test: FAIL, actual :" + actualResult + ", " +
"expected: " + expectedResult + ")";
Log.e(mTag, errorMessage);
@@ -63,6 +67,19 @@
}
/**
+ * assertTrue without eventTime
+ *
+ * @param message test message
+ * @param expectedResult expected value
+ * @param actualResult actual value
+ * @param condition condition for test
+ */
+ public void assertTrue(String message, String expectedResult,
+ String actualResult, boolean condition) {
+ assertTrue(message, null, expectedResult, actualResult, condition);
+ }
+
+ /**
* Check if a condition is true.
* NOTE: A failure is downgraded to a warning.
*
diff --git a/tests/tests/location/src/android/location/cts/TestGnssStatusCallback.java b/tests/tests/location/src/android/location/cts/TestGnssStatusCallback.java
index c63fb01..38b749f 100644
--- a/tests/tests/location/src/android/location/cts/TestGnssStatusCallback.java
+++ b/tests/tests/location/src/android/location/cts/TestGnssStatusCallback.java
@@ -72,6 +72,7 @@
@Override
public void onSatelliteStatusChanged(GnssStatus status) {
Log.i(mTag, "Gnss Status Listener Received Status Update");
+ mGnssStatus = status;
mLatchStatus.countDown();
}
diff --git a/tests/tests/location/src/android/location/cts/TestLocationListener.java b/tests/tests/location/src/android/location/cts/TestLocationListener.java
index f266d5a..2e4b826 100644
--- a/tests/tests/location/src/android/location/cts/TestLocationListener.java
+++ b/tests/tests/location/src/android/location/cts/TestLocationListener.java
@@ -20,6 +20,8 @@
import android.location.LocationManager;
import android.os.Bundle;
+import java.util.ArrayList;
+import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -32,14 +34,17 @@
// Timeout in sec for count down latch wait
private static final int TIMEOUT_IN_SEC = 120;
private final CountDownLatch mCountDownLatch;
+ private List<Location> mLocationList = null;
TestLocationListener(int locationToCollect) {
mCountDownLatch = new CountDownLatch(locationToCollect);
+ mLocationList = new ArrayList<>();
}
@Override
public void onLocationChanged(Location location) {
mLocationReceived = true;
+ mLocationList.add(location);
mCountDownLatch.countDown();
}
@@ -66,6 +71,15 @@
}
/**
+ * Get the list of locations received
+ *
+ * @return mLocationList, List of {@link Location}.
+ */
+ public List<Location> getReceivedLocationList(){
+ return mLocationList;
+ }
+
+ /**
* Check if location provider is enabled.
*
* @return {@code true} if the location provider is enabled and {@code false}
diff --git a/tests/tests/location/src/android/location/cts/TestLocationManager.java b/tests/tests/location/src/android/location/cts/TestLocationManager.java
index cca2b78..390d450 100644
--- a/tests/tests/location/src/android/location/cts/TestLocationManager.java
+++ b/tests/tests/location/src/android/location/cts/TestLocationManager.java
@@ -117,6 +117,21 @@
}
/**
+ * See {@code LocationManager#requestNetworkLocationUpdates}.
+ *
+ * @param locationListener location listener for request
+ */
+ public void requestNetworkLocationUpdates(LocationListener locationListener) {
+ if (mLocationManager.getProvider(LocationManager.NETWORK_PROVIDER) != null) {
+ Log.i(TAG, "Request Network Location updates.");
+ mLocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,
+ 0 /* minTime*/,
+ 0 /* minDistance */,
+ locationListener,
+ Looper.getMainLooper());
+ }
+ }
+ /**
* See {@link android.location.LocationManager#addGpsStatusListener (GpsStatus.Listener)}.
* @param listener the GpsStatus.Listener to add
*/
diff --git a/tests/tests/location/src/android/location/cts/TestMeasurementUtil.java b/tests/tests/location/src/android/location/cts/TestMeasurementUtil.java
index bdfd676..eb78a5b 100644
--- a/tests/tests/location/src/android/location/cts/TestMeasurementUtil.java
+++ b/tests/tests/location/src/android/location/cts/TestMeasurementUtil.java
@@ -263,6 +263,17 @@
String.valueOf(measurement.getSnrInDb()),
measurement.getSnrInDb() >= 0.0 && measurement.getSnrInDb() <= 63);
}
+
+ // Check Automatic Gain Control level in dB.
+ if (measurement.hasAutomaticGainControlLevelDb()) {
+ softAssert.assertTrue("Automatic Gain Control level in dB",
+ timeInNs,
+ "-100 >= X <= 100",
+ String.valueOf(measurement.getAutomaticGainControlLevelDb()),
+ measurement.getAutomaticGainControlLevelDb() >= -100
+ && measurement.getAutomaticGainControlLevelDb() <= 100);
+ }
+
}
/**
@@ -315,6 +326,12 @@
String svidLogMessageFormat = "svid: Space Vehicle ID. Constellation type = %s";
int constellationType = measurement.getConstellationType();
int svid = measurement.getSvid();
+ validateSvidSub(softAssert, timeInNs, constellationType, svid);
+ }
+
+ public static void validateSvidSub(SoftAssert softAssert, Long timeInNs,
+ int constellationType, int svid) {
+
String svidValue = String.valueOf(svid);
switch (constellationType) {
@@ -472,6 +489,15 @@
"0 day >= X <= 1 day",
String.valueOf(sv_time_days),
sv_time_days >= 0 && sv_time_days <= 1);
+ } else if ((state | GnssMeasurement.STATE_GLO_TOD_KNOWN)
+ == GnssMeasurement.STATE_GLO_TOD_KNOWN) {
+ softAssert.assertTrue(getReceivedSvTimeNsLogMessage(
+ "GNSS_MEASUREMENT_STATE_GLO_TOD_KNOWN",
+ "GnssStatus.CONSTELLATION_GLONASS"),
+ timeInNs,
+ "0 day >= X <= 1 day",
+ String.valueOf(sv_time_days),
+ sv_time_days >= 0 && sv_time_days <= 1);
} else if ((state | GnssMeasurement.STATE_GLO_STRING_SYNC)
== GnssMeasurement.STATE_GLO_STRING_SYNC) {
softAssert.assertTrue(getReceivedSvTimeNsLogMessage(
@@ -520,6 +546,15 @@
"0 >= X <= 7 days",
String.valueOf(sv_time_days),
sv_time_days >= 0 && sv_time_days <= 7);
+ } else if ((state | GnssMeasurement.STATE_TOW_KNOWN)
+ == GnssMeasurement.STATE_TOW_KNOWN) {
+ softAssert.assertTrue(getReceivedSvTimeNsLogMessage(
+ "GNSS_MEASUREMENT_STATE_TOW_DECODED",
+ "GnssStatus.CONSTELLATION_GALILEO"),
+ timeInNs,
+ "0 >= X <= 7 days",
+ String.valueOf(sv_time_days),
+ sv_time_days >= 0 && sv_time_days <= 7);
} else if ((state | GnssMeasurement.STATE_GAL_E1B_PAGE_SYNC)
== GnssMeasurement.STATE_GAL_E1B_PAGE_SYNC) {
softAssert.assertTrue(getReceivedSvTimeNsLogMessage(
diff --git a/tests/tests/location/src/android/location/cts/TestUtils.java b/tests/tests/location/src/android/location/cts/TestUtils.java
index a038457..113af49 100644
--- a/tests/tests/location/src/android/location/cts/TestUtils.java
+++ b/tests/tests/location/src/android/location/cts/TestUtils.java
@@ -16,6 +16,7 @@
package android.location.cts;
+import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -46,4 +47,14 @@
}
return false;
}
+ public static boolean waitForWithCondition(int timeInSec, Callable<Boolean> callback)
+ throws Exception {
+ long waitTimeRounds = (TimeUnit.SECONDS.toMillis(timeInSec)) / STANDARD_SLEEP_TIME_MS;
+ for (int i = 0; i < waitTimeRounds; ++i) {
+ Thread.sleep(STANDARD_SLEEP_TIME_MS);
+ if(callback.call()) return true;
+ }
+ return false;
+ }
+
}
diff --git a/tests/tests/media/libimagereaderjni/AImageReaderCts.cpp b/tests/tests/media/libimagereaderjni/AImageReaderCts.cpp
index a8ce1c7..c098f5f 100644
--- a/tests/tests/media/libimagereaderjni/AImageReaderCts.cpp
+++ b/tests/tests/media/libimagereaderjni/AImageReaderCts.cpp
@@ -48,14 +48,22 @@
static constexpr int kCaptureWaitRetry = 10;
static constexpr int kTestImageWidth = 640;
static constexpr int kTestImageHeight = 480;
-static constexpr int kTestImageFormat = AIMAGE_FORMAT_JPEG;
+static constexpr int kTestImageFormat = AIMAGE_FORMAT_YUV_420_888;
-static constexpr int supportedUsage[] = {
- AHARDWAREBUFFER_USAGE_CPU_READ_RARELY,
- AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN,
- AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE,
- AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER,
- AHARDWAREBUFFER_USAGE_VIDEO_ENCODE,
+struct FormatUsageCombination {
+ uint64_t format;
+ uint64_t usage;
+};
+
+static constexpr FormatUsageCombination supportedFormatUsage[] = {
+ {AIMAGE_FORMAT_YUV_420_888, AHARDWAREBUFFER_USAGE_CPU_READ_RARELY},
+ {AIMAGE_FORMAT_YUV_420_888, AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN},
+ {AIMAGE_FORMAT_JPEG, AHARDWAREBUFFER_USAGE_CPU_READ_RARELY},
+ {AIMAGE_FORMAT_JPEG, AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN},
+ {AIMAGE_FORMAT_RGBA_8888, AHARDWAREBUFFER_USAGE_CPU_READ_RARELY},
+ {AIMAGE_FORMAT_RGBA_8888, AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN},
+ {AIMAGE_FORMAT_RGBA_8888, AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE},
+ {AIMAGE_FORMAT_RGBA_8888, AHARDWAREBUFFER_USAGE_VIDEO_ENCODE},
};
class CameraHelper {
@@ -283,7 +291,7 @@
}
media_status_t ret = AImageReader_newWithUsage(
- mWidth, mHeight, mFormat, mUsage, 0, mMaxImages, &mImgReader);
+ mWidth, mHeight, mFormat, mUsage, mMaxImages, &mImgReader);
if (ret != AMEDIA_OK || mImgReader == nullptr) {
ALOGE("Failed to create new AImageReader, ret=%d, mImgReader=%p", ret, mImgReader);
return -1;
@@ -497,16 +505,17 @@
// Test that newWithUsage can create AImageReader correctly.
extern "C" jboolean Java_android_media_cts_NativeImageReaderTest_\
-testSucceedsWithSupportedUsageNative(JNIEnv* /*env*/, jclass /*clazz*/) {
+testSucceedsWithSupportedUsageFormatNative(JNIEnv* /*env*/, jclass /*clazz*/) {
static constexpr int kTestImageCount = 8;
- for (auto& usage : supportedUsage) {
+ for (auto& combination : supportedFormatUsage) {
AImageReader* outReader;
media_status_t ret = AImageReader_newWithUsage(
- kTestImageWidth, kTestImageHeight, kTestImageFormat, usage, 0, kTestImageCount,
- &outReader);
+ kTestImageWidth, kTestImageHeight, combination.format, combination.usage,
+ kTestImageCount, &outReader);
if (ret != AMEDIA_OK || outReader == nullptr) {
- ALOGE("AImageReader_newWithUsage failed with usage: %d", usage);
+ ALOGE("AImageReader_newWithUsage failed with format: %" PRId64 ", usage: %" PRId64 ".",
+ combination.format, combination.usage);
return false;
}
AImageReader_delete(outReader);
@@ -518,7 +527,7 @@
extern "C" jboolean Java_android_media_cts_NativeImageReaderTest_\
testTakePicturesNative(JNIEnv* /*env*/, jclass /*clazz*/) {
for (auto& readerUsage :
- {AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN, AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER}) {
+ {AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN}) {
for (auto& readerMaxImages : {1, 4, 8}) {
for (auto& readerAsync : {true, false}) {
for (auto& pictureCount : {1, 8, 16}) {
diff --git a/tests/tests/media/src/android/media/cts/AudioFocusTest.java b/tests/tests/media/src/android/media/cts/AudioFocusTest.java
index 2ef0c68..afc272e 100644
--- a/tests/tests/media/src/android/media/cts/AudioFocusTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioFocusTest.java
@@ -88,7 +88,7 @@
final AudioFocusRequest reqToCopy =
new AudioFocusRequest.Builder(focusGain)
.setAudioAttributes(ATTR_DRIVE_DIR)
- .setOnAudioFocusChangeListener(focusListener, null)
+ .setOnAudioFocusChangeListener(focusListener)
.setAcceptsDelayedFocusGain(true)
.setWillPauseWhenDucked(true)
.build();
@@ -111,6 +111,34 @@
assertEquals("Delayed focus differs", true, newReq.acceptsDelayedFocusGain());
}
+ public void testNullListenerHandlerNpe() throws Exception {
+ final AudioFocusRequest.Builder afBuilder =
+ new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN);
+ try {
+ afBuilder.setOnAudioFocusChangeListener(null);
+ fail("no NPE when setting a null listener");
+ } catch (NullPointerException e) {
+ }
+
+ final HandlerThread handlerThread = new HandlerThread(TAG);
+ handlerThread.start();
+ final Handler h = new Handler(handlerThread.getLooper());
+ final AudioFocusRequest.Builder afBuilderH =
+ new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN);
+ try {
+ afBuilderH.setOnAudioFocusChangeListener(null, h);
+ fail("no NPE when setting a null listener with non-null Handler");
+ } catch (NullPointerException e) {
+ }
+
+ final AudioFocusRequest.Builder afBuilderL =
+ new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN);
+ try {
+ afBuilderL.setOnAudioFocusChangeListener(new FocusChangeListener(), null);
+ fail("no NPE when setting a non-null listener with null Handler");
+ } catch (NullPointerException e) {
+ }
+ }
public void testAudioFocusRequestGainLoss() throws Exception {
final AudioAttributes[] attributes = { ATTR_DRIVE_DIR, ATTR_MEDIA };
@@ -188,10 +216,17 @@
try {
for (int i = 0 ; i < NB_FOCUS_OWNERS ; i++) {
focusListeners[i] = new FocusChangeListener();
- focusRequests[i] = new AudioFocusRequest.Builder(focusGains[i])
- .setAudioAttributes(attributes[i])
- .setOnAudioFocusChangeListener(focusListeners[i], h /*handler*/)
- .build();
+ if (h != null) {
+ focusRequests[i] = new AudioFocusRequest.Builder(focusGains[i])
+ .setAudioAttributes(attributes[i])
+ .setOnAudioFocusChangeListener(focusListeners[i], h /*handler*/)
+ .build();
+ } else {
+ focusRequests[i] = new AudioFocusRequest.Builder(focusGains[i])
+ .setAudioAttributes(attributes[i])
+ .setOnAudioFocusChangeListener(focusListeners[i])
+ .build();
+ }
}
// focus owner 0 requests focus with GAIN,
diff --git a/tests/tests/media/src/android/media/cts/NativeImageReaderTest.java b/tests/tests/media/src/android/media/cts/NativeImageReaderTest.java
index 8fac952..1d57609 100644
--- a/tests/tests/media/src/android/media/cts/NativeImageReaderTest.java
+++ b/tests/tests/media/src/android/media/cts/NativeImageReaderTest.java
@@ -34,9 +34,10 @@
Log.i("NativeImageReaderTest", "after loadlibrary");
}
- public void testSucceedsWithSupportedUsage() {
+ public void testSucceedsWithSupportedUsageFormat() {
assertTrue(
- "Native test failed, see log for details", testSucceedsWithSupportedUsageNative());
+ "Native test failed, see log for details",
+ testSucceedsWithSupportedUsageFormatNative());
}
public void testTakePictures() {
@@ -49,7 +50,7 @@
assertTrue("Surface created is invalid.", surface.isValid());
}
- private static native boolean testSucceedsWithSupportedUsageNative();
+ private static native boolean testSucceedsWithSupportedUsageFormatNative();
private static native boolean testTakePicturesNative();
private static native Surface testCreateSurfaceNative();
}
diff --git a/tests/tests/nativehardware/Android.mk b/tests/tests/nativehardware/Android.mk
index 7f05395..a001387 100644
--- a/tests/tests/nativehardware/Android.mk
+++ b/tests/tests/nativehardware/Android.mk
@@ -12,32 +12,83 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-# Build the unit tests.
-
LOCAL_PATH:= $(call my-dir)
+#
+# Reusable test classes and helpers.
+#
include $(CLEAR_VARS)
-LOCAL_MODULE:= CtsNativeHardwareTestCases
-LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+
+LOCAL_MODULE := cts-nativehardware-tests
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-util
+
+LOCAL_JAVA_LIBRARIES := platform-test-annotations
+
+LOCAL_SDK_VERSION := current
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+#
+# JNI components for testing NDK.
+#
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libcts-nativehardware-ndk-jni
+
+LOCAL_CFLAGS += -Werror -Wall -Wextra
+
+LOCAL_MODULE_TAGS := tests
LOCAL_SRC_FILES := \
- src/AHardwareBufferTest.cpp \
+ jni/AHardwareBufferTest.cpp \
+ jni/android_hardware_cts_AHardwareBufferNativeTest.cpp \
+ jni/NativeTestHelper.cpp
-LOCAL_SHARED_LIBRARIES := \
- libnativewindow \
- liblog \
+LOCAL_C_INCLUDES := $(JNI_H_INCLUDE)
-LOCAL_STATIC_LIBRARIES := \
- libgtest_ndk_c++ \
- libgtest_main_ndk_c++ \
+LOCAL_SHARED_LIBRARIES := libandroid liblog
-LOCAL_CTS_TEST_PACKAGE := android.nativehardware
+LOCAL_SDK_VERSION := current
+
+LOCAL_NDK_STL_VARIANT := c++_shared
+
+include $(BUILD_SHARED_LIBRARY)
+
+#
+# CtsNativeHardwareTestCases package
+#
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
# Tag this module as a cts test artifact
LOCAL_COMPATIBILITY_SUITE := cts
-LOCAL_SDK_VERSION := current
-LOCAL_NDK_STL_VARIANT := c++_static
+# include both the 32 and 64 bit versions
+LOCAL_MULTILIB := both
-include $(BUILD_NATIVE_TEST)
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ compatibility-device-util \
+ ctstestrunner \
+ cts-nativehardware-tests \
+
+LOCAL_JNI_SHARED_LIBRARIES := libcts-nativehardware-ndk-jni
+
+LOCAL_PACKAGE_NAME := CtsNativeHardwareTestCases
+
+LOCAL_SDK_VERSION := current
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
+LOCAL_NDK_STL_VARIANT := c++_shared
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/nativehardware/AndroidManifest.xml b/tests/tests/nativehardware/AndroidManifest.xml
new file mode 100644
index 0000000..f453fa8
--- /dev/null
+++ b/tests/tests/nativehardware/AndroidManifest.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.hardware.nativehardware.cts">
+
+ <uses-permission android:name="android.permission.WAKE_LOCK" />
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.hardware.nativehardware.cts"
+ android:label="CTS native hardware tests">
+ <meta-data android:name="listener"
+ android:value="com.android.cts.runner.CtsTestRunListener" />
+ </instrumentation>
+
+</manifest>
+
diff --git a/tests/tests/nativehardware/AndroidTest.xml b/tests/tests/nativehardware/AndroidTest.xml
index 8fec974..787a89d 100644
--- a/tests/tests/nativehardware/AndroidTest.xml
+++ b/tests/tests/nativehardware/AndroidTest.xml
@@ -14,14 +14,12 @@
limitations under the License.
-->
<configuration description="Config for CTS Native Hardware test cases">
- <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
- <option name="cleanup" value="true" />
- <option name="push" value="CtsNativeHardwareTestCases->/data/local/tmp/CtsNativeHardwareTestCases" />
- <option name="append-bitness" value="true" />
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="CtsNativeHardwareTestCases.apk" />
</target_preparer>
- <test class="com.android.tradefed.testtype.GTest" >
- <option name="native-test-device-path" value="/data/local/tmp" />
- <option name="module-name" value="CtsNativeHardwareTestCases" />
- <option name="runtime-hint" value="1s" />
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="android.hardware.nativehardware.cts" />
+ <option name="runtime-hint" value="10s" />
</test>
</configuration>
diff --git a/tests/tests/nativehardware/src/AHardwareBufferTest.cpp b/tests/tests/nativehardware/jni/AHardwareBufferTest.cpp
similarity index 71%
rename from tests/tests/nativehardware/src/AHardwareBufferTest.cpp
rename to tests/tests/nativehardware/jni/AHardwareBufferTest.cpp
index d467929..adb9a91 100644
--- a/tests/tests/nativehardware/src/AHardwareBufferTest.cpp
+++ b/tests/tests/nativehardware/jni/AHardwareBufferTest.cpp
@@ -14,36 +14,45 @@
* limitations under the License.
*/
-#define LOG_TAG "AHardwareBuffer_test"
-//#define LOG_NDEBUG 0
+#include "AHardwareBufferTest.h"
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
+#include <unistd.h>
+
+#include <sstream>
+#include <string>
#include <android/hardware_buffer.h>
-#include <gtest/gtest.h>
+#define LOG_TAG "AHardwareBufferTest"
+#include "NativeTestHelper.h"
+
+namespace android {
+
+//#define LOG_NDEBUG 0
#define BAD_VALUE -EINVAL
#define NO_ERROR 0
-static ::testing::AssertionResult BuildHexFailureMessage(uint64_t expected,
+static std::string BuildHexFailureMessage(uint64_t expected,
uint64_t actual, const char* type) {
std::ostringstream ss;
ss << type << " 0x" << std::hex << actual
<< " does not match expected " << type << " 0x" << std::hex
<< expected;
- return ::testing::AssertionFailure() << ss.str();
+ return ss.str();
}
-static ::testing::AssertionResult BuildFailureMessage(uint32_t expected,
+static std::string BuildFailureMessage(uint32_t expected,
uint32_t actual, const char* type) {
- return ::testing::AssertionFailure() << "Buffer " << type << " do not match"
- << ": " << actual << " != " << expected;
+ std::ostringstream ss;
+ ss << "Buffer " << type << " do not match" << ": " << actual << " != " << expected;
+ return ss.str();
}
-static ::testing::AssertionResult CheckAHardwareBufferMatchesDesc(
+static std::string CheckAHardwareBufferMatchesDesc(
AHardwareBuffer* abuffer, const AHardwareBuffer_Desc& desc) {
AHardwareBuffer_Desc bufferDesc;
AHardwareBuffer_describe(abuffer, &bufferDesc);
@@ -57,26 +66,30 @@
return BuildHexFailureMessage(desc.usage, bufferDesc.usage, "usage");
if (bufferDesc.format != desc.format)
return BuildFailureMessage(desc.format, bufferDesc.format, "formats");
- return ::testing::AssertionSuccess();
+ return std::string();
}
+bool AHardwareBufferTest::SetUp() { return true; }
+
+void AHardwareBufferTest::TearDown() {}
+
// Test that passing in NULL values to allocate works as expected.
-TEST(AHardwareBufferTest, AHardwareBuffer_allocate_FailsWithNullInput) {
+void AHardwareBufferTest::testAHardwareBuffer_allocate_FailsWithNullInput(JNIEnv* env) {
AHardwareBuffer* buffer;
AHardwareBuffer_Desc desc;
memset(&desc, 0, sizeof(AHardwareBuffer_Desc));
int res = AHardwareBuffer_allocate(&desc, NULL);
- EXPECT_EQ(BAD_VALUE, res);
+ ASSERT_EQ(BAD_VALUE, res);
res = AHardwareBuffer_allocate(NULL, &buffer);
- EXPECT_EQ(BAD_VALUE, res);
+ ASSERT_EQ(BAD_VALUE, res);
res = AHardwareBuffer_allocate(NULL, NULL);
- EXPECT_EQ(BAD_VALUE, res);
+ ASSERT_EQ(BAD_VALUE, res);
}
// Test that passing in NULL values to allocate works as expected.
-TEST(AHardwareBufferTest, AHardwareBuffer_allocate_BlobFormatRequiresHeight1) {
+void AHardwareBufferTest::testAHardwareBuffer_allocate_BlobFormatRequiresHeight1(JNIEnv* env) {
AHardwareBuffer* buffer;
AHardwareBuffer_Desc desc = {};
@@ -86,18 +99,18 @@
desc.usage = AHARDWAREBUFFER_USAGE_CPU_READ_RARELY;
desc.format = AHARDWAREBUFFER_FORMAT_BLOB;
int res = AHardwareBuffer_allocate(&desc, &buffer);
- EXPECT_EQ(BAD_VALUE, res);
+ ASSERT_EQ(BAD_VALUE, res);
desc.height = 1;
res = AHardwareBuffer_allocate(&desc, &buffer);
- EXPECT_EQ(NO_ERROR, res);
- EXPECT_TRUE(CheckAHardwareBufferMatchesDesc(buffer, desc));
+ ASSERT_EQ(NO_ERROR, res);
+ ASSERT_EQ(std::string(), CheckAHardwareBufferMatchesDesc(buffer, desc));
AHardwareBuffer_release(buffer);
buffer = NULL;
}
// Test that allocate can create an AHardwareBuffer correctly.
-TEST(AHardwareBufferTest, AHardwareBuffer_allocate_Succeeds) {
+void AHardwareBufferTest::testAHardwareBuffer_allocate_Succeeds(JNIEnv* env) {
AHardwareBuffer* buffer = NULL;
AHardwareBuffer_Desc desc = {};
@@ -107,8 +120,8 @@
desc.usage = AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE;
desc.format = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM;
int res = AHardwareBuffer_allocate(&desc, &buffer);
- EXPECT_EQ(NO_ERROR, res);
- EXPECT_TRUE(CheckAHardwareBufferMatchesDesc(buffer, desc));
+ ASSERT_EQ(NO_ERROR, res);
+ ASSERT_EQ(std::string(), CheckAHardwareBufferMatchesDesc(buffer, desc));
AHardwareBuffer_release(buffer);
buffer = NULL;
@@ -118,12 +131,12 @@
desc.usage = AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE;
desc.format = AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM;
res = AHardwareBuffer_allocate(&desc, &buffer);
- EXPECT_EQ(NO_ERROR, res);
- EXPECT_TRUE(CheckAHardwareBufferMatchesDesc(buffer, desc));
+ ASSERT_EQ(NO_ERROR, res);
+ ASSERT_EQ(std::string(), CheckAHardwareBufferMatchesDesc(buffer, desc));
AHardwareBuffer_release(buffer);
}
-TEST(AHardwareBufferTest, AHardwareBuffer_describe_Succeeds) {
+void AHardwareBufferTest::testAHardwareBuffer_describe_Succeeds(JNIEnv* env) {
AHardwareBuffer* buffer = NULL;
AHardwareBuffer_Desc desc = {};
@@ -133,36 +146,39 @@
desc.usage = AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE;
desc.format = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM;
int res = AHardwareBuffer_allocate(&desc, &buffer);
- EXPECT_EQ(NO_ERROR, res);
+ ASSERT_EQ(NO_ERROR, res);
AHardwareBuffer_Desc expected_desc;
memset(&expected_desc, 0, sizeof(AHardwareBuffer_Desc));
AHardwareBuffer_describe(NULL, &desc);
- EXPECT_EQ(0U, expected_desc.width);
+ ASSERT_EQ(0U, expected_desc.width);
AHardwareBuffer_describe(buffer, NULL);
- EXPECT_EQ(0U, expected_desc.width);
+ ASSERT_EQ(0U, expected_desc.width);
AHardwareBuffer_describe(buffer, &desc);
- EXPECT_TRUE(CheckAHardwareBufferMatchesDesc(buffer, desc));
+ ASSERT_EQ(std::string(), CheckAHardwareBufferMatchesDesc(buffer, desc));
AHardwareBuffer_release(buffer);
}
struct ClientData {
int fd;
+ JNIEnv* env;
AHardwareBuffer* buffer;
- ClientData(int fd_in, AHardwareBuffer* buffer_in)
- : fd(fd_in), buffer(buffer_in) {}
+ ClientData(int fd_in, JNIEnv* env_in, AHardwareBuffer* buffer_in)
+ : fd(fd_in), env(env_in), buffer(buffer_in) {}
};
static void* clientFunction(void* data) {
ClientData* pdata = reinterpret_cast<ClientData*>(data);
int err = AHardwareBuffer_sendHandleToUnixSocket(pdata->buffer, pdata->fd);
- EXPECT_EQ(NO_ERROR, err);
+ if (err != NO_ERROR) {
+ fail(pdata->env, "Error sending handle to socket: %d", err);
+ }
close(pdata->fd);
return 0;
}
-TEST(AHardwareBufferTest, AHardwareBuffer_SendAndRecv_Succeeds) {
+void AHardwareBufferTest::testAHardwareBuffer_SendAndRecv_Succeeds(JNIEnv* env) {
AHardwareBuffer* buffer = NULL;
AHardwareBuffer_Desc desc = {};
@@ -174,32 +190,32 @@
// Test that an invalid buffer fails.
int err = AHardwareBuffer_sendHandleToUnixSocket(NULL, 0);
- EXPECT_EQ(BAD_VALUE, err);
+ ASSERT_EQ(BAD_VALUE, err);
err = 0;
err = AHardwareBuffer_sendHandleToUnixSocket(buffer, 0);
- EXPECT_EQ(BAD_VALUE, err);
+ ASSERT_EQ(BAD_VALUE, err);
// Allocate the buffer.
err = AHardwareBuffer_allocate(&desc, &buffer);
- EXPECT_EQ(NO_ERROR, err);
+ ASSERT_EQ(NO_ERROR, err);
int fds[2];
err = socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, fds);
// Launch a client that will send the buffer back.
- ClientData data(fds[1], buffer);
+ ClientData data(fds[1], env, buffer);
pthread_t thread;
ASSERT_EQ(0, pthread_create(&thread, NULL, clientFunction, &data));
// Receive the buffer.
err = AHardwareBuffer_recvHandleFromUnixSocket(fds[0], NULL);
- EXPECT_EQ(BAD_VALUE, err);
+ ASSERT_EQ(BAD_VALUE, err);
AHardwareBuffer* received = NULL;
err = AHardwareBuffer_recvHandleFromUnixSocket(fds[0], &received);
- EXPECT_EQ(NO_ERROR, err);
+ ASSERT_EQ(NO_ERROR, err);
ASSERT_TRUE(received != NULL);
- EXPECT_TRUE(CheckAHardwareBufferMatchesDesc(received, desc));
+ ASSERT_EQ(std::string(), CheckAHardwareBufferMatchesDesc(received, desc));
void* ret_val;
ASSERT_EQ(0, pthread_join(thread, &ret_val));
@@ -210,7 +226,7 @@
AHardwareBuffer_release(received);
}
-TEST(AHardwareBufferTest, AHardwareBuffer_Lock_and_Unlock_Succeed) {
+void AHardwareBufferTest::testAHardwareBuffer_Lock_and_Unlock_Succeed(JNIEnv* env) {
AHardwareBuffer* buffer = NULL;
AHardwareBuffer_Desc desc = {};
@@ -222,19 +238,21 @@
// Test that an invalid buffer fails.
int err = AHardwareBuffer_lock(NULL, 0, -1, NULL, NULL);
- EXPECT_EQ(BAD_VALUE, err);
+ ASSERT_EQ(BAD_VALUE, err);
err = 0;
// Allocate the buffer.
err = AHardwareBuffer_allocate(&desc, &buffer);
- EXPECT_EQ(NO_ERROR, err);
+ ASSERT_EQ(NO_ERROR, err);
void* bufferData = NULL;
err = AHardwareBuffer_lock(buffer, AHARDWAREBUFFER_USAGE_CPU_READ_RARELY, -1,
NULL, &bufferData);
- EXPECT_EQ(NO_ERROR, err);
- EXPECT_TRUE(bufferData != NULL);
+ ASSERT_EQ(NO_ERROR, err);
+ ASSERT_TRUE(bufferData != NULL);
int32_t fence = -1;
err = AHardwareBuffer_unlock(buffer, &fence);
AHardwareBuffer_release(buffer);
}
+
+} // namespace android
diff --git a/tests/tests/nativehardware/jni/AHardwareBufferTest.h b/tests/tests/nativehardware/jni/AHardwareBufferTest.h
new file mode 100644
index 0000000..7f39f57
--- /dev/null
+++ b/tests/tests/nativehardware/jni/AHardwareBufferTest.h
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+
+#ifndef AHARDWAREBUFFER_TEST_H
+#define AHARDWAREBUFFER_TEST_H
+
+#include "NativeHardwareTest.h"
+
+#include <jni.h>
+
+namespace android {
+
+class AHardwareBufferTest : public NativeHardwareTest {
+public:
+ bool SetUp() override;
+ void TearDown() override;
+ virtual ~AHardwareBufferTest() = default;
+
+ // tests
+ void testAHardwareBuffer_allocate_FailsWithNullInput(JNIEnv *env);
+ void testAHardwareBuffer_allocate_BlobFormatRequiresHeight1(JNIEnv *env);
+ void testAHardwareBuffer_allocate_Succeeds(JNIEnv *env);
+ void testAHardwareBuffer_describe_Succeeds(JNIEnv *env);
+ void testAHardwareBuffer_SendAndRecv_Succeeds(JNIEnv *env);
+ void testAHardwareBuffer_Lock_and_Unlock_Succeed(JNIEnv *env);
+};
+
+} // namespace android
+
+#endif // AHARDWAREBUFFER_TEST_H
diff --git a/tests/tests/nativehardware/jni/NativeHardwareTest.h b/tests/tests/nativehardware/jni/NativeHardwareTest.h
new file mode 100644
index 0000000..2b3fda9
--- /dev/null
+++ b/tests/tests/nativehardware/jni/NativeHardwareTest.h
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+#ifndef NATIVEHARDWARE_TEST_H
+#define NATIVEHARDWARE_TEST_H
+
+namespace android {
+
+class NativeHardwareTest {
+public:
+ virtual bool SetUp() = 0;
+ virtual void TearDown() = 0;
+ virtual ~NativeHardwareTest() = default;
+};
+
+} // namespace android
+
+#endif // NATIVEHARDWARE_TEST_H
diff --git a/tests/tests/nativehardware/jni/NativeTestHelper.cpp b/tests/tests/nativehardware/jni/NativeTestHelper.cpp
new file mode 100644
index 0000000..5737d0c
--- /dev/null
+++ b/tests/tests/nativehardware/jni/NativeTestHelper.cpp
@@ -0,0 +1,49 @@
+/*
+ * 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 "NativeTestHelper.h"
+
+#include <cstdlib>
+#include <cstring>
+
+extern int register_android_hardware_cts_AHardwareBufferNativeTest(JNIEnv* env);
+
+void fail(JNIEnv* env, const char* format, ...) {
+ va_list args;
+
+ va_start(args, format);
+ char *msg;
+ vasprintf(&msg, format, args);
+ va_end(args);
+
+ jclass exClass;
+ const char *className = "java/lang/AssertionError";
+ exClass = env->FindClass(className);
+ env->ThrowNew(exClass, msg);
+ free(msg);
+}
+
+jint JNI_OnLoad(JavaVM *vm, void *) {
+ JNIEnv *env = NULL;
+ if (vm->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK) {
+ return JNI_ERR;
+ }
+ if (register_android_hardware_cts_AHardwareBufferNativeTest(env)) {
+ return JNI_ERR;
+ }
+ return JNI_VERSION_1_4;
+}
diff --git a/tests/tests/nativehardware/jni/NativeTestHelper.h b/tests/tests/nativehardware/jni/NativeTestHelper.h
new file mode 100644
index 0000000..fcc8d7f
--- /dev/null
+++ b/tests/tests/nativehardware/jni/NativeTestHelper.h
@@ -0,0 +1,63 @@
+/*
+ * 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 <android/log.h>
+#define ALOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, TAG, __VA_ARGS__)
+#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)
+#define ALOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__)
+
+#define ASSERT(condition, format, args...) \
+ if (!(condition)) { \
+ fail(env, format, ## args); \
+ return; \
+ }
+
+// gtest style assert
+#define ASSERT_TRUE(a) ASSERT((a), "assert failed on (" #a ") at " __FILE__ ":%d", __LINE__)
+#define ASSERT_FALSE(a) ASSERT(!(a), "assert failed on (!" #a ") at " __FILE__ ":%d", __LINE__)
+#define ASSERT_EQ(a, b) \
+ ASSERT((a) == (b), "assert failed on (" #a " == " #b ") at " __FILE__ ":%d", __LINE__)
+#define ASSERT_NE(a, b) \
+ ASSERT((a) != (b), "assert failed on (" #a " != " #b ") at " __FILE__ ":%d", __LINE__)
+#define ASSERT_GT(a, b) \
+ ASSERT((a) > (b), "assert failed on (" #a " > " #b ") at " __FILE__ ":%d", __LINE__)
+#define ASSERT_GE(a, b) \
+ ASSERT((a) >= (b), "assert failed on (" #a " >= " #b ") at " __FILE__ ":%d", __LINE__)
+#define ASSERT_LT(a, b) \
+ ASSERT((a) < (b), "assert failed on (" #a " < " #b ") at " __FILE__ ":%d", __LINE__)
+#define ASSERT_LE(a, b) \
+ ASSERT((a) <= (b), "assert failed on (" #a " <= " #b ") at " __FILE__ ":%d", __LINE__)
+#define ASSERT_NULL(a) \
+ ASSERT((a) == nullptr, "assert failed on isNull(" #a ") at " __FILE__ ":%d", __LINE__)
+#define ASSERT_NOT_NULL(a) \
+ ASSERT((a) != nullptr, "assert failed on isNotNull(" #a ") at " __FILE__ ":%d", __LINE__)
+#define ASSERT_NAN(a) \
+ ASSERT(isnan(a), "assert failed on isNan(" #a ") at " __FILE__ ":%d", __LINE__)
+#define ASSERT_EMPTY_CSTR(a) do { \
+ const char *tmp = a; \
+ ASSERT(tmp != nullptr, \
+ "assert failed on (empty_cstr(" #a "): " #a " != nullptr) " \
+ "at " __FILE__ ":%d", __LINE__); \
+ ASSERT(tmp[0] == '\0', \
+ "assert failed on (empty_cstr(" #a "): strlen() == 0) " \
+ "at " __FILE__ ":%d", __LINE__); \
+ } while (false)
+
+
+void fail(JNIEnv* env, const char* format, ...);
diff --git a/tests/tests/nativehardware/jni/android_hardware_cts_AHardwareBufferNativeTest.cpp b/tests/tests/nativehardware/jni/android_hardware_cts_AHardwareBufferNativeTest.cpp
new file mode 100644
index 0000000..afe17d6
--- /dev/null
+++ b/tests/tests/nativehardware/jni/android_hardware_cts_AHardwareBufferNativeTest.cpp
@@ -0,0 +1,81 @@
+/*
+ * 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 "AHardwareBufferTest.h"
+#define TAG "AHardwareBufferTest"
+#include "NativeTestHelper.h"
+
+namespace {
+
+using android::AHardwareBufferTest;
+
+#define RETURN_ON_EXCEPTION() do { if (env->ExceptionCheck()) { return; } } while(false)
+
+jlong setUp(JNIEnv*, jclass) {
+ AHardwareBufferTest* test = new AHardwareBufferTest();
+ if (test != nullptr) {
+ test->SetUp();
+ }
+ return reinterpret_cast<jlong>(test);
+}
+
+void tearDown(JNIEnv*, jclass, jlong instance) {
+ delete reinterpret_cast<AHardwareBufferTest*>(instance);
+}
+
+void test(JNIEnv* env, jclass, jlong instance) {
+ AHardwareBufferTest *test = reinterpret_cast<AHardwareBufferTest*>(instance);
+ ASSERT_NOT_NULL(test);
+
+ ALOGI("testAHardwareBuffer_allocate_FailsWithNullInput");
+ test->testAHardwareBuffer_allocate_FailsWithNullInput(env);
+ RETURN_ON_EXCEPTION();
+
+ ALOGI("testAHardwareBuffer_allocate_BlobFormatRequiresHeight1");
+ test->testAHardwareBuffer_allocate_BlobFormatRequiresHeight1(env);
+ RETURN_ON_EXCEPTION();
+
+ ALOGI("testAHardwareBuffer_allocate_Succeeds");
+ test->testAHardwareBuffer_allocate_Succeeds(env);
+ RETURN_ON_EXCEPTION();
+
+ ALOGI("testAHardwareBuffer_describe_Succeeds");
+ test->testAHardwareBuffer_describe_Succeeds(env);
+ RETURN_ON_EXCEPTION();
+
+ ALOGI("testAHardwareBuffer_SendAndRecv_Succeeds");
+ test->testAHardwareBuffer_SendAndRecv_Succeeds(env);
+ RETURN_ON_EXCEPTION();
+
+ ALOGI("testAHardwareBuffer_Lock_and_Unlock_Succeed");
+ test->testAHardwareBuffer_Lock_and_Unlock_Succeed(env);
+ RETURN_ON_EXCEPTION();
+}
+
+JNINativeMethod gMethods[] = {
+ {"nativeSetUp", "()J", (void *)setUp},
+ {"nativeTearDown", "(J)V", (void *)tearDown},
+ {"nativeTest", "(J)V", (void *)test},
+};
+
+} // namespace
+
+int register_android_hardware_cts_AHardwareBufferNativeTest(JNIEnv* env) {
+ jclass clazz = env->FindClass("android/hardware/cts/AHardwareBufferNativeTest");
+ return env->RegisterNatives(clazz, gMethods,
+ sizeof(gMethods) / sizeof(JNINativeMethod));
+}
diff --git a/tests/tests/nativehardware/src/android/hardware/cts/AHardwareBufferNativeTest.java b/tests/tests/nativehardware/src/android/hardware/cts/AHardwareBufferNativeTest.java
new file mode 100644
index 0000000..883ca3f
--- /dev/null
+++ b/tests/tests/nativehardware/src/android/hardware/cts/AHardwareBufferNativeTest.java
@@ -0,0 +1,51 @@
+/*
+ * 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.hardware.cts;
+
+import android.test.AndroidTestCase;
+
+/**
+ * Check AHardwareBuffer functionality.
+ *
+ * This is the place to implement AHardwareBuffer NDK CTS tests.
+ */
+public class AHardwareBufferNativeTest extends AndroidTestCase {
+ protected native long nativeSetUp();
+ protected native void nativeTearDown(long instance);
+ private native void nativeTest(long instance);
+ private long mNativeInstance;
+
+ static {
+ System.loadLibrary("cts-nativehardware-ndk-jni");
+ }
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ mNativeInstance = nativeSetUp();
+ assertTrue("create native instance failed", mNativeInstance != 0);
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ nativeTearDown(mNativeInstance);
+ }
+
+ public void testNative() throws AssertionError {
+ nativeTest(mNativeInstance);
+ }
+}
diff --git a/tests/tests/nativemedia/aaudio/src/test_aaudio.cpp b/tests/tests/nativemedia/aaudio/src/test_aaudio.cpp
index 37b434e..c57682f 100644
--- a/tests/tests/nativemedia/aaudio/src/test_aaudio.cpp
+++ b/tests/tests/nativemedia/aaudio/src/test_aaudio.cpp
@@ -57,6 +57,58 @@
}
+
+// Test creating a default stream with specific devices
+void runtest_aaudio_devices(int32_t deviceId, bool expectFail) {
+ AAudioStreamBuilder *aaudioBuilder = nullptr;
+ AAudioStream *aaudioStream = nullptr;
+
+ // Use an AAudioStreamBuilder to define the stream.
+ aaudio_result_t result = AAudio_createStreamBuilder(&aaudioBuilder);
+ ASSERT_EQ(AAUDIO_OK, result);
+ ASSERT_NE(nullptr, aaudioBuilder);
+
+ AAudioStreamBuilder_setDeviceId(aaudioBuilder,deviceId);
+
+ // Create an AAudioStream using the Builder.
+ result = AAudioStreamBuilder_openStream(aaudioBuilder, &aaudioStream);
+ if (expectFail) {
+ ASSERT_NE(AAUDIO_OK, result);
+ ASSERT_EQ(nullptr, aaudioStream);
+ } else {
+ // Pass or fail is OK. Just don't crash.
+ ASSERT_TRUE(((result < 0) && (aaudioStream == nullptr))
+ || ((result == AAUDIO_OK) && (aaudioStream != nullptr)));
+ }
+
+ // Cleanup
+ EXPECT_EQ(AAUDIO_OK, AAudioStreamBuilder_delete(aaudioBuilder));
+ if (aaudioStream != nullptr) {
+ AAudioStream_close(aaudioStream);
+ }
+}
+
+TEST(test_aaudio, aaudio_stream_device_unspecified) {
+ runtest_aaudio_devices(AAUDIO_DEVICE_UNSPECIFIED, false);
+}
+
+/* FIXME - why can we open this device? What is an illegal deviceId?
+TEST(test_aaudio, aaudio_stream_device_absurd) {
+ runtest_aaudio_devices(19736459, true);
+}
+*/
+/* FIXME review
+TEST(test_aaudio, aaudio_stream_device_reasonable) {
+ runtest_aaudio_devices(1, false);
+}
+*/
+
+/* FIXME - why can we open this device? What is an illegal deviceId?
+TEST(test_aaudio, aaudio_stream_device_negative) {
+ runtest_aaudio_devices(-765, true);
+}
+*/
+
// Test creating a default stream with everything unspecified.
TEST(test_aaudio, aaudio_stream_unspecified) {
AAudioStreamBuilder *aaudioBuilder = nullptr;
@@ -120,7 +172,12 @@
AAudioStreamBuilder_setBufferCapacityInFrames(aaudioBuilder, 2000);
// Create an AAudioStream using the Builder.
- ASSERT_EQ(AAUDIO_OK, AAudioStreamBuilder_openStream(aaudioBuilder, &aaudioStream));
+ result = AAudioStreamBuilder_openStream(aaudioBuilder, &aaudioStream);
+ if (requestedSharingMode == AAUDIO_SHARING_MODE_EXCLUSIVE
+ && result != AAUDIO_OK) {
+ return; // EXCLUSIVE just may not be available. Should not crash.
+ }
+ ASSERT_EQ(AAUDIO_OK, result);
EXPECT_EQ(AAUDIO_OK, AAudioStreamBuilder_delete(aaudioBuilder));
EXPECT_EQ(AAUDIO_STREAM_STATE_OPEN, AAudioStream_getState(aaudioStream));
@@ -184,15 +241,14 @@
// Write some data while we are running. Read counter should be advancing.
writeLoops = 1 * actualSampleRate / framesPerBurst; // 1 second
ASSERT_LT(2, writeLoops); // detect absurdly high framesPerBurst
- timeoutNanos = 10 * NANOS_PER_SECOND * framesPerBurst / actualSampleRate; // bursts
+ timeoutNanos = 100 * (NANOS_PER_SECOND * framesPerBurst / actualSampleRate); // N bursts
framesWritten = 1;
aaudioFramesRead = AAudioStream_getFramesRead(aaudioStream);
aaudioFramesRead1 = aaudioFramesRead;
int64_t beginTime = getNanoseconds(CLOCK_MONOTONIC);
do {
framesWritten = AAudioStream_write(aaudioStream, data, framesPerBurst, timeoutNanos);
- ASSERT_GE(framesWritten, 0);
- ASSERT_LE(framesWritten, framesPerBurst);
+ ASSERT_EQ(framesWritten, framesPerBurst);
framesTotal += framesWritten;
aaudioFramesWritten = AAudioStream_getFramesWritten(aaudioStream);
@@ -211,8 +267,8 @@
aaudioFramesRead2 = AAudioStream_getFramesRead(aaudioStream);
int64_t endTime = getNanoseconds(CLOCK_MONOTONIC);
ASSERT_GT(aaudioFramesRead2, 0);
- ASSERT_GT(aaudioFramesRead2, aaudioFramesRead1);
- ASSERT_LE(aaudioFramesRead2, aaudioFramesWritten);
+ EXPECT_GT(aaudioFramesRead2, aaudioFramesRead1);
+
// TODO why is AudioTrack path so inaccurate?
const double rateTolerance = 200.0; // arbitrary tolerance for sample rate
@@ -236,15 +292,16 @@
aaudioFramesRead = AAudioStream_getFramesRead(aaudioStream);
ASSERT_GE(aaudioFramesRead, aaudioFramesRead2); // monotonic increase
- // Use this to sleep by waiting for something that won't happen.
- AAudioStream_waitForStateChange(aaudioStream, AAUDIO_STREAM_STATE_PAUSED, &state, timeoutNanos);
+ // Use this to sleep by waiting for a state that won't happen.
+ timeoutNanos = 100 * NANOS_PER_MILLISECOND;
+ AAudioStream_waitForStateChange(aaudioStream, AAUDIO_STREAM_STATE_OPEN, &state, timeoutNanos);
aaudioFramesRead2 = AAudioStream_getFramesRead(aaudioStream);
EXPECT_EQ(aaudioFramesRead, aaudioFramesRead2);
// ------------------- TEST FLUSH -----------------
// Prime the buffer.
timeoutNanos = 0;
- writeLoops = 100;
+ writeLoops = 1000;
do {
framesWritten = AAudioStream_write(aaudioStream, data, framesPerBurst, timeoutNanos);
framesTotal += framesWritten;
@@ -282,12 +339,10 @@
runtest_aaudio_stream(AAUDIO_SHARING_MODE_SHARED);
}
-/* TODO Enable exclusive mode test.
-// Test Writing to an AAudioStream using EXCLUSIVE sharing mode.
+// Test Writing to an AAudioStream using EXCLUSIVE sharing mode. It may fail gracefully.
TEST(test_aaudio, aaudio_stream_exclusive) {
runtest_aaudio_stream(AAUDIO_SHARING_MODE_EXCLUSIVE);
}
-*/
int main(int argc, char **argv) {
testing::InitGoogleTest(&argc, argv);
diff --git a/tests/tests/nativemedia/aaudio/src/test_aaudio_callback.cpp b/tests/tests/nativemedia/aaudio/src/test_aaudio_callback.cpp
index 2ec1e1c..3786967 100644
--- a/tests/tests/nativemedia/aaudio/src/test_aaudio_callback.cpp
+++ b/tests/tests/nativemedia/aaudio/src/test_aaudio_callback.cpp
@@ -18,17 +18,55 @@
#define LOG_TAG "AAudioTest"
#include <gtest/gtest.h>
+#include <atomic>
#include <utils/Log.h>
#include <aaudio/AAudio.h>
#include "test_aaudio.h"
typedef struct AAudioCallbackTestData {
- int32_t callbackCount;
int32_t expectedFramesPerCallback;
int32_t actualFramesPerCallback;
+ int32_t minLatency;
+ int32_t maxLatency;
+ std::atomic<aaudio_result_t> callbackError;
+ std::atomic<int32_t> callbackCount;
} AAudioCallbackTestData;
+static int32_t measureLatency(AAudioStream *stream) {
+ int64_t presentationTime = 0;
+ int64_t presentationPosition = 0;
+ int64_t now = getNanoseconds();
+ int32_t sampleRate = AAudioStream_getSampleRate(stream);
+ int64_t framesWritten = AAudioStream_getFramesWritten(stream);
+ aaudio_result_t result = AAudioStream_getTimestamp(stream,
+ CLOCK_MONOTONIC,
+ &presentationPosition,
+ &presentationTime);
+ if (result < 0) {
+ return result;
+ }
+ // Calculate when the last frame written would be played.
+ int64_t deltaFrames = framesWritten - presentationPosition;
+ EXPECT_GE(framesWritten, presentationPosition);
+ int64_t calculatedDeltaNanos = deltaFrames * NANOS_PER_SECOND / sampleRate;
+ int64_t calculatedTimeNanos = presentationTime + calculatedDeltaNanos;
+ int64_t latencyNanos = calculatedTimeNanos - now;
+ int32_t latencyMillis = (int32_t) ((latencyNanos + NANOS_PER_MILLISECOND - 1)
+ / NANOS_PER_MILLISECOND);
+ return latencyMillis;
+}
+
+static void MyErrorCallbackProc(
+ AAudioStream *stream,
+ void *userData,
+ aaudio_result_t error) {
+ (void) stream;
+ AAudioCallbackTestData *myData = (AAudioCallbackTestData *) userData;
+ myData->callbackError = error;
+
+}
+
// Callback function that fills the audio output buffer.
static aaudio_data_callback_result_t MyDataCallbackProc(
AAudioStream *stream,
@@ -54,6 +92,17 @@
float *floatData = (float *) audioData;
for (int i = 0; i < numSamples; i++) *floatData++ = 0.0f;
}
+
+ int32_t latency = measureLatency(stream);
+ if (latency > 0) {
+ if (latency < myData->minLatency) {
+ myData->minLatency = latency;
+ }
+ if (latency > myData->maxLatency) {
+ myData->maxLatency = latency;
+ }
+ }
+
myData->callbackCount++;
return AAUDIO_CALLBACK_RESULT_CONTINUE;
}
@@ -61,7 +110,7 @@
// Test Writing to an AAudioStream using a Callback
void runtest_aaudio_callback(aaudio_sharing_mode_t requestedSharingMode,
int32_t framesPerDataCallback) {
- AAudioCallbackTestData myTestData = { 0, 0, 0 };
+ AAudioCallbackTestData myTestData;
const int32_t requestedSampleRate = 48000;
const int32_t requestedSamplesPerFrame = 2;
const aaudio_audio_format_t requestedDataFormat = AAUDIO_FORMAT_PCM_I16;
@@ -79,6 +128,10 @@
AAudioStream *stream = nullptr;
aaudio_result_t result = AAUDIO_OK;
+ myTestData.callbackCount.store(0);
+ myTestData.callbackError = AAUDIO_OK;
+ myTestData.actualFramesPerCallback = 0;
+ myTestData.expectedFramesPerCallback = 0;
// Use an AAudioStreamBuilder to define the stream.
result = AAudio_createStreamBuilder(&builder);
@@ -93,6 +146,7 @@
AAudioStreamBuilder_setSharingMode(builder, requestedSharingMode);
AAudioStreamBuilder_setBufferCapacityInFrames(builder, 2000);
+ AAudioStreamBuilder_setErrorCallback(builder, MyErrorCallbackProc, &myTestData);
AAudioStreamBuilder_setDataCallback(builder, MyDataCallbackProc, &myTestData);
if (framesPerDataCallback != AAUDIO_UNSPECIFIED) {
AAudioStreamBuilder_setFramesPerDataCallback(builder, framesPerDataCallback);
@@ -118,8 +172,8 @@
actualDataFormat = AAudioStream_getFormat(stream);
- // TODO test this on full build
- // ASSERT_NE(AAUDIO_DEVICE_UNSPECIFIED, AAudioStream_getDeviceId(stream));
+ // TODO Why does getDeviceId() always return 0?
+ // EXPECT_NE(AAUDIO_DEVICE_UNSPECIFIED, AAudioStream_getDeviceId(stream));
framesPerBurst = AAudioStream_getFramesPerBurst(stream);
ASSERT_TRUE(framesPerBurst >= 16 && framesPerBurst <= 1024); // TODO what is min/max?
@@ -136,18 +190,27 @@
// 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++) {
+
myTestData.callbackCount = 0;
+ myTestData.minLatency = INT32_MAX;
+ myTestData.maxLatency = 0;
+ myTestData.callbackCount.store(0);
+
myTestData.expectedFramesPerCallback = actualFramesPerDataCallback;
// Start and wait for server to respond.
ASSERT_EQ(AAUDIO_OK, AAudioStream_requestStart(stream));
+
ASSERT_EQ(AAUDIO_OK, AAudioStream_waitForStateChange(stream,
AAUDIO_STREAM_STATE_STARTING,
&state,
DEFAULT_STATE_TIMEOUT));
EXPECT_EQ(AAUDIO_STREAM_STATE_STARTED, state);
- sleep(2);
+ sleep(2); // let the stream run
+
+ ASSERT_EQ(myTestData.callbackError.load(), AAUDIO_OK);
+ ASSERT_GT(myTestData.callbackCount.load(), 10);
// For more coverage, alternate pausing and stopping.
if ((loopIndex & 1) == 0) {
@@ -168,29 +231,34 @@
EXPECT_EQ(AAUDIO_STREAM_STATE_STOPPED, state);
}
- int32_t oldCallbackCount = myTestData.callbackCount;
+ int32_t oldCallbackCount = myTestData.callbackCount.load();
EXPECT_GT(oldCallbackCount, 10);
sleep(1);
- EXPECT_EQ(oldCallbackCount, myTestData.callbackCount); // expect not advancing
+ EXPECT_EQ(oldCallbackCount, myTestData.callbackCount.load()); // expect not advancing
if (framesPerDataCallback != AAUDIO_UNSPECIFIED) {
ASSERT_EQ(framesPerDataCallback, myTestData.actualFramesPerCallback);
}
+
+ EXPECT_GE(myTestData.minLatency, 1); // Absurdly low
+ EXPECT_LE(myTestData.maxLatency, 200); // Absurdly high, should be < 30
+ //printf("latency: %d, %d\n", myTestData.minLatency, myTestData.maxLatency);
}
+ ASSERT_EQ(myTestData.callbackError.load(), AAUDIO_OK);
+
EXPECT_EQ(AAUDIO_OK, AAudioStream_close(stream));
}
// Test Using an AAudioStream callback in SHARED mode.
-
TEST(test_aaudio, aaudio_callback_shared_unspecified) {
-runtest_aaudio_callback(AAUDIO_SHARING_MODE_SHARED, AAUDIO_UNSPECIFIED);
+ runtest_aaudio_callback(AAUDIO_SHARING_MODE_SHARED, AAUDIO_UNSPECIFIED);
}
TEST(test_aaudio, aaudio_callback_shared_109) {
-runtest_aaudio_callback(AAUDIO_SHARING_MODE_SHARED, 109); // arbitrary prime number < 192
+ runtest_aaudio_callback(AAUDIO_SHARING_MODE_SHARED, 109); // arbitrary prime number < 192
}
TEST(test_aaudio, aaudio_callback_shared_223) {
-runtest_aaudio_callback(AAUDIO_SHARING_MODE_SHARED, 223); // arbitrary prime number > 192
+ runtest_aaudio_callback(AAUDIO_SHARING_MODE_SHARED, 223); // arbitrary prime number > 192
}
diff --git a/tests/tests/nativemedia/aaudio/src/test_aaudio_misc.cpp b/tests/tests/nativemedia/aaudio/src/test_aaudio_misc.cpp
index 8599d48..6662b72 100644
--- a/tests/tests/nativemedia/aaudio/src/test_aaudio_misc.cpp
+++ b/tests/tests/nativemedia/aaudio/src/test_aaudio_misc.cpp
@@ -1,7 +1,7 @@
/*
* Copyright 2017 The Android Open Source Project
*
- * Licensed under the Apache License, Version 2.0 (the "License");
+ * Licensed under the Apache License, Version 2.0 (the "License", ENUM_CANNOT_CHANGE);
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
@@ -25,53 +25,55 @@
// Make sure enums do not change value.
TEST(test_aaudio_misc, aaudio_freeze_enums) {
- ASSERT_EQ(0, AAUDIO_DIRECTION_OUTPUT);
- ASSERT_EQ(1, AAUDIO_DIRECTION_INPUT);
+#define ENUM_CANNOT_CHANGE "enum in API cannot change"
- ASSERT_EQ(-1, AAUDIO_FORMAT_INVALID);
- ASSERT_EQ(0, AAUDIO_FORMAT_UNSPECIFIED);
- ASSERT_EQ(1, AAUDIO_FORMAT_PCM_I16);
- ASSERT_EQ(2, AAUDIO_FORMAT_PCM_FLOAT);
+ static_assert(0 == AAUDIO_DIRECTION_OUTPUT, ENUM_CANNOT_CHANGE);
+ static_assert(1 == AAUDIO_DIRECTION_INPUT, ENUM_CANNOT_CHANGE);
- ASSERT_EQ(0, AAUDIO_OK);
- ASSERT_EQ(-900, AAUDIO_ERROR_BASE);
- ASSERT_EQ(-899, AAUDIO_ERROR_DISCONNECTED);
- ASSERT_EQ(-898, AAUDIO_ERROR_ILLEGAL_ARGUMENT);
- ASSERT_EQ(-897, AAUDIO_ERROR_INCOMPATIBLE);
- ASSERT_EQ(-896, AAUDIO_ERROR_INTERNAL);
- ASSERT_EQ(-895, AAUDIO_ERROR_INVALID_STATE);
- ASSERT_EQ(-894, AAUDIO_ERROR_UNEXPECTED_STATE);
- ASSERT_EQ(-893, AAUDIO_ERROR_UNEXPECTED_VALUE);
- ASSERT_EQ(-892, AAUDIO_ERROR_INVALID_HANDLE);
- ASSERT_EQ(-891, AAUDIO_ERROR_INVALID_QUERY);
- ASSERT_EQ(-890, AAUDIO_ERROR_UNIMPLEMENTED);
- ASSERT_EQ(-889, AAUDIO_ERROR_UNAVAILABLE);
- ASSERT_EQ(-888, AAUDIO_ERROR_NO_FREE_HANDLES);
- ASSERT_EQ(-887, AAUDIO_ERROR_NO_MEMORY);
- ASSERT_EQ(-886, AAUDIO_ERROR_NULL);
- ASSERT_EQ(-885, AAUDIO_ERROR_TIMEOUT);
- ASSERT_EQ(-884, AAUDIO_ERROR_WOULD_BLOCK);
- ASSERT_EQ(-883, AAUDIO_ERROR_INVALID_FORMAT);
- ASSERT_EQ(-882, AAUDIO_ERROR_OUT_OF_RANGE);
- ASSERT_EQ(-881, AAUDIO_ERROR_NO_SERVICE);
+ static_assert(-1 == AAUDIO_FORMAT_INVALID, ENUM_CANNOT_CHANGE);
+ static_assert(0 == AAUDIO_FORMAT_UNSPECIFIED, ENUM_CANNOT_CHANGE);
+ static_assert(1 == AAUDIO_FORMAT_PCM_I16, ENUM_CANNOT_CHANGE);
+ static_assert(2 == AAUDIO_FORMAT_PCM_FLOAT, ENUM_CANNOT_CHANGE);
- ASSERT_EQ(0, AAUDIO_STREAM_STATE_UNINITIALIZED);
- ASSERT_EQ(1, AAUDIO_STREAM_STATE_UNKNOWN);
- ASSERT_EQ(2, AAUDIO_STREAM_STATE_OPEN);
- ASSERT_EQ(3, AAUDIO_STREAM_STATE_STARTING);
- ASSERT_EQ(4, AAUDIO_STREAM_STATE_STARTED);
- ASSERT_EQ(5, AAUDIO_STREAM_STATE_PAUSING);
- ASSERT_EQ(6, AAUDIO_STREAM_STATE_PAUSED);
- ASSERT_EQ(7, AAUDIO_STREAM_STATE_FLUSHING);
- ASSERT_EQ(8, AAUDIO_STREAM_STATE_FLUSHED);
- ASSERT_EQ(9, AAUDIO_STREAM_STATE_STOPPING);
- ASSERT_EQ(10, AAUDIO_STREAM_STATE_STOPPED);
- ASSERT_EQ(11, AAUDIO_STREAM_STATE_CLOSING);
- ASSERT_EQ(12, AAUDIO_STREAM_STATE_CLOSED);
+ static_assert(0 == AAUDIO_OK, ENUM_CANNOT_CHANGE);
+ static_assert(-900 == AAUDIO_ERROR_BASE, ENUM_CANNOT_CHANGE);
+ static_assert(-899 == AAUDIO_ERROR_DISCONNECTED, ENUM_CANNOT_CHANGE);
+ static_assert(-898 == AAUDIO_ERROR_ILLEGAL_ARGUMENT, ENUM_CANNOT_CHANGE);
+ static_assert(-897 == AAUDIO_ERROR_INCOMPATIBLE, ENUM_CANNOT_CHANGE);
+ static_assert(-896 == AAUDIO_ERROR_INTERNAL, ENUM_CANNOT_CHANGE);
+ static_assert(-895 == AAUDIO_ERROR_INVALID_STATE, ENUM_CANNOT_CHANGE);
+ static_assert(-894 == AAUDIO_ERROR_UNEXPECTED_STATE, ENUM_CANNOT_CHANGE);
+ static_assert(-893 == AAUDIO_ERROR_UNEXPECTED_VALUE, ENUM_CANNOT_CHANGE);
+ static_assert(-892 == AAUDIO_ERROR_INVALID_HANDLE, ENUM_CANNOT_CHANGE);
+ static_assert(-891 == AAUDIO_ERROR_INVALID_QUERY, ENUM_CANNOT_CHANGE);
+ static_assert(-890 == AAUDIO_ERROR_UNIMPLEMENTED, ENUM_CANNOT_CHANGE);
+ static_assert(-889 == AAUDIO_ERROR_UNAVAILABLE, ENUM_CANNOT_CHANGE);
+ static_assert(-888 == AAUDIO_ERROR_NO_FREE_HANDLES, ENUM_CANNOT_CHANGE);
+ static_assert(-887 == AAUDIO_ERROR_NO_MEMORY, ENUM_CANNOT_CHANGE);
+ static_assert(-886 == AAUDIO_ERROR_NULL, ENUM_CANNOT_CHANGE);
+ static_assert(-885 == AAUDIO_ERROR_TIMEOUT, ENUM_CANNOT_CHANGE);
+ static_assert(-884 == AAUDIO_ERROR_WOULD_BLOCK, ENUM_CANNOT_CHANGE);
+ static_assert(-883 == AAUDIO_ERROR_INVALID_FORMAT, ENUM_CANNOT_CHANGE);
+ static_assert(-882 == AAUDIO_ERROR_OUT_OF_RANGE, ENUM_CANNOT_CHANGE);
+ static_assert(-881 == AAUDIO_ERROR_NO_SERVICE, ENUM_CANNOT_CHANGE);
- ASSERT_EQ(0, AAUDIO_SHARING_MODE_EXCLUSIVE);
- ASSERT_EQ(1, AAUDIO_SHARING_MODE_SHARED);
+ static_assert(0 == AAUDIO_STREAM_STATE_UNINITIALIZED, ENUM_CANNOT_CHANGE);
+ static_assert(1 == AAUDIO_STREAM_STATE_UNKNOWN, ENUM_CANNOT_CHANGE);
+ static_assert(2 == AAUDIO_STREAM_STATE_OPEN, ENUM_CANNOT_CHANGE);
+ static_assert(3 == AAUDIO_STREAM_STATE_STARTING, ENUM_CANNOT_CHANGE);
+ static_assert(4 == AAUDIO_STREAM_STATE_STARTED, ENUM_CANNOT_CHANGE);
+ static_assert(5 == AAUDIO_STREAM_STATE_PAUSING, ENUM_CANNOT_CHANGE);
+ static_assert(6 == AAUDIO_STREAM_STATE_PAUSED, ENUM_CANNOT_CHANGE);
+ static_assert(7 == AAUDIO_STREAM_STATE_FLUSHING, ENUM_CANNOT_CHANGE);
+ static_assert(8 == AAUDIO_STREAM_STATE_FLUSHED, ENUM_CANNOT_CHANGE);
+ static_assert(9 == AAUDIO_STREAM_STATE_STOPPING, ENUM_CANNOT_CHANGE);
+ static_assert(10 == AAUDIO_STREAM_STATE_STOPPED, ENUM_CANNOT_CHANGE);
+ static_assert(11 == AAUDIO_STREAM_STATE_CLOSING, ENUM_CANNOT_CHANGE);
+ static_assert(12 == AAUDIO_STREAM_STATE_CLOSED, ENUM_CANNOT_CHANGE);
- ASSERT_EQ(0, AAUDIO_CALLBACK_RESULT_CONTINUE);
- ASSERT_EQ(1, AAUDIO_CALLBACK_RESULT_STOP);
+ static_assert(0 == AAUDIO_SHARING_MODE_EXCLUSIVE, ENUM_CANNOT_CHANGE);
+ static_assert(1 == AAUDIO_SHARING_MODE_SHARED, ENUM_CANNOT_CHANGE);
+
+ static_assert(0 == AAUDIO_CALLBACK_RESULT_CONTINUE, ENUM_CANNOT_CHANGE);
+ static_assert(1 == AAUDIO_CALLBACK_RESULT_STOP, ENUM_CANNOT_CHANGE);
}
diff --git a/tests/tests/net/src/android/net/wifi/cts/PpsMoParserTest.java b/tests/tests/net/src/android/net/wifi/cts/PpsMoParserTest.java
index b5e2f77..0304fda 100644
--- a/tests/tests/net/src/android/net/wifi/cts/PpsMoParserTest.java
+++ b/tests/tests/net/src/android/net/wifi/cts/PpsMoParserTest.java
@@ -107,19 +107,19 @@
assertEquals(subscriptionUpdate, config.getSubscriptionUpdate());
// Subscription parameters.
- config.setSubscriptionCreationTimeInMs(format.parse("2016-02-01T10:00:00Z").getTime());
+ config.setSubscriptionCreationTimeInMillis(format.parse("2016-02-01T10:00:00Z").getTime());
assertEquals(format.parse("2016-02-01T10:00:00Z").getTime(),
- config.getSubscriptionCreationTimeInMs());
- config.setSubscriptionExpirationTimeInMs(format.parse("2016-03-01T10:00:00Z").getTime());
+ config.getSubscriptionCreationTimeInMillis());
+ config.setSubscriptionExpirationTimeInMillis(format.parse("2016-03-01T10:00:00Z").getTime());
assertEquals(format.parse("2016-03-01T10:00:00Z").getTime(),
- config.getSubscriptionExpirationTimeInMs());
+ config.getSubscriptionExpirationTimeInMillis());
config.setSubscriptionType("Gold");
assertEquals("Gold", config.getSubscriptionType());
config.setUsageLimitDataLimit(921890);
assertEquals(921890, config.getUsageLimitDataLimit());
- config.setUsageLimitStartTimeInMs(format.parse("2016-12-01T10:00:00Z").getTime());
+ config.setUsageLimitStartTimeInMillis(format.parse("2016-12-01T10:00:00Z").getTime());
assertEquals(format.parse("2016-12-01T10:00:00Z").getTime(),
- config.getUsageLimitStartTimeInMs());
+ config.getUsageLimitStartTimeInMillis());
config.setUsageLimitTimeLimitInMinutes(120);
assertEquals(120, config.getUsageLimitTimeLimitInMinutes());
config.setUsageLimitUsageTimePeriodInMinutes(99910);
@@ -153,12 +153,12 @@
// Credential configuration.
Credential credential = new Credential();
- credential.setCreationTimeInMs(format.parse("2016-01-01T10:00:00Z").getTime());
+ credential.setCreationTimeInMillis(format.parse("2016-01-01T10:00:00Z").getTime());
assertEquals(format.parse("2016-01-01T10:00:00Z").getTime(),
- credential.getCreationTimeInMs());
- credential.setExpirationTimeInMs(format.parse("2016-02-01T10:00:00Z").getTime());
+ credential.getCreationTimeInMillis());
+ credential.setExpirationTimeInMillis(format.parse("2016-02-01T10:00:00Z").getTime());
assertEquals(format.parse("2016-02-01T10:00:00Z").getTime(),
- credential.getExpirationTimeInMs());
+ credential.getExpirationTimeInMillis());
credential.setRealm("shaken.stirred.com");
assertEquals("shaken.stirred.com", credential.getRealm());
credential.setCheckAaaServerCertStatus(true);
diff --git a/tests/tests/net/src/android/net/wifi/cts/WifiManagerTest.java b/tests/tests/net/src/android/net/wifi/cts/WifiManagerTest.java
index 14ae1b4..e888c14 100644
--- a/tests/tests/net/src/android/net/wifi/cts/WifiManagerTest.java
+++ b/tests/tests/net/src/android/net/wifi/cts/WifiManagerTest.java
@@ -239,11 +239,10 @@
assertTrue(mWifiManager.reconnect());
assertTrue(mWifiManager.reassociate());
assertTrue(mWifiManager.disconnect());
- assertTrue(mWifiManager.pingSupplicant());
startScan();
setWifiEnabled(false);
Thread.sleep(DURATION);
- assertTrue(mWifiManager.pingSupplicant() == mWifiManager.isScanAlwaysAvailable());
+ assertTrue(mWifiManager.isScanAlwaysAvailable());
final String TAG = "Test";
assertNotNull(mWifiManager.createWifiLock(TAG));
assertNotNull(mWifiManager.createWifiLock(WifiManager.WIFI_MODE_FULL, TAG));
diff --git a/tests/tests/permission2/res/raw/android_manifest.xml b/tests/tests/permission2/res/raw/android_manifest.xml
index 704cc93..3b281a6 100644
--- a/tests/tests/permission2/res/raw/android_manifest.xml
+++ b/tests/tests/permission2/res/raw/android_manifest.xml
@@ -18,8 +18,8 @@
*/
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="android" coreApp="true" android:sharedUserId="android.uid.system"
- android:sharedUserLabel="@string/android_system_label">
+ package="android" coreApp="true" android:sharedUserId="android.uid.system"
+ android:sharedUserLabel="@string/android_system_label">
<!-- ================================================ -->
<!-- Special broadcasts that only the system can send -->
@@ -149,59 +149,59 @@
<protected-broadcast android:name="android.bluetooth.devicepicker.action.LAUNCH" />
<protected-broadcast android:name="android.bluetooth.devicepicker.action.DEVICE_SELECTED" />
<protected-broadcast
- android:name="android.bluetooth.headset.profile.action.CONNECTION_STATE_CHANGED" />
+ android:name="android.bluetooth.headset.profile.action.CONNECTION_STATE_CHANGED" />
<protected-broadcast
- android:name="android.bluetooth.headset.profile.action.AUDIO_STATE_CHANGED" />
+ android:name="android.bluetooth.headset.profile.action.AUDIO_STATE_CHANGED" />
<protected-broadcast
- android:name="android.bluetooth.headset.action.VENDOR_SPECIFIC_HEADSET_EVENT" />
+ android:name="android.bluetooth.headset.action.VENDOR_SPECIFIC_HEADSET_EVENT" />
<protected-broadcast
- android:name="android.bluetooth.headset.action.HF_INDICATORS_VALUE_CHANGED" />
+ android:name="android.bluetooth.headset.action.HF_INDICATORS_VALUE_CHANGED" />
<protected-broadcast
- android:name="android.bluetooth.headsetclient.profile.action.CONNECTION_STATE_CHANGED" />
+ android:name="android.bluetooth.headsetclient.profile.action.CONNECTION_STATE_CHANGED" />
<protected-broadcast
- android:name="android.bluetooth.headsetclient.profile.action.AUDIO_STATE_CHANGED" />
+ android:name="android.bluetooth.headsetclient.profile.action.AUDIO_STATE_CHANGED" />
<protected-broadcast
- android:name="android.bluetooth.headsetclient.profile.action.AG_EVENT" />
+ android:name="android.bluetooth.headsetclient.profile.action.AG_EVENT" />
<protected-broadcast
- android:name="android.bluetooth.headsetclient.profile.action.AG_CALL_CHANGED" />
+ android:name="android.bluetooth.headsetclient.profile.action.AG_CALL_CHANGED" />
<protected-broadcast
- android:name="android.bluetooth.headsetclient.profile.action.RESULT" />
+ android:name="android.bluetooth.headsetclient.profile.action.RESULT" />
<protected-broadcast
- android:name="android.bluetooth.headsetclient.profile.action.LAST_VTAG" />
+ android:name="android.bluetooth.headsetclient.profile.action.LAST_VTAG" />
<protected-broadcast
- android:name="android.bluetooth.a2dp.profile.action.CONNECTION_STATE_CHANGED" />
+ android:name="android.bluetooth.a2dp.profile.action.CONNECTION_STATE_CHANGED" />
<protected-broadcast
- android:name="android.bluetooth.a2dp.profile.action.PLAYING_STATE_CHANGED" />
+ android:name="android.bluetooth.a2dp.profile.action.PLAYING_STATE_CHANGED" />
<protected-broadcast
- android:name="android.bluetooth.a2dp.profile.action.CODEC_CONFIG_CHANGED" />
+ android:name="android.bluetooth.a2dp.profile.action.CODEC_CONFIG_CHANGED" />
<protected-broadcast
- android:name="android.bluetooth.a2dp-sink.profile.action.CONNECTION_STATE_CHANGED" />
+ android:name="android.bluetooth.a2dp-sink.profile.action.CONNECTION_STATE_CHANGED" />
<protected-broadcast
- android:name="android.bluetooth.a2dp-sink.profile.action.PLAYING_STATE_CHANGED" />
+ android:name="android.bluetooth.a2dp-sink.profile.action.PLAYING_STATE_CHANGED" />
<protected-broadcast
- android:name="android.bluetooth.a2dp-sink.profile.action.AUDIO_CONFIG_CHANGED" />
+ android:name="android.bluetooth.a2dp-sink.profile.action.AUDIO_CONFIG_CHANGED" />
<protected-broadcast
- android:name="android.bluetooth.avrcp-controller.profile.action.CONNECTION_STATE_CHANGED" />
+ android:name="android.bluetooth.avrcp-controller.profile.action.CONNECTION_STATE_CHANGED" />
<protected-broadcast
- android:name="android.bluetooth.input.profile.action.CONNECTION_STATE_CHANGED" />
+ android:name="android.bluetooth.input.profile.action.CONNECTION_STATE_CHANGED" />
<protected-broadcast
- android:name="android.bluetooth.input.profile.action.PROTOCOL_MODE_CHANGED" />
+ android:name="android.bluetooth.input.profile.action.PROTOCOL_MODE_CHANGED" />
<protected-broadcast
- android:name="android.bluetooth.input.profile.action.VIRTUAL_UNPLUG_STATUS" />
+ android:name="android.bluetooth.input.profile.action.VIRTUAL_UNPLUG_STATUS" />
<protected-broadcast
- android:name="android.bluetooth.inputhost.profile.action.CONNECTION_STATE_CHANGED" />
+ android:name="android.bluetooth.inputhost.profile.action.CONNECTION_STATE_CHANGED" />
<protected-broadcast
- android:name="android.bluetooth.map.profile.action.CONNECTION_STATE_CHANGED" />
+ android:name="android.bluetooth.map.profile.action.CONNECTION_STATE_CHANGED" />
<protected-broadcast android:name="android.bluetooth.mapmce.profile.action.CONNECTION_STATE_CHANGED" />
<protected-broadcast android:name="android.bluetooth.mapmce.profile.action.MESSAGE_RECEIVED" />
<protected-broadcast android:name="android.bluetooth.mapmce.profile.action.MESSAGE_SENT_SUCCESSFULLY" />
<protected-broadcast android:name="android.bluetooth.mapmce.profile.action.MESSAGE_DELIVERED_SUCCESSFULLY" />
<protected-broadcast
- android:name="com.android.bluetooth.BluetoothMapContentObserver.action.MESSAGE_SENT" />
+ android:name="com.android.bluetooth.BluetoothMapContentObserver.action.MESSAGE_SENT" />
<protected-broadcast
- android:name="com.android.bluetooth.BluetoothMapContentObserver.action.MESSAGE_DELIVERY" />
+ android:name="com.android.bluetooth.BluetoothMapContentObserver.action.MESSAGE_DELIVERY" />
<protected-broadcast
- android:name="android.bluetooth.pan.profile.action.CONNECTION_STATE_CHANGED" />
+ android:name="android.bluetooth.pan.profile.action.CONNECTION_STATE_CHANGED" />
<protected-broadcast android:name="android.bluetooth.pbap.intent.action.PBAP_STATE_CHANGED" />
<protected-broadcast android:name="android.bluetooth.pbap.profile.action.CONNECTION_STATE_CHANGED" />
<protected-broadcast android:name="android.bluetooth.sap.profile.action.CONNECTION_STATE_CHANGED" />
@@ -363,11 +363,11 @@
<protected-broadcast android:name="android.provider.Telephony.MMS_DOWNLOADED" />
<protected-broadcast
- android:name="com.android.server.connectivityservice.CONNECTED_TO_PROVISIONING_NETWORK_ACTION" />
+ android:name="com.android.server.connectivityservice.CONNECTED_TO_PROVISIONING_NETWORK_ACTION" />
<!-- Defined in RestrictionsManager -->
<protected-broadcast
- android:name="android.intent.action.PERMISSION_RESPONSE_RECEIVED" />
+ android:name="android.intent.action.PERMISSION_RESPONSE_RECEIVED" />
<!-- Defined in RestrictionsManager -->
<protected-broadcast android:name="android.intent.action.REQUEST_PERMISSION" />
@@ -553,28 +553,28 @@
<!-- Used for runtime permissions related to contacts and profiles on this
device. -->
<permission-group android:name="android.permission-group.CONTACTS"
- android:icon="@drawable/perm_group_contacts"
- android:label="@string/permgrouplab_contacts"
- android:description="@string/permgroupdesc_contacts"
- android:priority="100" />
+ android:icon="@drawable/perm_group_contacts"
+ android:label="@string/permgrouplab_contacts"
+ android:description="@string/permgroupdesc_contacts"
+ android:priority="100" />
<!-- Allows an application to read the user's contacts data.
<p>Protection level: dangerous
-->
<permission android:name="android.permission.READ_CONTACTS"
- android:permissionGroup="android.permission-group.CONTACTS"
- android:label="@string/permlab_readContacts"
- android:description="@string/permdesc_readContacts"
- android:protectionLevel="dangerous" />
+ android:permissionGroup="android.permission-group.CONTACTS"
+ android:label="@string/permlab_readContacts"
+ android:description="@string/permdesc_readContacts"
+ android:protectionLevel="dangerous" />
<!-- Allows an application to write the user's contacts data.
<p>Protection level: dangerous
-->
<permission android:name="android.permission.WRITE_CONTACTS"
- android:permissionGroup="android.permission-group.CONTACTS"
- android:label="@string/permlab_writeContacts"
- android:description="@string/permdesc_writeContacts"
- android:protectionLevel="dangerous" />
+ android:permissionGroup="android.permission-group.CONTACTS"
+ android:label="@string/permlab_writeContacts"
+ android:description="@string/permdesc_writeContacts"
+ android:protectionLevel="dangerous" />
<!-- ====================================================================== -->
<!-- Permissions for accessing user's calendar -->
@@ -583,28 +583,28 @@
<!-- Used for runtime permissions related to user's calendar. -->
<permission-group android:name="android.permission-group.CALENDAR"
- android:icon="@drawable/perm_group_calendar"
- android:label="@string/permgrouplab_calendar"
- android:description="@string/permgroupdesc_calendar"
- android:priority="200" />
+ android:icon="@drawable/perm_group_calendar"
+ android:label="@string/permgrouplab_calendar"
+ android:description="@string/permgroupdesc_calendar"
+ android:priority="200" />
<!-- Allows an application to read the user's calendar data.
<p>Protection level: dangerous
-->
<permission android:name="android.permission.READ_CALENDAR"
- android:permissionGroup="android.permission-group.CALENDAR"
- android:label="@string/permlab_readCalendar"
- android:description="@string/permdesc_readCalendar"
- android:protectionLevel="dangerous" />
+ android:permissionGroup="android.permission-group.CALENDAR"
+ android:label="@string/permlab_readCalendar"
+ android:description="@string/permdesc_readCalendar"
+ android:protectionLevel="dangerous" />
<!-- Allows an application to write the user's calendar data.
<p>Protection level: dangerous
-->
<permission android:name="android.permission.WRITE_CALENDAR"
- android:permissionGroup="android.permission-group.CALENDAR"
- android:label="@string/permlab_writeCalendar"
- android:description="@string/permdesc_writeCalendar"
- android:protectionLevel="dangerous" />
+ android:permissionGroup="android.permission-group.CALENDAR"
+ android:label="@string/permlab_writeCalendar"
+ android:description="@string/permdesc_writeCalendar"
+ android:protectionLevel="dangerous" />
<!-- ====================================================================== -->
<!-- Permissions for accessing and modifying user's SMS messages -->
@@ -613,56 +613,56 @@
<!-- Used for runtime permissions related to user's SMS messages. -->
<permission-group android:name="android.permission-group.SMS"
- android:icon="@drawable/perm_group_sms"
- android:label="@string/permgrouplab_sms"
- android:description="@string/permgroupdesc_sms"
- android:priority="300" />
+ android:icon="@drawable/perm_group_sms"
+ android:label="@string/permgrouplab_sms"
+ android:description="@string/permgroupdesc_sms"
+ android:priority="300" />
<!-- Allows an application to send SMS messages.
<p>Protection level: dangerous
-->
<permission android:name="android.permission.SEND_SMS"
- android:permissionGroup="android.permission-group.SMS"
- android:label="@string/permlab_sendSms"
- android:description="@string/permdesc_sendSms"
- android:permissionFlags="costsMoney"
- android:protectionLevel="dangerous" />
+ android:permissionGroup="android.permission-group.SMS"
+ android:label="@string/permlab_sendSms"
+ android:description="@string/permdesc_sendSms"
+ android:permissionFlags="costsMoney"
+ android:protectionLevel="dangerous" />
<!-- Allows an application to receive SMS messages.
<p>Protection level: dangerous
-->
<permission android:name="android.permission.RECEIVE_SMS"
- android:permissionGroup="android.permission-group.SMS"
- android:label="@string/permlab_receiveSms"
- android:description="@string/permdesc_receiveSms"
- android:protectionLevel="dangerous"/>
+ android:permissionGroup="android.permission-group.SMS"
+ android:label="@string/permlab_receiveSms"
+ android:description="@string/permdesc_receiveSms"
+ android:protectionLevel="dangerous"/>
<!-- Allows an application to read SMS messages.
<p>Protection level: dangerous
-->
<permission android:name="android.permission.READ_SMS"
- android:permissionGroup="android.permission-group.SMS"
- android:label="@string/permlab_readSms"
- android:description="@string/permdesc_readSms"
- android:protectionLevel="dangerous" />
+ android:permissionGroup="android.permission-group.SMS"
+ android:label="@string/permlab_readSms"
+ android:description="@string/permdesc_readSms"
+ android:protectionLevel="dangerous" />
<!-- Allows an application to receive WAP push messages.
<p>Protection level: dangerous
-->
<permission android:name="android.permission.RECEIVE_WAP_PUSH"
- android:permissionGroup="android.permission-group.SMS"
- android:label="@string/permlab_receiveWapPush"
- android:description="@string/permdesc_receiveWapPush"
- android:protectionLevel="dangerous" />
+ android:permissionGroup="android.permission-group.SMS"
+ android:label="@string/permlab_receiveWapPush"
+ android:description="@string/permdesc_receiveWapPush"
+ android:protectionLevel="dangerous" />
<!-- Allows an application to monitor incoming MMS messages.
<p>Protection level: dangerous
-->
<permission android:name="android.permission.RECEIVE_MMS"
- android:permissionGroup="android.permission-group.SMS"
- android:label="@string/permlab_receiveMms"
- android:description="@string/permdesc_receiveMms"
- android:protectionLevel="dangerous" />
+ android:permissionGroup="android.permission-group.SMS"
+ android:label="@string/permlab_receiveMms"
+ android:description="@string/permdesc_receiveMms"
+ android:protectionLevel="dangerous" />
<!-- Allows an application to read previously received cell broadcast
messages and to register a content observer to get notifications when
@@ -677,10 +677,10 @@
<p>Protection level: dangerous
@hide Pending API council approval -->
<permission android:name="android.permission.READ_CELL_BROADCASTS"
- android:permissionGroup="android.permission-group.SMS"
- android:label="@string/permlab_readCellBroadcasts"
- android:description="@string/permdesc_readCellBroadcasts"
- android:protectionLevel="dangerous" />
+ android:permissionGroup="android.permission-group.SMS"
+ android:label="@string/permlab_readCellBroadcasts"
+ android:description="@string/permdesc_readCellBroadcasts"
+ android:protectionLevel="dangerous" />
<!-- ====================================================================== -->
<!-- Permissions for accessing external storage -->
@@ -689,10 +689,10 @@
<!-- Used for runtime permissions related to the shared external storage. -->
<permission-group android:name="android.permission-group.STORAGE"
- android:icon="@drawable/perm_group_storage"
- android:label="@string/permgrouplab_storage"
- android:description="@string/permgroupdesc_storage"
- android:priority="900" />
+ android:icon="@drawable/perm_group_storage"
+ android:label="@string/permgrouplab_storage"
+ android:description="@string/permgroupdesc_storage"
+ android:priority="900" />
<!-- Allows an application to read from external storage.
<p>Any app that declares the {@link #WRITE_EXTERNAL_STORAGE} permission is implicitly
@@ -717,10 +717,10 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.READ_EXTERNAL_STORAGE"
- android:permissionGroup="android.permission-group.STORAGE"
- android:label="@string/permlab_sdcardRead"
- android:description="@string/permdesc_sdcardRead"
- android:protectionLevel="dangerous" />
+ android:permissionGroup="android.permission-group.STORAGE"
+ android:label="@string/permlab_sdcardRead"
+ android:description="@string/permdesc_sdcardRead"
+ android:protectionLevel="dangerous" />
<!-- Allows an application to write to external storage.
<p class="note"><strong>Note:</strong> If <em>both</em> your <a
@@ -738,10 +738,10 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
- android:permissionGroup="android.permission-group.STORAGE"
- android:label="@string/permlab_sdcardWrite"
- android:description="@string/permdesc_sdcardWrite"
- android:protectionLevel="dangerous" />
+ android:permissionGroup="android.permission-group.STORAGE"
+ android:label="@string/permlab_sdcardWrite"
+ android:description="@string/permdesc_sdcardWrite"
+ android:protectionLevel="dangerous" />
<!-- ====================================================================== -->
<!-- Permissions for accessing the device location -->
@@ -750,30 +750,30 @@
<!-- Used for permissions that allow accessing the device location. -->
<permission-group android:name="android.permission-group.LOCATION"
- android:icon="@drawable/perm_group_location"
- android:label="@string/permgrouplab_location"
- android:description="@string/permgroupdesc_location"
- android:priority="400" />
+ android:icon="@drawable/perm_group_location"
+ android:label="@string/permgrouplab_location"
+ android:description="@string/permgroupdesc_location"
+ android:priority="400" />
<!-- Allows an app to access precise location.
Alternatively, you might want {@link #ACCESS_COARSE_LOCATION}.
<p>Protection level: dangerous
-->
<permission android:name="android.permission.ACCESS_FINE_LOCATION"
- android:permissionGroup="android.permission-group.LOCATION"
- android:label="@string/permlab_accessFineLocation"
- android:description="@string/permdesc_accessFineLocation"
- android:protectionLevel="dangerous|ephemeral" />
+ android:permissionGroup="android.permission-group.LOCATION"
+ android:label="@string/permlab_accessFineLocation"
+ android:description="@string/permdesc_accessFineLocation"
+ android:protectionLevel="dangerous|ephemeral" />
<!-- Allows an app to access approximate location.
Alternatively, you might want {@link #ACCESS_FINE_LOCATION}.
<p>Protection level: dangerous
-->
<permission android:name="android.permission.ACCESS_COARSE_LOCATION"
- android:permissionGroup="android.permission-group.LOCATION"
- android:label="@string/permlab_accessCoarseLocation"
- android:description="@string/permdesc_accessCoarseLocation"
- android:protectionLevel="dangerous|ephemeral" />
+ android:permissionGroup="android.permission-group.LOCATION"
+ android:label="@string/permlab_accessCoarseLocation"
+ android:description="@string/permdesc_accessCoarseLocation"
+ android:protectionLevel="dangerous|ephemeral" />
<!-- ====================================================================== -->
<!-- Permissions for accessing the device telephony -->
@@ -782,10 +782,10 @@
<!-- Used for permissions that are associated telephony features. -->
<permission-group android:name="android.permission-group.PHONE"
- android:icon="@drawable/perm_group_phone_calls"
- android:label="@string/permgrouplab_phone"
- android:description="@string/permgroupdesc_phone"
- android:priority="500" />
+ android:icon="@drawable/perm_group_phone_calls"
+ android:label="@string/permgrouplab_phone"
+ android:description="@string/permgroupdesc_phone"
+ android:priority="500" />
<!-- Allows read only access to phone state, including the phone number of the device,
current cellular network information, the status of any ongoing calls, and a list of any
@@ -801,40 +801,30 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.READ_PHONE_STATE"
- android:permissionGroup="android.permission-group.PHONE"
- android:label="@string/permlab_readPhoneState"
- android:description="@string/permdesc_readPhoneState"
- android:protectionLevel="dangerous" />
+ android:permissionGroup="android.permission-group.PHONE"
+ android:label="@string/permlab_readPhoneState"
+ android:description="@string/permdesc_readPhoneState"
+ android:protectionLevel="dangerous" />
<!-- Allows read access to the device's phone number(s). This is a subset of the capabilities
granted by {@link #READ_PHONE_STATE} but is exposed to ephemeral applications.
<p>Protection level: dangerous-->
<permission android:name="android.permission.READ_PHONE_NUMBERS"
- android:permissionGroup="android.permission-group.PHONE"
- android:label="@string/permlab_readPhoneNumbers"
- android:description="@string/permdesc_readPhoneNumbers"
- android:protectionLevel="dangerous|ephemeral" />
+ android:permissionGroup="android.permission-group.PHONE"
+ android:label="@string/permlab_readPhoneNumbers"
+ android:description="@string/permdesc_readPhoneNumbers"
+ android:protectionLevel="dangerous|ephemeral" />
<!-- Allows an application to initiate a phone call without going through
the Dialer user interface for the user to confirm the call.
<p>Protection level: dangerous
-->
<permission android:name="android.permission.CALL_PHONE"
- android:permissionGroup="android.permission-group.PHONE"
- android:permissionFlags="costsMoney"
- android:label="@string/permlab_callPhone"
- android:description="@string/permdesc_callPhone"
- android:protectionLevel="dangerous" />
-
- <!-- Allows an application to manage its own calls, but rely on the system to route focus to the
- currently active call.
- <p>Protection level: dangerous
- -->
- <permission android:name="android.permission.MANAGE_OWN_CALLS"
- android:permissionGroup="android.permission-group.PHONE"
- android:label="@string/permlab_manageOwnCalls"
- android:description="@string/permdesc_manageOwnCalls"
- android:protectionLevel="dangerous" />
+ android:permissionGroup="android.permission-group.PHONE"
+ android:permissionFlags="costsMoney"
+ android:label="@string/permlab_callPhone"
+ android:description="@string/permdesc_callPhone"
+ android:protectionLevel="dangerous" />
<!-- Allows an application to access the IMS call service: making and
modifying a call
@@ -842,10 +832,10 @@
@hide
-->
<permission android:name="android.permission.ACCESS_IMS_CALL_SERVICE"
- android:permissionGroup="android.permission-group.PHONE"
- android:label="@string/permlab_accessImsCallService"
- android:description="@string/permdesc_accessImsCallService"
- android:protectionLevel="signature|privileged" />
+ android:permissionGroup="android.permission-group.PHONE"
+ android:label="@string/permlab_accessImsCallService"
+ android:description="@string/permdesc_accessImsCallService"
+ android:protectionLevel="signature|privileged" />
<!-- Allows an application to read the user's call log.
<p class="note"><strong>Note:</strong> If your app uses the
@@ -860,10 +850,10 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.READ_CALL_LOG"
- android:permissionGroup="android.permission-group.PHONE"
- android:label="@string/permlab_readCallLog"
- android:description="@string/permdesc_readCallLog"
- android:protectionLevel="dangerous" />
+ android:permissionGroup="android.permission-group.PHONE"
+ android:label="@string/permlab_readCallLog"
+ android:description="@string/permdesc_readCallLog"
+ android:protectionLevel="dangerous" />
<!-- Allows an application to write (but not read) the user's
call log data.
@@ -879,28 +869,28 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.WRITE_CALL_LOG"
- android:permissionGroup="android.permission-group.PHONE"
- android:label="@string/permlab_writeCallLog"
- android:description="@string/permdesc_writeCallLog"
- android:protectionLevel="dangerous" />
+ android:permissionGroup="android.permission-group.PHONE"
+ android:label="@string/permlab_writeCallLog"
+ android:description="@string/permdesc_writeCallLog"
+ android:protectionLevel="dangerous" />
<!-- Allows an application to add voicemails into the system.
<p>Protection level: dangerous
-->
<permission android:name="com.android.voicemail.permission.ADD_VOICEMAIL"
- android:permissionGroup="android.permission-group.PHONE"
- android:label="@string/permlab_addVoicemail"
- android:description="@string/permdesc_addVoicemail"
- android:protectionLevel="dangerous" />
+ android:permissionGroup="android.permission-group.PHONE"
+ android:label="@string/permlab_addVoicemail"
+ android:description="@string/permdesc_addVoicemail"
+ android:protectionLevel="dangerous" />
<!-- Allows an application to use SIP service.
<p>Protection level: dangerous
-->
<permission android:name="android.permission.USE_SIP"
- android:permissionGroup="android.permission-group.PHONE"
- android:description="@string/permdesc_use_sip"
- android:label="@string/permlab_use_sip"
- android:protectionLevel="dangerous"/>
+ android:permissionGroup="android.permission-group.PHONE"
+ android:description="@string/permdesc_use_sip"
+ android:label="@string/permlab_use_sip"
+ android:protectionLevel="dangerous"/>
<!-- Allows an application to see the number being dialed during an outgoing
call with the option to redirect the call to a different number or
@@ -908,21 +898,31 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.PROCESS_OUTGOING_CALLS"
- android:permissionGroup="android.permission-group.PHONE"
- android:label="@string/permlab_processOutgoingCalls"
- android:description="@string/permdesc_processOutgoingCalls"
- android:protectionLevel="dangerous" />
+ android:permissionGroup="android.permission-group.PHONE"
+ android:label="@string/permlab_processOutgoingCalls"
+ android:description="@string/permdesc_processOutgoingCalls"
+ android:protectionLevel="dangerous" />
<!-- Allows the app to answer an incoming phone call.
<p>Protection level: dangerous
-->
<permission android:name="android.permission.ANSWER_PHONE_CALLS"
- android:permissionGroup="android.permission-group.PHONE"
- android:label="@string/permlab_answerPhoneCalls"
- android:description="@string/permdesc_answerPhoneCalls"
- android:protectionLevel="dangerous|runtime" />
+ android:permissionGroup="android.permission-group.PHONE"
+ android:label="@string/permlab_answerPhoneCalls"
+ android:description="@string/permdesc_answerPhoneCalls"
+ android:protectionLevel="dangerous|runtime" />
+ <!-- Allows a calling application which manages it own calls through the self-managed
+ {@link android.telecom.ConnectionService} APIs. See
+ {@link android.telecom.PhoneAccount#CAPABILITY_SELF_MANAGED for more information on the
+ self-managed ConnectionService APIs.
+ <p>Protection level: normal
+ -->
+ <permission android:name="android.permission.MANAGE_OWN_CALLS"
+ android:label="@string/permlab_manageOwnCalls"
+ android:description="@string/permdesc_manageOwnCalls"
+ android:protectionLevel="normal" />
<!-- ====================================================================== -->
<!-- Permissions for accessing the device microphone -->
@@ -933,19 +933,19 @@
microphone audio from the device. Note that phone calls also capture audio
but are in a separate (more visible) permission group. -->
<permission-group android:name="android.permission-group.MICROPHONE"
- android:icon="@drawable/perm_group_microphone"
- android:label="@string/permgrouplab_microphone"
- android:description="@string/permgroupdesc_microphone"
- android:priority="600" />
+ android:icon="@drawable/perm_group_microphone"
+ android:label="@string/permgrouplab_microphone"
+ android:description="@string/permgroupdesc_microphone"
+ android:priority="600" />
<!-- Allows an application to record audio.
<p>Protection level: dangerous
-->
<permission android:name="android.permission.RECORD_AUDIO"
- android:permissionGroup="android.permission-group.MICROPHONE"
- android:label="@string/permlab_recordAudio"
- android:description="@string/permdesc_recordAudio"
- android:protectionLevel="dangerous"/>
+ android:permissionGroup="android.permission-group.MICROPHONE"
+ android:label="@string/permlab_recordAudio"
+ android:description="@string/permdesc_recordAudio"
+ android:protectionLevel="dangerous"/>
<!-- ====================================================================== -->
<!-- Permissions for accessing the UCE Service -->
@@ -955,15 +955,15 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.ACCESS_UCE_PRESENCE_SERVICE"
- android:permissionGroup="android.permission-group.PHONE"
- android:protectionLevel="signatureOrSystem"/>
+ android:permissionGroup="android.permission-group.PHONE"
+ android:protectionLevel="signatureOrSystem"/>
<!-- @hide Allows an application to Access UCE-OPTIONS.
<p>Protection level: dangerous
-->
<permission android:name="android.permission.ACCESS_UCE_OPTIONS_SERVICE"
- android:permissionGroup="android.permission-group.PHONE"
- android:protectionLevel="signatureOrSystem"/>
+ android:permissionGroup="android.permission-group.PHONE"
+ android:protectionLevel="signatureOrSystem"/>
@@ -975,10 +975,10 @@
<!-- Used for permissions that are associated with accessing
camera or capturing images/video from the device. -->
<permission-group android:name="android.permission-group.CAMERA"
- android:icon="@drawable/perm_group_camera"
- android:label="@string/permgrouplab_camera"
- android:description="@string/permgroupdesc_camera"
- android:priority="700" />
+ android:icon="@drawable/perm_group_camera"
+ android:label="@string/permgrouplab_camera"
+ android:description="@string/permgroupdesc_camera"
+ android:priority="700" />
<!-- Required to be able to access the camera device.
<p>This will automatically enforce the <a
@@ -990,10 +990,10 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.CAMERA"
- android:permissionGroup="android.permission-group.CAMERA"
- android:label="@string/permlab_camera"
- android:description="@string/permdesc_camera"
- android:protectionLevel="dangerous|ephemeral" />
+ android:permissionGroup="android.permission-group.CAMERA"
+ android:label="@string/permlab_camera"
+ android:description="@string/permdesc_camera"
+ android:protectionLevel="dangerous|ephemeral" />
<!-- ====================================================================== -->
@@ -1004,28 +1004,28 @@
<!-- Used for permissions that are associated with accessing
camera or capturing images/video from the device. -->
<permission-group android:name="android.permission-group.SENSORS"
- android:icon="@drawable/perm_group_sensors"
- android:label="@string/permgrouplab_sensors"
- android:description="@string/permgroupdesc_sensors"
- android:priority="800" />
+ android:icon="@drawable/perm_group_sensors"
+ android:label="@string/permgrouplab_sensors"
+ android:description="@string/permgroupdesc_sensors"
+ android:priority="800" />
<!-- Allows an application to access data from sensors that the user uses to
measure what is happening inside his/her body, such as heart rate.
<p>Protection level: dangerous -->
<permission android:name="android.permission.BODY_SENSORS"
- android:permissionGroup="android.permission-group.SENSORS"
- android:label="@string/permlab_bodySensors"
- android:description="@string/permdesc_bodySensors"
- android:protectionLevel="dangerous" />
+ android:permissionGroup="android.permission-group.SENSORS"
+ android:label="@string/permlab_bodySensors"
+ android:description="@string/permdesc_bodySensors"
+ android:protectionLevel="dangerous" />
<!-- Allows an app to use fingerprint hardware.
<p>Protection level: normal
-->
<permission android:name="android.permission.USE_FINGERPRINT"
- android:permissionGroup="android.permission-group.SENSORS"
- android:label="@string/permlab_useFingerprint"
- android:description="@string/permdesc_useFingerprint"
- android:protectionLevel="normal" />
+ android:permissionGroup="android.permission-group.SENSORS"
+ android:label="@string/permlab_useFingerprint"
+ android:description="@string/permdesc_useFingerprint"
+ android:protectionLevel="normal" />
<!-- ====================================================================== -->
<!-- REMOVED PERMISSIONS -->
@@ -1033,78 +1033,78 @@
<!-- @hide We need to keep this around for backwards compatibility -->
<permission android:name="android.permission.READ_PROFILE"
- android:protectionLevel="normal"
- android:permissionFlags="removed"/>
+ android:protectionLevel="normal"
+ android:permissionFlags="removed"/>
<!-- @hide We need to keep this around for backwards compatibility -->
<permission android:name="android.permission.WRITE_PROFILE"
- android:protectionLevel="normal"
- android:permissionFlags="removed"/>
+ android:protectionLevel="normal"
+ android:permissionFlags="removed"/>
<!-- @hide We need to keep this around for backwards compatibility -->
<permission android:name="android.permission.READ_SOCIAL_STREAM"
- android:protectionLevel="normal"
- android:permissionFlags="removed"/>
+ android:protectionLevel="normal"
+ android:permissionFlags="removed"/>
<!-- @hide We need to keep this around for backwards compatibility -->
<permission android:name="android.permission.WRITE_SOCIAL_STREAM"
- android:protectionLevel="normal"
- android:permissionFlags="removed"/>
+ android:protectionLevel="normal"
+ android:permissionFlags="removed"/>
<!-- @hide We need to keep this around for backwards compatibility -->
<permission android:name="android.permission.READ_USER_DICTIONARY"
- android:protectionLevel="normal"
- android:permissionFlags="removed"/>
+ android:protectionLevel="normal"
+ android:permissionFlags="removed"/>
<!-- @hide We need to keep this around for backwards compatibility -->
<permission android:name="android.permission.WRITE_USER_DICTIONARY"
- android:protectionLevel="normal"
- android:permissionFlags="removed"/>
+ android:protectionLevel="normal"
+ android:permissionFlags="removed"/>
<!-- @hide We need to keep this around for backwards compatibility -->
<permission android:name="android.permission.WRITE_SMS"
- android:protectionLevel="normal"
- android:permissionFlags="removed"/>
+ android:protectionLevel="normal"
+ android:permissionFlags="removed"/>
<!-- @hide We need to keep this around for backwards compatibility -->
<permission android:name="com.android.browser.permission.READ_HISTORY_BOOKMARKS"
- android:protectionLevel="normal"
- android:permissionFlags="removed"/>
+ android:protectionLevel="normal"
+ android:permissionFlags="removed"/>
<!-- @hide We need to keep this around for backwards compatibility -->
<permission android:name="com.android.browser.permission.WRITE_HISTORY_BOOKMARKS"
- android:protectionLevel="normal"
- android:permissionFlags="removed"/>
+ android:protectionLevel="normal"
+ android:permissionFlags="removed"/>
<!-- @hide We need to keep this around for backwards compatibility -->
<permission android:name="android.permission.AUTHENTICATE_ACCOUNTS"
- android:protectionLevel="normal"
- android:permissionFlags="removed"/>
+ android:protectionLevel="normal"
+ android:permissionFlags="removed"/>
<!-- @hide We need to keep this around for backwards compatibility -->
<permission android:name="android.permission.MANAGE_ACCOUNTS"
- android:protectionLevel="normal"
- android:permissionFlags="removed"/>
+ android:protectionLevel="normal"
+ android:permissionFlags="removed"/>
<!-- @hide We need to keep this around for backwards compatibility -->
<permission android:name="android.permission.USE_CREDENTIALS"
- android:protectionLevel="normal"
- android:permissionFlags="removed"/>
+ android:protectionLevel="normal"
+ android:permissionFlags="removed"/>
<!-- @hide We need to keep this around for backwards compatibility -->
<permission android:name="android.permission.SUBSCRIBED_FEEDS_READ"
- android:protectionLevel="normal"
- android:permissionFlags="removed"/>
+ android:protectionLevel="normal"
+ android:permissionFlags="removed"/>
<!-- @hide We need to keep this around for backwards compatibility -->
<permission android:name="android.permission.SUBSCRIBED_FEEDS_WRITE"
- android:protectionLevel="normal"
- android:permissionFlags="removed"/>
+ android:protectionLevel="normal"
+ android:permissionFlags="removed"/>
<!-- @hide We need to keep this around for backwards compatibility -->
<permission android:name="android.permission.FLASHLIGHT"
- android:protectionLevel="normal"
- android:permissionFlags="removed"/>
+ android:protectionLevel="normal"
+ android:permissionFlags="removed"/>
<!-- ====================================================================== -->
<!-- INSTALL PERMISSIONS -->
@@ -1119,37 +1119,37 @@
to handle the respond-via-message action during incoming calls.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.SEND_RESPOND_VIA_MESSAGE"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows an application to send SMS to premium shortcodes without user permission.
<p>Not for use by third-party applications.
@hide -->
<permission android:name="android.permission.SEND_SMS_NO_CONFIRMATION"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Allows an application to filter carrier specific sms.
@hide -->
<permission android:name="android.permission.CARRIER_FILTER_SMS"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows an application to receive emergency cell broadcast messages,
to record or display them to the user.
<p>Not for use by third-party applications.
@hide -->
<permission android:name="android.permission.RECEIVE_EMERGENCY_BROADCAST"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Allows an application to monitor incoming Bluetooth MAP messages, to record
or perform processing on them. -->
<!-- @hide -->
<permission android:name="android.permission.RECEIVE_BLUETOOTH_MAP"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi @hide Allows an application to execute contacts directory search.
This should only be used by ContactsProvider.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.BIND_DIRECTORY_SEARCH"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi @hide Allows an application to modify cell broadcasts through the content provider.
<p>Not for use by third-party applications. -->
@@ -1165,9 +1165,9 @@
<p>Protection level: normal
-->
<permission android:name="com.android.alarm.permission.SET_ALARM"
- android:label="@string/permlab_setAlarm"
- android:description="@string/permdesc_setAlarm"
- android:protectionLevel="normal" />
+ android:label="@string/permlab_setAlarm"
+ android:description="@string/permdesc_setAlarm"
+ android:protectionLevel="normal" />
<!-- =============================================================== -->
<!-- Permissions for accessing the user voicemail -->
@@ -1178,13 +1178,13 @@
<p>Protection level: signature|privileged
-->
<permission android:name="com.android.voicemail.permission.WRITE_VOICEMAIL"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Allows an application to read voicemails in the system.
<p>Protection level: signature|privileged
-->
<permission android:name="com.android.voicemail.permission.READ_VOICEMAIL"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- ======================================= -->
<!-- Permissions for accessing location info -->
@@ -1195,26 +1195,26 @@
<p>Protection level: normal
-->
<permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS"
- android:label="@string/permlab_accessLocationExtraCommands"
- android:description="@string/permdesc_accessLocationExtraCommands"
- android:protectionLevel="normal" />
+ android:label="@string/permlab_accessLocationExtraCommands"
+ android:description="@string/permdesc_accessLocationExtraCommands"
+ android:protectionLevel="normal" />
<!-- @SystemApi Allows an application to install a location provider into the Location Manager.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.INSTALL_LOCATION_PROVIDER"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi @hide Allows HDMI-CEC service to access device and configuration files.
This should only be used by HDMI-CEC service.
-->
<permission android:name="android.permission.HDMI_CEC"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows an application to use location features in hardware,
such as the geofencing api.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.LOCATION_HARDWARE"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<uses-permission android:name="android.permission.LOCATION_HARDWARE"/>
<!-- @SystemApi Allows an application to create mock location providers for testing.
@@ -1222,7 +1222,7 @@
@hide
-->
<permission android:name="android.permission.ACCESS_MOCK_LOCATION"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- ======================================= -->
<!-- Permissions for accessing networks -->
@@ -1233,79 +1233,79 @@
<p>Protection level: normal
-->
<permission android:name="android.permission.INTERNET"
- android:description="@string/permdesc_createNetworkSockets"
- android:label="@string/permlab_createNetworkSockets"
- android:protectionLevel="normal|ephemeral" />
+ android:description="@string/permdesc_createNetworkSockets"
+ android:label="@string/permlab_createNetworkSockets"
+ android:protectionLevel="normal|ephemeral" />
<!-- Allows applications to access information about networks.
<p>Protection level: normal
-->
<permission android:name="android.permission.ACCESS_NETWORK_STATE"
- android:description="@string/permdesc_accessNetworkState"
- android:label="@string/permlab_accessNetworkState"
- android:protectionLevel="normal|ephemeral" />
+ android:description="@string/permdesc_accessNetworkState"
+ android:label="@string/permlab_accessNetworkState"
+ android:protectionLevel="normal|ephemeral" />
<!-- Allows applications to access information about Wi-Fi networks.
<p>Protection level: normal
-->
<permission android:name="android.permission.ACCESS_WIFI_STATE"
- android:description="@string/permdesc_accessWifiState"
- android:label="@string/permlab_accessWifiState"
- android:protectionLevel="normal" />
+ android:description="@string/permdesc_accessWifiState"
+ android:label="@string/permlab_accessWifiState"
+ android:protectionLevel="normal" />
<!-- Allows applications to change Wi-Fi connectivity state.
<p>Protection level: normal
-->
<permission android:name="android.permission.CHANGE_WIFI_STATE"
- android:description="@string/permdesc_changeWifiState"
- android:label="@string/permlab_changeWifiState"
- android:protectionLevel="normal" />
+ android:description="@string/permdesc_changeWifiState"
+ android:label="@string/permlab_changeWifiState"
+ android:protectionLevel="normal" />
<!-- @SystemApi @hide Allows applications to read Wi-Fi credential.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.READ_WIFI_CREDENTIAL"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi @hide Allows applications to change tether state and run
tether carrier provisioning.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.TETHER_PRIVILEGED"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi @hide Allow system apps to receive broadcast
when a wifi network credential is changed.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.RECEIVE_WIFI_CREDENTIAL_CHANGE"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi @hide Allows an application to modify any wifi configuration, even if created
by another application. Once reconfigured the original creator cannot make any further
modifications.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.OVERRIDE_WIFI_CONFIG"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @hide -->
<permission android:name="android.permission.ACCESS_WIMAX_STATE"
- android:description="@string/permdesc_accessWimaxState"
- android:label="@string/permlab_accessWimaxState"
- android:protectionLevel="normal" />
+ android:description="@string/permdesc_accessWimaxState"
+ android:label="@string/permlab_accessWimaxState"
+ android:protectionLevel="normal" />
<!-- @hide -->
<permission android:name="android.permission.CHANGE_WIMAX_STATE"
- android:description="@string/permdesc_changeWimaxState"
- android:label="@string/permlab_changeWimaxState"
- android:protectionLevel="normal" />
+ android:description="@string/permdesc_changeWimaxState"
+ android:label="@string/permlab_changeWimaxState"
+ android:protectionLevel="normal" />
<!-- Allows applications to act as network scorers. @hide @SystemApi-->
<permission android:name="android.permission.SCORE_NETWORKS"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Allows applications to request network
recommendations and scores from the NetworkScoreService.
<p>Not for use by third-party applications. @hide -->
<permission android:name="android.permission.REQUEST_NETWORK_SCORES"
- android:protectionLevel="signature|setup" />
+ android:protectionLevel="signature|setup" />
<!-- Allows network stack services (Connectivity and Wifi) to coordinate
<p>Not for use by third-party or privileged applications.
@@ -1323,73 +1323,73 @@
<p>Protection level: normal
-->
<permission android:name="android.permission.BLUETOOTH"
- android:description="@string/permdesc_bluetooth"
- android:label="@string/permlab_bluetooth"
- android:protectionLevel="normal" />
+ android:description="@string/permdesc_bluetooth"
+ android:label="@string/permlab_bluetooth"
+ android:protectionLevel="normal" />
<!-- Allows applications to discover and pair bluetooth devices.
<p>Protection level: normal
-->
<permission android:name="android.permission.BLUETOOTH_ADMIN"
- android:description="@string/permdesc_bluetoothAdmin"
- android:label="@string/permlab_bluetoothAdmin"
- android:protectionLevel="normal" />
+ android:description="@string/permdesc_bluetoothAdmin"
+ android:label="@string/permlab_bluetoothAdmin"
+ android:protectionLevel="normal" />
<!-- @SystemApi Allows applications to pair bluetooth devices without user interaction, and to
allow or disallow phonebook access or message access.
This is not available to third party applications. -->
<permission android:name="android.permission.BLUETOOTH_PRIVILEGED"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Control access to email providers exclusively for Bluetooth
@hide
-->
<permission android:name="android.permission.BLUETOOTH_MAP"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Allows bluetooth stack to access files
@hide This should only be used by Bluetooth apk.
-->
<permission android:name="android.permission.BLUETOOTH_STACK"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Allows applications to perform I/O operations over NFC.
<p>Protection level: normal
-->
<permission android:name="android.permission.NFC"
- android:description="@string/permdesc_nfc"
- android:label="@string/permlab_nfc"
- android:protectionLevel="normal" />
+ android:description="@string/permdesc_nfc"
+ android:label="@string/permlab_nfc"
+ android:protectionLevel="normal" />
<!-- @SystemApi Allows an internal user to use privileged ConnectivityManager APIs.
@hide -->
<permission android:name="android.permission.CONNECTIVITY_INTERNAL"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows an internal user to use restricted Networks.
@hide -->
<permission android:name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Allows a system application to access hardware packet offload capabilities.
@hide -->
<permission android:name="android.permission.PACKET_KEEPALIVE_OFFLOAD"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi
@hide -->
<permission android:name="android.permission.RECEIVE_DATA_ACTIVITY_CHANGE"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows access to the loop radio (Android@Home mesh network) device.
@hide -->
<permission android:name="android.permission.LOOP_RADIO"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Allows sending and receiving handover transfer status from Wifi and Bluetooth
@hide -->
<permission android:name="android.permission.NFC_HANDOVER_STATUS"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- ================================== -->
<!-- Permissions for accessing accounts -->
@@ -1408,16 +1408,16 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.GET_ACCOUNTS"
- android:permissionGroup="android.permission-group.CONTACTS"
- android:protectionLevel="dangerous"
- android:description="@string/permdesc_getAccounts"
- android:label="@string/permlab_getAccounts" />
+ android:permissionGroup="android.permission-group.CONTACTS"
+ android:protectionLevel="dangerous"
+ android:description="@string/permdesc_getAccounts"
+ android:label="@string/permlab_getAccounts" />
<uses-permission android:name="android.permission.GET_ACCOUNTS"/>
<!-- @SystemApi Allows applications to call into AccountAuthenticators.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.ACCOUNT_MANAGER"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- ================================== -->
<!-- Permissions for accessing hardware that may effect battery life-->
@@ -1428,34 +1428,34 @@
<p>Protection level: normal
-->
<permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE"
- android:description="@string/permdesc_changeWifiMulticastState"
- android:label="@string/permlab_changeWifiMulticastState"
- android:protectionLevel="normal" />
+ android:description="@string/permdesc_changeWifiMulticastState"
+ android:label="@string/permlab_changeWifiMulticastState"
+ android:protectionLevel="normal" />
<!-- Allows access to the vibrator.
<p>Protection level: normal
-->
<permission android:name="android.permission.VIBRATE"
- android:label="@string/permlab_vibrate"
- android:description="@string/permdesc_vibrate"
- android:protectionLevel="normal|ephemeral" />
+ android:label="@string/permlab_vibrate"
+ android:description="@string/permdesc_vibrate"
+ android:protectionLevel="normal|ephemeral" />
<!-- Allows using PowerManager WakeLocks to keep processor from sleeping or screen
from dimming.
<p>Protection level: normal
-->
<permission android:name="android.permission.WAKE_LOCK"
- android:label="@string/permlab_wakeLock"
- android:description="@string/permdesc_wakeLock"
- android:protectionLevel="normal|ephemeral" />
+ android:label="@string/permlab_wakeLock"
+ android:description="@string/permdesc_wakeLock"
+ android:protectionLevel="normal|ephemeral" />
<!-- Allows using the device's IR transmitter, if available.
<p>Protection level: normal
-->
<permission android:name="android.permission.TRANSMIT_IR"
- android:label="@string/permlab_transmitIr"
- android:description="@string/permdesc_transmitIr"
- android:protectionLevel="normal" />
+ android:label="@string/permlab_transmitIr"
+ android:description="@string/permdesc_transmitIr"
+ android:protectionLevel="normal" />
<!-- ==================================================== -->
<!-- Permissions related to changing audio settings -->
@@ -1466,9 +1466,9 @@
<p>Protection level: normal
-->
<permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"
- android:label="@string/permlab_modifyAudioSettings"
- android:description="@string/permdesc_modifyAudioSettings"
- android:protectionLevel="normal" />
+ android:label="@string/permlab_modifyAudioSettings"
+ android:description="@string/permdesc_modifyAudioSettings"
+ android:protectionLevel="normal" />
<!-- ================================== -->
<!-- Permissions for accessing hardware -->
@@ -1478,83 +1478,83 @@
<!-- @SystemApi Allows an application to manage preferences and permissions for USB devices
@hide -->
<permission android:name="android.permission.MANAGE_USB"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows an application to access the MTP USB kernel driver.
For use only by the device side MTP implementation.
@hide -->
<permission android:name="android.permission.ACCESS_MTP"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows access to hardware peripherals. Intended only for hardware testing.
<p>Not for use by third-party applications.
@hide
-->
<permission android:name="android.permission.HARDWARE_TEST"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @SystemApi Allows access to FM
@hide This is not a third-party API (intended for system apps).-->
<permission android:name="android.permission.ACCESS_FM_RADIO"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Allows access to configure network interfaces, configure/use IPSec, etc.
@hide -->
<permission android:name="android.permission.NET_ADMIN"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Allows registration for remote audio playback. @hide -->
<permission android:name="android.permission.REMOTE_AUDIO_PLAYBACK"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @SystemApi Allows TvInputService to access underlying TV input hardware such as
built-in tuners and HDMI-in's.
@hide This should only be used by OEM's TvInputService's.
-->
<permission android:name="android.permission.TV_INPUT_HARDWARE"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows to capture a frame of TV input hardware such as
built-in tuners and HDMI-in's.
@hide <p>Not for use by third-party applications.
-->
<permission android:name="android.permission.CAPTURE_TV_INPUT"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @hide Allows TvInputService to access DVB device.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.DVB_DEVICE"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows reading and enabling/disabling the OEM unlock allowed by carrier state
@hide <p>Not for use by third-party applications. -->
<permission android:name="android.permission.MANAGE_CARRIER_OEM_UNLOCK_STATE"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows reading and enabling/disabling the OEM unlock allowed by user state
@hide <p>Not for use by third-party applications. -->
<permission android:name="android.permission.MANAGE_USER_OEM_UNLOCK_STATE"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows reading the OEM unlock state
@hide <p>Not for use by third-party applications. -->
<permission android:name="android.permission.READ_OEM_UNLOCK_STATE"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @hide Allows enabling/disabling OEM unlock
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.OEM_UNLOCK_STATE"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @hide Allows querying state of PersistentDataBlock
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.ACCESS_PDB_STATE"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @hide Allows system update service to notify device owner about pending updates.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.NOTIFY_PENDING_SYSTEM_UPDATE"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- =========================================== -->
<!-- Permissions associated with camera and image capture -->
@@ -1565,12 +1565,12 @@
a camera is in use by an application.
@hide -->
<permission android:name="android.permission.CAMERA_DISABLE_TRANSMIT_LED"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Allows sending the camera service notifications about system-wide events.
@hide -->
<permission android:name="android.permission.CAMERA_SEND_SYSTEM_EVENTS"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- =========================================== -->
<!-- Permissions associated with telephony state -->
@@ -1581,58 +1581,58 @@
Does not include placing calls.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.MODIFY_PHONE_STATE"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Allows read only access to precise phone state.
@hide Pending API council approval -->
<permission android:name="android.permission.READ_PRECISE_PHONE_STATE"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows read access to privileged phone state.
@hide Used internally. -->
<permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Protects the ability to register any PhoneAccount with
PhoneAccount#CAPABILITY_SIM_SUBSCRIPTION. This capability indicates that the PhoneAccount
corresponds to a device SIM.
@hide -->
<permission android:name="android.permission.REGISTER_SIM_SUBSCRIPTION"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Protects the ability to register any PhoneAccount with
PhoneAccount#CAPABILITY_CALL_PROVIDER.
@hide -->
<permission android:name="android.permission.REGISTER_CALL_PROVIDER"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Protects the ability to register any PhoneAccount with
PhoneAccount#CAPABILITY_CONNECTION_MANAGER
@hide -->
<permission android:name="android.permission.REGISTER_CONNECTION_MANAGER"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Must be required by a {@link android.telecom.InCallService},
to ensure that only the system can bind to it.
<p>Protection level: signature|privileged
-->
<permission android:name="android.permission.BIND_INCALL_SERVICE"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Must be required by a link {@link android.telephony.VisualVoicemailService} to ensure that
only the system can bind to it.
<p>Protection level: signature|privileged
-->
<permission
- android:name="android.permission.BIND_VISUAL_VOICEMAIL_SERVICE"
- android:protectionLevel="signature|privileged"/>
+ android:name="android.permission.BIND_VISUAL_VOICEMAIL_SERVICE"
+ android:protectionLevel="signature|privileged"/>
<!-- Must be required by a {@link android.telecom.CallScreeningService},
to ensure that only the system can bind to it.
<p>Protection level: signature|privileged
-->
<permission android:name="android.permission.BIND_SCREENING_SERVICE"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Must be required by a {@link android.telecom.ConnectionService},
to ensure that only the system can bind to it.
@@ -1641,24 +1641,24 @@
@SystemApi
@hide -->
<permission android:name="android.permission.BIND_CONNECTION_SERVICE"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Must be required by a {@link android.telecom.ConnectionService},
to ensure that only the system can bind to it.
<p>Protection level: signature|privileged
-->
<permission android:name="android.permission.BIND_TELECOM_CONNECTION_SERVICE"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows an application to control the in-call experience.
@hide -->
<permission android:name="android.permission.CONTROL_INCALL_EXPERIENCE"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Allows an application to receive STK related commands.
@hide -->
<permission android:name="android.permission.RECEIVE_STK_COMMANDS"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Must be required by an ImsService to ensure that only the
system can bind to it.
@@ -1667,7 +1667,7 @@
@hide
-->
<permission android:name="android.permission.BIND_IMS_SERVICE"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- ================================== -->
@@ -1678,7 +1678,7 @@
<!-- @SystemApi Allows an application to write to internal media storage
@hide -->
<permission android:name="android.permission.WRITE_MEDIA_STORAGE"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Allows an application to manage access to documents, usually as part
of a document picker.
@@ -1688,20 +1688,21 @@
<p>Protection level: signature
-->
<permission android:name="android.permission.MANAGE_DOCUMENTS"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @hide Allows an application to cache content.
<p>Not for use by third-party applications.
<p>Protection level: signature
-->
<permission android:name="android.permission.CACHE_CONTENT"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
- <!-- Allows an application to aggressively allocate disk space.
+ <!-- @SystemApi @hide
+ Allows an application to aggressively allocate disk space.
<p>Not for use by third-party applications.
-->
<permission android:name="android.permission.ALLOCATE_AGGRESSIVE"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- ================================== -->
<!-- Permissions for screenlock -->
@@ -1712,9 +1713,9 @@
<p>Protection level: normal
-->
<permission android:name="android.permission.DISABLE_KEYGUARD"
- android:description="@string/permdesc_disableKeyguard"
- android:label="@string/permlab_disableKeyguard"
- android:protectionLevel="normal" />
+ android:description="@string/permdesc_disableKeyguard"
+ android:label="@string/permlab_disableKeyguard"
+ android:protectionLevel="normal" />
<!-- ================================== -->
<!-- Permissions to access other installed applications -->
@@ -1723,9 +1724,9 @@
<!-- @deprecated No longer enforced. -->
<permission android:name="android.permission.GET_TASKS"
- android:label="@string/permlab_getTasks"
- android:description="@string/permdesc_getTasks"
- android:protectionLevel="normal" />
+ android:label="@string/permlab_getTasks"
+ android:description="@string/permdesc_getTasks"
+ android:protectionLevel="normal" />
<!-- New version of GET_TASKS that apps can request, since GET_TASKS doesn't really
give access to task information. We need this new one because there are
@@ -1738,32 +1739,32 @@
@hide
@SystemApi -->
<permission android:name="android.permission.REAL_GET_TASKS"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Allows an application to start a task from a ActivityManager#RecentTaskInfo.
@hide -->
<permission android:name="android.permission.START_TASKS_FROM_RECENTS"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi @hide Allows an application to call APIs that allow it to do interactions
across the users on the device, using singleton services and
user-targeted broadcasts. This permission is not available to
third party applications. -->
<permission android:name="android.permission.INTERACT_ACROSS_USERS"
- android:protectionLevel="signature|privileged|development" />
+ android:protectionLevel="signature|privileged|development" />
<!-- @SystemApi Fuller form of {@link android.Manifest.permission#INTERACT_ACROSS_USERS}
that removes restrictions on where broadcasts can be sent and allows other
types of interactions
@hide -->
<permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL"
- android:protectionLevel="signature|installer" />
+ android:protectionLevel="signature|installer" />
<!-- @SystemApi @hide Allows an application to call APIs that allow it to query and manage
users on the device. This permission is not available to
third party applications. -->
<permission android:name="android.permission.MANAGE_USERS"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @hide Allows an application to create, remove users and get the list of
users on the device. Applications holding this permission can only create restricted,
@@ -1771,69 +1772,69 @@
{@link android.Manifest.permission#MANAGE_USERS} is needed.
This permission is not available to third party applications. -->
<permission android:name="android.permission.CREATE_USERS"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @hide Allows an application to set the profile owners and the device owner.
This permission is not available to third party applications.-->
<permission android:name="android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS"
- android:protectionLevel="signature"
- android:label="@string/permlab_manageProfileAndDeviceOwners"
- android:description="@string/permdesc_manageProfileAndDeviceOwners" />
+ android:protectionLevel="signature"
+ android:label="@string/permlab_manageProfileAndDeviceOwners"
+ android:description="@string/permdesc_manageProfileAndDeviceOwners" />
<!-- Allows an application to get full detailed information about
recently running tasks, with full fidelity to the real state.
@hide -->
<permission android:name="android.permission.GET_DETAILED_TASKS"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Allows an application to change the Z-order of tasks.
<p>Protection level: normal
-->
<permission android:name="android.permission.REORDER_TASKS"
- android:label="@string/permlab_reorderTasks"
- android:description="@string/permdesc_reorderTasks"
- android:protectionLevel="normal" />
+ android:label="@string/permlab_reorderTasks"
+ android:description="@string/permdesc_reorderTasks"
+ android:protectionLevel="normal" />
<!-- @hide Allows an application to change to remove/kill tasks -->
<permission android:name="android.permission.REMOVE_TASKS"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @SystemApi @hide Allows an application to create/manage/remove stacks -->
<permission android:name="android.permission.MANAGE_ACTIVITY_STACKS"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Allows an application to start any activity, regardless of permission
protection or exported state.
@hide -->
<permission android:name="android.permission.START_ANY_ACTIVITY"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @deprecated The {@link android.app.ActivityManager#restartPackage}
API is no longer supported. -->
<permission android:name="android.permission.RESTART_PACKAGES"
- android:label="@string/permlab_killBackgroundProcesses"
- android:description="@string/permdesc_killBackgroundProcesses"
- android:protectionLevel="normal" />
+ android:label="@string/permlab_killBackgroundProcesses"
+ android:description="@string/permdesc_killBackgroundProcesses"
+ android:protectionLevel="normal" />
<!-- Allows an application to call
{@link android.app.ActivityManager#killBackgroundProcesses}.
<p>Protection level: normal
-->
<permission android:name="android.permission.KILL_BACKGROUND_PROCESSES"
- android:label="@string/permlab_killBackgroundProcesses"
- android:description="@string/permdesc_killBackgroundProcesses"
- android:protectionLevel="normal" />
+ android:label="@string/permlab_killBackgroundProcesses"
+ android:description="@string/permdesc_killBackgroundProcesses"
+ android:protectionLevel="normal" />
<!-- @SystemApi @hide Allows an application to query process states and current
OOM adjustment scores.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.GET_PROCESS_STATE_AND_OOM_SCORE"
- android:protectionLevel="signature|privileged|development" />
+ android:protectionLevel="signature|privileged|development" />
<!-- Allows use of PendingIntent.getIntent().
@hide -->
<permission android:name="android.permission.GET_INTENT_SENDER_INTENT"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- ================================== -->
<!-- Permissions affecting the display of other applications -->
@@ -1856,25 +1857,43 @@
Settings.canDrawOverlays()}.
<p>Protection level: signature -->
<permission android:name="android.permission.SYSTEM_ALERT_WINDOW"
- android:label="@string/permlab_systemAlertWindow"
- android:description="@string/permdesc_systemAlertWindow"
- android:protectionLevel="signature|preinstalled|appop|pre23|development" />
+ android:label="@string/permlab_systemAlertWindow"
+ android:description="@string/permdesc_systemAlertWindow"
+ android:protectionLevel="signature|preinstalled|appop|pre23|development" />
- <!-- Allows an app to run in the background.
- <p>Protection level: signature
+ <!-- @deprecated Use {@link android.Manifest.permission#REQUEST_COMPANION_RUN_IN_BACKGROUND}
+ @hide
-->
<permission android:name="android.permission.RUN_IN_BACKGROUND"
- android:label="@string/permlab_runInBackground"
- android:description="@string/permdesc_runInBackground"
- android:protectionLevel="signature" />
+ android:label="@string/permlab_runInBackground"
+ android:description="@string/permdesc_runInBackground"
+ android:protectionLevel="signature" />
- <!-- Allows an app to use data in the background.
- <p>Protection level: signature
+ <!-- @deprecated Use
+ {@link android.Manifest.permission#REQUEST_COMPANION_USE_DATA_IN_BACKGROUND}
+ @hide
-->
<permission android:name="android.permission.USE_DATA_IN_BACKGROUND"
- android:label="@string/permlab_useDataInBackground"
- android:description="@string/permdesc_useDataInBackground"
- android:protectionLevel="signature" />
+ android:label="@string/permlab_useDataInBackground"
+ android:description="@string/permdesc_useDataInBackground"
+ android:protectionLevel="signature" />
+
+ <!-- Allows a companion app to run in the background.
+ <p>Protection level: normal
+ -->
+ <permission android:name="android.permission.REQUEST_COMPANION_RUN_IN_BACKGROUND"
+ android:label="@string/permlab_runInBackground"
+ android:description="@string/permdesc_runInBackground"
+ android:protectionLevel="normal" />
+
+ <!-- Allows a companion app to use data in the background.
+ <p>Protection level: normal
+ -->
+ <permission android:name="android.permission.REQUEST_COMPANION_USE_DATA_IN_BACKGROUND"
+ android:label="@string/permlab_useDataInBackground"
+ android:description="@string/permdesc_useDataInBackground"
+ android:protectionLevel="normal" />
+
<!-- ================================== -->
<!-- Permissions affecting the system wallpaper -->
@@ -1885,17 +1904,17 @@
<p>Protection level: normal
-->
<permission android:name="android.permission.SET_WALLPAPER"
- android:label="@string/permlab_setWallpaper"
- android:description="@string/permdesc_setWallpaper"
- android:protectionLevel="normal" />
+ android:label="@string/permlab_setWallpaper"
+ android:description="@string/permdesc_setWallpaper"
+ android:protectionLevel="normal" />
<!-- Allows applications to set the wallpaper hints.
<p>Protection level: normal
-->
<permission android:name="android.permission.SET_WALLPAPER_HINTS"
- android:label="@string/permlab_setWallpaperHints"
- android:description="@string/permdesc_setWallpaperHints"
- android:protectionLevel="normal" />
+ android:label="@string/permlab_setWallpaperHints"
+ android:description="@string/permdesc_setWallpaperHints"
+ android:protectionLevel="normal" />
<!-- ============================================ -->
<!-- Permissions for changing the system clock -->
@@ -1905,15 +1924,15 @@
<!-- @SystemApi Allows applications to set the system time.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.SET_TIME"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Allows applications to set the system time zone.
<p>Not for use by third-party applications.
-->
<permission android:name="android.permission.SET_TIME_ZONE"
- android:label="@string/permlab_setTimeZone"
- android:description="@string/permdesc_setTimeZone"
- android:protectionLevel="signature|privileged" />
+ android:label="@string/permlab_setTimeZone"
+ android:description="@string/permdesc_setTimeZone"
+ android:protectionLevel="signature|privileged" />
<!-- ==================================================== -->
<!-- Permissions related to changing status bar -->
@@ -1924,9 +1943,9 @@
<p>Protection level: normal
-->
<permission android:name="android.permission.EXPAND_STATUS_BAR"
- android:label="@string/permlab_expandStatusBar"
- android:description="@string/permdesc_expandStatusBar"
- android:protectionLevel="normal" />
+ android:label="@string/permlab_expandStatusBar"
+ android:description="@string/permdesc_expandStatusBar"
+ android:protectionLevel="normal" />
<!-- ============================================================== -->
<!-- Permissions related to adding/removing shortcuts from Launcher -->
@@ -1937,16 +1956,16 @@
<p>Protection level: normal
-->
<permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT"
- android:label="@string/permlab_install_shortcut"
- android:description="@string/permdesc_install_shortcut"
- android:protectionLevel="normal"/>
+ android:label="@string/permlab_install_shortcut"
+ android:description="@string/permdesc_install_shortcut"
+ android:protectionLevel="normal"/>
<!--This permission is no longer supported.
-->
<permission android:name="com.android.launcher.permission.UNINSTALL_SHORTCUT"
- android:label="@string/permlab_uninstall_shortcut"
- android:description="@string/permdesc_uninstall_shortcut"
- android:protectionLevel="normal"/>
+ android:label="@string/permlab_uninstall_shortcut"
+ android:description="@string/permdesc_uninstall_shortcut"
+ android:protectionLevel="normal"/>
<!-- ==================================================== -->
<!-- Permissions related to accessing sync settings -->
@@ -1957,25 +1976,25 @@
<p>Protection level: normal
-->
<permission android:name="android.permission.READ_SYNC_SETTINGS"
- android:description="@string/permdesc_readSyncSettings"
- android:label="@string/permlab_readSyncSettings"
- android:protectionLevel="normal" />
+ android:description="@string/permdesc_readSyncSettings"
+ android:label="@string/permlab_readSyncSettings"
+ android:protectionLevel="normal" />
<!-- Allows applications to write the sync settings.
<p>Protection level: normal
-->
<permission android:name="android.permission.WRITE_SYNC_SETTINGS"
- android:description="@string/permdesc_writeSyncSettings"
- android:label="@string/permlab_writeSyncSettings"
- android:protectionLevel="normal" />
+ android:description="@string/permdesc_writeSyncSettings"
+ android:label="@string/permlab_writeSyncSettings"
+ android:protectionLevel="normal" />
<!-- Allows applications to read the sync stats.
<p>Protection level: normal
-->
<permission android:name="android.permission.READ_SYNC_STATS"
- android:description="@string/permdesc_readSyncStats"
- android:label="@string/permlab_readSyncStats"
- android:protectionLevel="normal" />
+ android:description="@string/permdesc_readSyncStats"
+ android:label="@string/permlab_readSyncStats"
+ android:protectionLevel="normal" />
<!-- ============================================ -->
<!-- Permissions for low-level system interaction -->
@@ -1984,12 +2003,12 @@
<!-- @SystemApi @hide Change the screen compatibility mode of applications -->
<permission android:name="android.permission.SET_SCREEN_COMPATIBILITY"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @SystemApi Allows an application to modify the current configuration, such
as locale. -->
<permission android:name="android.permission.CHANGE_CONFIGURATION"
- android:protectionLevel="signature|privileged|development" />
+ android:protectionLevel="signature|privileged|development" />
<!-- Allows an application to read or write the system settings.
@@ -2004,51 +2023,51 @@
<p>Protection level: signature
-->
<permission android:name="android.permission.WRITE_SETTINGS"
- android:label="@string/permlab_writeSettings"
- android:description="@string/permdesc_writeSettings"
- android:protectionLevel="signature|preinstalled|appop|pre23" />
+ android:label="@string/permlab_writeSettings"
+ android:description="@string/permdesc_writeSettings"
+ android:protectionLevel="signature|preinstalled|appop|pre23" />
<!-- @SystemApi Allows an application to modify the Google service map.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.WRITE_GSERVICES"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows an application to call
{@link android.app.ActivityManager#forceStopPackage}.
@hide -->
<permission android:name="android.permission.FORCE_STOP_PACKAGES"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi @hide Allows an application to retrieve the content of the active window
An active window is the window that has fired an accessibility event. -->
<permission android:name="android.permission.RETRIEVE_WINDOW_CONTENT"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Modify the global animation scaling factor.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.SET_ANIMATION_SCALE"
- android:protectionLevel="signature|privileged|development" />
+ android:protectionLevel="signature|privileged|development" />
<!-- @deprecated This functionality will be removed in the future; please do
not use. Allow an application to make its activities persistent. -->
<permission android:name="android.permission.PERSISTENT_ACTIVITY"
- android:label="@string/permlab_persistentActivity"
- android:description="@string/permdesc_persistentActivity"
- android:protectionLevel="normal" />
+ android:label="@string/permlab_persistentActivity"
+ android:description="@string/permdesc_persistentActivity"
+ android:protectionLevel="normal" />
<!-- Allows an application to find out the space used by any package.
<p>Protection level: normal
-->
<permission android:name="android.permission.GET_PACKAGE_SIZE"
- android:label="@string/permlab_getPackageSize"
- android:description="@string/permdesc_getPackageSize"
- android:protectionLevel="normal" />
+ android:label="@string/permlab_getPackageSize"
+ android:description="@string/permdesc_getPackageSize"
+ android:protectionLevel="normal" />
<!-- @deprecated No longer useful, see
{@link android.content.pm.PackageManager#addPackageToPreferred}
for details. -->
<permission android:name="android.permission.SET_PREFERRED_APPLICATIONS"
- android:protectionLevel="signature|verifier" />
+ android:protectionLevel="signature|verifier" />
<!-- Allows an application to receive the
{@link android.content.Intent#ACTION_BOOT_COMPLETED} that is
@@ -2064,9 +2083,9 @@
<p>Protection level: normal
-->
<permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"
- android:label="@string/permlab_receiveBootCompleted"
- android:description="@string/permdesc_receiveBootCompleted"
- android:protectionLevel="normal" />
+ android:label="@string/permlab_receiveBootCompleted"
+ android:description="@string/permdesc_receiveBootCompleted"
+ android:protectionLevel="normal" />
<!-- Allows an application to broadcast sticky intents. These are
broadcasts whose data is held by the system after being finished,
@@ -2075,90 +2094,90 @@
<p>Protection level: normal
-->
<permission android:name="android.permission.BROADCAST_STICKY"
- android:label="@string/permlab_broadcastSticky"
- android:description="@string/permdesc_broadcastSticky"
- android:protectionLevel="normal" />
+ android:label="@string/permlab_broadcastSticky"
+ android:description="@string/permdesc_broadcastSticky"
+ android:protectionLevel="normal" />
<!-- @SystemApi Allows mounting and unmounting file systems for removable storage.
<p>Not for use by third-party applications.-->
<permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows formatting file systems for removable storage.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.MOUNT_FORMAT_FILESYSTEMS"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @hide -->
<permission android:name="android.permission.STORAGE_INTERNAL"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Allows access to ASEC non-destructive API calls
@hide -->
<permission android:name="android.permission.ASEC_ACCESS"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Allows creation of ASEC volumes
@hide -->
<permission android:name="android.permission.ASEC_CREATE"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Allows destruction of ASEC volumes
@hide -->
<permission android:name="android.permission.ASEC_DESTROY"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Allows mount / unmount of ASEC volumes
@hide -->
<permission android:name="android.permission.ASEC_MOUNT_UNMOUNT"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Allows rename of ASEC volumes
@hide -->
<permission android:name="android.permission.ASEC_RENAME"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @SystemApi Allows applications to write the apn settings.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.WRITE_APN_SETTINGS"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Allows applications to change network connectivity state.
<p>Protection level: normal
-->
<permission android:name="android.permission.CHANGE_NETWORK_STATE"
- android:description="@string/permdesc_changeNetworkState"
- android:label="@string/permlab_changeNetworkState"
- android:protectionLevel="normal" />
+ android:description="@string/permdesc_changeNetworkState"
+ android:label="@string/permlab_changeNetworkState"
+ android:protectionLevel="normal" />
<!-- Allows an application to clear the caches of all installed
applications on the device.
<p>Protection level: system|signature
-->
<permission android:name="android.permission.CLEAR_APP_CACHE"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows an application to use any media decoder when decoding for playback
@hide -->
<permission android:name="android.permission.ALLOW_ANY_CODEC_FOR_PLAYBACK"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows an application to install and/or uninstall CA certificates on
behalf of the user.
@hide -->
<permission android:name="android.permission.MANAGE_CA_CERTIFICATES"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows an application to do certain operations needed for
interacting with the recovery (system update) system.
@hide -->
<permission android:name="android.permission.RECOVERY"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Allows the system to bind to an application's task services
@hide -->
<permission android:name="android.permission.BIND_JOB_SERVICE"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<uses-permission android:name="android.permission.BIND_JOB_SERVICE"/>
<!-- Allows an application to initiate configuration updates
@@ -2167,24 +2186,24 @@
it off to the various individual installer components
@hide -->
<permission android:name="android.permission.UPDATE_CONFIG"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Allows the system to reset throttling in shortcut manager.
@hide -->
<permission android:name="android.permission.RESET_SHORTCUT_MANAGER_THROTTLING"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Allows the system to bind to the discovered Network Recommendation Service.
@SystemApi @hide -->
<permission android:name="android.permission.BIND_NETWORK_RECOMMENDATION_SERVICE"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<uses-permission android:name="android.permission.BIND_NETWORK_RECOMMENDATION_SERVICE"/>
<!-- Allows an application to enable, disable and change priority of
runtime resource overlays.
@hide -->
<permission android:name="android.permission.CHANGE_OVERLAY_PACKAGES"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- ========================================= -->
<!-- Permissions for special development tools -->
@@ -2194,40 +2213,40 @@
<!-- @SystemApi Allows an application to read or write the secure system settings.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.WRITE_SECURE_SETTINGS"
- android:protectionLevel="signature|privileged|development" />
+ android:protectionLevel="signature|privileged|development" />
<!-- @SystemApi Allows an application to retrieve state dump information from system services.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.DUMP"
- android:protectionLevel="signature|privileged|development" />
+ android:protectionLevel="signature|privileged|development" />
<!-- @SystemApi Allows an application to read the low-level system log files.
<p>Not for use by third-party applications, because
Log entries can contain the user's private information. -->
<permission android:name="android.permission.READ_LOGS"
- android:protectionLevel="signature|privileged|development" />
+ android:protectionLevel="signature|privileged|development" />
<!-- @SystemApi Configure an application for debugging.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.SET_DEBUG_APP"
- android:protectionLevel="signature|privileged|development" />
+ android:protectionLevel="signature|privileged|development" />
<!-- @SystemApi Allows an application to set the maximum number of (not needed)
application processes that can be running.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.SET_PROCESS_LIMIT"
- android:protectionLevel="signature|privileged|development" />
+ android:protectionLevel="signature|privileged|development" />
<!-- @SystemApi Allows an application to control whether activities are immediately
finished when put in the background.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.SET_ALWAYS_FINISH"
- android:protectionLevel="signature|privileged|development" />
+ android:protectionLevel="signature|privileged|development" />
<!-- @SystemApi Allow an application to request that a signal be sent to all persistent processes.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.SIGNAL_PERSISTENT_PROCESSES"
- android:protectionLevel="signature|privileged|development" />
+ android:protectionLevel="signature|privileged|development" />
<!-- ==================================== -->
<!-- Private permissions -->
@@ -2236,34 +2255,34 @@
<!-- @SystemApi Allows access to the list of accounts in the Accounts Service. -->
<permission android:name="android.permission.GET_ACCOUNTS_PRIVILEGED"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Allows but does not guarantee access to user passwords at the conclusion of add account
@hide -->
<permission android:name="android.permission.GET_PASSWORD"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @SystemApi Allows applications to RW to diagnostic resources.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.DIAGNOSTIC"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @SystemApi Allows an application to open, close, or disable the status bar
and its icons.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.STATUS_BAR"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Allows an application to be the status bar. Currently used only by SystemUI.apk
@hide -->
<permission android:name="android.permission.STATUS_BAR_SERVICE"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Allows an application to bind to third party quick settings tiles.
<p>Should only be requested by the System, should be required by
TileService declarations.-->
<permission android:name="android.permission.BIND_QUICK_SETTINGS_TILE"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @SystemApi Allows an application to force a BACK operation on whatever is the
top activity.
@@ -2271,28 +2290,28 @@
@hide
-->
<permission android:name="android.permission.FORCE_BACK"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @SystemApi Allows an application to update device statistics.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.UPDATE_DEVICE_STATS"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi @hide Allows an application to collect battery statistics -->
<permission android:name="android.permission.GET_APP_OPS_STATS"
- android:protectionLevel="signature|privileged|development" />
+ android:protectionLevel="signature|privileged|development" />
<!-- @SystemApi Allows an application to update application operation statistics. Not for
use by third party apps.
@hide -->
<permission android:name="android.permission.UPDATE_APP_OPS_STATS"
- android:protectionLevel="signature|privileged|installer" />
+ android:protectionLevel="signature|privileged|installer" />
<!-- @SystemApi Allows an application to update the user app op restrictions.
Not for use by third party apps.
@hide -->
<permission android:name="android.permission.MANAGE_APP_OPS_RESTRICTIONS"
- android:protectionLevel="signature|installer" />
+ android:protectionLevel="signature|installer" />
<!-- @SystemApi Allows an application to open windows that are for use by parts
of the system user interface.
@@ -2300,7 +2319,7 @@
@hide
-->
<permission android:name="android.permission.INTERNAL_SYSTEM_WINDOW"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @SystemApi Allows an application to manage (create, destroy,
Z-order) application tokens in the window manager.
@@ -2308,17 +2327,17 @@
@hide
-->
<permission android:name="android.permission.MANAGE_APP_TOKENS"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Allows System UI to register listeners for events from Window Manager.
@hide -->
<permission android:name="android.permission.REGISTER_WINDOW_MANAGER_LISTENERS"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @hide Allows the application to temporarily freeze the screen for a
full-screen transition. -->
<permission android:name="android.permission.FREEZE_SCREEN"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @SystemApi Allows an application to inject user events (keys, touch, trackball)
into the event stream and deliver them to ANY window. Without this
@@ -2327,16 +2346,16 @@
@hide
-->
<permission android:name="android.permission.INJECT_EVENTS"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @hide Allows an application to register an input filter which filters the stream
of user events (keys, touch, trackball) before they are dispatched to any window. -->
<permission android:name="android.permission.FILTER_EVENTS"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @hide Allows an application to retrieve the window token from the accessibility manager. -->
<permission android:name="android.permission.RETRIEVE_WINDOW_TOKEN"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @hide Allows an application to modify accessibility information from another app. -->
<permission android:name="android.permission.MODIFY_ACCESSIBILITY_DATA"
@@ -2348,11 +2367,11 @@
<!-- @hide Allows an application to collect frame statistics -->
<permission android:name="android.permission.FRAME_STATS"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @hide Allows an application to temporary enable accessibility on the device. -->
<permission android:name="android.permission.TEMPORARY_ENABLE_ACCESSIBILITY"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @SystemApi Allows an application to watch and control how activities are
started globally in the system. Only for is in debugging
@@ -2361,13 +2380,13 @@
@hide
-->
<permission android:name="android.permission.SET_ACTIVITY_WATCHER"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @SystemApi Allows an application to call the activity manager shutdown() API
to put the higher-level system there into a shutdown state.
@hide -->
<permission android:name="android.permission.SHUTDOWN"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows an application to tell the activity manager to temporarily
stop application switches, putting it into a special mode that
@@ -2375,7 +2394,7 @@
critical UI such as the home screen.
@hide -->
<permission android:name="android.permission.STOP_APP_SWITCHES"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows an application to retrieve private information about
the current top activity, such as any assist context it can provide.
@@ -2383,42 +2402,42 @@
@hide
-->
<permission android:name="android.permission.GET_TOP_ACTIVITY_INFO"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Allows an application to retrieve the current state of keys and
switches.
<p>Not for use by third-party applications.
@deprecated The API that used this permission has been removed. -->
<permission android:name="android.permission.READ_INPUT_STATE"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Must be required by an {@link android.inputmethodservice.InputMethodService},
to ensure that only the system can bind to it.
<p>Protection level: signature
-->
<permission android:name="android.permission.BIND_INPUT_METHOD"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Must be required by an {@link android.media.midi.MidiDeviceService},
to ensure that only the system can bind to it.
<p>Protection level: signature
-->
<permission android:name="android.permission.BIND_MIDI_DEVICE_SERVICE"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Must be required by an {@link android.accessibilityservice.AccessibilityService},
to ensure that only the system can bind to it.
<p>Protection level: signature
-->
<permission android:name="android.permission.BIND_ACCESSIBILITY_SERVICE"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Must be required by a {@link android.printservice.PrintService},
to ensure that only the system can bind to it.
<p>Protection level: signature
-->
<permission android:name="android.permission.BIND_PRINT_SERVICE"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Must be required by a {@link android.printservice.recommendation.RecommendationService},
to ensure that only the system can bind to it.
@@ -2427,7 +2446,7 @@
<p>Protection level: signature
-->
<permission android:name="android.permission.BIND_PRINT_RECOMMENDATION_SERVICE"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Must be required by a {@link android.nfc.cardemulation.HostApduService}
or {@link android.nfc.cardemulation.OffHostApduService} to ensure that only
@@ -2435,81 +2454,81 @@
<p>Protection level: signature
-->
<permission android:name="android.permission.BIND_NFC_SERVICE"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Must be required by the PrintSpooler to ensure that only the system can bind to it.
@hide -->
<permission android:name="android.permission.BIND_PRINT_SPOOLER_SERVICE"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Must be required by the CompanionDeviceManager to ensure that only the system can bind to it.
@hide -->
<permission android:name="android.permission.BIND_COMPANION_DEVICE_MANAGER_SERVICE"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @SystemApi Must be required by the RuntimePermissionPresenterService to ensure
that only the system can bind to it.
@hide -->
<permission android:name="android.permission.BIND_RUNTIME_PERMISSION_PRESENTER_SERVICE"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Must be required by a TextService (e.g. SpellCheckerService)
to ensure that only the system can bind to it.
<p>Protection level: signature
-->
<permission android:name="android.permission.BIND_TEXT_SERVICE"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Must be required by a {@link android.net.VpnService},
to ensure that only the system can bind to it.
<p>Protection level: signature
-->
<permission android:name="android.permission.BIND_VPN_SERVICE"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Must be required by a {@link android.service.wallpaper.WallpaperService},
to ensure that only the system can bind to it.
<p>Protection level: system|signature
-->
<permission android:name="android.permission.BIND_WALLPAPER"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Must be required by a {@link android.service.voice.VoiceInteractionService},
to ensure that only the system can bind to it.
<p>Protection level: signature
-->
<permission android:name="android.permission.BIND_VOICE_INTERACTION"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Must be required by a {@link android.service.autofill.AutofillService},
to ensure that only the system can bind to it.
<p>Protection level: signature
-->
<permission android:name="android.permission.BIND_AUTOFILL_SERVICE"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @hide TODO(b/37563972): remove once clients use BIND_AUTOFILL_SERVICE -->
<permission android:name="android.permission.BIND_AUTOFILL"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Must be required by hotword enrollment application,
to ensure that only the system can interact with it.
@hide <p>Not for use by third-party applications.</p> -->
<permission android:name="android.permission.MANAGE_VOICE_KEYPHRASES"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Must be required by a {@link com.android.media.remotedisplay.RemoteDisplayProvider},
to ensure that only the system can bind to it.
@hide -->
<permission android:name="android.permission.BIND_REMOTE_DISPLAY"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Must be required by a {@link android.media.tv.TvInputService}
to ensure that only the system can bind to it.
<p>Protection level: signature
-->
<permission android:name="android.permission.BIND_TV_INPUT"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi
Must be required by a {@link com.android.media.tv.remoteprovider.TvRemoteProvider}
@@ -2518,7 +2537,7 @@
<p>Not for use by third-party applications. </p>
@hide -->
<permission android:name="android.permission.BIND_TV_REMOTE_SERVICE"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi
Must be required for a virtual remote controller for TV.
@@ -2526,13 +2545,13 @@
<p>Not for use by third-party applications. </p>
@hide -->
<permission android:name="android.permission.TV_VIRTUAL_REMOTE_CONTROLLER"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows an application to modify parental controls
<p>Not for use by third-party applications.
@hide -->
<permission android:name="android.permission.MODIFY_PARENTAL_CONTROLS"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows an application to notify TV inputs by sending broadcasts.
<p>Protection level: signature|privileged
@@ -2545,20 +2564,20 @@
to ensure that only the system can interact with it.
@hide -->
<permission android:name="android.permission.BIND_ROUTE_PROVIDER"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Must be required by device administration receiver, to ensure that only the
system can interact with it.
<p>Protection level: signature
-->
<permission android:name="android.permission.BIND_DEVICE_ADMIN"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @SystemApi Required to add or remove another application as a device admin.
<p>Not for use by third-party applications.
@hide -->
<permission android:name="android.permission.MANAGE_DEVICE_ADMINS"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows low-level access to setting the orientation (actually
rotation) of the screen.
@@ -2566,33 +2585,33 @@
@hide
-->
<permission android:name="android.permission.SET_ORIENTATION"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @SystemApi Allows low-level access to setting the pointer speed.
<p>Not for use by third-party applications.
@hide
-->
<permission android:name="android.permission.SET_POINTER_SPEED"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Allows low-level access to setting input device calibration.
<p>Not for use by normal applications.
@hide -->
<permission android:name="android.permission.SET_INPUT_CALIBRATION"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Allows low-level access to setting the keyboard layout.
<p>Not for use by third-party applications.
@hide -->
<permission android:name="android.permission.SET_KEYBOARD_LAYOUT"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Allows an application to query tablet mode state and monitor changes
in it.
<p>Not for use by third-party applications.
@hide -->
<permission android:name="android.permission.TABLET_MODE"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Allows an application to request installing packages. Apps
targeting APIs greater than 25 must hold this permission in
@@ -2600,9 +2619,9 @@
<p>Protection level: signature
-->
<permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"
- android:label="@string/permlab_requestInstallPackages"
- android:description="@string/permdesc_requestInstallPackages"
- android:protectionLevel="signature|appop" />
+ android:label="@string/permlab_requestInstallPackages"
+ android:description="@string/permdesc_requestInstallPackages"
+ android:protectionLevel="signature|appop" />
<!-- Allows an application to request deleting packages. Apps
targeting APIs greater than 25 must hold this permission in
@@ -2610,41 +2629,41 @@
<p>Protection level: normal
-->
<permission android:name="android.permission.REQUEST_DELETE_PACKAGES"
- android:label="@string/permlab_requestDeletePackages"
- android:description="@string/permdesc_requestDeletePackages"
- android:protectionLevel="normal" />
+ android:label="@string/permlab_requestDeletePackages"
+ android:description="@string/permdesc_requestDeletePackages"
+ android:protectionLevel="normal" />
<!-- @SystemApi Allows an application to install packages.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.INSTALL_PACKAGES"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows an application to clear user data.
<p>Not for use by third-party applications
@hide
-->
<permission android:name="android.permission.CLEAR_APP_USER_DATA"
- android:protectionLevel="signature|installer" />
+ android:protectionLevel="signature|installer" />
<!-- @hide Allows an application to get the URI permissions
granted to another application.
<p>Not for use by third-party applications
-->
<permission android:name="android.permission.GET_APP_GRANTED_URI_PERMISSIONS"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @hide Allows an application to clear the URI permissions
granted to another application.
<p>Not for use by third-party applications
-->
<permission
- android:name="android.permission.CLEAR_APP_GRANTED_URI_PERMISSIONS"
- android:protectionLevel="signature" />
+ android:name="android.permission.CLEAR_APP_GRANTED_URI_PERMISSIONS"
+ android:protectionLevel="signature" />
<!-- @SystemApi Allows an application to delete cache files.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.DELETE_CACHE_FILES"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows an application to delete packages.
<p>Not for use by third-party applications.
@@ -2652,120 +2671,120 @@
when the application deleting the package is not the same application that installed the
package. -->
<permission android:name="android.permission.DELETE_PACKAGES"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows an application to move location of installed package.
@hide -->
<permission android:name="android.permission.MOVE_PACKAGE"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows an application to change whether an application component (other than its own) is
enabled or not.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.CHANGE_COMPONENT_ENABLED_STATE"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows an application to grant specific permissions.
@hide -->
<permission android:name="android.permission.GRANT_RUNTIME_PERMISSIONS"
- android:protectionLevel="signature|installer|verifier" />
+ android:protectionLevel="signature|installer|verifier" />
<!-- @SystemApi Allows an app that has this permission and the permissions to install packages
to request certain runtime permissions to be granted at installation.
@hide -->
<permission android:name="android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS"
- android:protectionLevel="signature|installer|verifier" />
+ android:protectionLevel="signature|installer|verifier" />
<!-- @SystemApi Allows an application to revoke specific permissions.
@hide -->
<permission android:name="android.permission.REVOKE_RUNTIME_PERMISSIONS"
- android:protectionLevel="signature|installer|verifier" />
+ android:protectionLevel="signature|installer|verifier" />
<!-- @hide Allows an application to observe permission changes. -->
<permission android:name="android.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows an application to use SurfaceFlinger's low level features.
<p>Not for use by third-party applications.
@hide
-->
<permission android:name="android.permission.ACCESS_SURFACE_FLINGER"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @SystemApi Allows an application to take screen shots and more generally
get access to the frame buffer data.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.READ_FRAME_BUFFER"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Allows an application to use InputFlinger's low level features.
@hide -->
<permission android:name="android.permission.ACCESS_INPUT_FLINGER"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Allows an application to configure and connect to Wifi displays
@hide -->
<permission android:name="android.permission.CONFIGURE_WIFI_DISPLAY"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Allows an application to control low-level features of Wifi displays
such as opening an RTSP socket. This permission should only be used
by the display manager.
@hide -->
<permission android:name="android.permission.CONTROL_WIFI_DISPLAY"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Allows an application to control the color modes set for displays system-wide.
<p>Not for use by third-party applications.</p>
@hide -->
<permission android:name="android.permission.CONFIGURE_DISPLAY_COLOR_MODE"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @SystemApi Allows an application to control VPN.
<p>Not for use by third-party applications.</p>
@hide -->
<permission android:name="android.permission.CONTROL_VPN"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<uses-permission android:name="android.permission.CONTROL_VPN" />
<!-- @SystemApi Allows an application to capture audio output.
<p>Not for use by third-party applications.</p> -->
<permission android:name="android.permission.CAPTURE_AUDIO_OUTPUT"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows an application to capture audio for hotword detection.
<p>Not for use by third-party applications.</p>
@hide -->
<permission android:name="android.permission.CAPTURE_AUDIO_HOTWORD"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows an application to modify audio routing and override policy decisions.
<p>Not for use by third-party applications.</p>
@hide -->
<permission android:name="android.permission.MODIFY_AUDIO_ROUTING"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows an application to capture video output.
<p>Not for use by third-party applications.</p> -->
<permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows an application to capture secure video output.
<p>Not for use by third-party applications.</p> -->
<permission android:name="android.permission.CAPTURE_SECURE_VIDEO_OUTPUT"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows an application to know what content is playing and control its playback.
<p>Not for use by third-party applications due to privacy of media consumption</p> -->
<permission android:name="android.permission.MEDIA_CONTENT_CONTROL"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi @hide Allows an application to set the volume key long-press listener.
<p>When it's set, the application will receive the volume key long-press event
instead of changing volume.</p>
<p>Not for use by third-party applications</p> -->
<permission android:name="android.permission.SET_VOLUME_KEY_LONG_PRESS_LISTENER"
- android:protectionLevel="signature|privileged|development" />
+ android:protectionLevel="signature|privileged|development" />
<!-- @SystemApi @hide Allows an application to set media key event listener.
<p>When it's set, the application will receive the media key event before
@@ -2773,61 +2792,61 @@
cannot get the event.</p>
<p>Not for use by third-party applications</p> -->
<permission android:name="android.permission.SET_MEDIA_KEY_LISTENER"
- android:protectionLevel="signature|privileged|development" />
+ android:protectionLevel="signature|privileged|development" />
<!-- @SystemApi Required to be able to disable the device (very dangerous!).
<p>Not for use by third-party applications.
@hide
-->
<permission android:name="android.permission.BRICK"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @SystemApi Required to be able to reboot the device.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.REBOOT"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
- <!-- @SystemApi Allows low-level access to power management.
- <p>Not for use by third-party applications.
- @hide
- -->
- <permission android:name="android.permission.DEVICE_POWER"
- android:protectionLevel="signature" />
+ <!-- @SystemApi Allows low-level access to power management.
+ <p>Not for use by third-party applications.
+ @hide
+ -->
+ <permission android:name="android.permission.DEVICE_POWER"
+ android:protectionLevel="signature" />
- <!-- Allows access to the PowerManager.userActivity function.
- <p>Not for use by third-party applications. @hide @SystemApi -->
+ <!-- Allows access to the PowerManager.userActivity function.
+ <p>Not for use by third-party applications. @hide @SystemApi -->
<permission android:name="android.permission.USER_ACTIVITY"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
- <!-- @hide Allows low-level access to tun tap driver -->
+ <!-- @hide Allows low-level access to tun tap driver -->
<permission android:name="android.permission.NET_TUNNELING"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Run as a manufacturer test application, running as the root user.
Only available when the device is running in manufacturer test mode.
<p>Not for use by third-party applications.
-->
<permission android:name="android.permission.FACTORY_TEST"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Allows an application to broadcast a notification that an application
package has been removed.
<p>Not for use by third-party applications.
-->
<permission android:name="android.permission.BROADCAST_PACKAGE_REMOVED"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Allows an application to broadcast an SMS receipt notification.
<p>Not for use by third-party applications.
-->
<permission android:name="android.permission.BROADCAST_SMS"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Allows an application to broadcast a WAP PUSH receipt notification.
<p>Not for use by third-party applications.
-->
<permission android:name="android.permission.BROADCAST_WAP_PUSH"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @SystemApi Allows an application to broadcast privileged networking requests.
<p>Not for use by third-party applications.
@@ -2835,87 +2854,87 @@
@deprecated Use {@link android.Manifest.permission#REQUEST_NETWORK_SCORES} instead
-->
<permission android:name="android.permission.BROADCAST_NETWORK_PRIVILEGED"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Not for use by third-party applications. -->
<permission android:name="android.permission.MASTER_CLEAR"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows an application to call any phone number, including emergency
numbers, without going through the Dialer user interface for the user
to confirm the call being placed.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.CALL_PRIVILEGED"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows an application to perform CDMA OTA provisioning @hide -->
<permission android:name="android.permission.PERFORM_CDMA_PROVISIONING"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows an application to perform SIM Activation @hide -->
<permission android:name="android.permission.PERFORM_SIM_ACTIVATION"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows enabling/disabling location update notifications from
the radio.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.CONTROL_LOCATION_UPDATES"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows read/write access to the "properties" table in the checkin
database, to change values that get uploaded.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.ACCESS_CHECKIN_PROPERTIES"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows an application to collect component usage
statistics
<p>Declaring the permission implies intention to use the API and the user of the
device can grant permission through the Settings application. -->
<permission android:name="android.permission.PACKAGE_USAGE_STATS"
- android:protectionLevel="signature|privileged|development|appop" />
+ android:protectionLevel="signature|privileged|development|appop" />
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
<!-- @hide Allows an application to change the app idle state of an app.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.CHANGE_APP_IDLE_STATE"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @hide @SystemApi Allows an application to temporarily whitelist an inactive app to
access the network and acquire wakelocks.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Permission an application must hold in order to use
{@link android.provider.Settings#ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS}.
This is a normal permission: an app requesting it will always be granted the
permission, without the user needing to approve or see it. -->
<permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"
- android:label="@string/permlab_requestIgnoreBatteryOptimizations"
- android:description="@string/permdesc_requestIgnoreBatteryOptimizations"
- android:protectionLevel="normal" />
+ android:label="@string/permlab_requestIgnoreBatteryOptimizations"
+ android:description="@string/permdesc_requestIgnoreBatteryOptimizations"
+ android:protectionLevel="normal" />
<!-- @SystemApi Allows an application to collect battery statistics -->
<permission android:name="android.permission.BATTERY_STATS"
- android:protectionLevel="signature|privileged|development" />
+ android:protectionLevel="signature|privileged|development" />
<!-- @SystemApi Allows an application to control the backup and restore process.
<p>Not for use by third-party applications.
@hide pending API council -->
<permission android:name="android.permission.BACKUP"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Allows a package to launch the secure full-backup confirmation UI.
ONLY the system process may hold this permission.
@hide -->
<permission android:name="android.permission.CONFIRM_FULL_BACKUP"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @SystemApi Must be required by a {@link android.widget.RemoteViewsService},
to ensure that only the system can bind to it. -->
<permission android:name="android.permission.BIND_REMOTEVIEWS"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows an application to tell the AppWidget service which application
can access AppWidget's data. The normal user flow is that a user
@@ -2924,25 +2943,25 @@
An application that has this permission should honor that contract.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.BIND_APPWIDGET"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Private permission, to restrict who can bring up a dialog to add a new
keyguard widget
@hide -->
<permission android:name="android.permission.BIND_KEYGUARD_APPWIDGET"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Internal permission allowing an application to query/set which
applications can bind AppWidgets.
@hide -->
<permission android:name="android.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Allows applications to change the background data setting.
<p>Not for use by third-party applications.
@hide pending API council -->
<permission android:name="android.permission.CHANGE_BACKGROUND_DATA_SETTING"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @SystemApi This permission can be used on content providers to allow the global
search system to access their data. Typically it used when the
@@ -2953,7 +2972,7 @@
it is used by applications to protect themselves from everyone else
besides global search. -->
<permission android:name="android.permission.GLOBAL_SEARCH"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Internal permission protecting access to the global search
system: ensures that only the system can access the provider
@@ -2963,33 +2982,33 @@
ranking).
@hide -->
<permission android:name="android.permission.GLOBAL_SEARCH_CONTROL"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @SystemApi Internal permission to allows an application to read indexable data.
@hide -->
<permission android:name="android.permission.READ_SEARCH_INDEXABLES"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows applications to set a live wallpaper.
@hide XXX Change to signature once the picker is moved to its
own apk as Ghod Intended. -->
<permission android:name="android.permission.SET_WALLPAPER_COMPONENT"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows applications to read dream settings and dream state.
@hide -->
<permission android:name="android.permission.READ_DREAM_STATE"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows applications to write dream settings, and start or stop dreaming.
@hide -->
<permission android:name="android.permission.WRITE_DREAM_STATE"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allow an application to read and write the cache partition.
@hide -->
<permission android:name="android.permission.ACCESS_CACHE_FILESYSTEM"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Must be required by default container service so that only
the system can bind to it and use it to copy
@@ -2997,67 +3016,67 @@
accessible to the system.
@hide -->
<permission android:name="android.permission.COPY_PROTECTED_DATA"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @SystemApi Internal permission protecting access to the encryption methods
@hide
-->
<permission android:name="android.permission.CRYPT_KEEPER"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows an application to read historical network usage for
specific networks and applications. @hide -->
<permission android:name="android.permission.READ_NETWORK_USAGE_HISTORY"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Allows an application to manage network policies (such as warning and disable
limits) and to define application-specific rules. @hide -->
<permission android:name="android.permission.MANAGE_NETWORK_POLICY"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @SystemApi Allows an application to account its network traffic against other UIDs. Used
by system services like download manager and media server. Not for use by
third party apps. @hide -->
<permission android:name="android.permission.MODIFY_NETWORK_ACCOUNTING"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- C2DM permission.
@hide Used internally.
-->
<permission android:name="android.intent.category.MASTER_CLEAR.permission.C2D_MESSAGE"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<uses-permission android:name="android.intent.category.MASTER_CLEAR.permission.C2D_MESSAGE"/>
<!-- @SystemApi @hide Package verifier needs to have this permission before the PackageManager will
trust it to verify packages.
-->
<permission android:name="android.permission.PACKAGE_VERIFICATION_AGENT"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Must be required by package verifier receiver, to ensure that only the
system can interact with it.
@hide
-->
<permission android:name="android.permission.BIND_PACKAGE_VERIFIER"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @SystemApi @hide Intent filter verifier needs to have this permission before the
PackageManager will trust it to verify intent filters.
-->
<permission android:name="android.permission.INTENT_FILTER_VERIFICATION_AGENT"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Must be required by intent filter verifier receiver, to ensure that only the
system can interact with it.
@hide
-->
<permission android:name="android.permission.BIND_INTENT_FILTER_VERIFIER"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @SystemApi Allows applications to access serial ports via the SerialManager.
@hide -->
<permission android:name="android.permission.SERIAL_PORT"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Allows the holder to access content providers from outside an ApplicationThread.
This permission is enforced by the ActivityManagerService on the corresponding APIs,
@@ -3066,27 +3085,27 @@
@hide
-->
<permission android:name="android.permission.ACCESS_CONTENT_PROVIDERS_EXTERNALLY"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @SystemApi Allows an application to hold an UpdateLock, recommending that a headless
OTA reboot *not* occur while the lock is held.
@hide -->
<permission android:name="android.permission.UPDATE_LOCK"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows an application to read the current set of notifications, including
any metadata and intents attached.
@hide -->
<permission android:name="android.permission.ACCESS_NOTIFICATIONS"
- android:protectionLevel="signature|privileged|appop" />
+ android:protectionLevel="signature|privileged|appop" />
<!-- Marker permission for applications that wish to access notification policy.
<p>Protection level: normal
-->
<permission android:name="android.permission.ACCESS_NOTIFICATION_POLICY"
- android:description="@string/permdesc_access_notification_policy"
- android:label="@string/permlab_access_notification_policy"
- android:protectionLevel="normal" />
+ android:description="@string/permdesc_access_notification_policy"
+ android:label="@string/permlab_access_notification_policy"
+ android:protectionLevel="normal" />
<!-- Allows modification of do not disturb rules and policies. Only allowed for system
processes.
@@ -3097,42 +3116,42 @@
<!-- Allows access to keyguard secure storage. Only allowed for system processes.
@hide -->
<permission android:name="android.permission.ACCESS_KEYGUARD_SECURE_STORAGE"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Allows managing (adding, removing) fingerprint templates. Reserved for the system. @hide -->
<permission android:name="android.permission.MANAGE_FINGERPRINT"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Allows an app to reset fingerprint attempt counter. Reserved for the system. @hide -->
<permission android:name="android.permission.RESET_FINGERPRINT_LOCKOUT"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Allows an application to control keyguard. Only allowed for system processes.
@hide -->
<permission android:name="android.permission.CONTROL_KEYGUARD"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Allows an application to listen to trust changes. Only allowed for system processes.
@hide -->
<permission android:name="android.permission.TRUST_LISTENER"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @SystemApi Allows an application to provide a trust agent.
@hide For security reasons, this is a platform-only permission. -->
<permission android:name="android.permission.PROVIDE_TRUST_AGENT"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Allows an application to launch the trust agent settings activity.
@hide -->
<permission android:name="android.permission.LAUNCH_TRUST_AGENT_SETTINGS"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Must be required by an {@link
android.service.trust.TrustAgentService},
to ensure that only the system can bind to it.
@hide -->
<permission android:name="android.permission.BIND_TRUST_AGENT"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Must be required by an {@link
android.service.notification.NotificationListenerService},
@@ -3140,7 +3159,7 @@
<p>Protection level: signature
-->
<permission android:name="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @SystemApi Must be required by an {@link
android.service.notification.NotificationAssistantService} to ensure that only the system
@@ -3149,7 +3168,7 @@
@hide
-->
<permission android:name="android.permission.BIND_NOTIFICATION_ASSISTANT_SERVICE"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Must be required by a {@link
android.service.chooser.ChooserTargetService}, to ensure that
@@ -3157,7 +3176,7 @@
<p>Protection level: signature
-->
<permission android:name="android.permission.BIND_CHOOSER_TARGET_SERVICE"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @SystemApi Must be held by services that extend
{@link android.service.resolver.ResolverRankerService}.
@@ -3182,14 +3201,14 @@
<p>Protection level: signature
-->
<permission android:name="android.permission.BIND_CONDITION_PROVIDER_SERVICE"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Must be required by an {@link android.service.dreams.DreamService},
to ensure that only the system can bind to it.
<p>Protection level: signature
-->
<permission android:name="android.permission.BIND_DREAM_SERVICE"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Must be required by an {@link android.app.usage.CacheQuotaService} to ensure that only the
system can bind to it.
@@ -3202,44 +3221,44 @@
carrier setup application to enforce that this permission is required
@hide This is not a third-party API (intended for OEMs and system apps). -->
<permission android:name="android.permission.INVOKE_CARRIER_SETUP"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows an application to listen for network condition observations.
@hide This is not a third-party API (intended for system apps). -->
<permission android:name="android.permission.ACCESS_NETWORK_CONDITIONS"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows an application to provision and access DRM certificates
@hide This is not a third-party API (intended for system apps). -->
<permission android:name="android.permission.ACCESS_DRM_CERTIFICATES"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Api Allows an application to manage media projection sessions.
@hide This is not a third-party API (intended for system apps). -->
<permission android:name="android.permission.MANAGE_MEDIA_PROJECTION"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @SystemApi Allows an application to read install sessions
@hide This is not a third-party API (intended for system apps). -->
<permission android:name="android.permission.READ_INSTALL_SESSIONS"
- android:label="@string/permlab_readInstallSessions"
- android:description="@string/permdesc_readInstallSessions"
- android:protectionLevel="normal"/>
+ android:label="@string/permlab_readInstallSessions"
+ android:description="@string/permdesc_readInstallSessions"
+ android:protectionLevel="normal"/>
<!-- @SystemApi Allows an application to remove DRM certificates
@hide This is not a third-party API (intended for system apps). -->
<permission android:name="android.permission.REMOVE_DRM_CERTIFICATES"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @deprecated Use {@link android.Manifest.permission#BIND_CARRIER_SERVICES} instead -->
<permission android:name="android.permission.BIND_CARRIER_MESSAGING_SERVICE"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Allows an application to interact with the currently active
{@link android.service.voice.VoiceInteractionService}.
@hide -->
<permission android:name="android.permission.ACCESS_VOICE_INTERACTION_SERVICE"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- The system process that is allowed to bind to services in carrier apps will
have this permission. Carrier apps should use this permission to protect
@@ -3247,9 +3266,9 @@
<p>Protection level: system|signature
-->
<permission android:name="android.permission.BIND_CARRIER_SERVICES"
- android:label="@string/permlab_bindCarrierServices"
- android:description="@string/permdesc_bindCarrierServices"
- android:protectionLevel="signature|privileged" />
+ android:label="@string/permlab_bindCarrierServices"
+ android:description="@string/permdesc_bindCarrierServices"
+ android:protectionLevel="signature|privileged" />
<!-- Allows an application to query whether DO_NOT_ASK_CREDENTIALS_ON_BOOT
flag is set.
@@ -3295,7 +3314,7 @@
<!-- Allows the holder to access the instant applications on the device.
@hide -->
<permission android:name="android.permission.ACCESS_INSTANT_APPS"
- android:protectionLevel="signature|installer|verifier" />
+ android:protectionLevel="signature|installer|verifier" />
<!-- Allows receiving the usage of media resource e.g. video/audio codec and
graphic memory.
@@ -3307,7 +3326,7 @@
APIs given by {@link SoundTriggerManager}.
@hide <p>Not for use by third-party applications.</p> -->
<permission android:name="android.permission.MANAGE_SOUND_TRIGGER"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows trusted applications to dispatch managed provisioning message to Managed
Provisioning app. If requesting app does not have permission, it will be ignored.
@@ -3331,24 +3350,24 @@
the system can bind to it.
<p>Protection level: signature -->
<permission android:name="android.permission.BIND_VR_LISTENER_SERVICE"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Must be required by system apps when accessing restricted VR APIs.
@hide
@SystemApi
<p>Protection level: signature -->
<permission android:name="android.permission.RESTRICTED_VR_ACCESS"
- android:protectionLevel="signature|preinstalled" />
+ android:protectionLevel="signature|preinstalled" />
<!-- Required to make calls to {@link android.service.vr.IVrManager}.
@hide -->
<permission android:name="android.permission.ACCESS_VR_MANAGER"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Allows an application to whitelist tasks during lock task mode
@hide <p>Not for use by third-party applications.</p> -->
<permission android:name="android.permission.UPDATE_LOCK_TASK_PACKAGES"
- android:protectionLevel="signature|setup" />
+ android:protectionLevel="signature|setup" />
<!-- @SystemApi Allows an application to replace the app name displayed alongside notifications
in the N-release and later.
@@ -3364,7 +3383,7 @@
<!-- @SystemApi Allows an application to manage auto-fill sessions.
@hide <p>Not for use by third-party applications.</p> -->
<permission android:name="android.permission.MANAGE_AUTO_FILL"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Allows an app to set the theme overlay in /vendor/overlay
being used.
@@ -3374,7 +3393,7 @@
<!-- Allows an instant app to create foreground services. -->
<permission android:name="android.permission.INSTANT_APP_FOREGROUND_SERVICE"
- android:protectionLevel="signature|development|ephemeral|appop" />
+ android:protectionLevel="signature|development|ephemeral|appop" />
<application android:process="system"
android:persistent="true"
@@ -3389,14 +3408,14 @@
android:defaultToDeviceProtectedStorage="true"
android:directBootAware="true">
<activity android:name="com.android.internal.app.ChooserActivity"
- android:theme="@style/Theme.DeviceDefault.Resolver"
- android:finishOnCloseSystemDialogs="true"
- android:excludeFromRecents="true"
- android:documentLaunchMode="never"
- android:relinquishTaskIdentity="true"
- android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|keyboard|keyboardHidden"
- android:process=":ui"
- android:visibleToInstantApps="true">
+ android:theme="@style/Theme.DeviceDefault.Resolver"
+ android:finishOnCloseSystemDialogs="true"
+ android:excludeFromRecents="true"
+ android:documentLaunchMode="never"
+ android:relinquishTaskIdentity="true"
+ android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|keyboard|keyboardHidden"
+ android:process=":ui"
+ android:visibleToInstantApps="true">
<intent-filter>
<action android:name="android.intent.action.CHOOSER" />
<category android:name="android.intent.category.DEFAULT" />
@@ -3419,106 +3438,106 @@
</intent-filter>
</activity>
<activity android:name="com.android.internal.app.IntentForwarderActivity"
- android:finishOnCloseSystemDialogs="true"
- android:theme="@style/Theme.NoDisplay"
- android:excludeFromRecents="true"
- android:label="@string/user_owner_label"
- android:exported="true"
- >
+ android:finishOnCloseSystemDialogs="true"
+ android:theme="@style/Theme.NoDisplay"
+ android:excludeFromRecents="true"
+ android:label="@string/user_owner_label"
+ android:exported="true"
+ >
</activity>
<activity-alias android:name="com.android.internal.app.ForwardIntentToParent"
- android:targetActivity="com.android.internal.app.IntentForwarderActivity"
- android:exported="true"
- android:label="@string/user_owner_label">
+ android:targetActivity="com.android.internal.app.IntentForwarderActivity"
+ android:exported="true"
+ android:label="@string/user_owner_label">
</activity-alias>
<activity-alias android:name="com.android.internal.app.ForwardIntentToManagedProfile"
- android:targetActivity="com.android.internal.app.IntentForwarderActivity"
- android:icon="@drawable/ic_corp_icon"
- android:exported="true"
- android:label="@string/managed_profile_label">
+ android:targetActivity="com.android.internal.app.IntentForwarderActivity"
+ android:icon="@drawable/ic_corp_icon"
+ android:exported="true"
+ android:label="@string/managed_profile_label">
</activity-alias>
<activity android:name="com.android.internal.app.HeavyWeightSwitcherActivity"
- android:theme="@style/Theme.DeviceDefault.Light.Dialog"
- android:label="@string/heavy_weight_switcher_title"
- android:finishOnCloseSystemDialogs="true"
- android:excludeFromRecents="true"
- android:process=":ui">
+ android:theme="@style/Theme.DeviceDefault.Light.Dialog"
+ android:label="@string/heavy_weight_switcher_title"
+ android:finishOnCloseSystemDialogs="true"
+ android:excludeFromRecents="true"
+ android:process=":ui">
</activity>
<activity android:name="com.android.internal.app.PlatLogoActivity"
- android:theme="@style/Theme.Wallpaper.NoTitleBar.Fullscreen"
- android:configChanges="orientation|keyboardHidden"
- android:process=":ui">
+ android:theme="@style/Theme.Wallpaper.NoTitleBar.Fullscreen"
+ android:configChanges="orientation|keyboardHidden"
+ android:process=":ui">
</activity>
<activity android:name="com.android.internal.app.DisableCarModeActivity"
- android:theme="@style/Theme.NoDisplay"
- android:excludeFromRecents="true"
- android:process=":ui">
+ android:theme="@style/Theme.NoDisplay"
+ android:excludeFromRecents="true"
+ android:process=":ui">
</activity>
<activity android:name="com.android.internal.app.DumpHeapActivity"
- android:theme="@style/Theme.Translucent.NoTitleBar"
- android:label="@string/dump_heap_title"
- android:finishOnCloseSystemDialogs="true"
- android:noHistory="true"
- android:excludeFromRecents="true"
- android:process=":ui">
+ android:theme="@style/Theme.Translucent.NoTitleBar"
+ android:label="@string/dump_heap_title"
+ android:finishOnCloseSystemDialogs="true"
+ android:noHistory="true"
+ android:excludeFromRecents="true"
+ android:process=":ui">
</activity>
<provider android:name="com.android.server.am.DumpHeapProvider"
- android:authorities="com.android.server.heapdump"
- android:grantUriPermissions="true"
- android:multiprocess="false"
- android:singleUser="true" />
+ android:authorities="com.android.server.heapdump"
+ android:grantUriPermissions="true"
+ android:multiprocess="false"
+ android:singleUser="true" />
<activity android:name="android.accounts.ChooseAccountActivity"
- android:excludeFromRecents="true"
- android:exported="true"
- android:theme="@style/Theme.DeviceDefault.Light.Dialog"
- android:label="@string/choose_account_label"
- android:process=":ui"
- android:visibleToInstantApps="true">
+ android:excludeFromRecents="true"
+ android:exported="true"
+ android:theme="@style/Theme.DeviceDefault.Light.Dialog"
+ android:label="@string/choose_account_label"
+ android:process=":ui"
+ android:visibleToInstantApps="true">
</activity>
<activity android:name="android.accounts.ChooseTypeAndAccountActivity"
- android:excludeFromRecents="true"
- android:exported="true"
- android:theme="@style/Theme.DeviceDefault.Light.Dialog"
- android:label="@string/choose_account_label"
- android:process=":ui"
- android:visibleToInstantApps="true">
+ android:excludeFromRecents="true"
+ android:exported="true"
+ android:theme="@style/Theme.DeviceDefault.Light.Dialog"
+ android:label="@string/choose_account_label"
+ android:process=":ui"
+ android:visibleToInstantApps="true">
</activity>
<activity android:name="android.accounts.ChooseAccountTypeActivity"
- android:excludeFromRecents="true"
- android:theme="@style/Theme.DeviceDefault.Light.Dialog"
- android:label="@string/choose_account_label"
- android:process=":ui"
- android:visibleToInstantApps="true">
+ android:excludeFromRecents="true"
+ android:theme="@style/Theme.DeviceDefault.Light.Dialog"
+ android:label="@string/choose_account_label"
+ android:process=":ui"
+ android:visibleToInstantApps="true">
</activity>
<activity android:name="android.accounts.CantAddAccountActivity"
- android:excludeFromRecents="true"
- android:exported="true"
- android:theme="@style/Theme.DeviceDefault.Light.Dialog.NoActionBar"
- android:process=":ui">
+ android:excludeFromRecents="true"
+ android:exported="true"
+ android:theme="@style/Theme.DeviceDefault.Light.Dialog.NoActionBar"
+ android:process=":ui">
</activity>
<activity android:name="android.accounts.GrantCredentialsPermissionActivity"
- android:excludeFromRecents="true"
- android:exported="true"
- android:theme="@style/Theme.DeviceDefault.Light.DialogWhenLarge"
- android:process=":ui"
- android:visibleToInstantApps="true">
+ android:excludeFromRecents="true"
+ android:exported="true"
+ android:theme="@style/Theme.DeviceDefault.Light.DialogWhenLarge"
+ android:process=":ui"
+ android:visibleToInstantApps="true">
</activity>
<activity android:name="android.content.SyncActivityTooManyDeletes"
- android:theme="@style/Theme.DeviceDefault.Light.Dialog"
- android:label="@string/sync_too_many_deletes"
- android:process=":ui">
+ android:theme="@style/Theme.DeviceDefault.Light.Dialog"
+ android:label="@string/sync_too_many_deletes"
+ android:process=":ui">
</activity>
<activity android:name="com.android.internal.app.ShutdownActivity"
- android:permission="android.permission.SHUTDOWN"
- android:theme="@style/Theme.NoDisplay"
- android:excludeFromRecents="true">
+ android:permission="android.permission.SHUTDOWN"
+ android:theme="@style/Theme.NoDisplay"
+ android:excludeFromRecents="true">
<intent-filter>
<action android:name="com.android.internal.intent.action.REQUEST_SHUTDOWN" />
<category android:name="android.intent.category.DEFAULT" />
@@ -3530,9 +3549,9 @@
</activity>
<activity android:name="com.android.internal.app.NetInitiatedActivity"
- android:theme="@style/Theme.DeviceDefault.Light.Dialog.Alert"
- android:excludeFromRecents="true"
- android:process=":ui">
+ android:theme="@style/Theme.DeviceDefault.Light.Dialog.Alert"
+ android:excludeFromRecents="true"
+ android:process=":ui">
</activity>
<activity android:name="com.android.internal.app.SystemUserHomeActivity"
@@ -3549,9 +3568,9 @@
<!-- Activity to prompt user if it's ok to create a new user sandbox for a
specified account. -->
<activity android:name="com.android.internal.app.ConfirmUserCreationActivity"
- android:excludeFromRecents="true"
- android:process=":ui"
- android:theme="@style/Theme.DeviceDefault.Light.Dialog.Alert">
+ android:excludeFromRecents="true"
+ android:process=":ui"
+ android:theme="@style/Theme.DeviceDefault.Light.Dialog.Alert">
<intent-filter android:priority="1000">
<action android:name="android.os.action.CREATE_USER" />
<category android:name="android.intent.category.DEFAULT" />
@@ -3559,9 +3578,9 @@
</activity>
<activity android:name="com.android.internal.app.UnlaunchableAppActivity"
- android:theme="@style/Theme.DeviceDefault.Light.Dialog.Alert"
- android:excludeFromRecents="true"
- android:process=":ui">
+ android:theme="@style/Theme.DeviceDefault.Light.Dialog.Alert"
+ android:excludeFromRecents="true"
+ android:process=":ui">
</activity>
<activity android:name="com.android.settings.notification.NotificationAccessConfirmationActivity"
@@ -3570,14 +3589,14 @@
</activity>
<receiver android:name="com.android.server.BootReceiver"
- android:systemUserOnly="true">
+ android:systemUserOnly="true">
<intent-filter android:priority="1000">
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<receiver android:name="com.android.server.updates.CertPinInstallReceiver"
- android:permission="android.permission.UPDATE_CONFIG">
+ android:permission="android.permission.UPDATE_CONFIG">
<intent-filter>
<action android:name="android.intent.action.UPDATE_PINS" />
<data android:scheme="content" android:host="*" android:mimeType="*/*" />
@@ -3585,7 +3604,7 @@
</receiver>
<receiver android:name="com.android.server.updates.IntentFirewallInstallReceiver"
- android:permission="android.permission.UPDATE_CONFIG">
+ android:permission="android.permission.UPDATE_CONFIG">
<intent-filter>
<action android:name="android.intent.action.UPDATE_INTENT_FIREWALL" />
<data android:scheme="content" android:host="*" android:mimeType="*/*" />
@@ -3593,7 +3612,7 @@
</receiver>
<receiver android:name="com.android.server.updates.SmsShortCodesInstallReceiver"
- android:permission="android.permission.UPDATE_CONFIG">
+ android:permission="android.permission.UPDATE_CONFIG">
<intent-filter>
<action android:name="android.intent.action.UPDATE_SMS_SHORT_CODES" />
<data android:scheme="content" android:host="*" android:mimeType="*/*" />
@@ -3601,7 +3620,7 @@
</receiver>
<receiver android:name="com.android.server.updates.ApnDbInstallReceiver"
- android:permission="android.permission.UPDATE_CONFIG">
+ android:permission="android.permission.UPDATE_CONFIG">
<intent-filter>
<action android:name="com.android.internal.intent.action.UPDATE_APN_DB" />
<data android:scheme="content" android:host="*" android:mimeType="*/*" />
@@ -3609,7 +3628,7 @@
</receiver>
<receiver android:name="com.android.server.updates.CarrierProvisioningUrlsInstallReceiver"
- android:permission="android.permission.UPDATE_CONFIG">
+ android:permission="android.permission.UPDATE_CONFIG">
<intent-filter>
<action android:name="android.intent.action.UPDATE_CARRIER_PROVISIONING_URLS" />
<data android:scheme="content" android:host="*" android:mimeType="*/*" />
@@ -3617,7 +3636,7 @@
</receiver>
<receiver android:name="com.android.server.updates.TzDataInstallReceiver"
- android:permission="android.permission.UPDATE_CONFIG">
+ android:permission="android.permission.UPDATE_CONFIG">
<intent-filter>
<action android:name="android.intent.action.UPDATE_TZDATA" />
<data android:scheme="content" android:host="*" android:mimeType="*/*" />
@@ -3625,7 +3644,7 @@
</receiver>
<receiver android:name="com.android.server.updates.CertificateTransparencyLogInstallReceiver"
- android:permission="android.permission.UPDATE_CONFIG">
+ android:permission="android.permission.UPDATE_CONFIG">
<intent-filter>
<action android:name="android.intent.action.UPDATE_CT_LOGS" />
<data android:scheme="content" android:host="*" android:mimeType="*/*" />
@@ -3649,7 +3668,7 @@
</receiver>
<receiver android:name="com.android.server.MasterClearReceiver"
- android:permission="android.permission.MASTER_CLEAR">
+ android:permission="android.permission.MASTER_CLEAR">
<intent-filter
android:priority="100" >
<!-- For Checkin, Settings, etc.: action=FACTORY_RESET -->
@@ -3665,12 +3684,12 @@
</receiver>
<service android:name="android.hardware.location.GeofenceHardwareService"
- android:permission="android.permission.LOCATION_HARDWARE"
- android:exported="false" />
+ android:permission="android.permission.LOCATION_HARDWARE"
+ android:exported="false" />
<service android:name="com.android.internal.backup.LocalTransportService"
- android:permission="android.permission.CONFIRM_FULL_BACKUP"
- android:exported="false">
+ android:permission="android.permission.CONFIRM_FULL_BACKUP"
+ android:exported="false">
<intent-filter>
<action android:name="android.backup.TRANSPORT_HOST" />
</intent-filter>
diff --git a/tests/tests/provider/src/android/provider/cts/FontsContractTest.java b/tests/tests/provider/src/android/provider/cts/FontsContractTest.java
index 745a490..fd25f99e 100644
--- a/tests/tests/provider/src/android/provider/cts/FontsContractTest.java
+++ b/tests/tests/provider/src/android/provider/cts/FontsContractTest.java
@@ -17,44 +17,45 @@
package android.provider.cts;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.fail;
import android.app.Instrumentation;
-import android.content.pm.Signature;
+import android.content.Context;
+import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.PackageInfo;
-import android.content.Context;
+import android.content.pm.Signature;
import android.graphics.Typeface;
-import android.graphics.fonts.FontVariationAxis;
+import android.os.Handler;
+import android.os.Looper;
import android.provider.FontRequest;
import android.provider.FontsContract;
+import android.provider.FontsContract.Columns;
import android.provider.FontsContract.FontFamilyResult;
import android.provider.FontsContract.FontInfo;
-import android.provider.FontsContract.Columns;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
-import java.util.List;
-import java.util.ArrayList;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.locks.Condition;
-import java.util.concurrent.locks.ReentrantLock;
+
+import com.android.internal.annotations.GuardedBy;
+
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.ArrayList;
+import java.util.List;
+
@SmallTest
@RunWith(AndroidJUnit4.class)
public class FontsContractTest {
private static final String AUTHORITY = "android.provider.fonts.cts.font";
private static final String PACKAGE = "android.provider.cts";
- private static long TIMEOUT_MILLIS = 1000;
-
// Signature to be used for authentication to access content provider.
// In this test case, the content provider and consumer live in the same package, self package's
// signature works.
@@ -76,12 +77,115 @@
}
}
+ private Instrumentation mInstrumentation;
+ private Context mContext;
+ private Handler mMainThreadHandler;
+
+ @Before
+ public void setUp() throws Exception {
+ mMainThreadHandler = new Handler(Looper.getMainLooper());
+ mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ mContext = mInstrumentation.getTargetContext();
+ MockFontProvider.prepareFontFiles(
+ InstrumentationRegistry.getInstrumentation().getTargetContext());
+ }
+
+ @After
+ public void tearDown() {
+ mMainThreadHandler = null;
+ MockFontProvider.cleanUpFontFiles(
+ InstrumentationRegistry.getInstrumentation().getTargetContext());
+ }
+
+ private static class TestCallback extends FontsContract.FontRequestCallback {
+ private final Object mLock = new Object();
+ @GuardedBy("mLock")
+ private Typeface mTypeface;
+ @GuardedBy("mLock")
+ private int mFailedReason;
+ @GuardedBy("mLock")
+ private int mSuccessCallCount;
+ @GuardedBy("mLock")
+ private int mFailedCallCount;
+
+ public void onTypefaceRetrieved(Typeface typeface) {
+ synchronized(mLock) {
+ mTypeface = typeface;
+ mSuccessCallCount++;
+ }
+ }
+
+ public void onTypefaceRequestFailed(int reason) {
+ synchronized(mLock) {
+ mFailedCallCount++;
+ mFailedReason = reason;
+ }
+ }
+
+ public Typeface getTypeface() {
+ synchronized(mLock) {
+ return mTypeface;
+ }
+ }
+
+ public int getFailedReason() {
+ synchronized(mLock) {
+ return mFailedReason;
+ }
+ }
+
+ public int getSuccessCallCount() {
+ synchronized(mLock) {
+ return mSuccessCallCount;
+ }
+ }
+
+ public int getFailedCallCount() {
+ synchronized(mLock) {
+ return mFailedCallCount;
+ }
+ }
+ }
+
+
+ @Test
+ public void requestFont() throws NameNotFoundException {
+ FontRequest request = new FontRequest(AUTHORITY, PACKAGE,
+ MockFontProvider.SINGLE_FONT_FAMILY_QUERY, SIGNATURE);
+ TestCallback callback = new TestCallback();
+
+ mInstrumentation.runOnMainSync(() -> FontsContract.requestFonts(
+ mContext, request, mMainThreadHandler, null, callback));
+
+ mInstrumentation.waitForIdleSync();
+ assertEquals(1, callback.getSuccessCallCount());
+ assertEquals(0, callback.getFailedCallCount());
+ assertNotNull(callback.mTypeface);
+ }
+
+ @Test
+ public void requestFontNegativeErrorCode() throws NameNotFoundException {
+ FontRequest request = new FontRequest(AUTHORITY, PACKAGE,
+ MockFontProvider.NEGATIVE_ERROR_CODE_QUERY, SIGNATURE);
+ TestCallback callback = new TestCallback();
+
+ mInstrumentation.runOnMainSync(() -> FontsContract.requestFonts(
+ mContext, request, mMainThreadHandler, null, callback));
+
+ mInstrumentation.waitForIdleSync();
+ assertNull(callback.mTypeface);
+ assertEquals(1, callback.getFailedCallCount());
+ assertEquals(0, callback.getSuccessCallCount());
+ assertEquals(FontsContract.FontRequestCallback.FAIL_REASON_FONT_LOAD_ERROR,
+ callback.getFailedReason());
+ }
+
@Test
public void querySingleFont() throws NameNotFoundException {
- Context ctx = InstrumentationRegistry.getInstrumentation().getTargetContext();
- FontRequest request = new FontRequest(AUTHORITY, PACKAGE, "singleFontFamily", SIGNATURE);
+ FontRequest request = new FontRequest(AUTHORITY, PACKAGE,
+ MockFontProvider.SINGLE_FONT_FAMILY_QUERY, SIGNATURE);
FontFamilyResult result = FontsContract.fetchFonts(
- ctx, null /* cancellation signal */, request);
+ mContext, null /* cancellation signal */, request);
assertNotNull(result);
assertEquals(FontFamilyResult.STATUS_OK, result.getStatusCode());
@@ -90,17 +194,15 @@
FontInfo font = fonts[0];
assertNotNull(font.getUri());
assertEquals(Columns.RESULT_CODE_OK, font.getResultCode());
- // TODO: add more test cases for FontInfo members once the MockFontProvider becomes
- // configurable.
- assertNotNull(FontsContract.buildTypeface(ctx, null /* cancellation signal */, fonts));
+ assertNotNull(FontsContract.buildTypeface(mContext, null /* cancellation signal */, fonts));
}
@Test
public void queryMultipleFont() throws NameNotFoundException {
- Context ctx = InstrumentationRegistry.getInstrumentation().getTargetContext();
- FontRequest request = new FontRequest(AUTHORITY, PACKAGE, "multipleFontFamily", SIGNATURE);
+ FontRequest request = new FontRequest(AUTHORITY, PACKAGE,
+ MockFontProvider.MULTIPLE_FAMILY_QUERY, SIGNATURE);
FontFamilyResult result = FontsContract.fetchFonts(
- ctx, null /* cancellation signal */, request);
+ mContext, null /* cancellation signal */, request);
assertNotNull(result);
assertEquals(FontFamilyResult.STATUS_OK, result.getStatusCode());
@@ -110,17 +212,122 @@
assertNotNull(font.getUri());
assertEquals(Columns.RESULT_CODE_OK, font.getResultCode());
}
- // TODO: add more test cases for FontInfo members once the MockFontProvider becomes
- // configuarable.
- assertNotNull(FontsContract.buildTypeface(ctx, null /* cancellation signal */, fonts));
+ assertNotNull(FontsContract.buildTypeface(mContext, null /* cancellation signal */, fonts));
+ }
+
+ @Test
+ public void queryAttributes() throws NameNotFoundException {
+ FontRequest request = new FontRequest(AUTHORITY, PACKAGE,
+ MockFontProvider.SINGLE_FONT_FAMILY2_QUERY, SIGNATURE);
+ FontFamilyResult result = FontsContract.fetchFonts(
+ mContext, null /* cancellation signal */, request);
+ assertNotNull(result);
+ assertEquals(FontFamilyResult.STATUS_OK, result.getStatusCode());
+
+ FontInfo[] fonts = result.getFonts();
+ assertEquals(1, fonts.length);
+ FontInfo font = fonts[0];
+ assertNotNull(font.getUri());
+ assertEquals(700, font.getWeight());
+ assertEquals(1, font.getAxes().length);
+ assertTrue(font.isItalic());
+ assertEquals(Columns.RESULT_CODE_OK, font.getResultCode());
+ assertNotNull(FontsContract.buildTypeface(mContext, null /* cancellation signal */, fonts));
+ }
+
+ @Test
+ public void queryNotFound() throws NameNotFoundException {
+ FontRequest request = new FontRequest(AUTHORITY, PACKAGE,
+ MockFontProvider.NOT_FOUND_QUERY, SIGNATURE);
+ FontFamilyResult result = FontsContract.fetchFonts(
+ mContext, null /* cancellation signal */, request);
+ assertNotNull(result);
+ assertEquals(FontFamilyResult.STATUS_OK, result.getStatusCode());
+
+ FontInfo[] fonts = result.getFonts();
+ assertEquals(1, fonts.length);
+ FontInfo font = fonts[0];
+ assertEquals(Columns.RESULT_CODE_FONT_NOT_FOUND, font.getResultCode());
+ assertNull(FontsContract.buildTypeface(mContext, null /* cancellation signal */, fonts));
+ }
+
+ @Test
+ public void queryUnavailable() throws NameNotFoundException {
+ FontRequest request = new FontRequest(AUTHORITY, PACKAGE,
+ MockFontProvider.UNAVAILABLE_QUERY, SIGNATURE);
+ FontFamilyResult result = FontsContract.fetchFonts(
+ mContext, null /* cancellation signal */, request);
+ assertNotNull(result);
+ assertEquals(FontFamilyResult.STATUS_OK, result.getStatusCode());
+
+ FontInfo[] fonts = result.getFonts();
+ assertEquals(1, fonts.length);
+ FontInfo font = fonts[0];
+ assertEquals(Columns.RESULT_CODE_FONT_UNAVAILABLE, font.getResultCode());
+ assertNull(FontsContract.buildTypeface(mContext, null /* cancellation signal */, fonts));
+ }
+
+ @Test
+ public void queryMalformed() throws NameNotFoundException {
+ FontRequest request = new FontRequest(AUTHORITY, PACKAGE,
+ MockFontProvider.MALFORMED_QUERY, SIGNATURE);
+ FontFamilyResult result = FontsContract.fetchFonts(
+ mContext, null /* cancellation signal */, request);
+ assertNotNull(result);
+ assertEquals(FontFamilyResult.STATUS_OK, result.getStatusCode());
+
+ FontInfo[] fonts = result.getFonts();
+ assertEquals(1, fonts.length);
+ FontInfo font = fonts[0];
+ assertEquals(Columns.RESULT_CODE_MALFORMED_QUERY, font.getResultCode());
+ assertNull(FontsContract.buildTypeface(mContext, null /* cancellation signal */, fonts));
+ }
+
+ @Test
+ public void queryMultipleOneNotFound() throws NameNotFoundException {
+ FontRequest request = new FontRequest(AUTHORITY, PACKAGE,
+ MockFontProvider.NOT_FOUND_SECOND_QUERY, SIGNATURE);
+ FontFamilyResult result = FontsContract.fetchFonts(
+ mContext, null /* cancellation signal */, request);
+ assertNotNull(result);
+ assertEquals(FontFamilyResult.STATUS_OK, result.getStatusCode());
+
+ FontInfo[] fonts = result.getFonts();
+ assertEquals(2, fonts.length);
+ FontInfo font = fonts[0];
+ assertEquals(Columns.RESULT_CODE_OK, font.getResultCode());
+ FontInfo font2 = fonts[1];
+ assertEquals(Columns.RESULT_CODE_FONT_NOT_FOUND, font2.getResultCode());
+ assertNotNull(FontsContract.buildTypeface(mContext, null /* cancellation signal */, fonts));
+ }
+
+ @Test
+ public void queryMandatoryFieldsOnly() throws NameNotFoundException {
+ FontRequest request = new FontRequest(AUTHORITY, PACKAGE,
+ MockFontProvider.MANDATORY_FIELDS_ONLY_QUERY, SIGNATURE);
+ FontFamilyResult result = FontsContract.fetchFonts(
+ mContext, null /* cancellation signal */, request);
+ assertNotNull(result);
+ assertEquals(FontFamilyResult.STATUS_OK, result.getStatusCode());
+
+ FontInfo[] fonts = result.getFonts();
+ assertEquals(1, fonts.length);
+ FontInfo font = fonts[0];
+ assertNotNull(font.getUri());
+ assertEquals(400, font.getWeight());
+ assertNull(font.getAxes());
+ assertFalse(font.isItalic());
+ assertEquals(Columns.RESULT_CODE_OK, font.getResultCode());
+ assertNotNull(FontsContract.buildTypeface(mContext, null /* cancellation signal */, fonts));
}
@Test
public void restrictContextRejection() throws NameNotFoundException {
- Context ctx = InstrumentationRegistry.getInstrumentation().getTargetContext();
- Context restrictedContext = ctx.createPackageContext(PACKAGE, Context.CONTEXT_RESTRICTED);
+ Context restrictedContext = mContext.createPackageContext(
+ PACKAGE, Context.CONTEXT_RESTRICTED);
- FontRequest request = new FontRequest(AUTHORITY, PACKAGE, "singleFontFamily", SIGNATURE);
+ FontRequest request = new FontRequest(AUTHORITY, PACKAGE,
+ MockFontProvider.SINGLE_FONT_FAMILY_QUERY, SIGNATURE);
// Rejected if restricted context is used.
FontFamilyResult result = FontsContract.fetchFonts(
@@ -128,11 +335,9 @@
assertEquals(FontFamilyResult.STATUS_REJECTED, result.getStatusCode());
// Even if you have a result, buildTypeface should fail with restricted context.
- result = FontsContract.fetchFonts(ctx, null /* cancellation signal */, request);
+ result = FontsContract.fetchFonts(mContext, null /* cancellation signal */, request);
assertEquals(FontFamilyResult.STATUS_OK, result.getStatusCode());
assertNull(FontsContract.buildTypeface(
restrictedContext, null /* cancellation signal */, result.getFonts()));
}
-
- // TODO: Add more test case.
}
diff --git a/tests/tests/provider/src/android/provider/cts/MockFontProvider.java b/tests/tests/provider/src/android/provider/cts/MockFontProvider.java
index a348de2..9d925c7 100644
--- a/tests/tests/provider/src/android/provider/cts/MockFontProvider.java
+++ b/tests/tests/provider/src/android/provider/cts/MockFontProvider.java
@@ -21,25 +21,22 @@
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
-import android.content.res.AssetFileDescriptor;
import android.content.res.AssetManager;
import android.database.Cursor;
import android.database.MatrixCursor;
-import android.graphics.fonts.FontVariationAxis;
import android.net.Uri;
-import android.os.CancellationSignal;
import android.os.ParcelFileDescriptor;
-import android.util.SparseArray;
-import java.util.Collections;
-import java.util.Map;
-import java.util.HashMap;
import java.io.File;
-import java.nio.file.Files;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
-import java.io.FileNotFoundException;
+import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
public class MockFontProvider extends ContentProvider {
final static String AUTHORITY = "android.provider.fonts.cts.font";
@@ -50,9 +47,20 @@
private static final int SAMPLE_FONT_FILE_0_ID = 0;
private static final int SAMPLE_FONT_FILE_1_ID = 1;
+ static final String SINGLE_FONT_FAMILY_QUERY = "singleFontFamily";
+ static final String SINGLE_FONT_FAMILY2_QUERY = "singleFontFamily2";
+ static final String MULTIPLE_FAMILY_QUERY = "multipleFontFamily";
+ static final String NOT_FOUND_QUERY = "notFound";
+ static final String UNAVAILABLE_QUERY = "unavailable";
+ static final String MALFORMED_QUERY = "malformed";
+ static final String NOT_FOUND_SECOND_QUERY = "notFoundSecond";
+ static final String NOT_FOUND_THIRD_QUERY = "notFoundThird";
+ static final String NEGATIVE_ERROR_CODE_QUERY = "negativeCode";
+ static final String MANDATORY_FIELDS_ONLY_QUERY = "mandatoryFields";
+
static class Font {
public Font(int id, int fileId, int ttcIndex, String varSettings, int weight, int italic,
- int resultCode) {
+ int resultCode, boolean returnAllFields) {
mId = id;
mFileId = fileId;
mTtcIndex = ttcIndex;
@@ -60,6 +68,7 @@
mWeight = weight;
mItalic = italic;
mResultCode = resultCode;
+ mReturnAllFields = returnAllFields;
}
public int getId() {
@@ -90,6 +99,10 @@
return mFileId;
}
+ public boolean isReturnAllFields() {
+ return mReturnAllFields;
+ }
+
private int mId;
private int mFileId;
private int mTtcIndex;
@@ -97,6 +110,7 @@
private int mWeight;
private int mItalic;
private int mResultCode;
+ private final boolean mReturnAllFields;
};
private static Map<String, Font[]> QUERY_MAP;
@@ -104,21 +118,71 @@
HashMap<String, Font[]> map = new HashMap<>();
int id = 0;
- map.put("singleFontFamily", new Font[] {
- new Font(id++, SAMPLE_FONT_FILE_0_ID, 0, null, 400, 0, Columns.RESULT_CODE_OK),
+ map.put(SINGLE_FONT_FAMILY_QUERY, new Font[] {
+ new Font(id++, SAMPLE_FONT_FILE_0_ID, 0, null, 400, 0, Columns.RESULT_CODE_OK, true),
});
- map.put("multipleFontFamily", new Font[] {
- new Font(id++, SAMPLE_FONT_FILE_0_ID, 0, null, 400, 0, Columns.RESULT_CODE_OK),
- new Font(id++, SAMPLE_FONT_FILE_1_ID, 0, null, 400, 0, Columns.RESULT_CODE_OK),
- new Font(id++, SAMPLE_FONT_FILE_0_ID, 0, null, 700, 1, Columns.RESULT_CODE_OK),
- new Font(id++, SAMPLE_FONT_FILE_1_ID, 0, null, 700, 1, Columns.RESULT_CODE_OK),
+ map.put(MULTIPLE_FAMILY_QUERY, new Font[] {
+ new Font(id++, SAMPLE_FONT_FILE_0_ID, 0, null, 400, 0, Columns.RESULT_CODE_OK, true),
+ new Font(id++, SAMPLE_FONT_FILE_1_ID, 0, null, 400, 0, Columns.RESULT_CODE_OK, true),
+ new Font(id++, SAMPLE_FONT_FILE_0_ID, 0, null, 700, 1, Columns.RESULT_CODE_OK, true),
+ new Font(id++, SAMPLE_FONT_FILE_1_ID, 0, null, 700, 1, Columns.RESULT_CODE_OK, true),
});
+ map.put(SINGLE_FONT_FAMILY2_QUERY, new Font[] {
+ new Font(id++, SAMPLE_FONT_FILE_0_ID, 0, "'wght' 100", 700, 1,
+ Columns.RESULT_CODE_OK, true),
+ });
+
+ map.put(NOT_FOUND_QUERY, new Font[] {
+ new Font(0, 0, 0, null, 400, 0, Columns.RESULT_CODE_FONT_NOT_FOUND, true),
+ });
+
+ map.put(UNAVAILABLE_QUERY, new Font[] {
+ new Font(0, 0, 0, null, 400, 0, Columns.RESULT_CODE_FONT_UNAVAILABLE, true),
+ });
+
+ map.put(MALFORMED_QUERY, new Font[] {
+ new Font(0, 0, 0, null, 400, 0, Columns.RESULT_CODE_MALFORMED_QUERY, true),
+ });
+
+ map.put(NOT_FOUND_SECOND_QUERY, new Font[] {
+ new Font(id++, SAMPLE_FONT_FILE_0_ID, 0, null, 700, 0, Columns.RESULT_CODE_OK,
+ true),
+ new Font(0, 0, 0, null, 400, 0, Columns.RESULT_CODE_FONT_NOT_FOUND, true),
+ });
+
+ map.put(NOT_FOUND_THIRD_QUERY, new Font[] {
+ new Font(id++, SAMPLE_FONT_FILE_0_ID, 0, null, 700, 0, Columns.RESULT_CODE_OK,
+ true),
+ new Font(0, 0, 0, null, 400, 0, Columns.RESULT_CODE_FONT_NOT_FOUND, true),
+ new Font(id++, SAMPLE_FONT_FILE_0_ID, 0, null, 700, 0, Columns.RESULT_CODE_OK,
+ true),
+ });
+
+ map.put(NEGATIVE_ERROR_CODE_QUERY, new Font[] {
+ new Font(id++, SAMPLE_FONT_FILE_0_ID, 0, null, 700, 0, -5, true),
+ });
+
+ map.put(MANDATORY_FIELDS_ONLY_QUERY, new Font[] {
+ new Font(id++, SAMPLE_FONT_FILE_0_ID, 0, null, 400, 0,
+ Columns.RESULT_CODE_OK, false),
+ });
+
+
QUERY_MAP = Collections.unmodifiableMap(map);
}
private static Cursor buildCursor(Font[] in) {
+ if (!in[0].mReturnAllFields) {
+ MatrixCursor cursor = new MatrixCursor(new String[] { Columns._ID, Columns.FILE_ID });
+ for (Font font : in) {
+ MatrixCursor.RowBuilder builder = cursor.newRow();
+ builder.add(Columns._ID, font.getId());
+ builder.add(Columns.FILE_ID, font.getFileId());
+ }
+ return cursor;
+ }
MatrixCursor cursor = new MatrixCursor(new String[] {
Columns._ID, Columns.TTC_INDEX, Columns.VARIATION_SETTINGS, Columns.WEIGHT,
Columns.ITALIC, Columns.RESULT_CODE, Columns.FILE_ID});
@@ -135,35 +199,42 @@
return cursor;
}
- public MockFontProvider() {
- }
-
- @Override
- public ParcelFileDescriptor openFile(Uri uri, String mode) {
- final int id = (int)ContentUris.parseId(uri);
- final File targetFile = getCopiedFile(FONT_FILES[id]);
- try {
- return ParcelFileDescriptor.open(targetFile, ParcelFileDescriptor.MODE_READ_ONLY);
- } catch (FileNotFoundException e) {
- throw new RuntimeException(e);
- }
- }
-
- public File getCopiedFile(String path) {
- return new File(getContext().getFilesDir(), path);
- }
-
- @Override
- public boolean onCreate() {
- final AssetManager mgr = getContext().getAssets();
+ public static void prepareFontFiles(Context context) {
+ final AssetManager mgr = context.getAssets();
for (String path : FONT_FILES) {
try (InputStream is = mgr.open(path)) {
- Files.copy(is, getCopiedFile(path).toPath(), StandardCopyOption.REPLACE_EXISTING);
+ Files.copy(is, getCopiedFile(context, path).toPath(),
+ StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
- // TODO: do we have good time to remove above files from files directory?
+ }
+
+ public static void cleanUpFontFiles(Context context) {
+ for (String file : FONT_FILES) {
+ getCopiedFile(context, file).delete();
+ }
+ }
+
+ public static File getCopiedFile(Context context, String path) {
+ return new File(context.getFilesDir(), path);
+ }
+
+ @Override
+ public ParcelFileDescriptor openFile(Uri uri, String mode) {
+ final int id = (int) ContentUris.parseId(uri);
+ final File targetFile = getCopiedFile(getContext(), FONT_FILES[id]);
+ try {
+ return ParcelFileDescriptor.open(targetFile, ParcelFileDescriptor.MODE_READ_ONLY);
+ } catch (FileNotFoundException e) {
+ throw new RuntimeException(
+ "Failed to find font file. Did you forget to call prepareFontFiles in setUp?");
+ }
+ }
+
+ @Override
+ public boolean onCreate() {
return true;
}
diff --git a/tests/tests/telecom/src/android/telecom/cts/ExtendedInCallServiceTest.java b/tests/tests/telecom/src/android/telecom/cts/ExtendedInCallServiceTest.java
index a739f23..ca12b34 100644
--- a/tests/tests/telecom/src/android/telecom/cts/ExtendedInCallServiceTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/ExtendedInCallServiceTest.java
@@ -103,11 +103,14 @@
assertCallState(call, Call.STATE_DIALING);
final int currentInvokeCount = mOnCallAudioStateChangedCounter.getInvokeCount();
-
+ mOnCallAudioStateChangedCounter.waitForCount(WAIT_FOR_STATE_CHANGE_TIMEOUT_MS);
+ CallAudioState callAudioState =
+ (CallAudioState) mOnCallAudioStateChangedCounter.getArgs(0)[0];
// We need to check what audio routes are available. If speaker and either headset or
// earpiece aren't available, then we should skip this test.
- int availableRoutes = connection.getCallAudioState().getSupportedRouteMask();
+
+ int availableRoutes = callAudioState.getSupportedRouteMask();
if ((availableRoutes & CallAudioState.ROUTE_SPEAKER) == 0) {
return;
}
diff --git a/tests/tests/telephony/src/android/telephony/cts/VisualVoicemailServiceTest.java b/tests/tests/telephony/src/android/telephony/cts/VisualVoicemailServiceTest.java
index 57ec1ae..7fd12d9 100644
--- a/tests/tests/telephony/src/android/telephony/cts/VisualVoicemailServiceTest.java
+++ b/tests/tests/telephony/src/android/telephony/cts/VisualVoicemailServiceTest.java
@@ -40,7 +40,6 @@
import android.telephony.SmsManager;
import android.telephony.SmsMessage;
import android.telephony.TelephonyManager;
-import android.telephony.VisualVoicemailService;
import android.telephony.VisualVoicemailSms;
import android.telephony.VisualVoicemailSmsFilterSettings;
import android.test.InstrumentationTestCase;
@@ -527,7 +526,7 @@
MockVisualVoicemailService.setSmsFuture(future);
setupSmsReceiver(text);
- try (SentSmsObserver observer = new SentSmsObserver(mContext)) {
+ try (SentSmsObserver observer = new SentSmsObserver(mContext, text)) {
mTelephonyManager
.sendVisualVoicemailSms(mPhoneNumber,0, text, null);
@@ -654,12 +653,14 @@
private static class SentSmsObserver extends ContentObserver implements AutoCloseable {
private final Context mContext;
+ private final String mText;
public CompletableFuture<Boolean> mFuture = new CompletableFuture<>();
- public SentSmsObserver(Context context) {
+ public SentSmsObserver(Context context, String text) {
super(new Handler(Looper.getMainLooper()));
mContext = context;
+ mText = text;
mContext.getContentResolver().registerContentObserver(Sms.CONTENT_URI, true, this);
}
@@ -678,14 +679,15 @@
@Override
public void onChange(boolean selfChange, Uri uri) {
try (Cursor cursor = mContext.getContentResolver()
- .query(uri, new String[] {Sms.TYPE}, null, null, null)) {
+ .query(uri, new String[] {Sms.TYPE, Sms.BODY}, null, null, null)) {
if (cursor == null){
return;
}
if (!cursor.moveToFirst()){
return;
}
- if (cursor.getInt(0) == Sms.MESSAGE_TYPE_SENT) {
+ if (cursor.getInt(0) == Sms.MESSAGE_TYPE_SENT && TextUtils
+ .equals(cursor.getString(1), mText)) {
mFuture.complete(true);
}
} catch (SQLiteException e) {
diff --git a/tests/tests/text/src/android/text/cts/StaticLayoutTest.java b/tests/tests/text/src/android/text/cts/StaticLayoutTest.java
index 308e750..df60064 100644
--- a/tests/tests/text/src/android/text/cts/StaticLayoutTest.java
+++ b/tests/tests/text/src/android/text/cts/StaticLayoutTest.java
@@ -227,6 +227,16 @@
}
}
+ @Test
+ public void testBuilder_setJustificationMode() {
+ StaticLayout.Builder builder = StaticLayout.Builder.obtain(LAYOUT_TEXT, 0,
+ LAYOUT_TEXT.length(), mDefaultPaint, DEFAULT_OUTER_WIDTH);
+ builder.setJustificationMode(Layout.JUSTIFICATION_MODE_INTER_WORD);
+ StaticLayout layout = builder.build();
+ // Hard to expect the justification result. Just make sure the final layout is created
+ // without causing any exceptions.
+ assertNotNull(layout);
+ }
/*
* Get the line number corresponding to the specified vertical position.
* If you ask for a position above 0, you get 0. above 0 means pixel above the fire line
diff --git a/tests/tests/text/src/android/text/cts/TextUtilsTest.java b/tests/tests/text/src/android/text/cts/TextUtilsTest.java
index 979dc15..d549cfa 100644
--- a/tests/tests/text/src/android/text/cts/TextUtilsTest.java
+++ b/tests/tests/text/src/android/text/cts/TextUtilsTest.java
@@ -259,8 +259,6 @@
@Test
public void testConcat() {
- // issue 1695243
- // the javadoc for concat() doesn't describe the expected result when parameter is empty.
assertEquals("", TextUtils.concat().toString());
assertEquals("first", TextUtils.concat("first").toString());
@@ -291,17 +289,76 @@
assertEquals(string1, TextUtils.concat(string1));
- // issue 1695243, the javadoc for concat() doesn't describe
- // the expected result when parameters are null.
assertEquals(null, TextUtils.concat((CharSequence) null));
}
- @Test(expected=NullPointerException.class)
- public void testConcatNullArray() {
+ @Test(expected = NullPointerException.class)
+ public void testConcat_NullArray() {
TextUtils.concat((CharSequence[]) null);
}
@Test
+ public void testConcat_NullParameters() {
+ assertEquals("nullA", TextUtils.concat(null, "A"));
+ assertEquals("Anull", TextUtils.concat("A", null));
+ assertEquals("AnullB", TextUtils.concat("A", null, "B"));
+
+ final SpannableString piece = new SpannableString("A");
+ final Object span = new Object();
+ piece.setSpan(span, 0, piece.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
+ final Spanned result = (Spanned) TextUtils.concat(piece, null);
+ assertEquals("Anull", result.toString());
+ final Object[] spans = result.getSpans(0, result.length(), Object.class);
+ assertEquals(1, spans.length);
+ assertSame(span, spans[0]);
+ assertEquals(0, result.getSpanStart(spans[0]));
+ assertEquals(piece.length(), result.getSpanEnd(spans[0]));
+ }
+
+ @Test
+ public void testConcat_twoParagraphSpans() {
+ // Two paragraph spans. The first will get extended to cover the whole string and the second
+ // will be dropped.
+ final SpannableString string1 = new SpannableString("a");
+ final SpannableString string2 = new SpannableString("b");
+ final Object span1 = new Object();
+ final Object span2 = new Object();
+ string1.setSpan(span1, 0, string1.length(), Spanned.SPAN_PARAGRAPH);
+ string2.setSpan(span2, 0, string2.length(), Spanned.SPAN_PARAGRAPH);
+
+ final Spanned result = (Spanned) TextUtils.concat(string1, string2);
+ assertEquals("ab", result.toString());
+ final Object[] spans = result.getSpans(0, result.length(), Object.class);
+ assertEquals(1, spans.length);
+ assertSame(span1, spans[0]);
+ assertEquals(0, result.getSpanStart(spans[0]));
+ assertEquals(result.length(), result.getSpanEnd(spans[0]));
+ }
+
+ @Test
+ public void testConcat_oneParagraphSpanAndOneInclusiveSpan() {
+ // One paragraph span and one double-inclusive span. The first will get extended to cover
+ // the whole string and the second will be kept.
+ final SpannableString string1 = new SpannableString("a");
+ final SpannableString string2 = new SpannableString("b");
+ final Object span1 = new Object();
+ final Object span2 = new Object();
+ string1.setSpan(span1, 0, string1.length(), Spanned.SPAN_PARAGRAPH);
+ string2.setSpan(span2, 0, string2.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+
+ final Spanned result = (Spanned) TextUtils.concat(string1, string2);
+ assertEquals("ab", result.toString());
+ final Object[] spans = result.getSpans(0, result.length(), Object.class);
+ assertEquals(2, spans.length);
+ assertSame(span1, spans[0]);
+ assertEquals(0, result.getSpanStart(spans[0]));
+ assertEquals(result.length(), result.getSpanEnd(spans[0]));
+ assertSame(span2, spans[1]);
+ assertEquals(string1.length(), result.getSpanStart(spans[1]));
+ assertEquals(result.length(), result.getSpanEnd(spans[1]));
+ }
+
+ @Test
public void testCopySpansFrom() {
Object[] spans;
String text = "content";
diff --git a/tests/tests/text/src/android/text/format/cts/FormatterTest.java b/tests/tests/text/src/android/text/format/cts/FormatterTest.java
index fa11023..8c7fdc9 100644
--- a/tests/tests/text/src/android/text/format/cts/FormatterTest.java
+++ b/tests/tests/text/src/android/text/format/cts/FormatterTest.java
@@ -19,6 +19,8 @@
import static org.junit.Assert.assertEquals;
import android.content.Context;
+import android.content.res.Configuration;
+import android.os.LocaleList;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -29,6 +31,7 @@
import java.math.BigDecimal;
import java.math.MathContext;
+import java.util.Locale;
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -38,9 +41,12 @@
// test null Context
assertEquals("", Formatter.formatFileSize(null, 0));
- MathContext mc = MathContext.DECIMAL64;
- BigDecimal bd = new BigDecimal((long) 1000, mc);
- Context context = InstrumentationRegistry.getTargetContext();
+ final MathContext mc = MathContext.DECIMAL64;
+ final BigDecimal bd = new BigDecimal((long) 1000, mc);
+ final Configuration config = new Configuration();
+ config.setLocales(new LocaleList(Locale.US));
+ final Context context =
+ InstrumentationRegistry.getTargetContext().createConfigurationContext(config);
// test different long values with various length
assertEquals("0 B", Formatter.formatFileSize(context, 0));
@@ -53,6 +59,8 @@
assertEquals("0.90 kB", Formatter.formatFileSize(context, 901));
assertEquals("1.00 kB", Formatter.formatFileSize(context, bd.pow(1).longValue()));
+ assertEquals("1.50 kB", Formatter.formatFileSize(context, bd.pow(1).longValue() * 3 / 2));
+ assertEquals("12.50 kB", Formatter.formatFileSize(context, bd.pow(1).longValue() * 25 / 2));
assertEquals("1.00 MB", Formatter.formatFileSize(context, bd.pow(2).longValue()));
@@ -69,6 +77,48 @@
}
@Test
+ public void testFormatShortFileSize() {
+ // test null Context
+ assertEquals("", Formatter.formatFileSize(null, 0));
+
+ final MathContext mc = MathContext.DECIMAL64;
+ final BigDecimal bd = new BigDecimal((long) 1000, mc);
+ final Configuration config = new Configuration();
+ config.setLocales(new LocaleList(Locale.US));
+ final Context context =
+ InstrumentationRegistry.getTargetContext().createConfigurationContext(config);
+
+ // test different long values with various length
+ assertEquals("0 B", Formatter.formatShortFileSize(context, 0));
+ assertEquals("1 B", Formatter.formatShortFileSize(context, 1));
+ assertEquals("9 B", Formatter.formatShortFileSize(context, 9));
+ assertEquals("10 B", Formatter.formatShortFileSize(context, 10));
+ assertEquals("99 B", Formatter.formatShortFileSize(context, 99));
+ assertEquals("100 B", Formatter.formatShortFileSize(context, 100));
+ assertEquals("900 B", Formatter.formatShortFileSize(context, 900));
+ assertEquals("0.90 kB", Formatter.formatShortFileSize(context, 901));
+
+ assertEquals("1.0 kB", Formatter.formatShortFileSize(context, bd.pow(1).longValue()));
+ assertEquals("1.5 kB", Formatter.formatShortFileSize(context,
+ bd.pow(1).longValue() * 3 / 2));
+ assertEquals("13 kB", Formatter.formatShortFileSize(context,
+ bd.pow(1).longValue() * 25 / 2));
+
+ assertEquals("1.0 MB", Formatter.formatShortFileSize(context, bd.pow(2).longValue()));
+
+ assertEquals("1.0 GB", Formatter.formatShortFileSize(context, bd.pow(3).longValue()));
+
+ assertEquals("1.0 TB", Formatter.formatShortFileSize(context, bd.pow(4).longValue()));
+
+ assertEquals("1.0 PB", Formatter.formatShortFileSize(context, bd.pow(5).longValue()));
+
+ assertEquals("1000 PB", Formatter.formatShortFileSize(context, bd.pow(6).longValue()));
+
+ // test Negative value
+ assertEquals("-1 B", Formatter.formatShortFileSize(context, -1));
+ }
+
+ @Test
public void testFormatIpAddress() {
assertEquals("1.0.168.192", Formatter.formatIpAddress(0xC0A80001));
assertEquals("1.0.0.127", Formatter.formatIpAddress(0x7F000001));
diff --git a/tests/tests/text/src/android/text/format/cts/TimeTest.java b/tests/tests/text/src/android/text/format/cts/TimeTest.java
index 7c44c77..e085a29 100644
--- a/tests/tests/text/src/android/text/format/cts/TimeTest.java
+++ b/tests/tests/text/src/android/text/format/cts/TimeTest.java
@@ -1995,6 +1995,54 @@
}
@Test
+ public void testGetJulianMondayFromWeeksSinceEpoch() {
+ final int mondayBeforeEpoch = Time.MONDAY_BEFORE_JULIAN_EPOCH;
+ assertEquals(mondayBeforeEpoch, Time.getJulianMondayFromWeeksSinceEpoch(0));
+ assertEquals(mondayBeforeEpoch + 7, Time.getJulianMondayFromWeeksSinceEpoch(1));
+ assertEquals(mondayBeforeEpoch + 14, Time.getJulianMondayFromWeeksSinceEpoch(2));
+ assertEquals(mondayBeforeEpoch - 7, Time.getJulianMondayFromWeeksSinceEpoch(-1));
+ }
+
+ @Test
+ public void testGetWeeksSinceEpochFromJulianDay() {
+ final int epoch = Time.EPOCH_JULIAN_DAY; // a Thursday
+ assertEquals(0, Time.getWeeksSinceEpochFromJulianDay(epoch, Time.SUNDAY));
+ assertEquals(0, Time.getWeeksSinceEpochFromJulianDay(epoch, Time.MONDAY));
+ assertEquals(0, Time.getWeeksSinceEpochFromJulianDay(epoch, Time.TUESDAY));
+ assertEquals(0, Time.getWeeksSinceEpochFromJulianDay(epoch, Time.WEDNESDAY));
+ assertEquals(0, Time.getWeeksSinceEpochFromJulianDay(epoch, Time.THURSDAY));
+ assertEquals(0, Time.getWeeksSinceEpochFromJulianDay(epoch, Time.FRIDAY));
+ assertEquals(0, Time.getWeeksSinceEpochFromJulianDay(epoch, Time.SATURDAY));
+
+ final int epochFriday = epoch + 1;
+ assertEquals(0, Time.getWeeksSinceEpochFromJulianDay(epochFriday, Time.SUNDAY));
+ assertEquals(0, Time.getWeeksSinceEpochFromJulianDay(epochFriday, Time.MONDAY));
+ assertEquals(0, Time.getWeeksSinceEpochFromJulianDay(epochFriday, Time.TUESDAY));
+ assertEquals(0, Time.getWeeksSinceEpochFromJulianDay(epochFriday, Time.WEDNESDAY));
+ assertEquals(0, Time.getWeeksSinceEpochFromJulianDay(epochFriday, Time.THURSDAY));
+ assertEquals(1, Time.getWeeksSinceEpochFromJulianDay(epochFriday, Time.FRIDAY));
+ assertEquals(0, Time.getWeeksSinceEpochFromJulianDay(epochFriday, Time.SATURDAY));
+
+ final int epochSaturday = epoch + 2;
+ assertEquals(0, Time.getWeeksSinceEpochFromJulianDay(epochSaturday, Time.SUNDAY));
+ assertEquals(0, Time.getWeeksSinceEpochFromJulianDay(epochSaturday, Time.MONDAY));
+ assertEquals(0, Time.getWeeksSinceEpochFromJulianDay(epochSaturday, Time.TUESDAY));
+ assertEquals(0, Time.getWeeksSinceEpochFromJulianDay(epochSaturday, Time.WEDNESDAY));
+ assertEquals(0, Time.getWeeksSinceEpochFromJulianDay(epochSaturday, Time.THURSDAY));
+ assertEquals(1, Time.getWeeksSinceEpochFromJulianDay(epochSaturday, Time.FRIDAY));
+ assertEquals(1, Time.getWeeksSinceEpochFromJulianDay(epochSaturday, Time.SATURDAY));
+
+ final int tenWeeksLater = epochSaturday + 10 * 7;
+ assertEquals(10, Time.getWeeksSinceEpochFromJulianDay(tenWeeksLater, Time.SUNDAY));
+ assertEquals(10, Time.getWeeksSinceEpochFromJulianDay(tenWeeksLater, Time.MONDAY));
+ assertEquals(10, Time.getWeeksSinceEpochFromJulianDay(tenWeeksLater, Time.TUESDAY));
+ assertEquals(10, Time.getWeeksSinceEpochFromJulianDay(tenWeeksLater, Time.WEDNESDAY));
+ assertEquals(10, Time.getWeeksSinceEpochFromJulianDay(tenWeeksLater, Time.THURSDAY));
+ assertEquals(11, Time.getWeeksSinceEpochFromJulianDay(tenWeeksLater, Time.FRIDAY));
+ assertEquals(11, Time.getWeeksSinceEpochFromJulianDay(tenWeeksLater, Time.SATURDAY));
+ }
+
+ @Test
public void testNormalize_utc() {
Time t = new Time(Time.TIMEZONE_UTC);
Time expected = new Time(Time.TIMEZONE_UTC);
diff --git a/tests/tests/uirendering/Android.mk b/tests/tests/uirendering/Android.mk
index 956a673..52d8580 100644
--- a/tests/tests/uirendering/Android.mk
+++ b/tests/tests/uirendering/Android.mk
@@ -33,7 +33,7 @@
android-support-test \
legacy-android-test
-LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-files-under, src)
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_PACKAGE_NAME := CtsUiRenderingTestCases
diff --git a/tests/tests/uirendering/res/drawable-nodpi/pathclippingtest_torus.png b/tests/tests/uirendering/res/drawable-nodpi/pathclippingtest_torus.png
new file mode 100644
index 0000000..76dcbc5
--- /dev/null
+++ b/tests/tests/uirendering/res/drawable-nodpi/pathclippingtest_torus.png
Binary files differ
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/bitmapcomparers/MeanSquaredComparer.java b/tests/tests/uirendering/src/android/uirendering/cts/bitmapcomparers/MeanSquaredComparer.java
deleted file mode 100644
index 2b6ba0c..0000000
--- a/tests/tests/uirendering/src/android/uirendering/cts/bitmapcomparers/MeanSquaredComparer.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.uirendering.cts.bitmapcomparers;
-
-import android.graphics.Color;
-import android.util.Log;
-
-/**
- * Finds the MSE using two images.
- */
-public class MeanSquaredComparer extends BitmapComparer {
- private static final String TAG = "MeanSquared";
- private float mErrorPerPixel;
-
- /**
- * @param errorPerPixel threshold for which the test will pass/fail. This is the mean-squared
- * error averaged across all of those before comparing.
- */
- public MeanSquaredComparer(float errorPerPixel) {
- mErrorPerPixel = errorPerPixel;
- }
-
- @Override
- public boolean verifySame(int[] ideal, int[] given, int offset, int stride, int width,
- int height) {
- float totalError = getMSE(ideal, given, offset, stride, width, height);
- Log.d(TAG, "Error : " + totalError);
- return (totalError < (mErrorPerPixel));
- }
-
- /**
- * Gets the Mean Squared Error between two data sets.
- */
- public static float getMSE(int[] ideal, int[] given, int offset, int stride, int width,
- int height) {
- float totalError = 0;
-
- for (int y = 0 ; y < height ; y++) {
- for (int x = 0 ; x < width ; x++) {
- int index = indexFromXAndY(x, y, stride, offset);
- float idealSum = getColorSum(ideal[index]);
- float givenSum = getColorSum(given[index]);
- float difference = idealSum - givenSum;
- totalError += (difference * difference);
- }
- }
-
- totalError /= (width * height);
- return totalError;
- }
-
- private static float getColorSum(int color) {
- float red = Color.red(color) / 255.0f;
- float green = Color.green(color) / 255.0f;
- float blue = Color.blue(color) / 255.0f;
- return (red + green + blue);
- }
-}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/bitmapcomparers/NearPixelComparer.java b/tests/tests/uirendering/src/android/uirendering/cts/bitmapcomparers/NearPixelComparer.java
deleted file mode 100644
index bfb2c77..0000000
--- a/tests/tests/uirendering/src/android/uirendering/cts/bitmapcomparers/NearPixelComparer.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.uirendering.cts.bitmapcomparers;
-
-import android.graphics.Color;
-import android.util.Log;
-
-/**
- * Checks to see that a pixel at a given location is the same as the corresponding pixel. If the
- * pixel is different, it checks the pixels around it to see if it is the same.
- */
-public class NearPixelComparer extends BitmapComparer {
- private static final String TAG = "NearPixelComparer";
- private static final int THRESHOLD = 10;
- private static final int NEAR_PIXEL_RADIUS = 1;
-
- @Override
- public boolean verifySame(int[] ideal, int[] given, int offset, int stride, int width,
- int height) {
- for (int y = 0 ; y < height ; y++) {
- for (int x = 0 ; x < width ; x++) {
- boolean success = false;
-
- // we need to check the surrounding pixels
- for (int dx = -NEAR_PIXEL_RADIUS ; dx <= NEAR_PIXEL_RADIUS ; dx++) {
- for (int dy = -NEAR_PIXEL_RADIUS ; dy <= NEAR_PIXEL_RADIUS ; dy++) {
- // need to be sure we don't hit pixels that aren't there
- if (x + dx >= width || x + dx < 0 || y + dy >= height || y + dy < 0) {
- continue;
- }
- int index = indexFromXAndY(x + dx, y + dy, stride, offset);
- if (!pixelsAreSame(ideal[index], given[index])) {
- success = true;
- break;
- }
- }
- }
- if (!success) {
- Log.d(TAG, "Failure at pixel (" + x + "," + y + ")");
- return false;
- }
- }
- }
- return true;
- }
-
- private boolean pixelsAreSame(int ideal, int given) {
- int error = Math.abs(Color.red(ideal) - Color.red(given));
- error += Math.abs(Color.green(ideal) - Color.green(given));
- error += Math.abs(Color.blue(ideal) - Color.blue(given));
- return (error < THRESHOLD);
- }
-}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/bitmapcomparers/PSNRComparer.java b/tests/tests/uirendering/src/android/uirendering/cts/bitmapcomparers/PSNRComparer.java
deleted file mode 100644
index a38a381..0000000
--- a/tests/tests/uirendering/src/android/uirendering/cts/bitmapcomparers/PSNRComparer.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.uirendering.cts.bitmapcomparers;
-
-import android.graphics.Color;
-import android.util.Log;
-
-/**
- * Uses the Peak Signal-to-Noise Ratio approach to determine if two images are considered the same.
- */
-public class PSNRComparer extends BitmapComparer {
- private static final String TAG = "PSNR";
- private final float MAX = 255;
- private final int REGION_SIZE = 10;
-
- private float mThreshold;
-
- /**
- * @param threshold the PSNR necessary to pass the test, if the calculated PSNR is below this
- * value, then the test will fail.
- */
- public PSNRComparer(float threshold) {
- mThreshold = threshold;
- }
-
- @Override
- public boolean verifySame(int[] ideal, int[] given, int offset, int stride, int width,
- int height) {
- float MSE = 0f;
- int interestingRegions = 0;
- for (int y = 0 ; y < height ; y += REGION_SIZE) {
- for (int x = 0 ; x < width ; x += REGION_SIZE) {
- int index = indexFromXAndY(x, y, stride, offset);
- if (inspectRegion(ideal, index)) {
- interestingRegions++;
- }
- }
- }
-
- if (interestingRegions == 0) {
- return true;
- }
-
- for (int y = 0 ; y < height ; y += REGION_SIZE) {
- for (int x = 0 ; x < width ; x += REGION_SIZE) {
- int index = indexFromXAndY(x, y, stride, offset);
- if (ideal[index] == given[index]) {
- continue;
- }
- MSE += (Color.red(ideal[index]) - Color.red(given[index])) *
- (Color.red(ideal[index]) - Color.red(given[index]));
- MSE += (Color.blue(ideal[index]) - Color.blue(given[index])) *
- (Color.blue(ideal[index]) - Color.blue(given[index]));
- MSE += (Color.green(ideal[index]) - Color.green(given[index])) *
- (Color.green(ideal[index]) - Color.green(given[index]));
- }
- }
- MSE /= (interestingRegions * REGION_SIZE * 3);
-
- float fraction = (MAX * MAX) / MSE;
- fraction = (float) Math.log(fraction);
- fraction *= 10;
-
- Log.d(TAG, "PSNR : " + fraction);
-
- return (fraction > mThreshold);
- }
-
- private boolean inspectRegion(int[] ideal, int index) {
- int regionColor = ideal[index];
- for (int i = 0 ; i < REGION_SIZE ; i++) {
- if (regionColor != ideal[index + i]) {
- return true;
- }
- }
- return false;
- }
-}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/bitmapcomparers/PassComparer.java b/tests/tests/uirendering/src/android/uirendering/cts/bitmapcomparers/PassComparer.java
deleted file mode 100644
index 79923cd..0000000
--- a/tests/tests/uirendering/src/android/uirendering/cts/bitmapcomparers/PassComparer.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.uirendering.cts.bitmapcomparers;
-
-/**
- * This class is purely for debug purposes. It will automatically pass any tests.
- */
-public class PassComparer extends BitmapComparer {
- @Override
- public boolean verifySame(int[] ideal, int[] given, int offset, int stride, int width,
- int height) {
- return true;
- }
-}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/bitmapcomparers/ThresholdDifferenceComparer.java b/tests/tests/uirendering/src/android/uirendering/cts/bitmapcomparers/ThresholdDifferenceComparer.java
deleted file mode 100644
index b7a608a..0000000
--- a/tests/tests/uirendering/src/android/uirendering/cts/bitmapcomparers/ThresholdDifferenceComparer.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.uirendering.cts.bitmapcomparers;
-
-import android.graphics.Color;
-import android.util.Log;
-
-/**
- * Compares two images to see if each pixel is the same, within a certain threshold value
- */
-public class ThresholdDifferenceComparer extends BitmapComparer {
- private static final String TAG = "ThresholdDifference";
- private int mThreshold;
-
- /**
- * @param threshold Each pixel is compared against each other, in each of the individual
- * channels. If the sum of the errors amongst the channels is greater than some
- * threshold, then this test will fail.
- */
- public ThresholdDifferenceComparer(int threshold) {
- mThreshold = threshold;
- }
-
- @Override
- public boolean verifySame(int[] ideal, int[] given, int offset, int stride, int width,
- int height) {
- int differentPixels = 0;
- for (int y = 0 ; y < height ; y++) {
- for (int x = 0 ; x < width ; x++) {
- int index = indexFromXAndY(x, y, stride, offset);
- int error = Math.abs(Color.red(ideal[index]) - Color.red(given[index]));
- error += Math.abs(Color.blue(ideal[index]) - Color.blue(given[index]));
- error += Math.abs(Color.green(ideal[index]) - Color.green(given[index]));
- if (error > mThreshold) {
- Log.d(TAG, "Failure at position x = " + x + " y = " + y);
- Log.d(TAG, "Expected color " + Integer.toHexString(ideal[index]) +
- " given color " + Integer.toHexString(given[index]));
- differentPixels++;
- }
- }
- }
- Log.d(TAG, "Number of different pixels : " + differentPixels);
- return (differentPixels == 0);
- }
-}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/bitmapcomparers/WeightedPixelDifference.java b/tests/tests/uirendering/src/android/uirendering/cts/bitmapcomparers/WeightedPixelDifference.java
deleted file mode 100644
index f243c29..0000000
--- a/tests/tests/uirendering/src/android/uirendering/cts/bitmapcomparers/WeightedPixelDifference.java
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.uirendering.cts.bitmapcomparers;
-
-import android.graphics.Color;
-import android.util.Log;
-
-/**
- * This class contains methods to add the error amongst all pixels in two images while taking into
- * account the number of pixels that are non-white. Note only use this if the content background is
- * white.
- */
-public class WeightedPixelDifference extends BitmapComparer {
- private static final String TAG = "WeightedPixel";
- private static final int NUM_OF_COLUMNS = 10;
- private static final float TOTAL_ERROR_DIVISOR = 1024.0f;
-
- private float mThreshold;
-
- public WeightedPixelDifference(float threshold) {
- mThreshold = threshold;
- }
-
- /**
- * Calculates if pixels in a specific line are the same color
- * @return true if the pixels are the same color
- */
- private static boolean inspectRegions(int[] ideal, int start, int stride, int regionSize) {
- int regionColor = ideal[start];
- for (int y = 0 ; y < regionSize ; y++) {
- for (int x = 0 ; x < regionSize ; x++) {
- int index = indexFromXAndY(x, y, stride, start);
- if (ideal[index] != regionColor) {
- return true;
- }
- }
- }
- return false;
- }
-
- /**
- * Finds the error between each individual channel in the color.
- */
- private static float errorBetweenPixels(int color1, int color2) {
- float error = 0f;
- error += Math.abs(Color.red(color1) - Color.red(color2));
- error += Math.abs(Color.green(color1) - Color.green(color2));
- error += Math.abs(Color.blue(color1) - Color.blue(color2));
- error += Math.abs(Color.alpha(color1) - Color.alpha(color2));
- return error;
- }
-
- /**
- * Calculates the error between the pixels in the ideal and given
- * @return true if the accumulated error is smaller than the threshold
- */
- @Override
- public boolean verifySame(int[] ideal, int[] given, int offset, int stride, int width,
- int height) {
- int interestingRegions = 0;
- int regionSize = width / NUM_OF_COLUMNS;
-
- for (int y = 0 ; y < height ; y += regionSize) {
- for (int x = 0 ; x < width ; x += regionSize) {
- int index = indexFromXAndY(x, y,stride, offset);
- if (inspectRegions(ideal, index, stride, regionSize)) {
- interestingRegions++;
- }
- }
- }
-
- int interestingPixels = Math.max(1, interestingRegions) * regionSize * regionSize;
-
- float totalError = 0;
-
- for (int y = 0 ; y < height ; y++) {
- for (int x = 0 ; x < width ; x++) {
- int index = indexFromXAndY(x, y, stride, offset);
- int idealColor = ideal[index];
- int givenColor = given[index];
- if (idealColor == givenColor) {
- continue;
- }
- totalError += errorBetweenPixels(idealColor, givenColor);
- }
- }
-
- totalError /= TOTAL_ERROR_DIVISOR;
- totalError /= interestingPixels;
-
- Log.d(TAG, "Total error : " + totalError);
-
- return totalError < mThreshold;
- }
-}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/InvertVerifier.java b/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/InvertVerifier.java
deleted file mode 100644
index fe0db96..0000000
--- a/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/InvertVerifier.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.uirendering.cts.bitmapverifiers;
-
-/**
- * Used when the tester wants to find the opposite result from a Verifier
- */
-public class InvertVerifier extends BitmapVerifier {
- private BitmapVerifier mBitmapVerifier;
-
- public InvertVerifier(BitmapVerifier bitmapVerifier) {
- mBitmapVerifier = bitmapVerifier;
- }
-
- @Override
- public boolean verify(int[] bitmap, int offset, int stride, int width, int height) {
- boolean success = mBitmapVerifier.verify(bitmap, offset, stride, width, height);
- mDifferenceBitmap = mBitmapVerifier.getDifferenceBitmap();
- return !success;
- }
-}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ColorFilterAlphaTest.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ColorFilterAlphaTest.java
index 5d875ab..4991aac 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ColorFilterAlphaTest.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ColorFilterAlphaTest.java
@@ -17,12 +17,14 @@
import android.graphics.Bitmap;
import android.graphics.Canvas;
+import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.PorterDuffXfermode;
-import android.support.test.filters.MediumTest;
+import android.graphics.drawable.ColorDrawable;
+import android.support.test.filters.LargeTest;
import android.uirendering.cts.bitmapverifiers.SamplePointVerifier;
import android.uirendering.cts.testinfrastructure.ActivityTestBase;
import android.uirendering.cts.testinfrastructure.CanvasClient;
@@ -33,7 +35,7 @@
import java.util.List;
-@MediumTest
+@LargeTest // Temporarily hidden from presubmit
@RunWith(Parameterized.class)
public class ColorFilterAlphaTest extends ActivityTestBase {
// We care about one point in each of the four rectangles of different alpha values, as well as
@@ -120,12 +122,25 @@
return bitmap;
}
+
+ @Override
+ public void setUp() {
+ super.setUp();
+
+ // temporary - ensure test isn't capturing window bg only
+ getInstrumentation().runOnMainSync(() -> getActivity().getWindow().setBackgroundDrawable(
+ new ColorDrawable(Color.GREEN)));
+
+ }
+
private CanvasClient mCanvasClient = new CanvasClient() {
final Paint mPaint = new Paint();
private final Bitmap mBitmap = createMultiRectBitmap();
@Override
public void draw(Canvas canvas, int width, int height) {
+ canvas.drawColor(Color.WHITE); // temporary - ensure test isn't capturing window bg only
+
mPaint.setColorFilter(new PorterDuffColorFilter(FILTER_COLOR, mConfig.mode));
canvas.drawBitmap(mBitmap, 0, 0, mPaint);
}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/PathClippingTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/PathClippingTests.java
index ae4fee1..d78972f 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/PathClippingTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/PathClippingTests.java
@@ -29,6 +29,7 @@
import android.support.test.runner.AndroidJUnit4;
import android.uirendering.cts.R;
import android.uirendering.cts.bitmapcomparers.MSSIMComparer;
+import android.uirendering.cts.bitmapverifiers.GoldenImageVerifier;
import android.uirendering.cts.bitmapverifiers.SamplePointVerifier;
import android.uirendering.cts.testinfrastructure.ActivityTestBase;
import android.uirendering.cts.testinfrastructure.CanvasClient;
@@ -71,18 +72,38 @@
canvas.restore();
};
+ // draw circle with hole in it, by path operations + path clipping
+ static final CanvasClient sTorusClipOutCanvasClient = (canvas, width, height) -> {
+ canvas.save();
+
+ Path path1 = new Path();
+ path1.addCircle(30, 30, 50, Path.Direction.CW);
+
+ Path path2 = new Path();
+ path2.addCircle(30, 30, 30, Path.Direction.CW);
+
+ canvas.clipPath(path1);
+ canvas.clipOutPath(path2);
+ canvas.drawColor(Color.BLUE);
+
+ canvas.restore();
+ };
+
@Test
public void testCircleWithCircle() {
createTest()
.addCanvasClient("TorusDraw", sTorusDrawCanvasClient, false)
.addCanvasClient("TorusClip", sTorusClipCanvasClient)
- .runWithComparer(new MSSIMComparer(0.90));
+ .addCanvasClient("TorusClipOut", sTorusClipOutCanvasClient)
+ .runWithVerifier(new GoldenImageVerifier(getActivity(),
+ R.drawable.pathclippingtest_torus, new MSSIMComparer(0.95)));
}
@Test
public void testCircleWithPoints() {
createTest()
.addCanvasClient("TorusClip", sTorusClipCanvasClient)
+ .addCanvasClient("TorusClipOut", sTorusClipOutCanvasClient)
.runWithVerifier(new SamplePointVerifier(
new Point[] {
// inside of circle
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/XfermodeTest.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/XfermodeTest.java
index 38a4d40..acae80f 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/XfermodeTest.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/XfermodeTest.java
@@ -17,12 +17,14 @@
import android.graphics.Bitmap;
import android.graphics.Canvas;
+import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.RectF;
-import android.support.test.filters.MediumTest;
+import android.graphics.drawable.ColorDrawable;
+import android.support.test.filters.LargeTest;
import android.uirendering.cts.bitmapverifiers.SamplePointVerifier;
import android.uirendering.cts.testinfrastructure.ActivityTestBase;
import android.uirendering.cts.testinfrastructure.CanvasClient;
@@ -34,7 +36,7 @@
import java.util.ArrayList;
import java.util.List;
-@MediumTest
+@LargeTest // Temporarily hidden from presubmit
@RunWith(Parameterized.class)
public class XfermodeTest extends ActivityTestBase {
/**
@@ -137,6 +139,17 @@
mConfig = config;
}
+
+ @Override
+ public void setUp() {
+ super.setUp();
+
+ // temporary - ensure test isn't capturing window bg only
+ getInstrumentation().runOnMainSync(() -> getActivity().getWindow().setBackgroundDrawable(
+ new ColorDrawable(Color.GREEN)));
+
+ }
+
private CanvasClient mCanvasClient = new CanvasClient() {
final Paint mPaint = new Paint();
private final RectF mSrcRect = new RectF(30, 30, 80, 80);
@@ -146,6 +159,8 @@
@Override
public void draw(Canvas canvas, int width, int height) {
+ canvas.drawColor(Color.WHITE); // temporary - ensure test isn't capturing window bg only
+
int sc = canvas.saveLayer(0, 0, TEST_WIDTH, TEST_HEIGHT, null);
canvas.drawBitmap(mDstBitmap, 0, 0, null);
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/ActivityTestBase.java b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/ActivityTestBase.java
index 94c981f..d6a196a 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/ActivityTestBase.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/ActivityTestBase.java
@@ -127,6 +127,7 @@
TEST_WIDTH, TEST_HEIGHT, Config.ARGB_8888);
Rect srcRect = new Rect(testOffset.x, testOffset.y,
testOffset.x + TEST_WIDTH, testOffset.y + TEST_HEIGHT);
+ Log.d("UiRendering", "capturing screenshot of " + srcRect.toShortString());
int copyResult = copy.request(getActivity().getWindow(), srcRect, dest);
Assert.assertEquals(PixelCopy.SUCCESS, copyResult);
return dest;
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DrawActivity.java b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DrawActivity.java
index 4784cbd..2b8f3b3 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DrawActivity.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DrawActivity.java
@@ -15,18 +15,27 @@
*/
package android.uirendering.cts.testinfrastructure;
+import static org.junit.Assert.fail;
+
import android.app.Activity;
import android.content.res.Configuration;
import android.graphics.Point;
import android.os.Bundle;
import android.os.Handler;
+import android.os.HandlerThread;
import android.os.Message;
import android.support.annotation.Nullable;
import android.uirendering.cts.R;
+import android.util.Log;
+import android.view.FrameMetrics;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewStub;
import android.view.ViewTreeObserver;
+import android.view.Window;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
/**
* A generic activity that uses a view specified by the user.
@@ -47,6 +56,33 @@
mHandler = new RenderSpecHandler();
int uiMode = getResources().getConfiguration().uiMode;
mOnTv = (uiMode & Configuration.UI_MODE_TYPE_MASK) == Configuration.UI_MODE_TYPE_TELEVISION;
+
+ // log frame metrics
+ HandlerThread handlerThread = new HandlerThread("FrameMetrics");
+ handlerThread.start();
+ getWindow().addOnFrameMetricsAvailableListener(
+ new Window.OnFrameMetricsAvailableListener() {
+ int mRtFrameCount = 0;
+ @Override
+ public void onFrameMetricsAvailable(Window window, FrameMetrics frameMetrics,
+ int dropCountSinceLastInvocation) {
+ Log.d("UiRendering", "Window frame count " + mRtFrameCount
+ + ", frame drops " + dropCountSinceLastInvocation);
+ mRtFrameCount++;
+ }
+ }, new Handler(handlerThread.getLooper()));
+
+ // log draw metrics
+ View view = new View(this);
+ setContentView(view);
+ view.getViewTreeObserver().addOnDrawListener(new ViewTreeObserver.OnDrawListener() {
+ int mFrameCount;
+ @Override
+ public void onDraw() {
+ Log.d("UiRendering", "View tree frame count " + mFrameCount);
+ mFrameCount++;
+ }
+ });
}
public boolean getOnTv() {
@@ -77,7 +113,15 @@
}
public void reset() {
- mHandler.sendEmptyMessage(RenderSpecHandler.RESET_MSG);
+ CountDownLatch fence = new CountDownLatch(1);
+ mHandler.obtainMessage(RenderSpecHandler.RESET_MSG, fence).sendToTarget();
+ try {
+ if (!fence.await(10, TimeUnit.SECONDS)) {
+ fail("Timeout exception");
+ }
+ } catch (InterruptedException ex) {
+ fail(ex.getMessage());
+ }
}
private ViewInitializer mViewInitializer;
@@ -99,8 +143,10 @@
}
public void handleMessage(Message message) {
+ Log.d("UiRendering", "message of type " + message.what);
if (message.what == RESET_MSG) {
((ViewGroup)findViewById(android.R.id.content)).removeAllViews();
+ ((CountDownLatch)message.obj).countDown();
return;
}
setContentView(R.layout.test_container);
@@ -163,6 +209,7 @@
@Override
public void onDraw() {
mView.post(() -> {
+ Log.d("UiRendering", "notifying capture");
mView.getViewTreeObserver().removeOnDrawListener(this);
synchronized (mLock) {
mViewWrapper.getLocationOnScreen(mLocationOnScreen);
diff --git a/tests/tests/view/AndroidManifest.xml b/tests/tests/view/AndroidManifest.xml
index 5f50e9c..5d23183 100644
--- a/tests/tests/view/AndroidManifest.xml
+++ b/tests/tests/view/AndroidManifest.xml
@@ -177,7 +177,8 @@
<activity android:name="android.view.cts.PixelCopyViewProducerActivity"
android:label="PixelCopyViewProducerActivity"
- android:screenOrientation="locked"
+ android:screenOrientation="portrait"
+ android:rotationAnimation="jumpcut"
android:theme="@android:style/Theme.DeviceDefault.NoActionBar"
android:configChanges="orientation|screenSize" />
diff --git a/tests/tests/view/src/android/view/cts/FocusFinderTest.java b/tests/tests/view/src/android/view/cts/FocusFinderTest.java
index 8d56087..a8015ab 100644
--- a/tests/tests/view/src/android/view/cts/FocusFinderTest.java
+++ b/tests/tests/view/src/android/view/cts/FocusFinderTest.java
@@ -18,6 +18,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import android.graphics.Rect;
@@ -244,6 +245,19 @@
assertTrue(nextFocus == mBottomRight || nextFocus == mBottomLeft);
}
+ @Test
+ public void testChainVisibility() {
+ mBottomRight.setNextFocusForwardId(mBottomLeft.getId());
+ mBottomLeft.setNextFocusForwardId(mTopRight.getId());
+ mBottomLeft.setVisibility(View.INVISIBLE);
+ View next = mFocusFinder.findNextFocus(mLayout, mBottomRight, View.FOCUS_FORWARD);
+ assertSame(mTopRight, next);
+
+ mBottomLeft.setNextFocusForwardId(View.NO_ID);
+ next = mFocusFinder.findNextFocus(mLayout, mBottomRight, View.FOCUS_FORWARD);
+ assertSame(mTopLeft, next);
+ }
+
private void verifyNextCluster(View currentCluster, int direction, View expectedNextCluster) {
View actualNextCluster = mFocusFinder.findNextKeyboardNavigationCluster(
mLayout, currentCluster, direction);
diff --git a/tests/tests/view/src/android/view/cts/MenuTest.java b/tests/tests/view/src/android/view/cts/MenuTest.java
index d01cd8b..d931c0d 100644
--- a/tests/tests/view/src/android/view/cts/MenuTest.java
+++ b/tests/tests/view/src/android/view/cts/MenuTest.java
@@ -49,6 +49,7 @@
public ActivityTestRule<MenuTestActivity> mActivityRule =
new ActivityTestRule<>(MenuTestActivity.class);
+ @UiThreadTest
@Before
public void setup() {
mActivity = (MenuTestActivity) mActivityRule.getActivity();
diff --git a/tests/tests/view/src/android/view/cts/PixelCopyViewProducerActivity.java b/tests/tests/view/src/android/view/cts/PixelCopyViewProducerActivity.java
index ab59f3a..e553aa5 100644
--- a/tests/tests/view/src/android/view/cts/PixelCopyViewProducerActivity.java
+++ b/tests/tests/view/src/android/view/cts/PixelCopyViewProducerActivity.java
@@ -16,10 +16,11 @@
package android.view.cts;
+import static org.junit.Assert.fail;
+
import android.app.Activity;
import android.content.Context;
import android.content.pm.ActivityInfo;
-import android.content.res.Configuration;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
@@ -42,8 +43,7 @@
private int mCurrentOrientation = 0;
private View mContent;
private Rect mContentBounds = new Rect();
- private CountDownLatch mFence = new CountDownLatch(1);
- private boolean mListenForRotate = false;
+ private CountDownLatch mFence = new CountDownLatch(3);
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -55,19 +55,65 @@
}
@Override
- public void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
- if (mListenForRotate) {
- mListenForRotate = false;
- mContent.getViewTreeObserver().addOnDrawListener(this);
+ public void onDraw() {
+ final int requestedOrientation = ORIENTATIONS[mCurrentOrientation];
+ boolean screenPortrait =
+ requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
+ || requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
+ boolean contentPortrait = mContent.getHeight() > mContent.getWidth();
+ if (screenPortrait != contentPortrait) {
+ return;
}
+ mContent.post(() -> {
+ Point offset = new Point();
+ // We pass mContentBounds here just as a throwaway rect, we don't care about
+ // the visible rect just the global offset.
+ mContent.getGlobalVisibleRect(mContentBounds, offset);
+ mContentBounds.set(offset.x, offset.y,
+ offset.x + mContent.getWidth(), offset.y + mContent.getHeight());
+ mFence.countDown();
+ if (mFence.getCount() > 0) {
+ mContent.invalidate();
+ }
+ });
+ }
+
+ public void waitForFirstDrawCompleted(int timeout, TimeUnit unit) {
+ try {
+ if (!mFence.await(timeout, unit)) {
+ fail("Timeout");
+ }
+ } catch (InterruptedException ex) {
+ fail(ex.getMessage());
+ }
+ }
+
+ public boolean rotate() {
+ mFence = new CountDownLatch(3);
+ runOnUiThread(() -> {
+ mCurrentOrientation = (mCurrentOrientation + 1) % ORIENTATIONS.length;
+ setRequestedOrientation(ORIENTATIONS[mCurrentOrientation]);
+ });
+ waitForFirstDrawCompleted(3, TimeUnit.SECONDS);
+ return mCurrentOrientation != 0;
+ }
+
+ // Convert a rect in normalized 0-100 dimensions to the bounds of the actual View.
+ public void normalizedToSurface(Rect inOut) {
+ float sx = mContentBounds.width() / 100.0f;
+ float sy = mContentBounds.height() / 100.0f;
+ inOut.left = (int) (inOut.left * sx);
+ inOut.top = (int) (inOut.top * sy);
+ inOut.right = (int) (inOut.right * sx + 0.5f);
+ inOut.bottom = (int) (inOut.bottom * sy + 0.5f);
+ inOut.offset(mContentBounds.left, mContentBounds.top);
}
private static final class ColoredGrid extends View {
private Paint mPaint = new Paint();
private Rect mRect = new Rect();
- public ColoredGrid(Context context) {
+ ColoredGrid(Context context) {
super(context);
setWillNotDraw(false);
}
@@ -96,53 +142,4 @@
canvas.drawRect(mRect, mPaint);
}
}
-
- @Override
- public void onDraw() {
- mContent.post(() -> {
- mContent.getViewTreeObserver().removeOnDrawListener(PixelCopyViewProducerActivity.this);
- Point offset = new Point();
- // We pass mContentBounds here just as a throwaway rect, we don't care about
- // the visible rect just the global offset.
- mContent.getGlobalVisibleRect(mContentBounds, offset);
- mContentBounds.set(offset.x, offset.y,
- offset.x + mContent.getWidth(), offset.y + mContent.getHeight());
- mFence.countDown();
- });
- }
-
- public void waitForFirstDrawCompleted(int timeout, TimeUnit unit) {
- boolean succeeded = false;
- Exception reason = null;
- try {
- succeeded = mFence.await(timeout, unit);
- } catch (Exception e) {
- reason = e;
- }
- if (!succeeded) {
- throw new AssertionError("Timed out waiting for fence", reason);
- }
- }
-
- public boolean rotate() {
- mFence = new CountDownLatch(1);
- runOnUiThread(() -> {
- mCurrentOrientation = (mCurrentOrientation + 1) % ORIENTATIONS.length;
- mListenForRotate = true;
- setRequestedOrientation(ORIENTATIONS[mCurrentOrientation]);
- });
- waitForFirstDrawCompleted(3, TimeUnit.SECONDS);
- return mCurrentOrientation != 0;
- }
-
- // Convert a rect in normalized 0-100 dimensions to the bounds of the actual View.
- public void normalizedToSurface(Rect inOut) {
- float sx = mContentBounds.width() / 100.0f;
- float sy = mContentBounds.height() / 100.0f;
- inOut.left = (int) (inOut.left * sx);
- inOut.top = (int) (inOut.top * sy);
- inOut.right = (int) (inOut.right * sx + 0.5f);
- inOut.bottom = (int) (inOut.bottom * sy + 0.5f);
- inOut.offset(mContentBounds.left, mContentBounds.top);
- }
}
diff --git a/tests/tests/view/src/android/view/cts/SurfaceViewCtsActivity.java b/tests/tests/view/src/android/view/cts/SurfaceViewCtsActivity.java
index cb85712..590a4be 100644
--- a/tests/tests/view/src/android/view/cts/SurfaceViewCtsActivity.java
+++ b/tests/tests/view/src/android/view/cts/SurfaceViewCtsActivity.java
@@ -23,6 +23,7 @@
import android.graphics.Paint;
import android.graphics.PorterDuff.Mode;
import android.os.Bundle;
+import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
@@ -75,6 +76,8 @@
private int mOldHOnSizeChanged;
private int mVisibilityOnWindowVisibilityChanged;
+ Surface mSurface;
+
public MockSurfaceView(Context context) {
super(context);
mHolder = getHolder();
@@ -174,6 +177,8 @@
public void surfaceCreated(SurfaceHolder holder) {
mSurfaceCreatedCalled = true;
+ mSurface = holder.getSurface();
+
// Use mock canvas listening to the drawColor() calling.
mCanvas = new Canvas(Bitmap.createBitmap( BITMAP_WIDTH,
BITMAP_HEIGHT,
diff --git a/tests/tests/view/src/android/view/cts/SurfaceViewTest.java b/tests/tests/view/src/android/view/cts/SurfaceViewTest.java
index 5d90c73..3ccca48 100644
--- a/tests/tests/view/src/android/view/cts/SurfaceViewTest.java
+++ b/tests/tests/view/src/android/view/cts/SurfaceViewTest.java
@@ -32,10 +32,12 @@
import android.view.KeyEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
+import android.view.ViewGroup;
import android.view.cts.SurfaceViewCtsActivity.MockSurfaceView;
import com.android.compatibility.common.util.CtsKeyEventUtil;
import com.android.compatibility.common.util.PollingCheck;
+import com.android.compatibility.common.util.WidgetTestUtils;
import org.junit.Before;
import org.junit.Rule;
@@ -161,4 +163,15 @@
PollingCheck.waitFor(() -> mMockSurfaceView.isDetachedFromWindow() &&
!mMockSurfaceView.isShown());
}
+
+ @Test
+ public void surfaceInvalidatedWhileDetaching() throws Throwable {
+ assertTrue(mMockSurfaceView.mSurface.isValid());
+ assertFalse(mMockSurfaceView.isDetachedFromWindow());
+ WidgetTestUtils.runOnMainAndLayoutSync(mActivityRule, () -> {
+ ((ViewGroup)mMockSurfaceView.getParent()).removeView(mMockSurfaceView);
+ }, false);
+ assertTrue(mMockSurfaceView.isDetachedFromWindow());
+ assertFalse(mMockSurfaceView.mSurface.isValid());
+ }
}
diff --git a/tests/tests/view/src/android/view/cts/TextureViewCtsActivity.java b/tests/tests/view/src/android/view/cts/TextureViewCtsActivity.java
index 16bbc18..e71e275 100644
--- a/tests/tests/view/src/android/view/cts/TextureViewCtsActivity.java
+++ b/tests/tests/view/src/android/view/cts/TextureViewCtsActivity.java
@@ -35,6 +35,7 @@
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
@@ -50,6 +51,7 @@
private TextureView mTextureView;
private HandlerThread mGLThreadLooper;
private Handler mGLThread;
+ private CountDownLatch mEnterAnimationFence = new CountDownLatch(1);
private SurfaceTexture mSurface;
private int mSurfaceUpdatedCount;
@@ -88,6 +90,18 @@
setContentView(content);
}
+ @Override
+ public void onEnterAnimationComplete() {
+ super.onEnterAnimationComplete();
+ mEnterAnimationFence.countDown();
+ }
+
+ public void waitForEnterAnimationComplete() throws TimeoutException, InterruptedException {
+ if (!mEnterAnimationFence.await(TIME_OUT_MS, TimeUnit.MILLISECONDS)) {
+ throw new TimeoutException();
+ }
+ }
+
private class RunSignalAndCatch implements Runnable {
public Throwable error;
private Runnable mRunnable;
@@ -114,7 +128,9 @@
CountDownLatch fence = new CountDownLatch(1);
RunSignalAndCatch wrapper = new RunSignalAndCatch(r, fence);
mGLThread.post(wrapper);
- fence.await(TIME_OUT_MS, TimeUnit.MILLISECONDS);
+ if (!fence.await(TIME_OUT_MS, TimeUnit.MILLISECONDS)) {
+ throw new TimeoutException();
+ }
if (wrapper.error != null) {
throw wrapper.error;
}
diff --git a/tests/tests/view/src/android/view/cts/TextureViewTest.java b/tests/tests/view/src/android/view/cts/TextureViewTest.java
index a9e215b..c771c18 100644
--- a/tests/tests/view/src/android/view/cts/TextureViewTest.java
+++ b/tests/tests/view/src/android/view/cts/TextureViewTest.java
@@ -59,8 +59,9 @@
@Test
public void testFirstFrames() throws Throwable {
+ mActivity.waitForEnterAnimationComplete();
+
final Point center = new Point();
- mInstrumentation.waitForIdleSync();
mActivityRule.runOnUiThread(() -> {
View content = mActivity.findViewById(android.R.id.content);
int[] outLocation = new int[2];
diff --git a/tests/tests/webkit/src/android/webkit/cts/CookieSyncManagerCtsActivity.java b/tests/tests/webkit/src/android/webkit/cts/CookieSyncManagerCtsActivity.java
index 10369c6..7661013 100644
--- a/tests/tests/webkit/src/android/webkit/cts/CookieSyncManagerCtsActivity.java
+++ b/tests/tests/webkit/src/android/webkit/cts/CookieSyncManagerCtsActivity.java
@@ -18,6 +18,8 @@
import android.app.Activity;
import android.os.Bundle;
+import android.view.ViewGroup;
+import android.view.ViewParent;
import android.webkit.CookieSyncManager;
import android.webkit.WebView;
@@ -51,6 +53,18 @@
}
@Override
+ public void onDestroy() {
+ if (mWebView != null) {
+ ViewParent parent = mWebView.getParent();
+ if (parent instanceof ViewGroup) {
+ ((ViewGroup) parent).removeView(mWebView);
+ }
+ mWebView.destroy();
+ }
+ super.onDestroy();
+ }
+
+ @Override
protected void onStop() {
super.onStop();
try {
diff --git a/tests/tests/widget/src/android/widget/cts/ListViewTest.java b/tests/tests/widget/src/android/widget/cts/ListViewTest.java
index 4b1e6bb..8cd85b1 100644
--- a/tests/tests/widget/src/android/widget/cts/ListViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/ListViewTest.java
@@ -348,6 +348,8 @@
public void testAccessHeaderView() {
final TextView headerView1 = (TextView) mActivity.findViewById(R.id.headerview1);
final TextView headerView2 = (TextView) mActivity.findViewById(R.id.headerview2);
+ ((ViewGroup) headerView1.getParent()).removeView(headerView1);
+ ((ViewGroup) headerView2.getParent()).removeView(headerView2);
mListView.setHeaderDividersEnabled(true);
assertTrue(mListView.areHeaderDividersEnabled());
@@ -574,6 +576,7 @@
public void testFindViewTraversal() {
MyListView listView = new MyListView(mActivity, mAttributeSet);
TextView headerView = (TextView) mActivity.findViewById(R.id.headerview1);
+ ((ViewGroup) headerView.getParent()).removeView(headerView);
assertNull(listView.findViewTraversal(R.id.headerview1));
@@ -587,6 +590,7 @@
public void testFindViewWithTagTraversal() {
MyListView listView = new MyListView(mActivity, mAttributeSet);
TextView headerView = (TextView) mActivity.findViewById(R.id.headerview1);
+ ((ViewGroup) headerView.getParent()).removeView(headerView);
assertNull(listView.findViewWithTagTraversal("header"));
diff --git a/tests/tests/widget/src/android/widget/cts/NumberPickerTest.java b/tests/tests/widget/src/android/widget/cts/NumberPickerTest.java
index 4d25740..e8e0eb8 100644
--- a/tests/tests/widget/src/android/widget/cts/NumberPickerTest.java
+++ b/tests/tests/widget/src/android/widget/cts/NumberPickerTest.java
@@ -28,12 +28,14 @@
import static org.mockito.Mockito.verifyZeroInteractions;
import android.app.Instrumentation;
+import android.app.UiAutomation;
import android.support.test.InstrumentationRegistry;
import android.support.test.annotation.UiThreadTest;
import android.support.test.filters.SmallTest;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
import android.text.TextUtils;
+import android.view.accessibility.AccessibilityEvent;
import android.widget.NumberPicker;
import com.android.compatibility.common.util.CtsTouchUtils;
@@ -50,8 +52,10 @@
private static final String[] NUMBER_NAMES3 = {"One", "Two", "Three"};
private static final String[] NUMBER_NAMES_ALT3 = {"Three", "Four", "Five"};
private static final String[] NUMBER_NAMES5 = {"One", "Two", "Three", "Four", "Five"};
+ private static final long TIMEOUT_ACCESSIBILITY_EVENT = 5 * 1000;
private Instrumentation mInstrumentation;
+ private UiAutomation mUiAutomation;
private NumberPickerCtsActivity mActivity;
private NumberPicker mNumberPicker;
@@ -62,6 +66,7 @@
@Before
public void setup() {
mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ mUiAutomation = mInstrumentation.getUiAutomation();
mActivity = mActivityRule.getActivity();
mNumberPicker = (NumberPicker) mActivity.findViewById(R.id.number_picker);
}
@@ -260,32 +265,41 @@
mNumberPicker.getDisplayedValueForCurrentSelection()));
}
- @UiThreadTest
@Test
- public void testAccessValue() {
- mNumberPicker.setMinValue(20);
- mNumberPicker.setMaxValue(22);
- mNumberPicker.setDisplayedValues(NUMBER_NAMES3);
-
+ public void testAccessValue() throws Throwable {
final NumberPicker.OnValueChangeListener mockValueChangeListener =
mock(NumberPicker.OnValueChangeListener.class);
- mNumberPicker.setOnValueChangedListener(mockValueChangeListener);
- mNumberPicker.setValue(21);
- assertEquals(21, mNumberPicker.getValue());
+ mInstrumentation.runOnMainSync(() -> {
+ mNumberPicker.setMinValue(20);
+ mNumberPicker.setMaxValue(22);
+ mNumberPicker.setDisplayedValues(NUMBER_NAMES3);
- mNumberPicker.setValue(20);
- assertEquals(20, mNumberPicker.getValue());
+ mNumberPicker.setOnValueChangedListener(mockValueChangeListener);
+ });
- mNumberPicker.setValue(22);
- assertEquals(22, mNumberPicker.getValue());
+ mUiAutomation.executeAndWaitForEvent(() ->
+ mInstrumentation.runOnMainSync(() -> mNumberPicker.setValue(21)),
+ (AccessibilityEvent event) ->
+ event.getEventType() == AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED,
+ TIMEOUT_ACCESSIBILITY_EVENT);
- // Check trying to set value out of min/max range
- mNumberPicker.setValue(10);
- assertEquals(20, mNumberPicker.getValue());
+ mInstrumentation.runOnMainSync(() -> {
+ assertEquals(21, mNumberPicker.getValue());
- mNumberPicker.setValue(100);
- assertEquals(22, mNumberPicker.getValue());
+ mNumberPicker.setValue(20);
+ assertEquals(20, mNumberPicker.getValue());
+
+ mNumberPicker.setValue(22);
+ assertEquals(22, mNumberPicker.getValue());
+
+ // Check trying to set value out of min/max range
+ mNumberPicker.setValue(10);
+ assertEquals(20, mNumberPicker.getValue());
+
+ mNumberPicker.setValue(100);
+ assertEquals(22, mNumberPicker.getValue());
+ });
// Since all changes to value are via API calls, we should have no interactions /
// callbacks on our listener.
@@ -362,11 +376,15 @@
final int[] numberPickerLocationOnScreen = new int[2];
mNumberPicker.getLocationOnScreen(numberPickerLocationOnScreen);
- CtsTouchUtils.emulateDragGesture(mInstrumentation,
- numberPickerLocationOnScreen[0] + mNumberPicker.getWidth() / 2,
- numberPickerLocationOnScreen[1] + mNumberPicker.getHeight() - 1,
- 0,
- - (mNumberPicker.getHeight() - 2));
+ mUiAutomation.executeAndWaitForEvent(() ->
+ CtsTouchUtils.emulateDragGesture(mInstrumentation,
+ numberPickerLocationOnScreen[0] + mNumberPicker.getWidth() / 2,
+ numberPickerLocationOnScreen[1] + mNumberPicker.getHeight() - 1,
+ 0,
+ -(mNumberPicker.getHeight() - 2)),
+ (AccessibilityEvent event) ->
+ event.getEventType() == AccessibilityEvent.TYPE_VIEW_SCROLLED,
+ TIMEOUT_ACCESSIBILITY_EVENT);
// At this point we expect that the drag-up gesture has selected the value
// that was "below" the previously selected one, and that our value change listener
diff --git a/tests/tests/widget/src/android/widget/cts/TextViewTest.java b/tests/tests/widget/src/android/widget/cts/TextViewTest.java
index a70fd54..b40b2f1 100644
--- a/tests/tests/widget/src/android/widget/cts/TextViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/TextViewTest.java
@@ -162,7 +162,6 @@
import java.io.IOException;
import java.lang.annotation.Retention;
-import java.util.Arrays;
import java.util.Locale;
/**
@@ -2809,12 +2808,18 @@
mInstrumentation.waitForIdleSync();
assertTrue(mTextView.isFocused());
- // Arrows should not cause focus to leave the textfield
+ // Pure-keyboard arrows should not cause focus to leave the textfield
CtsKeyEventUtil.sendKeyDownUp(mInstrumentation, mTextView, KeyEvent.KEYCODE_DPAD_UP);
mInstrumentation.waitForIdleSync();
assertTrue(mTextView.isFocused());
- CtsKeyEventUtil.sendKeyDownUp(mInstrumentation, mTextView, KeyEvent.KEYCODE_DPAD_DOWN);
+ // Non-pure-keyboard arrows, however, should.
+ int dpadRemote = InputDevice.SOURCE_DPAD | InputDevice.SOURCE_KEYBOARD;
+ sendSourceKeyDownUp(mInstrumentation, mTextView, KeyEvent.KEYCODE_DPAD_UP, dpadRemote);
+ mInstrumentation.waitForIdleSync();
+ assertFalse(mTextView.isFocused());
+
+ sendSourceKeyDownUp(mInstrumentation, mTextView, KeyEvent.KEYCODE_DPAD_DOWN, dpadRemote);
mInstrumentation.waitForIdleSync();
assertTrue(mTextView.isFocused());
@@ -2824,6 +2829,16 @@
assertFalse(mTextView.isFocused());
}
+ private void sendSourceKeyDownUp(Instrumentation instrumentation, View targetView, int key,
+ int source) {
+ KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, key);
+ event.setSource(source);
+ CtsKeyEventUtil.sendKey(instrumentation, targetView, event);
+ event = new KeyEvent(KeyEvent.ACTION_UP, key);
+ event.setSource(source);
+ CtsKeyEventUtil.sendKey(instrumentation, targetView, event);
+ }
+
@Test
public void testSetIncludeFontPadding() throws Throwable {
mTextView = findTextView(R.id.textview_text);
@@ -3537,6 +3552,7 @@
public void testGetOffsetForPositionSingleLineLtr() throws Throwable {
// asserts getOffsetPosition returns correct values for a single line LTR text
final String text = "aaaaa";
+
mActivityRule.runOnUiThread(() -> {
mTextView = new TextView(mActivity);
mTextView.setText(text);
@@ -3560,17 +3576,21 @@
mActivityRule.runOnUiThread(() -> mActivity.setContentView(layout));
mInstrumentation.waitForIdleSync();
+ final float halfCharWidth = (float) Math.ceil(mTextView.getPaint().measureText("a") / 2f);
+ final int paddingTop = mTextView.getTotalPaddingTop();
+ final int paddingLeft = mTextView.getTotalPaddingLeft();
+
final int firstOffset = 0;
final int lastOffset = text.length() - 1;
final int midOffset = text.length() / 2;
// left edge of view
float x = 0f;
- float y = mTextView.getHeight() / 2f;
+ float y = mTextView.getHeight() / 2f + paddingTop;
assertEquals(firstOffset, mTextView.getOffsetForPosition(x, y));
// right edge of text
- x = mTextView.getLayout().getLineWidth(0) - 1f;
+ x = mTextView.getLayout().getLineWidth(0) + paddingLeft - halfCharWidth;
assertEquals(lastOffset, mTextView.getOffsetForPosition(x, y));
// right edge of view
@@ -3582,7 +3602,7 @@
assertEquals(firstOffset, mTextView.getOffsetForPosition(x, y));
// horizontal center of text
- x = (float) Math.floor(mTextView.getLayout().getLineWidth(0) / 2f + 0.5f);
+ x = mTextView.getLayout().getLineWidth(0) / 2f + paddingLeft - halfCharWidth;
assertEquals(midOffset, mTextView.getOffsetForPosition(x, y));
}
@@ -3616,9 +3636,13 @@
final Rect lineBounds = new Rect();
mTextView.getLayout().getLineBounds(0, lineBounds);
+ final float halfCharWidth = (float) Math.ceil(mTextView.getPaint().measureText("a") / 2f);
+ final int paddingTop = mTextView.getTotalPaddingTop();
+ final int paddingLeft = mTextView.getTotalPaddingLeft();
+
// left edge of view at first line
float x = 0f;
- float y = lineBounds.height() / 2f;
+ float y = lineBounds.height() / 2f + paddingTop;
assertEquals(0, mTextView.getOffsetForPosition(x, y));
// right edge of view at first line
@@ -3627,14 +3651,14 @@
// update lineBounds to be the second line
mTextView.getLayout().getLineBounds(1, lineBounds);
- y = lineBounds.top + lineBounds.height() / 2;
+ y = lineBounds.top + lineBounds.height() / 2f + paddingTop;
// left edge of view at second line
x = 0f;
assertEquals(line.length(), mTextView.getOffsetForPosition(x, y));
// right edge of text at second line
- x = mTextView.getLayout().getLineWidth(1) - 1f;
+ x = mTextView.getLayout().getLineWidth(1) + paddingLeft - halfCharWidth;
assertEquals(line.length() + line.length() - 1, mTextView.getOffsetForPosition(x, y));
// right edge of view at second line
@@ -3642,7 +3666,7 @@
assertEquals(line.length() + line.length() - 1, mTextView.getOffsetForPosition(x, y));
// horizontal center of text at second line
- x = (float) Math.floor(mTextView.getLayout().getLineWidth(1) / 2f + 0.5f);
+ x = mTextView.getLayout().getLineWidth(1) / 2f + paddingLeft - halfCharWidth;
// second line mid offset should not include next line, therefore subtract one
assertEquals(line.length() + (line.length() - 1) / 2, mTextView.getOffsetForPosition(x, y));
}
@@ -3677,9 +3701,14 @@
final Rect lineBounds = new Rect();
mTextView.getLayout().getLineBounds(0, lineBounds);
+ final float halfCharWidth = (float) Math.ceil(
+ mTextView.getPaint().measureText("\u0635") / 2f);
+ final int paddingTop = mTextView.getTotalPaddingTop();
+ final int paddingRight = mTextView.getTotalPaddingRight();
+
// right edge of view at first line
float x = mTextView.getWidth() - 1f;
- float y = lineBounds.height() / 2f;
+ float y = lineBounds.height() / 2f + paddingTop;
assertEquals(0, mTextView.getOffsetForPosition(x, y));
// left edge of view at first line
@@ -3688,7 +3717,7 @@
// update lineBounds to be the second line
mTextView.getLayout().getLineBounds(1, lineBounds);
- y = lineBounds.top + lineBounds.height() / 2f;
+ y = lineBounds.top + lineBounds.height() / 2f + paddingTop;
// right edge of view at second line
x = mTextView.getWidth() - 1f;
@@ -3698,13 +3727,14 @@
x = 0f;
assertEquals(line.length() + line.length() - 1, mTextView.getOffsetForPosition(x, y));
- // right edge of text at second line
- x = mTextView.getWidth() - mTextView.getLayout().getLineWidth(1) + 1f;
+ // left edge of text at second line
+ x = mTextView.getWidth() - (mTextView.getLayout().getLineWidth(1) + paddingRight
+ - halfCharWidth);
assertEquals(line.length() + line.length() - 1, mTextView.getOffsetForPosition(x, y));
// horizontal center of text at second line
- x = mTextView.getWidth() - (float) Math.floor(
- mTextView.getLayout().getLineWidth(1) / 2f + 0.5f);
+ x = mTextView.getWidth() - (mTextView.getLayout().getLineWidth(1) / 2f + paddingRight
+ - halfCharWidth);
// second line mid offset should not include next line, therefore subtract one
assertEquals(line.length() + (line.length() - 1) / 2, mTextView.getOffsetForPosition(x, y));
}
@@ -6988,13 +7018,6 @@
(int) (0.5f + TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PT, 10f, m)),
(int) (0.5f + TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_IN, 10f, m)),
(int) (0.5f + TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_MM, 10f, m))};
- expectedSizesInPx = Arrays.stream(expectedSizesInPx)
- .filter(x -> x > 0)
- .distinct()
- .sorted()
- .toArray();
- assertArrayEquals(expectedSizesInPx,
- autoSizeTextViewUniform.getAutoSizeTextAvailableSizes());
boolean containsValueFromExpectedSizes = false;
int textSize = (int) autoSizeTextViewUniform.getTextSize();
@@ -7317,12 +7340,17 @@
PollingCheck.waitFor(() -> mTextView.getSelectionStart() == SMARTSELECT_START
&& mTextView.getSelectionEnd() == SMARTSELECT_END);
- // Click to reset selection. Expect selection of original selection.
+ // Tap to reset selection. Expect tapped word to be selected.
+ startIndex = text.indexOf("Filip");
+ endIndex = startIndex + "Filip".length();
+ offset = getCenterPositionOfTextAt(mTextView, startIndex, endIndex);
emulateClickOnView(mTextView, offset.x, offset.y);
- PollingCheck.waitFor(() -> mTextView.getSelectionStart() == startIndex
- && mTextView.getSelectionEnd() == endIndex);
+ final int selStart = startIndex;
+ final int selEnd = endIndex;
+ PollingCheck.waitFor(() -> mTextView.getSelectionStart() == selStart
+ && mTextView.getSelectionEnd() == selEnd);
- // Click one more time to dismiss the selection.
+ // Tap one more time to dismiss the selection.
emulateClickOnView(mTextView, offset.x, offset.y);
assertFalse(mTextView.hasSelection());
}
diff --git a/tools/cts-tradefed/res/config/cts-device-files.xml b/tools/cts-tradefed/res/config/cts-device-files.xml
new file mode 100644
index 0000000..e558a56
--- /dev/null
+++ b/tools/cts-tradefed/res/config/cts-device-files.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="CTS device files collection">
+ <option name="plan" value="cts-device-files" />
+
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DeviceFileCollector">
+ <option name="src-file" value="/sys/fs/selinux/policy" />
+ <option name="dest-file" value="selinux-files/policy"/>
+ </target_preparer>
+
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DeviceFileCollector">
+ <option name="src-file" value="/system/manifest.xml" />
+ <option name="dest-file" value="vintf-files/system/manifest.xml"/>
+ </target_preparer>
+
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DeviceFileCollector">
+ <option name="src-file" value="/system/compatibility_matrix.xml" />
+ <option name="dest-file" value="vintf-files/system/compatibility_matrix.xml"/>
+ </target_preparer>
+
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DeviceFileCollector">
+ <option name="src-file" value="/vendor/manifest.xml" />
+ <option name="dest-file" value="vintf-files/vendor/manifest.xml"/>
+ </target_preparer>
+
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DeviceFileCollector">
+ <option name="src-file" value="/vendor/compatibility_matrix.xml" />
+ <option name="dest-file" value="vintf-files/vendor/compatibility_matrix.xml"/>
+ </target_preparer>
+
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DeviceFileCollector">
+ <option name="src-file" value="/proc/config.gz" />
+ <option name="dest-file" value="vintf-files/proc/config.gz"/>
+ </target_preparer>
+</configuration>
diff --git a/tools/cts-tradefed/res/config/cts-known-failures.xml b/tools/cts-tradefed/res/config/cts-known-failures.xml
index 1d86477..f694daa 100644
--- a/tools/cts-tradefed/res/config/cts-known-failures.xml
+++ b/tools/cts-tradefed/res/config/cts-known-failures.xml
@@ -19,12 +19,6 @@
<!-- <option name="compatibility:exclude-filter" value="MODULE_NAME PACKAGE_NAME.CLASS_NAME" /> Excludes whole class -->
<!-- <option name="compatibility:exclude-filter" value="MODULE_NAME PACKAGE_NAME.CLASS_NAME#TEST_NAME" /> Excludes individual test -->
- <!-- b/37519234 -->
- <!-- Turn off VR virtual display tests till VR mode issues are resolved -->
- <option name="compatibility:exclude-filter" value="CtsServicesHostTestCases android.server.cts.ActivityManagerDisplayTests#testVrActivityLaunch" />
- <option name="compatibility:exclude-filter" value="CtsServicesHostTestCases android.server.cts.ActivityManagerDisplayTests#testVrActivityReLaunch" />
- <option name="compatibility:exclude-filter" value="CtsServicesHostTestCases android.server.cts.ActivityManagerDisplayTests#testActivityLaunchPostVr" />
-
<!-- b/35314835 -->
<option name="compatibility:exclude-filter" value="CtsServicesHostTestCases android.server.cts.ActivityManagerPinnedStackTests#testAlwaysFocusablePipActivity" />
<option name="compatibility:exclude-filter" value="CtsServicesHostTestCases android.server.cts.ActivityManagerPinnedStackTests#testLaunchIntoPinnedStack" />
diff --git a/tools/cts-tradefed/res/config/cts-preconditions.xml b/tools/cts-tradefed/res/config/cts-preconditions.xml
index 90c7b7c..21215df 100644
--- a/tools/cts-tradefed/res/config/cts-preconditions.xml
+++ b/tools/cts-tradefed/res/config/cts-preconditions.xml
@@ -15,6 +15,8 @@
-->
<configuration description="CTS precondition configs">
+ <include name="cts-device-files" />
+
<option name="plan" value="cts-preconditions" />
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher">
diff --git a/tools/cts-tradefed/res/config/cts-reference-aosp.xml b/tools/cts-tradefed/res/config/cts-reference-aosp.xml
new file mode 100644
index 0000000..88ef596
--- /dev/null
+++ b/tools/cts-tradefed/res/config/cts-reference-aosp.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Runs a subset of CTS tests using a reference AOSP system image">
+
+ <include name="cts" />
+
+ <option name="plan" value="cts-reference-aosp" />
+
+ <option name="compatibility:primary-abi-only" value="true" />
+
+ <!-- Tell all AndroidJUnitTests to exclude certain annotations -->
+ <option name="compatibility:test-arg" value="com.android.tradefed.testtype.AndroidJUnitTest:exclude-annotation:android.platform.test.annotations.RestrictedBuildTest" />
+
+ <!-- Tell all HostTests to exclude certain annotations -->
+ <option name="compatibility:test-arg" value="com.android.tradefed.testtype.HostTest:exclude-annotation:android.platform.test.annotations.RestrictedBuildTest" />
+ <option name="compatibility:test-arg" value="com.android.compatibility.common.tradefed.testtype.JarHostTest:exclude-annotation:android.platform.test.annotations.RestrictedBuildTest" />
+
+ <!-- Radio system of a reference AOSP system image is not checked -->
+ <option name="compatibility:exclude-filter" value="CtsTelephonyTestCases" />
+ <option name="compatibility:exclude-filter" value="CtsTelephony2TestCases" />
+</configuration>
diff --git a/tools/cts-tradefed/res/config/cts-vendor-interface.xml b/tools/cts-tradefed/res/config/cts-vendor-interface.xml
index 6785dd8..73ec49b 100644
--- a/tools/cts-tradefed/res/config/cts-vendor-interface.xml
+++ b/tools/cts-tradefed/res/config/cts-vendor-interface.xml
@@ -13,7 +13,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<configuration description="Runs a subset of CTS tests for the Treble 'CTS on AOSP' requirement">
+<configuration description="Runs a subset of CTS tests that can heavily exercise HALs">
<include name="cts" />