Snap for 11597038 from b41979a012 to 24Q3-release

Change-Id: I19a430a04227fd122519a84136664a64befb76d4
This commit is contained in:
Android Build Coastguard Worker
2024-03-19 23:21:20 +00:00
42 changed files with 2051 additions and 200 deletions

View File

@@ -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"

View File

@@ -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 &amp; 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>

View 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>

View File

@@ -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. -->

View File

@@ -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>

View File

@@ -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(

View File

@@ -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 */ }

View File

@@ -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,

View File

@@ -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

View File

@@ -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(

View File

@@ -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

View File

@@ -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() {

View File

@@ -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 = {

View File

@@ -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);
}
}

View File

@@ -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;

View File

@@ -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();
}
}

View File

@@ -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)

View File

@@ -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))
}
}

View File

@@ -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])
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}
}

View File

@@ -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();
}
}

View File

@@ -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);
}

View File

@@ -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)

View File

@@ -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;
}

View File

@@ -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";

View File

@@ -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);
}

View File

@@ -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,

View File

@@ -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(

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
},
)
}
}

View File

@@ -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);
}
}

View File

@@ -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(

View File

@@ -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));

View File

@@ -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 =

View File

@@ -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)
}
}

View File

@@ -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) {

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}
}