Snap for 11545716 from 34bd0481df to 24Q3-release
Change-Id: I3bb1ac950bccd84d04a6aa8a010dbb2c42a1d831
This commit is contained in:
@@ -11685,6 +11685,13 @@
|
||||
<!-- Body text of automatic data switching at dual sim onboarding's primary sim page or SIMs page. [CHAR LIMIT=NONE] -->
|
||||
<string name="primary_sim_automatic_data_msg">Use data from either SIM depending on coverage and availability</string>
|
||||
|
||||
<!-- Title of asking the user whether to restart device after enabling DSDS. [CHAR LIMIT=NONE] -->
|
||||
<string name="sim_action_restart_dialog_title">Restart to use 2 SIMs</string>
|
||||
<!-- Body text of asking the user whether to restart device after enabling DSDS. [CHAR LIMIT=NONE] -->
|
||||
<string name="sim_action_restart_dialog_msg">To use 2 SIMs at once, restart your device, then turn on both SIMs</string>
|
||||
<!-- Button text to cancel dialog and then enable the sim -->
|
||||
<string name="sim_action_restart_dialog_cancel">Use <xliff:g id="carrier_name" example="Google Fi">%1$s</xliff:g> only</string>
|
||||
|
||||
<!-- Text of phone number item when the sim is data only. [CHAR LIMIT=NONE] -->
|
||||
<string name="sim_onboarding_phoneNumber_data_only">Data only</string>
|
||||
|
||||
@@ -13002,11 +13009,6 @@
|
||||
<!-- Developer settings: select Grammatical gender dialog title [CHAR LIMIT=50]-->
|
||||
<string name="grammatical_gender_dialog_title">Select Grammatical gender</string>
|
||||
|
||||
<!-- Do not translate. Developer settings: Title for the screen allowing user to control Quarantined apps [CHAR LIMIT=50] -->
|
||||
<string name="quarantined_apps_title" translatable="false">Quarantined Apps</string>
|
||||
<!-- Do not translate. Developer settings: Button to unquarantine an app [CHAR LIMIT=20] -->
|
||||
<string name="unquarantine_app_button" translatable="false">Unquarantine app</string>
|
||||
|
||||
<!-- Title of preference to manage content protection settings -->
|
||||
<string name="content_protection_preference_title">Scanning for deceptive apps</string>
|
||||
<!-- Summary of the preference to manage content protection settings -->
|
||||
|
||||
@@ -769,12 +769,6 @@
|
||||
android:title="@string/enable_notes_role_title"
|
||||
android:summary="@string/enable_notes_role_summary" />
|
||||
|
||||
<Preference
|
||||
android:key="quarantined_apps"
|
||||
android:title="@string/quarantined_apps_title"
|
||||
settings:controller="com.android.settings.development.quarantine.QuarantinedAppsPreferenceController"
|
||||
android:fragment="com.android.settings.development.quarantine.QuarantinedAppsFragment" />
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory
|
||||
|
||||
@@ -18,9 +18,8 @@
|
||||
xmlns:settings="http://schemas.android.com/apk/res-auto"
|
||||
android:key="mobile_network_pref_screen">
|
||||
|
||||
<com.android.settings.widget.SettingsMainSwitchPreference
|
||||
<com.android.settings.spa.preference.ComposePreference
|
||||
android:key="use_sim_switch"
|
||||
android:title="@string/mobile_network_use_sim_on"
|
||||
settings:controller="com.android.settings.network.telephony.MobileNetworkSwitchController"/>
|
||||
|
||||
<PreferenceCategory
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2023 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="quarantined_apps_screen"
|
||||
android:title="@string/quarantined_apps_title"
|
||||
settings:controller="com.android.settings.development.quarantine.QuarantinedAppsScreenController"
|
||||
settings:searchable="true">
|
||||
</PreferenceScreen>
|
||||
@@ -28,7 +28,8 @@
|
||||
settings:searchable="false"/>
|
||||
|
||||
<com.android.settingslib.widget.ActionButtonsPreference
|
||||
android:key="header_view" />
|
||||
android:key="header_view"
|
||||
android:order="-999"/>
|
||||
|
||||
<com.android.settings.applications.appcompat.RadioWithImagePreference
|
||||
android:key="app_default_pref"
|
||||
|
||||
@@ -118,7 +118,8 @@ public class SettingsInitialize extends BroadcastReceiver {
|
||||
}
|
||||
|
||||
private void privateProfileSetup(Context context, PackageManager pm, UserInfo userInfo) {
|
||||
if (Flags.allowPrivateProfile()) {
|
||||
if (Flags.allowPrivateProfile()
|
||||
&& android.multiuser.Flags.enablePrivateSpaceFeatures()) {
|
||||
if (userInfo == null || !userInfo.isPrivateProfile()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1228,6 +1228,7 @@ public final class Utils extends com.android.settingslib.Utils {
|
||||
UserProperties userProperties = userManager.getUserProperties(userHandle);
|
||||
if (userProperties.getShowInSettings() == UserProperties.SHOW_IN_SETTINGS_SEPARATE) {
|
||||
if (Flags.allowPrivateProfile()
|
||||
&& android.multiuser.Flags.enablePrivateSpaceFeatures()
|
||||
&& userProperties.getShowInQuietMode()
|
||||
== UserProperties.SHOW_IN_QUIET_MODE_HIDDEN) {
|
||||
if (!userManager.isQuietModeEnabled(userHandle)) {
|
||||
|
||||
@@ -47,7 +47,7 @@ import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
|
||||
|
||||
|
||||
/**
|
||||
* A preference controller to turn on/off keyboard vibration state with a single toggle.
|
||||
* A preference controller to turn on/off keyboard vibration state with a single toggle.
|
||||
*/
|
||||
public class KeyboardVibrationTogglePreferenceController extends TogglePreferenceController
|
||||
implements DefaultLifecycleObserver {
|
||||
@@ -110,7 +110,9 @@ public class KeyboardVibrationTogglePreferenceController extends TogglePreferenc
|
||||
@Override
|
||||
public int getAvailabilityStatus() {
|
||||
if (Flags.keyboardCategoryEnabled()
|
||||
&& mContext.getResources().getBoolean(R.bool.config_keyboard_vibration_supported)) {
|
||||
&& mContext.getResources().getBoolean(R.bool.config_keyboard_vibration_supported)
|
||||
&& mContext.getResources().getFloat(
|
||||
com.android.internal.R.dimen.config_keyboardHapticFeedbackFixedAmplitude) > 0) {
|
||||
return AVAILABLE;
|
||||
}
|
||||
return UNSUPPORTED_ON_DEVICE;
|
||||
|
||||
@@ -311,11 +311,10 @@ public class AccountPreferenceController extends AbstractPreferenceController
|
||||
// should be shown or not.
|
||||
if (((profile.isManagedProfile()
|
||||
&& (mType & ProfileSelectFragment.ProfileType.WORK) != 0)
|
||||
|| (Flags.allowPrivateProfile()
|
||||
&& profile.isPrivateProfile()
|
||||
|| (isPrivateProfile(profile)
|
||||
&& (mType & ProfileSelectFragment.ProfileType.PRIVATE) != 0)
|
||||
|| (!profile.isManagedProfile()
|
||||
&& !(Flags.allowPrivateProfile() && profile.isPrivateProfile())
|
||||
&& !isPrivateProfile(profile)
|
||||
&& (mType & ProfileSelectFragment.ProfileType.PERSONAL) != 0))
|
||||
&& !(mUm.getUserProperties(profile.getUserHandle())
|
||||
.getShowInQuietMode() == UserProperties.SHOW_IN_QUIET_MODE_HIDDEN
|
||||
@@ -338,6 +337,12 @@ public class AccountPreferenceController extends AbstractPreferenceController
|
||||
mFragment.forceUpdatePreferences();
|
||||
}
|
||||
|
||||
private static boolean isPrivateProfile(UserInfo profile) {
|
||||
return Flags.allowPrivateProfile()
|
||||
&& android.multiuser.Flags.enablePrivateSpaceFeatures()
|
||||
&& profile.isPrivateProfile();
|
||||
}
|
||||
|
||||
private void updateProfileUi(final UserInfo userInfo) {
|
||||
if (mFragment.getPreferenceManager() == null) {
|
||||
return;
|
||||
|
||||
@@ -66,7 +66,8 @@ public class UserAspectRatioDetails extends AppInfoBase implements
|
||||
private static final String TAG = UserAspectRatioDetails.class.getSimpleName();
|
||||
|
||||
private static final String KEY_HEADER_SUMMARY = "app_aspect_ratio_summary";
|
||||
private static final String KEY_HEADER_BUTTONS = "header_view";
|
||||
@VisibleForTesting
|
||||
static final String KEY_HEADER_BUTTONS = "header_view";
|
||||
|
||||
private static final String KEY_PREF_HALF_SCREEN = "half_screen_pref";
|
||||
private static final String KEY_PREF_DISPLAY_SIZE = "display_size_pref";
|
||||
@@ -237,6 +238,7 @@ public class UserAspectRatioDetails extends AppInfoBase implements
|
||||
return;
|
||||
}
|
||||
pref.setTitle(mUserAspectRatioManager.getAccessibleEntry(aspectRatio, mPackageName));
|
||||
pref.setOrder(getAspectRatioManager().getUserMinAspectRatioOrder(aspectRatio));
|
||||
pref.setOnClickListener(this);
|
||||
mKeyToAspectRatioMap.put(key, aspectRatio);
|
||||
mAspectRatioPreferences.add(pref);
|
||||
|
||||
@@ -44,6 +44,7 @@ import android.os.RemoteException;
|
||||
import android.os.UserHandle;
|
||||
import android.provider.DeviceConfig;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.SparseIntArray;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
@@ -76,6 +77,7 @@ public class UserAspectRatioManager {
|
||||
/** Apps that have launcher entry defined in manifest */
|
||||
private final Map<Integer, String> mUserAspectRatioMap;
|
||||
private final Map<Integer, CharSequence> mUserAspectRatioA11yMap;
|
||||
private final SparseIntArray mUserAspectRatioOrder;
|
||||
|
||||
public UserAspectRatioManager(@NonNull Context context) {
|
||||
this(context, AppGlobals.getPackageManager());
|
||||
@@ -86,6 +88,7 @@ public class UserAspectRatioManager {
|
||||
mContext = context;
|
||||
mIPm = pm;
|
||||
mUserAspectRatioA11yMap = new ArrayMap<>();
|
||||
mUserAspectRatioOrder = new SparseIntArray();
|
||||
mUserAspectRatioMap = getUserMinAspectRatioMapping();
|
||||
}
|
||||
|
||||
@@ -152,6 +155,14 @@ public class UserAspectRatioManager {
|
||||
return getUserMinAspectRatioEntry(aspectRatio, packageName, userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the order of the aspect ratio option corresponding to
|
||||
* config_userAspectRatioOverrideValues
|
||||
*/
|
||||
int getUserMinAspectRatioOrder(@PackageManager.UserMinAspectRatio int option) {
|
||||
return mUserAspectRatioOrder.get(option);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether user aspect ratio option is specified in
|
||||
* {@link R.array.config_userAspectRatioOverrideValues}
|
||||
@@ -224,7 +235,6 @@ public class UserAspectRatioManager {
|
||||
&& isFullscreenCompatChangeEnabled(pkgName, userId);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
boolean isFullscreenCompatChangeEnabled(String pkgName, int userId) {
|
||||
return CompatChanges.isChangeEnabled(
|
||||
OVERRIDE_ANY_ORIENTATION_TO_USER, pkgName, UserHandle.of(userId));
|
||||
@@ -281,6 +291,7 @@ public class UserAspectRatioManager {
|
||||
mUserAspectRatioA11yMap.put(aspectRatioVal, accessibleSequence);
|
||||
}
|
||||
userMinAspectRatioMap.put(aspectRatioVal, aspectRatioString);
|
||||
mUserAspectRatioOrder.put(aspectRatioVal, i);
|
||||
}
|
||||
}
|
||||
if (!userMinAspectRatioMap.containsKey(USER_MIN_ASPECT_RATIO_UNSET)) {
|
||||
@@ -290,6 +301,8 @@ public class UserAspectRatioManager {
|
||||
if (mIsUserMinAspectRatioAppDefaultFlagEnabled) {
|
||||
userMinAspectRatioMap.put(USER_MIN_ASPECT_RATIO_APP_DEFAULT,
|
||||
userMinAspectRatioMap.get(USER_MIN_ASPECT_RATIO_UNSET));
|
||||
mUserAspectRatioOrder.put(USER_MIN_ASPECT_RATIO_APP_DEFAULT,
|
||||
mUserAspectRatioOrder.get(USER_MIN_ASPECT_RATIO_UNSET));
|
||||
if (mUserAspectRatioA11yMap.containsKey(USER_MIN_ASPECT_RATIO_UNSET)) {
|
||||
mUserAspectRatioA11yMap.put(USER_MIN_ASPECT_RATIO_APP_DEFAULT,
|
||||
mUserAspectRatioA11yMap.get(USER_MIN_ASPECT_RATIO_UNSET));
|
||||
|
||||
@@ -167,8 +167,9 @@ public class DeviceAdminListPreferenceController extends BasePreferenceControlle
|
||||
}
|
||||
|
||||
private boolean shouldSkipProfile(UserHandle profile) {
|
||||
return android.os.Flags.allowPrivateProfile()
|
||||
return android.os.Flags.allowPrivateProfile()
|
||||
&& android.multiuser.Flags.handleInterleavedSettingsForPrivateSpace()
|
||||
&& android.multiuser.Flags.enablePrivateSpaceFeatures()
|
||||
&& mUm.isQuietModeEnabled(profile)
|
||||
&& mUm.getUserProperties(profile).getShowInQuietMode()
|
||||
== UserProperties.SHOW_IN_QUIET_MODE_HIDDEN;
|
||||
|
||||
@@ -340,6 +340,9 @@ public class FingerprintEnrollIntroduction extends BiometricEnrollIntroduction {
|
||||
final List<FingerprintSensorPropertiesInternal> props =
|
||||
mFingerprintManager.getSensorPropertiesInternal();
|
||||
// This will need to be updated for devices with multiple fingerprint sensors
|
||||
if (props == null || props.isEmpty()) {
|
||||
return R.string.fingerprint_intro_error_unknown;
|
||||
}
|
||||
final int max = props.get(0).maxEnrollmentsPerUser;
|
||||
final int numEnrolledFingerprints =
|
||||
mFingerprintManager.getEnrolledFingerprints(mUserId).size();
|
||||
|
||||
@@ -239,7 +239,9 @@ public abstract class ProfileSelectFragment extends DashboardFragment {
|
||||
return WORK_TAB;
|
||||
}
|
||||
UserInfo userInfo = UserManager.get(activity).getUserInfo(userId);
|
||||
if (Flags.allowPrivateProfile() && userInfo != null && userInfo.isPrivateProfile()) {
|
||||
if (Flags.allowPrivateProfile()
|
||||
&& android.multiuser.Flags.enablePrivateSpaceFeatures()
|
||||
&& userInfo != null && userInfo.isPrivateProfile()) {
|
||||
return PRIVATE_TAB;
|
||||
}
|
||||
}
|
||||
@@ -249,7 +251,9 @@ public abstract class ProfileSelectFragment extends DashboardFragment {
|
||||
return WORK_TAB;
|
||||
}
|
||||
UserInfo userInfo = UserManager.get(activity).getUserInfo(intentUser);
|
||||
if (Flags.allowPrivateProfile() && userInfo != null && userInfo.isPrivateProfile()) {
|
||||
if (Flags.allowPrivateProfile()
|
||||
&& android.multiuser.Flags.enablePrivateSpaceFeatures()
|
||||
&& userInfo != null && userInfo.isPrivateProfile()) {
|
||||
return PRIVATE_TAB;
|
||||
}
|
||||
|
||||
@@ -260,7 +264,7 @@ public abstract class ProfileSelectFragment extends DashboardFragment {
|
||||
final DevicePolicyManager devicePolicyManager =
|
||||
getContext().getSystemService(DevicePolicyManager.class);
|
||||
|
||||
if (Flags.allowPrivateProfile()) {
|
||||
if (Flags.allowPrivateProfile() && android.multiuser.Flags.enablePrivateSpaceFeatures()) {
|
||||
int tabForPosition =
|
||||
((ViewPagerAdapter) mViewPager.getAdapter()).getTabForPosition(position);
|
||||
|
||||
@@ -331,7 +335,9 @@ public abstract class ProfileSelectFragment extends DashboardFragment {
|
||||
ProfileType.WORK,
|
||||
bundle != null ? bundle.deepCopy() : new Bundle(),
|
||||
workFragmentConstructor));
|
||||
} else if (Flags.allowPrivateProfile() && userInfo.isPrivateProfile()) {
|
||||
} else if (Flags.allowPrivateProfile()
|
||||
&& android.multiuser.Flags.enablePrivateSpaceFeatures()
|
||||
&& userInfo.isPrivateProfile()) {
|
||||
if (!privateSpaceInfoProvider.isPrivateSpaceLocked(context)) {
|
||||
fragments.add(createAndGetFragment(
|
||||
ProfileType.PRIVATE,
|
||||
|
||||
@@ -87,6 +87,7 @@ public class UserAdapter extends BaseAdapter {
|
||||
return resources.getString(WORK_CATEGORY_HEADER,
|
||||
() -> context.getString(com.android.settingslib.R.string.category_work));
|
||||
} else if (android.os.Flags.allowPrivateProfile()
|
||||
&& android.multiuser.Flags.enablePrivateSpaceFeatures()
|
||||
&& mUserManager.getUserInfo(userId).isPrivateProfile()) {
|
||||
return resources.getString(PRIVATE_CATEGORY_HEADER,
|
||||
() -> context.getString(com.android.settingslib.R.string.category_private));
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
# Bug component: 316234
|
||||
|
||||
sudheersai@google.com
|
||||
yamasani@google.com
|
||||
@@ -1,76 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.development.quarantine;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
|
||||
import androidx.preference.PreferenceViewHolder;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settingslib.applications.AppUtils;
|
||||
import com.android.settingslib.applications.ApplicationsState.AppEntry;
|
||||
import com.android.settingslib.utils.ThreadUtils;
|
||||
import com.android.settingslib.widget.AppSwitchPreference;
|
||||
|
||||
public class QuarantinedAppPreference extends AppSwitchPreference {
|
||||
private final AppEntry mEntry;
|
||||
private Drawable mCacheIcon;
|
||||
|
||||
public QuarantinedAppPreference(Context context, AppEntry entry) {
|
||||
super(context);
|
||||
mEntry = entry;
|
||||
mCacheIcon = AppUtils.getIconFromCache(mEntry);
|
||||
|
||||
mEntry.ensureLabel(context);
|
||||
setKey(generateKey(mEntry));
|
||||
if (mCacheIcon != null) {
|
||||
setIcon(mCacheIcon);
|
||||
} else {
|
||||
setIcon(R.drawable.empty_icon);
|
||||
}
|
||||
updateState();
|
||||
}
|
||||
|
||||
static String generateKey(AppEntry entry) {
|
||||
return entry.info.packageName + "|" + entry.info.uid;
|
||||
}
|
||||
|
||||
public AppEntry getEntry() {
|
||||
return mEntry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(PreferenceViewHolder holder) {
|
||||
if (mCacheIcon == null) {
|
||||
ThreadUtils.postOnBackgroundThread(() -> {
|
||||
final Drawable icon = AppUtils.getIcon(getContext(), mEntry);
|
||||
ThreadUtils.postOnMainThread(() -> {
|
||||
setIcon(icon);
|
||||
mCacheIcon = icon;
|
||||
});
|
||||
});
|
||||
}
|
||||
super.onBindViewHolder(holder);
|
||||
}
|
||||
|
||||
void updateState() {
|
||||
setTitle(mEntry.label);
|
||||
setChecked((boolean) mEntry.extraInfo);
|
||||
notifyChanged();
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.development.quarantine;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.UserHandle;
|
||||
|
||||
import com.android.settings.applications.AppStateBaseBridge;
|
||||
import com.android.settingslib.applications.ApplicationsState;
|
||||
import com.android.settingslib.applications.ApplicationsState.AppEntry;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class QuarantinedAppStateBridge extends AppStateBaseBridge {
|
||||
private Context mContext;
|
||||
|
||||
public QuarantinedAppStateBridge(Context context,
|
||||
ApplicationsState appState, Callback callback) {
|
||||
super(appState, callback);
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void loadAllExtraInfo() {
|
||||
final ArrayList<AppEntry> apps = mAppSession.getAllApps();
|
||||
for (int i = 0; i < apps.size(); i++) {
|
||||
final AppEntry app = apps.get(i);
|
||||
updateExtraInfo(app, app.info.packageName, app.info.uid);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateExtraInfo(AppEntry app, String pkg, int uid) {
|
||||
app.extraInfo = isPackageQuarantined(pkg, uid);
|
||||
}
|
||||
|
||||
private boolean isPackageQuarantined(String pkg, int uid) {
|
||||
final PackageManager pm = mContext.createContextAsUser(
|
||||
UserHandle.getUserHandleForUid(uid), 0).getPackageManager();
|
||||
try {
|
||||
return pm.isPackageQuarantined(pkg);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,172 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.development.quarantine;
|
||||
|
||||
import static android.view.MenuItem.SHOW_AS_ACTION_ALWAYS;
|
||||
import static android.view.MenuItem.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW;
|
||||
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.widget.SearchView;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.dashboard.DashboardFragment;
|
||||
import com.android.settings.search.BaseSearchIndexProvider;
|
||||
import com.android.settingslib.applications.AppIconCacheManager;
|
||||
import com.android.settingslib.applications.ApplicationsState;
|
||||
import com.android.settingslib.applications.ApplicationsState.AppFilter;
|
||||
import com.android.settingslib.search.SearchIndexable;
|
||||
|
||||
import com.google.android.material.appbar.AppBarLayout;
|
||||
|
||||
// TODO: b/297934650 - Update this to use SPA framework
|
||||
@SearchIndexable
|
||||
public class QuarantinedAppsFragment extends DashboardFragment implements
|
||||
SearchView.OnQueryTextListener, SearchView.OnCloseListener,
|
||||
MenuItem.OnActionExpandListener {
|
||||
private static final String TAG = "QuarantinedApps";
|
||||
|
||||
private static final int MENU_SEARCH_APPS = Menu.FIRST + 42;
|
||||
private static final int MENU_SHOW_SYSTEM = Menu.FIRST + 43;
|
||||
private static final String EXTRA_SHOW_SYSTEM = "show_system";
|
||||
|
||||
private boolean mShowSystem;
|
||||
private SearchView mSearchView;
|
||||
private String mCurQuery;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle icicle) {
|
||||
super.onCreate(icicle);
|
||||
mShowSystem = icicle != null && icicle.getBoolean(EXTRA_SHOW_SYSTEM);
|
||||
use(QuarantinedAppsScreenController.class).setFilter(mCustomFilter);
|
||||
use(QuarantinedAppsScreenController.class).setSession(getSettingsLifecycle());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
mSearchView = new SearchView(getContext());
|
||||
mSearchView.setOnQueryTextListener(this);
|
||||
mSearchView.setOnCloseListener(this);
|
||||
mSearchView.setIconifiedByDefault(true);
|
||||
|
||||
menu.add(Menu.NONE, MENU_SEARCH_APPS, Menu.NONE, R.string.search_settings)
|
||||
.setIcon(R.drawable.ic_find_in_page_24px)
|
||||
.setActionView(mSearchView)
|
||||
.setOnActionExpandListener(this)
|
||||
.setShowAsAction(SHOW_AS_ACTION_ALWAYS | SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW);
|
||||
menu.add(Menu.NONE, MENU_SHOW_SYSTEM, Menu.NONE,
|
||||
mShowSystem ? R.string.menu_hide_system : R.string.menu_show_system);
|
||||
super.onCreateOptionsMenu(menu, inflater);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
if (item.getItemId() == MENU_SHOW_SYSTEM) {
|
||||
mShowSystem = !mShowSystem;
|
||||
item.setTitle(mShowSystem ? R.string.menu_hide_system : R.string.menu_show_system);
|
||||
use(QuarantinedAppsScreenController.class).setFilter(mCustomFilter);
|
||||
use(QuarantinedAppsScreenController.class).rebuild();
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onQueryTextChange(String newText) {
|
||||
mCurQuery = !TextUtils.isEmpty(newText) ? newText : null;
|
||||
use(QuarantinedAppsScreenController.class).rebuild();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onQueryTextSubmit(String query) {
|
||||
// Don't care about this.
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onClose() {
|
||||
if (!TextUtils.isEmpty(mSearchView.getQuery())) {
|
||||
mSearchView.setQuery(null, true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public final AppFilter mCustomFilter = new AppFilter() {
|
||||
@Override
|
||||
public void init() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean filterApp(ApplicationsState.AppEntry entry) {
|
||||
final AppFilter defaultFilter = mShowSystem ? ApplicationsState.FILTER_ALL_ENABLED
|
||||
: ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER;
|
||||
return defaultFilter.filterApp(entry) && (mCurQuery == null
|
||||
|| entry.label.toLowerCase().contains(mCurQuery.toLowerCase()));
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public boolean onMenuItemActionExpand(MenuItem item) {
|
||||
final AppBarLayout mAppBarLayout = getActivity().findViewById(R.id.app_bar);
|
||||
// To prevent a large space on tool bar.
|
||||
mAppBarLayout.setExpanded(false /*expanded*/, false /*animate*/);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMenuItemActionCollapse(MenuItem item) {
|
||||
final AppBarLayout mAppBarLayout = getActivity().findViewById(R.id.app_bar);
|
||||
// To prevent a large space on tool bar.
|
||||
mAppBarLayout.setExpanded(false /*expanded*/, false /*animate*/);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPreferenceScreenResId() {
|
||||
return R.xml.quarantined_apps;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
outState.putBoolean(EXTRA_SHOW_SYSTEM, mShowSystem);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getLogTag() {
|
||||
return TAG;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMetricsCategory() {
|
||||
return SettingsEnums.QUARANTINED_APPS_DEV_CONTROL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
AppIconCacheManager.getInstance().release();
|
||||
}
|
||||
|
||||
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
|
||||
new BaseSearchIndexProvider(R.xml.quarantined_apps);
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.development.quarantine;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.Flags;
|
||||
|
||||
import com.android.settings.core.BasePreferenceController;
|
||||
|
||||
public class QuarantinedAppsPreferenceController extends BasePreferenceController {
|
||||
public QuarantinedAppsPreferenceController(Context context, String preferenceKey) {
|
||||
super(context, preferenceKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAvailabilityStatus() {
|
||||
return Flags.quarantinedEnabled() ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
|
||||
}
|
||||
}
|
||||
@@ -1,232 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.development.quarantine;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.content.pm.Flags;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.SuspendDialogInfo;
|
||||
import android.os.UserHandle;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.applications.AppStateBaseBridge;
|
||||
import com.android.settings.core.BasePreferenceController;
|
||||
import com.android.settingslib.applications.AppUtils;
|
||||
import com.android.settingslib.applications.ApplicationsState;
|
||||
import com.android.settingslib.applications.ApplicationsState.AppEntry;
|
||||
import com.android.settingslib.applications.ApplicationsState.AppFilter;
|
||||
import com.android.settingslib.core.lifecycle.Lifecycle;
|
||||
import com.android.settingslib.core.lifecycle.LifecycleObserver;
|
||||
import com.android.settingslib.core.lifecycle.events.OnDestroy;
|
||||
import com.android.settingslib.core.lifecycle.events.OnStart;
|
||||
import com.android.settingslib.core.lifecycle.events.OnStop;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
public class QuarantinedAppsScreenController extends BasePreferenceController implements
|
||||
LifecycleObserver, OnStart, OnStop, OnDestroy,
|
||||
ApplicationsState.Callbacks, Preference.OnPreferenceChangeListener,
|
||||
AppStateBaseBridge.Callback {
|
||||
private final ApplicationsState mApplicationsState;
|
||||
private final QuarantinedAppStateBridge mQuarantinedAppStateBridge;
|
||||
private ApplicationsState.Session mSession;
|
||||
private PreferenceScreen mScreen;
|
||||
private AppFilter mFilter;
|
||||
private boolean mExtraLoaded;
|
||||
|
||||
public QuarantinedAppsScreenController(Context context, String preferenceKey) {
|
||||
super(context, preferenceKey);
|
||||
mApplicationsState = ApplicationsState.getInstance(
|
||||
(Application) context.getApplicationContext());
|
||||
mQuarantinedAppStateBridge = new QuarantinedAppStateBridge(context,
|
||||
mApplicationsState, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
mQuarantinedAppStateBridge.resume(true /* forceLoadAllApps */);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
mQuarantinedAppStateBridge.pause();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
mQuarantinedAppStateBridge.release();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void displayPreference(PreferenceScreen screen) {
|
||||
super.displayPreference(screen);
|
||||
mScreen = screen;
|
||||
}
|
||||
|
||||
public void setFilter(AppFilter filter) {
|
||||
mFilter = filter;
|
||||
}
|
||||
|
||||
public void setSession(Lifecycle lifecycle) {
|
||||
mSession = mApplicationsState.newSession(this, lifecycle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onExtraInfoUpdated() {
|
||||
mExtraLoaded = true;
|
||||
rebuild();
|
||||
}
|
||||
|
||||
public void rebuild() {
|
||||
if (!mExtraLoaded || mSession == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final ArrayList<AppEntry> apps = mSession.rebuild(mFilter,
|
||||
ApplicationsState.ALPHA_COMPARATOR);
|
||||
if (apps != null) {
|
||||
onRebuildComplete(apps);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRebuildComplete(ArrayList<AppEntry> apps) {
|
||||
if (apps == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Preload top visible icons of app list.
|
||||
AppUtils.preloadTopIcons(mContext, apps,
|
||||
mContext.getResources().getInteger(R.integer.config_num_visible_app_icons));
|
||||
|
||||
// Create apps key set for removing useless preferences
|
||||
final Set<String> appsKeySet = new TreeSet<>();
|
||||
// Add or update preferences
|
||||
final int count = apps.size();
|
||||
for (int i = 0; i < count; i++) {
|
||||
final AppEntry entry = apps.get(i);
|
||||
if (!shouldAddPreference(entry)) {
|
||||
continue;
|
||||
}
|
||||
final String prefkey = QuarantinedAppPreference.generateKey(entry);
|
||||
appsKeySet.add(prefkey);
|
||||
QuarantinedAppPreference preference = mScreen.findPreference(prefkey);
|
||||
if (preference == null) {
|
||||
preference = new QuarantinedAppPreference(mScreen.getContext(), entry);
|
||||
preference.setOnPreferenceChangeListener(this);
|
||||
mScreen.addPreference(preference);
|
||||
} else {
|
||||
preference.updateState();
|
||||
}
|
||||
preference.setOrder(i);
|
||||
}
|
||||
|
||||
// Remove useless preferences
|
||||
removeUselessPrefs(appsKeySet);
|
||||
}
|
||||
|
||||
private void removeUselessPrefs(final Set<String> appsKeySet) {
|
||||
final int prefCount = mScreen.getPreferenceCount();
|
||||
String prefKey;
|
||||
if (prefCount > 0) {
|
||||
for (int i = prefCount - 1; i >= 0; i--) {
|
||||
final Preference pref = mScreen.getPreference(i);
|
||||
prefKey = pref.getKey();
|
||||
if (!appsKeySet.isEmpty() && appsKeySet.contains(prefKey)) {
|
||||
continue;
|
||||
}
|
||||
mScreen.removePreference(pref);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
static boolean shouldAddPreference(AppEntry app) {
|
||||
return app != null && UserHandle.isApp(app.info.uid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
if (preference instanceof QuarantinedAppPreference) {
|
||||
final QuarantinedAppPreference quarantinedPreference =
|
||||
(QuarantinedAppPreference) preference;
|
||||
final boolean quarantined = newValue == Boolean.TRUE;
|
||||
setPackageQuarantined(quarantinedPreference.getEntry().info.packageName,
|
||||
quarantinedPreference.getEntry().info.uid, quarantined);
|
||||
quarantinedPreference.getEntry().extraInfo = quarantined;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void setPackageQuarantined(String pkg, int uid, boolean quarantined) {
|
||||
final PackageManager pm = mContext.createContextAsUser(
|
||||
UserHandle.getUserHandleForUid(uid), 0).getPackageManager();
|
||||
final SuspendDialogInfo dialogInfo;
|
||||
if (quarantined) {
|
||||
dialogInfo = new SuspendDialogInfo.Builder()
|
||||
.setNeutralButtonText(R.string.unquarantine_app_button)
|
||||
.setNeutralButtonAction(SuspendDialogInfo.BUTTON_ACTION_UNSUSPEND)
|
||||
.build();
|
||||
} else {
|
||||
dialogInfo = null;
|
||||
}
|
||||
pm.setPackagesSuspended(new String[] {pkg}, quarantined, null /* appExtras */,
|
||||
null /* launcherExtras */, dialogInfo,
|
||||
PackageManager.FLAG_SUSPEND_QUARANTINED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAvailabilityStatus() {
|
||||
return Flags.quarantinedEnabled() ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRunningStateChanged(boolean running) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPackageListChanged() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPackageIconChanged() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPackageSizeChanged(String packageName) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAllSizesComputed() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLauncherInfoChanged() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadEntriesCompleted() {
|
||||
}
|
||||
}
|
||||
@@ -257,6 +257,7 @@ public class EnterprisePrivacyFeatureProviderImpl implements EnterprisePrivacyFe
|
||||
private boolean shouldSkipProfile(UserInfo userInfo) {
|
||||
return android.os.Flags.allowPrivateProfile()
|
||||
&& android.multiuser.Flags.handleInterleavedSettingsForPrivateSpace()
|
||||
&& android.multiuser.Flags.enablePrivateSpaceFeatures()
|
||||
&& userInfo.isQuietModeEnabled()
|
||||
&& mUm.getUserProperties(userInfo.getUserHandle()).getShowInQuietMode()
|
||||
== UserProperties.SHOW_IN_QUIET_MODE_HIDDEN;
|
||||
|
||||
@@ -21,7 +21,6 @@ import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.telephony.SubscriptionManager
|
||||
import android.util.Log
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
@@ -45,7 +44,6 @@ import androidx.compose.material3.rememberModalBottomSheetState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
@@ -67,7 +65,6 @@ import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle
|
||||
import com.android.settingslib.spa.widget.dialog.AlertDialogButton
|
||||
import com.android.settingslib.spa.widget.dialog.getDialogWidth
|
||||
import com.android.settingslib.spa.widget.dialog.rememberAlertDialogPresenter
|
||||
import com.android.settingslib.spa.widget.editor.SettingsOutlinedTextField
|
||||
import com.android.settingslib.spa.widget.ui.SettingsTitle
|
||||
import com.android.settingslib.spaprivileged.framework.common.userManager
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
@@ -83,6 +80,8 @@ class SimOnboardingActivity : SpaBaseDialogActivity() {
|
||||
lateinit var showBottomSheet: MutableState<Boolean>
|
||||
lateinit var showError: MutableState<ErrorType>
|
||||
lateinit var showProgressDialog: MutableState<Boolean>
|
||||
lateinit var showDsdsProgressDialog: MutableState<Boolean>
|
||||
lateinit var showRestartDialog: MutableState<Boolean>
|
||||
|
||||
private var switchToEuiccSubscriptionSidecar: SwitchToEuiccSubscriptionSidecar? = null
|
||||
private var switchToRemovableSlotSidecar: SwitchToRemovableSlotSidecar? = null
|
||||
@@ -132,6 +131,12 @@ class SimOnboardingActivity : SpaBaseDialogActivity() {
|
||||
setProgressDialog(false)
|
||||
}
|
||||
|
||||
CallbackType.CALLBACK_ENABLE_DSDS-> {
|
||||
scope.launch {
|
||||
onboardingService.startEnableDsds(this@SimOnboardingActivity)
|
||||
}
|
||||
}
|
||||
|
||||
CallbackType.CALLBACK_ONBOARDING_COMPLETE -> {
|
||||
showBottomSheet.value = false
|
||||
setProgressDialog(true)
|
||||
@@ -179,12 +184,14 @@ class SimOnboardingActivity : SpaBaseDialogActivity() {
|
||||
showBottomSheet = remember { mutableStateOf(false) }
|
||||
showError = remember { mutableStateOf(ErrorType.ERROR_NONE) }
|
||||
showProgressDialog = remember { mutableStateOf(false) }
|
||||
showDsdsProgressDialog = remember { mutableStateOf(false) }
|
||||
showRestartDialog = remember { mutableStateOf(false) }
|
||||
scope = rememberCoroutineScope()
|
||||
|
||||
registerSidecarReceiverFlow()
|
||||
|
||||
ErrorDialogImpl()
|
||||
|
||||
RestartDialogImpl()
|
||||
LaunchedEffect(Unit) {
|
||||
if (onboardingService.activeSubInfoList.isNotEmpty()) {
|
||||
showBottomSheet.value = true
|
||||
@@ -196,29 +203,76 @@ class SimOnboardingActivity : SpaBaseDialogActivity() {
|
||||
BottomSheetImpl(
|
||||
sheetState = sheetState,
|
||||
nextAction = {
|
||||
// TODO: if the phone is SS mode and the isDsdsConditionSatisfied is true, then
|
||||
// enable the DSDS mode.
|
||||
// case#1: the device need the reboot after enabling DSDS. Showing the confirm
|
||||
// dialog to user whether reboot device or not.
|
||||
// case#2: The device don't need the reboot. Enabling DSDS and then showing
|
||||
// the SIM onboarding UI.
|
||||
|
||||
// case#2
|
||||
val route = getRoute(onboardingService.targetSubId)
|
||||
startSpaActivity(route)
|
||||
if (onboardingService.isDsdsConditionSatisfied()) {
|
||||
// TODO: if the phone is SS mode and the isDsdsConditionSatisfied is true,
|
||||
// then enable the DSDS mode.
|
||||
// case#1: the device need the reboot after enabling DSDS. Showing the
|
||||
// confirm dialog to user whether reboot device or not.
|
||||
// case#2: The device don't need the reboot. Enabling DSDS and then showing
|
||||
// the SIM onboarding UI.
|
||||
if (onboardingService.doesSwitchMultiSimConfigTriggerReboot) {
|
||||
// case#1
|
||||
Log.d(TAG, "Device does not support reboot free DSDS.")
|
||||
showRestartDialog.value = true
|
||||
} else {
|
||||
// case#2
|
||||
Log.d(TAG, "Enable DSDS mode")
|
||||
showDsdsProgressDialog.value = true
|
||||
enableMultiSimSidecar?.run(SimOnboardingService.NUM_OF_SIMS_FOR_DSDS)
|
||||
}
|
||||
} else {
|
||||
startSimOnboardingProvider()
|
||||
}
|
||||
},
|
||||
cancelAction = { finish() },
|
||||
)
|
||||
}
|
||||
|
||||
if(showProgressDialog.value) {
|
||||
ProgressDialogImpl()
|
||||
if (showProgressDialog.value) {
|
||||
ProgressDialogImpl(
|
||||
stringResource(
|
||||
R.string.sim_onboarding_progressbar_turning_sim_on,
|
||||
onboardingService.targetSubInfo?.displayName ?: ""
|
||||
)
|
||||
)
|
||||
}
|
||||
if (showDsdsProgressDialog.value) {
|
||||
ProgressDialogImpl(
|
||||
stringResource(R.string.sim_action_enabling_sim_without_carrier_name)
|
||||
)
|
||||
}
|
||||
}
|
||||
@Composable
|
||||
private fun RestartDialogImpl() {
|
||||
val restartDialogPresenter = rememberAlertDialogPresenter(
|
||||
confirmButton = AlertDialogButton(
|
||||
stringResource(R.string.sim_action_reboot)
|
||||
) {
|
||||
callbackListener(CallbackType.CALLBACK_ENABLE_DSDS)
|
||||
},
|
||||
dismissButton = AlertDialogButton(
|
||||
stringResource(
|
||||
R.string.sim_action_restart_dialog_cancel,
|
||||
onboardingService.targetSubInfo?.displayName ?: "")
|
||||
) {
|
||||
callbackListener(CallbackType.CALLBACK_ONBOARDING_COMPLETE)
|
||||
},
|
||||
title = stringResource(R.string.sim_action_restart_dialog_title),
|
||||
text = {
|
||||
Text(stringResource(R.string.sim_action_restart_dialog_msg))
|
||||
},
|
||||
)
|
||||
|
||||
if(showRestartDialog.value){
|
||||
LaunchedEffect(Unit) {
|
||||
restartDialogPresenter.open()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun ProgressDialogImpl() {
|
||||
fun ProgressDialogImpl(title: String) {
|
||||
// TODO: Create the SPA's ProgressDialog and using SPA's widget
|
||||
BasicAlertDialog(
|
||||
onDismissRequest = {},
|
||||
@@ -232,19 +286,14 @@ class SimOnboardingActivity : SpaBaseDialogActivity() {
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(SettingsDimension.itemPaddingStart),
|
||||
.fillMaxWidth()
|
||||
.padding(SettingsDimension.itemPaddingStart),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
CircularProgressIndicator()
|
||||
Column(modifier = Modifier
|
||||
.padding(start = SettingsDimension.itemPaddingStart)) {
|
||||
SettingsTitle(
|
||||
stringResource(
|
||||
R.string.sim_onboarding_progressbar_turning_sim_on,
|
||||
onboardingService.targetSubInfo?.displayName ?: ""
|
||||
)
|
||||
)
|
||||
SettingsTitle(title)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -329,7 +378,7 @@ class SimOnboardingActivity : SpaBaseDialogActivity() {
|
||||
Log.e(TAG, "Error while sidecarReceiverFlow", e)
|
||||
}.conflate()
|
||||
|
||||
fun startSimSwitching(){
|
||||
fun startSimSwitching() {
|
||||
Log.d(TAG, "startSimSwitching:")
|
||||
|
||||
var targetSubInfo = onboardingService.targetSubInfo
|
||||
@@ -376,8 +425,6 @@ class SimOnboardingActivity : SpaBaseDialogActivity() {
|
||||
switchToEuiccSubscriptionSidecar!!.reset()
|
||||
showError.value = ErrorType.ERROR_EUICC_SLOT
|
||||
callbackListener(CallbackType.CALLBACK_ERROR)
|
||||
// TODO: showErrorDialog and using privileged_action_disable_fail_title and
|
||||
// privileged_action_disable_fail_text
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -396,18 +443,19 @@ class SimOnboardingActivity : SpaBaseDialogActivity() {
|
||||
switchToRemovableSlotSidecar!!.reset()
|
||||
showError.value = ErrorType.ERROR_REMOVABLE_SLOT
|
||||
callbackListener(CallbackType.CALLBACK_ERROR)
|
||||
// TODO: showErrorDialog and using sim_action_enable_sim_fail_title and
|
||||
// sim_action_enable_sim_fail_text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun handleEnableMultiSimSidecarStateChange() {
|
||||
showDsdsProgressDialog.value = false
|
||||
when (enableMultiSimSidecar!!.state) {
|
||||
SidecarFragment.State.SUCCESS -> {
|
||||
enableMultiSimSidecar!!.reset()
|
||||
Log.i(TAG, "Successfully switched to DSDS without reboot.")
|
||||
handleEnableSubscriptionAfterEnablingDsds()
|
||||
// refresh data
|
||||
initServiceData(this, onboardingService.targetSubId, callbackListener)
|
||||
startSimOnboardingProvider()
|
||||
}
|
||||
|
||||
SidecarFragment.State.ERROR -> {
|
||||
@@ -415,34 +463,14 @@ class SimOnboardingActivity : SpaBaseDialogActivity() {
|
||||
Log.i(TAG, "Failed to switch to DSDS without rebooting.")
|
||||
showError.value = ErrorType.ERROR_ENABLE_DSDS
|
||||
callbackListener(CallbackType.CALLBACK_ERROR)
|
||||
// TODO: showErrorDialog and using dsds_activation_failure_title and
|
||||
// dsds_activation_failure_body_msg2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun handleEnableSubscriptionAfterEnablingDsds() {
|
||||
var targetSubInfo = onboardingService.targetSubInfo
|
||||
if (targetSubInfo?.isEmbedded == true) {
|
||||
Log.i(TAG,
|
||||
"DSDS enabled, start to enable profile: " + targetSubInfo.getSubscriptionId()
|
||||
)
|
||||
// For eSIM operations, we simply switch to the selected eSIM profile.
|
||||
switchToEuiccSubscriptionSidecar!!.run(
|
||||
targetSubInfo.subscriptionId,
|
||||
UiccSlotUtil.INVALID_PORT_ID,
|
||||
null
|
||||
)
|
||||
return
|
||||
}
|
||||
Log.i(TAG, "DSDS enabled, start to enable pSIM profile.")
|
||||
onboardingService.handleTogglePsimAction()
|
||||
callbackListener(CallbackType.CALLBACK_FINISH)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun BottomSheetBody(nextAction: () -> Unit) {
|
||||
Column(horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier.padding(bottom = SettingsDimension.itemPaddingVertical)) {
|
||||
Column(horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier.padding(bottom = SettingsDimension.itemPaddingVertical)) {
|
||||
Icon(
|
||||
imageVector = Icons.Outlined.SignalCellularAlt,
|
||||
contentDescription = null,
|
||||
@@ -497,6 +525,11 @@ class SimOnboardingActivity : SpaBaseDialogActivity() {
|
||||
onboardingService.initData(targetSubId, context,callback)
|
||||
}
|
||||
|
||||
private fun startSimOnboardingProvider() {
|
||||
val route = getRoute(onboardingService.targetSubId)
|
||||
startSpaActivity(route)
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun startSimOnboardingActivity(
|
||||
@@ -523,9 +556,10 @@ class SimOnboardingActivity : SpaBaseDialogActivity() {
|
||||
enum class CallbackType(val value:Int){
|
||||
CALLBACK_ERROR(-1),
|
||||
CALLBACK_ONBOARDING_COMPLETE(1),
|
||||
CALLBACK_SETUP_NAME(2),
|
||||
CALLBACK_SETUP_PRIMARY_SIM(3),
|
||||
CALLBACK_FINISH(4)
|
||||
CALLBACK_ENABLE_DSDS(2),
|
||||
CALLBACK_SETUP_NAME(3),
|
||||
CALLBACK_SETUP_PRIMARY_SIM(4),
|
||||
CALLBACK_FINISH(5)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.sim.SimActivationNotifier
|
||||
import com.android.settings.spa.network.setAutomaticData
|
||||
import com.android.settings.spa.network.setDefaultData
|
||||
import com.android.settings.spa.network.setDefaultSms
|
||||
@@ -32,9 +33,6 @@ import com.android.settingslib.utils.ThreadUtils
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
private const val TAG = "SimOnboardingService"
|
||||
private const val INVALID = SubscriptionManager.INVALID_SUBSCRIPTION_ID
|
||||
|
||||
class SimOnboardingService {
|
||||
var subscriptionManager:SubscriptionManager? = null
|
||||
var telephonyManager:TelephonyManager? = null
|
||||
@@ -70,7 +68,7 @@ class SimOnboardingService {
|
||||
}
|
||||
return uiccCardInfoList.any { it.isMultipleEnabledProfilesSupported }
|
||||
}
|
||||
var isRemovableSimEnabled: Boolean = false
|
||||
var isRemovablePsimProfileEnabled: Boolean = false
|
||||
get() {
|
||||
if(slotInfoList.isEmpty()) {
|
||||
Log.w(TAG, "UICC Slot info list is empty.")
|
||||
@@ -78,7 +76,11 @@ class SimOnboardingService {
|
||||
}
|
||||
return UiccSlotUtil.isRemovableSimEnabled(slotInfoList)
|
||||
}
|
||||
|
||||
var isEsimProfileEnabled: Boolean = false
|
||||
get() {
|
||||
activeSubInfoList.stream().anyMatch { it.isEmbedded }
|
||||
return false
|
||||
}
|
||||
var doesTargetSimHaveEsimOperation = false
|
||||
get() {
|
||||
return targetSubInfo?.isEmbedded ?: false
|
||||
@@ -109,6 +111,19 @@ class SimOnboardingService {
|
||||
}
|
||||
return getActiveModemCount != 0 && activeSubInfoList.size == getActiveModemCount
|
||||
}
|
||||
var isMultiSimEnabled = false
|
||||
get() {
|
||||
return getActiveModemCount > 1
|
||||
}
|
||||
var isMultiSimSupported = false
|
||||
get() {
|
||||
return telephonyManager?.isMultiSimSupported == TelephonyManager.MULTISIM_ALLOWED
|
||||
}
|
||||
|
||||
var doesSwitchMultiSimConfigTriggerReboot = false
|
||||
get() {
|
||||
return telephonyManager?.doesSwitchMultiSimConfigTriggerReboot() ?: false
|
||||
}
|
||||
|
||||
fun isValid(): Boolean {
|
||||
return targetSubId != INVALID
|
||||
@@ -161,9 +176,10 @@ class SimOnboardingService {
|
||||
targetPrimarySimCalls = SubscriptionManager.getDefaultVoiceSubscriptionId()
|
||||
targetPrimarySimTexts = SubscriptionManager.getDefaultSmsSubscriptionId()
|
||||
targetPrimarySimMobileData = SubscriptionManager.getDefaultDataSubscriptionId()
|
||||
|
||||
Log.d(
|
||||
TAG,"doesTargetSimHaveEsimOperation: $doesTargetSimHaveEsimOperation" +
|
||||
", isRemovableSimEnabled: $isRemovableSimEnabled" +
|
||||
", isRemovableSimEnabled: $isRemovablePsimProfileEnabled" +
|
||||
", isMultipleEnabledProfilesSupported: $isMultipleEnabledProfilesSupported" +
|
||||
", targetPrimarySimCalls: $targetPrimarySimCalls" +
|
||||
", targetPrimarySimTexts: $targetPrimarySimTexts" +
|
||||
@@ -261,6 +277,45 @@ class SimOnboardingService {
|
||||
}
|
||||
}
|
||||
|
||||
fun isDsdsConditionSatisfied(): Boolean {
|
||||
if (isMultiSimEnabled) {
|
||||
Log.d(
|
||||
TAG,
|
||||
"DSDS is already enabled. Condition not satisfied."
|
||||
)
|
||||
return false
|
||||
}
|
||||
if (!isMultiSimSupported) {
|
||||
Log.d(TAG, "Hardware does not support DSDS.")
|
||||
return false
|
||||
}
|
||||
val isActiveSim = activeSubInfoList.isNotEmpty()
|
||||
if (isMultipleEnabledProfilesSupported && isActiveSim) {
|
||||
Log.d(TAG,
|
||||
"Device supports MEP and eSIM operation and eSIM profile is enabled."
|
||||
+ " DSDS condition satisfied."
|
||||
)
|
||||
return true
|
||||
}
|
||||
|
||||
if (doesTargetSimHaveEsimOperation && isRemovablePsimProfileEnabled) {
|
||||
Log.d(TAG,
|
||||
"eSIM operation and removable PSIM is enabled. DSDS condition satisfied."
|
||||
)
|
||||
return true
|
||||
}
|
||||
|
||||
if (!doesTargetSimHaveEsimOperation && isEsimProfileEnabled) {
|
||||
Log.d(TAG,
|
||||
"Removable SIM operation and eSIM profile is enabled. DSDS condition"
|
||||
+ " satisfied."
|
||||
)
|
||||
return true
|
||||
}
|
||||
Log.d(TAG, "DSDS condition not satisfied.")
|
||||
return false
|
||||
}
|
||||
|
||||
fun startActivatingSim(){
|
||||
// TODO: start to activate sim
|
||||
callback(CallbackType.CALLBACK_FINISH)
|
||||
@@ -281,30 +336,50 @@ class SimOnboardingService {
|
||||
|
||||
suspend fun startSetupPrimarySim(context: Context) {
|
||||
withContext(Dispatchers.Default) {
|
||||
setDefaultVoice(subscriptionManager,targetPrimarySimCalls)
|
||||
setDefaultSms(subscriptionManager,targetPrimarySimTexts)
|
||||
setDefaultData(
|
||||
context,
|
||||
subscriptionManager,
|
||||
null,
|
||||
targetPrimarySimMobileData
|
||||
)
|
||||
if (SubscriptionUtil.getActiveSubscriptions(subscriptionManager).size <= 1) {
|
||||
Log.d(TAG,
|
||||
"startSetupPrimarySim: number of active subscriptionInfo is less than 2"
|
||||
)
|
||||
} else {
|
||||
setDefaultVoice(subscriptionManager, targetPrimarySimCalls)
|
||||
setDefaultSms(subscriptionManager, targetPrimarySimTexts)
|
||||
setDefaultData(
|
||||
context,
|
||||
subscriptionManager,
|
||||
null,
|
||||
targetPrimarySimMobileData
|
||||
)
|
||||
|
||||
var nonDds = targetNonDds
|
||||
Log.d(
|
||||
TAG,
|
||||
"setAutomaticData: targetNonDds: $nonDds," +
|
||||
" targetPrimarySimAutoDataSwitch: $targetPrimarySimAutoDataSwitch"
|
||||
)
|
||||
if (nonDds != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
|
||||
val telephonyManagerForNonDds: TelephonyManager? =
|
||||
context.getSystemService(TelephonyManager::class.java)
|
||||
?.createForSubscriptionId(nonDds)
|
||||
setAutomaticData(telephonyManagerForNonDds, targetPrimarySimAutoDataSwitch)
|
||||
var nonDds = targetNonDds
|
||||
Log.d(
|
||||
TAG,
|
||||
"setAutomaticData: targetNonDds: $nonDds," +
|
||||
" targetPrimarySimAutoDataSwitch: $targetPrimarySimAutoDataSwitch"
|
||||
)
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun startEnableDsds(context: Context) {
|
||||
withContext(Dispatchers.Default) {
|
||||
Log.d(TAG, "User confirmed reboot to enable DSDS.")
|
||||
SimActivationNotifier.setShowSimSettingsNotification(context, true)
|
||||
telephonyManager?.switchMultiSimConfig(NUM_OF_SIMS_FOR_DSDS)
|
||||
callback(CallbackType.CALLBACK_FINISH)
|
||||
}
|
||||
}
|
||||
|
||||
companion object{
|
||||
private const val TAG = "SimOnboardingService"
|
||||
private const val INVALID = SubscriptionManager.INVALID_SUBSCRIPTION_ID
|
||||
const val NUM_OF_SIMS_FOR_DSDS = 2
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,7 @@ import android.app.Application
|
||||
import android.telephony.SubscriptionManager
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.android.settings.network.telephony.getSelectableSubscriptionInfoList
|
||||
import com.android.settings.network.telephony.subscriptionsChangedFlow
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
@@ -41,10 +42,10 @@ class SubscriptionInfoListViewModel(application: Application) : AndroidViewModel
|
||||
}.stateIn(scope, SharingStarted.Eagerly, initialValue = emptyList())
|
||||
|
||||
/**
|
||||
* Getting the Selectable SubscriptionInfo List from the SubscriptionManager's
|
||||
* Getting the Selectable SubscriptionInfo List from the SubscriptionRepository's
|
||||
* getAvailableSubscriptionInfoList
|
||||
*/
|
||||
val selectableSubscriptionInfoListFlow = application.subscriptionsChangedFlow().map {
|
||||
SubscriptionUtil.getSelectableSubscriptionInfoList(application)
|
||||
application.getSelectableSubscriptionInfoList()
|
||||
}.stateIn(scope, SharingStarted.Eagerly, initialValue = emptyList())
|
||||
}
|
||||
|
||||
@@ -50,12 +50,12 @@ import com.android.settings.network.helper.SelectableSubscriptions;
|
||||
import com.android.settings.network.helper.SubscriptionAnnotation;
|
||||
import com.android.settings.network.telephony.DeleteEuiccSubscriptionDialogActivity;
|
||||
import com.android.settings.network.telephony.EuiccRacConnectivityDialogActivity;
|
||||
import com.android.settings.network.telephony.SubscriptionRepositoryKt;
|
||||
import com.android.settings.network.telephony.ToggleSubscriptionDialogActivity;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
@@ -499,40 +499,7 @@ public class SubscriptionUtil {
|
||||
* @return list of user selectable subscriptions.
|
||||
*/
|
||||
public static List<SubscriptionInfo> getSelectableSubscriptionInfoList(Context context) {
|
||||
SubscriptionManager subManager = context.getSystemService(SubscriptionManager.class);
|
||||
List<SubscriptionInfo> availableList = subManager.getAvailableSubscriptionInfoList();
|
||||
if (availableList == null) {
|
||||
return null;
|
||||
} else {
|
||||
// Multiple subscriptions in a group should only have one representative.
|
||||
// It should be the current active primary subscription if any, or any
|
||||
// primary subscription.
|
||||
List<SubscriptionInfo> selectableList = new ArrayList<>();
|
||||
Map<ParcelUuid, SubscriptionInfo> groupMap = new HashMap<>();
|
||||
|
||||
for (SubscriptionInfo info : availableList) {
|
||||
// Opportunistic subscriptions are considered invisible
|
||||
// to users so they should never be returned.
|
||||
if (!isSubscriptionVisible(subManager, context, info)) continue;
|
||||
|
||||
ParcelUuid groupUuid = info.getGroupUuid();
|
||||
if (groupUuid == null) {
|
||||
// Doesn't belong to any group. Add in the list.
|
||||
selectableList.add(info);
|
||||
} else if (!groupMap.containsKey(groupUuid)
|
||||
|| (groupMap.get(groupUuid).getSimSlotIndex() == INVALID_SIM_SLOT_INDEX
|
||||
&& info.getSimSlotIndex() != INVALID_SIM_SLOT_INDEX)) {
|
||||
// If it belongs to a group that has never been recorded or it's the current
|
||||
// active subscription, add it in the list.
|
||||
selectableList.remove(groupMap.get(groupUuid));
|
||||
selectableList.add(info);
|
||||
groupMap.put(groupUuid, info);
|
||||
}
|
||||
|
||||
}
|
||||
Log.d(TAG, "getSelectableSubscriptionInfoList: " + selectableList);
|
||||
return selectableList;
|
||||
}
|
||||
return SubscriptionRepositoryKt.getSelectableSubscriptionInfoList(context);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -82,7 +82,7 @@ object CellInfoUtil {
|
||||
*/
|
||||
@JvmStatic
|
||||
fun cellInfoListToString(cellInfos: List<CellInfo>): String =
|
||||
cellInfos.joinToString { cellInfo -> cellInfo.readableString() }
|
||||
cellInfos.joinToString(System.lineSeparator()) { cellInfo -> cellInfo.readableString() }
|
||||
|
||||
/**
|
||||
* Convert [CellInfo] to a readable string without sensitive info.
|
||||
|
||||
@@ -1,147 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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 android.telephony.TelephonyManager.CALL_STATE_IDLE;
|
||||
|
||||
import static androidx.lifecycle.Lifecycle.Event.ON_PAUSE;
|
||||
import static androidx.lifecycle.Lifecycle.Event.ON_RESUME;
|
||||
|
||||
import android.content.Context;
|
||||
import android.telephony.SubscriptionInfo;
|
||||
import android.telephony.SubscriptionManager;
|
||||
import android.telephony.TelephonyCallback;
|
||||
import android.telephony.TelephonyManager;
|
||||
|
||||
import androidx.lifecycle.LifecycleObserver;
|
||||
import androidx.lifecycle.OnLifecycleEvent;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
|
||||
import com.android.settings.core.BasePreferenceController;
|
||||
import com.android.settings.network.SubscriptionUtil;
|
||||
import com.android.settings.network.SubscriptionsChangeListener;
|
||||
import com.android.settings.widget.SettingsMainSwitchPreference;
|
||||
|
||||
/** This controls a switch to allow enabling/disabling a mobile network */
|
||||
public class MobileNetworkSwitchController extends BasePreferenceController implements
|
||||
SubscriptionsChangeListener.SubscriptionsChangeListenerClient, LifecycleObserver {
|
||||
private static final String TAG = "MobileNetworkSwitchCtrl";
|
||||
private SettingsMainSwitchPreference mSwitchBar;
|
||||
private int mSubId;
|
||||
private SubscriptionsChangeListener mChangeListener;
|
||||
private SubscriptionManager mSubscriptionManager;
|
||||
private TelephonyManager mTelephonyManager;
|
||||
private CallStateTelephonyCallback mCallStateCallback;
|
||||
|
||||
public MobileNetworkSwitchController(Context context, String preferenceKey) {
|
||||
super(context, preferenceKey);
|
||||
mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
|
||||
mSubscriptionManager = mContext.getSystemService(SubscriptionManager.class);
|
||||
mTelephonyManager = mContext.getSystemService(TelephonyManager.class);
|
||||
mChangeListener = new SubscriptionsChangeListener(context, this);
|
||||
}
|
||||
|
||||
void init(int subId) {
|
||||
mSubId = subId;
|
||||
mTelephonyManager = mTelephonyManager.createForSubscriptionId(mSubId);
|
||||
}
|
||||
|
||||
@OnLifecycleEvent(ON_RESUME)
|
||||
public void onResume() {
|
||||
mChangeListener.start();
|
||||
|
||||
if (mCallStateCallback == null) {
|
||||
mCallStateCallback = new CallStateTelephonyCallback();
|
||||
mTelephonyManager.registerTelephonyCallback(
|
||||
mContext.getMainExecutor(), mCallStateCallback);
|
||||
}
|
||||
update();
|
||||
}
|
||||
|
||||
@OnLifecycleEvent(ON_PAUSE)
|
||||
public void onPause() {
|
||||
if (mCallStateCallback != null) {
|
||||
mTelephonyManager.unregisterTelephonyCallback(mCallStateCallback);
|
||||
mCallStateCallback = null;
|
||||
}
|
||||
mChangeListener.stop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void displayPreference(PreferenceScreen screen) {
|
||||
super.displayPreference(screen);
|
||||
mSwitchBar = (SettingsMainSwitchPreference) screen.findPreference(mPreferenceKey);
|
||||
|
||||
mSwitchBar.setOnBeforeCheckedChangeListener((isChecked) -> {
|
||||
// TODO b/135222940: re-evaluate whether to use
|
||||
// mSubscriptionManager#isSubscriptionEnabled
|
||||
if (mSubscriptionManager.isActiveSubscriptionId(mSubId) != isChecked) {
|
||||
SubscriptionUtil.startToggleSubscriptionDialogActivity(mContext, mSubId, isChecked);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
update();
|
||||
}
|
||||
|
||||
private void update() {
|
||||
if (mSwitchBar == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
SubscriptionInfo subInfo = null;
|
||||
for (SubscriptionInfo info : SubscriptionUtil.getAvailableSubscriptions(mContext)) {
|
||||
if (info.getSubscriptionId() == mSubId) {
|
||||
subInfo = info;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// For eSIM, we always want the toggle. If telephony stack support disabling a pSIM
|
||||
// directly, we show the toggle.
|
||||
if (subInfo == null || (!subInfo.isEmbedded() && !SubscriptionUtil.showToggleForPhysicalSim(
|
||||
mSubscriptionManager))) {
|
||||
mSwitchBar.hide();
|
||||
} else {
|
||||
mSwitchBar.show();
|
||||
mSwitchBar.setCheckedInternal(mSubscriptionManager.isActiveSubscriptionId(mSubId));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAvailabilityStatus() {
|
||||
return AVAILABLE_UNSEARCHABLE;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAirplaneModeChanged(boolean airplaneModeEnabled) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSubscriptionsChanged() {
|
||||
update();
|
||||
}
|
||||
|
||||
private class CallStateTelephonyCallback extends TelephonyCallback implements
|
||||
TelephonyCallback.CallStateListener {
|
||||
@Override
|
||||
public void onCallStateChanged(int state) {
|
||||
mSwitchBar.setSwitchBarEnabled(state == CALL_STATE_IDLE);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* 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.SubscriptionManager
|
||||
import android.telephony.TelephonyManager
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.android.settings.R
|
||||
import com.android.settings.network.SubscriptionUtil
|
||||
import com.android.settings.spa.preference.ComposePreferenceController
|
||||
import com.android.settingslib.spa.widget.preference.MainSwitchPreference
|
||||
import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
||||
class MobileNetworkSwitchController @JvmOverloads constructor(
|
||||
context: Context,
|
||||
preferenceKey: String,
|
||||
private val subscriptionRepository: SubscriptionRepository = SubscriptionRepository(context),
|
||||
) : ComposePreferenceController(context, preferenceKey) {
|
||||
|
||||
private var subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID
|
||||
|
||||
override fun getAvailabilityStatus() = AVAILABLE_UNSEARCHABLE
|
||||
|
||||
fun init(subId: Int) {
|
||||
this.subId = subId
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun Content() {
|
||||
val context = LocalContext.current
|
||||
if (remember { !context.isVisible() }) return
|
||||
val checked by remember {
|
||||
subscriptionRepository.isSubscriptionEnabledFlow(subId)
|
||||
}.collectAsStateWithLifecycle(initialValue = null)
|
||||
val changeable by remember {
|
||||
context.callStateFlow(subId).map { it == TelephonyManager.CALL_STATE_IDLE }
|
||||
}.collectAsStateWithLifecycle(initialValue = true)
|
||||
MainSwitchPreference(model = object : SwitchPreferenceModel {
|
||||
override val title = stringResource(R.string.mobile_network_use_sim_on)
|
||||
override val changeable = { changeable }
|
||||
override val checked = { checked }
|
||||
override val onCheckedChange = { newChecked: Boolean ->
|
||||
SubscriptionUtil.startToggleSubscriptionDialogActivity(mContext, subId, newChecked)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun Context.isVisible(): Boolean {
|
||||
val subInfo = subscriptionRepository.getSelectableSubscriptionInfoList()
|
||||
.firstOrNull { it.subscriptionId == subId }
|
||||
?: return false
|
||||
// For eSIM, we always want the toggle. If telephony stack support disabling a pSIM
|
||||
// directly, we show the toggle.
|
||||
return subInfo.isEmbedded || requireSubscriptionManager().canDisablePhysicalSubscription()
|
||||
}
|
||||
}
|
||||
@@ -1,346 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.annotation.IntDef;
|
||||
import android.content.Context;
|
||||
import android.telephony.AccessNetworkConstants.AccessNetworkType;
|
||||
import android.telephony.CellInfo;
|
||||
import android.telephony.NetworkScan;
|
||||
import android.telephony.NetworkScanRequest;
|
||||
import android.telephony.PhoneCapability;
|
||||
import android.telephony.RadioAccessSpecifier;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.telephony.TelephonyScanManager;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import com.android.internal.telephony.CellNetworkScanResult;
|
||||
|
||||
import com.android.settings.R;
|
||||
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
import com.google.common.util.concurrent.Futures;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import com.google.common.util.concurrent.MoreExecutors;
|
||||
import com.google.common.util.concurrent.SettableFuture;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* A helper class that builds the common interface and performs the network scan for two different
|
||||
* network scan APIs.
|
||||
*/
|
||||
public class NetworkScanHelper {
|
||||
public static final String TAG = "NetworkScanHelper";
|
||||
|
||||
/**
|
||||
* Callbacks interface to inform the network scan results.
|
||||
*/
|
||||
public interface NetworkScanCallback {
|
||||
/**
|
||||
* Called when the results is returned from {@link TelephonyManager}. This method will be
|
||||
* called at least one time if there is no error occurred during the network scan.
|
||||
*
|
||||
* <p> This method can be called multiple times in one network scan, until
|
||||
* {@link #onComplete()} or {@link #onError(int)} is called.
|
||||
*
|
||||
* @param results
|
||||
*/
|
||||
void onResults(List<CellInfo> results);
|
||||
|
||||
/**
|
||||
* Called when the current network scan process is finished. No more
|
||||
* {@link #onResults(List)} will be called for the current network scan after this method is
|
||||
* called.
|
||||
*/
|
||||
void onComplete();
|
||||
|
||||
/**
|
||||
* Called when an error occurred during the network scan process.
|
||||
*
|
||||
* <p> There is no more result returned from {@link TelephonyManager} if an error occurred.
|
||||
*
|
||||
* <p> {@link #onComplete()} will not be called if an error occurred.
|
||||
*
|
||||
* @see {@link NetworkScan.ScanErrorCode}
|
||||
*/
|
||||
void onError(int errorCode);
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@IntDef({NETWORK_SCAN_TYPE_WAIT_FOR_ALL_RESULTS, NETWORK_SCAN_TYPE_INCREMENTAL_RESULTS})
|
||||
public @interface NetworkQueryType {}
|
||||
|
||||
/**
|
||||
* Performs the network scan using {@link TelephonyManager#getAvailableNetworks()}. The network
|
||||
* scan results won't be returned to the caller until the network scan is completed.
|
||||
*
|
||||
* <p> This is typically used when the modem doesn't support the new network scan api
|
||||
* {@link TelephonyManager#requestNetworkScan(
|
||||
* NetworkScanRequest, Executor, TelephonyScanManager.NetworkScanCallback)}.
|
||||
*/
|
||||
public static final int NETWORK_SCAN_TYPE_WAIT_FOR_ALL_RESULTS = 1;
|
||||
|
||||
/**
|
||||
* Performs the network scan using {@link TelephonyManager#requestNetworkScan(
|
||||
* NetworkScanRequest, Executor, TelephonyScanManager.NetworkScanCallback)} The network scan
|
||||
* results will be returned to the caller periodically in a small time window until the network
|
||||
* scan is completed. The complete results should be returned in the last called of
|
||||
* {@link NetworkScanCallback#onResults(List)}.
|
||||
*
|
||||
* <p> This is recommended to be used if modem supports the new network scan api
|
||||
* {@link TelephonyManager#requestNetworkScan(
|
||||
* NetworkScanRequest, Executor, TelephonyScanManager.NetworkScanCallback)}
|
||||
*/
|
||||
public static final int NETWORK_SCAN_TYPE_INCREMENTAL_RESULTS = 2;
|
||||
|
||||
/** The constants below are used in the async network scan. */
|
||||
@VisibleForTesting
|
||||
static final boolean INCREMENTAL_RESULTS = true;
|
||||
@VisibleForTesting
|
||||
static final int SEARCH_PERIODICITY_SEC = 5;
|
||||
@VisibleForTesting
|
||||
static final int MAX_SEARCH_TIME_SEC = 300;
|
||||
@VisibleForTesting
|
||||
static final int INCREMENTAL_RESULTS_PERIODICITY_SEC = 3;
|
||||
|
||||
private final NetworkScanCallback mNetworkScanCallback;
|
||||
private final TelephonyManager mTelephonyManager;
|
||||
private final TelephonyScanManager.NetworkScanCallback mInternalNetworkScanCallback;
|
||||
private final Executor mExecutor;
|
||||
|
||||
private int mMaxSearchTimeSec = MAX_SEARCH_TIME_SEC;
|
||||
private NetworkScan mNetworkScanRequester;
|
||||
|
||||
/** Callbacks for sync network scan */
|
||||
private ListenableFuture<List<CellInfo>> mNetworkScanFuture;
|
||||
|
||||
public NetworkScanHelper(TelephonyManager tm, NetworkScanCallback callback, Executor executor) {
|
||||
mTelephonyManager = tm;
|
||||
mNetworkScanCallback = callback;
|
||||
mInternalNetworkScanCallback = new NetworkScanCallbackImpl();
|
||||
mExecutor = executor;
|
||||
}
|
||||
|
||||
public NetworkScanHelper(Context context, TelephonyManager tm, NetworkScanCallback callback,
|
||||
Executor executor) {
|
||||
this(tm, callback, executor);
|
||||
mMaxSearchTimeSec = context.getResources().getInteger(
|
||||
R.integer.config_network_scan_helper_max_search_time_sec);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
NetworkScanRequest createNetworkScanForPreferredAccessNetworks() {
|
||||
long networkTypeBitmap3gpp = mTelephonyManager.getPreferredNetworkTypeBitmask()
|
||||
& TelephonyManager.NETWORK_STANDARDS_FAMILY_BITMASK_3GPP;
|
||||
|
||||
List<RadioAccessSpecifier> radioAccessSpecifiers = new ArrayList<>();
|
||||
// If the allowed network types are unknown or if they are of the right class, scan for
|
||||
// them; otherwise, skip them to save scan time and prevent users from being shown networks
|
||||
// that they can't connect to.
|
||||
if (networkTypeBitmap3gpp == 0
|
||||
|| (networkTypeBitmap3gpp & TelephonyManager.NETWORK_CLASS_BITMASK_2G) != 0) {
|
||||
radioAccessSpecifiers.add(
|
||||
new RadioAccessSpecifier(AccessNetworkType.GERAN, null, null));
|
||||
}
|
||||
if (networkTypeBitmap3gpp == 0
|
||||
|| (networkTypeBitmap3gpp & TelephonyManager.NETWORK_CLASS_BITMASK_3G) != 0) {
|
||||
radioAccessSpecifiers.add(
|
||||
new RadioAccessSpecifier(AccessNetworkType.UTRAN, null, null));
|
||||
}
|
||||
if (networkTypeBitmap3gpp == 0
|
||||
|| (networkTypeBitmap3gpp & TelephonyManager.NETWORK_CLASS_BITMASK_4G) != 0) {
|
||||
radioAccessSpecifiers.add(
|
||||
new RadioAccessSpecifier(AccessNetworkType.EUTRAN, null, null));
|
||||
}
|
||||
// If a device supports 5G stand-alone then the code below should be re-enabled; however
|
||||
// a device supporting only non-standalone mode cannot perform PLMN selection and camp on
|
||||
// a 5G network, which means that it shouldn't scan for 5G at the expense of battery as
|
||||
// part of the manual network selection process.
|
||||
//
|
||||
if (networkTypeBitmap3gpp == 0
|
||||
|| (hasNrSaCapability()
|
||||
&& (networkTypeBitmap3gpp & TelephonyManager.NETWORK_CLASS_BITMASK_5G) != 0)) {
|
||||
radioAccessSpecifiers.add(
|
||||
new RadioAccessSpecifier(AccessNetworkType.NGRAN, null, null));
|
||||
Log.d(TAG, "radioAccessSpecifiers add NGRAN.");
|
||||
}
|
||||
|
||||
return new NetworkScanRequest(
|
||||
NetworkScanRequest.SCAN_TYPE_ONE_SHOT,
|
||||
radioAccessSpecifiers.toArray(
|
||||
new RadioAccessSpecifier[radioAccessSpecifiers.size()]),
|
||||
SEARCH_PERIODICITY_SEC,
|
||||
mMaxSearchTimeSec,
|
||||
INCREMENTAL_RESULTS,
|
||||
INCREMENTAL_RESULTS_PERIODICITY_SEC,
|
||||
null /* List of PLMN ids (MCC-MNC) */);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a network scan for the given type {@code type}.
|
||||
* {@link #NETWORK_SCAN_TYPE_INCREMENTAL_RESULTS} is recommended if modem supports
|
||||
* {@link TelephonyManager#requestNetworkScan(
|
||||
* NetworkScanRequest, Executor, TelephonyScanManager.NetworkScanCallback)}.
|
||||
*
|
||||
* @param type used to tell which network scan API should be used.
|
||||
*/
|
||||
public void startNetworkScan(@NetworkQueryType int type) {
|
||||
if (type == NETWORK_SCAN_TYPE_WAIT_FOR_ALL_RESULTS) {
|
||||
mNetworkScanFuture = SettableFuture.create();
|
||||
Futures.addCallback(mNetworkScanFuture, new FutureCallback<List<CellInfo>>() {
|
||||
@Override
|
||||
public void onSuccess(List<CellInfo> result) {
|
||||
onResults(result);
|
||||
onComplete();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable t) {
|
||||
if (t instanceof CancellationException) {
|
||||
return;
|
||||
}
|
||||
int errCode = Integer.parseInt(t.getMessage());
|
||||
onError(errCode);
|
||||
}
|
||||
}, MoreExecutors.directExecutor());
|
||||
mExecutor.execute(new NetworkScanSyncTask(
|
||||
mTelephonyManager, (SettableFuture) mNetworkScanFuture));
|
||||
} else if (type == NETWORK_SCAN_TYPE_INCREMENTAL_RESULTS) {
|
||||
if (mNetworkScanRequester != null) {
|
||||
return;
|
||||
}
|
||||
mNetworkScanRequester = mTelephonyManager.requestNetworkScan(
|
||||
createNetworkScanForPreferredAccessNetworks(),
|
||||
mExecutor,
|
||||
mInternalNetworkScanCallback);
|
||||
if (mNetworkScanRequester == null) {
|
||||
onError(NetworkScan.ERROR_RADIO_INTERFACE_ERROR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The network scan of type {@link #NETWORK_SCAN_TYPE_WAIT_FOR_ALL_RESULTS} can't be stopped,
|
||||
* however, the result of the current network scan won't be returned to the callback after
|
||||
* calling this method.
|
||||
*/
|
||||
public void stopNetworkQuery() {
|
||||
if (mNetworkScanRequester != null) {
|
||||
mNetworkScanRequester.stopScan();
|
||||
mNetworkScanRequester = null;
|
||||
}
|
||||
|
||||
if (mNetworkScanFuture != null) {
|
||||
mNetworkScanFuture.cancel(true /* mayInterruptIfRunning */);
|
||||
mNetworkScanFuture = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void onResults(List<CellInfo> cellInfos) {
|
||||
mNetworkScanCallback.onResults(cellInfos);
|
||||
}
|
||||
|
||||
private void onComplete() {
|
||||
mNetworkScanCallback.onComplete();
|
||||
}
|
||||
|
||||
private void onError(int errCode) {
|
||||
mNetworkScanCallback.onError(errCode);
|
||||
}
|
||||
|
||||
private boolean hasNrSaCapability() {
|
||||
return Arrays.stream(
|
||||
mTelephonyManager.getPhoneCapability().getDeviceNrCapabilities())
|
||||
.anyMatch(i -> i == PhoneCapability.DEVICE_NR_CAPABILITY_SA);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the status code of {@link CellNetworkScanResult} to one of the
|
||||
* {@link NetworkScan.ScanErrorCode}.
|
||||
* @param errCode status code from {@link CellNetworkScanResult}.
|
||||
*
|
||||
* @return one of the scan error code from {@link NetworkScan.ScanErrorCode}.
|
||||
*/
|
||||
private static int convertToScanErrorCode(int errCode) {
|
||||
switch (errCode) {
|
||||
case CellNetworkScanResult.STATUS_RADIO_NOT_AVAILABLE:
|
||||
return NetworkScan.ERROR_RADIO_INTERFACE_ERROR;
|
||||
case CellNetworkScanResult.STATUS_RADIO_GENERIC_FAILURE:
|
||||
default:
|
||||
return NetworkScan.ERROR_MODEM_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
private final class NetworkScanCallbackImpl extends TelephonyScanManager.NetworkScanCallback {
|
||||
public void onResults(List<CellInfo> results) {
|
||||
Log.d(TAG, "Async scan onResults() results = "
|
||||
+ CellInfoUtil.cellInfoListToString(results));
|
||||
NetworkScanHelper.this.onResults(results);
|
||||
}
|
||||
|
||||
public void onComplete() {
|
||||
Log.d(TAG, "async scan onComplete()");
|
||||
NetworkScanHelper.this.onComplete();
|
||||
}
|
||||
|
||||
public void onError(@NetworkScan.ScanErrorCode int errCode) {
|
||||
Log.d(TAG, "async scan onError() errorCode = " + errCode);
|
||||
NetworkScanHelper.this.onError(errCode);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class NetworkScanSyncTask implements Runnable {
|
||||
private final SettableFuture<List<CellInfo>> mCallback;
|
||||
private final TelephonyManager mTelephonyManager;
|
||||
|
||||
NetworkScanSyncTask(
|
||||
TelephonyManager telephonyManager, SettableFuture<List<CellInfo>> callback) {
|
||||
mTelephonyManager = telephonyManager;
|
||||
mCallback = callback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
final CellNetworkScanResult result = mTelephonyManager.getAvailableNetworks();
|
||||
if (result.getStatus() == CellNetworkScanResult.STATUS_SUCCESS) {
|
||||
final List<CellInfo> cellInfos = result.getOperators()
|
||||
.stream()
|
||||
.map(operatorInfo
|
||||
-> CellInfoUtil.convertOperatorInfoToCellInfo(operatorInfo))
|
||||
.collect(Collectors.toList());
|
||||
Log.d(TAG, "Sync network scan completed, cellInfos = "
|
||||
+ CellInfoUtil.cellInfoListToString(cellInfos));
|
||||
mCallback.set(cellInfos);
|
||||
} else {
|
||||
final Throwable error = new Throwable(
|
||||
Integer.toString(convertToScanErrorCode(result.getStatus())));
|
||||
mCallback.setException(error);
|
||||
Log.d(TAG, "Sync network scan error, ex = " + error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
package com.android.settings.network.telephony;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
@@ -39,6 +38,7 @@ import android.util.Log;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.Keep;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.preference.Preference;
|
||||
@@ -48,14 +48,21 @@ import com.android.internal.annotations.Initializer;
|
||||
import com.android.internal.telephony.OperatorInfo;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.dashboard.DashboardFragment;
|
||||
import com.android.settings.network.telephony.scan.NetworkScanRepository;
|
||||
import com.android.settings.network.telephony.scan.NetworkScanRepository.NetworkScanCellInfos;
|
||||
import com.android.settings.network.telephony.scan.NetworkScanRepository.NetworkScanComplete;
|
||||
import com.android.settings.network.telephony.scan.NetworkScanRepository.NetworkScanError;
|
||||
import com.android.settings.network.telephony.scan.NetworkScanRepository.NetworkScanResult;
|
||||
import com.android.settings.overlay.FeatureFactory;
|
||||
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
|
||||
import com.android.settingslib.utils.ThreadUtils;
|
||||
|
||||
import kotlin.Unit;
|
||||
import kotlin.jvm.functions.Function1;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
@@ -70,12 +77,8 @@ public class NetworkSelectSettings extends DashboardFragment {
|
||||
private static final String TAG = "NetworkSelectSettings";
|
||||
|
||||
private static final int EVENT_SET_NETWORK_SELECTION_MANUALLY_DONE = 1;
|
||||
private static final int EVENT_NETWORK_SCAN_RESULTS = 2;
|
||||
private static final int EVENT_NETWORK_SCAN_ERROR = 3;
|
||||
private static final int EVENT_NETWORK_SCAN_COMPLETED = 4;
|
||||
|
||||
private static final String PREF_KEY_NETWORK_OPERATORS = "network_operators_preference";
|
||||
private static final int MIN_NUMBER_OF_SCAN_REQUIRED = 2;
|
||||
|
||||
private PreferenceCategory mPreferenceCategory;
|
||||
@VisibleForTesting
|
||||
@@ -90,18 +93,14 @@ public class NetworkSelectSettings extends DashboardFragment {
|
||||
private CarrierConfigManager mCarrierConfigManager;
|
||||
private List<String> mForbiddenPlmns;
|
||||
private boolean mShow4GForLTE = false;
|
||||
private NetworkScanHelper mNetworkScanHelper;
|
||||
private final ExecutorService mNetworkScanExecutor = Executors.newFixedThreadPool(1);
|
||||
private MetricsFeatureProvider mMetricsFeatureProvider;
|
||||
private boolean mUseNewApi;
|
||||
private long mRequestIdManualNetworkSelect;
|
||||
private long mRequestIdManualNetworkScan;
|
||||
private long mWaitingForNumberOfScanResults;
|
||||
@VisibleForTesting
|
||||
boolean mIsAggregationEnabled = false;
|
||||
private CarrierConfigManager.CarrierConfigChangeListener mCarrierConfigChangeListener;
|
||||
private AtomicBoolean mShouldFilterOutSatellitePlmn = new AtomicBoolean();
|
||||
|
||||
private NetworkScanRepository mNetworkScanRepository;
|
||||
private boolean mUpdateScanResult = false;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle icicle) {
|
||||
super.onCreate(icicle);
|
||||
@@ -113,7 +112,6 @@ public class NetworkSelectSettings extends DashboardFragment {
|
||||
@Initializer
|
||||
protected void onCreateInitialization() {
|
||||
Context context = getContext();
|
||||
mUseNewApi = enableNewAutoSelectNetworkUI(context);
|
||||
mSubId = getSubId();
|
||||
|
||||
mPreferenceCategory = getPreferenceCategory(PREF_KEY_NETWORK_OPERATORS);
|
||||
@@ -123,8 +121,6 @@ public class NetworkSelectSettings extends DashboardFragment {
|
||||
mTelephonyManager = getTelephonyManager(context, mSubId);
|
||||
mSatelliteManager = getSatelliteManager(context);
|
||||
mCarrierConfigManager = getCarrierConfigManager(context);
|
||||
mNetworkScanHelper = new NetworkScanHelper(
|
||||
mTelephonyManager, mCallback, mNetworkScanExecutor);
|
||||
PersistableBundle bundle = mCarrierConfigManager.getConfigForSubId(mSubId,
|
||||
CarrierConfigManager.KEY_SHOW_4G_FOR_LTE_DATA_ICON_BOOL,
|
||||
CarrierConfigManager.KEY_REMOVE_SATELLITE_PLMN_IN_MANUAL_NETWORK_SCAN_BOOL);
|
||||
@@ -135,30 +131,13 @@ public class NetworkSelectSettings extends DashboardFragment {
|
||||
true));
|
||||
|
||||
mMetricsFeatureProvider = getMetricsFeatureProvider(context);
|
||||
mIsAggregationEnabled = enableAggregation(context);
|
||||
Log.d(TAG, "init: mUseNewApi:" + mUseNewApi
|
||||
+ " ,mIsAggregationEnabled:" + mIsAggregationEnabled + " ,mSubId:" + mSubId);
|
||||
|
||||
mCarrierConfigChangeListener =
|
||||
(slotIndex, subId, carrierId, specificCarrierId) -> handleCarrierConfigChanged(
|
||||
subId);
|
||||
mCarrierConfigManager.registerCarrierConfigChangeListener(mNetworkScanExecutor,
|
||||
mCarrierConfigChangeListener);
|
||||
|
||||
}
|
||||
|
||||
@Keep
|
||||
@VisibleForTesting
|
||||
protected boolean enableNewAutoSelectNetworkUI(Context context) {
|
||||
return context.getResources().getBoolean(
|
||||
com.android.internal.R.bool.config_enableNewAutoSelectNetworkUI);
|
||||
}
|
||||
|
||||
@Keep
|
||||
@VisibleForTesting
|
||||
protected boolean enableAggregation(Context context) {
|
||||
return context.getResources().getBoolean(
|
||||
R.bool.config_network_selection_list_aggregation_enabled);
|
||||
mNetworkScanRepository = new NetworkScanRepository(context, mSubId);
|
||||
}
|
||||
|
||||
@Keep
|
||||
@@ -217,17 +196,42 @@ public class NetworkSelectSettings extends DashboardFragment {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View view, Bundle savedInstanceState) {
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
|
||||
final Activity activity = getActivity();
|
||||
if (activity != null) {
|
||||
mProgressHeader = setPinnedHeaderView(
|
||||
com.android.settingslib.widget.progressbar.R.layout.progress_header)
|
||||
.findViewById(com.android.settingslib.widget.progressbar.R.id.progress_bar_animation);
|
||||
setProgressBarVisible(false);
|
||||
}
|
||||
mProgressHeader = setPinnedHeaderView(
|
||||
com.android.settingslib.widget.progressbar.R.layout.progress_header
|
||||
).findViewById(com.android.settingslib.widget.progressbar.R.id.progress_bar_animation);
|
||||
forceUpdateConnectedPreferenceCategory();
|
||||
launchNetworkScan();
|
||||
}
|
||||
|
||||
private void launchNetworkScan() {
|
||||
mNetworkScanRepository.launchNetworkScan(getViewLifecycleOwner(), new Function1<>() {
|
||||
@Override
|
||||
public Unit invoke(@NonNull NetworkScanResult networkScanResult) {
|
||||
if (!mUpdateScanResult) {
|
||||
// Not update UI if not in scan mode.
|
||||
return Unit.INSTANCE;
|
||||
}
|
||||
if (networkScanResult instanceof NetworkScanCellInfos networkScanCellInfos) {
|
||||
scanResultHandler(networkScanCellInfos.getCellInfos());
|
||||
return Unit.INSTANCE;
|
||||
}
|
||||
if (!isPreferenceScreenEnabled()) {
|
||||
clearPreferenceSummary();
|
||||
enablePreferenceScreen(true);
|
||||
} else if (networkScanResult instanceof NetworkScanComplete
|
||||
&& mCellInfoList == null) {
|
||||
// In case the scan timeout before getting any results
|
||||
addMessagePreference(R.string.empty_networks_list);
|
||||
} else if (networkScanResult instanceof NetworkScanError) {
|
||||
addMessagePreference(R.string.network_query_error);
|
||||
}
|
||||
|
||||
return Unit.INSTANCE;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -235,12 +239,8 @@ public class NetworkSelectSettings extends DashboardFragment {
|
||||
super.onStart();
|
||||
|
||||
updateForbiddenPlmns();
|
||||
if (isProgressBarVisible()) {
|
||||
return;
|
||||
}
|
||||
if (mWaitingForNumberOfScanResults <= 0) {
|
||||
startNetworkQuery();
|
||||
}
|
||||
setProgressBarVisible(true);
|
||||
mUpdateScanResult = true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -255,14 +255,6 @@ public class NetworkSelectSettings extends DashboardFragment {
|
||||
: new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
if (mWaitingForNumberOfScanResults <= 0) {
|
||||
stopNetworkQuery();
|
||||
}
|
||||
super.onStop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPreferenceTreeClick(Preference preference) {
|
||||
if (preference == mSelectedPreference) {
|
||||
@@ -274,7 +266,7 @@ public class NetworkSelectSettings extends DashboardFragment {
|
||||
return false;
|
||||
}
|
||||
|
||||
stopNetworkQuery();
|
||||
mUpdateScanResult = false;
|
||||
|
||||
// Refresh the last selected item in case users reselect network.
|
||||
clearPreferenceSummary();
|
||||
@@ -293,8 +285,6 @@ public class NetworkSelectSettings extends DashboardFragment {
|
||||
// Disable the screen until network is manually set
|
||||
enablePreferenceScreen(false);
|
||||
|
||||
mRequestIdManualNetworkSelect = getNewRequestId();
|
||||
mWaitingForNumberOfScanResults = MIN_NUMBER_OF_SCAN_REQUIRED;
|
||||
final OperatorInfo operator = mSelectedPreference.getOperatorInfo();
|
||||
ThreadUtils.postOnBackgroundThread(() -> {
|
||||
final Message msg = mHandler.obtainMessage(
|
||||
@@ -328,7 +318,6 @@ public class NetworkSelectSettings extends DashboardFragment {
|
||||
switch (msg.what) {
|
||||
case EVENT_SET_NETWORK_SELECTION_MANUALLY_DONE:
|
||||
final boolean isSucceed = (boolean) msg.obj;
|
||||
stopNetworkQuery();
|
||||
setProgressBarVisible(false);
|
||||
enablePreferenceScreen(true);
|
||||
|
||||
@@ -340,86 +329,15 @@ public class NetworkSelectSettings extends DashboardFragment {
|
||||
Log.e(TAG, "No preference to update!");
|
||||
}
|
||||
break;
|
||||
case EVENT_NETWORK_SCAN_RESULTS:
|
||||
scanResultHandler((List<CellInfo>) msg.obj);
|
||||
break;
|
||||
|
||||
case EVENT_NETWORK_SCAN_ERROR:
|
||||
stopNetworkQuery();
|
||||
Log.i(TAG, "Network scan failure " + msg.arg1 + ":"
|
||||
+ " scan request 0x" + Long.toHexString(mRequestIdManualNetworkScan)
|
||||
+ ", waiting for scan results = " + mWaitingForNumberOfScanResults
|
||||
+ ", select request 0x"
|
||||
+ Long.toHexString(mRequestIdManualNetworkSelect));
|
||||
if (mRequestIdManualNetworkScan < mRequestIdManualNetworkSelect) {
|
||||
break;
|
||||
}
|
||||
if (!isPreferenceScreenEnabled()) {
|
||||
clearPreferenceSummary();
|
||||
enablePreferenceScreen(true);
|
||||
} else {
|
||||
addMessagePreference(R.string.network_query_error);
|
||||
}
|
||||
break;
|
||||
|
||||
case EVENT_NETWORK_SCAN_COMPLETED:
|
||||
stopNetworkQuery();
|
||||
Log.d(TAG, "Network scan complete:"
|
||||
+ " scan request 0x" + Long.toHexString(mRequestIdManualNetworkScan)
|
||||
+ ", waiting for scan results = " + mWaitingForNumberOfScanResults
|
||||
+ ", select request 0x"
|
||||
+ Long.toHexString(mRequestIdManualNetworkSelect));
|
||||
if (mRequestIdManualNetworkScan < mRequestIdManualNetworkSelect) {
|
||||
break;
|
||||
}
|
||||
if (!isPreferenceScreenEnabled()) {
|
||||
clearPreferenceSummary();
|
||||
enablePreferenceScreen(true);
|
||||
} else if (mCellInfoList == null) {
|
||||
// In case the scan timeout before getting any results
|
||||
addMessagePreference(R.string.empty_networks_list);
|
||||
}
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
@VisibleForTesting
|
||||
List<CellInfo> doAggregation(List<CellInfo> cellInfoListInput) {
|
||||
if (!mIsAggregationEnabled) {
|
||||
Log.d(TAG, "no aggregation");
|
||||
return new ArrayList<>(cellInfoListInput);
|
||||
}
|
||||
ArrayList<CellInfo> aggregatedList = new ArrayList<>();
|
||||
for (CellInfo cellInfo : cellInfoListInput) {
|
||||
String plmn = CellInfoUtil.getNetworkTitle(cellInfo.getCellIdentity());
|
||||
Class className = cellInfo.getClass();
|
||||
|
||||
Optional<CellInfo> itemInTheList = aggregatedList.stream().filter(
|
||||
item -> {
|
||||
String itemPlmn = CellInfoUtil.getNetworkTitle(item.getCellIdentity());
|
||||
return itemPlmn.equals(plmn) && item.getClass().equals(className);
|
||||
})
|
||||
.findFirst();
|
||||
if (itemInTheList.isPresent()) {
|
||||
if (cellInfo.isRegistered() && !itemInTheList.get().isRegistered()) {
|
||||
// Adding the registered cellinfo item into list. If there are two registered
|
||||
// cellinfo items, then select first one from source list.
|
||||
aggregatedList.set(aggregatedList.indexOf(itemInTheList.get()), cellInfo);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
aggregatedList.add(cellInfo);
|
||||
}
|
||||
|
||||
return filterOutSatellitePlmn(aggregatedList);
|
||||
}
|
||||
|
||||
/* We do not want to expose carrier satellite plmns to the user when manually scan the
|
||||
cellular network. Therefore, it is needed to filter out satellite plmns from current cell
|
||||
info list */
|
||||
private List<CellInfo> filterOutSatellitePlmn(List<CellInfo> cellInfoList) {
|
||||
@VisibleForTesting
|
||||
List<CellInfo> filterOutSatellitePlmn(List<CellInfo> cellInfoList) {
|
||||
List<String> aggregatedSatellitePlmn = getSatellitePlmnsForCarrierWrapper();
|
||||
if (!mShouldFilterOutSatellitePlmn.get() || aggregatedSatellitePlmn.isEmpty()) {
|
||||
return cellInfoList;
|
||||
@@ -451,39 +369,10 @@ public class NetworkSelectSettings extends DashboardFragment {
|
||||
}
|
||||
}
|
||||
|
||||
private final NetworkScanHelper.NetworkScanCallback mCallback =
|
||||
new NetworkScanHelper.NetworkScanCallback() {
|
||||
public void onResults(List<CellInfo> results) {
|
||||
final Message msg = mHandler.obtainMessage(EVENT_NETWORK_SCAN_RESULTS, results);
|
||||
msg.sendToTarget();
|
||||
}
|
||||
|
||||
public void onComplete() {
|
||||
final Message msg = mHandler.obtainMessage(EVENT_NETWORK_SCAN_COMPLETED);
|
||||
msg.sendToTarget();
|
||||
}
|
||||
|
||||
public void onError(int error) {
|
||||
final Message msg = mHandler.obtainMessage(EVENT_NETWORK_SCAN_ERROR, error,
|
||||
0 /* arg2 */);
|
||||
msg.sendToTarget();
|
||||
}
|
||||
};
|
||||
|
||||
@Keep
|
||||
@VisibleForTesting
|
||||
protected void scanResultHandler(List<CellInfo> results) {
|
||||
if (mRequestIdManualNetworkScan < mRequestIdManualNetworkSelect) {
|
||||
Log.d(TAG, "CellInfoList (drop): "
|
||||
+ CellInfoUtil.cellInfoListToString(new ArrayList<>(results)));
|
||||
return;
|
||||
}
|
||||
mWaitingForNumberOfScanResults--;
|
||||
if ((mWaitingForNumberOfScanResults <= 0) && (!isResumed())) {
|
||||
stopNetworkQuery();
|
||||
}
|
||||
|
||||
mCellInfoList = doAggregation(results);
|
||||
mCellInfoList = filterOutSatellitePlmn(results);
|
||||
Log.d(TAG, "CellInfoList: " + CellInfoUtil.cellInfoListToString(mCellInfoList));
|
||||
if (mCellInfoList != null && mCellInfoList.size() != 0) {
|
||||
final NetworkOperatorPreference connectedPref = updateAllPreferenceCategory();
|
||||
@@ -642,11 +531,6 @@ public class NetworkSelectSettings extends DashboardFragment {
|
||||
}
|
||||
}
|
||||
|
||||
private long getNewRequestId() {
|
||||
return Math.max(mRequestIdManualNetworkSelect,
|
||||
mRequestIdManualNetworkScan) + 1;
|
||||
}
|
||||
|
||||
private boolean isProgressBarVisible() {
|
||||
if (mProgressHeader == null) {
|
||||
return false;
|
||||
@@ -667,29 +551,8 @@ public class NetworkSelectSettings extends DashboardFragment {
|
||||
mPreferenceCategory.addPreference(mStatusMessagePreference);
|
||||
}
|
||||
|
||||
private void startNetworkQuery() {
|
||||
setProgressBarVisible(true);
|
||||
if (mNetworkScanHelper != null) {
|
||||
mRequestIdManualNetworkScan = getNewRequestId();
|
||||
mWaitingForNumberOfScanResults = MIN_NUMBER_OF_SCAN_REQUIRED;
|
||||
mNetworkScanHelper.startNetworkScan(
|
||||
mUseNewApi
|
||||
? NetworkScanHelper.NETWORK_SCAN_TYPE_INCREMENTAL_RESULTS
|
||||
: NetworkScanHelper.NETWORK_SCAN_TYPE_WAIT_FOR_ALL_RESULTS);
|
||||
}
|
||||
}
|
||||
|
||||
private void stopNetworkQuery() {
|
||||
setProgressBarVisible(false);
|
||||
if (mNetworkScanHelper != null) {
|
||||
mWaitingForNumberOfScanResults = 0;
|
||||
mNetworkScanHelper.stopNetworkQuery();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
stopNetworkQuery();
|
||||
mNetworkScanExecutor.shutdown();
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@@ -32,9 +32,24 @@ import kotlinx.coroutines.flow.onEach
|
||||
|
||||
private const val TAG = "SubscriptionRepository"
|
||||
|
||||
fun Context.isSubscriptionEnabledFlow(subId: Int) = subscriptionsChangedFlow().map {
|
||||
val subscriptionManager = getSystemService(SubscriptionManager::class.java)
|
||||
class SubscriptionRepository(private val context: Context) {
|
||||
/**
|
||||
* Return a list of subscriptions that are available and visible to the user.
|
||||
*
|
||||
* @return list of user selectable subscriptions.
|
||||
*/
|
||||
fun getSelectableSubscriptionInfoList(): List<SubscriptionInfo> =
|
||||
context.getSelectableSubscriptionInfoList()
|
||||
|
||||
fun isSubscriptionEnabledFlow(subId: Int) = context.isSubscriptionEnabledFlow(subId)
|
||||
}
|
||||
|
||||
val Context.subscriptionManager: SubscriptionManager?
|
||||
get() = getSystemService(SubscriptionManager::class.java)
|
||||
|
||||
fun Context.requireSubscriptionManager(): SubscriptionManager = subscriptionManager!!
|
||||
|
||||
fun Context.isSubscriptionEnabledFlow(subId: Int) = subscriptionsChangedFlow().map {
|
||||
subscriptionManager?.isSubscriptionEnabled(subId) ?: false
|
||||
}.flowOn(Dispatchers.Default)
|
||||
|
||||
@@ -43,7 +58,7 @@ fun Context.phoneNumberFlow(subscriptionInfo: SubscriptionInfo) = subscriptionsC
|
||||
}.flowOn(Dispatchers.Default)
|
||||
|
||||
fun Context.subscriptionsChangedFlow() = callbackFlow {
|
||||
val subscriptionManager = getSystemService(SubscriptionManager::class.java)!!
|
||||
val subscriptionManager = requireSubscriptionManager()
|
||||
|
||||
val listener = object : SubscriptionManager.OnSubscriptionsChangedListener() {
|
||||
override fun onSubscriptionsChanged() {
|
||||
@@ -58,3 +73,35 @@ fun Context.subscriptionsChangedFlow() = callbackFlow {
|
||||
|
||||
awaitClose { subscriptionManager.removeOnSubscriptionsChangedListener(listener) }
|
||||
}.conflate().onEach { Log.d(TAG, "subscriptions changed") }.flowOn(Dispatchers.Default)
|
||||
|
||||
/**
|
||||
* Return a list of subscriptions that are available and visible to the user.
|
||||
*
|
||||
* @return list of user selectable subscriptions.
|
||||
*/
|
||||
fun Context.getSelectableSubscriptionInfoList(): List<SubscriptionInfo> {
|
||||
val subscriptionManager = requireSubscriptionManager()
|
||||
val availableList = subscriptionManager.getAvailableSubscriptionInfoList() ?: return emptyList()
|
||||
val visibleList = availableList.filter { subInfo ->
|
||||
// Opportunistic subscriptions are considered invisible
|
||||
// to users so they should never be returned.
|
||||
SubscriptionUtil.isSubscriptionVisible(subscriptionManager, this, subInfo)
|
||||
}
|
||||
// Multiple subscriptions in a group should only have one representative.
|
||||
// It should be the current active primary subscription if any, or any primary subscription.
|
||||
val groupUuidToSelectedIdMap = visibleList
|
||||
.groupBy { it.groupUuid }
|
||||
.mapValues { (_, subInfos) ->
|
||||
subInfos.filter { it.simSlotIndex != SubscriptionManager.INVALID_SIM_SLOT_INDEX }
|
||||
.ifEmpty { subInfos }
|
||||
.minOf { it.subscriptionId }
|
||||
}
|
||||
|
||||
return visibleList
|
||||
.filter { subInfo ->
|
||||
val groupUuid = subInfo.groupUuid ?: return@filter true
|
||||
groupUuidToSelectedIdMap[groupUuid] == subInfo.subscriptionId
|
||||
}
|
||||
.sortedBy { it.subscriptionId }
|
||||
.also { Log.d(TAG, "getSelectableSubscriptionInfoList: $it") }
|
||||
}
|
||||
|
||||
@@ -22,10 +22,7 @@ import android.telecom.TelecomManager
|
||||
import android.telephony.SubscriptionManager
|
||||
import android.telephony.TelephonyManager
|
||||
import android.telephony.ims.ImsMmTelManager
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.lifecycle.repeatOnLifecycle
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.PreferenceScreen
|
||||
import com.android.settings.R
|
||||
@@ -33,7 +30,6 @@ import com.android.settings.network.telephony.wificalling.WifiCallingRepository
|
||||
import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
/**
|
||||
@@ -81,17 +77,12 @@ open class WifiCallingPreferenceController @JvmOverloads constructor(
|
||||
|
||||
override fun onViewCreated(viewLifecycleOwner: LifecycleOwner) {
|
||||
wifiCallingRepositoryFactory(mSubId).wifiCallingReadyFlow()
|
||||
.collectLatestWithLifecycle(viewLifecycleOwner) {
|
||||
preference.isVisible = it
|
||||
callingPreferenceCategoryController.updateChildVisible(preferenceKey, it)
|
||||
.collectLatestWithLifecycle(viewLifecycleOwner) { isReady ->
|
||||
preference.isVisible = isReady
|
||||
callingPreferenceCategoryController.updateChildVisible(preferenceKey, isReady)
|
||||
if (isReady) update()
|
||||
}
|
||||
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||
update()
|
||||
}
|
||||
}
|
||||
|
||||
callStateFlowFactory(mSubId).collectLatestWithLifecycle(viewLifecycleOwner) {
|
||||
preference.isEnabled = (it == TelephonyManager.CALL_STATE_IDLE)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
* 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.scan
|
||||
|
||||
import android.content.Context
|
||||
import android.telephony.AccessNetworkConstants.AccessNetworkType
|
||||
import android.telephony.CellInfo
|
||||
import android.telephony.NetworkScanRequest
|
||||
import android.telephony.PhoneCapability
|
||||
import android.telephony.RadioAccessSpecifier
|
||||
import android.telephony.TelephonyManager
|
||||
import android.telephony.TelephonyScanManager
|
||||
import android.util.Log
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import com.android.settings.network.telephony.CellInfoUtil
|
||||
import com.android.settings.network.telephony.CellInfoUtil.getNetworkTitle
|
||||
import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.asExecutor
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.callbackFlow
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
|
||||
class NetworkScanRepository(context: Context, subId: Int) {
|
||||
sealed interface NetworkScanResult
|
||||
|
||||
data class NetworkScanCellInfos(val cellInfos: List<CellInfo>) : NetworkScanResult
|
||||
data object NetworkScanComplete : NetworkScanResult
|
||||
data class NetworkScanError(val error: Int) : NetworkScanResult
|
||||
|
||||
private val telephonyManager =
|
||||
context.getSystemService(TelephonyManager::class.java)!!.createForSubscriptionId(subId)
|
||||
|
||||
/** TODO: Move this to UI layer, when UI layer migrated to Kotlin. */
|
||||
fun launchNetworkScan(lifecycleOwner: LifecycleOwner, onResult: (NetworkScanResult) -> Unit) {
|
||||
networkScanFlow().collectLatestWithLifecycle(lifecycleOwner, action = onResult)
|
||||
}
|
||||
|
||||
data class CellInfoScanKey(
|
||||
val title: String?,
|
||||
val className: String,
|
||||
val isRegistered: Boolean,
|
||||
) {
|
||||
constructor(cellInfo: CellInfo) : this(
|
||||
title = cellInfo.cellIdentity.getNetworkTitle(),
|
||||
className = cellInfo.javaClass.name,
|
||||
isRegistered = cellInfo.isRegistered,
|
||||
)
|
||||
}
|
||||
|
||||
fun networkScanFlow(): Flow<NetworkScanResult> = callbackFlow {
|
||||
val callback = object : TelephonyScanManager.NetworkScanCallback() {
|
||||
override fun onResults(results: List<CellInfo>) {
|
||||
val cellInfos = results.distinctBy { CellInfoScanKey(it) }
|
||||
trySend(NetworkScanCellInfos(cellInfos))
|
||||
Log.d(TAG, "CellInfoList: ${CellInfoUtil.cellInfoListToString(cellInfos)}")
|
||||
}
|
||||
|
||||
override fun onComplete() {
|
||||
trySend(NetworkScanComplete)
|
||||
close()
|
||||
Log.d(TAG, "onComplete")
|
||||
}
|
||||
|
||||
override fun onError(error: Int) {
|
||||
trySend(NetworkScanError(error))
|
||||
close()
|
||||
Log.d(TAG, "onError: $error")
|
||||
}
|
||||
}
|
||||
|
||||
val networkScan = telephonyManager.requestNetworkScan(
|
||||
createNetworkScan(),
|
||||
Dispatchers.Default.asExecutor(),
|
||||
callback,
|
||||
)
|
||||
|
||||
awaitClose { networkScan.stopScan() }
|
||||
}.flowOn(Dispatchers.Default)
|
||||
|
||||
/** Create network scan for allowed network types. */
|
||||
private fun createNetworkScan(): NetworkScanRequest {
|
||||
val allowedNetworkTypes = getAllowedNetworkTypes()
|
||||
Log.d(TAG, "createNetworkScan: allowedNetworkTypes = $allowedNetworkTypes")
|
||||
val radioAccessSpecifiers = allowedNetworkTypes
|
||||
.map { RadioAccessSpecifier(it, null, null) }
|
||||
.toTypedArray()
|
||||
return NetworkScanRequest(
|
||||
NetworkScanRequest.SCAN_TYPE_ONE_SHOT,
|
||||
radioAccessSpecifiers,
|
||||
NetworkScanRequest.MIN_SEARCH_PERIODICITY_SEC, // one shot, not used
|
||||
MAX_SEARCH_TIME_SEC,
|
||||
true,
|
||||
INCREMENTAL_RESULTS_PERIODICITY_SEC,
|
||||
null,
|
||||
)
|
||||
}
|
||||
|
||||
private fun getAllowedNetworkTypes(): List<Int> {
|
||||
val networkTypeBitmap3gpp: Long =
|
||||
telephonyManager.getAllowedNetworkTypesBitmask() and
|
||||
TelephonyManager.NETWORK_STANDARDS_FAMILY_BITMASK_3GPP
|
||||
return buildList {
|
||||
// If the allowed network types are unknown or if they are of the right class, scan for
|
||||
// them; otherwise, skip them to save scan time and prevent users from being shown
|
||||
// networks that they can't connect to.
|
||||
if (networkTypeBitmap3gpp == 0L
|
||||
|| networkTypeBitmap3gpp and TelephonyManager.NETWORK_CLASS_BITMASK_2G != 0L
|
||||
) {
|
||||
add(AccessNetworkType.GERAN)
|
||||
}
|
||||
if (networkTypeBitmap3gpp == 0L
|
||||
|| networkTypeBitmap3gpp and TelephonyManager.NETWORK_CLASS_BITMASK_3G != 0L
|
||||
) {
|
||||
add(AccessNetworkType.UTRAN)
|
||||
}
|
||||
if (networkTypeBitmap3gpp == 0L
|
||||
|| networkTypeBitmap3gpp and TelephonyManager.NETWORK_CLASS_BITMASK_4G != 0L
|
||||
) {
|
||||
add(AccessNetworkType.EUTRAN)
|
||||
}
|
||||
// If a device supports 5G stand-alone then the code below should be re-enabled; however
|
||||
// a device supporting only non-standalone mode cannot perform PLMN selection and camp
|
||||
// on a 5G network, which means that it shouldn't scan for 5G at the expense of battery
|
||||
// as part of the manual network selection process.
|
||||
//
|
||||
if (networkTypeBitmap3gpp == 0L
|
||||
|| (networkTypeBitmap3gpp and TelephonyManager.NETWORK_CLASS_BITMASK_5G != 0L &&
|
||||
hasNrSaCapability())
|
||||
) {
|
||||
add(AccessNetworkType.NGRAN)
|
||||
Log.d(TAG, "radioAccessSpecifiers add NGRAN.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun hasNrSaCapability(): Boolean {
|
||||
val phoneCapability = telephonyManager.getPhoneCapability()
|
||||
return PhoneCapability.DEVICE_NR_CAPABILITY_SA in phoneCapability.deviceNrCapabilities
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "NetworkScanRepository"
|
||||
|
||||
@VisibleForTesting
|
||||
val MAX_SEARCH_TIME_SEC = 300
|
||||
|
||||
@VisibleForTesting
|
||||
val INCREMENTAL_RESULTS_PERIODICITY_SEC = 3
|
||||
}
|
||||
}
|
||||
@@ -416,6 +416,7 @@ public class ChooseLockPassword extends SettingsActivity {
|
||||
public String getHint(Context context, boolean isAlpha, int type, ProfileType profile) {
|
||||
if (isAlpha) {
|
||||
if (android.os.Flags.allowPrivateProfile()
|
||||
&& android.multiuser.Flags.enablePrivateSpaceFeatures()
|
||||
&& profile.equals(ProfileType.Private)) {
|
||||
return context.getString(alphaHintForPrivateProfile);
|
||||
} else if (type == TYPE_FINGERPRINT) {
|
||||
@@ -433,6 +434,7 @@ public class ChooseLockPassword extends SettingsActivity {
|
||||
}
|
||||
} else {
|
||||
if (android.os.Flags.allowPrivateProfile()
|
||||
&& android.multiuser.Flags.enablePrivateSpaceFeatures()
|
||||
&& profile.equals(ProfileType.Private)) {
|
||||
return context.getString(numericHintForPrivateProfile);
|
||||
} else if (type == TYPE_FINGERPRINT) {
|
||||
@@ -1147,7 +1149,9 @@ public class ChooseLockPassword extends SettingsActivity {
|
||||
/*flags=*/0).getSystemService(UserManager.class);
|
||||
if (userManager.isManagedProfile()) {
|
||||
return ProfileType.Managed;
|
||||
} else if (android.os.Flags.allowPrivateProfile() && userManager.isPrivateProfile()) {
|
||||
} else if (android.os.Flags.allowPrivateProfile()
|
||||
&& android.multiuser.Flags.enablePrivateSpaceFeatures()
|
||||
&& userManager.isPrivateProfile()) {
|
||||
return ProfileType.Private;
|
||||
} else if (userManager.isProfile()) {
|
||||
return ProfileType.Other;
|
||||
|
||||
@@ -478,7 +478,9 @@ public class ChooseLockPattern extends SettingsActivity {
|
||||
.getString(SET_WORK_PROFILE_PATTERN_HEADER,
|
||||
() -> getString(
|
||||
R.string.lockpassword_choose_your_profile_pattern_header));
|
||||
} else if (android.os.Flags.allowPrivateProfile() && isPrivateProfile()) {
|
||||
} else if (android.os.Flags.allowPrivateProfile()
|
||||
&& android.multiuser.Flags.enablePrivateSpaceFeatures()
|
||||
&& isPrivateProfile()) {
|
||||
msg = getString(R.string.private_space_choose_your_pattern_header);
|
||||
} else {
|
||||
msg = getString(R.string.lockpassword_choose_your_pattern_header);
|
||||
|
||||
@@ -300,6 +300,7 @@ public class ConfirmDeviceCredentialActivity extends FragmentActivity {
|
||||
launchedCDC = true;
|
||||
}
|
||||
} else if (android.os.Flags.allowPrivateProfile()
|
||||
&& android.multiuser.Flags.enablePrivateSpaceFeatures()
|
||||
&& userProperties != null
|
||||
&& userProperties.isAuthAlwaysRequiredToDisableQuietMode()
|
||||
&& isInternalActivity()) {
|
||||
@@ -413,7 +414,8 @@ public class ConfirmDeviceCredentialActivity extends FragmentActivity {
|
||||
|
||||
private boolean doesUserStateEnforceStrongAuth(int userId) {
|
||||
if (android.os.Flags.allowPrivateProfile()
|
||||
&& android.multiuser.Flags.enableBiometricsToUnlockPrivateSpace()) {
|
||||
&& android.multiuser.Flags.enableBiometricsToUnlockPrivateSpace()
|
||||
&& android.multiuser.Flags.enablePrivateSpaceFeatures()) {
|
||||
// Check if CE storage for user is locked since biometrics can't unlock fbe/keystore of
|
||||
// the profile user using verifyTiedProfileChallenge. Biometrics can still be used if
|
||||
// the user is stopped with delayed locking (i.e., with storage unlocked), so the user
|
||||
|
||||
@@ -105,7 +105,8 @@ public class ConfirmDeviceCredentialUtils {
|
||||
|
||||
private static boolean isBiometricUnlockEnabledForPrivateSpace() {
|
||||
return android.os.Flags.allowPrivateProfile()
|
||||
&& android.multiuser.Flags.enableBiometricsToUnlockPrivateSpace();
|
||||
&& android.multiuser.Flags.enableBiometricsToUnlockPrivateSpace()
|
||||
&& android.multiuser.Flags.enablePrivateSpaceFeatures();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -104,7 +104,8 @@ public class AutoAdvanceSetupFragment extends InstrumentedFragment {
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
if (android.os.Flags.allowPrivateProfile()) {
|
||||
if (android.os.Flags.allowPrivateProfile()
|
||||
&& android.multiuser.Flags.enablePrivateSpaceFeatures()) {
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,10 @@ public class HidePrivateSpaceController extends TogglePreferenceController {
|
||||
@Override
|
||||
@AvailabilityStatus
|
||||
public int getAvailabilityStatus() {
|
||||
return android.os.Flags.allowPrivateProfile() ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
|
||||
return android.os.Flags.allowPrivateProfile()
|
||||
&& android.multiuser.Flags.enablePrivateSpaceFeatures()
|
||||
? AVAILABLE
|
||||
: UNSUPPORTED_ON_DEVICE;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -52,6 +52,7 @@ public class HidePrivateSpaceSensitiveNotificationsController extends TogglePref
|
||||
public int getAvailabilityStatus() {
|
||||
if (!android.os.Flags.allowPrivateProfile()
|
||||
|| !android.multiuser.Flags.enablePsSensitiveNotificationsToggle()
|
||||
|| !android.multiuser.Flags.enablePrivateSpaceFeatures()
|
||||
|| !mPrivateSpaceMaintainer.doesPrivateSpaceExist()) {
|
||||
return UNSUPPORTED_ON_DEVICE;
|
||||
}
|
||||
|
||||
@@ -27,7 +27,8 @@ public class HidePrivateSpaceSettings extends DashboardFragment{
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle icicle) {
|
||||
if (android.os.Flags.allowPrivateProfile()) {
|
||||
if (android.os.Flags.allowPrivateProfile()
|
||||
&& android.multiuser.Flags.enablePrivateSpaceFeatures()) {
|
||||
super.onCreate(icicle);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,10 @@ public final class HidePrivateSpaceSummaryController extends BasePreferenceContr
|
||||
|
||||
@Override
|
||||
public int getAvailabilityStatus() {
|
||||
return android.os.Flags.allowPrivateProfile() ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
|
||||
return android.os.Flags.allowPrivateProfile()
|
||||
&& android.multiuser.Flags.enablePrivateSpaceFeatures()
|
||||
? AVAILABLE
|
||||
: UNSUPPORTED_ON_DEVICE;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -59,7 +59,8 @@ public class PrivateProfileContextHelperActivity extends FragmentActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
if (!android.os.Flags.allowPrivateProfile()) {
|
||||
if (!android.os.Flags.allowPrivateProfile()
|
||||
|| !android.multiuser.Flags.enablePrivateSpaceFeatures()) {
|
||||
return;
|
||||
}
|
||||
setTheme(SetupWizardUtils.getTheme(this, getIntent()));
|
||||
|
||||
@@ -77,7 +77,8 @@ public class PrivateSpaceAuthenticationActivity extends FragmentActivity {
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
if (Flags.allowPrivateProfile()) {
|
||||
if (Flags.allowPrivateProfile()
|
||||
&& android.multiuser.Flags.enablePrivateSpaceFeatures()) {
|
||||
ThemeHelper.trySetDynamicColor(this);
|
||||
mPrivateSpaceMaintainer =
|
||||
new Injector().injectPrivateSpaceMaintainer(getApplicationContext());
|
||||
|
||||
@@ -32,7 +32,8 @@ public class PrivateSpaceDashboardFragment extends DashboardFragment {
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle icicle) {
|
||||
if (android.os.Flags.allowPrivateProfile()) {
|
||||
if (android.os.Flags.allowPrivateProfile()
|
||||
&& android.multiuser.Flags.enablePrivateSpaceFeatures()) {
|
||||
super.onCreate(icicle);
|
||||
if (icicle == null
|
||||
&& getIntent().getBooleanExtra(EXTRA_SHOW_PRIVATE_SPACE_UNLOCKED, false)) {
|
||||
|
||||
@@ -43,7 +43,8 @@ public class PrivateSpaceEducation extends InstrumentedFragment {
|
||||
LayoutInflater inflater,
|
||||
@Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
if (!android.os.Flags.allowPrivateProfile()) {
|
||||
if (!android.os.Flags.allowPrivateProfile()
|
||||
|| !android.multiuser.Flags.enablePrivateSpaceFeatures()) {
|
||||
return null;
|
||||
}
|
||||
GlifLayout rootView =
|
||||
|
||||
@@ -78,7 +78,8 @@ public class PrivateSpaceMaintainer {
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public final synchronized boolean createPrivateSpace() {
|
||||
if (!Flags.allowPrivateProfile()) {
|
||||
if (!Flags.allowPrivateProfile()
|
||||
|| !android.multiuser.Flags.enablePrivateSpaceFeatures()) {
|
||||
return false;
|
||||
}
|
||||
// Check if Private space already exists
|
||||
@@ -146,7 +147,8 @@ public class PrivateSpaceMaintainer {
|
||||
|
||||
/** Returns true if the Private space exists. */
|
||||
public synchronized boolean doesPrivateSpaceExist() {
|
||||
if (!Flags.allowPrivateProfile()) {
|
||||
if (!Flags.allowPrivateProfile()
|
||||
|| !android.multiuser.Flags.enablePrivateSpaceFeatures()) {
|
||||
return false;
|
||||
}
|
||||
if (mUserHandle != null) {
|
||||
@@ -322,6 +324,7 @@ public class PrivateSpaceMaintainer {
|
||||
|
||||
private boolean isPrivateSpaceAutoLockSupported() {
|
||||
return android.os.Flags.allowPrivateProfile()
|
||||
&& android.multiuser.Flags.supportAutolockForPrivateSpace();
|
||||
&& android.multiuser.Flags.supportAutolockForPrivateSpace()
|
||||
&& android.multiuser.Flags.enablePrivateSpaceFeatures();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,7 +51,8 @@ public final class PrivateSpaceSafetySource {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Flags.allowPrivateProfile()) {
|
||||
if (!Flags.allowPrivateProfile()
|
||||
|| !android.multiuser.Flags.enablePrivateSpaceFeatures()) {
|
||||
// Setting null safetySourceData so that an old entry gets cleared out and this way
|
||||
// provide a response since SC always expects one on rescan.
|
||||
SafetyCenterManagerWrapper.get().setSafetySourceData(
|
||||
|
||||
@@ -51,7 +51,8 @@ public class PrivateSpaceSetLockFragment extends InstrumentedFragment {
|
||||
LayoutInflater inflater,
|
||||
@Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
if (!android.os.Flags.allowPrivateProfile()) {
|
||||
if (!android.os.Flags.allowPrivateProfile()
|
||||
|| !android.multiuser.Flags.enablePrivateSpaceFeatures()) {
|
||||
return null;
|
||||
}
|
||||
GlifLayout rootView =
|
||||
|
||||
@@ -42,7 +42,8 @@ public class PrivateSpaceSetupActivity extends FragmentActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
if (!android.os.Flags.allowPrivateProfile()) {
|
||||
if (!android.os.Flags.allowPrivateProfile()
|
||||
|| !android.multiuser.Flags.enablePrivateSpaceFeatures()) {
|
||||
return;
|
||||
}
|
||||
setTheme(SetupWizardUtils.getTheme(this, getIntent()));
|
||||
|
||||
@@ -47,7 +47,8 @@ public class SetupSuccessFragment extends InstrumentedFragment {
|
||||
LayoutInflater inflater,
|
||||
@Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
if (!android.os.Flags.allowPrivateProfile()) {
|
||||
if (!android.os.Flags.allowPrivateProfile()
|
||||
|| !android.multiuser.Flags.enablePrivateSpaceFeatures()) {
|
||||
return null;
|
||||
}
|
||||
GlifLayout rootView =
|
||||
|
||||
@@ -40,7 +40,8 @@ public class AutoLockPreferenceController extends BasePreferenceController {
|
||||
@Override
|
||||
public int getAvailabilityStatus() {
|
||||
return android.os.Flags.allowPrivateProfile()
|
||||
&& android.multiuser.Flags.supportAutolockForPrivateSpace()
|
||||
&& android.multiuser.Flags.supportAutolockForPrivateSpace()
|
||||
&& android.multiuser.Flags.enablePrivateSpaceFeatures()
|
||||
? AVAILABLE
|
||||
: UNSUPPORTED_ON_DEVICE;
|
||||
}
|
||||
|
||||
@@ -47,7 +47,8 @@ public class AutoLockSettingsFragment extends RadioButtonPickerFragment {
|
||||
@Override
|
||||
public void onCreate(@NonNull Bundle icicle) {
|
||||
if (android.os.Flags.allowPrivateProfile()
|
||||
&& android.multiuser.Flags.supportAutolockForPrivateSpace()) {
|
||||
&& android.multiuser.Flags.supportAutolockForPrivateSpace()
|
||||
&& android.multiuser.Flags.enablePrivateSpaceFeatures()) {
|
||||
super.onCreate(icicle);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,10 @@ public class DeletePrivateSpaceController extends BasePreferenceController {
|
||||
|
||||
@Override
|
||||
public int getAvailabilityStatus() {
|
||||
return android.os.Flags.allowPrivateProfile() ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
|
||||
return android.os.Flags.allowPrivateProfile()
|
||||
&& android.multiuser.Flags.enablePrivateSpaceFeatures()
|
||||
? AVAILABLE
|
||||
: UNSUPPORTED_ON_DEVICE;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -35,7 +35,8 @@ public class PrivateSpaceDeleteActivity extends InstrumentedActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
if (!android.os.Flags.allowPrivateProfile()) {
|
||||
if (!android.os.Flags.allowPrivateProfile()
|
||||
|| !android.multiuser.Flags.enablePrivateSpaceFeatures()) {
|
||||
return;
|
||||
}
|
||||
setTheme(SetupWizardUtils.getTheme(this, getIntent()));
|
||||
|
||||
@@ -56,7 +56,8 @@ public class PrivateSpaceDeleteFragment extends InstrumentedFragment {
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle icicle) {
|
||||
if (android.os.Flags.allowPrivateProfile()) {
|
||||
if (android.os.Flags.allowPrivateProfile()
|
||||
&& android.multiuser.Flags.enablePrivateSpaceFeatures()) {
|
||||
super.onCreate(icicle);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,7 +62,8 @@ public class PrivateSpaceDeletionProgressFragment extends InstrumentedFragment {
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
if (android.os.Flags.allowPrivateProfile()) {
|
||||
if (android.os.Flags.allowPrivateProfile()
|
||||
&& android.multiuser.Flags.enablePrivateSpaceFeatures()) {
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ public class FaceFingerprintUnlockController extends CombinedBiometricStatusPref
|
||||
protected boolean isUserSupported() {
|
||||
return android.os.Flags.allowPrivateProfile()
|
||||
&& android.multiuser.Flags.enableBiometricsToUnlockPrivateSpace()
|
||||
&& android.multiuser.Flags.enablePrivateSpaceFeatures()
|
||||
&& mProfileUserId != UserHandle.USER_NULL;
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,8 @@ public class PrivateSpaceBiometricSettings extends BiometricsSettingsBase {
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
if (android.os.Flags.allowPrivateProfile()
|
||||
&& android.multiuser.Flags.enableBiometricsToUnlockPrivateSpace()) {
|
||||
&& android.multiuser.Flags.enableBiometricsToUnlockPrivateSpace()
|
||||
&& android.multiuser.Flags.enablePrivateSpaceFeatures()) {
|
||||
super.onAttach(context);
|
||||
UserHandle privateProfileHandle =
|
||||
PrivateSpaceMaintainer.getInstance(context).getPrivateProfileHandle();
|
||||
|
||||
@@ -45,6 +45,7 @@ public class PrivateSpaceFacePreferenceController extends BiometricFaceStatusPre
|
||||
protected boolean isUserSupported() {
|
||||
return android.os.Flags.allowPrivateProfile()
|
||||
&& android.multiuser.Flags.enableBiometricsToUnlockPrivateSpace()
|
||||
&& android.multiuser.Flags.enablePrivateSpaceFeatures()
|
||||
&& getUserId() != UserHandle.USER_NULL;
|
||||
}
|
||||
|
||||
@@ -63,7 +64,8 @@ public class PrivateSpaceFacePreferenceController extends BiometricFaceStatusPre
|
||||
@Override
|
||||
public int getAvailabilityStatus() {
|
||||
return android.os.Flags.allowPrivateProfile()
|
||||
&& android.multiuser.Flags.enableBiometricsToUnlockPrivateSpace()
|
||||
&& android.multiuser.Flags.enableBiometricsToUnlockPrivateSpace()
|
||||
&& android.multiuser.Flags.enablePrivateSpaceFeatures()
|
||||
? AVAILABLE
|
||||
: UNSUPPORTED_ON_DEVICE;
|
||||
}
|
||||
|
||||
@@ -47,6 +47,7 @@ public class PrivateSpaceFingerprintPreferenceController
|
||||
protected boolean isUserSupported() {
|
||||
return android.os.Flags.allowPrivateProfile()
|
||||
&& android.multiuser.Flags.enableBiometricsToUnlockPrivateSpace()
|
||||
&& android.multiuser.Flags.enablePrivateSpaceFeatures()
|
||||
&& getUserId() != UserHandle.USER_NULL;
|
||||
}
|
||||
|
||||
@@ -65,7 +66,8 @@ public class PrivateSpaceFingerprintPreferenceController
|
||||
@Override
|
||||
public int getAvailabilityStatus() {
|
||||
return android.os.Flags.allowPrivateProfile()
|
||||
&& android.multiuser.Flags.enableBiometricsToUnlockPrivateSpace()
|
||||
&& android.multiuser.Flags.enableBiometricsToUnlockPrivateSpace()
|
||||
&& android.multiuser.Flags.enablePrivateSpaceFeatures()
|
||||
? AVAILABLE
|
||||
: UNSUPPORTED_ON_DEVICE;
|
||||
}
|
||||
|
||||
@@ -74,7 +74,8 @@ public class PrivateSpaceLockController extends AbstractPreferenceController {
|
||||
|
||||
@Override
|
||||
public boolean isAvailable() {
|
||||
return android.os.Flags.allowPrivateProfile();
|
||||
return android.os.Flags.allowPrivateProfile()
|
||||
&& android.multiuser.Flags.enablePrivateSpaceFeatures();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -45,7 +45,10 @@ public class UseOneLockController extends BasePreferenceController {
|
||||
}
|
||||
@Override
|
||||
public int getAvailabilityStatus() {
|
||||
return android.os.Flags.allowPrivateProfile() ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
|
||||
return android.os.Flags.allowPrivateProfile()
|
||||
&& android.multiuser.Flags.enablePrivateSpaceFeatures()
|
||||
? AVAILABLE
|
||||
: UNSUPPORTED_ON_DEVICE;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -95,7 +95,8 @@ public class UseOneLockControllerSwitch extends AbstractPreferenceController
|
||||
|
||||
@Override
|
||||
public boolean isAvailable() {
|
||||
return android.os.Flags.allowPrivateProfile();
|
||||
return android.os.Flags.allowPrivateProfile()
|
||||
&& android.multiuser.Flags.enablePrivateSpaceFeatures();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -39,7 +39,8 @@ public class UseOneLockSettingsFragment extends DashboardFragment {
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle icicle) {
|
||||
if (android.os.Flags.allowPrivateProfile()) {
|
||||
if (android.os.Flags.allowPrivateProfile()
|
||||
&& android.multiuser.Flags.enablePrivateSpaceFeatures()) {
|
||||
super.onCreate(icicle);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,7 +62,9 @@ public final class BiometricsSafetySource {
|
||||
}
|
||||
final Context profileParentContext =
|
||||
context.createContextAsUser(profileParentUserHandle, 0);
|
||||
if (android.os.Flags.allowPrivateProfile() && userManager.isPrivateProfile()) {
|
||||
if (android.os.Flags.allowPrivateProfile()
|
||||
&& android.multiuser.Flags.enablePrivateSpaceFeatures()
|
||||
&& userManager.isPrivateProfile()) {
|
||||
// SC always expects a response from the source if the broadcast has been sent for this
|
||||
// source, therefore, we need to send a null SafetySourceData.
|
||||
SafetyCenterManagerWrapper.get().setSafetySourceData(
|
||||
|
||||
@@ -62,6 +62,7 @@ import com.android.settingslib.spaprivileged.template.app.AppListItem
|
||||
import com.android.settingslib.spaprivileged.template.app.AppListItemModel
|
||||
import com.android.settingslib.spaprivileged.template.app.AppListPage
|
||||
import com.google.common.annotations.VisibleForTesting
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
@@ -130,8 +131,10 @@ data class UserAspectRatioAppListItemModel(
|
||||
val canDisplay: Boolean,
|
||||
) : AppRecord
|
||||
|
||||
class UserAspectRatioAppListModel(private val context: Context)
|
||||
: AppListModel<UserAspectRatioAppListItemModel> {
|
||||
class UserAspectRatioAppListModel(
|
||||
private val context: Context,
|
||||
private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO
|
||||
) : AppListModel<UserAspectRatioAppListItemModel> {
|
||||
|
||||
private val packageManager = context.packageManager
|
||||
private val userAspectRatioManager = UserAspectRatioManager(context)
|
||||
@@ -203,7 +206,7 @@ class UserAspectRatioAppListModel(private val context: Context)
|
||||
flow {
|
||||
emit(userAspectRatioManager.getUserMinAspectRatioEntry(record.userOverride,
|
||||
record.app.packageName, record.app.userId))
|
||||
}.flowOn(Dispatchers.IO)
|
||||
}.flowOn(ioDispatcher)
|
||||
}.collectAsStateWithLifecycle(initialValue = stringResource(R.string.summary_placeholder))
|
||||
return { summary }
|
||||
}
|
||||
|
||||
@@ -84,6 +84,9 @@ public class KeyboardVibrationTogglePreferenceControllerTest {
|
||||
public void getAvailabilityStatus_featureSupported_available() {
|
||||
mSetFlagsRule.enableFlags(Flags.FLAG_KEYBOARD_CATEGORY_ENABLED);
|
||||
when(mResources.getBoolean(R.bool.config_keyboard_vibration_supported)).thenReturn(true);
|
||||
when(mResources.getFloat(
|
||||
com.android.internal.R.dimen.config_keyboardHapticFeedbackFixedAmplitude))
|
||||
.thenReturn(0.8f);
|
||||
|
||||
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
|
||||
}
|
||||
|
||||
@@ -16,16 +16,19 @@
|
||||
|
||||
package com.android.settings.applications.appcompat;
|
||||
|
||||
import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_3_2;
|
||||
import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_APP_DEFAULT;
|
||||
import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_FULLSCREEN;
|
||||
import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_UNSET;
|
||||
|
||||
import static com.android.settings.applications.AppInfoBase.ARG_PACKAGE_NAME;
|
||||
import static com.android.settings.applications.appcompat.UserAspectRatioDetails.KEY_HEADER_BUTTONS;
|
||||
import static com.android.settings.applications.appcompat.UserAspectRatioDetails.KEY_PREF_3_2;
|
||||
import static com.android.settings.applications.appcompat.UserAspectRatioDetails.KEY_PREF_DEFAULT;
|
||||
import static com.android.settings.applications.appcompat.UserAspectRatioDetails.KEY_PREF_FULLSCREEN;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
@@ -98,6 +101,31 @@ public class UserAspectRatioDetailsTest {
|
||||
mMetricsFeatureProvider = featureFactory.metricsFeatureProvider;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOrderOfOptionsFollowsConfig() {
|
||||
doReturn(true).when(mUserAspectRatioManager)
|
||||
.hasAspectRatioOption(anyInt(), anyString());
|
||||
doReturn(0).when(mUserAspectRatioManager)
|
||||
.getUserMinAspectRatioOrder(USER_MIN_ASPECT_RATIO_3_2);
|
||||
doReturn(1).when(mUserAspectRatioManager)
|
||||
.getUserMinAspectRatioOrder(USER_MIN_ASPECT_RATIO_FULLSCREEN);
|
||||
doReturn(2).when(mUserAspectRatioManager)
|
||||
.getUserMinAspectRatioOrder(USER_MIN_ASPECT_RATIO_UNSET);
|
||||
rule.getScenario().onActivity(a -> doReturn(a).when(mFragment).getActivity());
|
||||
final Bundle args = new Bundle();
|
||||
args.putString(ARG_PACKAGE_NAME, anyString());
|
||||
mFragment.setArguments(args);
|
||||
mFragment.onCreate(Bundle.EMPTY);
|
||||
|
||||
final int topOfList = mFragment.findPreference(KEY_HEADER_BUTTONS).getOrder();
|
||||
|
||||
assertTrue(topOfList < mFragment.findPreference(KEY_PREF_3_2).getOrder());
|
||||
assertTrue(mFragment.findPreference(KEY_PREF_3_2).getOrder()
|
||||
< mFragment.findPreference(KEY_PREF_FULLSCREEN).getOrder());
|
||||
assertTrue(mFragment.findPreference(KEY_PREF_FULLSCREEN).getOrder()
|
||||
< mFragment.findPreference(KEY_PREF_DEFAULT).getOrder());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onRadioButtonClicked_prefChange_shouldStopActivity() throws RemoteException {
|
||||
doReturn(USER_MIN_ASPECT_RATIO_UNSET).when(mFragment)
|
||||
|
||||
@@ -252,6 +252,24 @@ public class FingerprintEnrollIntroductionTest {
|
||||
assertThat(result).isEqualTo(R.string.fingerprint_intro_error_max);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void intro_CheckNullPropsReturnsErrorString() {
|
||||
setupFingerprintEnrollIntroWith(newTokenOnlyIntent());
|
||||
when(mFingerprintManager.getSensorPropertiesInternal()).thenReturn(null);
|
||||
final int result = mFingerprintEnrollIntroduction.checkMaxEnrolled();
|
||||
|
||||
assertThat(result).isEqualTo(R.string.fingerprint_intro_error_unknown);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void intro_CheckEmptyPropsReturnsErrorString() {
|
||||
setupFingerprintEnrollIntroWith(newTokenOnlyIntent());
|
||||
when(mFingerprintManager.getSensorPropertiesInternal()).thenReturn(List.of());
|
||||
final int result = mFingerprintEnrollIntroduction.checkMaxEnrolled();
|
||||
|
||||
assertThat(result).isEqualTo(R.string.fingerprint_intro_error_unknown);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void intro_CheckGenerateChallenge() {
|
||||
setupFingerprintEnrollIntroWith(newGkPwHandleAndFromSettingsIntent());
|
||||
|
||||
@@ -174,7 +174,8 @@ public class ProfileSelectFragmentTest {
|
||||
|
||||
@Test
|
||||
public void getTabId_setPrivateId_getCorrectTab() {
|
||||
mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE);
|
||||
mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE,
|
||||
android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
|
||||
final Bundle bundle = new Bundle();
|
||||
bundle.putInt(EXTRA_USER_ID, 11);
|
||||
mUserManager.setPrivateProfile(11, "private", 0);
|
||||
@@ -204,7 +205,8 @@ public class ProfileSelectFragmentTest {
|
||||
|
||||
@Test
|
||||
public void testGetFragments_whenOnlyPersonal_returnsOneFragment() {
|
||||
mSetFlagsRule.disableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE);
|
||||
mSetFlagsRule.disableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE,
|
||||
android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
|
||||
mUserManager.addProfile(
|
||||
new UserInfo(0, PRIMARY_USER_NAME, null, 0, USER_TYPE_FULL_SYSTEM));
|
||||
Fragment[] fragments = ProfileSelectFragment.getFragments(
|
||||
@@ -239,7 +241,8 @@ public class ProfileSelectFragmentTest {
|
||||
|
||||
@Test
|
||||
public void testGetFragments_whenPrivateEnabled_returnsTwoFragments() {
|
||||
mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE);
|
||||
mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE,
|
||||
android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
|
||||
mUserManager.addProfile(
|
||||
new UserInfo(0, PRIMARY_USER_NAME, null, 0, USER_TYPE_FULL_SYSTEM));
|
||||
mUserManager.addProfile(
|
||||
@@ -261,7 +264,8 @@ public class ProfileSelectFragmentTest {
|
||||
|
||||
@Test
|
||||
public void testGetFragments_whenManagedProfile_returnsTwoFragments() {
|
||||
mSetFlagsRule.disableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE);
|
||||
mSetFlagsRule.disableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE,
|
||||
android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
|
||||
mUserManager.addProfile(
|
||||
new UserInfo(0, PRIMARY_USER_NAME, null, 0, USER_TYPE_FULL_SYSTEM));
|
||||
mUserManager.addProfile(
|
||||
@@ -283,7 +287,8 @@ public class ProfileSelectFragmentTest {
|
||||
|
||||
@Test
|
||||
public void testGetFragments_whenAllProfiles_returnsThreeFragments() {
|
||||
mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE);
|
||||
mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE,
|
||||
android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
|
||||
mUserManager.addProfile(
|
||||
new UserInfo(0, PRIMARY_USER_NAME, null, 0, USER_TYPE_FULL_SYSTEM));
|
||||
mUserManager.addProfile(
|
||||
@@ -307,7 +312,8 @@ public class ProfileSelectFragmentTest {
|
||||
|
||||
@Test
|
||||
public void testGetFragments_whenAvailableBundle_returnsFragmentsWithCorrectBundles() {
|
||||
mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE);
|
||||
mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE,
|
||||
android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
|
||||
mUserManager.addProfile(
|
||||
new UserInfo(0, PRIMARY_USER_NAME, null, 0, USER_TYPE_FULL_SYSTEM));
|
||||
mUserManager.addProfile(
|
||||
|
||||
@@ -1,97 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.development.quarantine;
|
||||
|
||||
import static org.mockito.AdditionalMatchers.aryEq;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.ArgumentMatchers.isNull;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.SuspendDialogInfo;
|
||||
import android.os.UserHandle;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
|
||||
import com.android.settingslib.applications.ApplicationsState.AppEntry;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class QuarantinedAppsScreenControllerTest {
|
||||
private static final String PREF_KEY = "quarantined_apps_screen";
|
||||
private static final String TEST_PACKAGE = "com.example.test.pkg";
|
||||
private static final int TEST_APP_ID = 1234;
|
||||
private static final int TEST_USER_ID = 10;
|
||||
|
||||
private Context mContext;
|
||||
private QuarantinedAppsScreenController mController;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = spy(ApplicationProvider.getApplicationContext());
|
||||
mController = new QuarantinedAppsScreenController(mContext, PREF_KEY);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnPreferenceChange() {
|
||||
final Context userContext = mock(Context.class);
|
||||
doReturn(userContext).when(mContext).createContextAsUser(
|
||||
eq(UserHandle.of(TEST_USER_ID)), anyInt());
|
||||
final PackageManager packageManager = mock(PackageManager.class);
|
||||
doReturn(packageManager).when(userContext).getPackageManager();
|
||||
|
||||
final AppEntry entry = createAppEntry(TEST_PACKAGE, TEST_APP_ID, TEST_USER_ID);
|
||||
final QuarantinedAppPreference preference = new QuarantinedAppPreference(mContext, entry);
|
||||
|
||||
mController.onPreferenceChange(preference, true);
|
||||
verify(packageManager).setPackagesSuspended(aryEq(new String[] {TEST_PACKAGE}), eq(true),
|
||||
isNull(), isNull(), any(SuspendDialogInfo.class),
|
||||
eq(PackageManager.FLAG_SUSPEND_QUARANTINED));
|
||||
|
||||
mController.onPreferenceChange(preference, false);
|
||||
verify(packageManager).setPackagesSuspended(aryEq(new String[] {TEST_PACKAGE}), eq(false),
|
||||
isNull(), isNull(), isNull(),
|
||||
eq(PackageManager.FLAG_SUSPEND_QUARANTINED));
|
||||
}
|
||||
|
||||
private AppEntry createAppEntry(String packageName, int appId, int userId) {
|
||||
final AppEntry entry = mock(AppEntry.class);
|
||||
entry.info = createApplicationInfo(packageName, appId, userId);
|
||||
entry.extraInfo = false;
|
||||
return entry;
|
||||
}
|
||||
|
||||
private ApplicationInfo createApplicationInfo(String packageName, int appId, int userId) {
|
||||
final ApplicationInfo info = new ApplicationInfo();
|
||||
info.packageName = packageName;
|
||||
info.uid = UserHandle.getUid(userId, appId);
|
||||
return info;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
* 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.SubscriptionInfo
|
||||
import android.telephony.SubscriptionManager
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.test.assertIsDisplayed
|
||||
import androidx.compose.ui.test.hasText
|
||||
import androidx.compose.ui.test.isOff
|
||||
import androidx.compose.ui.test.isOn
|
||||
import androidx.compose.ui.test.junit4.createComposeRule
|
||||
import androidx.compose.ui.test.onNodeWithText
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.android.settings.R
|
||||
import com.android.settingslib.spa.testutils.waitUntilExists
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.kotlin.any
|
||||
import org.mockito.kotlin.doNothing
|
||||
import org.mockito.kotlin.doReturn
|
||||
import org.mockito.kotlin.mock
|
||||
import org.mockito.kotlin.spy
|
||||
import org.mockito.kotlin.stub
|
||||
import org.mockito.kotlin.whenever
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class MobileNetworkSwitchControllerTest {
|
||||
@get:Rule
|
||||
val composeTestRule = createComposeRule()
|
||||
|
||||
private val mockSubscriptionManager = mock<SubscriptionManager> {
|
||||
on { isSubscriptionEnabled(SUB_ID) } doReturn true
|
||||
}
|
||||
|
||||
private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
|
||||
on { subscriptionManager } doReturn mockSubscriptionManager
|
||||
doNothing().whenever(mock).startActivity(any())
|
||||
}
|
||||
|
||||
private val mockSubscriptionRepository = mock<SubscriptionRepository> {
|
||||
on { getSelectableSubscriptionInfoList() } doReturn listOf(SubInfo)
|
||||
on { isSubscriptionEnabledFlow(SUB_ID) } doReturn flowOf(false)
|
||||
}
|
||||
|
||||
private val controller = MobileNetworkSwitchController(
|
||||
context = context,
|
||||
preferenceKey = TEST_KEY,
|
||||
subscriptionRepository = mockSubscriptionRepository,
|
||||
).apply { init(SUB_ID) }
|
||||
|
||||
@Test
|
||||
fun isVisible_pSimAndCanDisablePhysicalSubscription_returnTrue() {
|
||||
val pSimSubInfo = SubscriptionInfo.Builder().apply {
|
||||
setId(SUB_ID)
|
||||
setEmbedded(false)
|
||||
}.build()
|
||||
mockSubscriptionManager.stub {
|
||||
on { canDisablePhysicalSubscription() } doReturn true
|
||||
}
|
||||
mockSubscriptionRepository.stub {
|
||||
on { getSelectableSubscriptionInfoList() } doReturn listOf(pSimSubInfo)
|
||||
}
|
||||
|
||||
setContent()
|
||||
|
||||
composeTestRule.onNodeWithText(context.getString(R.string.mobile_network_use_sim_on))
|
||||
.assertIsDisplayed()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isVisible_pSimAndCannotDisablePhysicalSubscription_returnFalse() {
|
||||
val pSimSubInfo = SubscriptionInfo.Builder().apply {
|
||||
setId(SUB_ID)
|
||||
setEmbedded(false)
|
||||
}.build()
|
||||
mockSubscriptionManager.stub {
|
||||
on { canDisablePhysicalSubscription() } doReturn false
|
||||
}
|
||||
mockSubscriptionRepository.stub {
|
||||
on { getSelectableSubscriptionInfoList() } doReturn listOf(pSimSubInfo)
|
||||
}
|
||||
|
||||
setContent()
|
||||
|
||||
composeTestRule.onNodeWithText(context.getString(R.string.mobile_network_use_sim_on))
|
||||
.assertDoesNotExist()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isVisible_eSim_returnTrue() {
|
||||
val eSimSubInfo = SubscriptionInfo.Builder().apply {
|
||||
setId(SUB_ID)
|
||||
setEmbedded(true)
|
||||
}.build()
|
||||
mockSubscriptionRepository.stub {
|
||||
on { getSelectableSubscriptionInfoList() } doReturn listOf(eSimSubInfo)
|
||||
}
|
||||
|
||||
setContent()
|
||||
|
||||
composeTestRule.onNodeWithText(context.getString(R.string.mobile_network_use_sim_on))
|
||||
.assertIsDisplayed()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isChecked_subscriptionEnabled_switchIsOn() {
|
||||
mockSubscriptionRepository.stub {
|
||||
on { isSubscriptionEnabledFlow(SUB_ID) } doReturn flowOf(true)
|
||||
}
|
||||
|
||||
setContent()
|
||||
|
||||
composeTestRule.waitUntilExists(
|
||||
hasText(context.getString(R.string.mobile_network_use_sim_on)) and isOn()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isChecked_subscriptionNotEnabled_switchIsOff() {
|
||||
mockSubscriptionRepository.stub {
|
||||
on { isSubscriptionEnabledFlow(SUB_ID) } doReturn flowOf(false)
|
||||
}
|
||||
|
||||
setContent()
|
||||
|
||||
composeTestRule.waitUntilExists(
|
||||
hasText(context.getString(R.string.mobile_network_use_sim_on)) and isOff()
|
||||
)
|
||||
}
|
||||
|
||||
private fun setContent() {
|
||||
composeTestRule.setContent {
|
||||
CompositionLocalProvider(LocalContext provides context) {
|
||||
controller.Content()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val TEST_KEY = "test_key"
|
||||
const val SUB_ID = 123
|
||||
|
||||
val SubInfo: SubscriptionInfo = SubscriptionInfo.Builder().apply {
|
||||
setId(SUB_ID)
|
||||
setEmbedded(true)
|
||||
}.build()
|
||||
}
|
||||
}
|
||||
@@ -17,12 +17,14 @@
|
||||
package com.android.settings.network.telephony
|
||||
|
||||
import android.content.Context
|
||||
import android.telephony.SubscriptionInfo
|
||||
import android.telephony.SubscriptionManager
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.android.settingslib.spa.testutils.firstWithTimeoutOrNull
|
||||
import com.android.settingslib.spa.testutils.toListWithTimeout
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import java.util.UUID
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.runBlocking
|
||||
@@ -47,16 +49,16 @@ class SubscriptionRepositoryTest {
|
||||
}
|
||||
|
||||
private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
|
||||
on { getSystemService(SubscriptionManager::class.java) } doReturn mockSubscriptionManager
|
||||
on { subscriptionManager } doReturn mockSubscriptionManager
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isSubscriptionEnabledFlow() = runBlocking {
|
||||
mockSubscriptionManager.stub {
|
||||
on { isSubscriptionEnabled(SUB_ID) } doReturn true
|
||||
on { isSubscriptionEnabled(SUB_ID_1) } doReturn true
|
||||
}
|
||||
|
||||
val isEnabled = context.isSubscriptionEnabledFlow(SUB_ID).firstWithTimeoutOrNull()
|
||||
val isEnabled = context.isSubscriptionEnabledFlow(SUB_ID_1).firstWithTimeoutOrNull()
|
||||
|
||||
assertThat(isEnabled).isTrue()
|
||||
}
|
||||
@@ -80,7 +82,69 @@ class SubscriptionRepositoryTest {
|
||||
assertThat(listDeferred.await()).hasSize(2)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getSelectableSubscriptionInfoList_sortedBySubId() {
|
||||
mockSubscriptionManager.stub {
|
||||
on { getAvailableSubscriptionInfoList() } doReturn listOf(
|
||||
SubscriptionInfo.Builder().apply {
|
||||
setId(SUB_ID_2)
|
||||
}.build(),
|
||||
SubscriptionInfo.Builder().apply {
|
||||
setId(SUB_ID_1)
|
||||
}.build(),
|
||||
)
|
||||
}
|
||||
|
||||
val subInfos = context.getSelectableSubscriptionInfoList()
|
||||
|
||||
assertThat(subInfos.map { it.subscriptionId }).containsExactly(SUB_ID_1, SUB_ID_2).inOrder()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getSelectableSubscriptionInfoList_sameGroupAndOneHasSlot_returnTheOneWithSimSlotIndex() {
|
||||
mockSubscriptionManager.stub {
|
||||
on { getAvailableSubscriptionInfoList() } doReturn listOf(
|
||||
SubscriptionInfo.Builder().apply {
|
||||
setId(SUB_ID_1)
|
||||
setGroupUuid(GROUP_UUID)
|
||||
}.build(),
|
||||
SubscriptionInfo.Builder().apply {
|
||||
setId(SUB_ID_2)
|
||||
setGroupUuid(GROUP_UUID)
|
||||
setSimSlotIndex(SIM_SLOT_INDEX)
|
||||
}.build(),
|
||||
)
|
||||
}
|
||||
|
||||
val subInfos = context.getSelectableSubscriptionInfoList()
|
||||
|
||||
assertThat(subInfos.map { it.subscriptionId }).containsExactly(SUB_ID_2)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getSelectableSubscriptionInfoList_sameGroupAndNonHasSlot_returnTheOneWithMinimumSubId() {
|
||||
mockSubscriptionManager.stub {
|
||||
on { getAvailableSubscriptionInfoList() } doReturn listOf(
|
||||
SubscriptionInfo.Builder().apply {
|
||||
setId(SUB_ID_2)
|
||||
setGroupUuid(GROUP_UUID)
|
||||
}.build(),
|
||||
SubscriptionInfo.Builder().apply {
|
||||
setId(SUB_ID_1)
|
||||
setGroupUuid(GROUP_UUID)
|
||||
}.build(),
|
||||
)
|
||||
}
|
||||
|
||||
val subInfos = context.getSelectableSubscriptionInfoList()
|
||||
|
||||
assertThat(subInfos.map { it.subscriptionId }).containsExactly(SUB_ID_1)
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val SUB_ID = 1
|
||||
const val SUB_ID_1 = 1
|
||||
const val SUB_ID_2 = 2
|
||||
val GROUP_UUID = UUID.randomUUID().toString()
|
||||
const val SIM_SLOT_INDEX = 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,265 @@
|
||||
/*
|
||||
* 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.scan
|
||||
|
||||
import android.content.Context
|
||||
import android.telephony.AccessNetworkConstants.AccessNetworkType
|
||||
import android.telephony.CellIdentityCdma
|
||||
import android.telephony.CellIdentityGsm
|
||||
import android.telephony.CellIdentityLte
|
||||
import android.telephony.CellInfoCdma
|
||||
import android.telephony.CellInfoGsm
|
||||
import android.telephony.CellInfoLte
|
||||
import android.telephony.NetworkScan
|
||||
import android.telephony.NetworkScanRequest
|
||||
import android.telephony.PhoneCapability
|
||||
import android.telephony.TelephonyManager
|
||||
import android.telephony.TelephonyManager.NETWORK_CLASS_BITMASK_5G
|
||||
import android.telephony.TelephonyScanManager
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.android.settings.network.telephony.scan.NetworkScanRepository.NetworkScanCellInfos
|
||||
import com.android.settings.network.telephony.scan.NetworkScanRepository.NetworkScanComplete
|
||||
import com.android.settings.network.telephony.scan.NetworkScanRepository.NetworkScanError
|
||||
import com.android.settingslib.spa.testutils.firstWithTimeoutOrNull
|
||||
import com.android.settingslib.spa.testutils.toListWithTimeout
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.kotlin.any
|
||||
import org.mockito.kotlin.argThat
|
||||
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)
|
||||
class NetworkScanRepositoryTest {
|
||||
|
||||
private var callback: TelephonyScanManager.NetworkScanCallback? = null
|
||||
|
||||
private val mockTelephonyManager = mock<TelephonyManager> {
|
||||
on { createForSubscriptionId(SUB_ID) } doReturn mock
|
||||
on { requestNetworkScan(any(), any(), any()) } doAnswer {
|
||||
callback = it.arguments[2] as TelephonyScanManager.NetworkScanCallback
|
||||
mock<NetworkScan>()
|
||||
}
|
||||
}
|
||||
|
||||
private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
|
||||
on { getSystemService(TelephonyManager::class.java) } doReturn mockTelephonyManager
|
||||
}
|
||||
|
||||
private val repository = NetworkScanRepository(context, SUB_ID)
|
||||
|
||||
@Test
|
||||
fun networkScanFlow_initial() = runBlocking {
|
||||
val result = repository.networkScanFlow().firstWithTimeoutOrNull()
|
||||
|
||||
assertThat(result).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun networkScanFlow_onResults(): Unit = runBlocking {
|
||||
val cellInfos = listOf(CellInfoCdma().apply { cellIdentity = CELL_IDENTITY_CDMA })
|
||||
val listDeferred = async {
|
||||
repository.networkScanFlow().toListWithTimeout()
|
||||
}
|
||||
delay(100)
|
||||
|
||||
callback?.onResults(cellInfos)
|
||||
|
||||
assertThat(listDeferred.await()).containsExactly(NetworkScanCellInfos(cellInfos))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun networkScanFlow_onComplete(): Unit = runBlocking {
|
||||
val listDeferred = async {
|
||||
repository.networkScanFlow().toListWithTimeout()
|
||||
}
|
||||
delay(100)
|
||||
|
||||
callback?.onComplete()
|
||||
|
||||
assertThat(listDeferred.await()).containsExactly(NetworkScanComplete)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun networkScanFlow_onError(): Unit = runBlocking {
|
||||
val listDeferred = async {
|
||||
repository.networkScanFlow().toListWithTimeout()
|
||||
}
|
||||
delay(100)
|
||||
|
||||
callback?.onError(1)
|
||||
|
||||
assertThat(listDeferred.await()).containsExactly(NetworkScanError(1))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun networkScanFlow_hasDuplicateItems(): Unit = runBlocking {
|
||||
val cellInfos = listOf(
|
||||
createCellInfoLte("123", false),
|
||||
createCellInfoLte("123", false),
|
||||
createCellInfoLte("124", true),
|
||||
createCellInfoLte("124", true),
|
||||
createCellInfoGsm("123", false),
|
||||
createCellInfoGsm("123", false),
|
||||
)
|
||||
val listDeferred = async {
|
||||
repository.networkScanFlow().toListWithTimeout()
|
||||
}
|
||||
delay(100)
|
||||
|
||||
callback?.onResults(cellInfos)
|
||||
|
||||
assertThat(listDeferred.await()).containsExactly(
|
||||
NetworkScanCellInfos(
|
||||
listOf(
|
||||
createCellInfoLte("123", false),
|
||||
createCellInfoLte("124", true),
|
||||
createCellInfoGsm("123", false),
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun networkScanFlow_noDuplicateItems(): Unit = runBlocking {
|
||||
val cellInfos = listOf(
|
||||
createCellInfoLte("123", false),
|
||||
createCellInfoLte("123", true),
|
||||
createCellInfoLte("124", false),
|
||||
createCellInfoLte("124", true),
|
||||
createCellInfoGsm("456", false),
|
||||
createCellInfoGsm("456", true),
|
||||
)
|
||||
val listDeferred = async {
|
||||
repository.networkScanFlow().toListWithTimeout()
|
||||
}
|
||||
delay(100)
|
||||
|
||||
callback?.onResults(cellInfos)
|
||||
|
||||
assertThat(listDeferred.await()).containsExactly(
|
||||
NetworkScanCellInfos(
|
||||
listOf(
|
||||
createCellInfoLte("123", false),
|
||||
createCellInfoLte("123", true),
|
||||
createCellInfoLte("124", false),
|
||||
createCellInfoLte("124", true),
|
||||
createCellInfoGsm("456", false),
|
||||
createCellInfoGsm("456", true),
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun createNetworkScan_deviceHasNrSa_requestNgran(): Unit = runBlocking {
|
||||
mockTelephonyManager.stub {
|
||||
on { getAllowedNetworkTypesBitmask() } doReturn NETWORK_CLASS_BITMASK_5G
|
||||
on { getPhoneCapability() } doReturn
|
||||
createPhoneCapability(intArrayOf(PhoneCapability.DEVICE_NR_CAPABILITY_SA))
|
||||
}
|
||||
|
||||
repository.networkScanFlow().firstWithTimeoutOrNull()
|
||||
|
||||
verify(mockTelephonyManager).requestNetworkScan(argThat<NetworkScanRequest> {
|
||||
specifiers.any { it.radioAccessNetwork == AccessNetworkType.NGRAN }
|
||||
}, any(), any())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun createNetworkScan_deviceNoNrSa_noNgran(): Unit = runBlocking {
|
||||
mockTelephonyManager.stub {
|
||||
on { getAllowedNetworkTypesBitmask() } doReturn NETWORK_CLASS_BITMASK_5G
|
||||
on { getPhoneCapability() } doReturn
|
||||
createPhoneCapability(intArrayOf(PhoneCapability.DEVICE_NR_CAPABILITY_NSA))
|
||||
}
|
||||
|
||||
repository.networkScanFlow().firstWithTimeoutOrNull()
|
||||
|
||||
verify(mockTelephonyManager).requestNetworkScan(argThat<NetworkScanRequest> {
|
||||
specifiers.none { it.radioAccessNetwork == AccessNetworkType.NGRAN }
|
||||
}, any(), any())
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val SUB_ID = 1
|
||||
const val LONG = "Long"
|
||||
const val SHORT = "Short"
|
||||
|
||||
val CELL_IDENTITY_CDMA = CellIdentityCdma(
|
||||
/* nid = */ 1,
|
||||
/* sid = */ 2,
|
||||
/* bid = */ 3,
|
||||
/* lon = */ 4,
|
||||
/* lat = */ 5,
|
||||
/* alphal = */ LONG,
|
||||
/* alphas = */ SHORT,
|
||||
)
|
||||
|
||||
private fun createCellInfoLte(alphaLong: String, registered: Boolean): CellInfoLte {
|
||||
val cellIdentityLte = CellIdentityLte(
|
||||
/* ci = */ 1,
|
||||
/* pci = */ 2,
|
||||
/* tac = */ 3,
|
||||
/* earfcn = */ 4,
|
||||
/* bands = */ intArrayOf(1, 2),
|
||||
/* bandwidth = */ 10000,
|
||||
/* mccStr = */ null,
|
||||
/* mncStr = */ null,
|
||||
/* alphal = */ alphaLong,
|
||||
/* alphas = */ null,
|
||||
/* additionalPlmns = */ emptyList(),
|
||||
/* csgInfo = */ null,
|
||||
)
|
||||
return CellInfoLte().apply {
|
||||
cellIdentity = cellIdentityLte
|
||||
isRegistered = registered
|
||||
}
|
||||
}
|
||||
|
||||
private fun createCellInfoGsm(alphaLong: String, registered: Boolean): CellInfoGsm {
|
||||
val cellIdentityGsm = CellIdentityGsm(
|
||||
/* lac = */ 1,
|
||||
/* cid = */ 2,
|
||||
/* arfcn = */ 3,
|
||||
/* bsic = */ 4,
|
||||
/* mccStr = */ "123",
|
||||
/* mncStr = */ "01",
|
||||
/* alphal = */ alphaLong,
|
||||
/* alphas = */ null,
|
||||
/* additionalPlmns = */ emptyList(),
|
||||
)
|
||||
return CellInfoGsm().apply {
|
||||
cellIdentity = cellIdentityGsm
|
||||
isRegistered = registered
|
||||
}
|
||||
}
|
||||
|
||||
private fun createPhoneCapability(deviceNrCapabilities: IntArray) =
|
||||
PhoneCapability.Builder().setDeviceNrCapabilities(deviceNrCapabilities).build()
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,11 @@ import com.android.settingslib.spa.testutils.FakeNavControllerWrapper
|
||||
import com.android.settingslib.spa.testutils.firstWithTimeoutOrNull
|
||||
import com.android.settingslib.spaprivileged.template.app.AppListItemModel
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.test.StandardTestDispatcher
|
||||
import kotlinx.coroutines.test.TestScope
|
||||
import kotlinx.coroutines.test.advanceUntilIdle
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
@@ -45,6 +49,8 @@ import org.junit.runner.RunWith
|
||||
class UserAspectRatioAppsPageProviderTest {
|
||||
@get:Rule
|
||||
val composeTestRule = createComposeRule()
|
||||
private val testDispatcher = StandardTestDispatcher()
|
||||
private val testScope = TestScope(testDispatcher)
|
||||
|
||||
private val context: Context = ApplicationProvider.getApplicationContext()
|
||||
private val fakeNavControllerWrapper = FakeNavControllerWrapper()
|
||||
@@ -137,33 +143,37 @@ class UserAspectRatioAppsPageProviderTest {
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@Test
|
||||
fun aspectRatioAppListModel_getSummaryDefault() {
|
||||
val summary = getSummary(USER_MIN_ASPECT_RATIO_UNSET)
|
||||
fun aspectRatioAppListModel_getSummaryDefault() = testScope.runTest {
|
||||
val summary = setSummary(USER_MIN_ASPECT_RATIO_UNSET)
|
||||
advanceUntilIdle()
|
||||
|
||||
assertThat(summary).isEqualTo(context.getString(R.string.user_aspect_ratio_app_default))
|
||||
assertThat(summary()).isEqualTo(context.getString(R.string.user_aspect_ratio_app_default))
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@Test
|
||||
fun aspectRatioAppListModel_getSummaryWhenSplitScreen() {
|
||||
val summary = getSummary(USER_MIN_ASPECT_RATIO_SPLIT_SCREEN)
|
||||
fun aspectRatioAppListModel_getSummaryWhenSplitScreen() = testScope.runTest {
|
||||
val summary = setSummary(USER_MIN_ASPECT_RATIO_SPLIT_SCREEN)
|
||||
advanceUntilIdle()
|
||||
|
||||
assertThat(summary).isEqualTo(context.getString(R.string.user_aspect_ratio_half_screen))
|
||||
assertThat(summary()).isEqualTo(context.getString(R.string.user_aspect_ratio_half_screen))
|
||||
}
|
||||
|
||||
private fun getSummary(userOverride: Int): String {
|
||||
val listModel = UserAspectRatioAppListModel(context)
|
||||
private fun setSummary(userOverride: Int): () -> String {
|
||||
val listModel = UserAspectRatioAppListModel(context, testDispatcher)
|
||||
val record = UserAspectRatioAppListItemModel(
|
||||
app = APP,
|
||||
userOverride = userOverride,
|
||||
suggested = false,
|
||||
canDisplay = true,
|
||||
)
|
||||
lateinit var summary: () -> String
|
||||
composeTestRule.setContent {
|
||||
summary = listModel.getSummary(option = 0,
|
||||
record = UserAspectRatioAppListItemModel(
|
||||
app = APP,
|
||||
userOverride = userOverride,
|
||||
suggested = false,
|
||||
canDisplay = true,
|
||||
))
|
||||
summary = listModel.getSummary(option = 0, record = record)
|
||||
}
|
||||
return summary()
|
||||
return summary
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -49,7 +49,8 @@ class SecuritySettingsTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@RequiresFlagsEnabled(Flags.FLAG_ALLOW_PRIVATE_PROFILE)
|
||||
@RequiresFlagsEnabled(Flags.FLAG_ALLOW_PRIVATE_PROFILE,
|
||||
android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES)
|
||||
fun privateSpace_ifFlagON() {
|
||||
device.assertHasTexts(listOf("Private Space"))
|
||||
}
|
||||
|
||||
@@ -38,7 +38,8 @@ import org.junit.runner.RunWith
|
||||
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@RequiresFlagsEnabled(Flags.FLAG_ALLOW_PRIVATE_PROFILE)
|
||||
@RequiresFlagsEnabled(Flags.FLAG_ALLOW_PRIVATE_PROFILE,
|
||||
android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES)
|
||||
class PrivateSpaceAuthenticationActivityTest {
|
||||
private val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
|
||||
|
||||
|
||||
@@ -310,7 +310,8 @@ public class CombinedBiometricStatusUtilsTest {
|
||||
when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(false);
|
||||
mSetFlagsRule.enableFlags(
|
||||
android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE,
|
||||
android.multiuser.Flags.FLAG_ENABLE_BIOMETRICS_TO_UNLOCK_PRIVATE_SPACE);
|
||||
android.multiuser.Flags.FLAG_ENABLE_BIOMETRICS_TO_UNLOCK_PRIVATE_SPACE,
|
||||
android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
|
||||
|
||||
assertThat(mCombinedBiometricStatusUtils.getPrivateProfileSettingsClassName())
|
||||
.isEqualTo(Settings.PrivateSpaceBiometricSettingsActivity.class.getName());
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.development.quarantine;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.UserHandle;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import com.android.settingslib.applications.ApplicationsState.AppEntry;
|
||||
|
||||
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 QuarantinedAppStateBridgeTest {
|
||||
private static final String TEST_PACKAGE = "com.example.test.pkg";
|
||||
private static final int TEST_APP_ID = 1234;
|
||||
private static final int TEST_USER_ID_1 = 0;
|
||||
private static final int TEST_USER_ID_2 = 10;
|
||||
|
||||
@Mock
|
||||
private Context mContext;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateExtraInfo_packageQuarantined() throws Exception {
|
||||
setPackageQuarantined(TEST_PACKAGE, TEST_USER_ID_1, false);
|
||||
setPackageQuarantined(TEST_PACKAGE, TEST_USER_ID_2, true);
|
||||
|
||||
final QuarantinedAppStateBridge bridge =
|
||||
new QuarantinedAppStateBridge(mContext, null, null);
|
||||
final AppEntry entry = mock(AppEntry.class);
|
||||
|
||||
bridge.updateExtraInfo(entry, TEST_PACKAGE, UserHandle.getUid(TEST_USER_ID_2, TEST_APP_ID));
|
||||
assertThat(entry.extraInfo).isEqualTo(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateExtraInfo_packageNotQuarantined() throws Exception {
|
||||
setPackageQuarantined(TEST_PACKAGE, TEST_USER_ID_1, false);
|
||||
setPackageQuarantined(TEST_PACKAGE, TEST_USER_ID_2, false);
|
||||
|
||||
final QuarantinedAppStateBridge bridge =
|
||||
new QuarantinedAppStateBridge(mContext, null, null);
|
||||
final AppEntry entry = mock(AppEntry.class);
|
||||
|
||||
bridge.updateExtraInfo(entry, TEST_PACKAGE, UserHandle.getUid(TEST_USER_ID_2, TEST_APP_ID));
|
||||
assertThat(entry.extraInfo).isEqualTo(false);
|
||||
}
|
||||
|
||||
private void setPackageQuarantined(String packageName, int userId, boolean quarantined)
|
||||
throws Exception {
|
||||
final Context userContext = mock(Context.class);
|
||||
when(mContext.createContextAsUser(eq(UserHandle.of(userId)), anyInt()))
|
||||
.thenReturn(userContext);
|
||||
final PackageManager packageManager = mock(PackageManager.class);
|
||||
when(userContext.getPackageManager()).thenReturn(packageManager);
|
||||
when(packageManager.isPackageQuarantined(packageName)).thenReturn(quarantined);
|
||||
}
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.development.quarantine;
|
||||
|
||||
import static com.android.settings.core.BasePreferenceController.AVAILABLE;
|
||||
import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.Flags;
|
||||
import android.platform.test.annotations.RequiresFlagsDisabled;
|
||||
import android.platform.test.annotations.RequiresFlagsEnabled;
|
||||
import android.platform.test.flag.junit.CheckFlagsRule;
|
||||
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
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 QuarantinedAppsPreferenceControllerTest {
|
||||
|
||||
private static final String PREF_KEY = "quarantined_apps";
|
||||
|
||||
@Rule
|
||||
public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
|
||||
|
||||
@Mock
|
||||
private Context mContext;
|
||||
private QuarantinedAppsPreferenceController mController;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mController = new QuarantinedAppsPreferenceController(mContext, PREF_KEY);
|
||||
}
|
||||
|
||||
@Test
|
||||
@RequiresFlagsEnabled(Flags.FLAG_QUARANTINED_ENABLED)
|
||||
public void testAvailabilityStatus_flagEnabled() {
|
||||
assertEquals(mController.getAvailabilityStatus(), AVAILABLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
@RequiresFlagsDisabled(Flags.FLAG_QUARANTINED_ENABLED)
|
||||
public void testAvailabilityStatus_flagDisabled() {
|
||||
assertEquals(mController.getAvailabilityStatus(), CONDITIONALLY_UNAVAILABLE);
|
||||
}
|
||||
}
|
||||
@@ -1,275 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.network.telephony;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.Looper;
|
||||
import android.platform.test.flag.junit.SetFlagsRule;
|
||||
import android.telephony.SubscriptionInfo;
|
||||
import android.telephony.SubscriptionManager;
|
||||
import android.telephony.TelephonyCallback;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import androidx.preference.PreferenceManager;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
import androidx.preference.PreferenceViewHolder;
|
||||
import androidx.test.annotation.UiThreadTest;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
|
||||
import com.android.settings.flags.Flags;
|
||||
import com.android.settings.network.SubscriptionUtil;
|
||||
import com.android.settings.widget.SettingsMainSwitchPreference;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnit;
|
||||
import org.mockito.junit.MockitoRule;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
public class MobileNetworkSwitchControllerTest {
|
||||
@Rule
|
||||
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
|
||||
@Rule
|
||||
public final MockitoRule mMockitoRule = MockitoJUnit.rule();
|
||||
|
||||
@Mock
|
||||
private SubscriptionManager mSubscriptionManager;
|
||||
@Mock
|
||||
private SubscriptionInfo mSubscription;
|
||||
@Mock
|
||||
private TelephonyManager mTelephonyManager;
|
||||
|
||||
private PreferenceScreen mScreen;
|
||||
private PreferenceManager mPreferenceManager;
|
||||
private SettingsMainSwitchPreference mSwitchBar;
|
||||
private Context mContext;
|
||||
private MobileNetworkSwitchController mController;
|
||||
private int mSubId = 123;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mSetFlagsRule.disableFlags(Flags.FLAG_IS_DUAL_SIM_ONBOARDING_ENABLED);
|
||||
|
||||
if (Looper.myLooper() == null) {
|
||||
Looper.prepare();
|
||||
}
|
||||
mContext = spy(ApplicationProvider.getApplicationContext());
|
||||
when(mContext.getSystemService(SubscriptionManager.class)).thenReturn(mSubscriptionManager);
|
||||
when(mSubscriptionManager.setSubscriptionEnabled(eq(mSubId), anyBoolean()))
|
||||
.thenReturn(true);
|
||||
|
||||
when(mSubscription.isEmbedded()).thenReturn(true);
|
||||
when(mSubscription.getSubscriptionId()).thenReturn(mSubId);
|
||||
// Most tests want to have 2 available subscriptions so that the switch bar will show.
|
||||
final SubscriptionInfo sub2 = mock(SubscriptionInfo.class);
|
||||
when(sub2.getSubscriptionId()).thenReturn(456);
|
||||
SubscriptionUtil.setAvailableSubscriptionsForTesting(Arrays.asList(mSubscription, sub2));
|
||||
|
||||
when(mContext.getSystemService(TelephonyManager.class)).thenReturn(mTelephonyManager);
|
||||
when(mTelephonyManager.createForSubscriptionId(mSubId))
|
||||
.thenReturn(mTelephonyManager);
|
||||
|
||||
final String key = "prefKey";
|
||||
mController = new MobileNetworkSwitchController(mContext, key);
|
||||
mController.init(mSubscription.getSubscriptionId());
|
||||
|
||||
mPreferenceManager = new PreferenceManager(mContext);
|
||||
mScreen = mPreferenceManager.createPreferenceScreen(mContext);
|
||||
mSwitchBar = new SettingsMainSwitchPreference(mContext);
|
||||
mSwitchBar.setKey(key);
|
||||
mSwitchBar.setTitle("123");
|
||||
mScreen.addPreference(mSwitchBar);
|
||||
|
||||
final LayoutInflater inflater = LayoutInflater.from(mContext);
|
||||
final View view = inflater.inflate(mSwitchBar.getLayoutResource(),
|
||||
new LinearLayout(mContext), false);
|
||||
final PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests(view);
|
||||
mSwitchBar.onBindViewHolder(holder);
|
||||
}
|
||||
|
||||
@After
|
||||
public void cleanUp() {
|
||||
SubscriptionUtil.setAvailableSubscriptionsForTesting(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void isAvailable_pSIM_isNotAvailable() {
|
||||
when(mSubscription.isEmbedded()).thenReturn(false);
|
||||
mController.displayPreference(mScreen);
|
||||
assertThat(mSwitchBar.isShowing()).isFalse();
|
||||
|
||||
when(mSubscriptionManager.canDisablePhysicalSubscription()).thenReturn(true);
|
||||
mController.displayPreference(mScreen);
|
||||
assertThat(mSwitchBar.isShowing()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void displayPreference_oneEnabledSubscription_switchBarNotHidden() {
|
||||
doReturn(true).when(mSubscriptionManager).isActiveSubscriptionId(mSubId);
|
||||
SubscriptionUtil.setAvailableSubscriptionsForTesting(Arrays.asList(mSubscription));
|
||||
mController.displayPreference(mScreen);
|
||||
assertThat(mSwitchBar.isShowing()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void displayPreference_oneDisabledSubscription_switchBarNotHidden() {
|
||||
doReturn(false).when(mSubscriptionManager).isActiveSubscriptionId(mSubId);
|
||||
SubscriptionUtil.setAvailableSubscriptionsForTesting(Arrays.asList(mSubscription));
|
||||
|
||||
mController.displayPreference(mScreen);
|
||||
|
||||
assertThat(mSwitchBar.isShowing()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void displayPreference_subscriptionEnabled_switchIsOn() {
|
||||
when(mSubscriptionManager.isActiveSubscriptionId(mSubId)).thenReturn(true);
|
||||
mController.displayPreference(mScreen);
|
||||
assertThat(mSwitchBar.isShowing()).isTrue();
|
||||
assertThat(mSwitchBar.isChecked()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void displayPreference_subscriptionDisabled_switchIsOff() {
|
||||
when(mSubscriptionManager.isActiveSubscriptionId(mSubId)).thenReturn(false);
|
||||
|
||||
mController.displayPreference(mScreen);
|
||||
|
||||
assertThat(mSwitchBar.isShowing()).isTrue();
|
||||
assertThat(mSwitchBar.isChecked()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void switchChangeListener_fromEnabledToDisabled_setSubscriptionEnabledCalledCorrectly() {
|
||||
when(mSubscriptionManager.isActiveSubscriptionId(mSubId)).thenReturn(true);
|
||||
mController.displayPreference(mScreen);
|
||||
assertThat(mSwitchBar.isShowing()).isTrue();
|
||||
assertThat(mSwitchBar.isChecked()).isTrue();
|
||||
|
||||
final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
|
||||
doNothing().when(mContext).startActivity(intentCaptor.capture());
|
||||
|
||||
// set switch off then should start a Activity.
|
||||
mSwitchBar.setChecked(false);
|
||||
|
||||
when(mSubscriptionManager.isActiveSubscriptionId(mSubId)).thenReturn(false);
|
||||
// Simulate action of back from previous activity.
|
||||
mController.displayPreference(mScreen);
|
||||
Bundle extra = intentCaptor.getValue().getExtras();
|
||||
|
||||
verify(mContext, times(1)).startActivity(any());
|
||||
assertThat(extra.getInt(ToggleSubscriptionDialogActivity.ARG_SUB_ID)).isEqualTo(mSubId);
|
||||
assertThat(extra.getBoolean(ToggleSubscriptionDialogActivity.ARG_enable))
|
||||
.isEqualTo(false);
|
||||
assertThat(mSwitchBar.isChecked()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void switchChangeListener_fromEnabledToDisabled_setSubscriptionEnabledFailed() {
|
||||
when(mSubscriptionManager.setSubscriptionEnabled(eq(mSubId), anyBoolean()))
|
||||
.thenReturn(false);
|
||||
when(mSubscriptionManager.isActiveSubscriptionId(mSubId)).thenReturn(true);
|
||||
mController.displayPreference(mScreen);
|
||||
assertThat(mSwitchBar.isShowing()).isTrue();
|
||||
assertThat(mSwitchBar.isChecked()).isTrue();
|
||||
|
||||
final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
|
||||
doNothing().when(mContext).startActivity(intentCaptor.capture());
|
||||
|
||||
// set switch off then should start a Activity.
|
||||
mSwitchBar.setChecked(false);
|
||||
|
||||
// Simulate action of back from previous activity.
|
||||
mController.displayPreference(mScreen);
|
||||
Bundle extra = intentCaptor.getValue().getExtras();
|
||||
|
||||
verify(mContext, times(1)).startActivity(any());
|
||||
assertThat(extra.getInt(ToggleSubscriptionDialogActivity.ARG_SUB_ID)).isEqualTo(mSubId);
|
||||
assertThat(extra.getBoolean(ToggleSubscriptionDialogActivity.ARG_enable))
|
||||
.isEqualTo(false);
|
||||
assertThat(mSwitchBar.isChecked()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void switchChangeListener_fromDisabledToEnabled_setSubscriptionEnabledCalledCorrectly() {
|
||||
when(mSubscriptionManager.isActiveSubscriptionId(mSubId)).thenReturn(false);
|
||||
mController.displayPreference(mScreen);
|
||||
assertThat(mSwitchBar.isShowing()).isTrue();
|
||||
assertThat(mSwitchBar.isChecked()).isFalse();
|
||||
|
||||
final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
|
||||
doNothing().when(mContext).startActivity(intentCaptor.capture());
|
||||
mSwitchBar.setChecked(true);
|
||||
Bundle extra = intentCaptor.getValue().getExtras();
|
||||
|
||||
verify(mContext, times(1)).startActivity(any());
|
||||
assertThat(extra.getInt(ToggleSubscriptionDialogActivity.ARG_SUB_ID)).isEqualTo(mSubId);
|
||||
assertThat(extra.getBoolean(ToggleSubscriptionDialogActivity.ARG_enable)).isEqualTo(true);
|
||||
}
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void onResumeAndonPause_registerAndUnregisterTelephonyCallback() {
|
||||
mController.onResume();
|
||||
|
||||
verify(mTelephonyManager)
|
||||
.registerTelephonyCallback(any(Executor.class), any(TelephonyCallback.class));
|
||||
|
||||
mController.onPause();
|
||||
verify(mTelephonyManager)
|
||||
.unregisterTelephonyCallback(any(TelephonyCallback.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void onPause_doNotRegisterAndUnregisterTelephonyCallback() {
|
||||
mController.onPause();
|
||||
verify(mTelephonyManager, times(0))
|
||||
.unregisterTelephonyCallback(any(TelephonyCallback.class));
|
||||
}
|
||||
}
|
||||
@@ -1,260 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.telephony.AccessNetworkConstants;
|
||||
import android.telephony.CellInfo;
|
||||
import android.telephony.ModemInfo;
|
||||
import android.telephony.NetworkScan;
|
||||
import android.telephony.NetworkScanRequest;
|
||||
import android.telephony.PhoneCapability;
|
||||
import android.telephony.RadioAccessSpecifier;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.telephony.TelephonyScanManager;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
import org.mockito.stubbing.Answer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class NetworkScanHelperTest {
|
||||
|
||||
@Mock
|
||||
private TelephonyManager mTelephonyManager;
|
||||
@Mock
|
||||
private List<CellInfo> mCellInfos;
|
||||
@Mock
|
||||
private NetworkScanHelper.NetworkScanCallback mNetworkScanCallback;
|
||||
|
||||
private static final long THREAD_EXECUTION_TIMEOUT_MS = 3000L;
|
||||
|
||||
private ExecutorService mNetworkScanExecutor;
|
||||
private NetworkScanHelper mNetworkScanHelper;
|
||||
|
||||
private static final int SCAN_ID = 1234;
|
||||
private static final int SUB_ID = 1;
|
||||
|
||||
private NetworkScan mNetworkScan;
|
||||
|
||||
public class NetworkScanMock extends NetworkScan {
|
||||
NetworkScanMock(int scanId, int subId) {
|
||||
super(scanId, subId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopScan() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
mNetworkScanExecutor = Executors.newFixedThreadPool(1);
|
||||
|
||||
mNetworkScanHelper = new NetworkScanHelper(mTelephonyManager,
|
||||
mNetworkScanCallback, mNetworkScanExecutor);
|
||||
|
||||
mNetworkScan = spy(new NetworkScanMock(SCAN_ID, SUB_ID));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void startNetworkScan_incrementalAndSuccess_completionWithResult() {
|
||||
when(mCellInfos.size()).thenReturn(1);
|
||||
|
||||
doAnswer(new Answer() {
|
||||
@Override
|
||||
public Object answer(InvocationOnMock invocation) throws Throwable {
|
||||
TelephonyScanManager.NetworkScanCallback callback =
|
||||
(TelephonyScanManager.NetworkScanCallback)
|
||||
(invocation.getArguments()[2]);
|
||||
callback.onResults(mCellInfos);
|
||||
callback.onComplete();
|
||||
return mNetworkScan;
|
||||
}
|
||||
}).when(mTelephonyManager).requestNetworkScan(
|
||||
any(NetworkScanRequest.class), any(Executor.class),
|
||||
any(TelephonyScanManager.NetworkScanCallback.class));
|
||||
|
||||
ArgumentCaptor<List<CellInfo>> argument = ArgumentCaptor.forClass(List.class);
|
||||
|
||||
startNetworkScan_incremental(true);
|
||||
|
||||
verify(mNetworkScanCallback, times(1)).onResults(argument.capture());
|
||||
List<CellInfo> actualResult = argument.getValue();
|
||||
assertThat(actualResult.size()).isEqualTo(mCellInfos.size());
|
||||
verify(mNetworkScanCallback, times(1)).onComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void startNetworkScan_incrementalAndImmediateFailure_failureWithErrorCode() {
|
||||
doReturn(null).when(mTelephonyManager).requestNetworkScan(
|
||||
any(NetworkScanRequest.class), any(Executor.class),
|
||||
any(TelephonyScanManager.NetworkScanCallback.class));
|
||||
|
||||
startNetworkScan_incremental(true);
|
||||
|
||||
verify(mNetworkScanCallback, times(1)).onError(anyInt());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void startNetworkScan_incrementalAndFailure_failureWithErrorCode() {
|
||||
doAnswer(new Answer() {
|
||||
@Override
|
||||
public Object answer(InvocationOnMock invocation) throws Throwable {
|
||||
TelephonyScanManager.NetworkScanCallback callback =
|
||||
(TelephonyScanManager.NetworkScanCallback)
|
||||
(invocation.getArguments()[2]);
|
||||
callback.onError(NetworkScan.ERROR_MODEM_ERROR);
|
||||
return mNetworkScan;
|
||||
}
|
||||
}).when(mTelephonyManager).requestNetworkScan(
|
||||
any(NetworkScanRequest.class), any(Executor.class),
|
||||
any(TelephonyScanManager.NetworkScanCallback.class));
|
||||
|
||||
startNetworkScan_incremental(true);
|
||||
|
||||
verify(mNetworkScanCallback, times(1)).onError(anyInt());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void startNetworkScan_incrementalAndAbort_doStop() {
|
||||
doReturn(mNetworkScan).when(mTelephonyManager).requestNetworkScan(
|
||||
any(NetworkScanRequest.class), any(Executor.class),
|
||||
any(TelephonyScanManager.NetworkScanCallback.class));
|
||||
|
||||
startNetworkScan_incremental(false);
|
||||
|
||||
verify(mNetworkScan, times(1)).stopScan();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createNetworkScanForPreferredAccessNetworks_deviceNoNrSa_noNgran() {
|
||||
int[] deviceNrCapabilities = new int[]{PhoneCapability.DEVICE_NR_CAPABILITY_NSA};
|
||||
PhoneCapability phoneCapability = createPhoneCapability(deviceNrCapabilities);
|
||||
doReturn(TelephonyManager.NETWORK_CLASS_BITMASK_2G
|
||||
| TelephonyManager.NETWORK_CLASS_BITMASK_3G
|
||||
| TelephonyManager.NETWORK_CLASS_BITMASK_4G
|
||||
| TelephonyManager.NETWORK_CLASS_BITMASK_5G).when(
|
||||
mTelephonyManager).getPreferredNetworkTypeBitmask();
|
||||
doReturn(phoneCapability).when(mTelephonyManager).getPhoneCapability();
|
||||
List<RadioAccessSpecifier> radioAccessSpecifiers = new ArrayList<>();
|
||||
radioAccessSpecifiers.add(
|
||||
new RadioAccessSpecifier(AccessNetworkConstants.AccessNetworkType.GERAN, null,
|
||||
null));
|
||||
radioAccessSpecifiers.add(
|
||||
new RadioAccessSpecifier(AccessNetworkConstants.AccessNetworkType.UTRAN, null,
|
||||
null));
|
||||
radioAccessSpecifiers.add(
|
||||
new RadioAccessSpecifier(AccessNetworkConstants.AccessNetworkType.EUTRAN, null,
|
||||
null));
|
||||
NetworkScanRequest expectedNetworkScanRequest = createNetworkScanRequest(
|
||||
radioAccessSpecifiers);
|
||||
|
||||
assertEquals(expectedNetworkScanRequest,
|
||||
mNetworkScanHelper.createNetworkScanForPreferredAccessNetworks());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createNetworkScanForPreferredAccessNetworks_deviceHasNrSa_hasNgran() {
|
||||
int[] deviceNrCapabilities = new int[]{PhoneCapability.DEVICE_NR_CAPABILITY_NSA,
|
||||
PhoneCapability.DEVICE_NR_CAPABILITY_SA};
|
||||
PhoneCapability phoneCapability = createPhoneCapability(deviceNrCapabilities);
|
||||
doReturn(TelephonyManager.NETWORK_CLASS_BITMASK_2G
|
||||
| TelephonyManager.NETWORK_CLASS_BITMASK_3G
|
||||
| TelephonyManager.NETWORK_CLASS_BITMASK_4G
|
||||
| TelephonyManager.NETWORK_CLASS_BITMASK_5G).when(
|
||||
mTelephonyManager).getPreferredNetworkTypeBitmask();
|
||||
doReturn(phoneCapability).when(mTelephonyManager).getPhoneCapability();
|
||||
List<RadioAccessSpecifier> radioAccessSpecifiers = new ArrayList<>();
|
||||
radioAccessSpecifiers.add(
|
||||
new RadioAccessSpecifier(AccessNetworkConstants.AccessNetworkType.GERAN, null,
|
||||
null));
|
||||
radioAccessSpecifiers.add(
|
||||
new RadioAccessSpecifier(AccessNetworkConstants.AccessNetworkType.UTRAN, null,
|
||||
null));
|
||||
radioAccessSpecifiers.add(
|
||||
new RadioAccessSpecifier(AccessNetworkConstants.AccessNetworkType.EUTRAN, null,
|
||||
null));
|
||||
radioAccessSpecifiers.add(
|
||||
new RadioAccessSpecifier(AccessNetworkConstants.AccessNetworkType.NGRAN, null,
|
||||
null));
|
||||
NetworkScanRequest expectedNetworkScanRequest = createNetworkScanRequest(
|
||||
radioAccessSpecifiers);
|
||||
|
||||
assertEquals(expectedNetworkScanRequest,
|
||||
mNetworkScanHelper.createNetworkScanForPreferredAccessNetworks());
|
||||
}
|
||||
|
||||
private PhoneCapability createPhoneCapability(int[] deviceNrCapabilities) {
|
||||
int maxActiveVoiceCalls = 1;
|
||||
int maxActiveData = 2;
|
||||
ModemInfo modemInfo = new ModemInfo(1, 2, true, false);
|
||||
List<ModemInfo> logicalModemList = new ArrayList<>();
|
||||
logicalModemList.add(modemInfo);
|
||||
return new PhoneCapability(maxActiveVoiceCalls, maxActiveData,
|
||||
logicalModemList, false, deviceNrCapabilities);
|
||||
}
|
||||
|
||||
private NetworkScanRequest createNetworkScanRequest(
|
||||
List<RadioAccessSpecifier> radioAccessSpecifiers) {
|
||||
return new NetworkScanRequest(
|
||||
NetworkScanRequest.SCAN_TYPE_ONE_SHOT,
|
||||
radioAccessSpecifiers.toArray(
|
||||
new RadioAccessSpecifier[radioAccessSpecifiers.size()]),
|
||||
mNetworkScanHelper.SEARCH_PERIODICITY_SEC,
|
||||
mNetworkScanHelper.MAX_SEARCH_TIME_SEC,
|
||||
mNetworkScanHelper.INCREMENTAL_RESULTS,
|
||||
mNetworkScanHelper.INCREMENTAL_RESULTS_PERIODICITY_SEC,
|
||||
null /* List of PLMN ids (MCC-MNC) */);
|
||||
}
|
||||
|
||||
private void startNetworkScan_incremental(boolean waitForCompletion) {
|
||||
mNetworkScanHelper.startNetworkScan(
|
||||
NetworkScanHelper.NETWORK_SCAN_TYPE_INCREMENTAL_RESULTS);
|
||||
if (!waitForCompletion) {
|
||||
mNetworkScanHelper.stopNetworkQuery();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -83,7 +83,6 @@ public class NetworkSelectSettingsTest {
|
||||
|
||||
public Context mContext;
|
||||
public PreferenceCategory mPreferenceCategory;
|
||||
public boolean mIsAggregationEnabled = true;
|
||||
|
||||
private TargetClass mNetworkSelectSettings;
|
||||
|
||||
@@ -104,7 +103,6 @@ public class NetworkSelectSettingsTest {
|
||||
doReturn(mCellId2).when(mCellInfo2).getCellIdentity();
|
||||
doReturn(mock(CellSignalStrength.class)).when(mCellInfo2).getCellSignalStrength();
|
||||
doReturn(CARRIER_NAME2).when(mCellId2).getOperatorAlphaLong();
|
||||
mIsAggregationEnabled = true;
|
||||
mNetworkSelectSettings = spy(new TargetClass(this));
|
||||
|
||||
PersistableBundle config = new PersistableBundle();
|
||||
@@ -176,11 +174,6 @@ public class NetworkSelectSettingsTest {
|
||||
return pref;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean enableAggregation(Context context) {
|
||||
return mTestEnv.mIsAggregationEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getSubId() {
|
||||
return SUB_ID;
|
||||
@@ -212,84 +205,7 @@ public class NetworkSelectSettingsTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void doAggregation_hasDuplicateItemsDiffCellIdCase1_removeSamePlmnRatItem() {
|
||||
mNetworkSelectSettings.onCreateInitialization();
|
||||
List<CellInfo> testList = Arrays.asList(
|
||||
createLteCellInfo(true, 123, "123", "232", "CarrierA"),
|
||||
createLteCellInfo(true, 1234, "123", "232", "CarrierA"),
|
||||
createGsmCellInfo(false, 123, "123", "232", "CarrierB"));
|
||||
List<CellInfo> expected = Arrays.asList(
|
||||
createLteCellInfo(true, 123, "123", "232", "CarrierA"),
|
||||
createGsmCellInfo(false, 123, "123", "232", "CarrierB"));
|
||||
assertThat(mNetworkSelectSettings.doAggregation(testList)).isEqualTo(expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void doAggregation_hasDuplicateItemsDiffCellIdCase2_removeSamePlmnRatItem() {
|
||||
mNetworkSelectSettings.onCreateInitialization();
|
||||
List<CellInfo> testList = Arrays.asList(
|
||||
createLteCellInfo(true, 123, "123", "232", "CarrierA"),
|
||||
createGsmCellInfo(false, 123, "123", "232", "CarrierB"),
|
||||
createLteCellInfo(false, 1234, "123", "232", "CarrierB"),
|
||||
createGsmCellInfo(false, 1234, "123", "232", "CarrierB"));
|
||||
List<CellInfo> expected = Arrays.asList(
|
||||
createLteCellInfo(true, 123, "123", "232", "CarrierA"),
|
||||
createGsmCellInfo(false, 123, "123", "232", "CarrierB"),
|
||||
createLteCellInfo(false, 1234, "123", "232", "CarrierB"));
|
||||
assertThat(mNetworkSelectSettings.doAggregation(testList)).isEqualTo(expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void doAggregation_hasDuplicateItemsDiffMccMncCase1_removeSamePlmnRatItem() {
|
||||
mNetworkSelectSettings.onCreateInitialization();
|
||||
List<CellInfo> testList = Arrays.asList(
|
||||
createLteCellInfo(true, 123, "123", "232", "CarrierA"),
|
||||
createLteCellInfo(true, 123, "456", "232", "CarrierA"),
|
||||
createGsmCellInfo(false, 123, "123", "232", "CarrierB"));
|
||||
List<CellInfo> expected = Arrays.asList(
|
||||
createLteCellInfo(true, 123, "123", "232", "CarrierA"),
|
||||
createGsmCellInfo(false, 123, "123", "232", "CarrierB"));
|
||||
assertThat(mNetworkSelectSettings.doAggregation(testList)).isEqualTo(expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void doAggregation_hasDuplicateItemsDiffMccMncCase2_removeSamePlmnRatItem() {
|
||||
mNetworkSelectSettings.onCreateInitialization();
|
||||
List<CellInfo> testList = Arrays.asList(
|
||||
createLteCellInfo(true, 123, "123", "232", "CarrierA"),
|
||||
createGsmCellInfo(false, 123, "123", "232", "CarrierB"),
|
||||
createLteCellInfo(false, 1234, "123", "232", "CarrierB"),
|
||||
createGsmCellInfo(false, 123, "456", "232", "CarrierB"));
|
||||
List<CellInfo> expected = Arrays.asList(
|
||||
createLteCellInfo(true, 123, "123", "232", "CarrierA"),
|
||||
createGsmCellInfo(false, 123, "123", "232", "CarrierB"),
|
||||
createLteCellInfo(false, 1234, "123", "232", "CarrierB"));
|
||||
assertThat(mNetworkSelectSettings.doAggregation(testList)).isEqualTo(expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void doAggregation_hasDuplicateItemsDiffMccMncCase3_removeSamePlmnRatItem() {
|
||||
PersistableBundle config = new PersistableBundle();
|
||||
config.putBoolean(
|
||||
CarrierConfigManager.KEY_REMOVE_SATELLITE_PLMN_IN_MANUAL_NETWORK_SCAN_BOOL, false);
|
||||
doReturn(config).when(mCarrierConfigManager).getConfigForSubId(eq(SUB_ID),
|
||||
eq(CarrierConfigManager.KEY_SHOW_4G_FOR_LTE_DATA_ICON_BOOL),
|
||||
eq(CarrierConfigManager.KEY_REMOVE_SATELLITE_PLMN_IN_MANUAL_NETWORK_SCAN_BOOL));
|
||||
|
||||
mNetworkSelectSettings.onCreateInitialization();
|
||||
List<CellInfo> testList = Arrays.asList(
|
||||
createLteCellInfo(false, 123, "123", "232", "CarrierA"),
|
||||
createLteCellInfo(false, 124, "123", "233", "CarrierA"),
|
||||
createLteCellInfo(true, 125, "123", "234", "CarrierA"),
|
||||
createGsmCellInfo(false, 126, "456", "232", "CarrierA"));
|
||||
List<CellInfo> expected = Arrays.asList(
|
||||
createLteCellInfo(true, 125, "123", "234", "CarrierA"),
|
||||
createGsmCellInfo(false, 126, "456", "232", "CarrierA"));
|
||||
assertThat(mNetworkSelectSettings.doAggregation(testList)).isEqualTo(expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void doAggregation_filterOutSatellitePlmn_whenKeyIsTrue() {
|
||||
public void filterOutSatellitePlmn_filterOutSatellitePlmn_whenKeyIsTrue() {
|
||||
PersistableBundle config = new PersistableBundle();
|
||||
config.putBoolean(
|
||||
CarrierConfigManager.KEY_REMOVE_SATELLITE_PLMN_IN_MANUAL_NETWORK_SCAN_BOOL, true);
|
||||
@@ -313,11 +229,11 @@ public class NetworkSelectSettingsTest {
|
||||
List<CellInfo> expected = Arrays.asList(
|
||||
createGsmCellInfo(false, 123, "123", "233", "CarrierB"),
|
||||
createLteCellInfo(false, 1234, "123", "234", "CarrierC"));
|
||||
assertThat(mNetworkSelectSettings.doAggregation(testList)).isEqualTo(expected);
|
||||
assertThat(mNetworkSelectSettings.filterOutSatellitePlmn(testList)).isEqualTo(expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void doAggregation_filterOutSatellitePlmn_whenNoSatellitePlmnIsAvailable() {
|
||||
public void filterOutSatellitePlmn_filterOutSatellitePlmn_whenNoSatellitePlmnIsAvailable() {
|
||||
PersistableBundle config = new PersistableBundle();
|
||||
config.putBoolean(
|
||||
CarrierConfigManager.KEY_REMOVE_SATELLITE_PLMN_IN_MANUAL_NETWORK_SCAN_BOOL, true);
|
||||
@@ -345,17 +261,17 @@ public class NetworkSelectSettingsTest {
|
||||
createGsmCellInfo(false, 123, "123", "233", "CarrierB"),
|
||||
createLteCellInfo(false, 1234, "123", "234", "CarrierC"),
|
||||
createGsmCellInfo(false, 12345, "123", "235", "CarrierD"));
|
||||
assertThat(mNetworkSelectSettings.doAggregation(testList)).isEqualTo(expected);
|
||||
assertThat(mNetworkSelectSettings.filterOutSatellitePlmn(testList)).isEqualTo(expected);
|
||||
|
||||
// Expect no filter out when KEY_REMOVE_SATELLITE_PLMN_IN_MANUAL_NETWORK_SCAN_BOOL is false.
|
||||
config.putBoolean(
|
||||
CarrierConfigManager.KEY_REMOVE_SATELLITE_PLMN_IN_MANUAL_NETWORK_SCAN_BOOL, false);
|
||||
mNetworkSelectSettings.onCreateInitialization();
|
||||
assertThat(mNetworkSelectSettings.doAggregation(testList)).isEqualTo(expected);
|
||||
assertThat(mNetworkSelectSettings.filterOutSatellitePlmn(testList)).isEqualTo(expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void doAggregation_filterOutSatellitePlmn_whenKeyIsFalse() {
|
||||
public void filterOutSatellitePlmn_filterOutSatellitePlmn_whenKeyIsFalse() {
|
||||
PersistableBundle config = new PersistableBundle();
|
||||
config.putBoolean(
|
||||
CarrierConfigManager.KEY_REMOVE_SATELLITE_PLMN_IN_MANUAL_NETWORK_SCAN_BOOL, true);
|
||||
@@ -381,7 +297,7 @@ public class NetworkSelectSettingsTest {
|
||||
createGsmCellInfo(false, 123, "123", "233", "CarrierB"),
|
||||
createLteCellInfo(false, 1234, "123", "234", "CarrierC"),
|
||||
createGsmCellInfo(false, 12345, "123", "235", "CarrierD"));
|
||||
assertThat(mNetworkSelectSettings.doAggregation(testList)).isEqualTo(expected);
|
||||
assertThat(mNetworkSelectSettings.filterOutSatellitePlmn(testList)).isEqualTo(expected);
|
||||
}
|
||||
|
||||
private CellInfoLte createLteCellInfo(boolean registered, int cellId, String mcc, String mnc,
|
||||
|
||||
@@ -75,7 +75,8 @@ public class FaceFingerprintUnlockControllerTest {
|
||||
public void getAvailabilityStatus_whenFlagsEnabled_returnsAvailable() {
|
||||
mSetFlagsRule.enableFlags(
|
||||
android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE,
|
||||
android.multiuser.Flags.FLAG_ENABLE_BIOMETRICS_TO_UNLOCK_PRIVATE_SPACE);
|
||||
android.multiuser.Flags.FLAG_ENABLE_BIOMETRICS_TO_UNLOCK_PRIVATE_SPACE,
|
||||
android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
|
||||
|
||||
assertThat(mFaceFingerprintUnlockController.isAvailable()).isEqualTo(true);
|
||||
}
|
||||
@@ -83,18 +84,33 @@ public class FaceFingerprintUnlockControllerTest {
|
||||
/** Tests that the controller is not available when Biometrics flag is not enabled. */
|
||||
@Test
|
||||
public void getAvailabilityStatus_whenBiometricFlagDisabled_returnsFalse() {
|
||||
mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE);
|
||||
mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE,
|
||||
android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
|
||||
mSetFlagsRule.disableFlags(
|
||||
android.multiuser.Flags.FLAG_ENABLE_BIOMETRICS_TO_UNLOCK_PRIVATE_SPACE);
|
||||
|
||||
assertThat(mFaceFingerprintUnlockController.isAvailable()).isEqualTo(false);
|
||||
}
|
||||
|
||||
/** Tests that the controller is not available when private feature flag is not enabled. */
|
||||
/**
|
||||
* Tests that the controller is not available when the main private space flag is not
|
||||
* enabled.
|
||||
*/
|
||||
@Test
|
||||
public void getAvailabilityStatus_whenPrivateFlagDisabled_returnsFalse() {
|
||||
public void getAvailabilityStatus_whenPsMainFlagDisabled_returnsFalse() {
|
||||
mSetFlagsRule.disableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE);
|
||||
mSetFlagsRule.enableFlags(
|
||||
android.multiuser.Flags.FLAG_ENABLE_BIOMETRICS_TO_UNLOCK_PRIVATE_SPACE,
|
||||
android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
|
||||
|
||||
assertThat(mFaceFingerprintUnlockController.isAvailable()).isEqualTo(false);
|
||||
}
|
||||
|
||||
/** Tests that the controller is not available when private features flag is not enabled. */
|
||||
@Test
|
||||
public void getAvailabilityStatus_whenPsFeaturesFlagDisabled_returnsFalse() {
|
||||
mSetFlagsRule.disableFlags(android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
|
||||
mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE,
|
||||
android.multiuser.Flags.FLAG_ENABLE_BIOMETRICS_TO_UNLOCK_PRIVATE_SPACE);
|
||||
|
||||
assertThat(mFaceFingerprintUnlockController.isAvailable()).isEqualTo(false);
|
||||
@@ -106,7 +122,8 @@ public class FaceFingerprintUnlockControllerTest {
|
||||
doReturn(false).when(mLockPatternUtils).isSeparateProfileChallengeEnabled(anyInt());
|
||||
mSetFlagsRule.enableFlags(
|
||||
android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE,
|
||||
android.multiuser.Flags.FLAG_ENABLE_BIOMETRICS_TO_UNLOCK_PRIVATE_SPACE);
|
||||
android.multiuser.Flags.FLAG_ENABLE_BIOMETRICS_TO_UNLOCK_PRIVATE_SPACE,
|
||||
android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
|
||||
|
||||
mFaceFingerprintUnlockController.updateState(mPreference);
|
||||
assertThat(mPreference.isEnabled()).isFalse();
|
||||
@@ -119,7 +136,8 @@ public class FaceFingerprintUnlockControllerTest {
|
||||
doReturn(true).when(mLockPatternUtils).isSeparateProfileChallengeEnabled(anyInt());
|
||||
mSetFlagsRule.enableFlags(
|
||||
android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE,
|
||||
android.multiuser.Flags.FLAG_ENABLE_BIOMETRICS_TO_UNLOCK_PRIVATE_SPACE);
|
||||
android.multiuser.Flags.FLAG_ENABLE_BIOMETRICS_TO_UNLOCK_PRIVATE_SPACE,
|
||||
android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
|
||||
|
||||
mFaceFingerprintUnlockController.updateState(mPreference);
|
||||
assertThat(mPreference.isEnabled()).isTrue();
|
||||
|
||||
@@ -40,7 +40,8 @@ import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@RequiresFlagsEnabled(Flags.FLAG_ALLOW_PRIVATE_PROFILE)
|
||||
@RequiresFlagsEnabled({Flags.FLAG_ALLOW_PRIVATE_PROFILE,
|
||||
android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES})
|
||||
public class HidePrivateSpaceControllerTest {
|
||||
@Rule
|
||||
public final CheckFlagsRule mCheckFlagsRule =
|
||||
|
||||
@@ -67,7 +67,8 @@ public class HidePrivateSpaceSensitiveNotificationsControllerTest {
|
||||
|
||||
mSetFlagsRule.enableFlags(
|
||||
android.multiuser.Flags.FLAG_ENABLE_PS_SENSITIVE_NOTIFICATIONS_TOGGLE);
|
||||
mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE);
|
||||
mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE,
|
||||
android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
|
||||
|
||||
mPrivateProfileId = PrivateSpaceMaintainer.getInstance(
|
||||
mContext).getPrivateProfileHandle().getIdentifier();
|
||||
@@ -143,7 +144,8 @@ public class HidePrivateSpaceSensitiveNotificationsControllerTest {
|
||||
public void getAvailabilityStatus_flagDisabled() {
|
||||
mSetFlagsRule.disableFlags(
|
||||
android.multiuser.Flags.FLAG_ENABLE_PS_SENSITIVE_NOTIFICATIONS_TOGGLE);
|
||||
mSetFlagsRule.disableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE);
|
||||
mSetFlagsRule.disableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE,
|
||||
android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
|
||||
Settings.Secure.putInt(mContext.getContentResolver(),
|
||||
Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1);
|
||||
Settings.Secure.putInt(mContext.getContentResolver(),
|
||||
|
||||
@@ -40,7 +40,8 @@ import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@RequiresFlagsEnabled(Flags.FLAG_ALLOW_PRIVATE_PROFILE)
|
||||
@RequiresFlagsEnabled({Flags.FLAG_ALLOW_PRIVATE_PROFILE,
|
||||
android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES})
|
||||
public class HidePrivateSpaceSummaryControllerTest {
|
||||
@Rule
|
||||
public final CheckFlagsRule mCheckFlagsRule =
|
||||
|
||||
@@ -88,7 +88,8 @@ public class PrivateSpaceAuthenticationActivityTest {
|
||||
/** Tests that when Private does not exist setup flow is started. */
|
||||
//TODO(b/307729746) Plan to add more tests for complete setup flow
|
||||
@Test
|
||||
@RequiresFlagsEnabled(Flags.FLAG_ALLOW_PRIVATE_PROFILE)
|
||||
@RequiresFlagsEnabled({Flags.FLAG_ALLOW_PRIVATE_PROFILE,
|
||||
android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES})
|
||||
public void whenPrivateProfileDoesNotExist_triggersSetupFlow() {
|
||||
when(mPrivateSpaceMaintainer.doesPrivateSpaceExist()).thenReturn(false);
|
||||
|
||||
|
||||
@@ -80,7 +80,8 @@ public class PrivateSpaceLockControllerTest {
|
||||
/** Tests that the controller is always available. */
|
||||
@Test
|
||||
public void getAvailabilityStatus_returnsAvailable() {
|
||||
mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE);
|
||||
mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE,
|
||||
android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
|
||||
|
||||
assertThat(mPrivateSpaceLockController.isAvailable()).isEqualTo(true);
|
||||
}
|
||||
@@ -89,7 +90,8 @@ public class PrivateSpaceLockControllerTest {
|
||||
@Test
|
||||
public void getSummary_whenScreenLock() {
|
||||
doReturn(false).when(mLockPatternUtils).isSeparateProfileChallengeEnabled(anyInt());
|
||||
mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE);
|
||||
mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE,
|
||||
android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
|
||||
|
||||
mPrivateSpaceLockController.updateState(mPreference);
|
||||
assertThat(mPreference.isEnabled()).isFalse();
|
||||
@@ -103,7 +105,8 @@ public class PrivateSpaceLockControllerTest {
|
||||
.when(mLockPatternUtils).isSeparateProfileChallengeEnabled(anyInt());
|
||||
doReturn(CREDENTIAL_TYPE_PATTERN)
|
||||
.when(mLockPatternUtils).getCredentialTypeForUser(anyInt());
|
||||
mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE);
|
||||
mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE,
|
||||
android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
|
||||
|
||||
mPrivateSpaceLockController.updateState(mPreference);
|
||||
assertThat(mPreference.isEnabled()).isTrue();
|
||||
@@ -115,7 +118,8 @@ public class PrivateSpaceLockControllerTest {
|
||||
public void getSummary_whenProfileLockPin() {
|
||||
doReturn(true).when(mLockPatternUtils).isSeparateProfileChallengeEnabled(anyInt());
|
||||
doReturn(CREDENTIAL_TYPE_PIN).when(mLockPatternUtils).getCredentialTypeForUser(anyInt());
|
||||
mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE);
|
||||
mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE,
|
||||
android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
|
||||
|
||||
mPrivateSpaceLockController.updateState(mPreference);
|
||||
assertThat(mPreference.isEnabled()).isTrue();
|
||||
@@ -129,7 +133,8 @@ public class PrivateSpaceLockControllerTest {
|
||||
.when(mLockPatternUtils).isSeparateProfileChallengeEnabled(anyInt());
|
||||
doReturn(CREDENTIAL_TYPE_PASSWORD)
|
||||
.when(mLockPatternUtils).getCredentialTypeForUser(anyInt());
|
||||
mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE);
|
||||
mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE,
|
||||
android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
|
||||
|
||||
mPrivateSpaceLockController.updateState(mPreference);
|
||||
assertThat(mPreference.isEnabled()).isTrue();
|
||||
|
||||
@@ -148,7 +148,8 @@ public class PrivateSpaceMaintainerTest {
|
||||
public void createPrivateSpace_psDoesNotExist_setsDefaultPsSensitiveNotificationsValue() {
|
||||
mSetFlagsRule.enableFlags(
|
||||
Flags.FLAG_ALLOW_PRIVATE_PROFILE,
|
||||
android.multiuser.Flags.FLAG_ENABLE_PS_SENSITIVE_NOTIFICATIONS_TOGGLE);
|
||||
android.multiuser.Flags.FLAG_ENABLE_PS_SENSITIVE_NOTIFICATIONS_TOGGLE,
|
||||
android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
|
||||
PrivateSpaceMaintainer privateSpaceMaintainer =
|
||||
PrivateSpaceMaintainer.getInstance(mContext);
|
||||
privateSpaceMaintainer.deletePrivateSpace();
|
||||
@@ -259,7 +260,8 @@ public class PrivateSpaceMaintainerTest {
|
||||
public void createPrivateSpace_psDoesNotExist_resetsPSAutoLockSettings() {
|
||||
mSetFlagsRule.enableFlags(
|
||||
Flags.FLAG_ALLOW_PRIVATE_PROFILE,
|
||||
android.multiuser.Flags.FLAG_SUPPORT_AUTOLOCK_FOR_PRIVATE_SPACE);
|
||||
android.multiuser.Flags.FLAG_SUPPORT_AUTOLOCK_FOR_PRIVATE_SPACE,
|
||||
android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
|
||||
final int autoLockOption = 2;
|
||||
PrivateSpaceMaintainer privateSpaceMaintainer =
|
||||
PrivateSpaceMaintainer.getInstance(mContext);
|
||||
@@ -282,7 +284,8 @@ public class PrivateSpaceMaintainerTest {
|
||||
public void createPrivateSpace_psExists_doesNotResetPSAutoLockSettings() {
|
||||
mSetFlagsRule.enableFlags(
|
||||
Flags.FLAG_ALLOW_PRIVATE_PROFILE,
|
||||
android.multiuser.Flags.FLAG_SUPPORT_AUTOLOCK_FOR_PRIVATE_SPACE);
|
||||
android.multiuser.Flags.FLAG_SUPPORT_AUTOLOCK_FOR_PRIVATE_SPACE,
|
||||
android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
|
||||
final int privateSpaceAutLockValue = 1;
|
||||
PrivateSpaceMaintainer privateSpaceMaintainer =
|
||||
PrivateSpaceMaintainer.getInstance(mContext);
|
||||
|
||||
@@ -86,7 +86,8 @@ public class PrivateSpaceSafetySourceTest {
|
||||
@Test
|
||||
public void onDeviceRebootedEvent_whenSafetyCenterEnabled_setsData() {
|
||||
when(mSafetyCenterManagerWrapper.isEnabled(mContext)).thenReturn(true);
|
||||
mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE);
|
||||
mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE,
|
||||
android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
|
||||
|
||||
PrivateSpaceSafetySource.setSafetySourceData(mContext, EVENT_TYPE_DEVICE_REBOOTED);
|
||||
|
||||
@@ -98,7 +99,8 @@ public class PrivateSpaceSafetySourceTest {
|
||||
@Test
|
||||
public void setSafetySourceData_whenFeatureDisabled_setsNullData() {
|
||||
when(mSafetyCenterManagerWrapper.isEnabled(mContext)).thenReturn(true);
|
||||
mSetFlagsRule.disableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE);
|
||||
mSetFlagsRule.disableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE,
|
||||
android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
|
||||
|
||||
PrivateSpaceSafetySource.setSafetySourceData(mContext, EVENT_TYPE_DEVICE_REBOOTED);
|
||||
|
||||
@@ -113,7 +115,8 @@ public class PrivateSpaceSafetySourceTest {
|
||||
@Test
|
||||
public void setSafetySourceData_setsEnabled() {
|
||||
when(mSafetyCenterManagerWrapper.isEnabled(mContext)).thenReturn(true);
|
||||
mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE);
|
||||
mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE,
|
||||
android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
|
||||
|
||||
PrivateSpaceSafetySource.setSafetySourceData(mContext, EVENT_TYPE_DEVICE_REBOOTED);
|
||||
|
||||
@@ -129,7 +132,8 @@ public class PrivateSpaceSafetySourceTest {
|
||||
@Test
|
||||
public void setSafetySourceData_setsPsAuthenticatorIntent() {
|
||||
when(mSafetyCenterManagerWrapper.isEnabled(mContext)).thenReturn(true);
|
||||
mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE);
|
||||
mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE,
|
||||
android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
|
||||
|
||||
PrivateSpaceSafetySource.setSafetySourceData(mContext, EVENT_TYPE_DEVICE_REBOOTED);
|
||||
|
||||
|
||||
@@ -74,7 +74,8 @@ public class UseOneLockControllerTest {
|
||||
/** Tests that the controller is always available. */
|
||||
@Test
|
||||
public void getAvailabilityStatus_returnsAvailable() {
|
||||
mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE);
|
||||
mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE,
|
||||
android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
|
||||
|
||||
assertThat(mUseOneLockController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
|
||||
}
|
||||
@@ -87,7 +88,8 @@ public class UseOneLockControllerTest {
|
||||
.when(mLockPatternUtils).isSeparateProfileChallengeEnabled(anyInt());
|
||||
doReturn(CREDENTIAL_TYPE_PATTERN)
|
||||
.when(mLockPatternUtils).getCredentialTypeForUser(anyInt());
|
||||
mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE);
|
||||
mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE,
|
||||
android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
|
||||
|
||||
mUseOneLockController.updateState(mPreference);
|
||||
assertThat(mUseOneLockController.getSummary().toString()).isEqualTo("Pattern");
|
||||
@@ -99,7 +101,8 @@ public class UseOneLockControllerTest {
|
||||
doReturn(true)
|
||||
.when(mLockPatternUtils).isSeparateProfileChallengeEnabled(anyInt());
|
||||
doReturn(CREDENTIAL_TYPE_PIN).when(mLockPatternUtils).getCredentialTypeForUser(anyInt());
|
||||
mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE);
|
||||
mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE,
|
||||
android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
|
||||
|
||||
mUseOneLockController.updateState(mPreference);
|
||||
assertThat(mUseOneLockController.getSummary().toString()).isEqualTo("PIN");
|
||||
@@ -112,7 +115,8 @@ public class UseOneLockControllerTest {
|
||||
.when(mLockPatternUtils).isSeparateProfileChallengeEnabled(anyInt());
|
||||
doReturn(CREDENTIAL_TYPE_PASSWORD)
|
||||
.when(mLockPatternUtils).getCredentialTypeForUser(anyInt());
|
||||
mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE);
|
||||
mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE,
|
||||
android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
|
||||
|
||||
mUseOneLockController.updateState(mPreference);
|
||||
assertThat(mUseOneLockController.getSummary().toString()).isEqualTo("Password");
|
||||
|
||||
@@ -75,7 +75,8 @@ public class AutoLockPreferenceControllerTest {
|
||||
public void getAvailabilityStatus_withAutoLockFlagEnabled_returnsAvailable() {
|
||||
mSetFlagsRule.enableFlags(
|
||||
Flags.FLAG_ALLOW_PRIVATE_PROFILE,
|
||||
android.multiuser.Flags.FLAG_SUPPORT_AUTOLOCK_FOR_PRIVATE_SPACE);
|
||||
android.multiuser.Flags.FLAG_SUPPORT_AUTOLOCK_FOR_PRIVATE_SPACE,
|
||||
android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
|
||||
|
||||
assertThat(mAutoLockPreferenceController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
|
||||
}
|
||||
@@ -83,7 +84,8 @@ public class AutoLockPreferenceControllerTest {
|
||||
/** Tests that the controller is not available when auto lock flag is off. */
|
||||
@Test
|
||||
public void getAvailabilityStatus_withAutoLockFlagDisabled_returnsNull() {
|
||||
mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE);
|
||||
mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE,
|
||||
android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
|
||||
mSetFlagsRule.disableFlags(android.multiuser.Flags.FLAG_SUPPORT_AUTOLOCK_FOR_PRIVATE_SPACE);
|
||||
|
||||
assertThat(mAutoLockPreferenceController.getAvailabilityStatus())
|
||||
@@ -98,7 +100,8 @@ public class AutoLockPreferenceControllerTest {
|
||||
public void getSummary_whenOptionEveryTimeDeviceLocks_returnsEveryTimeDeviceLocks() {
|
||||
mSetFlagsRule.enableFlags(
|
||||
Flags.FLAG_ALLOW_PRIVATE_PROFILE,
|
||||
android.multiuser.Flags.FLAG_SUPPORT_AUTOLOCK_FOR_PRIVATE_SPACE);
|
||||
android.multiuser.Flags.FLAG_SUPPORT_AUTOLOCK_FOR_PRIVATE_SPACE,
|
||||
android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
|
||||
|
||||
Settings.Secure.putInt(
|
||||
mContentResolver,
|
||||
@@ -116,7 +119,8 @@ public class AutoLockPreferenceControllerTest {
|
||||
public void getSummary_whenOptionAfter5MinutesOfInactivity_returnsAfter5MinutesOfInactivity() {
|
||||
mSetFlagsRule.enableFlags(
|
||||
Flags.FLAG_ALLOW_PRIVATE_PROFILE,
|
||||
android.multiuser.Flags.FLAG_SUPPORT_AUTOLOCK_FOR_PRIVATE_SPACE);
|
||||
android.multiuser.Flags.FLAG_SUPPORT_AUTOLOCK_FOR_PRIVATE_SPACE,
|
||||
android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
|
||||
|
||||
Settings.Secure.putInt(
|
||||
mContentResolver,
|
||||
@@ -131,7 +135,8 @@ public class AutoLockPreferenceControllerTest {
|
||||
public void getSummary_whenOptionNever_returnsNever() {
|
||||
mSetFlagsRule.enableFlags(
|
||||
Flags.FLAG_ALLOW_PRIVATE_PROFILE,
|
||||
android.multiuser.Flags.FLAG_SUPPORT_AUTOLOCK_FOR_PRIVATE_SPACE);
|
||||
android.multiuser.Flags.FLAG_SUPPORT_AUTOLOCK_FOR_PRIVATE_SPACE,
|
||||
android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
|
||||
|
||||
Settings.Secure.putInt(
|
||||
mContentResolver,
|
||||
|
||||
@@ -94,7 +94,8 @@ public class AutoLockSettingsFragmentTest {
|
||||
public void verifyMetricsConstant() {
|
||||
mSetFlagsRule.enableFlags(
|
||||
Flags.FLAG_ALLOW_PRIVATE_PROFILE,
|
||||
android.multiuser.Flags.FLAG_SUPPORT_AUTOLOCK_FOR_PRIVATE_SPACE);
|
||||
android.multiuser.Flags.FLAG_SUPPORT_AUTOLOCK_FOR_PRIVATE_SPACE,
|
||||
android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
|
||||
assertThat(mFragment.getMetricsCategory()).isEqualTo(SettingsEnums.PRIVATE_SPACE_SETTINGS);
|
||||
}
|
||||
|
||||
@@ -103,7 +104,8 @@ public class AutoLockSettingsFragmentTest {
|
||||
public void getCandidates_returnsCandidateInfoListWithAllKeys() {
|
||||
mSetFlagsRule.enableFlags(
|
||||
Flags.FLAG_ALLOW_PRIVATE_PROFILE,
|
||||
android.multiuser.Flags.FLAG_SUPPORT_AUTOLOCK_FOR_PRIVATE_SPACE);
|
||||
android.multiuser.Flags.FLAG_SUPPORT_AUTOLOCK_FOR_PRIVATE_SPACE,
|
||||
android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
|
||||
mFragment.onAttach(mContext);
|
||||
|
||||
final List<? extends CandidateInfo> candidates = mFragment.getCandidates();
|
||||
@@ -120,7 +122,8 @@ public class AutoLockSettingsFragmentTest {
|
||||
public void getDefaultKey_returnsStoredAutoLockOptionsValue() {
|
||||
mSetFlagsRule.enableFlags(
|
||||
Flags.FLAG_ALLOW_PRIVATE_PROFILE,
|
||||
android.multiuser.Flags.FLAG_SUPPORT_AUTOLOCK_FOR_PRIVATE_SPACE);
|
||||
android.multiuser.Flags.FLAG_SUPPORT_AUTOLOCK_FOR_PRIVATE_SPACE,
|
||||
android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
|
||||
|
||||
mFragment.onAttach(mContext);
|
||||
|
||||
@@ -144,7 +147,8 @@ public class AutoLockSettingsFragmentTest {
|
||||
public void setDefaultKey_storesCorrectAutoLockOptionValue() {
|
||||
mSetFlagsRule.enableFlags(
|
||||
Flags.FLAG_ALLOW_PRIVATE_PROFILE,
|
||||
android.multiuser.Flags.FLAG_SUPPORT_AUTOLOCK_FOR_PRIVATE_SPACE);
|
||||
android.multiuser.Flags.FLAG_SUPPORT_AUTOLOCK_FOR_PRIVATE_SPACE,
|
||||
android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
|
||||
|
||||
mFragment.onAttach(mContext);
|
||||
mFragment.setDefaultKey("2");
|
||||
|
||||
@@ -58,7 +58,8 @@ public class DeletePrivateSpaceControllerTest {
|
||||
/** Tests that the controller is available when private space flag is enabled. */
|
||||
@Test
|
||||
public void getAvailabilityStatus_whenPrivateFlagEnabled_returnsAvailable() {
|
||||
mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE);
|
||||
mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE,
|
||||
android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
|
||||
|
||||
assertThat(mDeletePrivateSpaceController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
|
||||
}
|
||||
@@ -66,7 +67,8 @@ public class DeletePrivateSpaceControllerTest {
|
||||
/** Tests that the controller is not available when private space flag is disabled. */
|
||||
@Test
|
||||
public void getAvailabilityStatus_whenPrivateFlagDisabled_returnsUnsupportedOnDevice() {
|
||||
mSetFlagsRule.disableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE);
|
||||
mSetFlagsRule.disableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE,
|
||||
android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
|
||||
|
||||
assertThat(mDeletePrivateSpaceController.getAvailabilityStatus())
|
||||
.isEqualTo(UNSUPPORTED_ON_DEVICE);
|
||||
|
||||
@@ -39,7 +39,8 @@ public class PrivateSpaceDeleteFragmentTest {
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void verifyMetricsConstant() {
|
||||
mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE);
|
||||
mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE,
|
||||
android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
|
||||
mFragment = spy(new PrivateSpaceDeleteFragment());
|
||||
assertThat(mFragment.getMetricsCategory()).isEqualTo(SettingsEnums.PRIVATE_SPACE_SETTINGS);
|
||||
}
|
||||
|
||||
@@ -77,7 +77,8 @@ public class PrivateSpaceDeletionProgressFragmentTest {
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void verifyMetricsConstant() {
|
||||
mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE);
|
||||
mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE,
|
||||
android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
|
||||
assertThat(mFragment.getMetricsCategory()).isEqualTo(SettingsEnums.PRIVATE_SPACE_SETTINGS);
|
||||
}
|
||||
|
||||
@@ -87,7 +88,8 @@ public class PrivateSpaceDeletionProgressFragmentTest {
|
||||
public void deletePrivateSpace_deletesPS() {
|
||||
PrivateSpaceDeletionProgressFragment spyFragment = spy(mFragment);
|
||||
doNothing().when(spyFragment).showSuccessfulDeletionToast();
|
||||
mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE);
|
||||
mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE,
|
||||
android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
|
||||
|
||||
mPrivateSpaceMaintainer.createPrivateSpace();
|
||||
spyFragment.deletePrivateSpace();
|
||||
@@ -100,7 +102,8 @@ public class PrivateSpaceDeletionProgressFragmentTest {
|
||||
public void deletePrivateSpace_onDeletion_showsDeletedToast() {
|
||||
PrivateSpaceDeletionProgressFragment spyFragment = spy(mFragment);
|
||||
doNothing().when(spyFragment).showSuccessfulDeletionToast();
|
||||
mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE);
|
||||
mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE,
|
||||
android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
|
||||
|
||||
mPrivateSpaceMaintainer.createPrivateSpace();
|
||||
spyFragment.deletePrivateSpace();
|
||||
@@ -123,7 +126,8 @@ public class PrivateSpaceDeletionProgressFragmentTest {
|
||||
spyFragment.setPrivateSpaceMaintainer(injector);
|
||||
doReturn(DELETE_PS_ERROR_INTERNAL).when(mPrivateSpaceMaintainerMock).deletePrivateSpace();
|
||||
doNothing().when(spyFragment).showDeletionInternalErrorToast();
|
||||
mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE);
|
||||
mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE,
|
||||
android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
|
||||
|
||||
spyFragment.deletePrivateSpace();
|
||||
|
||||
|
||||
@@ -247,7 +247,8 @@ public class SafetySourceBroadcastReceiverTest {
|
||||
@Test
|
||||
public void onReceive_onRefresh_withPrivateSpaceFeatureDisabled_setsNullData() {
|
||||
when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true);
|
||||
mSetFlagsRule.disableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE);
|
||||
mSetFlagsRule.disableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE,
|
||||
android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
|
||||
|
||||
Intent intent =
|
||||
new Intent()
|
||||
|
||||
Reference in New Issue
Block a user