Snap for 11545716 from 34bd0481df to 24Q3-release

Change-Id: I3bb1ac950bccd84d04a6aa8a010dbb2c42a1d831
This commit is contained in:
Android Build Coastguard Worker
2024-03-08 00:21:15 +00:00
100 changed files with 1397 additions and 2439 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,4 +0,0 @@
# Bug component: 316234
sudheersai@google.com
yamasani@google.com

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -24,6 +24,7 @@ import android.telephony.UiccCardInfo
import android.telephony.UiccSlotInfo
import android.util.Log
import com.android.settings.network.SimOnboardingActivity.Companion.CallbackType
import com.android.settings.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
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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