diff --git a/src/com/android/settings/network/MobileNetworkSummaryController.java b/src/com/android/settings/network/MobileNetworkSummaryController.java index abffe112010..f2e28a33888 100644 --- a/src/com/android/settings/network/MobileNetworkSummaryController.java +++ b/src/com/android/settings/network/MobileNetworkSummaryController.java @@ -23,7 +23,6 @@ import android.content.Context; import android.content.Intent; import android.os.UserManager; import android.provider.Settings; -import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.telephony.euicc.EuiccManager; @@ -36,8 +35,9 @@ import androidx.preference.PreferenceScreen; import com.android.settings.R; import com.android.settings.core.PreferenceControllerMixin; import com.android.settings.dashboard.DashboardFragment; +import com.android.settings.network.helper.SelectableSubscriptions; +import com.android.settings.network.helper.SubscriptionAnnotation; import com.android.settings.network.telephony.MobileNetworkActivity; -import com.android.settings.network.telephony.MobileNetworkUtils; import com.android.settings.overlay.FeatureFactory; import com.android.settings.widget.AddPreference; import com.android.settingslib.Utils; @@ -61,6 +61,8 @@ public class MobileNetworkSummaryController extends AbstractPreferenceController private SubscriptionsChangeListener mChangeListener; private AddPreference mPreference; + private MobileNetworkSummaryStatus mStatusCache = new MobileNetworkSummaryStatus(); + /** * This controls the summary text and click behavior of the "Mobile network" item on the * Network & internet page. There are 3 separate cases depending on the number of mobile network @@ -82,8 +84,8 @@ public class MobileNetworkSummaryController extends AbstractPreferenceController mSubscriptionManager = context.getSystemService(SubscriptionManager.class); mUserManager = context.getSystemService(UserManager.class); if (lifecycle != null) { - mChangeListener = new SubscriptionsChangeListener(context, this); - lifecycle.addObserver(this); + mChangeListener = new SubscriptionsChangeListener(context, this); + lifecycle.addObserver(this); } } @@ -106,25 +108,24 @@ public class MobileNetworkSummaryController extends AbstractPreferenceController @Override public CharSequence getSummary() { - final List subs = SubscriptionUtil.getAvailableSubscriptions( - mContext); + mStatusCache.update(mContext, null); + List subs = mStatusCache.getSubscriptionList(); + if (subs.isEmpty()) { - if (MobileNetworkUtils.showEuiccSettings(mContext)) { + if (mStatusCache.isEuiccConfigSupport()) { return mContext.getResources().getString( R.string.mobile_network_summary_add_a_network); } - return null; + // set empty string to override previous text for carrier when SIM available + return ""; } else if (subs.size() == 1) { - final SubscriptionInfo info = subs.get(0); - final CharSequence displayName = SubscriptionUtil.getUniqueSubscriptionDisplayName( - info, mContext); - final int subId = info.getSubscriptionId(); - if (!info.isEmbedded() && !mSubscriptionManager.isActiveSubscriptionId(subId) - && !SubscriptionUtil.showToggleForPhysicalSim(mSubscriptionManager)) { - return mContext.getString(R.string.mobile_network_tap_to_activate, displayName); - } else { + SubscriptionAnnotation info = subs.get(0); + CharSequence displayName = mStatusCache.getDisplayName(info.getSubscriptionId()); + if (info.getSubInfo().isEmbedded() || info.isActive() + || mStatusCache.isPhysicalSimDisableSupport()) { return displayName; } + return mContext.getString(R.string.mobile_network_tap_to_activate, displayName); } else { if (com.android.settings.Utils.isProviderModelEnabled(mContext)) { return getSummaryForProviderModel(subs); @@ -135,10 +136,16 @@ public class MobileNetworkSummaryController extends AbstractPreferenceController } } - private CharSequence getSummaryForProviderModel(List subs) { - return String.join(", ", subs.stream().map(subInfo -> { - return SubscriptionUtil.getUniqueSubscriptionDisplayName(subInfo, mContext); - }).collect(Collectors.toList())); + private CharSequence getSummaryForProviderModel(List subs) { + return subs.stream() + .mapToInt(SubscriptionAnnotation::getSubscriptionId) + .mapToObj(subId -> mStatusCache.getDisplayName(subId)) + .collect(Collectors.joining(", ")); + } + + private void logPreferenceClick(Preference preference) { + mMetricsFeatureProvider.logClickedPreference(preference, + preference.getExtras().getInt(DashboardFragment.CATEGORY)); } private void startAddSimFlow() { @@ -147,62 +154,64 @@ public class MobileNetworkSummaryController extends AbstractPreferenceController mContext.startActivity(intent); } - private void update() { - if (mPreference == null || mPreference.isDisabledByAdmin()) { - return; - } + private void initPreference() { refreshSummary(mPreference); mPreference.setOnPreferenceClickListener(null); mPreference.setOnAddClickListener(null); mPreference.setFragment(null); mPreference.setEnabled(!mChangeListener.isAirplaneModeOn()); + } - final List subs = SubscriptionUtil.getAvailableSubscriptions( - mContext); + private void update() { + if (mPreference == null || mPreference.isDisabledByAdmin()) { + return; + } + mStatusCache.update(mContext, statusCache -> initPreference()); + + List subs = mStatusCache.getSubscriptionList(); if (subs.isEmpty()) { - if (MobileNetworkUtils.showEuiccSettings(mContext)) { + if (mStatusCache.isEuiccConfigSupport()) { mPreference.setOnPreferenceClickListener((Preference pref) -> { - mMetricsFeatureProvider.logClickedPreference(pref, - pref.getExtras().getInt(DashboardFragment.CATEGORY)); + logPreferenceClick(pref); startAddSimFlow(); return true; }); } else { mPreference.setEnabled(false); } - } else { - // We have one or more existing subscriptions, so we want the plus button if eSIM is - // supported. - if (MobileNetworkUtils.showEuiccSettings(mContext)) { - mPreference.setAddWidgetEnabled(!mChangeListener.isAirplaneModeOn()); - mPreference.setOnAddClickListener(p -> { - mMetricsFeatureProvider.logClickedPreference(p, - p.getExtras().getInt(DashboardFragment.CATEGORY)); - startAddSimFlow(); - }); - } + return; + } - if (subs.size() == 1) { - mPreference.setOnPreferenceClickListener((Preference pref) -> { - mMetricsFeatureProvider.logClickedPreference(pref, - pref.getExtras().getInt(DashboardFragment.CATEGORY)); - final SubscriptionInfo info = subs.get(0); - final int subId = info.getSubscriptionId(); - if (!info.isEmbedded() && !mSubscriptionManager.isActiveSubscriptionId(subId) - && !SubscriptionUtil.showToggleForPhysicalSim(mSubscriptionManager)) { - SubscriptionUtil.startToggleSubscriptionDialogActivity( - mContext, subId, true); - } else { - final Intent intent = new Intent(mContext, MobileNetworkActivity.class); - intent.putExtra(Settings.EXTRA_SUB_ID, subs.get(0).getSubscriptionId()); - mContext.startActivity(intent); - } + // We have one or more existing subscriptions, so we want the plus button if eSIM is + // supported. + if (mStatusCache.isEuiccConfigSupport()) { + mPreference.setAddWidgetEnabled(!mChangeListener.isAirplaneModeOn()); + mPreference.setOnAddClickListener(p -> { + logPreferenceClick(p); + startAddSimFlow(); + }); + } + + if (subs.size() == 1) { + mPreference.setOnPreferenceClickListener((Preference pref) -> { + logPreferenceClick(pref); + + SubscriptionAnnotation info = subs.get(0); + if (info.getSubInfo().isEmbedded() || info.isActive() + || mStatusCache.isPhysicalSimDisableSupport()) { + final Intent intent = new Intent(mContext, MobileNetworkActivity.class); + intent.putExtra(Settings.EXTRA_SUB_ID, info.getSubscriptionId()); + mContext.startActivity(intent); return true; - }); - } else { - mPreference.setFragment(MobileNetworkListFragment.class.getCanonicalName()); - } + } + + SubscriptionUtil.startToggleSubscriptionDialogActivity( + mContext, info.getSubscriptionId(), true); + return true; + }); + } else { + mPreference.setFragment(MobileNetworkListFragment.class.getCanonicalName()); } } @@ -218,12 +227,14 @@ public class MobileNetworkSummaryController extends AbstractPreferenceController @Override public void onAirplaneModeChanged(boolean airplaneModeEnabled) { - update(); + mStatusCache.update(mContext, statusCache -> update()); } @Override public void onSubscriptionsChanged() { - refreshSummary(mPreference); - update(); + mStatusCache.update(mContext, statusCache -> { + refreshSummary(mPreference); + update(); + }); } } diff --git a/src/com/android/settings/network/MobileNetworkSummaryStatus.java b/src/com/android/settings/network/MobileNetworkSummaryStatus.java new file mode 100644 index 00000000000..b22e48ed021 --- /dev/null +++ b/src/com/android/settings/network/MobileNetworkSummaryStatus.java @@ -0,0 +1,176 @@ +/* + * 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; + +import android.content.Context; +import android.telephony.SubscriptionManager; +import android.util.Log; + +import com.android.settings.network.SubscriptionUtil; +import com.android.settings.network.helper.SelectableSubscriptions; +import com.android.settings.network.helper.SubscriptionAnnotation; +import com.android.settings.network.helper.SubscriptionGrouping; +import com.android.settings.network.telephony.MobileNetworkUtils; +import com.android.settingslib.utils.ThreadUtils; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.Future; +import java.util.function.Consumer; +import java.util.stream.Collectors; + +/** + * This one keeps the information required by MobileNetworkSummaryController. + */ +public class MobileNetworkSummaryStatus { + private static final String LOG_TAG = "MobileNetworkSummaryStatus"; + + private Future> mUniqueNameMapping; + private Map mUniqueNameMappingCache; + + private Future mIsEuiccConfiguable; + private Boolean mIsEuiccConfiguableCache; + + private Future mIsPsimDisableSupported; + private Boolean mIsPsimDisableSupportedCache; + + private List mSubscriptionList; + + private boolean mDisableReEntranceUpdate; + + // Constructor + public MobileNetworkSummaryStatus() {} + + /** + * Update the status + * @param context + * @param andThen Consumer which always performed by the end of #update() + * and avoid from repeated queries. + */ + public void update(Context context, Consumer andThen) { + if (mDisableReEntranceUpdate) { + Log.d(LOG_TAG, "network summary query ignored"); + if (andThen != null) { + andThen.accept(this); + } + return; + } + mDisableReEntranceUpdate = true; + Log.d(LOG_TAG, "network summary query"); + + // Query Euicc in background + mIsEuiccConfiguable = (Future) + ThreadUtils.postOnBackgroundThread(() -> isEuiccConfiguable(context)); + + // Query display name in background + mUniqueNameMapping = (Future>) + ThreadUtils.postOnBackgroundThread(() -> getUniqueNameForDisplay(context)); + + // Query support status of pSIM disable feature + mIsPsimDisableSupported = (Future) ThreadUtils.postOnBackgroundThread(() + -> isPhysicalSimDisableSupported(context)); + + // Query subscription + mSubscriptionList = getSubscriptions(context); + + if (andThen != null) { + andThen.accept(this); + } + mDisableReEntranceUpdate = false; + } + + /** + * Get the subscription information + * @return a list of SubscriptionAnnotation + */ + public List getSubscriptionList() { + return mSubscriptionList; + } + + /** + * Get unique display name for a specific subscription + * @param subscriptionId subscription ID + * @return display name for that subscription + */ + public CharSequence getDisplayName(int subscriptionId) { + if (mUniqueNameMapping != null) { + try { + mUniqueNameMappingCache = mUniqueNameMapping.get(); + } catch (Exception exception) { + Log.w(LOG_TAG, "Fail to get display names", exception); + } + mUniqueNameMapping = null; + } + if (mUniqueNameMappingCache == null) { + return null; + } + return mUniqueNameMappingCache.get(subscriptionId); + } + + // Check if Euicc is currently available + public boolean isEuiccConfigSupport() { + if (mIsEuiccConfiguable != null) { + try { + mIsEuiccConfiguableCache = mIsEuiccConfiguable.get(); + } catch (Exception exception) { + Log.w(LOG_TAG, "Fail to get euicc config status", exception); + } + mIsEuiccConfiguable = null; + } + return (mIsEuiccConfiguableCache == null) ? + false : mIsEuiccConfiguableCache.booleanValue(); + } + + // Check if disable physical SIM is supported + public boolean isPhysicalSimDisableSupport() { + if (mIsPsimDisableSupported != null) { + try { + mIsPsimDisableSupportedCache = mIsPsimDisableSupported.get(); + } catch (Exception exception) { + Log.w(LOG_TAG, "Fail to get pSIM disable support", exception); + } + mIsPsimDisableSupported = null; + } + return (mIsPsimDisableSupportedCache == null) ? + false : mIsPsimDisableSupportedCache.booleanValue(); + } + + private List getSubscriptions(Context context) { + return (new SelectableSubscriptions(context, true)) + + // To maintain the consistency with SubscriptionUtil#getAvailableSubscriptions(). + .addFinisher(new SubscriptionGrouping()) + + .call() + .stream() + .filter(SubscriptionAnnotation::isDisplayAllowed) + .collect(Collectors.toList()); + } + + private Map getUniqueNameForDisplay(Context context) { + return SubscriptionUtil.getUniqueSubscriptionDisplayNames(context); + } + + private boolean isPhysicalSimDisableSupported(Context context) { + SubscriptionManager subMgr = context.getSystemService(SubscriptionManager.class); + return SubscriptionUtil.showToggleForPhysicalSim(subMgr); + } + + private boolean isEuiccConfiguable(Context context) { + return MobileNetworkUtils.showEuiccSettingsDetecting(context); + } +} diff --git a/src/com/android/settings/network/helper/QueryEsimCardId.java b/src/com/android/settings/network/helper/QueryEsimCardId.java index dc29c47f133..7ac73726070 100644 --- a/src/com/android/settings/network/helper/QueryEsimCardId.java +++ b/src/com/android/settings/network/helper/QueryEsimCardId.java @@ -50,8 +50,7 @@ public class QueryEsimCardId implements Callable { } return new AtomicIntegerArray(cardInfos.stream() .filter(Objects::nonNull) - .filter(cardInfo -> (!cardInfo.isRemovable() - && (cardInfo.getCardId() != TelephonyManager.UNSUPPORTED_CARD_ID))) + .filter(cardInfo -> (!cardInfo.isRemovable() && (cardInfo.getCardId() >= 0))) .mapToInt(UiccCardInfo::getCardId) .toArray()); } diff --git a/src/com/android/settings/network/helper/SelectableSubscriptions.java b/src/com/android/settings/network/helper/SelectableSubscriptions.java index 436e84c08b7..45d842b29b9 100644 --- a/src/com/android/settings/network/helper/SelectableSubscriptions.java +++ b/src/com/android/settings/network/helper/SelectableSubscriptions.java @@ -71,8 +71,17 @@ public class SelectableSubscriptions implements Callable getAvailableSubInfoList(context)) : (() -> getActiveSubInfoList(context)); - mFilter = disabledSlotsIncluded ? (subAnno -> subAnno.isExisted()) : - (subAnno -> subAnno.isActive()); + if (disabledSlotsIncluded) { + mFilter = subAnno -> { + if (subAnno.isExisted()) { + return true; + } + return ((subAnno.getType() == SubscriptionAnnotation.TYPE_ESIM) + && (subAnno.isDisplayAllowed())); + }; + } else { + mFilter = subAnno -> subAnno.isActive(); + } mFinisher = annoList -> annoList; } diff --git a/src/com/android/settings/network/helper/SubscriptionAnnotation.java b/src/com/android/settings/network/helper/SubscriptionAnnotation.java index f6d3ccd53ee..29d4fb5b900 100644 --- a/src/com/android/settings/network/helper/SubscriptionAnnotation.java +++ b/src/com/android/settings/network/helper/SubscriptionAnnotation.java @@ -93,10 +93,9 @@ public class SubscriptionAnnotation { if (mType == TYPE_ESIM) { int cardId = mSubInfo.getCardId(); mIsExisted = eSimCardId.contains(cardId); - if (mIsExisted) { - mIsActive = activeSimSlotIndexList.contains(mSubInfo.getSimSlotIndex()); - mIsAllowToDisplay = isDisplayAllowed(context); - } + mIsActive = activeSimSlotIndexList.contains(mSubInfo.getSimSlotIndex()); + mIsAllowToDisplay = (cardId < 0) // always allow when eSIM not in slot + || isDisplayAllowed(context); return; } @@ -159,4 +158,11 @@ public class SubscriptionAnnotation { return SubscriptionUtil.isSubscriptionVisible( context.getSystemService(SubscriptionManager.class), context, mSubInfo); } + + public String toString() { + return TAG + "{" + "subId=" + getSubscriptionId() + + ",type=" + getType() + ",exist=" + isExisted() + + ",active=" + isActive() + ",displayAllow=" + isDisplayAllowed() + + "}"; + } } \ No newline at end of file diff --git a/src/com/android/settings/network/telephony/MobileNetworkUtils.java b/src/com/android/settings/network/telephony/MobileNetworkUtils.java index 78d12378c0b..18984848b2f 100644 --- a/src/com/android/settings/network/telephony/MobileNetworkUtils.java +++ b/src/com/android/settings/network/telephony/MobileNetworkUtils.java @@ -266,7 +266,8 @@ public class MobileNetworkUtils { return false; } - private static Boolean showEuiccSettingsDetecting(Context context) { + // The same as #showEuiccSettings(Context context) + public static Boolean showEuiccSettingsDetecting(Context context) { final EuiccManager euiccManager = (EuiccManager) context.getSystemService(EuiccManager.class); if (!euiccManager.isEnabled()) {