Merge "Add BootBroadcastReceiver required permissions" into main
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 810538e..340bee3 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -632,6 +632,17 @@
android:value="true" />
</activity>
+ <activity android:name="Settings$CellularSecuritySettingsActivity"
+ android:label="@string/cellular_security_settings_title"
+ android:exported="true">
+ <intent-filter android:priority="1">
+ <action android:name="android.settings.CELLULAR_NETWORK_SECURITY" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
+ android:value="com.android.settings.network.telephony.CellularSecuritySettingsFragment"/>
+ </activity>
+
<activity android:name="Settings$SatelliteSettingActivity"
android:label="@string/satellite_setting"
android:exported="true"
diff --git a/res/values/strings.xml b/res/values/strings.xml
index b275638..de733b8 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1236,8 +1236,10 @@
<string name="private_space_category_ways_to_unlock">Ways to unlock</string>
<!-- Summary for one lock when device screen lock is used as private profile lock. [CHAR LIMIT=40] -->
<string name="private_space_screen_lock_summary">Same as device screen lock</string>
- <!-- Dialog message to choose a new lock for Private Space. [CHAR LIMIT=50] -->
+ <!-- Dialog title to choose a new lock for Private Space. [CHAR LIMIT=50] -->
<string name="private_space_new_lock_title">Choose a new lock for private space?</string>
+ <!-- Dialog message mentioning device PIN, pattern or password needs to be entered on the next screen. [CHAR LIMIT=NONE] -->
+ <string name="private_space_new_lock_message">You\’ll need to enter your device PIN, pattern, or password on the next screen</string>
<!-- Title for the preference for auto lock private space and auto lock private space settings page. [CHAR LIMIT=50] -->
<string name="private_space_auto_lock_title">Lock private space automatically</string>
<!-- Description for private space auto lock settings page. [CHAR LIMIT=NONE] -->
@@ -1248,28 +1250,26 @@
<string name="private_space_auto_lock_after_inactivity">After 5 minutes of inactivity</string>
<!-- Configure auto lock: Value for auto lock configuration to never lock private space. [CHAR LIMIT=20] -->
<string name="private_space_auto_lock_never">Never</string>
- <!-- Title for the preference to hide Private Space. [CHAR LIMIT=60] -->
- <string name="private_space_hide_title">Hide when locked</string>
- <!-- Title for the hide Private Space setting. [CHAR LIMIT=60] -->
- <string name="privatespace_hide_page_title">Hide private space when locked</string>
+ <!-- Title for the settings preference and settings page for hide private space when locked setting. [CHAR LIMIT=60] -->
+ <string name="private_space_hide_page_title">Hide private space when it\'s locked</string>
<!-- Description for hide Private Space settings page. [CHAR LIMIT=NONE] -->
- <string name="privatespace_hide_page_summary">To stop other people knowing you have a private space on your device, you can hide it from your apps list</string>
+ <string name="private_space_hide_page_summary">To stop other people knowing you have a private space on your device, you can hide it from your apps list</string>
<!-- Header in hide Private Space settings page to access Private Space when hidden. [CHAR LIMIT=60] -->
- <string name="privatespace_access_header">Access private space when hidden</string>
+ <string name="private_space_access_header">Access private space when hidden</string>
<!-- Text in hide Private Space settings page on how to search Private Space when hidden. [CHAR LIMIT=90] -->
- <string name="privatespace_search_description">From your apps list, enter \"private space\" in the search bar</string>
+ <string name="private_space_search_description">In the search bar, enter \"private space\"</string>
<!-- Text in hide Private Space settings page to tap on Private Space tile. [CHAR LIMIT=60] -->
- <string name="privatespace_tap_tile_description">Tap the private space tile</string>
+ <string name="private_space_tap_tile_description">Tap <b>Private space</b></string>
<!-- Text in hide Private Space settings page to Unlock Private Space. [CHAR LIMIT=60] -->
- <string name="privatespace_unlock_description">Unlock your private space</string>
+ <string name="private_space_unlock_description">Unlock your private space</string>
<!-- Used to describe the off state of Private space hidden [CHAR LIMIT=30] -->
- <string name="privatespace_hide_off_summary">Off</string>
+ <string name="private_space_hide_off_summary">Off</string>
<!-- Used to describe the off state of Private space hidden [CHAR LIMIT=30] -->
- <string name="privatespace_hide_on_summary">On</string>
+ <string name="private_space_hide_on_summary">On</string>
<!-- Title for the dialog shown when Private space hidden setting is turned on. [CHAR LIMIT=60] -->
- <string name="private_space_hide_dialog_title">Private space will hide when you lock it next</string>
+ <string name="private_space_hide_dialog_title">Private space will hide the next time you lock it</string>
<!-- Text message in the dialog shown when Private space hidden setting is turned on. [CHAR LIMIT=90] -->
- <string name="private_space_hide_dialog_message">To access your space when it’s hidden, enter \“private space\” in the search bar on your apps list</string>
+ <string name="private_space_hide_dialog_message">To access private space, enter \“private space\” in the search bar from your apps list.</string>
<!-- Label for the dialog shown when Private space hidden setting is turned on. [CHAR LIMIT=90] -->
<string name="private_space_hide_dialog_button">Got it</string>
<!-- System category for the Private Space page. [CHAR LIMIT=30] -->
@@ -1554,20 +1554,12 @@
<string name="unlock_disable_frp_warning_content_pattern_face_fingerprint">"A pattern protects your phone if it\u2019s lost or stolen.<xliff:g id="empty_line">\n\n</xliff:g>This deletes the fingerprint model stored on your device. Your face model will also be permanently and securely deleted. You won\u2019t be able to use your face or fingerprint for authentication in apps."</string>
<!-- Content of the dialog shown when the user removes the device lock PIN [CHAR LIMIT=NONE] -->
<string name="unlock_disable_frp_warning_content_pin">"A PIN protects your phone if it\u2019s lost or stolen"</string>
- <!-- Content of the dialog shown when the user removes the device lock PIN and there are apps with auth-bound keys that will be affected [CHAR LIMIT=NONE] -->
- <string name="unlock_disable_frp_warning_content_pin_authbound_keys">"You will lose saved data like your PIN.<xliff:g id="empty_line">\n\n</xliff:g>Cards set up for tap to pay will be removed.<xliff:g id="empty_line">\n\n</xliff:g>Wallets and other apps that require device unlock may not work properly."</string>
<!-- Content of the dialog shown when the user removes the device lock PIN and the user has fingerprints enrolled [CHAR LIMIT=NONE] -->
<string name="unlock_disable_frp_warning_content_pin_fingerprint">"A PIN protects your phone if it\u2019s lost or stolen.<xliff:g id="empty_line">\n\n</xliff:g>This also deletes the fingerprint model stored on your device. You won\u2019t be able to use your fingerprint for authentication in apps."</string>
- <!-- Content of the dialog shown when the user removes the device lock PIN and the user has fingerprints enrolled and there are apps with auth-bound keys that will be affected [CHAR LIMIT=NONE] -->
- <string name="unlock_disable_frp_warning_content_pin_fingerprint_authbound_keys">"You will lose saved data like your PIN and fingerprint model.<xliff:g id="empty_line">\n\n</xliff:g>Cards set up for tap to pay will be removed.<xliff:g id="empty_line">\n\n</xliff:g>Wallets and other apps that require device unlock may not work properly."</string>
<!-- Content of the dialog shown when the user removes the device lock PIN and the user has face enrolled [CHAR LIMIT=NONE] -->
<string name="unlock_disable_frp_warning_content_pin_face">"A PIN protects your phone if it\u2019s lost or stolen.<xliff:g id="empty_line">\n\n</xliff:g>Your face model will also be permanently and securely deleted. You won\u2019t be able to use your face for authentication in apps."</string>
- <!-- Content of the dialog shown when the user removes the device lock PIN and the user has face enrolled and there are apps with auth-bound keys that will be affected [CHAR LIMIT=NONE] -->
- <string name="unlock_disable_frp_warning_content_pin_face_authbound_keys">"You will lose saved data like your PIN and face model.<xliff:g id="empty_line">\n\n</xliff:g>Cards set up for tap to pay will be removed.<xliff:g id="empty_line">\n\n</xliff:g>Wallets and other apps that require device unlock may not work properly."</string>
<!-- Content of the dialog shown when the user removes the device lock PIN and the user has face authentication and fingerprint enrolled [CHAR LIMIT=NONE] -->
<string name="unlock_disable_frp_warning_content_pin_face_fingerprint">"A PIN protects your phone if it\u2019s lost or stolen.<xliff:g id="empty_line">\n\n</xliff:g>This deletes the fingerprint model stored on your device. Your face model will also be permanently and securely deleted. You won\u2019t be able to use your face or fingerprint for authentication in apps."</string>
- <!-- Content of the dialog shown when the user removes the device lock PIN and the user has face authentication and fingerprint enrolled and there are apps with auth-bound keys that will be affected [CHAR LIMIT=NONE] -->
- <string name="unlock_disable_frp_warning_content_pin_face_fingerprint_authbound_keys">"You will lose saved data like your PIN, face and fingerprint models.<xliff:g id="empty_line">\n\n</xliff:g>Cards set up for tap to pay will be removed.<xliff:g id="empty_line">\n\n</xliff:g>Wallets and other apps that require device unlock may not work properly."</string>
<!-- Content of the dialog shown when the user removes the device lock password [CHAR LIMIT=NONE] -->
<string name="unlock_disable_frp_warning_content_password">"A password protects your phone if it\u2019s lost or stolen"</string>
<!-- Content of the dialog shown when the user removes the device lock password and the user has fingerprints enrolled [CHAR LIMIT=NONE] -->
@@ -1584,6 +1576,15 @@
<string name="unlock_disable_frp_warning_content_unknown_face">"Device protection features will not work without your screen lock.<xliff:g id="empty_line">\n\n</xliff:g>Your face model will also be permanently and securely deleted. You won\u2019t be able to use your face for authentication in apps."</string>
<!-- Content of the dialog shown when the user removes the device lock of unknown type and the user has face authentication and fingerprint enrolled [CHAR LIMIT=NONE] -->
<string name="unlock_disable_frp_warning_content_unknown_face_fingerprint">"Device protection features will not work without your screen lock.<xliff:g id="empty_line">\n\n</xliff:g>This deletes the fingerprint model stored on your device. Your face model will also be permanently and securely deleted. You won\u2019t be able to use your face or fingerprint for authentication in apps."</string>
+ <!-- Content of the dialog shown when the user removes any device screenlock and there are apps with auth-bound keys that will be affected [CHAR LIMIT=NONE] -->
+ <string name="unlock_disable_frp_warning_content_authbound_keys">"Your screen lock will be removed.<xliff:g id="empty_line">\n\n</xliff:g>Tap to pay won\u2019t be available.<xliff:g id="empty_line">\n\n</xliff:g>Wallet, payment, and other apps that require authentication may not work properly."</string>
+ <!-- Content of the dialog shown when the user removes any device screen lock and the user has fingerprints enrolled and there are apps with auth-bound keys that will be affected [CHAR LIMIT=NONE] -->
+ <string name="unlock_disable_frp_warning_content_fingerprint_authbound_keys">"Your screen lock and Fingerprint Unlock will be removed.<xliff:g id="empty_line">\n\n</xliff:g>Tap to pay won\u2019t be available.<xliff:g id="empty_line">\n\n</xliff:g>Wallet, payment, and other apps that require authentication may not work properly."</string>
+ <!-- Content of the dialog shown when the user removes any device screen lock and the user has face enrolled and there are apps with auth-bound keys that will be affected [CHAR LIMIT=NONE] -->
+ <string name="unlock_disable_frp_warning_content_face_authbound_keys">"Your screen lock and Face Unlock will be removed.<xliff:g id="empty_line">\n\n</xliff:g>Tap to pay won\u2019t be available.<xliff:g id="empty_line">\n\n</xliff:g>Wallet, payment, and other apps that require authentication may not work properly."</string>
+ <!-- Content of the dialog shown when the user removes any device screen lock and the user has face authentication and fingerprint enrolled and there are apps with auth-bound keys that will be affected [CHAR LIMIT=NONE] -->
+ <string name="unlock_disable_frp_warning_content_face_fingerprint_authbound_keys">"Your screen lock and Face & Fingerprint Unlock will be removed.<xliff:g id="empty_line">\n\n</xliff:g>Tap to pay won\u2019t be available.<xliff:g id="empty_line">\n\n</xliff:g>Wallet, payment, and other apps that require authentication may not work properly."</string>
+
<!-- Affirmative action of the dialog shown when the user removes the device lock [CHAR LIMIT=25] -->
<string name="unlock_disable_frp_warning_ok">Delete</string>
@@ -2859,7 +2860,7 @@
<!-- Dark UI screen footer action text shown when the when Dark theme turns on/off automatically according to a user bedtime schedule. [CHAR LIMIT=NONE] -->
<string name="dark_ui_bedtime_footer_action">Bedtime mode settings</string>
<!-- Even Dimmer setting title. Allows device to reduce brightness even further than standard range. [CHAR LIMIT=NONE] -->
- <string name="even_dimmer_display_title">Even Dimmer</string>
+ <string name="even_dimmer_display_title">Even dimmer</string>
<!-- Even Dimmer setting summary. [CHAR LIMIT=NONE] -->
<string name="even_dimmer_display_summary">Allow device to go dimmer than usual</string>
@@ -3203,9 +3204,18 @@
<!-- Body of dialog confirming that user wants to forget an internal storage device [CHAR LIMIT=NONE]-->
<string name="storage_internal_forget_confirm">All the apps, photos, and data stored on this <xliff:g id="name" example="SD card">^1</xliff:g> will be lost forever.</string>
- <!-- Body of dialog informing user about the storage used by the Android System [CHAR LIMIT=NONE]-->
+ <!-- Old body of dialog informing user about the storage used by the Android System [CHAR LIMIT=NONE]-->
<string name="storage_detail_dialog_system">System includes files used to run Android version <xliff:g id="version" example="8.0">%s</xliff:g></string>
+ <!-- New body of dialog informing user about the storage used by the Android System [CHAR LIMIT=NONE]-->
+ <string name="storage_os_detail_dialog_system">This includes your operating system and the files that are needed to keep your phone running smoothly. To protect their integrity, these files can\u2019t be accessed.</string>
+
+ <!-- Body of dialog informing user about the storage used by the Android temporary system files [CHAR LIMIT=NONE]-->
+ <string name="storage_other_files_detail_dialog_system">This includes cache and other temporary files that are needed by your operating system. You may notice changes to the amount of storage used over time.</string>
+
+ <!-- Label for categories splitter in Settings > Storage [CHAR LIMIT=none] -->
+ <string name="storage_system_label">System</string>
+
<!-- Message to notify guest users as to why they can't set up the storage device [CHAR LIMIT=50]-->
<string name="storage_wizard_guest">Guest mode users cannot format SD cards</string>
@@ -6206,6 +6216,29 @@
<!-- Title for adaptive connectivity main switch preferences. [CHAR LIMIT=50] -->
<string name="adaptive_connectivity_main_switch_title">Use adaptive connectivity</string>
+ <!-- Cellular security related strings -->
+ <!-- Title of Cellular security tile in Network & Internet settings page. [CHAR LIMIT=60]-->
+ <string name="cellular_security_title">Cellular network security</string>
+ <!-- Summary of Cellular security tile in Network & Internet settings page. [CHAR LIMIT=NONE]-->
+ <string name="cellular_security_summary">Network type, encryption, notification controls</string>
+
+ <!-- Title of cellular security settings page [CHAR LIMIT=60]-->
+ <string name="cellular_security_settings_title">Cellular network security</string>
+ <!-- Title of Cellular security notifications section [CHAR LIMIT=60]-->
+ <string name="cellular_security_notifications">Notifications</string>
+ <!-- Title of Cellular security notifications toggle [CHAR LIMIT=60]-->
+ <string name="cellular_security_notifications_controller_title">Security notifications</string>
+ <!-- Summary of Cellular security notifications toggle [CHAR LIMIT=NONE]-->
+ <string name="cellular_security_notifications_controller_summary">Receive notifications in case the cellular network you are connected to is insecure due to lack of encryption, or if the cellular network records your unique decive or SIM identifiers (IMEI & IMSI)</string>
+
+ <!--Cellular encryption -->
+ <!--Cellular encryption title [CHAR LIMIT=60] -->
+ <string name="cellular_security_settings_encryption_title">Encryption</string>
+ <!-- Title of Cellular security Network generations divider [CHAR LIMIT=30]-->
+ <string name="cellular_security_network_generations_title">Network generations</string>
+ <!-- Summary of Cellular security Network generations divider [CHAR LIMIT=NONE]-->
+ <string name="cellular_security_network_generations_summary">You can configure each installed SIM card to only connect to networks that support 3G, 4G, and 5G. The SIM will not connect to older, insecure 2G networks. This setting may limit your connectivity in case the only available network is 2G. In case of an emergency, 2G may be used.</string>
+
<!-- Title of preference group for credential storage settings [CHAR LIMIT=30] -->
<string name="credentials_title">Credential storage</string>
<!-- Title of preference to install certificates [CHAR LIMIT=30] -->
@@ -10522,7 +10555,7 @@
<string name="back_sensitivity_dialog_title">Back Sensitivity</string>
<!-- Title for the screen to show all the gesture navigation settings [CHAR LIMIT=80] -->
- <string name="gesture_settings_activity_title">Gesture Navigation Sensitivity</string>
+ <string name="gesture_settings_activity_title">Gesture Navigation</string>
<!-- Title for the screen to show all the 2- and 3-button navigation settings. [CHAR LIMIT=80] -->
<string name="button_navigation_settings_activity_title">Button navigation</string>
@@ -10854,15 +10887,27 @@
<!-- Preference label for the Audio storage section. [CHAR LIMIT=50] -->
<string name="storage_audio">Audio</string>
+ <!-- Preference label for the Documents storage section. [CHAR LIMIT=50] -->
+ <string name="storage_documents">Documents</string>
+
+ <!-- Preference label for the Other storage section. [CHAR LIMIT=50] -->
+ <string name="storage_other">Other</string>
+
<!-- Preference label for the Apps storage section. [CHAR LIMIT=50] -->
<string name="storage_apps">Apps</string>
<!-- Preference label for the Documents & other storage section. [CHAR LIMIT=50] -->
<string name="storage_documents_and_other">Documents & other</string>
- <!-- Preference label for the System storage section. [CHAR LIMIT=50] -->
+ <!-- Old Preference label for the System storage section. [CHAR LIMIT=50] -->
<string name="storage_system">System</string>
+ <!-- New Preference label for the System storage section. [CHAR LIMIT=50] -->
+ <string name="storage_os_name">Android <xliff:g id="version" example="8">%s</xliff:g></string>
+
+ <!-- Preference label for the System storage section. [CHAR LIMIT=50] -->
+ <string name="storage_temporary_files">Temporary system files</string>
+
<!-- Preference label for the Trash storage section. [CHAR LIMIT=50] -->
<string name="storage_trash">Trash</string>
@@ -12143,7 +12188,7 @@
<!-- Developer settings: Title for disable app and notification screen share protections [CHAR LIMIT=50] -->
<string name="disable_screen_share_protections_for_apps_and_notifications">Disable screen share protections</string>
<!-- Developer settings: Summary for disable app and notification screen share protections summary [CHAR LIMIT=150] -->
- <string name="disable_screen_share_protections_for_apps_and_notifications_summary">Disables system applied app and notifications protections during screen sharing</string>
+ <string name="disable_screen_share_protections_for_apps_and_notifications_summary">Turn off system protections for sensitive app content for upcoming screen share sessions</string>
<!-- Title for media control settings [CHAR LIMIT=50]-->
<string name="media_controls_title">Media</string>
diff --git a/res/xml/cellular_security.xml b/res/xml/cellular_security.xml
new file mode 100644
index 0000000..e5fee15
--- /dev/null
+++ b/res/xml/cellular_security.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<PreferenceScreen
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:settings="http://schemas.android.com/apk/res-auto"
+ android:key="cellular_security_settings_screen"
+ android:title="@string/cellular_security_settings_title">
+ <PreferenceCategory
+ android:key="cellular_security_notifications_category"
+ android:title="@string/cellular_security_notifications"
+ settings:controller="com.android.settings.network.CellularSecurityNotificationsDividerController">
+ <SwitchPreferenceCompat
+ android:key="cellular_security_notifications"
+ android:title="@string/cellular_security_notifications_controller_title"
+ android:summary="@string/cellular_security_notifications_controller_summary"
+ settings:controller=
+ "com.android.settings.network.CellularSecurityNotificationsPreferenceController"/>
+ </PreferenceCategory>
+ <PreferenceCategory
+ android:title="@string/cellular_security_settings_encryption_title"
+ settings:controller="com.android.settings.network.CellularSecurityEncryptionDividerController">
+ <SwitchPreferenceCompat
+ android:key="require_cellular_encryption"
+ android:title="@string/require_cellular_encryption_title"
+ android:summary="@string/require_cellular_encryption_summary"
+ settings:controller=
+ "com.android.settings.network.telephony.NullAlgorithmsPreferenceController"/>
+ </PreferenceCategory>
+</PreferenceScreen>
diff --git a/res/xml/more_security_privacy_settings.xml b/res/xml/more_security_privacy_settings.xml
index 92c3fa7..3e11db2 100644
--- a/res/xml/more_security_privacy_settings.xml
+++ b/res/xml/more_security_privacy_settings.xml
@@ -118,6 +118,12 @@
android:summary="@string/content_capture_summary"
settings:controller="com.android.settings.privacy.EnableContentCaptureWithServiceSettingsPreferenceController"/>
+ <Preference
+ android:key="cellular_security_settings_privacy"
+ android:title="@string/cellular_security_title"
+ android:summary="@string/cellular_security_summary"
+ android:fragment="com.android.settings.network.telephony.CellularSecuritySettingsFragment"
+ settings:searchable="false"/>
</PreferenceCategory>
<!-- Security section. -->
diff --git a/res/xml/network_provider_internet.xml b/res/xml/network_provider_internet.xml
index 04f248e..2a08aae 100644
--- a/res/xml/network_provider_internet.xml
+++ b/res/xml/network_provider_internet.xml
@@ -109,4 +109,11 @@
android:summary="@string/summary_placeholder"
android:order="25"
settings:controller="com.android.settings.network.AdaptiveConnectivityPreferenceController"/>
+
+ <Preference
+ android:key="cellular_security_network_internet"
+ android:title="@string/cellular_security_title"
+ android:summary="@string/cellular_security_summary"
+ android:order="30"
+ settings:controller="com.android.settings.network.CellularSecurityPreferenceController"/>
</PreferenceScreen>
diff --git a/res/xml/private_space_hide_locked.xml b/res/xml/private_space_hide_locked.xml
index cd1c406..4c83e84 100644
--- a/res/xml/private_space_hide_locked.xml
+++ b/res/xml/private_space_hide_locked.xml
@@ -16,7 +16,7 @@
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res-auto"
- android:title="@string/privatespace_hide_page_title">
+ android:title="@string/private_space_hide_page_title">
<com.android.settingslib.widget.IllustrationPreference
android:key="privatespace_hide_video"
@@ -25,36 +25,36 @@
<com.android.settingslib.widget.MainSwitchPreference
android:key="hide_when_locked"
- android:title="@string/privatespace_hide_page_title"
+ android:title="@string/private_space_hide_page_title"
settings:controller="com.android.settings.privatespace.HidePrivateSpaceController"/>
<Preference
android:key="private_space_hidden"
- android:summary="@string/privatespace_hide_page_summary"
+ android:summary="@string/private_space_hide_page_summary"
android:selectable="false"
settings:searchable="false" />
<PreferenceCategory
- android:title="@string/privatespace_access_header">
+ android:title="@string/private_space_access_header">
<Preference
android:key="search_when_locked_footer"
android:icon="@drawable/counter_1_24dp"
- android:title="@string/privatespace_search_description"
+ android:title="@string/private_space_search_description"
android:selectable="false"
settings:searchable="false" />
<Preference
android:key="tap_tile_footer"
android:icon="@drawable/counter_2_24dp"
- android:title="@string/privatespace_tap_tile_description"
+ android:title="@string/private_space_tap_tile_description"
android:selectable="false"
settings:searchable="false" />
<Preference
android:key="unlock_profile_footer"
android:icon="@drawable/counter_3_24dp"
- android:title="@string/privatespace_unlock_description"
+ android:title="@string/private_space_unlock_description"
android:selectable="false"
settings:searchable="false" />
diff --git a/res/xml/private_space_settings.xml b/res/xml/private_space_settings.xml
index 86537cc..f979599 100644
--- a/res/xml/private_space_settings.xml
+++ b/res/xml/private_space_settings.xml
@@ -52,7 +52,7 @@
<Preference
android:key="private_space_hidden"
- android:title="@string/private_space_hide_title"
+ android:title="@string/private_space_hide_page_title"
android:fragment="com.android.settings.privatespace.HidePrivateSpaceSettings"
settings:controller="com.android.settings.privatespace.HidePrivateSpaceSummaryController"
settings:searchable="false" />
diff --git a/src/com/android/settings/MainClear.java b/src/com/android/settings/MainClear.java
index 87b11f7..0639037 100644
--- a/src/com/android/settings/MainClear.java
+++ b/src/com/android/settings/MainClear.java
@@ -17,6 +17,7 @@
package com.android.settings;
import static android.app.admin.DevicePolicyResources.Strings.Settings.PERSONAL_CATEGORY_HEADER;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.PRIVATE_CATEGORY_HEADER;
import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_CATEGORY_HEADER;
import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
@@ -505,6 +506,9 @@
final UserInfo userInfo = profiles.get(profileIndex);
final int profileId = userInfo.id;
final UserHandle userHandle = new UserHandle(profileId);
+ if (Utils.shouldHideUser(userHandle, um)) {
+ continue;
+ }
Account[] accounts = mgr.getAccountsAsUser(profileId);
final int accountLength = accounts.length;
if (accountLength == 0) {
@@ -529,6 +533,13 @@
titleText.setText(devicePolicyManager.getResources().getString(
WORK_CATEGORY_HEADER, () -> getString(
com.android.settingslib.R.string.category_work)));
+ } else if (android.os.Flags.allowPrivateProfile()
+ && android.multiuser.Flags.enablePrivateSpaceFeatures()
+ && android.multiuser.Flags.handleInterleavedSettingsForPrivateSpace()
+ && userInfo.isPrivateProfile()) {
+ titleText.setText(devicePolicyManager.getResources().getString(
+ PRIVATE_CATEGORY_HEADER, () -> getString(
+ com.android.settingslib.R.string.category_private)));
} else {
titleText.setText(devicePolicyManager.getResources().getString(
PERSONAL_CATEGORY_HEADER, () -> getString(
diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java
index 2f6f04a..4c2ee67 100644
--- a/src/com/android/settings/Settings.java
+++ b/src/com/android/settings/Settings.java
@@ -348,6 +348,7 @@
/* empty */
}
+ public static class CellularSecuritySettingsActivity extends SettingsActivity { /* empty */ }
public static class SatelliteSettingActivity extends SettingsActivity { /* empty */ }
public static class ApnSettingsActivity extends SettingsActivity { /* empty */ }
public static class WifiCallingSettingsActivity extends SettingsActivity { /* empty */ }
diff --git a/src/com/android/settings/TrustedCredentialsFragment.java b/src/com/android/settings/TrustedCredentialsFragment.java
index a150850..fc8fea3 100644
--- a/src/com/android/settings/TrustedCredentialsFragment.java
+++ b/src/com/android/settings/TrustedCredentialsFragment.java
@@ -17,6 +17,7 @@
package com.android.settings;
import static android.app.admin.DevicePolicyResources.Strings.Settings.PERSONAL_CATEGORY_HEADER;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.PRIVATE_CATEGORY_HEADER;
import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_CATEGORY_HEADER;
import static android.widget.LinearLayout.LayoutParams.MATCH_PARENT;
import static android.widget.LinearLayout.LayoutParams.WRAP_CONTENT;
@@ -108,18 +109,37 @@
mKeyChainConnectionByProfileId = new SparseArray<>();
private ViewGroup mFragmentView;
- private final BroadcastReceiver mWorkProfileChangedReceiver = new BroadcastReceiver() {
+ private final BroadcastReceiver mProfileChangedReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (Intent.ACTION_MANAGED_PROFILE_AVAILABLE.equals(action)
- || Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE.equals(action)
- || Intent.ACTION_MANAGED_PROFILE_UNLOCKED.equals(action)) {
+ if (isBroadcastValidForAction(intent)) {
mGroupAdapter.load();
}
}
};
+ private boolean isBroadcastValidForAction(Intent intent) {
+ String action = intent.getAction();
+ if (android.os.Flags.allowPrivateProfile()
+ && android.multiuser.Flags.enablePrivateSpaceFeatures()
+ && android.multiuser.Flags.handleInterleavedSettingsForPrivateSpace()) {
+ UserHandle userHandle = intent.getParcelableExtra(Intent.EXTRA_USER, UserHandle.class);
+ if (userHandle == null) {
+ Log.w(TAG, "received action " + action + " with missing user extra");
+ return false;
+ }
+
+ UserInfo userInfo = mUserManager.getUserInfo(userHandle.getIdentifier());
+ return (Intent.ACTION_PROFILE_AVAILABLE.equals(action)
+ || Intent.ACTION_PROFILE_UNAVAILABLE.equals(action)
+ || Intent.ACTION_PROFILE_ACCESSIBLE.equals(action))
+ && (userInfo.isManagedProfile() || userInfo.isPrivateProfile());
+ }
+ return (Intent.ACTION_MANAGED_PROFILE_AVAILABLE.equals(action)
+ || Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE.equals(action)
+ || Intent.ACTION_MANAGED_PROFILE_UNLOCKED.equals(action));
+ }
+
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -142,10 +162,18 @@
}
IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE);
- filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
- filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNLOCKED);
- activity.registerReceiver(mWorkProfileChangedReceiver, filter);
+ if (android.os.Flags.allowPrivateProfile()
+ && android.multiuser.Flags.enablePrivateSpaceFeatures()
+ && android.multiuser.Flags.handleInterleavedSettingsForPrivateSpace()) {
+ filter.addAction(Intent.ACTION_PROFILE_AVAILABLE);
+ filter.addAction(Intent.ACTION_PROFILE_UNAVAILABLE);
+ filter.addAction(Intent.ACTION_PROFILE_ACCESSIBLE);
+ } else {
+ filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE);
+ filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
+ filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNLOCKED);
+ }
+ activity.registerReceiver(mProfileChangedReceiver, filter);
}
@Override
@@ -177,7 +205,16 @@
private void createChildView(
LayoutInflater inflater, ViewGroup parent, Bundle childState, int i) {
- boolean isWork = mGroupAdapter.getUserInfoByGroup(i).isManagedProfile();
+ UserInfo userInfo = mGroupAdapter.getUserInfoByGroup(i);
+ if (Utils.shouldHideUser(userInfo.getUserHandle(), mUserManager)) {
+ return;
+ }
+ boolean isProfile = userInfo.isManagedProfile();
+ if (android.os.Flags.allowPrivateProfile()
+ && android.multiuser.Flags.enablePrivateSpaceFeatures()
+ && android.multiuser.Flags.handleInterleavedSettingsForPrivateSpace()) {
+ isProfile |= userInfo.isPrivateProfile();
+ }
ChildAdapter adapter = mGroupAdapter.createChildAdapter(i);
LinearLayout containerView = (LinearLayout) inflater.inflate(
@@ -186,9 +223,9 @@
int profilesSize = mGroupAdapter.getGroupCount();
adapter.showHeader(profilesSize > 1);
- adapter.showDivider(isWork);
- adapter.setExpandIfAvailable(profilesSize <= 2 || !isWork, childState);
- if (isWork) {
+ adapter.showDivider(isProfile);
+ adapter.setExpandIfAvailable(profilesSize <= 2 || !isProfile, childState);
+ if (isProfile) {
parent.addView(containerView);
} else {
parent.addView(containerView, 0);
@@ -203,7 +240,7 @@
@Override
public void onDestroy() {
- getActivity().unregisterReceiver(mWorkProfileChangedReceiver);
+ getActivity().unregisterReceiver(mProfileChangedReceiver);
for (AdapterData.AliasLoader aliasLoader : mAliasLoaders) {
aliasLoader.cancel(true);
}
@@ -331,9 +368,16 @@
}
TextView title = convertView.findViewById(android.R.id.title);
- if (getUserInfoByGroup(groupPosition).isManagedProfile()) {
+ UserInfo userInfo = getUserInfoByGroup(groupPosition);
+ if (userInfo.isManagedProfile()) {
title.setText(mDevicePolicyManager.getResources().getString(WORK_CATEGORY_HEADER,
() -> getString(com.android.settingslib.R.string.category_work)));
+ } else if (android.os.Flags.allowPrivateProfile()
+ && android.multiuser.Flags.enablePrivateSpaceFeatures()
+ && android.multiuser.Flags.handleInterleavedSettingsForPrivateSpace()
+ && userInfo.isPrivateProfile()) {
+ title.setText(mDevicePolicyManager.getResources().getString(PRIVATE_CATEGORY_HEADER,
+ () -> getString(com.android.settingslib.R.string.category_private)));
} else {
title.setText(mDevicePolicyManager.getResources().getString(
PERSONAL_CATEGORY_HEADER,
diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java
index c38ebfe..c4b0014 100644
--- a/src/com/android/settings/Utils.java
+++ b/src/com/android/settings/Utils.java
@@ -1365,6 +1365,16 @@
}
}
+ /**
+ * Returns true if the user should be hidden in Settings when it's in quiet mode.
+ */
+ public static boolean shouldHideUser(
+ @NonNull UserHandle userHandle, @NonNull UserManager userManager) {
+ UserProperties userProperties = userManager.getUserProperties(userHandle);
+ return userProperties.getShowInQuietMode() == UserProperties.SHOW_IN_QUIET_MODE_HIDDEN
+ && userManager.isQuietModeEnabled(userHandle);
+ }
+
private static FaceManager.RemovalCallback faceManagerRemovalCallback(int userId) {
return new FaceManager.RemovalCallback() {
@Override
diff --git a/src/com/android/settings/accessibility/AccessibilityServiceWarning.java b/src/com/android/settings/accessibility/AccessibilityServiceWarning.java
deleted file mode 100644
index fb50ef1..0000000
--- a/src/com/android/settings/accessibility/AccessibilityServiceWarning.java
+++ /dev/null
@@ -1,155 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.accessibility;
-
-import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
-
-import android.accessibilityservice.AccessibilityServiceInfo;
-import android.app.Dialog;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.graphics.drawable.Drawable;
-import android.text.BidiFormatter;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.Window;
-import android.view.WindowManager;
-import android.widget.Button;
-import android.widget.ImageView;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import androidx.annotation.NonNull;
-import androidx.appcompat.app.AlertDialog;
-import androidx.core.content.ContextCompat;
-
-import com.android.settings.R;
-
-import java.util.Locale;
-
-/**
- * Utility class for creating the dialog that asks users for explicit permission for an
- * accessibility service to access user data before the service is enabled
- */
-public class AccessibilityServiceWarning {
- private static final View.OnTouchListener filterTouchListener = (View v, MotionEvent event) -> {
- // Filter obscured touches by consuming them.
- if (((event.getFlags() & MotionEvent.FLAG_WINDOW_IS_OBSCURED) != 0)
- || ((event.getFlags() & MotionEvent.FLAG_WINDOW_IS_PARTIALLY_OBSCURED) != 0)) {
- if (event.getAction() == MotionEvent.ACTION_UP) {
- Toast.makeText(v.getContext(), R.string.touch_filtered_warning,
- Toast.LENGTH_SHORT).show();
- }
- return true;
- }
- return false;
- };
-
- /**
- * The interface to execute the uninstallation action.
- */
- interface UninstallActionPerformer {
- void uninstallPackage();
- }
-
- /**
- * Returns a {@link Dialog} to be shown to confirm that they want to enable a service.
- * @deprecated Use {@link com.android.internal.accessibility.dialog.AccessibilityServiceWarning}
- */
- @Deprecated
- public static Dialog createCapabilitiesDialog(@NonNull Context context,
- @NonNull AccessibilityServiceInfo info, @NonNull View.OnClickListener listener,
- @NonNull UninstallActionPerformer performer) {
- final AlertDialog ad = new AlertDialog.Builder(context)
- .setView(createEnableDialogContentView(context, info, listener, performer))
- .create();
-
- Window window = ad.getWindow();
- WindowManager.LayoutParams params = window.getAttributes();
- params.privateFlags |= SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
- window.setAttributes(params);
- ad.create();
- ad.setCanceledOnTouchOutside(true);
-
- return ad;
- }
-
- private static View createEnableDialogContentView(Context context,
- @NonNull AccessibilityServiceInfo info, View.OnClickListener listener,
- UninstallActionPerformer performer) {
- LayoutInflater inflater = (LayoutInflater) context.getSystemService(
- Context.LAYOUT_INFLATER_SERVICE);
-
- View content = inflater.inflate(R.layout.enable_accessibility_service_dialog_content,
- null);
-
- final Drawable icon;
- if (info.getResolveInfo().getIconResource() == 0) {
- icon = ContextCompat.getDrawable(context, R.drawable.ic_accessibility_generic);
- } else {
- icon = info.getResolveInfo().loadIcon(context.getPackageManager());
- }
-
- ImageView permissionDialogIcon = content.findViewById(
- R.id.permissionDialog_icon);
- permissionDialogIcon.setImageDrawable(icon);
-
- TextView permissionDialogTitle = content.findViewById(R.id.permissionDialog_title);
- permissionDialogTitle.setText(context.getString(R.string.enable_service_title,
- getServiceName(context, info)));
-
- Button permissionAllowButton = content.findViewById(
- R.id.permission_enable_allow_button);
- Button permissionDenyButton = content.findViewById(
- R.id.permission_enable_deny_button);
- permissionAllowButton.setOnClickListener(listener);
- permissionAllowButton.setOnTouchListener(filterTouchListener);
- permissionDenyButton.setOnClickListener(listener);
-
- final Button uninstallButton = content.findViewById(
- R.id.permission_enable_uninstall_button);
- // Shows an uninstall button to help users quickly remove the non-system App due to the
- // required permissions.
- if (!AccessibilityUtil.isSystemApp(info)) {
- uninstallButton.setVisibility(View.VISIBLE);
- uninstallButton.setOnClickListener(v -> performer.uninstallPackage());
- }
- return content;
- }
-
- /** Returns a {@link Dialog} to be shown to confirm that they want to disable a service. */
- public static Dialog createDisableDialog(Context context,
- AccessibilityServiceInfo info, DialogInterface.OnClickListener listener) {
- CharSequence serviceName = getServiceName(context, info);
-
- return new AlertDialog.Builder(context)
- .setTitle(context.getString(R.string.disable_service_title, serviceName))
- .setCancelable(true)
- .setPositiveButton(R.string.accessibility_dialog_button_stop, listener)
- .setNegativeButton(R.string.accessibility_dialog_button_cancel, listener)
- .create();
- }
-
- // Get the service name and bidi wrap it to protect from bidi side effects.
- private static CharSequence getServiceName(Context context, AccessibilityServiceInfo info) {
- final Locale locale = context.getResources().getConfiguration().getLocales().get(0);
- final CharSequence label =
- info.getResolveInfo().loadLabel(context.getPackageManager());
- return BidiFormatter.getInstance(locale).unicodeWrap(label);
- }
-}
diff --git a/src/com/android/settings/accessibility/InvisibleToggleAccessibilityServicePreferenceFragment.java b/src/com/android/settings/accessibility/InvisibleToggleAccessibilityServicePreferenceFragment.java
index 21ac998..eb7b27d 100644
--- a/src/com/android/settings/accessibility/InvisibleToggleAccessibilityServicePreferenceFragment.java
+++ b/src/com/android/settings/accessibility/InvisibleToggleAccessibilityServicePreferenceFragment.java
@@ -18,9 +18,7 @@
import android.accessibilityservice.AccessibilityServiceInfo;
import android.content.DialogInterface;
-import android.view.View;
-import com.android.settings.R;
import com.android.settingslib.accessibility.AccessibilityUtils;
/**
@@ -52,7 +50,6 @@
super.onToggleClicked(preference);
boolean enabled = getArguments().getBoolean(AccessibilitySettings.EXTRA_CHECKED)
&& preference.isChecked();
-
AccessibilityUtils.setAccessibilityServiceState(getContext(), mComponentName, enabled);
}
@@ -62,27 +59,9 @@
* Enables accessibility service when user clicks permission allow button.
*/
@Override
- void onDialogButtonFromShortcutToggleClicked(View view) {
- super.onDialogButtonFromShortcutToggleClicked(view);
- if (!android.view.accessibility.Flags.cleanupAccessibilityWarningDialog()) {
- if (view.getId() == R.id.permission_enable_allow_button) {
- AccessibilityUtils.setAccessibilityServiceState(getContext(), mComponentName,
- true);
- }
- }
- }
-
- /**
- * {@inheritDoc}
- *
- * Enables accessibility service when user clicks permission allow button.
- */
- @Override
void onAllowButtonFromShortcutToggleClicked() {
super.onAllowButtonFromShortcutToggleClicked();
- if (android.view.accessibility.Flags.cleanupAccessibilityWarningDialog()) {
- AccessibilityUtils.setAccessibilityServiceState(getContext(), mComponentName, true);
- }
+ AccessibilityUtils.setAccessibilityServiceState(getContext(), mComponentName, true);
}
/**
diff --git a/src/com/android/settings/accessibility/PreferredShortcuts.java b/src/com/android/settings/accessibility/PreferredShortcuts.java
index e166f38..895430a 100644
--- a/src/com/android/settings/accessibility/PreferredShortcuts.java
+++ b/src/com/android/settings/accessibility/PreferredShortcuts.java
@@ -42,7 +42,8 @@
/**
* Retrieves the user preferred shortcut types for the given {@code componentName} from
- * SharedPreferences.
+ * SharedPreferences. If the user doesn't have a preferred shortcut,
+ * {@link ShortcutConstants.UserShortcutType.SOFTWARE} is returned.
*
* @param context {@link Context} to access the {@link SharedPreferences}
* @param componentName Name of the service or activity, should be the format of {@link
@@ -52,7 +53,26 @@
@ShortcutConstants.UserShortcutType
public static int retrieveUserShortcutType(
@NonNull Context context, @NonNull String componentName) {
- final int defaultTypes = getDefaultPreferredShortcutTypesForTarget(componentName);
+ return retrieveUserShortcutType(
+ context, componentName, ShortcutConstants.UserShortcutType.SOFTWARE);
+ }
+
+ /**
+ * Retrieves the user preferred shortcut types for the given {@code componentName} from
+ * SharedPreferences.
+ *
+ * @param context {@link Context} to access the {@link SharedPreferences}
+ * @param componentName Name of the service or activity, should be the format of {@link
+ * ComponentName#flattenToString()}.
+ * @param defaultTypes The default shortcut types to use if the user doesn't have a
+ * preferred shortcut.
+ * @return {@link ShortcutConstants.UserShortcutType}
+ */
+ @ShortcutConstants.UserShortcutType
+ public static int retrieveUserShortcutType(
+ @NonNull Context context,
+ @NonNull String componentName,
+ @ShortcutConstants.UserShortcutType int defaultTypes) {
// Create a mutable set to modify
final Set<String> info = new HashSet<>(getFromSharedPreferences(context));
@@ -150,14 +170,5 @@
getSharedPreferences(context).edit().clear().apply();
}
- /**
- * Returns the default shortcut types for the given accessibility feature.
- */
- @ShortcutConstants.UserShortcutType
- private static int getDefaultPreferredShortcutTypesForTarget(@NonNull String componentName) {
- // TODO (b/322712028): return different default shortcut types for the given component
- return ShortcutConstants.UserShortcutType.SOFTWARE;
- }
-
private PreferredShortcuts() {}
}
diff --git a/src/com/android/settings/accessibility/ReduceBrightColorsPreferenceController.java b/src/com/android/settings/accessibility/ReduceBrightColorsPreferenceController.java
index 7b40024..3ca089c 100644
--- a/src/com/android/settings/accessibility/ReduceBrightColorsPreferenceController.java
+++ b/src/com/android/settings/accessibility/ReduceBrightColorsPreferenceController.java
@@ -32,6 +32,7 @@
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
+import com.android.server.display.feature.flags.Flags;
import com.android.settings.R;
import com.android.settingslib.PrimarySwitchPreference;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
@@ -88,6 +89,15 @@
@Override
public int getAvailabilityStatus() {
+ // Successor to this feature is Even Dimmer
+ // found in display/EvenDimmerPreferenceController
+ // Only allow RBC if even dimmer is not possible on this device
+ if (Flags.evenDimmer() && mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_evenDimmerEnabled)) {
+ return UNSUPPORTED_ON_DEVICE;
+ }
+
+
return ColorDisplayManager.isReduceBrightColorsAvailable(mContext) ? AVAILABLE
: UNSUPPORTED_ON_DEVICE;
}
diff --git a/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java b/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java
index 0b5ad3e..72e1ad8 100644
--- a/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java
+++ b/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java
@@ -21,6 +21,7 @@
import static com.android.settings.accessibility.PreferredShortcuts.retrieveUserShortcutType;
import android.accessibilityservice.AccessibilityServiceInfo;
+import android.app.AlertDialog;
import android.app.Dialog;
import android.app.settings.SettingsEnums;
import android.content.BroadcastReceiver;
@@ -35,6 +36,7 @@
import android.net.Uri;
import android.os.Bundle;
import android.os.SystemClock;
+import android.text.BidiFormatter;
import android.text.TextUtils;
import android.util.Log;
import android.view.Menu;
@@ -45,12 +47,14 @@
import androidx.annotation.Nullable;
+import com.android.internal.accessibility.common.ShortcutConstants;
import com.android.settings.R;
import com.android.settings.accessibility.AccessibilityUtil.QuickSettingsTooltipType;
import com.android.settings.accessibility.shortcuts.EditShortcutsPreferenceFragment;
import com.android.settingslib.accessibility.AccessibilityUtils;
import java.util.List;
+import java.util.Locale;
import java.util.concurrent.atomic.AtomicBoolean;
/** Fragment for providing toggle bar and basic accessibility service setup. */
@@ -157,69 +161,63 @@
if (info == null) {
return null;
}
- if (android.view.accessibility.Flags.cleanupAccessibilityWarningDialog()) {
- mWarningDialog =
- com.android.internal.accessibility.dialog.AccessibilityServiceWarning
- .createAccessibilityServiceWarningDialog(getPrefContext(), info,
- v -> onAllowButtonFromEnableToggleClicked(),
- v -> onDenyButtonFromEnableToggleClicked(),
- v -> onDialogButtonFromUninstallClicked());
- } else {
- mWarningDialog = AccessibilityServiceWarning
- .createCapabilitiesDialog(getPrefContext(), info,
- this::onDialogButtonFromEnableToggleClicked,
- this::onDialogButtonFromUninstallClicked);
- }
+ mWarningDialog =
+ com.android.internal.accessibility.dialog.AccessibilityServiceWarning
+ .createAccessibilityServiceWarningDialog(getPrefContext(), info,
+ v -> onAllowButtonFromEnableToggleClicked(),
+ v -> onDenyButtonFromEnableToggleClicked(),
+ v -> onDialogButtonFromUninstallClicked());
return mWarningDialog;
case DialogEnums.ENABLE_WARNING_FROM_SHORTCUT_TOGGLE:
if (info == null) {
return null;
}
- if (android.view.accessibility.Flags.cleanupAccessibilityWarningDialog()) {
- mWarningDialog =
- com.android.internal.accessibility.dialog.AccessibilityServiceWarning
- .createAccessibilityServiceWarningDialog(getPrefContext(), info,
- v -> onAllowButtonFromShortcutToggleClicked(),
- v -> onDenyButtonFromShortcutToggleClicked(),
- v -> onDialogButtonFromUninstallClicked());
- } else {
- mWarningDialog = AccessibilityServiceWarning
- .createCapabilitiesDialog(getPrefContext(), info,
- this::onDialogButtonFromShortcutToggleClicked,
- this::onDialogButtonFromUninstallClicked);
- }
+ mWarningDialog =
+ com.android.internal.accessibility.dialog.AccessibilityServiceWarning
+ .createAccessibilityServiceWarningDialog(getPrefContext(), info,
+ v -> onAllowButtonFromShortcutToggleClicked(),
+ v -> onDenyButtonFromShortcutToggleClicked(),
+ v -> onDialogButtonFromUninstallClicked());
return mWarningDialog;
case DialogEnums.ENABLE_WARNING_FROM_SHORTCUT:
if (info == null) {
return null;
}
- if (android.view.accessibility.Flags.cleanupAccessibilityWarningDialog()) {
- mWarningDialog =
- com.android.internal.accessibility.dialog.AccessibilityServiceWarning
- .createAccessibilityServiceWarningDialog(getPrefContext(), info,
- v -> onAllowButtonFromShortcutClicked(),
- v -> onDenyButtonFromShortcutClicked(),
- v -> onDialogButtonFromUninstallClicked());
- } else {
- mWarningDialog = AccessibilityServiceWarning
- .createCapabilitiesDialog(getPrefContext(), info,
- this::onDialogButtonFromShortcutClicked,
- this::onDialogButtonFromUninstallClicked);
- }
+ mWarningDialog =
+ com.android.internal.accessibility.dialog.AccessibilityServiceWarning
+ .createAccessibilityServiceWarningDialog(getPrefContext(), info,
+ v -> onAllowButtonFromShortcutClicked(),
+ v -> onDenyButtonFromShortcutClicked(),
+ v -> onDialogButtonFromUninstallClicked());
return mWarningDialog;
case DialogEnums.DISABLE_WARNING_FROM_TOGGLE:
if (info == null) {
return null;
}
- mWarningDialog = AccessibilityServiceWarning
- .createDisableDialog(getPrefContext(), info,
- this::onDialogButtonFromDisableToggleClicked);
+ mWarningDialog = createDisableDialog(
+ getPrefContext(), info, this::onDialogButtonFromDisableToggleClicked);
return mWarningDialog;
default:
return super.onCreateDialog(dialogId);
}
}
+ /** Returns a {@link Dialog} to be shown to confirm that they want to disable a service. */
+ private static Dialog createDisableDialog(Context context,
+ AccessibilityServiceInfo info, DialogInterface.OnClickListener listener) {
+ final Locale locale = context.getResources().getConfiguration().getLocales().get(0);
+ final CharSequence label =
+ info.getResolveInfo().loadLabel(context.getPackageManager());
+ CharSequence serviceName = BidiFormatter.getInstance(locale).unicodeWrap(label);
+
+ return new AlertDialog.Builder(context)
+ .setTitle(context.getString(R.string.disable_service_title, serviceName))
+ .setCancelable(true)
+ .setPositiveButton(R.string.accessibility_dialog_button_stop, listener)
+ .setNegativeButton(R.string.accessibility_dialog_button_cancel, listener)
+ .create();
+ }
+
@Override
public int getDialogMetricsCategory(int dialogId) {
switch (dialogId) {
@@ -330,15 +328,11 @@
@Override
public void onToggleClicked(ShortcutPreference preference) {
final int shortcutTypes = retrieveUserShortcutType(getPrefContext(),
- mComponentName.flattenToString());
+ mComponentName.flattenToString(), getDefaultShortcutTypes());
if (preference.isChecked()) {
- final boolean isWarningRequired;
- if (android.view.accessibility.Flags.cleanupAccessibilityWarningDialog()) {
- isWarningRequired = getPrefContext().getSystemService(AccessibilityManager.class)
- .isAccessibilityServiceWarningRequired(getAccessibilityServiceInfo());
- } else {
- isWarningRequired = !mToggleServiceSwitchPreference.isChecked();
- }
+ final boolean isWarningRequired =
+ getPrefContext().getSystemService(AccessibilityManager.class)
+ .isAccessibilityServiceWarningRequired(getAccessibilityServiceInfo());
if (isWarningRequired) {
preference.setChecked(false);
showPopupDialog(DialogEnums.ENABLE_WARNING_FROM_SHORTCUT_TOGGLE);
@@ -354,15 +348,9 @@
@Override
public void onSettingsClicked(ShortcutPreference preference) {
- final boolean isWarningRequired;
- if (android.view.accessibility.Flags.cleanupAccessibilityWarningDialog()) {
- isWarningRequired = getPrefContext().getSystemService(AccessibilityManager.class)
- .isAccessibilityServiceWarningRequired(getAccessibilityServiceInfo());
- } else {
- isWarningRequired = !(mShortcutPreference.isChecked()
- || mToggleServiceSwitchPreference.isChecked());
- }
-
+ final boolean isWarningRequired =
+ getPrefContext().getSystemService(AccessibilityManager.class)
+ .isAccessibilityServiceWarningRequired(getAccessibilityServiceInfo());
if (isWarningRequired) {
showPopupDialog(DialogEnums.ENABLE_WARNING_FROM_SHORTCUT);
} else {
@@ -476,6 +464,16 @@
return TAG;
}
+ @Override
+ protected int getDefaultShortcutTypes() {
+ if (android.view.accessibility.Flags.a11yQsShortcut()) {
+ return getTileComponentName() == null ? super.getDefaultShortcutTypes()
+ : ShortcutConstants.UserShortcutType.QUICK_SETTINGS;
+ }
+
+ return super.getDefaultShortcutTypes();
+ }
+
private void onAllowButtonFromEnableToggleClicked() {
handleConfirmServiceEnabled(/* confirmed= */ true);
if (serviceSupportsAccessibilityButton()) {
@@ -507,7 +505,7 @@
mShortcutPreference.setChecked(true);
final int shortcutTypes = retrieveUserShortcutType(getPrefContext(),
- mComponentName.flattenToString());
+ mComponentName.flattenToString(), getDefaultShortcutTypes());
AccessibilityUtil.optInAllValuesToSettings(getPrefContext(), shortcutTypes, mComponentName);
mIsDialogShown.set(false);
@@ -526,17 +524,6 @@
mWarningDialog.dismiss();
}
- void onDialogButtonFromShortcutClicked(View view) {
- final int viewId = view.getId();
- if (viewId == R.id.permission_enable_allow_button) {
- onAllowButtonFromShortcutClicked();
- } else if (viewId == R.id.permission_enable_deny_button) {
- onDenyButtonFromShortcutClicked();
- } else {
- throw new IllegalArgumentException("Unexpected view id");
- }
- }
-
private void onAllowButtonFromShortcutClicked() {
mIsDialogShown.set(false);
if (Flags.editShortcutsInFullScreen()) {
@@ -565,13 +552,9 @@
mToggleServiceSwitchPreference.setChecked(false);
getArguments().putBoolean(AccessibilitySettings.EXTRA_CHECKED,
/* disableService */ false);
- final boolean isWarningRequired;
- if (android.view.accessibility.Flags.cleanupAccessibilityWarningDialog()) {
- isWarningRequired = getPrefContext().getSystemService(AccessibilityManager.class)
- .isAccessibilityServiceWarningRequired(getAccessibilityServiceInfo());
- } else {
- isWarningRequired = !mShortcutPreference.isChecked();
- }
+ final boolean isWarningRequired =
+ getPrefContext().getSystemService(AccessibilityManager.class)
+ .isAccessibilityServiceWarningRequired(getAccessibilityServiceInfo());
if (isWarningRequired) {
showPopupDialog(DialogEnums.ENABLE_WARNING_FROM_TOGGLE);
} else {
diff --git a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java
index ed47007..ef828db 100644
--- a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java
+++ b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java
@@ -54,6 +54,7 @@
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceScreen;
+import com.android.internal.accessibility.common.ShortcutConstants;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.accessibility.AccessibilityDialogUtils.DialogType;
@@ -661,7 +662,7 @@
int value = restoreOnConfigChangedValue();
if (value == NOT_SET) {
final int lastNonEmptyUserShortcutType = PreferredShortcuts.retrieveUserShortcutType(
- getPrefContext(), mComponentName.flattenToString());
+ getPrefContext(), mComponentName.flattenToString(), getDefaultShortcutTypes());
value = mShortcutPreference.isChecked() ? lastNonEmptyUserShortcutType
: UserShortcutType.EMPTY;
}
@@ -710,8 +711,8 @@
return context.getText(R.string.accessibility_shortcut_state_off);
}
- final int shortcutTypes = PreferredShortcuts.retrieveUserShortcutType(context,
- mComponentName.flattenToString());
+ final int shortcutTypes = PreferredShortcuts.retrieveUserShortcutType(
+ context, mComponentName.flattenToString(), getDefaultShortcutTypes());
final List<CharSequence> list = new ArrayList<>();
if (android.view.accessibility.Flags.a11yQsShortcut()) {
@@ -811,7 +812,7 @@
}
final int shortcutTypes = PreferredShortcuts.retrieveUserShortcutType(getPrefContext(),
- mComponentName.flattenToString());
+ mComponentName.flattenToString(), getDefaultShortcutTypes());
mShortcutPreference.setChecked(
AccessibilityUtil.hasValuesInSettings(getPrefContext(), shortcutTypes,
mComponentName));
@@ -829,7 +830,7 @@
}
final int shortcutTypes = PreferredShortcuts.retrieveUserShortcutType(getPrefContext(),
- mComponentName.flattenToString());
+ mComponentName.flattenToString(), getDefaultShortcutTypes());
if (preference.isChecked()) {
AccessibilityUtil.optInAllValuesToSettings(getPrefContext(), shortcutTypes,
mComponentName);
@@ -977,4 +978,13 @@
boolean isAnySetupWizard() {
return WizardManagerHelper.isAnySetupWizard(getIntent());
}
+
+ /**
+ * Returns the default preferred shortcut types when the user doesn't have a preferred shortcut
+ * types
+ */
+ @ShortcutConstants.UserShortcutType
+ protected int getDefaultShortcutTypes() {
+ return ShortcutConstants.UserShortcutType.SOFTWARE;
+ }
}
diff --git a/src/com/android/settings/activityembedding/ActivityEmbeddingRulesController.java b/src/com/android/settings/activityembedding/ActivityEmbeddingRulesController.java
index b2ec589..4e39070 100644
--- a/src/com/android/settings/activityembedding/ActivityEmbeddingRulesController.java
+++ b/src/com/android/settings/activityembedding/ActivityEmbeddingRulesController.java
@@ -39,6 +39,7 @@
import com.android.settings.SettingsActivity;
import com.android.settings.SubSettings;
import com.android.settings.biometrics.face.FaceEnrollIntroduction;
+import com.android.settings.biometrics.face.FaceEnrollIntroductionInternal;
import com.android.settings.biometrics.fingerprint.FingerprintEnrollEnrolling;
import com.android.settings.biometrics.fingerprint.FingerprintEnrollIntroduction;
import com.android.settings.biometrics.fingerprint.FingerprintEnrollIntroductionInternal;
@@ -259,6 +260,7 @@
addActivityFilter(activityFilters, FingerprintEnrollIntroduction.class);
addActivityFilter(activityFilters, FingerprintEnrollIntroductionInternal.class);
addActivityFilter(activityFilters, FingerprintEnrollEnrolling.class);
+ addActivityFilter(activityFilters, FaceEnrollIntroductionInternal.class);
addActivityFilter(activityFilters, FaceEnrollIntroduction.class);
addActivityFilter(activityFilters, RemoteAuthActivity.class);
addActivityFilter(activityFilters, RemoteAuthActivityInternal.class);
diff --git a/src/com/android/settings/biometrics/BiometricEnrollActivity.java b/src/com/android/settings/biometrics/BiometricEnrollActivity.java
index 73e1af1..c9616f1 100644
--- a/src/com/android/settings/biometrics/BiometricEnrollActivity.java
+++ b/src/com/android/settings/biometrics/BiometricEnrollActivity.java
@@ -87,6 +87,10 @@
// this only applies to fingerprint.
public static final String EXTRA_SKIP_INTRO = "skip_intro";
+ // Intent extra. If true, support fingerprint enrollment only and skip other biometric
+ // enrollment methods like face unlock.
+ public static final String EXTRA_FINGERPRINT_ENROLLMENT_ONLY = "fingerprint_enrollment_only";
+
// Intent extra. If true, parental consent will be requested before user enrollment.
public static final String EXTRA_REQUIRE_PARENTAL_CONSENT = "require_consent";
@@ -194,7 +198,8 @@
final PackageManager pm = getApplicationContext().getPackageManager();
mHasFeatureFingerprint = pm.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT);
- mHasFeatureFace = pm.hasSystemFeature(PackageManager.FEATURE_FACE);
+ mHasFeatureFace = pm.hasSystemFeature(PackageManager.FEATURE_FACE)
+ && !(intent.getBooleanExtra(EXTRA_FINGERPRINT_ENROLLMENT_ONLY, false));
// Default behavior is to enroll BIOMETRIC_WEAK or above. See ACTION_BIOMETRIC_ENROLL.
final int authenticators = getIntent().getIntExtra(
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java
index 90b9346..bbeaf2a 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java
@@ -71,7 +71,6 @@
import com.android.settings.biometrics.BiometricsSplitScreenDialog;
import com.android.settings.biometrics.fingerprint.feature.SfpsEnrollmentFeature;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
-import com.android.settings.flags.Flags;
import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.display.DisplayDensityUtils;
@@ -252,12 +251,6 @@
setContentView(layout);
setDescriptionText(R.string.security_settings_udfps_enroll_start_message);
-
- if (Flags.udfpsEnrollCalibration()) {
- mCalibrator = FeatureFactory.getFeatureFactory().getFingerprintFeatureProvider()
- .getUdfpsEnrollCalibrator(getApplicationContext(), savedInstanceState,
- getIntent());
- }
} else if (mCanAssumeSfps) {
mSfpsEnrollmentFeature = FeatureFactory.getFeatureFactory()
.getFingerprintFeatureProvider().getSfpsEnrollmentFeature();
@@ -342,6 +335,15 @@
final Configuration config = getApplicationContext().getResources().getConfiguration();
maybeHideSfpsText(config);
+
+ if (!mIsSetupWizard) {
+ mCalibrator = FeatureFactory.getFeatureFactory().getFingerprintFeatureProvider()
+ .getUdfpsEnrollCalibrator(getApplicationContext(), null, getIntent());
+ if (mCalibrator != null) {
+ mCalibrator.onWaitingPage(getLifecycle(),
+ getSupportFragmentManager(), null);
+ }
+ }
}
private void setHelpAnimation() {
@@ -371,11 +373,6 @@
super.onSaveInstanceState(outState);
outState.putBoolean(KEY_STATE_CANCELED, mIsCanceled);
outState.putInt(KEY_STATE_PREVIOUS_ROTATION, mPreviousRotation);
- if (Flags.udfpsEnrollCalibration()) {
- if (mCalibrator != null) {
- mCalibrator.onSaveInstanceState(outState);
- }
- }
}
private void restoreSavedState(Bundle savedInstanceState) {
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensor.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensor.java
index 6b7538a..aeb0dac 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensor.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensor.java
@@ -167,7 +167,7 @@
.getUdfpsEnrollCalibrator(getApplicationContext(), savedInstanceState,
getIntent());
if (mCalibrator != null) {
- mCalibrator.onFindSensorPage(
+ mCalibrator.onWaitingPage(
getLifecycle(),
getSupportFragmentManager(),
this::enableUdfpsLottieAndNextButton
@@ -296,7 +296,7 @@
getIntent().getIntExtra(BiometricUtils.EXTRA_ENROLL_REASON, -1));
if (Flags.udfpsEnrollCalibration()) {
if (mCalibrator != null) {
- ret.putExtras(mCalibrator.getExtrasForNextIntent(true));
+ ret.putExtras(mCalibrator.getExtrasForNextIntent());
}
}
return ret;
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroduction.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroduction.java
index 26965fa..a1f4eff 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroduction.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroduction.java
@@ -387,7 +387,7 @@
}
if (Flags.udfpsEnrollCalibration()) {
if (mCalibrator != null) {
- intent.putExtras(mCalibrator.getExtrasForNextIntent(false));
+ intent.putExtras(mCalibrator.getExtrasForNextIntent());
}
}
intent.putExtra(BiometricUtils.EXTRA_ENROLL_REASON,
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java b/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java
index 2aacbe4..9dac46d 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java
@@ -88,6 +88,7 @@
import com.android.settingslib.widget.FooterPreference;
import com.android.settingslib.widget.TwoTargetPreference;
+import com.google.android.setupcompat.util.WizardManagerHelper;
import com.google.android.setupdesign.util.DeviceHelper;
import java.util.ArrayList;
@@ -111,6 +112,9 @@
private static final int RESULT_SKIP = BiometricEnrollBase.RESULT_SKIP;
private static final int RESULT_TIMEOUT = BiometricEnrollBase.RESULT_TIMEOUT;
+ @Nullable
+ private UdfpsEnrollCalibrator mCalibrator;
+
@Override
public Intent getIntent() {
Intent modIntent = new Intent(super.getIntent());
@@ -131,6 +135,13 @@
setTitle(msg);
}
+ @Override
+ public void onResume() {
+ super.onResume();
+ mCalibrator = FeatureFactory.getFeatureFactory().getFingerprintFeatureProvider()
+ .getUdfpsEnrollCalibrator(getApplicationContext(), null, null);
+ }
+
/**
* @param context
* @return true if the Fingerprint hardware is detected.
@@ -800,6 +811,11 @@
}
intent.putExtra(Intent.EXTRA_USER_ID, mUserId);
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, mToken);
+ if (((FingerprintSettings) getActivity()).mCalibrator != null) {
+ intent.putExtras(
+ (((FingerprintSettings) getActivity()).mCalibrator)
+ .getExtrasForNextIntent());
+ }
startActivityForResult(intent, ADD_FINGERPRINT_REQUEST);
} else if (pref instanceof FingerprintPreference) {
FingerprintPreference fpref = (FingerprintPreference) pref;
diff --git a/src/com/android/settings/biometrics/fingerprint/SetupFingerprintEnrollFindSensor.java b/src/com/android/settings/biometrics/fingerprint/SetupFingerprintEnrollFindSensor.java
index 6590530..3037c2a 100644
--- a/src/com/android/settings/biometrics/fingerprint/SetupFingerprintEnrollFindSensor.java
+++ b/src/com/android/settings/biometrics/fingerprint/SetupFingerprintEnrollFindSensor.java
@@ -51,7 +51,7 @@
SetupWizardUtils.copySetupExtras(getIntent(), intent);
if (Flags.udfpsEnrollCalibration()) {
if (mCalibrator != null) {
- intent.putExtras(mCalibrator.getExtrasForNextIntent(true));
+ intent.putExtras(mCalibrator.getExtrasForNextIntent());
}
}
return intent;
diff --git a/src/com/android/settings/biometrics/fingerprint/SetupFingerprintEnrollIntroduction.java b/src/com/android/settings/biometrics/fingerprint/SetupFingerprintEnrollIntroduction.java
index 0ee9ad3..c263b2e 100644
--- a/src/com/android/settings/biometrics/fingerprint/SetupFingerprintEnrollIntroduction.java
+++ b/src/com/android/settings/biometrics/fingerprint/SetupFingerprintEnrollIntroduction.java
@@ -49,7 +49,7 @@
SetupWizardUtils.copySetupExtras(getIntent(), intent);
if (Flags.udfpsEnrollCalibration()) {
if (mCalibrator != null) {
- intent.putExtras(mCalibrator.getExtrasForNextIntent(false));
+ intent.putExtras(mCalibrator.getExtrasForNextIntent());
}
}
return intent;
diff --git a/src/com/android/settings/biometrics/fingerprint/UdfpsEnrollCalibrator.kt b/src/com/android/settings/biometrics/fingerprint/UdfpsEnrollCalibrator.kt
index c54c6b5..22a69ba 100644
--- a/src/com/android/settings/biometrics/fingerprint/UdfpsEnrollCalibrator.kt
+++ b/src/com/android/settings/biometrics/fingerprint/UdfpsEnrollCalibrator.kt
@@ -6,13 +6,14 @@
interface UdfpsEnrollCalibrator {
- fun getExtrasForNextIntent(isEnrolling: Boolean): Bundle
+ fun getExtrasForNextIntent(): Bundle
fun onSaveInstanceState(outState: Bundle)
- fun onFindSensorPage(
+ fun onWaitingPage(
lifecycle: Lifecycle,
fragmentManager: FragmentManager,
- enableEnrollingRunnable: Runnable
+ enableEnrollingRunnable: Runnable?
)
+
}
\ No newline at end of file
diff --git a/src/com/android/settings/bluetooth/BluetoothDetailsSpatialAudioController.java b/src/com/android/settings/bluetooth/BluetoothDetailsSpatialAudioController.java
index e5fb365..30e86fe 100644
--- a/src/com/android/settings/bluetooth/BluetoothDetailsSpatialAudioController.java
+++ b/src/com/android/settings/bluetooth/BluetoothDetailsSpatialAudioController.java
@@ -18,6 +18,7 @@
import static android.media.Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE;
+import android.app.settings.SettingsEnums;
import android.content.Context;
import android.media.AudioDeviceAttributes;
import android.media.AudioDeviceInfo;
@@ -60,6 +61,7 @@
AudioDeviceAttributes mAudioDevice = null;
AtomicBoolean mHasHeadTracker = new AtomicBoolean(false);
+ AtomicBoolean mInitialRefresh = new AtomicBoolean(true);
public BluetoothDetailsSpatialAudioController(
Context context,
@@ -81,6 +83,10 @@
TwoStatePreference switchPreference = (TwoStatePreference) preference;
String key = switchPreference.getKey();
if (TextUtils.equals(key, KEY_SPATIAL_AUDIO)) {
+ mMetricsFeatureProvider.action(
+ mContext,
+ SettingsEnums.ACTION_BLUETOOTH_DEVICE_DETAILS_SPATIAL_AUDIO_TOGGLE_CLICKED,
+ switchPreference.isChecked());
updateSpatializerEnabled(switchPreference.isChecked());
ThreadUtils.postOnBackgroundThread(
() -> {
@@ -91,6 +97,10 @@
});
return true;
} else if (TextUtils.equals(key, KEY_HEAD_TRACKING)) {
+ mMetricsFeatureProvider.action(
+ mContext,
+ SettingsEnums.ACTION_BLUETOOTH_DEVICE_DETAILS_HEAD_TRACKING_TOGGLE_CLICKED,
+ switchPreference.isChecked());
updateSpatializerHeadTracking(switchPreference.isChecked());
return true;
} else {
@@ -186,6 +196,20 @@
if (isHeadTrackingAvailable) {
headTrackingPref.setChecked(mSpatializer.isHeadTrackerEnabled(mAudioDevice));
}
+
+ if (mInitialRefresh.compareAndSet(true, false)) {
+ // Only triggered when shown for the first time
+ mMetricsFeatureProvider.action(
+ mContext,
+ SettingsEnums.ACTION_BLUETOOTH_DEVICE_DETAILS_SPATIAL_AUDIO_TRIGGERED,
+ spatialAudioPref.isChecked());
+ if (mHasHeadTracker.get()) {
+ mMetricsFeatureProvider.action(
+ mContext,
+ SettingsEnums.ACTION_BLUETOOTH_DEVICE_DETAILS_HEAD_TRACKING_TRIGGERED,
+ headTrackingPref.isChecked());
+ }
+ }
}
@VisibleForTesting
diff --git a/src/com/android/settings/connecteddevice/stylus/StylusDevicesController.java b/src/com/android/settings/connecteddevice/stylus/StylusDevicesController.java
index cd23103..7fbaf3c 100644
--- a/src/com/android/settings/connecteddevice/stylus/StylusDevicesController.java
+++ b/src/com/android/settings/connecteddevice/stylus/StylusDevicesController.java
@@ -214,7 +214,7 @@
Intent intent = new Intent(Intent.ACTION_MANAGE_DEFAULT_APP).setPackage(
packageName).putExtra(Intent.EXTRA_ROLE_NAME, RoleManager.ROLE_NOTES);
- List<UserHandle> users = getUserAndManagedProfiles();
+ List<UserHandle> users = getUserProfiles();
if (users.size() <= 1) {
mContext.startActivity(intent);
} else {
@@ -311,22 +311,23 @@
return inputMethod != null && inputMethod.supportsStylusHandwriting();
}
- private List<UserHandle> getUserAndManagedProfiles() {
+ private List<UserHandle> getUserProfiles() {
UserManager um = mContext.getSystemService(UserManager.class);
- final List<UserHandle> userManagedProfiles = new ArrayList<>();
- // Add the current user, then add all the associated managed profiles.
final UserHandle currentUser = Process.myUserHandle();
- userManagedProfiles.add(currentUser);
+ final List<UserHandle> userProfiles = new ArrayList<>();
+ userProfiles.add(currentUser);
- final List<UserInfo> userInfos = um.getUsers();
- for (UserInfo info : userInfos) {
- int userId = info.id;
- if (um.isManagedProfile(userId)
- && um.getProfileParent(userId).id == currentUser.getIdentifier()) {
- userManagedProfiles.add(UserHandle.of(userId));
+ final List<UserInfo> userInfos = um.getProfiles(currentUser.getIdentifier());
+ for (UserInfo userInfo : userInfos) {
+ if (userInfo.isManagedProfile()
+ || (android.os.Flags.allowPrivateProfile()
+ && android.multiuser.Flags.enablePrivateSpaceFeatures()
+ && android.multiuser.Flags.handleInterleavedSettingsForPrivateSpace()
+ && userInfo.isPrivateProfile())) {
+ userProfiles.add(userInfo.getUserHandle());
}
}
- return userManagedProfiles;
+ return userProfiles;
}
private UserHandle getDefaultNoteTaskProfile() {
diff --git a/src/com/android/settings/core/PreferenceControllerListHelper.java b/src/com/android/settings/core/PreferenceControllerListHelper.java
index f37140e..dea8c97 100644
--- a/src/com/android/settings/core/PreferenceControllerListHelper.java
+++ b/src/com/android/settings/core/PreferenceControllerListHelper.java
@@ -27,6 +27,8 @@
import android.util.Log;
import androidx.annotation.NonNull;
+import androidx.preference.PreferenceManager;
+import androidx.preference.PreferenceScreen;
import com.android.settings.core.PreferenceXmlParserUtils.MetadataFlag;
import com.android.settingslib.core.AbstractPreferenceController;
@@ -94,6 +96,25 @@
}
/**
+ * Checks if the given PreferenceScreen will be empty due to all preferences being unavailable.
+ *
+ * @param xmlResId resource id of the PreferenceScreen to check
+ * @return {@code true} if none of the preferences in the given screen will appear
+ */
+ public static boolean areAllPreferencesUnavailable(@NonNull Context context,
+ @NonNull PreferenceManager preferenceManager, @XmlRes int xmlResId) {
+ PreferenceScreen screen = preferenceManager.inflateFromResource(context, xmlResId,
+ /* rootPreferences= */ null);
+ List<BasePreferenceController> preferenceControllers =
+ getPreferenceControllersFromXml(context, xmlResId);
+ if (screen.getPreferenceCount() != preferenceControllers.size()) {
+ // There are some preferences without controllers, which will show regardless.
+ return false;
+ }
+ return preferenceControllers.stream().noneMatch(BasePreferenceController::isAvailable);
+ }
+
+ /**
* Return a sub list of {@link AbstractPreferenceController} to only contain controller that
* doesn't exist in filter.
*
diff --git a/src/com/android/settings/core/gateway/SettingsGateway.java b/src/com/android/settings/core/gateway/SettingsGateway.java
index e08e856..8b3dbf4 100644
--- a/src/com/android/settings/core/gateway/SettingsGateway.java
+++ b/src/com/android/settings/core/gateway/SettingsGateway.java
@@ -144,6 +144,7 @@
import com.android.settings.network.NetworkProviderSettings;
import com.android.settings.network.apn.ApnEditor;
import com.android.settings.network.apn.ApnSettings;
+import com.android.settings.network.telephony.CellularSecuritySettingsFragment;
import com.android.settings.network.telephony.MobileNetworkSettings;
import com.android.settings.network.telephony.NetworkSelectSettings;
import com.android.settings.network.telephony.SatelliteSetting;
@@ -388,6 +389,7 @@
ScreenTimeoutSettings.class.getName(),
ResetNetwork.class.getName(),
VibrationIntensitySettingsFragment.class.getName(),
+ CellularSecuritySettingsFragment.class.getName(),
};
public static final String[] SETTINGS_FOR_RESTRICTED = {
diff --git a/src/com/android/settings/dashboard/profileselector/ProfileSelectDialog.java b/src/com/android/settings/dashboard/profileselector/ProfileSelectDialog.java
index 4df1fdd..b276a39 100644
--- a/src/com/android/settings/dashboard/profileselector/ProfileSelectDialog.java
+++ b/src/com/android/settings/dashboard/profileselector/ProfileSelectDialog.java
@@ -25,7 +25,6 @@
import android.content.DialogInterface.OnShowListener;
import android.content.Intent;
import android.content.pm.UserInfo;
-import android.content.pm.UserProperties;
import android.os.Bundle;
import android.os.UserHandle;
import android.os.UserManager;
@@ -42,6 +41,7 @@
import com.android.internal.widget.LinearLayoutManager;
import com.android.internal.widget.RecyclerView;
import com.android.settings.R;
+import com.android.settings.Utils;
import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.drawer.Tile;
@@ -186,7 +186,7 @@
UserInfo userInfo = userManager.getUserInfo(userHandles.get(i).getIdentifier());
if (userInfo == null
|| userInfo.isCloneProfile()
- || shouldHideUserInQuietMode(userHandles.get(i), userManager)) {
+ || Utils.shouldHideUser(userHandles.get(i), userManager)) {
if (DEBUG) {
Log.d(TAG, "Delete the user: " + userHandles.get(i).getIdentifier());
}
@@ -219,7 +219,7 @@
UserInfo userInfo = userManager.getUserInfo(userHandle.getIdentifier());
if (userInfo == null
|| userInfo.isCloneProfile()
- || shouldHideUserInQuietMode(userHandle, userManager)) {
+ || Utils.shouldHideUser(userHandle, userManager)) {
if (DEBUG) {
Log.d(TAG, "Delete the user: " + userHandle.getIdentifier());
}
@@ -228,11 +228,4 @@
}
}
}
-
- private static boolean shouldHideUserInQuietMode(
- UserHandle userHandle, UserManager userManager) {
- UserProperties userProperties = userManager.getUserProperties(userHandle);
- return userProperties.getShowInQuietMode() == UserProperties.SHOW_IN_QUIET_MODE_HIDDEN
- && userManager.isQuietModeEnabled(userHandle);
- }
}
diff --git a/src/com/android/settings/dashboard/profileselector/UserAdapter.java b/src/com/android/settings/dashboard/profileselector/UserAdapter.java
index 40d1a93..0fefa2f 100644
--- a/src/com/android/settings/dashboard/profileselector/UserAdapter.java
+++ b/src/com/android/settings/dashboard/profileselector/UserAdapter.java
@@ -64,7 +64,11 @@
UserInfo userInfo = um.getUserInfo(mUserHandle.getIdentifier());
int tintColor = Utils.getColorAttrDefaultColor(context,
com.android.internal.R.attr.materialColorPrimary);
- if (userInfo.isManagedProfile()) {
+ if (userInfo.isManagedProfile()
+ || (android.os.Flags.allowPrivateProfile()
+ && android.multiuser.Flags.enablePrivateSpaceFeatures()
+ && android.multiuser.Flags.handleInterleavedSettingsForPrivateSpace()
+ && userInfo.isPrivateProfile())) {
mIcon = context.getPackageManager().getUserBadgeForDensityNoBackground(
userHandle, /* density= */ 0);
mIcon.setTint(tintColor);
@@ -88,6 +92,7 @@
() -> context.getString(com.android.settingslib.R.string.category_work));
} else if (android.os.Flags.allowPrivateProfile()
&& android.multiuser.Flags.enablePrivateSpaceFeatures()
+ && android.multiuser.Flags.handleInterleavedSettingsForPrivateSpace()
&& mUserManager.getUserInfo(userId).isPrivateProfile()) {
return resources.getString(PRIVATE_CATEGORY_HEADER,
() -> context.getString(com.android.settingslib.R.string.category_private));
@@ -209,6 +214,7 @@
private static UserAdapter createUserAdapter(
UserManager userManager, Context context, List<UserHandle> userProfiles) {
+ updateUserHandlesIfNeeded(userManager, userProfiles);
ArrayList<UserDetails> userDetails = new ArrayList<>(userProfiles.size());
for (UserHandle userProfile : userProfiles) {
userDetails.add(new UserDetails(userProfile, userManager, context));
@@ -216,6 +222,15 @@
return new UserAdapter(context, userDetails);
}
+ private static void updateUserHandlesIfNeeded(
+ UserManager userManager, List<UserHandle> userProfiles) {
+ for (int i = userProfiles.size() - 1; i >= 0; --i) {
+ if (com.android.settings.Utils.shouldHideUser(userProfiles.get(i), userManager)) {
+ userProfiles.remove(i);
+ }
+ }
+ }
+
static class ViewHolder extends RecyclerView.ViewHolder {
private final ImageView mIconView;
private final TextView mTitleView;
diff --git a/src/com/android/settings/gestures/SystemNavigationGestureSettings.java b/src/com/android/settings/gestures/SystemNavigationGestureSettings.java
index c38be94..9f76fad 100644
--- a/src/com/android/settings/gestures/SystemNavigationGestureSettings.java
+++ b/src/com/android/settings/gestures/SystemNavigationGestureSettings.java
@@ -43,6 +43,8 @@
import com.android.settings.R;
import com.android.settings.accessibility.AccessibilityGestureNavigationTutorial;
+import com.android.settings.core.BasePreferenceController;
+import com.android.settings.core.PreferenceControllerListHelper;
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider;
import com.android.settings.overlay.FeatureFactory;
@@ -147,14 +149,20 @@
final PreferenceScreen screen = getPreferenceScreen();
screen.removeAll();
screen.addPreference(mVideoPreference);
+ addPreferencesFromResource(getPreferenceScreenResId());
+ final List<BasePreferenceController> preferenceControllers = PreferenceControllerListHelper
+ .getPreferenceControllersFromXml(getContext(), getPreferenceScreenResId());
+ preferenceControllers.forEach(controller -> {
+ controller.updateState(findPreference(controller.getPreferenceKey()));
+ controller.displayPreference(screen);
+ });
final List<? extends CandidateInfo> candidateList = getCandidates();
if (candidateList == null) {
return;
}
for (CandidateInfo info : candidateList) {
- SelectorWithWidgetPreference pref =
- new SelectorWithWidgetPreference(getPrefContext());
+ SelectorWithWidgetPreference pref = new SelectorWithWidgetPreference(getPrefContext());
bindPreference(pref, info.getKey(), info, defaultKey);
bindPreferenceExtra(pref, info.getKey(), info, defaultKey, systemDefaultKey);
screen.addPreference(pref);
@@ -176,8 +184,11 @@
GestureNavigationSettingsFragment.GESTURE_NAVIGATION_SETTINGS)));
}
- if (KEY_SYSTEM_NAV_2BUTTONS.equals(info.getKey()) || KEY_SYSTEM_NAV_3BUTTONS.equals(
- info.getKey())) {
+ if ((KEY_SYSTEM_NAV_2BUTTONS.equals(info.getKey())
+ || KEY_SYSTEM_NAV_3BUTTONS.equals(info.getKey()))
+ // Don't add the settings button if that page will be blank.
+ && !PreferenceControllerListHelper.areAllPreferencesUnavailable(
+ getContext(), getPreferenceManager(), R.xml.button_navigation_settings)) {
pref.setExtraWidgetOnClickListener((v) ->
new SubSettingLauncher(getContext())
.setDestination(ButtonNavigationSettingsFragment.class.getName())
diff --git a/src/com/android/settings/network/CellularSecurityPreferenceController.java b/src/com/android/settings/network/CellularSecurityPreferenceController.java
new file mode 100644
index 0000000..237e704
--- /dev/null
+++ b/src/com/android/settings/network/CellularSecurityPreferenceController.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.network;
+
+import android.app.settings.SettingsEnums;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Build;
+import android.os.Bundle;
+import android.safetycenter.SafetyCenterManager;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+
+import com.android.internal.telephony.flags.Flags;
+import com.android.settings.core.BasePreferenceController;
+import com.android.settings.core.SubSettingLauncher;
+import com.android.settings.network.telephony.CellularSecuritySettingsFragment;
+
+/**
+ * {@link BasePreferenceController} for accessing Cellular Security settings from Network &
+ * Internet Settings menu.
+ */
+public class CellularSecurityPreferenceController extends BasePreferenceController {
+
+ private static final String LOG_TAG = "CellularSecurityPreferenceController";
+
+ private @Nullable TelephonyManager mTelephonyManager;
+
+ /**
+ * Class constructor of "Cellular Security" preference.
+ *
+ * @param context of settings
+ * @param prefKey assigned within UI entry of XML file
+ */
+ public CellularSecurityPreferenceController(@NonNull Context context, @NonNull String prefKey) {
+ super(context, prefKey);
+ mTelephonyManager = context.getSystemService(TelephonyManager.class);
+ }
+
+ @Override
+ public void displayPreference(@NonNull PreferenceScreen screen) {
+ super.displayPreference(screen);
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ if (!Flags.enableIdentifierDisclosureTransparencyUnsolEvents()
+ || !Flags.enableModemCipherTransparencyUnsolEvents()
+ || !Flags.enableIdentifierDisclosureTransparency()
+ || !Flags.enableModemCipherTransparency()) {
+ return UNSUPPORTED_ON_DEVICE;
+ }
+ if (mTelephonyManager == null) {
+ Log.w(LOG_TAG, "Telephony manager not yet initialized");
+ mTelephonyManager = mContext.getSystemService(TelephonyManager.class);
+ }
+
+ boolean isNullCipherDisablementAvailable = false;
+ boolean areCellSecNotificationsAvailable = false;
+ try {
+ mTelephonyManager.isNullCipherAndIntegrityPreferenceEnabled();
+ isNullCipherDisablementAvailable = true; // true because it doesn't throw an exception,
+ // we don't want the value of
+ // isNullCipherAndIntegrityEnabled()
+ } catch (UnsupportedOperationException e) {
+ Log.i(LOG_TAG, "Null cipher enablement is unsupported, hiding divider: "
+ + e.getMessage());
+ } catch (Exception e) {
+ Log.e(LOG_TAG,
+ "Failed isNullCipherAndIntegrityEnabled. Setting availability to "
+ + "CONDITIONALLY_UNAVAILABLE. Exception: "
+ + e.getMessage());
+ }
+
+ try {
+ // Must call both APIs, as we can't use the combined toggle if both aren't available
+ areNotificationsEnabled();
+ areCellSecNotificationsAvailable = true; // true because it doesn't throw an exception
+ // and we don't want the value of
+ // areNotificationsEnabled()
+ } catch (UnsupportedOperationException e) {
+ Log.i(LOG_TAG, "Cellular security notifications are unsupported, hiding divider: "
+ + e.getMessage());
+ }
+
+ if (isNullCipherDisablementAvailable || areCellSecNotificationsAvailable) {
+ return AVAILABLE;
+ } else {
+ return UNSUPPORTED_ON_DEVICE;
+ }
+ }
+
+ @Override
+ public boolean handlePreferenceTreeClick(@NonNull Preference preference) {
+ if (!TextUtils.equals(preference.getKey(), getPreferenceKey())) {
+ return super.handlePreferenceTreeClick(preference);
+ }
+ boolean isSafetyCenterSupported = isSafetyCenterSupported();
+ if (isSafetyCenterSupported) {
+ Intent safetyCenterIntent = new Intent(Intent.ACTION_SAFETY_CENTER);
+ safetyCenterIntent.putExtra(SafetyCenterManager.EXTRA_SAFETY_SOURCES_GROUP_ID,
+ "AndroidCellularNetworkSecuritySources");
+ mContext.startActivity(safetyCenterIntent);
+ } else {
+ final Bundle bundle = new Bundle();
+ bundle.putString(CellularSecuritySettingsFragment.KEY_CELLULAR_SECURITY_PREFERENCE, "");
+
+ new SubSettingLauncher(mContext)
+ .setDestination(CellularSecuritySettingsFragment.class.getName())
+ .setArguments(bundle)
+ .setSourceMetricsCategory(SettingsEnums.CELLULAR_SECURITY_SETTINGS)
+ .launch();
+ }
+ return true;
+ }
+
+ @VisibleForTesting
+ protected boolean isSafetyCenterSupported() {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
+ return false;
+ }
+ SafetyCenterManager safetyCenterManager = mContext.getSystemService(
+ SafetyCenterManager.class);
+ if (safetyCenterManager == null) {
+ return false;
+ }
+ return safetyCenterManager.isSafetyCenterEnabled();
+ }
+
+ @VisibleForTesting
+ protected boolean areNotificationsEnabled() {
+ if (mTelephonyManager == null) {
+ Log.w(LOG_TAG, "Telephony manager not yet initialized");
+ mTelephonyManager = mContext.getSystemService(TelephonyManager.class);
+ }
+
+ return mTelephonyManager.isNullCipherNotificationsEnabled()
+ && mTelephonyManager.isCellularIdentifierDisclosureNotificationsEnabled();
+ }
+}
diff --git a/src/com/android/settings/network/MobileNetworkRepository.java b/src/com/android/settings/network/MobileNetworkRepository.java
index b0c85fc..381f3c1 100644
--- a/src/com/android/settings/network/MobileNetworkRepository.java
+++ b/src/com/android/settings/network/MobileNetworkRepository.java
@@ -295,7 +295,9 @@
}
public void removeRegister(MobileNetworkCallback mobileNetworkCallback) {
- sCallbacks.remove(mobileNetworkCallback);
+ synchronized (this) {
+ sCallbacks.remove(mobileNetworkCallback);
+ }
if (sCallbacks.isEmpty()) {
mSubscriptionManager.removeOnSubscriptionsChangedListener(this);
mAirplaneModeObserver.unRegister(mContext);
diff --git a/src/com/android/settings/network/SimOnboardingService.kt b/src/com/android/settings/network/SimOnboardingService.kt
index 2ec1ad3..f99a2b9 100644
--- a/src/com/android/settings/network/SimOnboardingService.kt
+++ b/src/com/android/settings/network/SimOnboardingService.kt
@@ -24,6 +24,7 @@
import android.telephony.UiccSlotInfo
import android.util.Log
import com.android.settings.network.SimOnboardingActivity.Companion.CallbackType
+import com.android.settings.network.telephony.TelephonyRepository
import com.android.settings.sim.SimActivationNotifier
import com.android.settings.spa.network.setAutomaticData
import com.android.settings.spa.network.setDefaultData
@@ -31,6 +32,7 @@
import com.android.settings.spa.network.setDefaultVoice
import com.android.settingslib.utils.ThreadUtils
import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.withContext
class SimOnboardingService {
@@ -46,7 +48,7 @@
var targetPrimarySimCalls: Int = SubscriptionManager.INVALID_SUBSCRIPTION_ID
var targetPrimarySimTexts: Int = SubscriptionManager.INVALID_SUBSCRIPTION_ID
var targetPrimarySimMobileData: Int = SubscriptionManager.INVALID_SUBSCRIPTION_ID
- var targetPrimarySimAutoDataSwitch: Boolean = false
+ val targetPrimarySimAutoDataSwitch = MutableStateFlow(false)
var targetNonDds: Int = SubscriptionManager.INVALID_SUBSCRIPTION_ID
get() {
if(targetPrimarySimMobileData == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
@@ -349,19 +351,10 @@
null,
targetPrimarySimMobileData
)
-
- var nonDds = targetNonDds
- Log.d(
- TAG,
- "setAutomaticData: targetNonDds: $nonDds," +
- " targetPrimarySimAutoDataSwitch: $targetPrimarySimAutoDataSwitch"
+ TelephonyRepository(context).setAutomaticData(
+ targetNonDds,
+ targetPrimarySimAutoDataSwitch.value
)
- if (nonDds != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
- val telephonyManagerForNonDds: TelephonyManager? =
- context.getSystemService(TelephonyManager::class.java)
- ?.createForSubscriptionId(nonDds)
- setAutomaticData(telephonyManagerForNonDds, targetPrimarySimAutoDataSwitch)
- }
}
// no next action, send finish
callback(CallbackType.CALLBACK_FINISH)
diff --git a/src/com/android/settings/network/apn/ApnNetworkTypeCheckBox.kt b/src/com/android/settings/network/apn/ApnNetworkTypeCheckBox.kt
index bc85f55..a42031b 100644
--- a/src/com/android/settings/network/apn/ApnNetworkTypeCheckBox.kt
+++ b/src/com/android/settings/network/apn/ApnNetworkTypeCheckBox.kt
@@ -24,18 +24,13 @@
@Composable
fun ApnNetworkTypeCheckBox(apnData: ApnData, onNetworkTypeChanged: (Long) -> Unit) {
- val options = remember { ApnNetworkTypes.getNetworkTypeOptions() }
- val selectedStateMap = remember {
- ApnNetworkTypes.networkTypeToSelectedStateMap(options, apnData.networkType)
- }
+ val options = remember { ApnNetworkTypes.getNetworkTypeOptions(apnData.networkType) }
SettingsDropdownCheckBox(
label = stringResource(R.string.network_type),
options = options,
emptyText = stringResource(R.string.network_type_unspecified),
enabled = apnData.networkTypeEnabled,
) {
- onNetworkTypeChanged(
- ApnNetworkTypes.selectedStateMapToNetworkType(options, selectedStateMap)
- )
+ onNetworkTypeChanged(ApnNetworkTypes.optionsToNetworkType(options))
}
}
diff --git a/src/com/android/settings/network/apn/ApnNetworkTypes.kt b/src/com/android/settings/network/apn/ApnNetworkTypes.kt
index e7a93b3..f14b0b3 100644
--- a/src/com/android/settings/network/apn/ApnNetworkTypes.kt
+++ b/src/com/android/settings/network/apn/ApnNetworkTypes.kt
@@ -17,8 +17,7 @@
package com.android.settings.network.apn
import android.telephony.TelephonyManager
-import androidx.compose.runtime.mutableStateMapOf
-import androidx.compose.runtime.snapshots.SnapshotStateMap
+import androidx.compose.runtime.mutableStateOf
import com.android.settingslib.spa.widget.editor.SettingsDropdownCheckOption
object ApnNetworkTypes {
@@ -40,38 +39,28 @@
TelephonyManager.NETWORK_TYPE_NR,
)
- fun getNetworkTypeOptions(): List<SettingsDropdownCheckOption> =
- Types.map { SettingsDropdownCheckOption(TelephonyManager.getNetworkTypeName(it)) }
-
/**
* Gets the selected Network type Selected Options according to network type.
* @param networkType Initialized network type bitmask, often multiple network type options may
* be included.
*/
- fun networkTypeToSelectedStateMap(
- options: List<SettingsDropdownCheckOption>,
- networkType: Long,
- ): SnapshotStateMap<SettingsDropdownCheckOption, Boolean> {
- val stateMap = mutableStateMapOf<SettingsDropdownCheckOption, Boolean>()
- Types.forEachIndexed { index, type ->
- if (networkType and TelephonyManager.getBitMaskForNetworkType(type) != 0L) {
- stateMap[options[index]] = true
- }
+ fun getNetworkTypeOptions(networkType: Long): List<SettingsDropdownCheckOption> =
+ Types.map { type ->
+ val selected = networkType and TelephonyManager.getBitMaskForNetworkType(type) != 0L
+ SettingsDropdownCheckOption(
+ text = TelephonyManager.getNetworkTypeName(type),
+ selected = mutableStateOf(selected),
+ )
}
- return stateMap
- }
/**
* Gets the network type according to the selected Network type Selected Options.
- * @param stateMap the selected Network type Selected Options.
+ * @param options the selected Network type Selected Options.
*/
- fun selectedStateMapToNetworkType(
- options: List<SettingsDropdownCheckOption>,
- stateMap: SnapshotStateMap<SettingsDropdownCheckOption, Boolean>,
- ): Long {
+ fun optionsToNetworkType(options: List<SettingsDropdownCheckOption>): Long {
var networkType = 0L
options.forEachIndexed { index, option ->
- if (stateMap[option] == true) {
+ if (option.selected.value) {
networkType = networkType or TelephonyManager.getBitMaskForNetworkType(Types[index])
}
}
diff --git a/src/com/android/settings/network/telephony/CellularSecurityEncryptionDividerController.java b/src/com/android/settings/network/telephony/CellularSecurityEncryptionDividerController.java
new file mode 100644
index 0000000..6d31c72
--- /dev/null
+++ b/src/com/android/settings/network/telephony/CellularSecurityEncryptionDividerController.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.network.telephony;
+
+import android.content.Context;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.preference.PreferenceScreen;
+
+import com.android.internal.telephony.flags.FeatureFlags;
+import com.android.internal.telephony.flags.FeatureFlagsImpl;
+import com.android.settings.core.BasePreferenceController;
+
+/**
+ * {@link BasePreferenceController} for visibility of Encryption divider on Cellular Security
+ * settings page.
+ */
+public class CellularSecurityEncryptionDividerController extends
+ BasePreferenceController {
+
+ private static final String LOG_TAG = "CellularSecurityEncryptionDividerController";
+
+ private TelephonyManager mTelephonyManager;
+
+ protected final FeatureFlags mFeatureFlags = new FeatureFlagsImpl();
+
+ /**
+ * Class constructor of "Cellular Security" preference.
+ *
+ * @param context of settings
+ * @param prefKey assigned within UI entry of XML file
+ */
+ public CellularSecurityEncryptionDividerController(
+ @NonNull Context context, @NonNull String prefKey) {
+ super(context, prefKey);
+ mTelephonyManager = mContext.getSystemService(TelephonyManager.class);
+ }
+
+ /**
+ * Initialization.
+ */
+ public CellularSecurityEncryptionDividerController init() {
+ return this;
+ }
+
+ @Override
+ public void displayPreference(@NonNull PreferenceScreen screen) {
+ super.displayPreference(screen);
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ if (mTelephonyManager == null) {
+ Log.w(LOG_TAG,
+ "Telephony manager not yet initialized. Marking availability as "
+ + "CONDITIONALLY_UNAVAILABLE");
+ mTelephonyManager = mContext.getSystemService(TelephonyManager.class);
+ return CONDITIONALLY_UNAVAILABLE;
+ }
+
+ try {
+ mTelephonyManager.isNullCipherAndIntegrityPreferenceEnabled();
+ } catch (UnsupportedOperationException e) {
+ Log.i(LOG_TAG, "Null cipher enablement is unsupported, hiding divider: "
+ + e.getMessage());
+ return UNSUPPORTED_ON_DEVICE;
+ } catch (Exception e) {
+ Log.e(LOG_TAG,
+ "Failed isNullCipherAndIntegrityEnabled. Setting availability to "
+ + "CONDITIONALLY_UNAVAILABLE. Exception: "
+ + e.getMessage());
+ return CONDITIONALLY_UNAVAILABLE;
+ }
+ return AVAILABLE;
+ }
+}
diff --git a/src/com/android/settings/network/telephony/CellularSecurityNotificationsDividerController.java b/src/com/android/settings/network/telephony/CellularSecurityNotificationsDividerController.java
new file mode 100644
index 0000000..bbe954c
--- /dev/null
+++ b/src/com/android/settings/network/telephony/CellularSecurityNotificationsDividerController.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.network.telephony;
+
+import android.content.Context;
+import android.os.Build;
+import android.safetycenter.SafetyCenterManager;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.PreferenceScreen;
+
+import com.android.internal.telephony.flags.Flags;
+import com.android.settings.core.BasePreferenceController;
+
+/**
+ * {@link BasePreferenceController} for visibility of Notifications divider on Cellular Security
+ * settings page.
+ */
+public class CellularSecurityNotificationsDividerController extends
+ BasePreferenceController {
+
+ private static final String LOG_TAG = "CellularSecurityNotificationsDividerController";
+
+ private TelephonyManager mTelephonyManager;
+ @VisibleForTesting
+ protected SafetyCenterManager mSafetyCenterManager;
+
+ /**
+ * Class constructor of "Cellular Security" preference.
+ *
+ * @param context of settings
+ * @param prefKey assigned within UI entry of XML file
+ */
+ public CellularSecurityNotificationsDividerController(
+ @NonNull Context context, @NonNull String prefKey) {
+ super(context, prefKey);
+ mTelephonyManager = mContext.getSystemService(TelephonyManager.class);
+ mSafetyCenterManager = mContext.getSystemService(SafetyCenterManager.class);
+ }
+
+ /**
+ * Initialization.
+ */
+ public CellularSecurityNotificationsDividerController init() {
+ return this;
+ }
+
+ @Override
+ public void displayPreference(@NonNull PreferenceScreen screen) {
+ super.displayPreference(screen);
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ if (!Flags.enableIdentifierDisclosureTransparencyUnsolEvents()
+ || !Flags.enableModemCipherTransparencyUnsolEvents()
+ || !Flags.enableIdentifierDisclosureTransparency()
+ || !Flags.enableModemCipherTransparency()) {
+ return UNSUPPORTED_ON_DEVICE;
+ }
+ if (!isSafetyCenterSupported()) {
+ return UNSUPPORTED_ON_DEVICE;
+ }
+ if (mTelephonyManager == null) {
+ Log.w(LOG_TAG, "Telephony manager not yet initialized");
+ mTelephonyManager = mContext.getSystemService(TelephonyManager.class);
+ }
+ // Checking for hardware support, i.e. IRadio AIDL version must be >= 2.2
+ try {
+ // Must call both APIs, as we can't use the combined toggle if both aren't available
+ areNotificationsEnabled();
+ } catch (UnsupportedOperationException e) {
+ Log.i(LOG_TAG, "Cellular security notifications are unsupported, hiding divider: "
+ + e.getMessage());
+ return UNSUPPORTED_ON_DEVICE;
+ }
+
+ return AVAILABLE;
+ }
+
+ @VisibleForTesting
+ protected boolean areNotificationsEnabled() {
+ return mTelephonyManager.isNullCipherNotificationsEnabled()
+ && mTelephonyManager.isCellularIdentifierDisclosureNotificationsEnabled();
+ }
+
+ protected boolean isSafetyCenterSupported() {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
+ return false;
+ }
+ mSafetyCenterManager = mContext.getSystemService(
+ SafetyCenterManager.class);
+ if (mSafetyCenterManager == null) {
+ return false;
+ }
+ return mSafetyCenterManager.isSafetyCenterEnabled();
+ }
+}
diff --git a/src/com/android/settings/network/telephony/CellularSecurityNotificationsPreferenceController.java b/src/com/android/settings/network/telephony/CellularSecurityNotificationsPreferenceController.java
new file mode 100644
index 0000000..520e7c5
--- /dev/null
+++ b/src/com/android/settings/network/telephony/CellularSecurityNotificationsPreferenceController.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.network.telephony;
+
+import android.content.Context;
+import android.os.Build;
+import android.safetycenter.SafetyCenterManager;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.PreferenceScreen;
+
+import com.android.internal.telephony.flags.Flags;
+
+/**
+ * {@link TelephonyTogglePreferenceController} for accessing Cellular Security settings through
+ * Safety Center.
+ */
+public class CellularSecurityNotificationsPreferenceController extends
+ TelephonyTogglePreferenceController {
+
+ private static final String LOG_TAG = "CellularSecurityNotificationsPreferenceController";
+
+ private TelephonyManager mTelephonyManager;
+ @VisibleForTesting
+ protected SafetyCenterManager mSafetyCenterManager;
+
+ /**
+ * Class constructor of "Cellular Security" preference.
+ *
+ * @param context of settings
+ * @param prefKey assigned within UI entry of XML file
+ */
+ public CellularSecurityNotificationsPreferenceController(
+ @NonNull Context context, @NonNull String prefKey) {
+ super(context, prefKey);
+ mTelephonyManager = mContext.getSystemService(TelephonyManager.class);
+ mSafetyCenterManager = mContext.getSystemService(SafetyCenterManager.class);
+ }
+
+ /**
+ * Initialization based on a given subscription id.
+ *
+ * @param subId is the subscription id
+ * @return this instance after initialization
+ */
+ public CellularSecurityNotificationsPreferenceController init(@NonNull int subId) {
+ mTelephonyManager = mContext.getSystemService(TelephonyManager.class)
+ .createForSubscriptionId(subId);
+ return this;
+ }
+
+ @Override
+ public void displayPreference(@NonNull PreferenceScreen screen) {
+ super.displayPreference(screen);
+ }
+
+ @Override
+ public int getAvailabilityStatus(int subId) {
+ if (!isSafetyCenterSupported()) {
+ return UNSUPPORTED_ON_DEVICE;
+ }
+
+ if (!areFlagsEnabled()) {
+ return UNSUPPORTED_ON_DEVICE;
+ }
+ if (mTelephonyManager == null) {
+ Log.w(LOG_TAG, "Telephony manager not yet initialized");
+ mTelephonyManager = mContext.getSystemService(TelephonyManager.class);
+ }
+
+ // Checking for hardware support, i.e. IRadio AIDL version must be >= 2.2
+ try {
+ areNotificationsEnabled();
+ } catch (UnsupportedOperationException e) {
+ Log.i(LOG_TAG, "Cellular security notifications are unsupported: " + e.getMessage());
+ return UNSUPPORTED_ON_DEVICE;
+ }
+
+ return AVAILABLE;
+ }
+
+ /**
+ * Return {@code true} if cellular security notifications are on
+ *
+ * <p><b>NOTE:</b> This method returns the active state of the preference controller and is not
+ * the parameter passed into {@link #setChecked(boolean)}, which is instead the requested future
+ * state.
+ */
+ @Override
+ public boolean isChecked() {
+ if (!areFlagsEnabled()) {
+ return false;
+ }
+
+ try {
+ // Note: the default behavior for this toggle is disabled (as the underlying
+ // TelephonyManager APIs are disabled by default)
+ return areNotificationsEnabled();
+ } catch (Exception e) {
+ Log.e(LOG_TAG,
+ "Failed isNullCipherNotificationsEnabled and "
+ + "isCellularIdentifierDisclosureNotificationsEnabled."
+ + "Defaulting toggle to checked = true. Exception: "
+ + e.getMessage());
+ return false;
+ }
+ }
+
+ /**
+ * Called when a user preference changes on the toggle. We pass this info on to the Telephony
+ * Framework so that the modem can be updated with the user's preference.
+ *
+ * <p>See {@link com.android.settings.core.TogglePreferenceController#setChecked(boolean)} for
+ * details.
+ *
+ * @param isChecked The toggle value that we're being requested to enforce. A value of {@code
+ * true} denotes that both (1) null cipher/integrity notifications, and
+ * (2) IMSI disclosure notifications will be enabled by the modem after this
+ * function completes, if they are not already.
+ */
+ @Override
+ public boolean setChecked(boolean isChecked) {
+ if (isChecked) {
+ Log.i(LOG_TAG, "Enabling cellular security notifications.");
+ } else {
+ Log.i(LOG_TAG, "Disabling cellular security notifications.");
+ }
+
+ // Check flag status
+ if (!areFlagsEnabled()) {
+ return false;
+ }
+
+ try {
+ setNotifications(isChecked);
+ } catch (Exception e) {
+ Log.e(LOG_TAG,
+ "Failed setCellularIdentifierDisclosureNotificationEnabled or "
+ + " setNullCipherNotificationsEnabled. Setting not updated. Exception: "
+ + e.getMessage());
+ // Reset to defaults so we don't end up in an inconsistent state
+ setNotifications(!isChecked);
+ return false;
+ }
+ return true;
+ }
+
+ private void setNotifications(boolean isChecked) {
+ mTelephonyManager.setEnableCellularIdentifierDisclosureNotifications(isChecked);
+ mTelephonyManager.setNullCipherNotificationsEnabled(isChecked);
+ }
+
+ private boolean areNotificationsEnabled() {
+ return mTelephonyManager.isNullCipherNotificationsEnabled()
+ && mTelephonyManager.isCellularIdentifierDisclosureNotificationsEnabled();
+ }
+
+ private boolean areFlagsEnabled() {
+ if (!Flags.enableIdentifierDisclosureTransparencyUnsolEvents()
+ || !Flags.enableModemCipherTransparencyUnsolEvents()
+ || !Flags.enableIdentifierDisclosureTransparency()
+ || !Flags.enableModemCipherTransparency()) {
+ return false;
+ }
+ return true;
+ }
+
+ protected boolean isSafetyCenterSupported() {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
+ return false;
+ }
+ mSafetyCenterManager = mContext.getSystemService(
+ SafetyCenterManager.class);
+ if (mSafetyCenterManager == null) {
+ return false;
+ }
+ return mSafetyCenterManager.isSafetyCenterEnabled();
+ }
+}
diff --git a/src/com/android/settings/network/telephony/CellularSecuritySettingsFragment.java b/src/com/android/settings/network/telephony/CellularSecuritySettingsFragment.java
new file mode 100644
index 0000000..3e37352
--- /dev/null
+++ b/src/com/android/settings/network/telephony/CellularSecuritySettingsFragment.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settings.network.telephony;
+
+import android.app.settings.SettingsEnums;
+import android.os.Bundle;
+
+import com.android.settings.R;
+import com.android.settings.dashboard.DashboardFragment;
+import com.android.settings.search.BaseSearchIndexProvider;
+import com.android.settingslib.search.SearchIndexable;
+
+/**
+ * Cellular Security features (insecure network notifications, network security controls, etc)
+ */
+@SearchIndexable
+public class CellularSecuritySettingsFragment extends DashboardFragment {
+
+ private static final String TAG = "CellularSecuritySettingsFragment";
+
+ public static final String KEY_CELLULAR_SECURITY_PREFERENCE = "cellular_security";
+
+ @Override
+ public int getMetricsCategory() {
+ return SettingsEnums.CELLULAR_SECURITY_SETTINGS;
+ }
+
+ @Override
+ protected String getLogTag() {
+ return TAG;
+ }
+
+ @Override
+ protected int getPreferenceScreenResId() {
+ return R.xml.cellular_security;
+ }
+
+ @Override
+ public void onCreatePreferences(Bundle bundle, String rootKey) {
+ super.onCreatePreferences(bundle, rootKey);
+ setPreferencesFromResource(R.xml.cellular_security, rootKey);
+ }
+
+ public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
+ new BaseSearchIndexProvider(R.xml.cellular_security);
+}
diff --git a/src/com/android/settings/network/telephony/TelephonyRepository.kt b/src/com/android/settings/network/telephony/TelephonyRepository.kt
index 678aaac..18af621 100644
--- a/src/com/android/settings/network/telephony/TelephonyRepository.kt
+++ b/src/com/android/settings/network/telephony/TelephonyRepository.kt
@@ -17,8 +17,10 @@
package com.android.settings.network.telephony
import android.content.Context
+import android.telephony.SubscriptionManager
import android.telephony.TelephonyCallback
import android.telephony.TelephonyManager
+import android.util.Log
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.asExecutor
import kotlinx.coroutines.channels.ProducerScope
@@ -26,15 +28,51 @@
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.conflate
+import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+
+class TelephonyRepository(
+ private val context: Context,
+ private val subscriptionsChangedFlow: Flow<Unit> = context.subscriptionsChangedFlow(),
+) {
+ fun isMobileDataPolicyEnabledFlow(
+ subId: Int,
+ @TelephonyManager.MobileDataPolicy policy: Int,
+ ): Flow<Boolean> {
+ if (!SubscriptionManager.isValidSubscriptionId(subId)) return flowOf(false)
+
+ val telephonyManager = context.telephonyManager(subId)
+
+ return subscriptionsChangedFlow.map {
+ telephonyManager.isMobileDataPolicyEnabled(policy)
+ .also { Log.d(TAG, "[$subId] isMobileDataPolicyEnabled($policy): $it") }
+ }.conflate().flowOn(Dispatchers.Default)
+ }
+
+ fun setMobileDataPolicyEnabled(
+ subId: Int,
+ @TelephonyManager.MobileDataPolicy policy: Int,
+ enabled: Boolean,
+ ) {
+ if (!SubscriptionManager.isValidSubscriptionId(subId)) return
+
+ val telephonyManager = context.telephonyManager(subId)
+ Log.d(TAG, "[$subId] setMobileDataPolicyEnabled($policy): $enabled")
+ telephonyManager.setMobileDataPolicyEnabled(policy, enabled)
+ }
+
+ private companion object {
+ private const val TAG = "TelephonyRepository"
+ }
+}
/** Creates an instance of a cold Flow for Telephony callback of given [subId]. */
fun <T> Context.telephonyCallbackFlow(
subId: Int,
block: ProducerScope<T>.() -> TelephonyCallback,
): Flow<T> = callbackFlow {
- val telephonyManager = getSystemService(TelephonyManager::class.java)!!
- .createForSubscriptionId(subId)
+ val telephonyManager = telephonyManager(subId)
val callback = block()
@@ -42,3 +80,7 @@
awaitClose { telephonyManager.unregisterTelephonyCallback(callback) }
}.conflate().flowOn(Dispatchers.Default)
+
+fun Context.telephonyManager(subId: Int): TelephonyManager =
+ getSystemService(TelephonyManager::class.java)!!
+ .createForSubscriptionId(subId)
diff --git a/src/com/android/settings/password/ChooseLockGeneric.java b/src/com/android/settings/password/ChooseLockGeneric.java
index 178c387..ce9a566 100644
--- a/src/com/android/settings/password/ChooseLockGeneric.java
+++ b/src/com/android/settings/password/ChooseLockGeneric.java
@@ -33,6 +33,7 @@
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_CHOOSE_LOCK_SCREEN_DESCRIPTION;
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_CHOOSE_LOCK_SCREEN_TITLE;
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_DEVICE_PASSWORD_REQUIREMENT_ONLY;
+import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_FINGERPRINT_ENROLLMENT_ONLY;
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_IS_CALLING_APP_ADMIN;
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_REQUESTED_MIN_COMPLEXITY;
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_REQUEST_WRITE_REPAIR_MODE_PW;
@@ -63,6 +64,7 @@
import android.widget.TextView;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.annotation.VisibleForTesting;
import androidx.appcompat.app.AlertDialog;
@@ -167,7 +169,7 @@
private boolean mWaitingForActivityResult = false;
private LockscreenCredential mUserPassword;
private FingerprintManager mFingerprintManager;
- private FaceManager mFaceManager;
+ @Nullable private FaceManager mFaceManager;
private int mUserId;
private boolean mIsManagedProfile;
private ManagedLockPasswordProvider mManagedPasswordProvider;
@@ -206,6 +208,7 @@
private int mExtraLockScreenTitleResId;
private int mExtraLockScreenDescriptionResId;
private boolean mWaitingForBiometricEnrollment = false;
+ private boolean mEnrollFingerPrintOnly = false;
@Override
public int getMetricsCategory() {
@@ -225,8 +228,10 @@
}
final Intent intent = activity.getIntent();
String chooseLockAction = intent.getAction();
+ mEnrollFingerPrintOnly =
+ intent.getBooleanExtra(EXTRA_KEY_FINGERPRINT_ENROLLMENT_ONLY, false);
mFingerprintManager = Utils.getFingerprintManagerOrNull(activity);
- mFaceManager = Utils.getFaceManagerOrNull(activity);
+ mFaceManager = !mEnrollFingerPrintOnly ? Utils.getFaceManagerOrNull(activity) : null;
mDpm = (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
mLockPatternUtils = new LockPatternUtils(activity);
mIsSetNewPassword = ACTION_SET_NEW_PARENT_PROFILE_PASSWORD.equals(chooseLockAction)
@@ -530,6 +535,9 @@
final Intent intent =
new Intent(context, BiometricEnrollActivity.InternalActivity.class);
intent.putExtra(BiometricEnrollActivity.EXTRA_SKIP_INTRO, true);
+ if (mEnrollFingerPrintOnly) {
+ intent.putExtra(BiometricEnrollActivity.EXTRA_FINGERPRINT_ENROLLMENT_ONLY, true);
+ }
return intent;
}
@@ -927,34 +935,45 @@
switch (mLockPatternUtils.getKeyguardStoredPasswordQuality(mUserId)) {
case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
if (hasFingerprints && hasFace) {
- return R.string.unlock_disable_frp_warning_content_pattern_face_fingerprint;
+ return hasAppsWithAuthBoundKeys
+ ?
+ R.string.unlock_disable_frp_warning_content_face_fingerprint_authbound_keys
+ : R.string.unlock_disable_frp_warning_content_pattern_face_fingerprint;
} else if (hasFingerprints) {
- return R.string.unlock_disable_frp_warning_content_pattern_fingerprint;
+ return hasAppsWithAuthBoundKeys
+ ?
+ R.string.unlock_disable_frp_warning_content_fingerprint_authbound_keys
+ : R.string.unlock_disable_frp_warning_content_pattern_fingerprint;
} else if (hasFace) {
- return R.string.unlock_disable_frp_warning_content_pattern_face;
+ return hasAppsWithAuthBoundKeys
+ ?
+ R.string.unlock_disable_frp_warning_content_face_authbound_keys
+ : R.string.unlock_disable_frp_warning_content_pattern_face;
} else {
- return R.string.unlock_disable_frp_warning_content_pattern;
+ return hasAppsWithAuthBoundKeys
+ ? R.string.unlock_disable_frp_warning_content_authbound_keys
+ : R.string.unlock_disable_frp_warning_content_pattern;
}
case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX:
if (hasFingerprints && hasFace) {
return hasAppsWithAuthBoundKeys
?
- R.string.unlock_disable_frp_warning_content_pin_face_fingerprint_authbound_keys
+ R.string.unlock_disable_frp_warning_content_face_fingerprint_authbound_keys
: R.string.unlock_disable_frp_warning_content_pin_face_fingerprint;
} else if (hasFingerprints) {
return hasAppsWithAuthBoundKeys
?
- R.string.unlock_disable_frp_warning_content_pin_fingerprint_authbound_keys
+ R.string.unlock_disable_frp_warning_content_fingerprint_authbound_keys
: R.string.unlock_disable_frp_warning_content_pin_fingerprint;
} else if (hasFace) {
return hasAppsWithAuthBoundKeys
?
- R.string.unlock_disable_frp_warning_content_pin_face_authbound_keys
+ R.string.unlock_disable_frp_warning_content_face_authbound_keys
: R.string.unlock_disable_frp_warning_content_pin_face;
} else {
return hasAppsWithAuthBoundKeys
- ? R.string.unlock_disable_frp_warning_content_pin_authbound_keys
+ ? R.string.unlock_disable_frp_warning_content_authbound_keys
: R.string.unlock_disable_frp_warning_content_pin;
}
case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC:
@@ -962,24 +981,45 @@
case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX:
case DevicePolicyManager.PASSWORD_QUALITY_MANAGED:
if (hasFingerprints && hasFace) {
- return R.string
- .unlock_disable_frp_warning_content_password_face_fingerprint;
+ return hasAppsWithAuthBoundKeys
+ ?
+ R.string.unlock_disable_frp_warning_content_face_fingerprint_authbound_keys
+ : R.string.unlock_disable_frp_warning_content_password_face_fingerprint;
} else if (hasFingerprints) {
- return R.string.unlock_disable_frp_warning_content_password_fingerprint;
+ return hasAppsWithAuthBoundKeys
+ ?
+ R.string.unlock_disable_frp_warning_content_fingerprint_authbound_keys
+ : R.string.unlock_disable_frp_warning_content_password_fingerprint;
} else if (hasFace) {
- return R.string.unlock_disable_frp_warning_content_password_face;
+ return hasAppsWithAuthBoundKeys
+ ?
+ R.string.unlock_disable_frp_warning_content_face_authbound_keys
+ : R.string.unlock_disable_frp_warning_content_password_face;
} else {
- return R.string.unlock_disable_frp_warning_content_password;
+ return hasAppsWithAuthBoundKeys
+ ? R.string.unlock_disable_frp_warning_content_authbound_keys
+ : R.string.unlock_disable_frp_warning_content_password;
}
default:
if (hasFingerprints && hasFace) {
- return R.string.unlock_disable_frp_warning_content_unknown_face_fingerprint;
+ return hasAppsWithAuthBoundKeys
+ ?
+ R.string.unlock_disable_frp_warning_content_face_fingerprint_authbound_keys
+ : R.string.unlock_disable_frp_warning_content_unknown_face_fingerprint;
} else if (hasFingerprints) {
- return R.string.unlock_disable_frp_warning_content_unknown_fingerprint;
+ return hasAppsWithAuthBoundKeys
+ ?
+ R.string.unlock_disable_frp_warning_content_fingerprint_authbound_keys
+ : R.string.unlock_disable_frp_warning_content_unknown_fingerprint;
} else if (hasFace) {
- return R.string.unlock_disable_frp_warning_content_unknown_face;
+ return hasAppsWithAuthBoundKeys
+ ?
+ R.string.unlock_disable_frp_warning_content_face_authbound_keys
+ : R.string.unlock_disable_frp_warning_content_unknown_face;
} else {
- return R.string.unlock_disable_frp_warning_content_unknown;
+ return hasAppsWithAuthBoundKeys
+ ? R.string.unlock_disable_frp_warning_content_authbound_keys
+ : R.string.unlock_disable_frp_warning_content_unknown;
}
}
}
diff --git a/src/com/android/settings/password/ChooseLockSettingsHelper.java b/src/com/android/settings/password/ChooseLockSettingsHelper.java
index e74b776..91875cc 100644
--- a/src/com/android/settings/password/ChooseLockSettingsHelper.java
+++ b/src/com/android/settings/password/ChooseLockSettingsHelper.java
@@ -64,6 +64,8 @@
public static final String EXTRA_KEY_FOR_FACE = "for_face";
// For the paths where multiple biometric sensors exist
public static final String EXTRA_KEY_FOR_BIOMETRICS = "for_biometrics";
+ // To support fingerprint enrollment only and skip other biometric enrollments like face.
+ public static final String EXTRA_KEY_FINGERPRINT_ENROLLMENT_ONLY = "for_fingerprint_only";
// For the paths where setup biometrics in suw flow
public static final String EXTRA_KEY_IS_SUW = "is_suw";
public static final String EXTRA_KEY_FOREGROUND_ONLY = "foreground_only";
diff --git a/src/com/android/settings/password/SetNewPasswordActivity.java b/src/com/android/settings/password/SetNewPasswordActivity.java
index 9614d28..0ba52ea 100644
--- a/src/com/android/settings/password/SetNewPasswordActivity.java
+++ b/src/com/android/settings/password/SetNewPasswordActivity.java
@@ -27,6 +27,7 @@
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_CHOOSE_LOCK_SCREEN_DESCRIPTION;
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_CHOOSE_LOCK_SCREEN_TITLE;
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_DEVICE_PASSWORD_REQUIREMENT_ONLY;
+import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_FINGERPRINT_ENROLLMENT_ONLY;
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_IS_CALLING_APP_ADMIN;
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_REQUESTED_MIN_COMPLEXITY;
@@ -132,6 +133,8 @@
getIntent().getIntExtra(EXTRA_KEY_CHOOSE_LOCK_SCREEN_TITLE, -1));
intent.putExtra(EXTRA_KEY_CHOOSE_LOCK_SCREEN_DESCRIPTION,
getIntent().getIntExtra(EXTRA_KEY_CHOOSE_LOCK_SCREEN_DESCRIPTION, -1));
+ intent.putExtra(EXTRA_KEY_FINGERPRINT_ENROLLMENT_ONLY,
+ getIntent().getBooleanExtra(EXTRA_KEY_FINGERPRINT_ENROLLMENT_ONLY, false));
if (mCallerAppName != null) {
intent.putExtra(EXTRA_KEY_CALLER_APP_NAME, mCallerAppName);
}
diff --git a/src/com/android/settings/password/SetNewPasswordController.java b/src/com/android/settings/password/SetNewPasswordController.java
index aa8fe51..96c0b8e 100644
--- a/src/com/android/settings/password/SetNewPasswordController.java
+++ b/src/com/android/settings/password/SetNewPasswordController.java
@@ -21,6 +21,7 @@
import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT;
import static com.android.internal.util.Preconditions.checkNotNull;
+import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_FINGERPRINT_ENROLLMENT_ONLY;
import android.app.ActivityManager;
import android.app.admin.DevicePolicyManager;
@@ -79,7 +80,10 @@
}
// Create a wrapper of FingerprintManager for testing, see IFingerPrintManager for details.
final FingerprintManager fingerprintManager = Utils.getFingerprintManagerOrNull(context);
- final FaceManager faceManager = Utils.getFaceManagerOrNull(context);
+ final FaceManager faceManager =
+ !intent.getBooleanExtra(EXTRA_KEY_FINGERPRINT_ENROLLMENT_ONLY, false)
+ ? Utils.getFaceManagerOrNull(context)
+ : null;
return new SetNewPasswordController(userId,
context.getPackageManager(),
fingerprintManager, faceManager,
diff --git a/src/com/android/settings/privatespace/HidePrivateSpaceSummaryController.java b/src/com/android/settings/privatespace/HidePrivateSpaceSummaryController.java
index a366b70..f5100a7 100644
--- a/src/com/android/settings/privatespace/HidePrivateSpaceSummaryController.java
+++ b/src/com/android/settings/privatespace/HidePrivateSpaceSummaryController.java
@@ -49,8 +49,8 @@
@Override
public CharSequence getSummary() {
- return isHidden() ? mContext.getString(R.string.privatespace_hide_on_summary)
- : mContext.getString(R.string.privatespace_hide_off_summary);
+ return isHidden() ? mContext.getString(R.string.private_space_hide_on_summary)
+ : mContext.getString(R.string.private_space_hide_off_summary);
}
private boolean isHidden() {
diff --git a/src/com/android/settings/privatespace/PrivateProfileContextHelperActivity.java b/src/com/android/settings/privatespace/PrivateProfileContextHelperActivity.java
index f2a50dc..416b2dd 100644
--- a/src/com/android/settings/privatespace/PrivateProfileContextHelperActivity.java
+++ b/src/com/android/settings/privatespace/PrivateProfileContextHelperActivity.java
@@ -22,6 +22,7 @@
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_CHOOSE_LOCK_SCREEN_DESCRIPTION;
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_CHOOSE_LOCK_SCREEN_TITLE;
+import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_FINGERPRINT_ENROLLMENT_ONLY;
import static com.android.settings.privatespace.PrivateSpaceSetupActivity.ACCOUNT_LOGIN_ACTION;
import static com.android.settings.privatespace.PrivateSpaceSetupActivity.EXTRA_ACTION_TYPE;
import static com.android.settings.privatespace.PrivateSpaceSetupActivity.SET_LOCK_ACTION;
@@ -85,6 +86,7 @@
private void createPrivateSpaceLock() {
final Intent intent = new Intent(ACTION_SET_NEW_PASSWORD);
intent.putExtra(EXTRA_PASSWORD_COMPLEXITY, PASSWORD_COMPLEXITY_LOW);
+ intent.putExtra(EXTRA_KEY_FINGERPRINT_ENROLLMENT_ONLY, true);
intent.putExtra(
EXTRA_KEY_CHOOSE_LOCK_SCREEN_TITLE, R.string.private_space_lock_setup_title);
intent.putExtra(
diff --git a/src/com/android/settings/privatespace/onelock/UseOneLockControllerSwitch.java b/src/com/android/settings/privatespace/onelock/UseOneLockControllerSwitch.java
index 680f5c7..dfac100 100644
--- a/src/com/android/settings/privatespace/onelock/UseOneLockControllerSwitch.java
+++ b/src/com/android/settings/privatespace/onelock/UseOneLockControllerSwitch.java
@@ -192,7 +192,8 @@
return;
}
new AlertDialog.Builder(mContext)
- .setMessage(R.string.private_space_new_lock_title)
+ .setTitle(R.string.private_space_new_lock_title)
+ .setMessage(R.string.private_space_new_lock_message)
.setPositiveButton(
R.string.private_space_set_lock_label,
(dialog, which) -> {
diff --git a/src/com/android/settings/spa/network/AutomaticDataSwitchingPreference.kt b/src/com/android/settings/spa/network/AutomaticDataSwitchingPreference.kt
new file mode 100644
index 0000000..824a935
--- /dev/null
+++ b/src/com/android/settings/spa/network/AutomaticDataSwitchingPreference.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.spa.network
+
+import android.telephony.TelephonyManager
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.ui.res.stringResource
+import com.android.settings.R
+import com.android.settings.network.telephony.TelephonyRepository
+import com.android.settingslib.spa.widget.preference.SwitchPreference
+import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+
+@Composable
+fun AutomaticDataSwitchingPreference(
+ isAutoDataEnabled: () -> Boolean?,
+ setAutoDataEnabled: (newEnabled: Boolean) -> Unit,
+) {
+ val autoDataSummary = stringResource(id = R.string.primary_sim_automatic_data_msg)
+ val coroutineScope = rememberCoroutineScope()
+ SwitchPreference(
+ object : SwitchPreferenceModel {
+ override val title = stringResource(id = R.string.primary_sim_automatic_data_title)
+ override val summary = { autoDataSummary }
+ override val checked = { isAutoDataEnabled() }
+ override val onCheckedChange: (Boolean) -> Unit = { newEnabled ->
+ coroutineScope.launch(Dispatchers.Default) {
+ setAutoDataEnabled(newEnabled)
+ }
+ }
+ }
+ )
+}
+
+fun TelephonyRepository.setAutomaticData(subId: Int, newEnabled: Boolean) {
+ setMobileDataPolicyEnabled(
+ subId = subId,
+ policy = TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH,
+ enabled = newEnabled,
+ )
+ //TODO: setup backup calling
+}
diff --git a/src/com/android/settings/spa/network/NetworkCellularGroupProvider.kt b/src/com/android/settings/spa/network/NetworkCellularGroupProvider.kt
index 5a2a394..bc5a4b7 100644
--- a/src/com/android/settings/spa/network/NetworkCellularGroupProvider.kt
+++ b/src/com/android/settings/spa/network/NetworkCellularGroupProvider.kt
@@ -30,7 +30,6 @@
import androidx.compose.runtime.MutableIntState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
-import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
@@ -44,6 +43,7 @@
import com.android.settings.R
import com.android.settings.network.SubscriptionInfoListViewModel
import com.android.settings.network.telephony.MobileNetworkUtils
+import com.android.settings.network.telephony.TelephonyRepository
import com.android.settings.spa.network.PrimarySimRepository.PrimarySimInfo
import com.android.settings.wifi.WifiPickerTrackerHelper
import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
@@ -53,8 +53,6 @@
import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
-import com.android.settingslib.spa.widget.preference.SwitchPreference
-import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
import com.android.settingslib.spa.widget.scaffold.RegularScaffold
import com.android.settingslib.spa.widget.ui.Category
import com.android.settingslib.spaprivileged.framework.common.broadcastReceiverFlow
@@ -193,7 +191,6 @@
callsSelectedId: MutableIntState,
textsSelectedId: MutableIntState,
mobileDataSelectedId: MutableIntState,
- nonDds: MutableIntState,
subscriptionManager: SubscriptionManager? =
LocalContext.current.getSystemService(SubscriptionManager::class.java),
coroutineScope: CoroutineScope = rememberCoroutineScope(),
@@ -223,23 +220,9 @@
)
}
},
- actionSetAutoDataSwitch: (Boolean) -> Unit = { newState ->
- coroutineScope.launch {
- val telephonyManagerForNonDds: TelephonyManager? =
- context.getSystemService(TelephonyManager::class.java)
- ?.createForSubscriptionId(nonDds.intValue)
- Log.d(NetworkCellularGroupProvider.name, "NonDds:${nonDds.intValue} setAutomaticData")
- setAutomaticData(telephonyManagerForNonDds, newState)
- }
- },
+ isAutoDataEnabled: () -> Boolean?,
+ setAutoDataEnabled: (newEnabled: Boolean) -> Unit,
) {
- val telephonyManagerForNonDds: TelephonyManager? =
- context.getSystemService(TelephonyManager::class.java)
- ?.createForSubscriptionId(nonDds.intValue)
- val automaticDataChecked = rememberSaveable() {
- mutableStateOf(false)
- }
-
CreatePrimarySimListPreference(
stringResource(id = R.string.primary_sim_calls_title),
primarySimInfo.callsAndSmsList,
@@ -262,31 +245,7 @@
actionSetMobileData
)
- val autoDataTitle = stringResource(id = R.string.primary_sim_automatic_data_title)
- val autoDataSummary = stringResource(id = R.string.primary_sim_automatic_data_msg)
- SwitchPreference(
- object : SwitchPreferenceModel {
- override val title = autoDataTitle
- override val summary = { autoDataSummary }
- override val checked = {
- if (nonDds.intValue != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
- coroutineScope.launch {
- automaticDataChecked.value = getAutomaticData(telephonyManagerForNonDds)
- Log.d(
- NetworkCellularGroupProvider.name,
- "NonDds:${nonDds.intValue}" +
- "getAutomaticData:${automaticDataChecked.value}"
- )
- }
- }
- automaticDataChecked.value
- }
- override val onCheckedChange: ((Boolean) -> Unit)? = {
- automaticDataChecked.value = it
- actionSetAutoDataSwitch(it)
- }
- }
- )
+ AutomaticDataSwitchingPreference(isAutoDataEnabled, setAutoDataEnabled)
}
@Composable
@@ -308,12 +267,21 @@
}.collectAsStateWithLifecycle(initialValue = null).value ?: return
Category(title = stringResource(id = R.string.primary_sim_title)) {
+ val isAutoDataEnabled by remember(nonDds.intValue) {
+ TelephonyRepository(context).isMobileDataPolicyEnabledFlow(
+ subId = nonDds.intValue,
+ policy = TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH
+ )
+ }.collectAsStateWithLifecycle(initialValue = null)
PrimarySimImpl(
primarySimInfo,
callsSelectedId,
textsSelectedId,
mobileDataSelectedId,
- nonDds
+ isAutoDataEnabled = { isAutoDataEnabled },
+ setAutoDataEnabled = { newEnabled ->
+ TelephonyRepository(context).setAutomaticData(nonDds.intValue, newEnabled)
+ },
)
}
}
@@ -381,23 +349,3 @@
wifiPickerTrackerHelper.setCarrierNetworkEnabled(true)
}
}
-
-suspend fun getAutomaticData(telephonyManagerForNonDds: TelephonyManager?): Boolean =
- withContext(Dispatchers.Default) {
- telephonyManagerForNonDds != null
- && telephonyManagerForNonDds.isMobileDataPolicyEnabled(
- TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH)
- }
-
-suspend fun setAutomaticData(telephonyManager: TelephonyManager?, newState: Boolean): Unit =
- withContext(Dispatchers.Default) {
- Log.d(
- NetworkCellularGroupProvider.name,
- "setAutomaticData: MOBILE_DATA_POLICY_AUTO_DATA_SWITCH as $newState"
- )
- telephonyManager?.setMobileDataPolicyEnabled(
- TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH,
- newState
- )
- //TODO: setup backup calling
- }
\ No newline at end of file
diff --git a/src/com/android/settings/spa/network/SimOnboardingPrimarySim.kt b/src/com/android/settings/spa/network/SimOnboardingPrimarySim.kt
index a8c0575..4fad332 100644
--- a/src/com/android/settings/spa/network/SimOnboardingPrimarySim.kt
+++ b/src/com/android/settings/spa/network/SimOnboardingPrimarySim.kt
@@ -23,6 +23,7 @@
import androidx.compose.material.icons.outlined.SignalCellularAlt
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableIntState
+import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
@@ -75,9 +76,6 @@
val mobileDataSelectedId = rememberSaveable {
mutableIntStateOf(SubscriptionManager.INVALID_SUBSCRIPTION_ID)
}
- val nonDdsRemember = rememberSaveable {
- mutableIntStateOf(SubscriptionManager.INVALID_SUBSCRIPTION_ID)
- }
Column(Modifier.padding(SettingsDimension.itemPadding)) {
SettingsBody(stringResource(id = R.string.sim_onboarding_primary_sim_msg))
@@ -94,12 +92,14 @@
callsSelectedId.intValue = onboardingService.targetPrimarySimCalls
textsSelectedId.intValue = onboardingService.targetPrimarySimTexts
mobileDataSelectedId.intValue = onboardingService.targetPrimarySimMobileData
+ val isAutoDataEnabled by
+ onboardingService.targetPrimarySimAutoDataSwitch
+ .collectAsStateWithLifecycle(initialValue = null)
PrimarySimImpl(
primarySimInfo = primarySimInfo,
callsSelectedId = callsSelectedId,
textsSelectedId = textsSelectedId,
mobileDataSelectedId = mobileDataSelectedId,
- nonDds = nonDdsRemember,
actionSetCalls = {
callsSelectedId.intValue = it
onboardingService.targetPrimarySimCalls = it},
@@ -109,8 +109,10 @@
actionSetMobileData = {
mobileDataSelectedId.intValue = it
onboardingService.targetPrimarySimMobileData = it},
- actionSetAutoDataSwitch = {
- onboardingService.targetPrimarySimAutoDataSwitch = it},
+ isAutoDataEnabled = { isAutoDataEnabled },
+ setAutoDataEnabled = { newEnabled ->
+ onboardingService.targetPrimarySimAutoDataSwitch.value = newEnabled
+ },
)
}
}
diff --git a/tests/robotests/res/xml-mcc997/location_settings.xml b/tests/robotests/res/xml-mcc997/location_settings.xml
new file mode 100644
index 0000000..d417d18
--- /dev/null
+++ b/tests/robotests/res/xml-mcc997/location_settings.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:settings="http://schemas.android.com/apk/res-auto"
+ android:key="fake_title_key"
+ android:title="screen_title">
+
+ <Preference
+ android:key="key1"
+ android:title="title"
+ android:icon="@drawable/ic_android"
+ android:summary="summary1"
+ settings:controller="com.android.settings.core.UnavailablePreferenceController"/>
+
+ <Preference
+ android:key="key2"
+ android:title="title"
+ android:icon="@drawable/ic_android"
+ android:summary="summary2"
+ settings:controller="com.android.settings.core.UnavailablePreferenceController"/>
+
+</PreferenceScreen>
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragmentTest.java b/tests/robotests/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragmentTest.java
index 3174101..9be03e8 100644
--- a/tests/robotests/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragmentTest.java
@@ -34,10 +34,10 @@
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.os.Bundle;
-import android.platform.test.annotations.RequiresFlagsDisabled;
-import android.platform.test.annotations.RequiresFlagsEnabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.provider.Settings;
import android.service.quicksettings.TileService;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.Flags;
@@ -47,6 +47,7 @@
import androidx.preference.PreferenceScreen;
import androidx.test.core.app.ApplicationProvider;
+import com.android.internal.accessibility.common.ShortcutConstants;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.accessibility.AccessibilityUtil.QuickSettingsTooltipType;
@@ -68,13 +69,13 @@
import org.robolectric.shadows.ShadowPackageManager;
import java.util.List;
+import java.util.Set;
/** Tests for {@link ToggleAccessibilityServicePreferenceFragment} */
@RunWith(RobolectricTestRunner.class)
public class ToggleAccessibilityServicePreferenceFragmentTest {
- @Rule
- public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+ @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
private static final String PLACEHOLDER_PACKAGE_NAME = "com.placeholder.example";
private static final String PLACEHOLDER_PACKAGE_NAME2 = "com.placeholder.example2";
@@ -236,7 +237,6 @@
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_CLEANUP_ACCESSIBILITY_WARNING_DIALOG)
public void enableService_warningRequired_showWarning() throws Throwable {
setupServiceWarningRequired(true);
mFragment.mToggleServiceSwitchPreference =
@@ -249,7 +249,6 @@
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_CLEANUP_ACCESSIBILITY_WARNING_DIALOG)
public void enableService_warningNotRequired_dontShowWarning() throws Throwable {
final AccessibilityServiceInfo info = setupServiceWarningRequired(false);
mFragment.mToggleServiceSwitchPreference =
@@ -263,7 +262,6 @@
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_CLEANUP_ACCESSIBILITY_WARNING_DIALOG)
public void toggleShortcutPreference_warningRequired_showWarning() throws Throwable {
setupServiceWarningRequired(true);
mFragment.mShortcutPreference = new ShortcutPreference(mContext, /* attrs= */null);
@@ -277,7 +275,6 @@
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_CLEANUP_ACCESSIBILITY_WARNING_DIALOG)
public void toggleShortcutPreference_warningNotRequired_dontShowWarning() throws Throwable {
setupServiceWarningRequired(false);
mFragment.mShortcutPreference = new ShortcutPreference(mContext, /* attrs= */null);
@@ -291,7 +288,6 @@
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_CLEANUP_ACCESSIBILITY_WARNING_DIALOG)
public void clickShortcutSettingsPreference_warningRequired_showWarning() throws Throwable {
setupServiceWarningRequired(true);
mFragment.mShortcutPreference = new ShortcutPreference(mContext, /* attrs= */null);
@@ -303,8 +299,7 @@
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_CLEANUP_ACCESSIBILITY_WARNING_DIALOG)
- @RequiresFlagsDisabled(
+ @DisableFlags(
com.android.settings.accessibility.Flags.FLAG_EDIT_SHORTCUTS_IN_FULL_SCREEN)
public void clickShortcutSettingsPreference_warningNotRequired_dontShowWarning_showDialog()
throws Throwable {
@@ -318,8 +313,7 @@
}
@Test
- @RequiresFlagsEnabled({Flags.FLAG_CLEANUP_ACCESSIBILITY_WARNING_DIALOG,
- com.android.settings.accessibility.Flags.FLAG_EDIT_SHORTCUTS_IN_FULL_SCREEN})
+ @EnableFlags(com.android.settings.accessibility.Flags.FLAG_EDIT_SHORTCUTS_IN_FULL_SCREEN)
public void clickShortcutSettingsPreference_warningNotRequired_dontShowWarning_launchActivity()
throws Throwable {
setupServiceWarningRequired(false);
@@ -334,6 +328,153 @@
.isEqualTo(EditShortcutsPreferenceFragment.class.getName());
}
+ @Test
+ public void getDefaultShortcutTypes_noAssociatedTile_softwareTypeIsDefault() {
+ PreferredShortcuts.clearPreferredShortcuts(mContext);
+ when(mFragment.getTileComponentName()).thenReturn(null);
+
+ assertThat(mFragment.getDefaultShortcutTypes())
+ .isEqualTo(ShortcutConstants.UserShortcutType.SOFTWARE);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
+ public void getDefaultShortcutTypes_hasAssociatedTile_qsTypeIsDefault() {
+ PreferredShortcuts.clearPreferredShortcuts(mContext);
+ when(mFragment.getTileComponentName()).thenReturn(PLACEHOLDER_TILE_COMPONENT_NAME);
+
+ assertThat(mFragment.getDefaultShortcutTypes())
+ .isEqualTo(ShortcutConstants.UserShortcutType.QUICK_SETTINGS);
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
+ public void getDefaultShortcutTypes_hasAssociatedTile_softwareTypeIsDefault() {
+ PreferredShortcuts.clearPreferredShortcuts(mContext);
+ when(mFragment.getTileComponentName()).thenReturn(PLACEHOLDER_TILE_COMPONENT_NAME);
+
+ assertThat(mFragment.getDefaultShortcutTypes())
+ .isEqualTo(ShortcutConstants.UserShortcutType.SOFTWARE);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
+ public void toggleShortcutPreference_noUserPreferredShortcut_hasQsTile_enableQsShortcut()
+ throws Throwable {
+ PreferredShortcuts.clearPreferredShortcuts(mContext);
+ setupServiceWarningRequired(false);
+ when(mFragment.getTileComponentName()).thenReturn(PLACEHOLDER_TILE_COMPONENT_NAME);
+ mFragment.mShortcutPreference = new ShortcutPreference(mContext, /* attrs= */ null);
+
+ mFragment.mShortcutPreference.setChecked(true);
+ mFragment.onToggleClicked(mFragment.mShortcutPreference);
+
+ verify(mMockAccessibilityManager)
+ .enableShortcutsForTargets(true,
+ ShortcutConstants.UserShortcutType.QUICK_SETTINGS,
+ Set.of(mFragment.mComponentName.flattenToString()), mContext.getUserId());
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
+ public void toggleShortcutPreference_noUserPreferredShortcut_noQsTile_enableSoftwareShortcut()
+ throws Throwable {
+ PreferredShortcuts.clearPreferredShortcuts(mContext);
+ setupServiceWarningRequired(false);
+ when(mFragment.getTileComponentName()).thenReturn(null);
+ mFragment.mShortcutPreference = new ShortcutPreference(mContext, /* attrs= */ null);
+
+ mFragment.mShortcutPreference.setChecked(true);
+ mFragment.onToggleClicked(mFragment.mShortcutPreference);
+
+ verify(mMockAccessibilityManager)
+ .enableShortcutsForTargets(true,
+ ShortcutConstants.UserShortcutType.SOFTWARE,
+ Set.of(mFragment.mComponentName.flattenToString()), mContext.getUserId());
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
+ public void toggleShortcutPreference_noUserPreferredShortcut_hasQsTile_flagOff_enableSoftwareShortcut()
+ throws Throwable {
+ PreferredShortcuts.clearPreferredShortcuts(mContext);
+ setupServiceWarningRequired(false);
+ when(mFragment.getTileComponentName()).thenReturn(PLACEHOLDER_TILE_COMPONENT_NAME);
+ mFragment.mShortcutPreference = new ShortcutPreference(mContext, /* attrs= */ null);
+
+ mFragment.mShortcutPreference.setChecked(true);
+ mFragment.onToggleClicked(mFragment.mShortcutPreference);
+
+ assertThat(
+ Settings.Secure.getString(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS))
+ .contains(mFragment.mComponentName.flattenToString());
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
+ public void toggleShortcutPreference_noUserPreferredShortcut_noQsTile_flagOff_enableSoftwareShortcut()
+ throws Throwable {
+ PreferredShortcuts.clearPreferredShortcuts(mContext);
+ setupServiceWarningRequired(false);
+ when(mFragment.getTileComponentName()).thenReturn(null);
+ mFragment.mShortcutPreference = new ShortcutPreference(mContext, /* attrs= */ null);
+
+ mFragment.mShortcutPreference.setChecked(true);
+ mFragment.onToggleClicked(mFragment.mShortcutPreference);
+
+ assertThat(
+ Settings.Secure.getString(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS))
+ .contains(mFragment.mComponentName.flattenToString());
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
+ public void toggleShortcutPreference_userPreferVolumeKeysShortcut_noQsTile_enableVolumeKeysShortcut()
+ throws Throwable {
+ setupServiceWarningRequired(false);
+ String componentName = mFragment.mComponentName.flattenToString();
+ PreferredShortcuts.saveUserShortcutType(
+ mContext,
+ new PreferredShortcut(componentName, ShortcutConstants.UserShortcutType.HARDWARE));
+ when(mFragment.getTileComponentName()).thenReturn(null);
+ mFragment.mShortcutPreference = new ShortcutPreference(mContext, /* attrs= */ null);
+
+ mFragment.mShortcutPreference.setChecked(true);
+ mFragment.onToggleClicked(mFragment.mShortcutPreference);
+
+ verify(mMockAccessibilityManager)
+ .enableShortcutsForTargets(
+ true,
+ ShortcutConstants.UserShortcutType.HARDWARE,
+ Set.of(componentName),
+ mContext.getUserId());
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
+ public void toggleShortcutPreference_userPreferVolumeKeysShortcut_hasQsTile_enableVolumeKeysShortcut()
+ throws Throwable {
+ setupServiceWarningRequired(false);
+ String componentName = mFragment.mComponentName.flattenToString();
+ PreferredShortcuts.saveUserShortcutType(
+ mContext,
+ new PreferredShortcut(componentName, ShortcutConstants.UserShortcutType.HARDWARE));
+ when(mFragment.getTileComponentName()).thenReturn(PLACEHOLDER_TILE_COMPONENT_NAME);
+ mFragment.mShortcutPreference = new ShortcutPreference(mContext, /* attrs= */ null);
+
+ mFragment.mShortcutPreference.setChecked(true);
+ mFragment.onToggleClicked(mFragment.mShortcutPreference);
+
+ verify(mMockAccessibilityManager)
+ .enableShortcutsForTargets(
+ true,
+ ShortcutConstants.UserShortcutType.HARDWARE,
+ Set.of(componentName),
+ mContext.getUserId());
+ }
+
private void setupTileService(String packageName, String name, String tileName) {
final Intent tileProbe = new Intent(TileService.ACTION_QS_TILE);
final ResolveInfo info = new ResolveInfo();
diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsSpatialAudioControllerTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsSpatialAudioControllerTest.java
index 2cc55a7..d9a917b 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsSpatialAudioControllerTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsSpatialAudioControllerTest.java
@@ -25,6 +25,7 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.settings.SettingsEnums;
import android.bluetooth.BluetoothDevice;
import android.media.AudioDeviceAttributes;
import android.media.AudioDeviceInfo;
@@ -34,8 +35,11 @@
import androidx.preference.PreferenceCategory;
import androidx.preference.TwoStatePreference;
+import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.google.common.collect.ImmutableList;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -55,36 +59,37 @@
private static final String KEY_SPATIAL_AUDIO = "spatial_audio";
private static final String KEY_HEAD_TRACKING = "head_tracking";
- @Mock
- private AudioManager mAudioManager;
- @Mock
- private Spatializer mSpatializer;
- @Mock
- private Lifecycle mSpatialAudioLifecycle;
- @Mock
- private PreferenceCategory mProfilesContainer;
- @Mock
- private BluetoothDevice mBluetoothDevice;
+ @Mock private AudioManager mAudioManager;
+ @Mock private Spatializer mSpatializer;
+ @Mock private Lifecycle mSpatialAudioLifecycle;
+ @Mock private PreferenceCategory mProfilesContainer;
+ @Mock private BluetoothDevice mBluetoothDevice;
private AudioDeviceAttributes mAvailableDevice;
private BluetoothDetailsSpatialAudioController mController;
private TwoStatePreference mSpatialAudioPref;
private TwoStatePreference mHeadTrackingPref;
+ private FakeFeatureFactory mFeatureFactory;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application);
+ mFeatureFactory = FakeFeatureFactory.setupForTest();
+
when(mContext.getSystemService(AudioManager.class)).thenReturn(mAudioManager);
when(mAudioManager.getSpatializer()).thenReturn(mSpatializer);
when(mCachedDevice.getAddress()).thenReturn(MAC_ADDRESS);
when(mCachedDevice.getDevice()).thenReturn(mBluetoothDevice);
when(mBluetoothDevice.getAnonymizedAddress()).thenReturn(MAC_ADDRESS);
+ when(mFeatureFactory.getBluetoothFeatureProvider().getSpatializer(mContext))
+ .thenReturn(mSpatializer);
- mController = new BluetoothDetailsSpatialAudioController(mContext, mFragment,
- mCachedDevice, mSpatialAudioLifecycle);
+ mController =
+ new BluetoothDetailsSpatialAudioController(
+ mContext, mFragment, mCachedDevice, mSpatialAudioLifecycle);
mController.mProfilesContainer = mProfilesContainer;
mSpatialAudioPref = mController.createSpatialAudioPreference(mContext);
@@ -93,10 +98,11 @@
when(mProfilesContainer.findPreference(KEY_SPATIAL_AUDIO)).thenReturn(mSpatialAudioPref);
when(mProfilesContainer.findPreference(KEY_HEAD_TRACKING)).thenReturn(mHeadTrackingPref);
- mAvailableDevice = new AudioDeviceAttributes(
- AudioDeviceAttributes.ROLE_OUTPUT,
- AudioDeviceInfo.TYPE_BLUETOOTH_A2DP,
- MAC_ADDRESS);
+ mAvailableDevice =
+ new AudioDeviceAttributes(
+ AudioDeviceAttributes.ROLE_OUTPUT,
+ AudioDeviceInfo.TYPE_BLUETOOTH_A2DP,
+ MAC_ADDRESS);
}
@Test
@@ -107,8 +113,8 @@
@Test
public void isAvailable_forSpatializerWithLevelNotNone_returnsTrue() {
- when(mSpatializer.getImmersiveAudioLevel()).thenReturn(
- SPATIALIZER_IMMERSIVE_LEVEL_MULTICHANNEL);
+ when(mSpatializer.getImmersiveAudioLevel())
+ .thenReturn(SPATIALIZER_IMMERSIVE_LEVEL_MULTICHANNEL);
assertThat(mController.isAvailable()).isTrue();
}
@@ -151,29 +157,77 @@
}
@Test
- public void
- refresh_spatialAudioOnAndHeadTrackingIsNotAvailable_hidesHeadTrackingPreference() {
- List<AudioDeviceAttributes> compatibleAudioDevices = new ArrayList<>();
+ public void refresh_spatialAudioOnHeadTrackingOff_recordMetrics() {
mController.setAvailableDevice(mAvailableDevice);
- compatibleAudioDevices.add(mController.mAudioDevice);
- when(mSpatializer.getCompatibleAudioDevices()).thenReturn(compatibleAudioDevices);
- when(mSpatializer.hasHeadTracker(mController.mAudioDevice)).thenReturn(false);
+ when(mSpatializer.isAvailableForDevice(mAvailableDevice)).thenReturn(true);
+ when(mSpatializer.getCompatibleAudioDevices())
+ .thenReturn(ImmutableList.of(mAvailableDevice));
+ when(mSpatializer.hasHeadTracker(mAvailableDevice)).thenReturn(true);
+ when(mSpatializer.isHeadTrackerEnabled(mController.mAudioDevice)).thenReturn(false);
mController.refresh();
ShadowLooper.idleMainLooper();
- verify(mProfilesContainer).removePreference(mHeadTrackingPref);
+ verify(mFeatureFactory.metricsFeatureProvider)
+ .action(
+ mContext,
+ SettingsEnums.ACTION_BLUETOOTH_DEVICE_DETAILS_SPATIAL_AUDIO_TRIGGERED,
+ true);
+ verify(mFeatureFactory.metricsFeatureProvider)
+ .action(
+ mContext,
+ SettingsEnums.ACTION_BLUETOOTH_DEVICE_DETAILS_HEAD_TRACKING_TRIGGERED,
+ false);
+ }
+
+ @Test
+ public void refresh_spatialAudioOff_recordMetrics() {
+ mController.setAvailableDevice(mAvailableDevice);
+ when(mSpatializer.isAvailableForDevice(mAvailableDevice)).thenReturn(true);
+ when(mSpatializer.getCompatibleAudioDevices()).thenReturn(ImmutableList.of());
+ when(mSpatializer.hasHeadTracker(mAvailableDevice)).thenReturn(true);
+ when(mSpatializer.isHeadTrackerEnabled(mController.mAudioDevice)).thenReturn(false);
+
+ mController.refresh();
+ ShadowLooper.idleMainLooper();
+
+ verify(mFeatureFactory.metricsFeatureProvider)
+ .action(
+ mContext,
+ SettingsEnums.ACTION_BLUETOOTH_DEVICE_DETAILS_SPATIAL_AUDIO_TRIGGERED,
+ false);
+ verify(mFeatureFactory.metricsFeatureProvider)
+ .action(
+ mContext,
+ SettingsEnums.ACTION_BLUETOOTH_DEVICE_DETAILS_HEAD_TRACKING_TRIGGERED,
+ false);
+ }
+
+ @Test
+ public void refresh_spatialAudioOnAndHeadTrackingIsNotAvailable_hidesHeadTrackingPreference() {
+ mController.setAvailableDevice(mAvailableDevice);
+ when(mSpatializer.isAvailableForDevice(mAvailableDevice)).thenReturn(true);
+ when(mSpatializer.getCompatibleAudioDevices())
+ .thenReturn(ImmutableList.of(mAvailableDevice));
+ when(mSpatializer.hasHeadTracker(mAvailableDevice)).thenReturn(false);
+
+ mController.refresh();
+ ShadowLooper.idleMainLooper();
+
+ assertThat(mHeadTrackingPref.isVisible()).isFalse();
}
@Test
public void refresh_spatialAudioOff_hidesHeadTrackingPreference() {
- List<AudioDeviceAttributes> compatibleAudioDevices = new ArrayList<>();
- when(mSpatializer.getCompatibleAudioDevices()).thenReturn(compatibleAudioDevices);
+ mController.setAvailableDevice(mAvailableDevice);
+ when(mSpatializer.isAvailableForDevice(mAvailableDevice)).thenReturn(true);
+ when(mSpatializer.getCompatibleAudioDevices()).thenReturn(ImmutableList.of());
+ when(mSpatializer.hasHeadTracker(mAvailableDevice)).thenReturn(true);
mController.refresh();
ShadowLooper.idleMainLooper();
- verify(mProfilesContainer).removePreference(mHeadTrackingPref);
+ assertThat(mHeadTrackingPref.isVisible()).isFalse();
}
@Test
@@ -190,6 +244,11 @@
ShadowLooper.idleMainLooper();
assertThat(mHeadTrackingPref.isChecked()).isTrue();
+ verify(mFeatureFactory.metricsFeatureProvider)
+ .action(
+ mContext,
+ SettingsEnums.ACTION_BLUETOOTH_DEVICE_DETAILS_HEAD_TRACKING_TRIGGERED,
+ true);
}
@Test
@@ -206,6 +265,11 @@
ShadowLooper.idleMainLooper();
assertThat(mHeadTrackingPref.isChecked()).isFalse();
+ verify(mFeatureFactory.metricsFeatureProvider)
+ .action(
+ mContext,
+ SettingsEnums.ACTION_BLUETOOTH_DEVICE_DETAILS_HEAD_TRACKING_TRIGGERED,
+ false);
}
@Test
@@ -214,6 +278,11 @@
mSpatialAudioPref.setChecked(true);
mController.onPreferenceClick(mSpatialAudioPref);
verify(mSpatializer).addCompatibleAudioDevice(mController.mAudioDevice);
+ verify(mFeatureFactory.metricsFeatureProvider)
+ .action(
+ mContext,
+ SettingsEnums.ACTION_BLUETOOTH_DEVICE_DETAILS_SPATIAL_AUDIO_TOGGLE_CLICKED,
+ true);
}
@Test
@@ -222,6 +291,11 @@
mSpatialAudioPref.setChecked(false);
mController.onPreferenceClick(mSpatialAudioPref);
verify(mSpatializer).removeCompatibleAudioDevice(mController.mAudioDevice);
+ verify(mFeatureFactory.metricsFeatureProvider)
+ .action(
+ mContext,
+ SettingsEnums.ACTION_BLUETOOTH_DEVICE_DETAILS_SPATIAL_AUDIO_TOGGLE_CLICKED,
+ false);
}
@Test
@@ -230,6 +304,11 @@
mHeadTrackingPref.setChecked(true);
mController.onPreferenceClick(mHeadTrackingPref);
verify(mSpatializer).setHeadTrackerEnabled(true, mController.mAudioDevice);
+ verify(mFeatureFactory.metricsFeatureProvider)
+ .action(
+ mContext,
+ SettingsEnums.ACTION_BLUETOOTH_DEVICE_DETAILS_HEAD_TRACKING_TOGGLE_CLICKED,
+ true);
}
@Test
@@ -238,5 +317,10 @@
mHeadTrackingPref.setChecked(false);
mController.onPreferenceClick(mHeadTrackingPref);
verify(mSpatializer).setHeadTrackerEnabled(false, mController.mAudioDevice);
+ verify(mFeatureFactory.metricsFeatureProvider)
+ .action(
+ mContext,
+ SettingsEnums.ACTION_BLUETOOTH_DEVICE_DETAILS_HEAD_TRACKING_TOGGLE_CLICKED,
+ false);
}
}
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/PreviouslyConnectedDevicePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/PreviouslyConnectedDevicePreferenceControllerTest.java
index f1cea6d..23649f3 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/PreviouslyConnectedDevicePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/PreviouslyConnectedDevicePreferenceControllerTest.java
@@ -53,7 +53,6 @@
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -133,6 +132,7 @@
doReturn(mPackageManager).when(mContext).getPackageManager();
when(mContext.getSystemService(BluetoothManager.class)).thenReturn(mBluetoothManager);
when(mBluetoothManager.getAdapter()).thenReturn(mBluetoothAdapter);
+ when(mBluetoothAdapter.isEnabled()).thenReturn(true);
mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
when(mCachedDevice1.getDevice()).thenReturn(mBluetoothDevice1);
@@ -223,7 +223,6 @@
AVAILABLE);
}
- @Ignore("b/322712259")
@Test
public void onDeviceAdded_addDevicePreference_displayIt() {
final BluetoothDevicePreference preference1 = new BluetoothDevicePreference(
@@ -234,7 +233,6 @@
assertThat(mPreferenceGroup.getPreferenceCount()).isEqualTo(2);
}
- @Ignore("b/322712259")
@Test
public void onDeviceAdded_addDockDevicePreference_displayIt() {
final SingleTargetGearPreference dockPreference = new SingleTargetGearPreference(
@@ -245,7 +243,6 @@
assertThat(mPreferenceGroup.getPreferenceCount()).isEqualTo(2);
}
- @Ignore("b/322712259")
@Test
public void onDeviceAdded_addFourDevicePreference_onlyDisplayThree() {
final BluetoothDevicePreference preference1 = new BluetoothDevicePreference(
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/stylus/StylusDevicesControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/stylus/StylusDevicesControllerTest.java
index 6c96f85..c1cbf17 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/stylus/StylusDevicesControllerTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/stylus/StylusDevicesControllerTest.java
@@ -41,9 +41,12 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
+import android.content.pm.UserProperties;
+import android.graphics.drawable.Drawable;
import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import android.provider.Settings.Secure;
import android.view.InputDevice;
@@ -64,6 +67,7 @@
import com.android.settingslib.core.lifecycle.Lifecycle;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -77,8 +81,12 @@
@RunWith(RobolectricTestRunner.class)
public class StylusDevicesControllerTest {
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
private static final String NOTES_PACKAGE_NAME = "notes.package";
private static final CharSequence NOTES_APP_LABEL = "App Label";
+ private static final int WORK_USER_ID = 1;
+ private static final int PRIVATE_USER_ID = 2;
private Context mContext;
private StylusDevicesController mController;
@@ -95,6 +103,12 @@
@Mock
private UserManager mUserManager;
@Mock
+ UserInfo mPersonalUserInfo;
+ @Mock
+ UserInfo mWorkUserInfo;
+ @Mock
+ UserInfo mPrivateUserInfo;
+ @Mock
private RoleManager mRm;
@Mock
private Lifecycle mLifecycle;
@@ -102,6 +116,8 @@
private CachedBluetoothDevice mCachedBluetoothDevice;
@Mock
private BluetoothDevice mBluetoothDevice;
+ @Mock
+ private Drawable mIcon;
@Before
public void setUp() throws Exception {
@@ -134,6 +150,7 @@
when(mPm.getApplicationInfo(eq(NOTES_PACKAGE_NAME),
any(PackageManager.ApplicationInfoFlags.class))).thenReturn(new ApplicationInfo());
when(mPm.getApplicationLabel(any(ApplicationInfo.class))).thenReturn(NOTES_APP_LABEL);
+ when(mPm.getUserBadgeForDensityNoBackground(any(), anyInt())).thenReturn(mIcon);
when(mUserManager.getUsers()).thenReturn(Arrays.asList(new UserInfo(0, "default", 0)));
when(mUserManager.isManagedProfile(anyInt())).thenReturn(false);
@@ -371,14 +388,26 @@
final String permissionPackageName = "permissions.package";
final UserHandle currentUser = Process.myUserHandle();
List<UserInfo> userInfos = Arrays.asList(
- new UserInfo(currentUser.getIdentifier(), "current", 0),
- new UserInfo(1, "profile", UserInfo.FLAG_PROFILE)
+ mPersonalUserInfo,
+ mWorkUserInfo
);
- when(mUserManager.getUsers()).thenReturn(userInfos);
- when(mUserManager.isManagedProfile(1)).thenReturn(true);
- when(mUserManager.getUserInfo(currentUser.getIdentifier())).thenReturn(userInfos.get(0));
- when(mUserManager.getUserInfo(1)).thenReturn(userInfos.get(1));
- when(mUserManager.getProfileParent(1)).thenReturn(userInfos.get(0));
+ UserProperties personalUserProperties =
+ new UserProperties.Builder()
+ .setShowInQuietMode(UserProperties.SHOW_IN_QUIET_MODE_DEFAULT)
+ .build();
+ UserProperties workUserProperties =
+ new UserProperties.Builder()
+ .setShowInQuietMode(UserProperties.SHOW_IN_QUIET_MODE_PAUSED)
+ .build();
+ when(mWorkUserInfo.isManagedProfile()).thenReturn(true);
+ when(mWorkUserInfo.getUserHandle()).thenReturn(UserHandle.of(WORK_USER_ID));
+ when(mUserManager.getProfiles(currentUser.getIdentifier())).thenReturn(userInfos);
+ when(mUserManager.getUserInfo(currentUser.getIdentifier())).thenReturn(mPersonalUserInfo);
+ when(mUserManager.getUserInfo(WORK_USER_ID)).thenReturn(mWorkUserInfo);
+ when(mUserManager.getProfileParent(WORK_USER_ID)).thenReturn(mPersonalUserInfo);
+ when(mUserManager.getUserProperties(currentUser)).thenReturn(personalUserProperties);
+ when(mUserManager.getUserProperties(UserHandle.of(WORK_USER_ID)))
+ .thenReturn(workUserProperties);
when(mPm.getPermissionControllerPackageName()).thenReturn(permissionPackageName);
showScreen(mController);
@@ -389,7 +418,55 @@
}
@Test
- public void defaultNotesPreferenceClick_noManagedProfile_sendsManageDefaultRoleIntent() {
+ public void defaultNotesPreferenceClick_multiUsers_showsProfileSelectorDialog() {
+ mSetFlagsRule.enableFlags(
+ android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE,
+ android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES,
+ android.multiuser.Flags.FLAG_HANDLE_INTERLEAVED_SETTINGS_FOR_PRIVATE_SPACE);
+ mContext.setTheme(androidx.appcompat.R.style.Theme_AppCompat);
+ final String permissionPackageName = "permissions.package";
+ final UserHandle currentUser = Process.myUserHandle();
+ List<UserInfo> userInfos = Arrays.asList(
+ mPersonalUserInfo,
+ mPrivateUserInfo,
+ mWorkUserInfo
+ );
+ UserProperties personalUserProperties =
+ new UserProperties.Builder()
+ .setShowInQuietMode(UserProperties.SHOW_IN_QUIET_MODE_DEFAULT)
+ .build();
+ UserProperties workUserProperties =
+ new UserProperties.Builder()
+ .setShowInQuietMode(UserProperties.SHOW_IN_QUIET_MODE_PAUSED)
+ .build();
+ UserProperties privateUserProperties =
+ new UserProperties.Builder()
+ .setShowInQuietMode(UserProperties.SHOW_IN_QUIET_MODE_HIDDEN)
+ .build();
+ when(mWorkUserInfo.isManagedProfile()).thenReturn(true);
+ when(mWorkUserInfo.getUserHandle()).thenReturn(UserHandle.of(WORK_USER_ID));
+ when(mPrivateUserInfo.isPrivateProfile()).thenReturn(true);
+ when(mPrivateUserInfo.getUserHandle()).thenReturn(UserHandle.of(PRIVATE_USER_ID));
+ when(mUserManager.getProfiles(currentUser.getIdentifier())).thenReturn(userInfos);
+ when(mUserManager.getUserInfo(currentUser.getIdentifier())).thenReturn(mPersonalUserInfo);
+ when(mUserManager.getUserInfo(WORK_USER_ID)).thenReturn(mWorkUserInfo);
+ when(mUserManager.getUserInfo(PRIVATE_USER_ID)).thenReturn(mPrivateUserInfo);
+ when(mUserManager.getUserProperties(currentUser)).thenReturn(personalUserProperties);
+ when(mUserManager.getUserProperties(UserHandle.of(PRIVATE_USER_ID)))
+ .thenReturn(privateUserProperties);
+ when(mUserManager.getUserProperties(UserHandle.of(WORK_USER_ID)))
+ .thenReturn(workUserProperties);
+ when(mPm.getPermissionControllerPackageName()).thenReturn(permissionPackageName);
+
+ showScreen(mController);
+ Preference defaultNotesPref = mPreferenceContainer.getPreference(0);
+ mController.onPreferenceClick(defaultNotesPref);
+
+ assertTrue(mController.mDialog.isShowing());
+ }
+
+ @Test
+ public void defaultNotesPreferenceClick_noProfiles_sendsManageDefaultRoleIntent() {
final ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
mContext.setTheme(androidx.appcompat.R.style.Theme_AppCompat);
final String permissionPackageName = "permissions.package";
@@ -398,7 +475,7 @@
new UserInfo(currentUser.getIdentifier(), "current", 0),
new UserInfo(1, "other", UserInfo.FLAG_FULL)
);
- when(mUserManager.getUsers()).thenReturn(userInfos);
+ when(mUserManager.getProfiles(currentUser.getIdentifier())).thenReturn(userInfos);
when(mUserManager.isManagedProfile(1)).thenReturn(false);
when(mUserManager.getUserInfo(currentUser.getIdentifier())).thenReturn(userInfos.get(0));
when(mUserManager.getUserInfo(1)).thenReturn(userInfos.get(1));
diff --git a/tests/robotests/src/com/android/settings/core/PreferenceControllerListHelperTest.java b/tests/robotests/src/com/android/settings/core/PreferenceControllerListHelperTest.java
index 68dfb79..0cc3857 100644
--- a/tests/robotests/src/com/android/settings/core/PreferenceControllerListHelperTest.java
+++ b/tests/robotests/src/com/android/settings/core/PreferenceControllerListHelperTest.java
@@ -20,6 +20,8 @@
import android.content.Context;
+import androidx.preference.PreferenceManager;
+
import com.android.settings.R;
import com.android.settings.slices.FakePreferenceController;
import com.android.settingslib.core.AbstractPreferenceController;
@@ -38,10 +40,12 @@
public class PreferenceControllerListHelperTest {
private Context mContext;
+ private PreferenceManager mPreferenceManager;
@Before
public void setUp() {
mContext = RuntimeEnvironment.application;
+ mPreferenceManager = new PreferenceManager(mContext);
}
@Test
@@ -69,6 +73,30 @@
}
@Test
+ @Config(qualifiers = "mcc999")
+ public void areAllPreferencesUnavailable_allAvailable() {
+ // All preferences have controllers indicating they are available.
+ assertThat(PreferenceControllerListHelper.areAllPreferencesUnavailable(mContext,
+ mPreferenceManager, R.xml.location_settings)).isFalse();
+ }
+
+ @Test
+ @Config(qualifiers = "mcc997")
+ public void areAllPreferencesUnavailable_allUnavailable() {
+ // All preferences have controllers indicating they are unavailable. (note the qualifier)
+ assertThat(PreferenceControllerListHelper.areAllPreferencesUnavailable(mContext,
+ mPreferenceManager, R.xml.location_settings)).isTrue();
+ }
+
+ @Test
+ @Config(qualifiers = "mcc999")
+ public void areAllPreferencesUnavailable_noControllersShouldAssumeAvailable() {
+ // None of the preferences have controllers, so they are assumed available.
+ assertThat(PreferenceControllerListHelper.areAllPreferencesUnavailable(mContext,
+ mPreferenceManager, R.xml.display_settings)).isFalse();
+ }
+
+ @Test
public void filterControllers_noFilter_shouldReturnSameList() {
final List<BasePreferenceController> controllers = new ArrayList<>();
controllers.add(new BasePreferenceController(mContext, "key") {
diff --git a/tests/robotests/src/com/android/settings/core/UnavailablePreferenceController.java b/tests/robotests/src/com/android/settings/core/UnavailablePreferenceController.java
new file mode 100644
index 0000000..bf6e7e2
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/core/UnavailablePreferenceController.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.core;
+
+import android.content.Context;
+
+public class UnavailablePreferenceController extends BasePreferenceController {
+
+ public UnavailablePreferenceController(Context context, String preferenceKey) {
+ super(context, preferenceKey);
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return UNSUPPORTED_ON_DEVICE;
+ }
+
+}
diff --git a/tests/robotests/src/com/android/settings/dashboard/profileselector/UserAdapterTest.java b/tests/robotests/src/com/android/settings/dashboard/profileselector/UserAdapterTest.java
index 2fb5e03..b6ac410 100644
--- a/tests/robotests/src/com/android/settings/dashboard/profileselector/UserAdapterTest.java
+++ b/tests/robotests/src/com/android/settings/dashboard/profileselector/UserAdapterTest.java
@@ -24,8 +24,10 @@
import android.content.Context;
import android.content.pm.UserInfo;
+import android.content.pm.UserProperties;
import android.os.UserHandle;
import android.os.UserManager;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.widget.FrameLayout;
import android.widget.TextView;
@@ -52,9 +54,12 @@
public class UserAdapterTest {
@Rule
public MockitoRule mRule = MockitoJUnit.rule();
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
private final int mPersonalUserId = UserHandle.myUserId();
private static final int WORK_USER_ID = 1;
+ private static final int PRIVATE_USER_ID = 2;
@Mock
private UserManager mUserManager;
@@ -64,6 +69,8 @@
@Mock
private UserInfo mWorkUserInfo;
+ @Mock
+ private UserInfo mPrivateUserInfo;
@Mock
private UserAdapter.OnClickListener mOnClickListener;
@@ -71,11 +78,31 @@
@Spy
private Context mContext = ApplicationProvider.getApplicationContext();
+ private UserProperties mPersonalUserProperties =
+ new UserProperties.Builder()
+ .setShowInQuietMode(UserProperties.SHOW_IN_QUIET_MODE_DEFAULT)
+ .build();
+ private UserProperties mWorkUserProperties =
+ new UserProperties.Builder()
+ .setShowInQuietMode(UserProperties.SHOW_IN_QUIET_MODE_PAUSED)
+ .build();
+ private UserProperties mPrivateUserProperties =
+ new UserProperties.Builder()
+ .setShowInQuietMode(UserProperties.SHOW_IN_QUIET_MODE_HIDDEN)
+ .build();
+
@Before
public void setUp() {
when(mContext.getSystemService(UserManager.class)).thenReturn(mUserManager);
when(mUserManager.getUserInfo(mPersonalUserId)).thenReturn(mPersonalUserInfo);
when(mUserManager.getUserInfo(WORK_USER_ID)).thenReturn(mWorkUserInfo);
+ when(mUserManager.getUserInfo(PRIVATE_USER_ID)).thenReturn(mPrivateUserInfo);
+ when(mUserManager.getUserProperties(UserHandle.of(mPersonalUserId)))
+ .thenReturn(mPersonalUserProperties);
+ when(mUserManager.getUserProperties(UserHandle.of(WORK_USER_ID)))
+ .thenReturn(mWorkUserProperties);
+ when(mUserManager.getUserProperties(UserHandle.of(PRIVATE_USER_ID)))
+ .thenReturn(mPrivateUserProperties);
}
@Test
@@ -103,6 +130,48 @@
}
@Test
+ public void createUserSpinnerAdapter_withWorkAndPrivateProfiles_shouldSucceed() {
+ mSetFlagsRule.enableFlags(
+ android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE,
+ android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES,
+ android.multiuser.Flags.FLAG_HANDLE_INTERLEAVED_SETTINGS_FOR_PRIVATE_SPACE);
+ when(mUserManager.getUserProfiles()).thenReturn(
+ Lists.newArrayList(
+ UserHandle.of(mPersonalUserId),
+ UserHandle.of(WORK_USER_ID),
+ UserHandle.of(PRIVATE_USER_ID)));
+
+ UserAdapter userSpinnerAdapter =
+ UserAdapter.createUserSpinnerAdapter(mUserManager, mContext);
+
+ assertThat(userSpinnerAdapter.getCount()).isEqualTo(3);
+ assertThat(userSpinnerAdapter.getUserHandle(0).getIdentifier()).isEqualTo(mPersonalUserId);
+ assertThat(userSpinnerAdapter.getUserHandle(1).getIdentifier()).isEqualTo(WORK_USER_ID);
+ assertThat(userSpinnerAdapter.getUserHandle(2).getIdentifier()).isEqualTo(PRIVATE_USER_ID);
+ }
+
+ @Test
+ public void createUserSpinnerAdapter_withWorkAndQuietPrivateProfile_shouldShowTwoProfiles() {
+ mSetFlagsRule.enableFlags(
+ android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE,
+ android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES,
+ android.multiuser.Flags.FLAG_HANDLE_INTERLEAVED_SETTINGS_FOR_PRIVATE_SPACE);
+ when(mUserManager.getUserProfiles()).thenReturn(
+ Lists.newArrayList(
+ UserHandle.of(mPersonalUserId),
+ UserHandle.of(WORK_USER_ID),
+ UserHandle.of(PRIVATE_USER_ID)));
+ when(mUserManager.isQuietModeEnabled(UserHandle.of(PRIVATE_USER_ID))).thenReturn(true);
+
+ UserAdapter userSpinnerAdapter =
+ UserAdapter.createUserSpinnerAdapter(mUserManager, mContext);
+
+ assertThat(userSpinnerAdapter.getCount()).isEqualTo(2);
+ assertThat(userSpinnerAdapter.getUserHandle(0).getIdentifier()).isEqualTo(mPersonalUserId);
+ assertThat(userSpinnerAdapter.getUserHandle(1).getIdentifier()).isEqualTo(WORK_USER_ID);
+ }
+
+ @Test
public void createUserRecycleViewAdapter_canBindViewHolderCorrectly() {
ArrayList<UserHandle> userHandles =
Lists.newArrayList(UserHandle.of(mPersonalUserId), UserHandle.of(WORK_USER_ID));
diff --git a/tests/robotests/src/com/android/settings/localepicker/AppLocalePickerActivityTest.java b/tests/robotests/src/com/android/settings/localepicker/AppLocalePickerActivityTest.java
index 2989324..58ce1b2 100644
--- a/tests/robotests/src/com/android/settings/localepicker/AppLocalePickerActivityTest.java
+++ b/tests/robotests/src/com/android/settings/localepicker/AppLocalePickerActivityTest.java
@@ -58,7 +58,6 @@
import org.junit.After;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -203,7 +202,6 @@
assertThat(controller.get().isFinishing()).isTrue();
}
- @Ignore("b/313604701")
@Test
public void onLocaleSelected_getLocaleNotNull_getLanguageTag() {
ActivityController<TestAppLocalePickerActivity> controller =
@@ -216,7 +214,10 @@
AppLocalePickerActivity mActivity = controller.get();
mActivity.onLocaleSelected(mLocaleInfo);
- verify(mLocaleInfo, times(2)).getLocale();
+ // 1st for getLocale()!= null
+ // 2nd for setAppDefaultLocale(getLocale())
+ // 3rd for broadcastAppLocaleChange()
+ verify(mLocaleInfo, times(3)).getLocale();
assertThat(mLocaleInfo.getLocale().toLanguageTag()).isEqualTo("en-US");
assertThat(controller.get().isFinishing()).isTrue();
}
diff --git a/tests/spa_unit/src/com/android/settings/network/apn/ApnNetworkTypesTest.kt b/tests/spa_unit/src/com/android/settings/network/apn/ApnNetworkTypesTest.kt
new file mode 100644
index 0000000..f8aed59
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/network/apn/ApnNetworkTypesTest.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.network.apn
+
+import android.telephony.TelephonyManager.NETWORK_TYPE_BITMASK_CDMA
+import android.telephony.TelephonyManager.NETWORK_TYPE_BITMASK_EDGE
+import android.telephony.TelephonyManager.NETWORK_TYPE_BITMASK_HSPAP
+import android.telephony.TelephonyManager.NETWORK_TYPE_BITMASK_HSUPA
+import androidx.compose.runtime.mutableStateOf
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settingslib.spa.widget.editor.SettingsDropdownCheckOption
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class ApnNetworkTypesTest {
+
+ @Test
+ fun getNetworkTypeOptions() {
+ val networkTypeOptions =
+ ApnNetworkTypes.getNetworkTypeOptions(
+ NETWORK_TYPE_BITMASK_EDGE xor NETWORK_TYPE_BITMASK_CDMA
+ )
+
+ assertThat(networkTypeOptions.single { it.text == "EDGE" }.selected.value).isTrue()
+ assertThat(networkTypeOptions.single { it.text == "CDMA" }.selected.value).isTrue()
+ assertThat(networkTypeOptions.single { it.text == "GPRS" }.selected.value).isFalse()
+ }
+
+ @Test
+ fun optionsToNetworkType() {
+ val options = listOf(
+ SettingsDropdownCheckOption(text = "", selected = mutableStateOf(false)),
+ SettingsDropdownCheckOption(text = "", selected = mutableStateOf(true)),
+ SettingsDropdownCheckOption(text = "", selected = mutableStateOf(false)),
+ SettingsDropdownCheckOption(text = "", selected = mutableStateOf(true)),
+ )
+
+ val networkType = ApnNetworkTypes.optionsToNetworkType(options)
+
+ assertThat(networkType).isEqualTo(NETWORK_TYPE_BITMASK_HSPAP xor NETWORK_TYPE_BITMASK_HSUPA)
+ }
+}
diff --git a/tests/spa_unit/src/com/android/settings/network/telephony/TelephonyRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/network/telephony/TelephonyRepositoryTest.kt
index b7e1dcc..ce27ed4 100644
--- a/tests/spa_unit/src/com/android/settings/network/telephony/TelephonyRepositoryTest.kt
+++ b/tests/spa_unit/src/com/android/settings/network/telephony/TelephonyRepositoryTest.kt
@@ -17,12 +17,14 @@
package com.android.settings.network.telephony
import android.content.Context
+import android.telephony.SubscriptionManager
import android.telephony.TelephonyCallback
import android.telephony.TelephonyManager
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settingslib.spa.testutils.firstWithTimeoutOrNull
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.runBlocking
import org.junit.Test
import org.junit.runner.RunWith
@@ -31,6 +33,7 @@
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
import org.mockito.kotlin.spy
+import org.mockito.kotlin.stub
import org.mockito.kotlin.verify
@RunWith(AndroidJUnit4::class)
@@ -48,6 +51,46 @@
on { getSystemService(TelephonyManager::class.java) } doReturn mockTelephonyManager
}
+ private val repository = TelephonyRepository(context, flowOf(Unit))
+
+ @Test
+ fun isMobileDataPolicyEnabledFlow_invalidSub_returnFalse() = runBlocking {
+ val flow = repository.isMobileDataPolicyEnabledFlow(
+ subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID,
+ policy = TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH,
+ )
+
+ assertThat(flow.firstWithTimeoutOrNull()).isFalse()
+ }
+
+ @Test
+ fun isMobileDataPolicyEnabledFlow_validSub_returnPolicyState() = runBlocking {
+ mockTelephonyManager.stub {
+ on {
+ isMobileDataPolicyEnabled(TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH)
+ } doReturn true
+ }
+
+ val flow = repository.isMobileDataPolicyEnabledFlow(
+ subId = SUB_ID,
+ policy = TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH,
+ )
+
+ assertThat(flow.firstWithTimeoutOrNull()).isTrue()
+ }
+
+ @Test
+ fun setMobileDataPolicyEnabled() = runBlocking {
+ repository.setMobileDataPolicyEnabled(
+ subId = SUB_ID,
+ policy = TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH,
+ enabled = true
+ )
+
+ verify(mockTelephonyManager)
+ .setMobileDataPolicyEnabled(TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH, true)
+ }
+
@Test
fun telephonyCallbackFlow_callbackRegistered() = runBlocking {
val flow = context.telephonyCallbackFlow<Unit>(SUB_ID) {
diff --git a/tests/unit/src/com/android/settings/accessibility/PreferredShortcutsTest.java b/tests/unit/src/com/android/settings/accessibility/PreferredShortcutsTest.java
index 4ef63d0..364b17c 100644
--- a/tests/unit/src/com/android/settings/accessibility/PreferredShortcutsTest.java
+++ b/tests/unit/src/com/android/settings/accessibility/PreferredShortcutsTest.java
@@ -208,6 +208,23 @@
assertThat(savedPreferredShortcut).isEqualTo(UserShortcutType.HARDWARE);
}
+ @Test
+ public void retrieveUserShortcutTypeWithoutDefault_noUserPreferredShortcuts_returnSoftwareShortcut() {
+ String target = COMPONENT_NAME_1.flattenToString();
+
+ assertThat(PreferredShortcuts.retrieveUserShortcutType(mContext, target))
+ .isEqualTo(UserShortcutType.SOFTWARE);
+ }
+
+ @Test
+ public void retrieveUserShortcutTypeWithDefaultAsDefault_noUserPreferredShortcuts_returnSpecifiedDefault() {
+ String target = COMPONENT_NAME_1.flattenToString();
+
+ assertThat(PreferredShortcuts.retrieveUserShortcutType(mContext, target,
+ UserShortcutType.HARDWARE))
+ .isEqualTo(UserShortcutType.HARDWARE);
+ }
+
private static void clearShortcuts() {
Settings.Secure.putString(sContentResolver,
Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, "");
diff --git a/tests/unit/src/com/android/settings/network/CellularSecurityPreferenceControllerTest.java b/tests/unit/src/com/android/settings/network/CellularSecurityPreferenceControllerTest.java
new file mode 100644
index 0000000..59e10c3
--- /dev/null
+++ b/tests/unit/src/com/android/settings/network/CellularSecurityPreferenceControllerTest.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.network;
+
+import static com.android.settings.core.BasePreferenceController.AVAILABLE;
+import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Build;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.safetycenter.SafetyCenterManager;
+import android.telephony.TelephonyManager;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceManager;
+import androidx.preference.PreferenceScreen;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.internal.telephony.flags.Flags;
+
+import org.junit.Assume;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+public final class CellularSecurityPreferenceControllerTest {
+ @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
+ @Mock
+ private TelephonyManager mTelephonyManager;
+ private Preference mPreference;
+ private PreferenceScreen mPreferenceScreen;
+
+ private static final String PREF_KEY = "cellular_security_pref_controller_test";
+ private Context mContext;
+ private CellularSecurityPreferenceController mController;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ // Tests must be skipped if these conditions aren't met as they cannot be mocked
+ Assume.assumeTrue(Build.VERSION.SDK_INT > Build.VERSION_CODES.TIRAMISU);
+ SafetyCenterManager mSafetyCenterManager = InstrumentationRegistry.getInstrumentation()
+ .getContext().getSystemService(SafetyCenterManager.class);
+ Assume.assumeTrue(mSafetyCenterManager != null);
+ Assume.assumeTrue(mSafetyCenterManager.isSafetyCenterEnabled());
+
+ mContext = spy(ApplicationProvider.getApplicationContext());
+ when(mContext.getSystemService(TelephonyManager.class)).thenReturn(mTelephonyManager);
+
+ doNothing().when(mContext).startActivity(any(Intent.class));
+
+ mController = new CellularSecurityPreferenceController(mContext, PREF_KEY);
+
+ mPreference = spy(new Preference(mContext));
+ mPreference.setKey(PREF_KEY);
+ mPreferenceScreen = new PreferenceManager(mContext).createPreferenceScreen(mContext);
+ mPreferenceScreen.addPreference(mPreference);
+ }
+
+ @Test
+ public void getAvailabilityStatus_hardwareSupported_shouldReturnTrue() {
+ // Enable telephony API flags for testing
+ enableFlags(true);
+
+ // Hardware support is enabled
+ doReturn(true).when(mTelephonyManager).isNullCipherNotificationsEnabled();
+ doReturn(true).when(mTelephonyManager)
+ .isCellularIdentifierDisclosureNotificationsEnabled();
+ doReturn(true).when(mTelephonyManager).isNullCipherAndIntegrityPreferenceEnabled();
+
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
+
+ // Disable null cipher toggle API, should still be available
+ doThrow(new UnsupportedOperationException("test")).when(mTelephonyManager)
+ .isNullCipherAndIntegrityPreferenceEnabled();
+
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
+
+ // Enable null cipher toggle API, disable notifications API, should still be available
+ doReturn(true).when(mTelephonyManager).isNullCipherAndIntegrityPreferenceEnabled();
+ doThrow(new UnsupportedOperationException("test")).when(mTelephonyManager)
+ .isNullCipherNotificationsEnabled();
+ doThrow(new UnsupportedOperationException("test")).when(mTelephonyManager)
+ .isCellularIdentifierDisclosureNotificationsEnabled();
+
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
+ }
+
+ @Test
+ public void getAvailabilityStatus_noHardwareSupport_shouldReturnFalse() {
+ // Enable telephony API flags for testing
+ enableFlags(true);
+
+ // Hardware support is disabled
+ doThrow(new UnsupportedOperationException("test")).when(mTelephonyManager)
+ .isNullCipherNotificationsEnabled();
+ doThrow(new UnsupportedOperationException("test")).when(mTelephonyManager)
+ .isCellularIdentifierDisclosureNotificationsEnabled();
+ doThrow(new UnsupportedOperationException("test")).when(mTelephonyManager)
+ .isNullCipherAndIntegrityPreferenceEnabled();
+
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
+ }
+
+ @Test
+ public void getAvailabilityStatus_flagsDisabled_shouldReturnFalse() {
+ // Both flags disabled
+ enableFlags(false);
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
+
+ // One flag is disabled
+ mSetFlagsRule.disableFlags(
+ Flags.FLAG_ENABLE_IDENTIFIER_DISCLOSURE_TRANSPARENCY_UNSOL_EVENTS);
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
+ }
+
+ @Test
+ public void handlePreferenceTreeClick_safetyCenterSupported_shouldRedirectToSafetyCenter() {
+ final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+
+ boolean prefHandled = mController.handlePreferenceTreeClick(mPreference);
+
+ assertThat(prefHandled).isTrue();
+ verify(mContext).startActivity(intentCaptor.capture());
+ assertThat(intentCaptor.getValue().getAction()).isEqualTo(Intent.ACTION_SAFETY_CENTER);
+ }
+
+ private void enableFlags(boolean enabled) {
+ if (enabled) {
+ mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_MODEM_CIPHER_TRANSPARENCY_UNSOL_EVENTS);
+ mSetFlagsRule.enableFlags(
+ Flags.FLAG_ENABLE_IDENTIFIER_DISCLOSURE_TRANSPARENCY_UNSOL_EVENTS);
+ mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_MODEM_CIPHER_TRANSPARENCY);
+ mSetFlagsRule.enableFlags(
+ Flags.FLAG_ENABLE_IDENTIFIER_DISCLOSURE_TRANSPARENCY);
+ } else {
+ mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_MODEM_CIPHER_TRANSPARENCY_UNSOL_EVENTS);
+ mSetFlagsRule.disableFlags(
+ Flags.FLAG_ENABLE_IDENTIFIER_DISCLOSURE_TRANSPARENCY_UNSOL_EVENTS);
+ mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_MODEM_CIPHER_TRANSPARENCY);
+ mSetFlagsRule.disableFlags(
+ Flags.FLAG_ENABLE_IDENTIFIER_DISCLOSURE_TRANSPARENCY);
+ }
+ }
+}
diff --git a/tests/unit/src/com/android/settings/network/telephony/CellularSecurityEncryptionDividerControllerTest.java b/tests/unit/src/com/android/settings/network/telephony/CellularSecurityEncryptionDividerControllerTest.java
new file mode 100644
index 0000000..f542209
--- /dev/null
+++ b/tests/unit/src/com/android/settings/network/telephony/CellularSecurityEncryptionDividerControllerTest.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.network.telephony;
+
+import static com.android.settings.core.BasePreferenceController.AVAILABLE;
+import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.telephony.TelephonyManager;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceManager;
+import androidx.preference.PreferenceScreen;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+public class CellularSecurityEncryptionDividerControllerTest {
+ @Mock
+ private TelephonyManager mTelephonyManager;
+ @Mock
+ private Preference mPreference;
+ private PreferenceScreen mPreferenceScreen;
+
+ private static final String PREF_KEY = "cellular_security_encryption_divider_test";
+ private Context mContext;
+ private CellularSecurityEncryptionDividerController mController;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mContext = spy(ApplicationProvider.getApplicationContext());
+ when(mContext.getSystemService(TelephonyManager.class)).thenReturn(mTelephonyManager);
+
+ mController = new CellularSecurityEncryptionDividerController(mContext, PREF_KEY);
+
+ mPreference = spy(new Preference(mContext));
+ mPreference.setKey(PREF_KEY);
+ mPreferenceScreen = new PreferenceManager(mContext).createPreferenceScreen(mContext);
+ mPreferenceScreen.addPreference(mPreference);
+ }
+
+ @Test
+ public void getAvailabilityStatus_hardwareSupported_shouldReturnAvailable() {
+ doReturn(true).when(mTelephonyManager).isNullCipherAndIntegrityPreferenceEnabled();
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
+ }
+
+ @Test
+ public void getAvailabilityStatus_noHardwareSupport_shouldReturnUnsupported() {
+ // Hardware support is disabled
+ doThrow(new UnsupportedOperationException("test")).when(mTelephonyManager)
+ .isNullCipherAndIntegrityPreferenceEnabled();
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
+ }
+}
diff --git a/tests/unit/src/com/android/settings/network/telephony/CellularSecurityNotificationsDividerControllerTest.java b/tests/unit/src/com/android/settings/network/telephony/CellularSecurityNotificationsDividerControllerTest.java
new file mode 100644
index 0000000..4e2351f
--- /dev/null
+++ b/tests/unit/src/com/android/settings/network/telephony/CellularSecurityNotificationsDividerControllerTest.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.network.telephony;
+
+import static com.android.settings.core.BasePreferenceController.AVAILABLE;
+import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.os.Build;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.safetycenter.SafetyCenterManager;
+import android.telephony.TelephonyManager;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceManager;
+import androidx.preference.PreferenceScreen;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.internal.telephony.flags.Flags;
+
+import org.junit.Assume;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+public class CellularSecurityNotificationsDividerControllerTest {
+ @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
+ @Mock
+ private TelephonyManager mTelephonyManager;
+ private Preference mPreference;
+ private PreferenceScreen mPreferenceScreen;
+
+ private static final String PREF_KEY = "cellular_security_notifications_divider_test";
+ private Context mContext;
+ private CellularSecurityNotificationsDividerController mController;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ // Tests must be skipped if these conditions aren't met as they cannot be mocked
+ Assume.assumeTrue(Build.VERSION.SDK_INT > Build.VERSION_CODES.TIRAMISU);
+ SafetyCenterManager mSafetyCenterManager = InstrumentationRegistry.getInstrumentation()
+ .getContext().getSystemService(SafetyCenterManager.class);
+ Assume.assumeTrue(mSafetyCenterManager != null);
+ Assume.assumeTrue(mSafetyCenterManager.isSafetyCenterEnabled());
+
+ mContext = spy(ApplicationProvider.getApplicationContext());
+ when(mContext.getSystemService(TelephonyManager.class)).thenReturn(mTelephonyManager);
+
+ mController = new CellularSecurityNotificationsDividerController(mContext, PREF_KEY);
+
+ mPreference = spy(new Preference(mContext));
+ mPreference.setKey(PREF_KEY);
+ mPreferenceScreen = new PreferenceManager(mContext).createPreferenceScreen(mContext);
+ mPreferenceScreen.addPreference(mPreference);
+ }
+
+ @Test
+ public void getAvailabilityStatus_hardwareSupported_shouldReturnTrue() {
+ // Enable telephony API flags for testing
+ enableFlags(true);
+
+ // Hardware support is enabled
+ doReturn(true).when(mTelephonyManager).isNullCipherNotificationsEnabled();
+ doReturn(true).when(mTelephonyManager)
+ .isCellularIdentifierDisclosureNotificationsEnabled();
+
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
+ }
+
+ @Test
+ public void getAvailabilityStatus_noHardwareSupport_shouldReturnFalse() {
+ // Enable telephony API flags for testing
+ enableFlags(true);
+
+ // Hardware support is disabled
+ doThrow(new UnsupportedOperationException("test")).when(mTelephonyManager)
+ .isNullCipherNotificationsEnabled();
+ doThrow(new UnsupportedOperationException("test")).when(mTelephonyManager)
+ .isCellularIdentifierDisclosureNotificationsEnabled();
+
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
+ }
+
+ @Test
+ public void getAvailabilityStatus_flagsDisabled_shouldReturnFalse() {
+ // Both flags disabled
+ enableFlags(false);
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
+
+ // One flag is disabled
+ mSetFlagsRule.disableFlags(
+ Flags.FLAG_ENABLE_IDENTIFIER_DISCLOSURE_TRANSPARENCY_UNSOL_EVENTS);
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
+ }
+
+ private void enableFlags(boolean enabled) {
+ if (enabled) {
+ mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_MODEM_CIPHER_TRANSPARENCY_UNSOL_EVENTS);
+ mSetFlagsRule.enableFlags(
+ Flags.FLAG_ENABLE_IDENTIFIER_DISCLOSURE_TRANSPARENCY_UNSOL_EVENTS);
+ mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_MODEM_CIPHER_TRANSPARENCY);
+ mSetFlagsRule.enableFlags(
+ Flags.FLAG_ENABLE_IDENTIFIER_DISCLOSURE_TRANSPARENCY);
+ } else {
+ mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_MODEM_CIPHER_TRANSPARENCY_UNSOL_EVENTS);
+ mSetFlagsRule.disableFlags(
+ Flags.FLAG_ENABLE_IDENTIFIER_DISCLOSURE_TRANSPARENCY_UNSOL_EVENTS);
+ mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_MODEM_CIPHER_TRANSPARENCY);
+ mSetFlagsRule.disableFlags(
+ Flags.FLAG_ENABLE_IDENTIFIER_DISCLOSURE_TRANSPARENCY);
+ }
+ }
+}
diff --git a/tests/unit/src/com/android/settings/network/telephony/CellularSecurityNotificationsPreferenceControllerTest.java b/tests/unit/src/com/android/settings/network/telephony/CellularSecurityNotificationsPreferenceControllerTest.java
new file mode 100644
index 0000000..8a72bd5
--- /dev/null
+++ b/tests/unit/src/com/android/settings/network/telephony/CellularSecurityNotificationsPreferenceControllerTest.java
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.network.telephony;
+
+import static com.android.settings.core.BasePreferenceController.AVAILABLE;
+import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.os.Build;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.safetycenter.SafetyCenterManager;
+import android.telephony.TelephonyManager;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceManager;
+import androidx.preference.PreferenceScreen;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.internal.telephony.flags.Flags;
+
+import org.junit.Assume;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+public class CellularSecurityNotificationsPreferenceControllerTest {
+ @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
+ @Mock
+ private TelephonyManager mTelephonyManager;
+ private Preference mPreference;
+ private PreferenceScreen mPreferenceScreen;
+
+ private static final String PREF_KEY = "cellular_security_notifications_pref_controller_test";
+ private Context mContext;
+ private CellularSecurityNotificationsPreferenceController mController;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ // Tests must be skipped if these conditions aren't met as they cannot be mocked
+ Assume.assumeTrue(Build.VERSION.SDK_INT > Build.VERSION_CODES.TIRAMISU);
+ SafetyCenterManager mSafetyCenterManager = InstrumentationRegistry.getInstrumentation()
+ .getContext().getSystemService(SafetyCenterManager.class);
+ Assume.assumeTrue(mSafetyCenterManager != null);
+ Assume.assumeTrue(mSafetyCenterManager.isSafetyCenterEnabled());
+
+ mContext = spy(ApplicationProvider.getApplicationContext());
+ when(mContext.getSystemService(TelephonyManager.class)).thenReturn(mTelephonyManager);
+
+ mController = new CellularSecurityNotificationsPreferenceController(mContext, PREF_KEY);
+
+ mPreference = spy(new Preference(mContext));
+ mPreference.setKey(PREF_KEY);
+ mPreferenceScreen = new PreferenceManager(mContext).createPreferenceScreen(mContext);
+ mPreferenceScreen.addPreference(mPreference);
+ }
+
+ @Test
+ public void getAvailabilityStatus_hardwareSupported_shouldReturnTrue() {
+ // All flags enabled
+ enableFlags(true);
+
+ // Hardware support is enabled
+ doReturn(true).when(mTelephonyManager).isNullCipherNotificationsEnabled();
+ doReturn(true).when(mTelephonyManager)
+ .isCellularIdentifierDisclosureNotificationsEnabled();
+
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
+ }
+
+ @Test
+ public void getAvailabilityStatus_noHardwareSupport_shouldReturnFalse() {
+ // All flags enabled
+ enableFlags(true);
+
+ // Hardware support is disabled
+ doThrow(new UnsupportedOperationException("test")).when(mTelephonyManager)
+ .isNullCipherNotificationsEnabled();
+ doThrow(new UnsupportedOperationException("test")).when(mTelephonyManager)
+ .isCellularIdentifierDisclosureNotificationsEnabled();
+
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
+ }
+
+ @Test
+ public void getAvailabilityStatus_flagsDisabled_shouldReturnFalse() {
+ // All flags disabled
+ enableFlags(false);
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
+
+ // One flag is disabled
+ enableFlags(true);
+ mSetFlagsRule.disableFlags(
+ Flags.FLAG_ENABLE_IDENTIFIER_DISCLOSURE_TRANSPARENCY_UNSOL_EVENTS);
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
+ }
+
+ @Test
+ public void setChecked_flagsDisabled_shouldReturnFalse() {
+ // Flags disabled
+ enableFlags(false);
+
+ // Hardware support is enabled
+ doNothing().when(mTelephonyManager).setNullCipherNotificationsEnabled(true);
+ doNothing().when(mTelephonyManager)
+ .setEnableCellularIdentifierDisclosureNotifications(true);
+ assertThat(mController.setChecked(false)).isFalse();
+ assertThat(mController.setChecked(true)).isFalse();
+
+ // Enable 1 flag, make sure it still fails
+ mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_MODEM_CIPHER_TRANSPARENCY_UNSOL_EVENTS);
+ assertThat(mController.setChecked(false)).isFalse();
+ assertThat(mController.setChecked(true)).isFalse();
+ }
+
+ @Test
+ public void setChecked_hardwareDisabled_shouldReturnFalse() {
+ // Flags disabled
+ enableFlags(false);
+
+ // Hardware support is enabled
+ doNothing().when(mTelephonyManager).setNullCipherNotificationsEnabled(true);
+ doNothing().when(mTelephonyManager)
+ .setEnableCellularIdentifierDisclosureNotifications(true);
+ assertThat(mController.setChecked(true)).isFalse();
+
+ // Hardware support is enabled, called with false
+ doNothing().when(mTelephonyManager).setNullCipherNotificationsEnabled(false);
+ doNothing().when(mTelephonyManager)
+ .setEnableCellularIdentifierDisclosureNotifications(false);
+ assertThat(mController.setChecked(false)).isFalse();
+ }
+
+ @Test
+ public void setChecked_shouldReturnTrue() {
+ enableFlags(true);
+
+ // Hardware support is enabled, enabling the feature
+ doNothing().when(mTelephonyManager).setNullCipherNotificationsEnabled(true);
+ doNothing().when(mTelephonyManager)
+ .setEnableCellularIdentifierDisclosureNotifications(true);
+ assertThat(mController.setChecked(true)).isTrue();
+
+ // Hardware support is enabled, disabling the feature
+ doNothing().when(mTelephonyManager).setNullCipherNotificationsEnabled(false);
+ doNothing().when(mTelephonyManager)
+ .setEnableCellularIdentifierDisclosureNotifications(false);
+ assertThat(mController.setChecked(false)).isTrue();
+ }
+
+ @Test
+ public void isChecked_shouldReturnTrue() {
+ // Hardware support is enabled
+ doReturn(true).when(mTelephonyManager).isNullCipherNotificationsEnabled();
+ doReturn(true).when(mTelephonyManager)
+ .isCellularIdentifierDisclosureNotificationsEnabled();
+ assertThat(mController.isChecked()).isTrue();
+ }
+
+ @Test
+ public void isChecked_flagsDisabled_shouldReturnFalse() {
+ enableFlags(false);
+
+ // Hardware support is enabled
+ doReturn(true).when(mTelephonyManager).isNullCipherNotificationsEnabled();
+ doReturn(true).when(mTelephonyManager)
+ .isCellularIdentifierDisclosureNotificationsEnabled();
+
+ assertThat(mController.isChecked()).isFalse();
+ }
+
+ @Test
+ public void isChecked_hardwareUnsupported_shouldReturnFalse() {
+ enableFlags(true);
+
+ // Hardware support is disabled
+ doThrow(new UnsupportedOperationException("test")).when(mTelephonyManager)
+ .isNullCipherNotificationsEnabled();
+ doThrow(new UnsupportedOperationException("test")).when(mTelephonyManager)
+ .isCellularIdentifierDisclosureNotificationsEnabled();
+
+ assertThat(mController.isChecked()).isFalse();
+ }
+
+ @Test
+ public void isChecked_notificationsDisabled_shouldReturnFalse() {
+ enableFlags(true);
+
+ // Hardware support is enabled, but APIs are disabled
+ doReturn(false).when(mTelephonyManager).isNullCipherNotificationsEnabled();
+ doReturn(false).when(mTelephonyManager)
+ .isCellularIdentifierDisclosureNotificationsEnabled();
+ assertThat(mController.isChecked()).isFalse();
+
+ // Enable 1 API, should still return false
+ doReturn(true).when(mTelephonyManager).isNullCipherNotificationsEnabled();
+ assertThat(mController.isChecked()).isFalse();
+ }
+
+ private void enableFlags(boolean enabled) {
+ if (enabled) {
+ mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_MODEM_CIPHER_TRANSPARENCY_UNSOL_EVENTS);
+ mSetFlagsRule.enableFlags(
+ Flags.FLAG_ENABLE_IDENTIFIER_DISCLOSURE_TRANSPARENCY_UNSOL_EVENTS);
+ mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_MODEM_CIPHER_TRANSPARENCY);
+ mSetFlagsRule.enableFlags(
+ Flags.FLAG_ENABLE_IDENTIFIER_DISCLOSURE_TRANSPARENCY);
+ } else {
+ mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_MODEM_CIPHER_TRANSPARENCY_UNSOL_EVENTS);
+ mSetFlagsRule.disableFlags(
+ Flags.FLAG_ENABLE_IDENTIFIER_DISCLOSURE_TRANSPARENCY_UNSOL_EVENTS);
+ mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_MODEM_CIPHER_TRANSPARENCY);
+ mSetFlagsRule.disableFlags(
+ Flags.FLAG_ENABLE_IDENTIFIER_DISCLOSURE_TRANSPARENCY);
+ }
+ }
+}