Snap for 11597038 from b41979a012 to 24Q3-release
Change-Id: I19a430a04227fd122519a84136664a64befb76d4
This commit is contained in:
@@ -629,6 +629,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"
|
||||
|
||||
@@ -6216,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] -->
|
||||
@@ -10864,6 +10887,12 @@
|
||||
<!-- 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>
|
||||
|
||||
@@ -12159,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>
|
||||
|
||||
42
res/xml/cellular_security.xml
Normal file
42
res/xml/cellular_security.xml
Normal file
@@ -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>
|
||||
@@ -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. -->
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 @@ public class MainClear extends InstrumentedFragment implements OnGlobalLayoutLis
|
||||
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 @@ public class MainClear extends InstrumentedFragment implements OnGlobalLayoutLis
|
||||
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(
|
||||
|
||||
@@ -348,6 +348,7 @@ public class Settings extends SettingsActivity {
|
||||
/* 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 */ }
|
||||
|
||||
@@ -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 @@ public class TrustedCredentialsFragment extends ObservableFragment
|
||||
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 @@ public class TrustedCredentialsFragment extends ObservableFragment
|
||||
}
|
||||
|
||||
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 @@ public class TrustedCredentialsFragment extends ObservableFragment
|
||||
|
||||
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 @@ public class TrustedCredentialsFragment extends ObservableFragment
|
||||
|
||||
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 @@ public class TrustedCredentialsFragment extends ObservableFragment
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
getActivity().unregisterReceiver(mWorkProfileChangedReceiver);
|
||||
getActivity().unregisterReceiver(mProfileChangedReceiver);
|
||||
for (AdapterData.AliasLoader aliasLoader : mAliasLoaders) {
|
||||
aliasLoader.cancel(true);
|
||||
}
|
||||
@@ -331,9 +368,16 @@ public class TrustedCredentialsFragment extends ObservableFragment
|
||||
}
|
||||
|
||||
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,
|
||||
|
||||
@@ -1365,6 +1365,16 @@ public final class Utils extends com.android.settingslib.Utils {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
||||
@@ -87,6 +87,10 @@ public class BiometricEnrollActivity extends InstrumentedActivity {
|
||||
// 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 @@ public class BiometricEnrollActivity extends InstrumentedActivity {
|
||||
|
||||
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(
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.android.settings.bluetooth;
|
||||
|
||||
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 @@ public class BluetoothDetailsSpatialAudioController extends BluetoothDetailsCont
|
||||
AudioDeviceAttributes mAudioDevice = null;
|
||||
|
||||
AtomicBoolean mHasHeadTracker = new AtomicBoolean(false);
|
||||
AtomicBoolean mInitialRefresh = new AtomicBoolean(true);
|
||||
|
||||
public BluetoothDetailsSpatialAudioController(
|
||||
Context context,
|
||||
@@ -81,6 +83,10 @@ public class BluetoothDetailsSpatialAudioController extends BluetoothDetailsCont
|
||||
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 @@ public class BluetoothDetailsSpatialAudioController extends BluetoothDetailsCont
|
||||
});
|
||||
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 @@ public class BluetoothDetailsSpatialAudioController extends BluetoothDetailsCont
|
||||
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
|
||||
|
||||
@@ -214,7 +214,7 @@ public class StylusDevicesController extends AbstractPreferenceController implem
|
||||
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 @@ public class StylusDevicesController extends AbstractPreferenceController implem
|
||||
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() {
|
||||
|
||||
@@ -144,6 +144,7 @@ import com.android.settings.network.NetworkDashboardFragment;
|
||||
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 @@ public class SettingsGateway {
|
||||
ScreenTimeoutSettings.class.getName(),
|
||||
ResetNetwork.class.getName(),
|
||||
VibrationIntensitySettingsFragment.class.getName(),
|
||||
CellularSecuritySettingsFragment.class.getName(),
|
||||
};
|
||||
|
||||
public static final String[] SETTINGS_FOR_RESTRICTED = {
|
||||
|
||||
@@ -25,7 +25,6 @@ import android.content.DialogInterface.OnDismissListener;
|
||||
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.DialogTitle;
|
||||
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 @@ public class ProfileSelectDialog extends DialogFragment implements UserAdapter.O
|
||||
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 @@ public class ProfileSelectDialog extends DialogFragment implements UserAdapter.O
|
||||
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 @@ public class ProfileSelectDialog extends DialogFragment implements UserAdapter.O
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,7 +64,11 @@ public class UserAdapter extends BaseAdapter {
|
||||
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 @@ public class UserAdapter extends BaseAdapter {
|
||||
() -> 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 @@ public class UserAdapter extends BaseAdapter {
|
||||
|
||||
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 @@ public class UserAdapter extends BaseAdapter {
|
||||
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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,7 @@ import android.telephony.UiccCardInfo
|
||||
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.setDefaultSms
|
||||
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 @@ class SimOnboardingService {
|
||||
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 @@ class SimOnboardingService {
|
||||
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)
|
||||
|
||||
@@ -24,18 +24,13 @@ import com.android.settingslib.spa.widget.editor.SettingsDropdownCheckBox
|
||||
|
||||
@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))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 @@ object ApnNetworkTypes {
|
||||
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])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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.channels.awaitClose
|
||||
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 @@ fun <T> Context.telephonyCallbackFlow(
|
||||
|
||||
awaitClose { telephonyManager.unregisterTelephonyCallback(callback) }
|
||||
}.conflate().flowOn(Dispatchers.Default)
|
||||
|
||||
fun Context.telephonyManager(subId: Int): TelephonyManager =
|
||||
getSystemService(TelephonyManager::class.java)!!
|
||||
.createForSubscriptionId(subId)
|
||||
|
||||
@@ -33,6 +33,7 @@ import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_C
|
||||
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.view.ViewGroup;
|
||||
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 @@ public class ChooseLockGeneric extends SettingsActivity {
|
||||
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 @@ public class ChooseLockGeneric extends SettingsActivity {
|
||||
private int mExtraLockScreenTitleResId;
|
||||
private int mExtraLockScreenDescriptionResId;
|
||||
private boolean mWaitingForBiometricEnrollment = false;
|
||||
private boolean mEnrollFingerPrintOnly = false;
|
||||
|
||||
@Override
|
||||
public int getMetricsCategory() {
|
||||
@@ -225,8 +228,10 @@ public class ChooseLockGeneric extends SettingsActivity {
|
||||
}
|
||||
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 @@ public class ChooseLockGeneric extends SettingsActivity {
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -64,6 +64,8 @@ public final class ChooseLockSettingsHelper {
|
||||
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";
|
||||
|
||||
@@ -27,6 +27,7 @@ import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_C
|
||||
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 @@ public class SetNewPasswordActivity extends Activity implements SetNewPasswordCo
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FACE;
|
||||
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 @@ final class SetNewPasswordController {
|
||||
}
|
||||
// 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,
|
||||
|
||||
@@ -22,6 +22,7 @@ import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_LOW;
|
||||
|
||||
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 @@ public class PrivateProfileContextHelperActivity extends FragmentActivity {
|
||||
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(
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -30,7 +30,6 @@ import androidx.compose.runtime.Composable
|
||||
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 androidx.lifecycle.viewmodel.compose.viewModel
|
||||
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.compose.navigator
|
||||
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 @@ fun PrimarySimImpl(
|
||||
callsSelectedId: MutableIntState,
|
||||
textsSelectedId: MutableIntState,
|
||||
mobileDataSelectedId: MutableIntState,
|
||||
nonDds: MutableIntState,
|
||||
subscriptionManager: SubscriptionManager? =
|
||||
LocalContext.current.getSystemService(SubscriptionManager::class.java),
|
||||
coroutineScope: CoroutineScope = rememberCoroutineScope(),
|
||||
@@ -223,23 +220,9 @@ fun PrimarySimImpl(
|
||||
)
|
||||
}
|
||||
},
|
||||
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 @@ fun PrimarySimImpl(
|
||||
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 @@ fun PrimarySimSectionImpl(
|
||||
}.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 @@ suspend fun setDefaultData(
|
||||
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
|
||||
}
|
||||
@@ -23,6 +23,7 @@ import androidx.compose.material.icons.Icons
|
||||
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 @@ fun SimOnboardingPrimarySimImpl(
|
||||
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 @@ fun SimOnboardingPrimarySimImpl(
|
||||
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 @@ fun SimOnboardingPrimarySimImpl(
|
||||
actionSetMobileData = {
|
||||
mobileDataSelectedId.intValue = it
|
||||
onboardingService.targetPrimarySimMobileData = it},
|
||||
actionSetAutoDataSwitch = {
|
||||
onboardingService.targetPrimarySimAutoDataSwitch = it},
|
||||
isAutoDataEnabled = { isAutoDataEnabled },
|
||||
setAutoDataEnabled = { newEnabled ->
|
||||
onboardingService.targetPrimarySimAutoDataSwitch.value = newEnabled
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import static org.mockito.Mockito.spy;
|
||||
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 android.media.Spatializer;
|
||||
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 @@ public class BluetoothDetailsSpatialAudioControllerTest extends BluetoothDetails
|
||||
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 @@ public class BluetoothDetailsSpatialAudioControllerTest extends BluetoothDetails
|
||||
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 @@ public class BluetoothDetailsSpatialAudioControllerTest extends BluetoothDetails
|
||||
|
||||
@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 @@ public class BluetoothDetailsSpatialAudioControllerTest extends BluetoothDetails
|
||||
}
|
||||
|
||||
@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 @@ public class BluetoothDetailsSpatialAudioControllerTest extends BluetoothDetails
|
||||
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 @@ public class BluetoothDetailsSpatialAudioControllerTest extends BluetoothDetails
|
||||
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 @@ public class BluetoothDetailsSpatialAudioControllerTest extends BluetoothDetails
|
||||
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 @@ public class BluetoothDetailsSpatialAudioControllerTest extends BluetoothDetails
|
||||
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 @@ public class BluetoothDetailsSpatialAudioControllerTest extends BluetoothDetails
|
||||
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 @@ public class BluetoothDetailsSpatialAudioControllerTest extends BluetoothDetails
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,7 +53,6 @@ import com.android.settings.widget.SingleTargetGearPreference;
|
||||
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 @@ public class PreviouslyConnectedDevicePreferenceControllerTest {
|
||||
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 @@ public class PreviouslyConnectedDevicePreferenceControllerTest {
|
||||
AVAILABLE);
|
||||
}
|
||||
|
||||
@Ignore("b/322712259")
|
||||
@Test
|
||||
public void onDeviceAdded_addDevicePreference_displayIt() {
|
||||
final BluetoothDevicePreference preference1 = new BluetoothDevicePreference(
|
||||
@@ -234,7 +233,6 @@ public class PreviouslyConnectedDevicePreferenceControllerTest {
|
||||
assertThat(mPreferenceGroup.getPreferenceCount()).isEqualTo(2);
|
||||
}
|
||||
|
||||
@Ignore("b/322712259")
|
||||
@Test
|
||||
public void onDeviceAdded_addDockDevicePreference_displayIt() {
|
||||
final SingleTargetGearPreference dockPreference = new SingleTargetGearPreference(
|
||||
@@ -245,7 +243,6 @@ public class PreviouslyConnectedDevicePreferenceControllerTest {
|
||||
assertThat(mPreferenceGroup.getPreferenceCount()).isEqualTo(2);
|
||||
}
|
||||
|
||||
@Ignore("b/322712259")
|
||||
@Test
|
||||
public void onDeviceAdded_addFourDevicePreference_onlyDisplayThree() {
|
||||
final BluetoothDevicePreference preference1 = new BluetoothDevicePreference(
|
||||
|
||||
@@ -41,9 +41,12 @@ import android.content.Intent;
|
||||
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.bluetooth.CachedBluetoothDevice;
|
||||
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 @@ import java.util.List;
|
||||
|
||||
@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 @@ public class StylusDevicesControllerTest {
|
||||
@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 @@ public class StylusDevicesControllerTest {
|
||||
private CachedBluetoothDevice mCachedBluetoothDevice;
|
||||
@Mock
|
||||
private BluetoothDevice mBluetoothDevice;
|
||||
@Mock
|
||||
private Drawable mIcon;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
@@ -134,6 +150,7 @@ public class StylusDevicesControllerTest {
|
||||
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 @@ public class StylusDevicesControllerTest {
|
||||
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 @@ public class StylusDevicesControllerTest {
|
||||
}
|
||||
|
||||
@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 @@ public class StylusDevicesControllerTest {
|
||||
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));
|
||||
|
||||
@@ -24,8 +24,10 @@ import static org.mockito.Mockito.when;
|
||||
|
||||
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 @@ import java.util.ArrayList;
|
||||
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 @@ public class UserAdapterTest {
|
||||
|
||||
@Mock
|
||||
private UserInfo mWorkUserInfo;
|
||||
@Mock
|
||||
private UserInfo mPrivateUserInfo;
|
||||
|
||||
@Mock
|
||||
private UserAdapter.OnClickListener mOnClickListener;
|
||||
@@ -71,11 +78,31 @@ public class UserAdapterTest {
|
||||
@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
|
||||
@@ -102,6 +129,48 @@ public class UserAdapterTest {
|
||||
assertThat(userSpinnerAdapter.getUserHandle(1).getIdentifier()).isEqualTo(WORK_USER_ID);
|
||||
}
|
||||
|
||||
@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 =
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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.doAnswer
|
||||
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 @@ class TelephonyRepositoryTest {
|
||||
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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user