From fb4a0c25e0931d049221f27145a20377cce2bd2f Mon Sep 17 00:00:00 2001 From: Bonian Chen Date: Fri, 29 May 2020 14:53:43 +0800 Subject: [PATCH 1/5] [Settings] Avoid crash when switch SIM during PIN lock PIN lock status are stored without subscription ID mapping on it. Which makes SIM status change can't be processed correctly. Through storing the subscription ID for PIN lock screen, dialog can be reset when SIM status changed and crash can be avoided. Bug: 155852345 Test: manual Change-Id: Ic520bc34c5a460b4fcd199cd524b180d307e9ab4 --- src/com/android/settings/IccLockSettings.java | 187 ++++++++++++------ 1 file changed, 124 insertions(+), 63 deletions(-) diff --git a/src/com/android/settings/IccLockSettings.java b/src/com/android/settings/IccLockSettings.java index 4d12043f1b2..77cc03e92e7 100644 --- a/src/com/android/settings/IccLockSettings.java +++ b/src/com/android/settings/IccLockSettings.java @@ -87,6 +87,7 @@ public class IccLockSettings extends SettingsPreferenceFragment private static final String PIN_DIALOG = "sim_pin"; private static final String PIN_TOGGLE = "sim_toggle"; // Keys in icicle + private static final String DIALOG_SUB_ID = "dialogSubId"; private static final String DIALOG_STATE = "dialogState"; private static final String DIALOG_PIN = "dialogPin"; private static final String DIALOG_ERROR = "dialogError"; @@ -127,7 +128,7 @@ public class IccLockSettings extends SettingsPreferenceFragment // @see android.widget.Toast$TN private static final long LONG_DURATION_TIMEOUT = 7000; - private int mSlotId; + private int mSlotId = -1; private int mSubId; private TelephonyManager mTelephonyManager; @@ -155,7 +156,7 @@ public class IccLockSettings extends SettingsPreferenceFragment // For top-level settings screen to query private boolean isIccLockEnabled() { - mTelephonyManager = mTelephonyManager.createForSubscriptionId(mSubId); + mTelephonyManager = mTelephonyManager.createForSubscriptionId(mSubId); return mTelephonyManager.isIccLockEnabled(); } @@ -187,27 +188,13 @@ public class IccLockSettings extends SettingsPreferenceFragment mPinDialog = (EditPinPreference) findPreference(PIN_DIALOG); mPinToggle = (SwitchPreference) findPreference(PIN_TOGGLE); - if (savedInstanceState != null && savedInstanceState.containsKey(DIALOG_STATE)) { - mDialogState = savedInstanceState.getInt(DIALOG_STATE); - mPin = savedInstanceState.getString(DIALOG_PIN); - mError = savedInstanceState.getString(DIALOG_ERROR); - mToState = savedInstanceState.getBoolean(ENABLE_TO_STATE); - - // Restore inputted PIN code - switch (mDialogState) { - case ICC_NEW_MODE: - mOldPin = savedInstanceState.getString(OLD_PINCODE); - break; - - case ICC_REENTER_MODE: - mOldPin = savedInstanceState.getString(OLD_PINCODE); - mNewPin = savedInstanceState.getString(NEW_PINCODE); - break; - - case ICC_LOCK_MODE: - case ICC_OLD_MODE: - default: - break; + if (savedInstanceState != null) { + if (savedInstanceState.containsKey(DIALOG_STATE) + && restoreDialogStates(savedInstanceState)) { + Log.d(TAG, "onCreate: restore dialog for slotId=" + mSlotId + ", subId=" + mSubId); + } else if (savedInstanceState.containsKey(CURRENT_TAB) + && restoreTabFocus(savedInstanceState)) { + Log.d(TAG, "onCreate: restore focus on slotId=" + mSlotId + ", subId=" + mSubId); } } @@ -219,29 +206,75 @@ public class IccLockSettings extends SettingsPreferenceFragment mRes = getResources(); } + private boolean restoreDialogStates(Bundle savedInstanceState) { + final SubscriptionInfo subInfo = mProxySubscriptionMgr + .getActiveSubscriptionInfo(savedInstanceState.getInt(DIALOG_SUB_ID)); + if (subInfo == null) { + return false; + } + + final SubscriptionInfo visibleSubInfo = getVisibleSubscriptionInfoForSimSlotIndex( + subInfo.getSimSlotIndex()); + if (visibleSubInfo == null) { + return false; + } + if (visibleSubInfo.getSubscriptionId() != subInfo.getSubscriptionId()) { + return false; + } + + mSlotId = subInfo.getSimSlotIndex(); + mSubId = subInfo.getSubscriptionId(); + mDialogState = savedInstanceState.getInt(DIALOG_STATE); + mPin = savedInstanceState.getString(DIALOG_PIN); + mError = savedInstanceState.getString(DIALOG_ERROR); + mToState = savedInstanceState.getBoolean(ENABLE_TO_STATE); + + // Restore inputted PIN code + switch (mDialogState) { + case ICC_NEW_MODE: + mOldPin = savedInstanceState.getString(OLD_PINCODE); + break; + + case ICC_REENTER_MODE: + mOldPin = savedInstanceState.getString(OLD_PINCODE); + mNewPin = savedInstanceState.getString(NEW_PINCODE); + break; + } + return true; + } + + private boolean restoreTabFocus(Bundle savedInstanceState) { + int slotId = 0; + try { + slotId = Integer.parseInt(savedInstanceState.getString(CURRENT_TAB)); + } catch (NumberFormatException exception) { + return false; + } + + final SubscriptionInfo subInfo = getVisibleSubscriptionInfoForSimSlotIndex(slotId); + if (subInfo == null) { + return false; + } + + mSlotId = subInfo.getSimSlotIndex(); + mSubId = subInfo.getSubscriptionId(); + if (mTabHost != null) { + mTabHost.setCurrentTabByTag(getTagForSlotId(mSlotId)); + } + return true; + } + @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { final int numSims = mProxySubscriptionMgr.getActiveSubscriptionInfoCountMax(); - final List subInfoList = - mProxySubscriptionMgr.getActiveSubscriptionsInfo(); - mSlotId = 0; final List componenterList = new ArrayList<>(); for (int i = 0; i < numSims; ++i) { - final SubscriptionInfo subInfo = - getActiveSubscriptionInfoForSimSlotIndex(subInfoList, i); + final SubscriptionInfo subInfo = getVisibleSubscriptionInfoForSimSlotIndex(i); if (subInfo != null) { - final CarrierConfigManager carrierConfigManager = getContext().getSystemService( - CarrierConfigManager.class); - final PersistableBundle bundle = carrierConfigManager.getConfigForSubId( - subInfo.getSubscriptionId()); - if (bundle != null - && !bundle.getBoolean(CarrierConfigManager - .KEY_HIDE_SIM_LOCK_SETTINGS_BOOL)) { - componenterList.add(subInfo); - } + componenterList.add(subInfo); } } @@ -250,6 +283,12 @@ public class IccLockSettings extends SettingsPreferenceFragment return super.onCreateView(inflater, container, savedInstanceState); } + if (mSlotId < 0) { + mSlotId = componenterList.get(0).getSimSlotIndex(); + mSubId = componenterList.get(0).getSubscriptionId(); + Log.d(TAG, "onCreateView: default slotId=" + mSlotId + ", subId=" + mSubId); + } + if (componenterList.size() > 1) { final View view = inflater.inflate(R.layout.icc_lock_tabs, container, false); final ViewGroup prefs_container = (ViewGroup) view.findViewById(R.id.prefs_container); @@ -262,25 +301,21 @@ public class IccLockSettings extends SettingsPreferenceFragment mListView = (ListView) view.findViewById(android.R.id.list); mTabHost.setup(); - mTabHost.setOnTabChangedListener(mTabListener); mTabHost.clearAllTabs(); for (SubscriptionInfo subInfo : componenterList) { - int slot = subInfo.getSimSlotIndex(); - mTabHost.addTab(buildTabSpec(String.valueOf(slot), + final int slot = subInfo.getSimSlotIndex(); + final String tag = getTagForSlotId(slot); + mTabHost.addTab(buildTabSpec(tag, String.valueOf(subInfo == null ? getContext().getString(R.string.sim_editor_title, slot + 1) : subInfo.getDisplayName()))); } - mSubId = componenterList.get(0).getSubscriptionId(); - - if (savedInstanceState != null && savedInstanceState.containsKey(CURRENT_TAB)) { - mTabHost.setCurrentTabByTag(savedInstanceState.getString(CURRENT_TAB)); - } + mTabHost.setCurrentTabByTag(getTagForSlotId(mSlotId)); + mTabHost.setOnTabChangedListener(mTabListener); return view; } else { - mSlotId = componenterList.get(0).getSimSlotIndex(); return super.onCreateView(inflater, container, savedInstanceState); } } @@ -293,17 +328,20 @@ public class IccLockSettings extends SettingsPreferenceFragment private void updatePreferences() { - final List subInfoList = - mProxySubscriptionMgr.getActiveSubscriptionsInfo(); - final SubscriptionInfo sir = getActiveSubscriptionInfoForSimSlotIndex(subInfoList, mSlotId); - mSubId = (sir == null) ? SubscriptionManager.INVALID_SUBSCRIPTION_ID - : sir.getSubscriptionId(); + final SubscriptionInfo sir = getVisibleSubscriptionInfoForSimSlotIndex(mSlotId); + final int subId = (sir != null) ? sir.getSubscriptionId() + : SubscriptionManager.INVALID_SUBSCRIPTION_ID; + + if (mSubId != subId) { + mSubId = subId; + resetDialogState(); + if ((mPinDialog != null) && mPinDialog.isDialogOpen()) { + mPinDialog.getDialog().dismiss(); + } + } if (mPinDialog != null) { mPinDialog.setEnabled(sir != null); - if (mSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { - mPinDialog.getDialog().dismiss(); - } } if (mPinToggle != null) { mPinToggle.setEnabled(sir != null); @@ -355,6 +393,7 @@ public class IccLockSettings extends SettingsPreferenceFragment // dialog state. In other cases, where this activity manually launches // the dialog, store the state of the dialog. if (mPinDialog.isDialogOpen()) { + out.putInt(DIALOG_SUB_ID, mSubId); out.putInt(DIALOG_STATE, mDialogState); out.putString(DIALOG_PIN, mPinDialog.getEditText().getText().toString()); out.putString(DIALOG_ERROR, mError); @@ -370,11 +409,6 @@ public class IccLockSettings extends SettingsPreferenceFragment out.putString(OLD_PINCODE, mOldPin); out.putString(NEW_PINCODE, mNewPin); break; - - case ICC_LOCK_MODE: - case ICC_OLD_MODE: - default: - break; } } else { super.onSaveInstanceState(out); @@ -672,23 +706,50 @@ public class IccLockSettings extends SettingsPreferenceFragment mDialogState = OFF_MODE; } - private static SubscriptionInfo getActiveSubscriptionInfoForSimSlotIndex( - List subInfoList, int slotId) { + private String getTagForSlotId(int slotId) { + return String.valueOf(slotId); + } + + private int getSlotIndexFromTag(String tag) { + int slotId = -1; + try { + slotId = Integer.parseInt(tag); + } catch (NumberFormatException exception) { + } + return slotId; + } + + private SubscriptionInfo getVisibleSubscriptionInfoForSimSlotIndex(int slotId) { + final List subInfoList = + mProxySubscriptionMgr.getActiveSubscriptionsInfo(); if (subInfoList == null) { return null; } + final CarrierConfigManager carrierConfigManager = getContext().getSystemService( + CarrierConfigManager.class); for (SubscriptionInfo subInfo : subInfoList) { - if (subInfo.getSimSlotIndex() == slotId) { + if ((isSubscriptionVisible(carrierConfigManager, subInfo) + && (subInfo.getSimSlotIndex() == slotId))) { return subInfo; } } return null; } + private boolean isSubscriptionVisible(CarrierConfigManager carrierConfigManager, + SubscriptionInfo subInfo) { + final PersistableBundle bundle = carrierConfigManager + .getConfigForSubId(subInfo.getSubscriptionId()); + if (bundle == null) { + return false; + } + return !bundle.getBoolean(CarrierConfigManager.KEY_HIDE_SIM_LOCK_SETTINGS_BOOL); + } + private OnTabChangeListener mTabListener = new OnTabChangeListener() { @Override public void onTabChanged(String tabId) { - mSlotId = Integer.parseInt(tabId); + mSlotId = getSlotIndexFromTag(tabId); // The User has changed tab; update the body. updatePreferences(); From 5e082cb40190a6abe5e2b5fc93ac86a724e8588a Mon Sep 17 00:00:00 2001 From: Yi-Ling Chuang Date: Tue, 2 Jun 2020 10:21:24 +0800 Subject: [PATCH 2/5] Fix contextual card robotests. We have to set SUPPORTED_SPECS for slice to test. Fixes: 157904094 Test: robotests Change-Id: I9f8edd7232d772b2376d4e6ec1e4738bc01ee4aa --- .../slices/SliceContextualCardRendererTest.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardRendererTest.java b/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardRendererTest.java index 154d106c12d..722833c8b04 100644 --- a/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardRendererTest.java +++ b/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardRendererTest.java @@ -36,6 +36,8 @@ import androidx.lifecycle.LiveData; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import androidx.slice.Slice; +import androidx.slice.SliceProvider; +import androidx.slice.widget.SliceLiveData; import com.android.settings.R; import com.android.settings.homepage.contextualcards.ContextualCard; @@ -79,6 +81,7 @@ public class SliceContextualCardRendererTest { mLifecycleOwner = new ContextualCardsFragment(); mRenderer = new SliceContextualCardRenderer(mActivity, mLifecycleOwner, mControllerRendererPool); + SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS); } @Test From f6841ef5483e38cd752c82f69e4780b8b842967e Mon Sep 17 00:00:00 2001 From: Tsung-Mao Fang Date: Fri, 29 May 2020 20:54:42 +0800 Subject: [PATCH 3/5] Add a setting for allowing overlays on Settings app In the past, we allowed non system overlays on user-debug rom. Acoording to the user-debug guildline, we should not have differnt behavior between the user build and user debug rom. To maintain the consistency between user and user debug rom, We're creating a new developer setting for allowing non-system overlay on Settings. By default, we don't allow any non-system app overlays on Settings app unless user turns it on explictly. Test: Run robotest. Turn on setting, see the overlays on Settings. Turn off setting, do not see the overlays on Settings. Fix: 144989059 Change-Id: I87f00a2eda91de003c6e542e7ec45a066f46fbf7 --- res/values/strings.xml | 5 + res/xml/development_settings.xml | 5 + .../core/HideNonSystemOverlayMixin.java | 5 +- .../DevelopmentSettingsDashboardFragment.java | 1 + .../OverlaySettingsPreferenceController.java | 88 +++++++++++++ .../core/HideNonSystemOverlayMixinTest.java | 27 ++-- ...erlaySettingsPreferenceControllerTest.java | 120 ++++++++++++++++++ 7 files changed, 241 insertions(+), 10 deletions(-) create mode 100644 src/com/android/settings/development/OverlaySettingsPreferenceController.java create mode 100644 tests/robotests/src/com/android/settings/development/OverlaySettingsPreferenceControllerTest.java diff --git a/res/values/strings.xml b/res/values/strings.xml index 182602691d5..b60bca7e1e0 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -12094,4 +12094,9 @@ Suspend execution for cached apps + + + Allow screen overlays on Settings + + Allow apps that can display over other apps to overlay Settings screens diff --git a/res/xml/development_settings.xml b/res/xml/development_settings.xml index cce0a37a506..e8605894f6d 100644 --- a/res/xml/development_settings.xml +++ b/res/xml/development_settings.xml @@ -251,6 +251,11 @@ android:title="@string/show_refresh_rate" android:summary="@string/show_refresh_rate_summary" /> + + Date: Sat, 30 May 2020 01:31:58 +0800 Subject: [PATCH 4/5] Suppress battery saver suggestion notification When users turn on battery saver manually more than 3 times, the "Tap to schedule Battery Saver" notification will suggest users to enable scheduling battery saver. But this notification is still shown if users have already turned on scheduling battery saver, which is caused by no mechanism to suppress this notification. Solution: Adding a mechanism to suppress battery saver suggestion if users scheduled battery saver. Bug: 147862112 Test: robotests Change-Id: I8290d76dac09104e64fb98b8a9f4d783a8177d7c --- ...rySaverScheduleRadioButtonsController.java | 7 ++++- ...verScheduleRadioButtonsControllerTest.java | 30 ++++++++++++++++++- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleRadioButtonsController.java b/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleRadioButtonsController.java index 2cf2b6df7e6..737fb0f8d2f 100644 --- a/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleRadioButtonsController.java +++ b/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleRadioButtonsController.java @@ -22,6 +22,7 @@ import android.os.PowerManager; import android.provider.Settings; import android.provider.Settings.Global; import android.text.TextUtils; + import com.android.settingslib.fuelgauge.BatterySaverUtils; /** @@ -72,7 +73,7 @@ public class BatterySaverScheduleRadioButtonsController { if (key == null) { return false; } - + final ContentResolver resolver = mContext.getContentResolver(); int mode = PowerManager.POWER_SAVE_MODE_TRIGGER_PERCENTAGE; int triggerLevel = 0; @@ -112,6 +113,10 @@ public class BatterySaverScheduleRadioButtonsController { if (mode != PowerManager.POWER_SAVE_MODE_TRIGGER_DYNAMIC) { Settings.Global.putInt(resolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, triggerLevel); } + // Suppress battery saver suggestion notification if enabling scheduling battery saver. + if (mode == PowerManager.POWER_SAVE_MODE_TRIGGER_DYNAMIC || triggerLevel != 0) { + BatterySaverUtils.suppressAutoBatterySaver(mContext); + } mSeekBarController.updateSeekBar(); return true; } diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleRadioButtonsControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleRadioButtonsControllerTest.java index 8654a4e5a7b..2919edc406c 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleRadioButtonsControllerTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleRadioButtonsControllerTest.java @@ -8,6 +8,7 @@ import android.os.PowerManager; import android.provider.Settings; import android.provider.Settings.Global; import android.provider.Settings.Secure; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -59,9 +60,36 @@ public class BatterySaverScheduleRadioButtonsControllerTest { @Test public void setDefaultKey_any_defaultsToNoScheduleIfWarningNotSeen() { Secure.putString( - mContext.getContentResolver(), Secure.LOW_POWER_WARNING_ACKNOWLEDGED, "null"); + mContext.getContentResolver(), Secure.LOW_POWER_WARNING_ACKNOWLEDGED, "null"); mController.setDefaultKey(BatterySaverScheduleRadioButtonsController.KEY_ROUTINE); assertThat(mController.getDefaultKey()) .isEqualTo(BatterySaverScheduleRadioButtonsController.KEY_NO_SCHEDULE); } + + @Test + public void setDefaultKey_percentage_shouldSuppressNotification() { + Secure.putInt( + mContext.getContentResolver(), Secure.LOW_POWER_WARNING_ACKNOWLEDGED, 1); + Settings.Global.putInt(mResolver, Global.AUTOMATIC_POWER_SAVE_MODE, + PowerManager.POWER_SAVE_MODE_TRIGGER_PERCENTAGE); + Settings.Global.putInt(mResolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, 5); + mController.setDefaultKey(BatterySaverScheduleRadioButtonsController.KEY_PERCENTAGE); + + final int result = Settings.Secure.getInt(mResolver, + Secure.SUPPRESS_AUTO_BATTERY_SAVER_SUGGESTION, 0); + assertThat(result).isEqualTo(1); + } + + @Test + public void setDefaultKey_routine_shouldSuppressNotification() { + Secure.putInt( + mContext.getContentResolver(), Secure.LOW_POWER_WARNING_ACKNOWLEDGED, 1); + Settings.Global.putInt(mResolver, Global.AUTOMATIC_POWER_SAVE_MODE, + PowerManager.POWER_SAVE_MODE_TRIGGER_DYNAMIC); + mController.setDefaultKey(BatterySaverScheduleRadioButtonsController.KEY_ROUTINE); + + final int result = Settings.Secure.getInt(mResolver, + Secure.SUPPRESS_AUTO_BATTERY_SAVER_SUGGESTION, 0); + assertThat(result).isEqualTo(1); + } } From d40f7f707e3aaf34d95b1f5fc99b8846a0e85640 Mon Sep 17 00:00:00 2001 From: Bonian Chen Date: Fri, 29 May 2020 17:28:34 +0800 Subject: [PATCH 5/5] [Settings] Move mobile data template out of framework Move mobile data template out of framework Bug: 151751844 Test: make RunSettingsRoboTests -j ROBOTEST_FILTER=DataUsageLibTest Change-Id: I5072cd1e673194d00b3ce82242bf5fbad88d23cd --- .../BillingCyclePreferenceController.java | 9 +- .../settings/datausage/DataUsageSummary.java | 3 +- .../DataUsageSummaryPreferenceController.java | 3 +- .../settings/datausage/DataUsageUtils.java | 3 +- .../settings/datausage/lib/DataUsageLib.java | 78 ++++++++++++ .../DataUsagePreferenceController.java | 3 +- .../datausage/lib/DataUsageLibTest.java | 113 ++++++++++++++++++ 7 files changed, 204 insertions(+), 8 deletions(-) create mode 100644 src/com/android/settings/datausage/lib/DataUsageLib.java create mode 100644 tests/robotests/src/com/android/settings/datausage/lib/DataUsageLibTest.java diff --git a/src/com/android/settings/datausage/BillingCyclePreferenceController.java b/src/com/android/settings/datausage/BillingCyclePreferenceController.java index 88bf63c72f4..2f144cb78a7 100644 --- a/src/com/android/settings/datausage/BillingCyclePreferenceController.java +++ b/src/com/android/settings/datausage/BillingCyclePreferenceController.java @@ -26,11 +26,12 @@ import android.os.UserManager; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; -import com.android.settings.core.BasePreferenceController; -import com.android.settingslib.NetworkPolicyEditor; - import androidx.preference.PreferenceScreen; +import com.android.settings.core.BasePreferenceController; +import com.android.settings.datausage.lib.DataUsageLib; +import com.android.settingslib.NetworkPolicyEditor; + public class BillingCyclePreferenceController extends BasePreferenceController { private int mSubscriptionId; @@ -58,7 +59,7 @@ public class BillingCyclePreferenceController extends BasePreferenceController { services.mSubscriptionManager = mContext.getSystemService(SubscriptionManager.class); services.mUserManager = mContext.getSystemService(UserManager.class); - NetworkTemplate template = DataUsageUtils.getMobileTemplate(mContext, mSubscriptionId); + NetworkTemplate template = DataUsageLib.getMobileTemplate(mContext, mSubscriptionId); preference.setTemplate(template, mSubscriptionId, services); } diff --git a/src/com/android/settings/datausage/DataUsageSummary.java b/src/com/android/settings/datausage/DataUsageSummary.java index 7b67a0282c2..9d9bf9c65ae 100644 --- a/src/com/android/settings/datausage/DataUsageSummary.java +++ b/src/com/android/settings/datausage/DataUsageSummary.java @@ -33,6 +33,7 @@ import androidx.preference.Preference; import androidx.preference.PreferenceScreen; import com.android.settings.R; +import com.android.settings.datausage.lib.DataUsageLib; import com.android.settings.network.ProxySubscriptionManager; import com.android.settingslib.NetworkPolicyEditor; import com.android.settingslib.core.AbstractPreferenceController; @@ -166,7 +167,7 @@ public class DataUsageSummary extends DataUsageBaseFragment implements DataUsage private void addMobileSection(int subId, SubscriptionInfo subInfo) { TemplatePreferenceCategory category = (TemplatePreferenceCategory) inflatePreferences(R.xml.data_usage_cellular); - category.setTemplate(DataUsageUtils.getMobileTemplate(getContext(), subId), + category.setTemplate(DataUsageLib.getMobileTemplate(getContext(), subId), subId, services); category.pushTemplates(services); if (subInfo != null && !TextUtils.isEmpty(subInfo.getDisplayName())) { diff --git a/src/com/android/settings/datausage/DataUsageSummaryPreferenceController.java b/src/com/android/settings/datausage/DataUsageSummaryPreferenceController.java index 4178f6cc8fe..97cf59963b4 100644 --- a/src/com/android/settings/datausage/DataUsageSummaryPreferenceController.java +++ b/src/com/android/settings/datausage/DataUsageSummaryPreferenceController.java @@ -39,6 +39,7 @@ import androidx.recyclerview.widget.RecyclerView; import com.android.internal.util.CollectionUtils; import com.android.settings.R; import com.android.settings.core.PreferenceControllerMixin; +import com.android.settings.datausage.lib.DataUsageLib; import com.android.settings.network.ProxySubscriptionManager; import com.android.settings.network.telephony.TelephonyBasePreferenceController; import com.android.settings.widget.EntityHeaderController; @@ -134,7 +135,7 @@ public class DataUsageSummaryPreferenceController extends TelephonyBasePreferenc if (subInfo != null) { mDataUsageTemplate = R.string.cell_data_template; - mDefaultTemplate = DataUsageUtils.getMobileTemplate(context, subscriptionId); + mDefaultTemplate = DataUsageLib.getMobileTemplate(context, subscriptionId); } else if (DataUsageUtils.hasWifiRadio(context)) { mDataUsageTemplate = R.string.wifi_data_template; mDefaultTemplate = NetworkTemplate.buildTemplateWifiWildcard(); diff --git a/src/com/android/settings/datausage/DataUsageUtils.java b/src/com/android/settings/datausage/DataUsageUtils.java index 6cdf2102468..da0ca63b22b 100644 --- a/src/com/android/settings/datausage/DataUsageUtils.java +++ b/src/com/android/settings/datausage/DataUsageUtils.java @@ -33,6 +33,7 @@ import android.text.format.Formatter; import android.text.format.Formatter.BytesResult; import android.util.Log; +import com.android.settings.datausage.lib.DataUsageLib; import com.android.settings.network.ProxySubscriptionManager; import java.util.List; @@ -183,7 +184,7 @@ public final class DataUsageUtils extends com.android.settingslib.net.DataUsageU */ public static NetworkTemplate getDefaultTemplate(Context context, int defaultSubId) { if (SubscriptionManager.isValidSubscriptionId(defaultSubId) && hasMobileData(context)) { - return getMobileTemplate(context, defaultSubId); + return DataUsageLib.getMobileTemplate(context, defaultSubId); } else if (hasWifiRadio(context)) { return NetworkTemplate.buildTemplateWifiWildcard(); } else { diff --git a/src/com/android/settings/datausage/lib/DataUsageLib.java b/src/com/android/settings/datausage/lib/DataUsageLib.java new file mode 100644 index 00000000000..cf2ce1fcf76 --- /dev/null +++ b/src/com/android/settings/datausage/lib/DataUsageLib.java @@ -0,0 +1,78 @@ +/* + * 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.datausage.lib; + +import android.content.Context; +import android.net.NetworkTemplate; +import android.telephony.SubscriptionInfo; +import android.telephony.SubscriptionManager; +import android.telephony.TelephonyManager; +import android.util.Log; + +import com.android.internal.util.ArrayUtils; + +import java.util.List; + +/** + * Lib class for data usage + */ +public class DataUsageLib { + private static final String TAG = "DataUsageLib"; + + /** + * Return mobile NetworkTemplate based on {@code subId} + */ + public static NetworkTemplate getMobileTemplate(Context context, int subId) { + final TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class); + final int mobileDefaultSubId = telephonyManager.getSubscriptionId(); + + final SubscriptionManager subscriptionManager = + context.getSystemService(SubscriptionManager.class); + final List subInfoList = + subscriptionManager.getAvailableSubscriptionInfoList(); + if (subInfoList == null) { + Log.i(TAG, "Subscription is not inited: " + subId); + return getMobileTemplateForSubId(telephonyManager, mobileDefaultSubId); + } + + for (SubscriptionInfo subInfo : subInfoList) { + if ((subInfo != null) && (subInfo.getSubscriptionId() == subId)) { + return getNormalizedMobileTemplate(telephonyManager, subId); + } + } + Log.i(TAG, "Subscription is not active: " + subId); + return getMobileTemplateForSubId(telephonyManager, mobileDefaultSubId); + } + + private static NetworkTemplate getNormalizedMobileTemplate( + TelephonyManager telephonyManager, int subId) { + final NetworkTemplate mobileTemplate = getMobileTemplateForSubId(telephonyManager, subId); + final String[] mergedSubscriberIds = telephonyManager + .createForSubscriptionId(subId).getMergedImsisFromGroup(); + if (ArrayUtils.isEmpty(mergedSubscriberIds)) { + Log.i(TAG, "mergedSubscriberIds is null."); + return mobileTemplate; + } + + return NetworkTemplate.normalize(mobileTemplate, mergedSubscriberIds); + } + + private static NetworkTemplate getMobileTemplateForSubId( + TelephonyManager telephonyManager, int subId) { + return NetworkTemplate.buildTemplateMobileAll(telephonyManager.getSubscriberId(subId)); + } +} diff --git a/src/com/android/settings/network/telephony/DataUsagePreferenceController.java b/src/com/android/settings/network/telephony/DataUsagePreferenceController.java index 08524d67f8c..40eb549308c 100644 --- a/src/com/android/settings/network/telephony/DataUsagePreferenceController.java +++ b/src/com/android/settings/network/telephony/DataUsagePreferenceController.java @@ -29,6 +29,7 @@ import androidx.preference.Preference; import com.android.internal.annotations.VisibleForTesting; import com.android.settings.R; import com.android.settings.datausage.DataUsageUtils; +import com.android.settings.datausage.lib.DataUsageLib; import com.android.settingslib.net.DataUsageController; import com.android.settingslib.utils.ThreadUtils; @@ -99,7 +100,7 @@ public class DataUsagePreferenceController extends TelephonyBasePreferenceContro if (!SubscriptionManager.isValidSubscriptionId(subId)) { return null; } - return DataUsageUtils.getMobileTemplate(context, subId); + return DataUsageLib.getMobileTemplate(context, subId); } private NetworkTemplate getNetworkTemplate() { diff --git a/tests/robotests/src/com/android/settings/datausage/lib/DataUsageLibTest.java b/tests/robotests/src/com/android/settings/datausage/lib/DataUsageLibTest.java new file mode 100644 index 00000000000..8d994d0550e --- /dev/null +++ b/tests/robotests/src/com/android/settings/datausage/lib/DataUsageLibTest.java @@ -0,0 +1,113 @@ +/* + * 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.datausage.lib; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Matchers.anyInt; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.net.NetworkTemplate; +import android.os.ParcelUuid; +import android.os.RemoteException; +import android.telephony.SubscriptionInfo; +import android.telephony.SubscriptionManager; +import android.telephony.TelephonyManager; + +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; + +import java.util.List; + +@RunWith(RobolectricTestRunner.class) +public class DataUsageLibTest { + + private static final int SUB_ID = 1; + private static final int SUB_ID_2 = 2; + private static final String SUBSCRIBER_ID = "Test Subscriber"; + private static final String SUBSCRIBER_ID_2 = "Test Subscriber 2"; + + @Mock + private TelephonyManager mTelephonyManager; + @Mock + private SubscriptionManager mSubscriptionManager; + @Mock + private SubscriptionInfo mInfo1; + @Mock + private SubscriptionInfo mInfo2; + @Mock + private ParcelUuid mParcelUuid; + private Context mContext; + private List mInfos; + + @Before + public void setUp() throws RemoteException { + MockitoAnnotations.initMocks(this); + + mContext = spy(RuntimeEnvironment.application); + when(mContext.getSystemService(TelephonyManager.class)).thenReturn(mTelephonyManager); + when(mContext.getSystemService(SubscriptionManager.class)).thenReturn(mSubscriptionManager); + when(mTelephonyManager.getSubscriberId(SUB_ID)).thenReturn(SUBSCRIBER_ID); + when(mTelephonyManager.getSubscriberId(SUB_ID_2)).thenReturn(SUBSCRIBER_ID_2); + when(mTelephonyManager.createForSubscriptionId(anyInt())).thenReturn(mTelephonyManager); + when(mSubscriptionManager.isActiveSubscriptionId(anyInt())).thenReturn(true); + } + + @Test + @Ignore + public void getMobileTemplate_infoNull_returnMobileAll() { + when(mSubscriptionManager.isActiveSubscriptionId(SUB_ID)).thenReturn(false); + + final NetworkTemplate networkTemplate = DataUsageLib.getMobileTemplate(mContext, SUB_ID); + assertThat(networkTemplate.matchesSubscriberId(SUBSCRIBER_ID)).isTrue(); + assertThat(networkTemplate.matchesSubscriberId(SUBSCRIBER_ID_2)).isFalse(); + } + + @Test + @Ignore + public void getMobileTemplate_groupUuidNull_returnMobileAll() { + when(mSubscriptionManager.getActiveSubscriptionInfo(SUB_ID)).thenReturn(mInfo1); + when(mInfo1.getGroupUuid()).thenReturn(null); + when(mTelephonyManager.getMergedImsisFromGroup()) + .thenReturn(new String[] {SUBSCRIBER_ID}); + + final NetworkTemplate networkTemplate = DataUsageLib.getMobileTemplate(mContext, SUB_ID); + assertThat(networkTemplate.matchesSubscriberId(SUBSCRIBER_ID)).isTrue(); + assertThat(networkTemplate.matchesSubscriberId(SUBSCRIBER_ID_2)).isFalse(); + } + + @Test + @Ignore + public void getMobileTemplate_groupUuidExist_returnMobileMerged() { + when(mSubscriptionManager.getActiveSubscriptionInfo(SUB_ID)).thenReturn(mInfo1); + when(mInfo1.getGroupUuid()).thenReturn(mParcelUuid); + when(mTelephonyManager.getMergedImsisFromGroup()) + .thenReturn(new String[] {SUBSCRIBER_ID, SUBSCRIBER_ID_2}); + + final NetworkTemplate networkTemplate = DataUsageLib.getMobileTemplate(mContext, SUB_ID); + assertThat(networkTemplate.matchesSubscriberId(SUBSCRIBER_ID)).isTrue(); + assertThat(networkTemplate.matchesSubscriberId(SUBSCRIBER_ID_2)).isTrue(); + } +}