Loading src/java/com/android/internal/telephony/MultiSimSettingController.java +107 −13 Original line number Diff line number Diff line Loading @@ -31,13 +31,16 @@ import static android.telephony.TelephonyManager.EXTRA_SUBSCRIPTION_ID; import android.annotation.IntDef; import android.annotation.NonNull; import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Handler; import android.os.Message; import android.os.ParcelUuid; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; import android.telephony.CarrierConfigManager; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; Loading @@ -51,6 +54,7 @@ import com.android.internal.util.ArrayUtils; 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.stream.Collectors; Loading @@ -73,6 +77,7 @@ public class MultiSimSettingController extends Handler { private static final int EVENT_SUBSCRIPTION_INFO_CHANGED = 4; private static final int EVENT_SUBSCRIPTION_GROUP_CHANGED = 5; private static final int EVENT_DEFAULT_DATA_SUBSCRIPTION_CHANGED = 6; private static final int EVENT_CARRIER_CONFIG_CHANGED = 7; @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = {"PRIMARY_SUB_"}, Loading Loading @@ -118,6 +123,32 @@ public class MultiSimSettingController extends Handler { // the SIMs are newly inserted instead of being initialized. private boolean mSubInfoInitialized = false; // mInitialHandling is to make sure we don't always ask user to re-select data SIM after reboot. // After boot-up when things are firstly initialized (mSubInfoInitialized is changed to true // and carrier configs are all loaded), we do a reEvaluateAll(). In the first reEvaluateAll(), // mInitialHandling will be true and we won't pop up SIM select dialog. private boolean mInitialHandling = true; // Keep a record of which subIds have carrier config loaded. Length of the array is phone count. // The index is phoneId, and value is subId. For example: // If carrier config of subId 2 is loaded on phone 0,mCarrierConfigLoadedSubIds[0] = 2. // Then if subId 2 is deactivated from phone 0, the value becomes INVALID, // mCarrierConfigLoadedSubIds[0] = INVALID_SUBSCRIPTION_ID. private int[] mCarrierConfigLoadedSubIds; private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED.equals(intent.getAction())) { int phoneId = intent.getIntExtra(CarrierConfigManager.EXTRA_SLOT_INDEX, SubscriptionManager.INVALID_SIM_SLOT_INDEX); int subId = intent.getIntExtra(CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX, SubscriptionManager.INVALID_SUBSCRIPTION_ID); notifyCarrierConfigChanged(phoneId, subId); } } }; /** * Return the singleton or create one if not existed. */ Loading Loading @@ -149,6 +180,15 @@ public class MultiSimSettingController extends Handler { public MultiSimSettingController(Context context, SubscriptionController sc) { mContext = context; mSubController = sc; // Initialize mCarrierConfigLoadedSubIds and register to listen to carrier config change. final int phoneCount = ((TelephonyManager) mContext.getSystemService( Context.TELEPHONY_SERVICE)).getPhoneCount(); mCarrierConfigLoadedSubIds = new int[phoneCount]; Arrays.fill(mCarrierConfigLoadedSubIds, SubscriptionManager.INVALID_SUBSCRIPTION_ID); context.registerReceiver(mIntentReceiver, new IntentFilter( CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)); } /** Loading Loading @@ -222,6 +262,11 @@ public class MultiSimSettingController extends Handler { case EVENT_DEFAULT_DATA_SUBSCRIPTION_CHANGED: onDefaultDataSettingChanged(); break; case EVENT_CARRIER_CONFIG_CHANGED: int phoneId = msg.arg1; int subId = msg.arg2; onCarrierConfigChanged(phoneId, subId); break; } } Loading Loading @@ -261,9 +306,7 @@ public class MultiSimSettingController extends Handler { private void onAllSubscriptionsLoaded() { if (DBG) log("onAllSubscriptionsLoaded"); mSubInfoInitialized = true; updateDefaults(/*init*/ true); disableDataForNonDefaultNonOpportunisticSubscriptions(); deactivateGroupedOpportunisticSubscriptionIfNeeded(); reEvaluateAll(); } /** Loading @@ -273,8 +316,58 @@ public class MultiSimSettingController extends Handler { */ private void onSubscriptionsChanged() { if (DBG) log("onSubscriptionsChanged"); if (!mSubInfoInitialized) return; updateDefaults(/*init*/ false); reEvaluateAll(); } /** * Called when carrier config changes on any phone. */ @VisibleForTesting public void notifyCarrierConfigChanged(int phoneId, int subId) { obtainMessage(EVENT_CARRIER_CONFIG_CHANGED, phoneId, subId).sendToTarget(); } private void onCarrierConfigChanged(int phoneId, int subId) { log("onCarrierConfigChanged phoneId " + phoneId + " subId " + subId); if (!SubscriptionManager.isValidPhoneId(phoneId)) { loge("Carrier config change with invalid phoneId " + phoneId); return; } mCarrierConfigLoadedSubIds[phoneId] = subId; reEvaluateAll(); } private boolean isCarrierConfigLoadedForAllSub() { int[] activeSubIds = mSubController.getActiveSubIdList(false); for (int activeSubId : activeSubIds) { boolean isLoaded = false; for (int configLoadedSub : mCarrierConfigLoadedSubIds) { if (configLoadedSub == activeSubId) { isLoaded = true; break; } } if (!isLoaded) { if (DBG) log("Carrier config subId " + activeSubId + " is not loaded."); return false; } } return true; } /** * Wait for subInfo initialization (after boot up) and carrier config load for all active * subscriptions before re-evaluate multi SIM settings. */ private boolean isReadyToReevaluate() { return mSubInfoInitialized && isCarrierConfigLoadedForAllSub(); } private void reEvaluateAll() { if (!isReadyToReevaluate()) return; updateDefaults(); disableDataForNonDefaultNonOpportunisticSubscriptions(); deactivateGroupedOpportunisticSubscriptionIfNeeded(); } Loading Loading @@ -353,12 +446,11 @@ public class MultiSimSettingController extends Handler { * not a user settable value anymore. * 4) If non above is met, clear the default value to INVALID. * * @param init whether the subscriptions are just initialized. */ private void updateDefaults(boolean init) { private void updateDefaults() { if (DBG) log("updateDefaults"); if (!mSubInfoInitialized) return; if (!isReadyToReevaluate()) return; List<SubscriptionInfo> activeSubInfos = mSubController .getActiveSubscriptionInfoList(mContext.getOpPackageName()); Loading @@ -372,7 +464,7 @@ public class MultiSimSettingController extends Handler { return; } int change = updatePrimarySubListAndGetChangeType(activeSubInfos, init); int change = updatePrimarySubListAndGetChangeType(activeSubInfos); if (DBG) log("[updateDefaultValues] change: " + change); if (change == PRIMARY_SUB_NO_CHANGE) return; Loading Loading @@ -414,8 +506,7 @@ public class MultiSimSettingController extends Handler { } @PrimarySubChangeType private int updatePrimarySubListAndGetChangeType(List<SubscriptionInfo> activeSubList, boolean init) { private int updatePrimarySubListAndGetChangeType(List<SubscriptionInfo> activeSubList) { // Update mPrimarySubList. Opportunistic subscriptions can't be default // data / voice / sms subscription. List<Integer> prevPrimarySubList = mPrimarySubList; Loading @@ -423,7 +514,10 @@ public class MultiSimSettingController extends Handler { .map(info -> info.getSubscriptionId()) .collect(Collectors.toList()); if (init) return PRIMARY_SUB_INITIALIZED; if (mInitialHandling) { mInitialHandling = false; return PRIMARY_SUB_INITIALIZED; } if (mPrimarySubList.equals(prevPrimarySubList)) return PRIMARY_SUB_NO_CHANGE; if (mPrimarySubList.size() > prevPrimarySubList.size()) return PRIMARY_SUB_ADDED; Loading Loading @@ -554,7 +648,7 @@ public class MultiSimSettingController extends Handler { } private void disableDataForNonDefaultNonOpportunisticSubscriptions() { if (!mSubInfoInitialized) return; if (!isReadyToReevaluate()) return; int defaultDataSub = mSubController.getDefaultDataSubId(); Loading tests/telephonytests/src/com/android/internal/telephony/MultiSimSettingControllerTest.java +70 −0 Original line number Diff line number Diff line Loading @@ -118,6 +118,7 @@ public class MultiSimSettingControllerTest extends TelephonyTest { List<SubscriptionInfo> infoList = Arrays.asList(mSubInfo1, mSubInfo2); doReturn(infoList).when(mSubControllerMock) .getActiveSubscriptionInfoList(anyString()); doReturn(new int[]{1, 2}).when(mSubControllerMock).getActiveSubIdList(anyBoolean()); mPhones = new Phone[] {mPhoneMock1, mPhoneMock2}; doReturn(mDataEnabledSettingsMock1).when(mPhoneMock1).getDataEnabledSettings(); Loading Loading @@ -175,6 +176,7 @@ public class MultiSimSettingControllerTest extends TelephonyTest { doReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID).when(mPhoneMock2).getSubId(); List<SubscriptionInfo> infoList = Arrays.asList(mSubInfo1); doReturn(infoList).when(mSubControllerMock).getActiveSubscriptionInfoList(anyString()); doReturn(new int[]{1}).when(mSubControllerMock).getActiveSubIdList(anyBoolean()); // Mark subscription ready as false. The below sub info change should be ignored. mMultiSimSettingControllerUT.notifySubscriptionInfoChanged(); Loading @@ -184,6 +186,7 @@ public class MultiSimSettingControllerTest extends TelephonyTest { verify(mSubControllerMock, never()).setDefaultSmsSubId(anyInt()); mMultiSimSettingControllerUT.notifyAllSubscriptionLoaded(); mMultiSimSettingControllerUT.notifyCarrierConfigChanged(0, 1); waitABit(); // Sub 1 should be default sub silently. Loading @@ -210,8 +213,10 @@ public class MultiSimSettingControllerTest extends TelephonyTest { doReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID).when(mPhoneMock2).getSubId(); List<SubscriptionInfo> infoList = Arrays.asList(mSubInfo1); doReturn(infoList).when(mSubControllerMock).getActiveSubscriptionInfoList(anyString()); doReturn(new int[]{1}).when(mSubControllerMock).getActiveSubIdList(anyBoolean()); mMultiSimSettingControllerUT.notifyAllSubscriptionLoaded(); mMultiSimSettingControllerUT.notifyCarrierConfigChanged(0, 1); waitABit(); // Sub 1 should be default sub silently. Loading @@ -223,8 +228,10 @@ public class MultiSimSettingControllerTest extends TelephonyTest { doReturn(2).when(mPhoneMock1).getSubId(); infoList = Arrays.asList(mSubInfo2); doReturn(infoList).when(mSubControllerMock).getActiveSubscriptionInfoList(anyString()); doReturn(new int[]{2}).when(mSubControllerMock).getActiveSubIdList(anyBoolean()); mMultiSimSettingControllerUT.notifySubscriptionInfoChanged(); mMultiSimSettingControllerUT.notifyCarrierConfigChanged(0, 2); waitABit(); // Sub 1 should be default sub silently. Loading @@ -244,8 +251,10 @@ public class MultiSimSettingControllerTest extends TelephonyTest { doReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID).when(mPhoneMock2).getSubId(); List<SubscriptionInfo> infoList = Arrays.asList(mSubInfo1); doReturn(infoList).when(mSubControllerMock).getActiveSubscriptionInfoList(anyString()); doReturn(new int[]{1}).when(mSubControllerMock).getActiveSubIdList(anyBoolean()); mMultiSimSettingControllerUT.notifyAllSubscriptionLoaded(); mMultiSimSettingControllerUT.notifyCarrierConfigChanged(0, 1); waitABit(); // Sub 1 should be default sub silently. Loading @@ -262,8 +271,10 @@ public class MultiSimSettingControllerTest extends TelephonyTest { doReturn(2).when(mPhoneMock2).getSubId(); infoList = Arrays.asList(mSubInfo1, mSubInfo2); doReturn(infoList).when(mSubControllerMock).getActiveSubscriptionInfoList(anyString()); doReturn(new int[]{1, 2}).when(mSubControllerMock).getActiveSubIdList(anyBoolean()); mMultiSimSettingControllerUT.notifySubscriptionInfoChanged(); mMultiSimSettingControllerUT.notifyCarrierConfigChanged(1, 2); waitABit(); // Intent should be broadcast to ask default data selection. Loading @@ -282,8 +293,10 @@ public class MultiSimSettingControllerTest extends TelephonyTest { doReturn(3).when(mPhoneMock2).getSubId(); infoList = Arrays.asList(mSubInfo1, mSubInfo3); doReturn(infoList).when(mSubControllerMock).getActiveSubscriptionInfoList(anyString()); doReturn(new int[]{1, 3}).when(mSubControllerMock).getActiveSubIdList(anyBoolean()); mMultiSimSettingControllerUT.notifySubscriptionInfoChanged(); mMultiSimSettingControllerUT.notifyCarrierConfigChanged(1, 3); waitABit(); // Intent should be broadcast to ask default data selection. Loading @@ -300,6 +313,8 @@ public class MultiSimSettingControllerTest extends TelephonyTest { doReturn(true).when(mPhoneMock2).isUserDataEnabled(); // After initialization, sub 2 should have mobile data off. mMultiSimSettingControllerUT.notifyAllSubscriptionLoaded(); mMultiSimSettingControllerUT.notifyCarrierConfigChanged(0, 1); mMultiSimSettingControllerUT.notifyCarrierConfigChanged(1, 2); waitABit(); verify(mDataEnabledSettingsMock2).setUserDataEnabled(false); Loading @@ -323,7 +338,10 @@ public class MultiSimSettingControllerTest extends TelephonyTest { doReturn(false).when(mSubControllerMock).isActiveSubId(1); List<SubscriptionInfo> infoList = Arrays.asList(mSubInfo2); doReturn(infoList).when(mSubControllerMock).getActiveSubscriptionInfoList(anyString()); doReturn(new int[]{2}).when(mSubControllerMock).getActiveSubIdList(anyBoolean()); mMultiSimSettingControllerUT.notifySubscriptionInfoChanged(); mMultiSimSettingControllerUT.notifyCarrierConfigChanged( 1, SubscriptionManager.INVALID_SUBSCRIPTION_ID); waitABit(); verify(mSubControllerMock).setDefaultDataSubId(SubscriptionManager.INVALID_SUBSCRIPTION_ID); verify(mSubControllerMock).setDefaultSmsSubId(SubscriptionManager.INVALID_SUBSCRIPTION_ID); Loading @@ -346,6 +364,8 @@ public class MultiSimSettingControllerTest extends TelephonyTest { GlobalSettingsHelper.setBoolean(mContext, Settings.Global.MOBILE_DATA, 2, true); GlobalSettingsHelper.setBoolean(mContext, Settings.Global.DATA_ROAMING, 2, false); mMultiSimSettingControllerUT.notifyAllSubscriptionLoaded(); mMultiSimSettingControllerUT.notifyCarrierConfigChanged(0, 1); mMultiSimSettingControllerUT.notifyCarrierConfigChanged(1, 2); waitABit(); // Create subscription grouping. Loading Loading @@ -391,8 +411,10 @@ public class MultiSimSettingControllerTest extends TelephonyTest { doReturn(1).when(mSubControllerMock).getDefaultVoiceSubId(); List<SubscriptionInfo> infoList = Arrays.asList(mSubInfo1, mSubInfo3); doReturn(infoList).when(mSubControllerMock).getActiveSubscriptionInfoList(anyString()); doReturn(new int[]{1, 3}).when(mSubControllerMock).getActiveSubIdList(anyBoolean()); mMultiSimSettingControllerUT.notifySubscriptionInfoChanged(); mMultiSimSettingControllerUT.notifyCarrierConfigChanged(1, 3); waitABit(); verify(mSubControllerMock).setDefaultDataSubId(3); Loading @@ -414,6 +436,8 @@ public class MultiSimSettingControllerTest extends TelephonyTest { // Notify subscriptions ready. Sub 2 should become the default. But shouldn't turn off // data of oppt sub 1. mMultiSimSettingControllerUT.notifyAllSubscriptionLoaded(); mMultiSimSettingControllerUT.notifyCarrierConfigChanged(0, 1); mMultiSimSettingControllerUT.notifyCarrierConfigChanged(1, 2); waitABit(); verify(mSubControllerMock).setDefaultDataSubId(2); verify(mDataEnabledSettingsMock1, never()).setUserDataEnabled(anyBoolean()); Loading Loading @@ -451,6 +475,8 @@ public class MultiSimSettingControllerTest extends TelephonyTest { // Notify subscriptions ready. Sub 2 should become the default, as sub 1 is opportunistic. mMultiSimSettingControllerUT.notifyAllSubscriptionLoaded(); mMultiSimSettingControllerUT.notifyCarrierConfigChanged(0, 1); mMultiSimSettingControllerUT.notifyCarrierConfigChanged(1, 2); waitABit(); verify(mSubControllerMock).setDefaultDataSubId(2); // No user selection needed, no intent should be sent. Loading Loading @@ -494,6 +520,8 @@ public class MultiSimSettingControllerTest extends TelephonyTest { GlobalSettingsHelper.setBoolean(mContext, Settings.Global.MOBILE_DATA, 1, true); GlobalSettingsHelper.setBoolean(mContext, Settings.Global.DATA_ROAMING, 1, false); mMultiSimSettingControllerUT.notifyAllSubscriptionLoaded(); mMultiSimSettingControllerUT.notifyCarrierConfigChanged(0, 1); mMultiSimSettingControllerUT.notifyCarrierConfigChanged(1, 2); waitABit(); // Create subscription grouping. Loading @@ -514,4 +542,46 @@ public class MultiSimSettingControllerTest extends TelephonyTest { waitABit(); verify(mDataEnabledSettingsMock2).setUserDataEnabled(false); } @Test @SmallTest public void testCarrierConfigLoading() throws Exception { doReturn(true).when(mPhoneMock1).isUserDataEnabled(); doReturn(true).when(mPhoneMock2).isUserDataEnabled(); // Sub 2 should have mobile data off, but it shouldn't happen until carrier configs are // loaded on both subscriptions. mMultiSimSettingControllerUT.notifyAllSubscriptionLoaded(); waitABit(); verify(mDataEnabledSettingsMock2, never()).setUserDataEnabled(false); mMultiSimSettingControllerUT.notifyCarrierConfigChanged(0, 1); waitABit(); verify(mDataEnabledSettingsMock2, never()).setUserDataEnabled(false); mMultiSimSettingControllerUT.notifyCarrierConfigChanged(1, 2); waitABit(); verify(mDataEnabledSettingsMock2).setUserDataEnabled(false); // Switch from sub 2 to sub 3 in phone[1]. clearInvocations(mSubControllerMock); doReturn(false).when(mSubControllerMock).isActiveSubId(2); doReturn(true).when(mSubControllerMock).isActiveSubId(3); doReturn(SubscriptionManager.INVALID_PHONE_INDEX).when(mSubControllerMock).getPhoneId(2); doReturn(1).when(mSubControllerMock).getPhoneId(3); doReturn(3).when(mPhoneMock2).getSubId(); List<SubscriptionInfo> infoList = Arrays.asList(mSubInfo1, mSubInfo3); doReturn(infoList).when(mSubControllerMock).getActiveSubscriptionInfoList(anyString()); doReturn(new int[]{1, 3}).when(mSubControllerMock).getActiveSubIdList(anyBoolean()); // Nothing should happen until carrier config change is notified on sub 3. mMultiSimSettingControllerUT.notifySubscriptionInfoChanged(); waitABit(); verify(mContext, never()).sendBroadcast(any()); mMultiSimSettingControllerUT.notifyCarrierConfigChanged(1, 3); waitABit(); // Intent should be broadcast to ask default data selection. Intent intent = captureBroadcastIntent(); assertEquals(ACTION_PRIMARY_SUBSCRIPTION_LIST_CHANGED, intent.getAction()); assertEquals(EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_DATA, intent.getIntExtra(EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE, -1)); } } Loading
src/java/com/android/internal/telephony/MultiSimSettingController.java +107 −13 Original line number Diff line number Diff line Loading @@ -31,13 +31,16 @@ import static android.telephony.TelephonyManager.EXTRA_SUBSCRIPTION_ID; import android.annotation.IntDef; import android.annotation.NonNull; import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Handler; import android.os.Message; import android.os.ParcelUuid; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; import android.telephony.CarrierConfigManager; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; Loading @@ -51,6 +54,7 @@ import com.android.internal.util.ArrayUtils; 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.stream.Collectors; Loading @@ -73,6 +77,7 @@ public class MultiSimSettingController extends Handler { private static final int EVENT_SUBSCRIPTION_INFO_CHANGED = 4; private static final int EVENT_SUBSCRIPTION_GROUP_CHANGED = 5; private static final int EVENT_DEFAULT_DATA_SUBSCRIPTION_CHANGED = 6; private static final int EVENT_CARRIER_CONFIG_CHANGED = 7; @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = {"PRIMARY_SUB_"}, Loading Loading @@ -118,6 +123,32 @@ public class MultiSimSettingController extends Handler { // the SIMs are newly inserted instead of being initialized. private boolean mSubInfoInitialized = false; // mInitialHandling is to make sure we don't always ask user to re-select data SIM after reboot. // After boot-up when things are firstly initialized (mSubInfoInitialized is changed to true // and carrier configs are all loaded), we do a reEvaluateAll(). In the first reEvaluateAll(), // mInitialHandling will be true and we won't pop up SIM select dialog. private boolean mInitialHandling = true; // Keep a record of which subIds have carrier config loaded. Length of the array is phone count. // The index is phoneId, and value is subId. For example: // If carrier config of subId 2 is loaded on phone 0,mCarrierConfigLoadedSubIds[0] = 2. // Then if subId 2 is deactivated from phone 0, the value becomes INVALID, // mCarrierConfigLoadedSubIds[0] = INVALID_SUBSCRIPTION_ID. private int[] mCarrierConfigLoadedSubIds; private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED.equals(intent.getAction())) { int phoneId = intent.getIntExtra(CarrierConfigManager.EXTRA_SLOT_INDEX, SubscriptionManager.INVALID_SIM_SLOT_INDEX); int subId = intent.getIntExtra(CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX, SubscriptionManager.INVALID_SUBSCRIPTION_ID); notifyCarrierConfigChanged(phoneId, subId); } } }; /** * Return the singleton or create one if not existed. */ Loading Loading @@ -149,6 +180,15 @@ public class MultiSimSettingController extends Handler { public MultiSimSettingController(Context context, SubscriptionController sc) { mContext = context; mSubController = sc; // Initialize mCarrierConfigLoadedSubIds and register to listen to carrier config change. final int phoneCount = ((TelephonyManager) mContext.getSystemService( Context.TELEPHONY_SERVICE)).getPhoneCount(); mCarrierConfigLoadedSubIds = new int[phoneCount]; Arrays.fill(mCarrierConfigLoadedSubIds, SubscriptionManager.INVALID_SUBSCRIPTION_ID); context.registerReceiver(mIntentReceiver, new IntentFilter( CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)); } /** Loading Loading @@ -222,6 +262,11 @@ public class MultiSimSettingController extends Handler { case EVENT_DEFAULT_DATA_SUBSCRIPTION_CHANGED: onDefaultDataSettingChanged(); break; case EVENT_CARRIER_CONFIG_CHANGED: int phoneId = msg.arg1; int subId = msg.arg2; onCarrierConfigChanged(phoneId, subId); break; } } Loading Loading @@ -261,9 +306,7 @@ public class MultiSimSettingController extends Handler { private void onAllSubscriptionsLoaded() { if (DBG) log("onAllSubscriptionsLoaded"); mSubInfoInitialized = true; updateDefaults(/*init*/ true); disableDataForNonDefaultNonOpportunisticSubscriptions(); deactivateGroupedOpportunisticSubscriptionIfNeeded(); reEvaluateAll(); } /** Loading @@ -273,8 +316,58 @@ public class MultiSimSettingController extends Handler { */ private void onSubscriptionsChanged() { if (DBG) log("onSubscriptionsChanged"); if (!mSubInfoInitialized) return; updateDefaults(/*init*/ false); reEvaluateAll(); } /** * Called when carrier config changes on any phone. */ @VisibleForTesting public void notifyCarrierConfigChanged(int phoneId, int subId) { obtainMessage(EVENT_CARRIER_CONFIG_CHANGED, phoneId, subId).sendToTarget(); } private void onCarrierConfigChanged(int phoneId, int subId) { log("onCarrierConfigChanged phoneId " + phoneId + " subId " + subId); if (!SubscriptionManager.isValidPhoneId(phoneId)) { loge("Carrier config change with invalid phoneId " + phoneId); return; } mCarrierConfigLoadedSubIds[phoneId] = subId; reEvaluateAll(); } private boolean isCarrierConfigLoadedForAllSub() { int[] activeSubIds = mSubController.getActiveSubIdList(false); for (int activeSubId : activeSubIds) { boolean isLoaded = false; for (int configLoadedSub : mCarrierConfigLoadedSubIds) { if (configLoadedSub == activeSubId) { isLoaded = true; break; } } if (!isLoaded) { if (DBG) log("Carrier config subId " + activeSubId + " is not loaded."); return false; } } return true; } /** * Wait for subInfo initialization (after boot up) and carrier config load for all active * subscriptions before re-evaluate multi SIM settings. */ private boolean isReadyToReevaluate() { return mSubInfoInitialized && isCarrierConfigLoadedForAllSub(); } private void reEvaluateAll() { if (!isReadyToReevaluate()) return; updateDefaults(); disableDataForNonDefaultNonOpportunisticSubscriptions(); deactivateGroupedOpportunisticSubscriptionIfNeeded(); } Loading Loading @@ -353,12 +446,11 @@ public class MultiSimSettingController extends Handler { * not a user settable value anymore. * 4) If non above is met, clear the default value to INVALID. * * @param init whether the subscriptions are just initialized. */ private void updateDefaults(boolean init) { private void updateDefaults() { if (DBG) log("updateDefaults"); if (!mSubInfoInitialized) return; if (!isReadyToReevaluate()) return; List<SubscriptionInfo> activeSubInfos = mSubController .getActiveSubscriptionInfoList(mContext.getOpPackageName()); Loading @@ -372,7 +464,7 @@ public class MultiSimSettingController extends Handler { return; } int change = updatePrimarySubListAndGetChangeType(activeSubInfos, init); int change = updatePrimarySubListAndGetChangeType(activeSubInfos); if (DBG) log("[updateDefaultValues] change: " + change); if (change == PRIMARY_SUB_NO_CHANGE) return; Loading Loading @@ -414,8 +506,7 @@ public class MultiSimSettingController extends Handler { } @PrimarySubChangeType private int updatePrimarySubListAndGetChangeType(List<SubscriptionInfo> activeSubList, boolean init) { private int updatePrimarySubListAndGetChangeType(List<SubscriptionInfo> activeSubList) { // Update mPrimarySubList. Opportunistic subscriptions can't be default // data / voice / sms subscription. List<Integer> prevPrimarySubList = mPrimarySubList; Loading @@ -423,7 +514,10 @@ public class MultiSimSettingController extends Handler { .map(info -> info.getSubscriptionId()) .collect(Collectors.toList()); if (init) return PRIMARY_SUB_INITIALIZED; if (mInitialHandling) { mInitialHandling = false; return PRIMARY_SUB_INITIALIZED; } if (mPrimarySubList.equals(prevPrimarySubList)) return PRIMARY_SUB_NO_CHANGE; if (mPrimarySubList.size() > prevPrimarySubList.size()) return PRIMARY_SUB_ADDED; Loading Loading @@ -554,7 +648,7 @@ public class MultiSimSettingController extends Handler { } private void disableDataForNonDefaultNonOpportunisticSubscriptions() { if (!mSubInfoInitialized) return; if (!isReadyToReevaluate()) return; int defaultDataSub = mSubController.getDefaultDataSubId(); Loading
tests/telephonytests/src/com/android/internal/telephony/MultiSimSettingControllerTest.java +70 −0 Original line number Diff line number Diff line Loading @@ -118,6 +118,7 @@ public class MultiSimSettingControllerTest extends TelephonyTest { List<SubscriptionInfo> infoList = Arrays.asList(mSubInfo1, mSubInfo2); doReturn(infoList).when(mSubControllerMock) .getActiveSubscriptionInfoList(anyString()); doReturn(new int[]{1, 2}).when(mSubControllerMock).getActiveSubIdList(anyBoolean()); mPhones = new Phone[] {mPhoneMock1, mPhoneMock2}; doReturn(mDataEnabledSettingsMock1).when(mPhoneMock1).getDataEnabledSettings(); Loading Loading @@ -175,6 +176,7 @@ public class MultiSimSettingControllerTest extends TelephonyTest { doReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID).when(mPhoneMock2).getSubId(); List<SubscriptionInfo> infoList = Arrays.asList(mSubInfo1); doReturn(infoList).when(mSubControllerMock).getActiveSubscriptionInfoList(anyString()); doReturn(new int[]{1}).when(mSubControllerMock).getActiveSubIdList(anyBoolean()); // Mark subscription ready as false. The below sub info change should be ignored. mMultiSimSettingControllerUT.notifySubscriptionInfoChanged(); Loading @@ -184,6 +186,7 @@ public class MultiSimSettingControllerTest extends TelephonyTest { verify(mSubControllerMock, never()).setDefaultSmsSubId(anyInt()); mMultiSimSettingControllerUT.notifyAllSubscriptionLoaded(); mMultiSimSettingControllerUT.notifyCarrierConfigChanged(0, 1); waitABit(); // Sub 1 should be default sub silently. Loading @@ -210,8 +213,10 @@ public class MultiSimSettingControllerTest extends TelephonyTest { doReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID).when(mPhoneMock2).getSubId(); List<SubscriptionInfo> infoList = Arrays.asList(mSubInfo1); doReturn(infoList).when(mSubControllerMock).getActiveSubscriptionInfoList(anyString()); doReturn(new int[]{1}).when(mSubControllerMock).getActiveSubIdList(anyBoolean()); mMultiSimSettingControllerUT.notifyAllSubscriptionLoaded(); mMultiSimSettingControllerUT.notifyCarrierConfigChanged(0, 1); waitABit(); // Sub 1 should be default sub silently. Loading @@ -223,8 +228,10 @@ public class MultiSimSettingControllerTest extends TelephonyTest { doReturn(2).when(mPhoneMock1).getSubId(); infoList = Arrays.asList(mSubInfo2); doReturn(infoList).when(mSubControllerMock).getActiveSubscriptionInfoList(anyString()); doReturn(new int[]{2}).when(mSubControllerMock).getActiveSubIdList(anyBoolean()); mMultiSimSettingControllerUT.notifySubscriptionInfoChanged(); mMultiSimSettingControllerUT.notifyCarrierConfigChanged(0, 2); waitABit(); // Sub 1 should be default sub silently. Loading @@ -244,8 +251,10 @@ public class MultiSimSettingControllerTest extends TelephonyTest { doReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID).when(mPhoneMock2).getSubId(); List<SubscriptionInfo> infoList = Arrays.asList(mSubInfo1); doReturn(infoList).when(mSubControllerMock).getActiveSubscriptionInfoList(anyString()); doReturn(new int[]{1}).when(mSubControllerMock).getActiveSubIdList(anyBoolean()); mMultiSimSettingControllerUT.notifyAllSubscriptionLoaded(); mMultiSimSettingControllerUT.notifyCarrierConfigChanged(0, 1); waitABit(); // Sub 1 should be default sub silently. Loading @@ -262,8 +271,10 @@ public class MultiSimSettingControllerTest extends TelephonyTest { doReturn(2).when(mPhoneMock2).getSubId(); infoList = Arrays.asList(mSubInfo1, mSubInfo2); doReturn(infoList).when(mSubControllerMock).getActiveSubscriptionInfoList(anyString()); doReturn(new int[]{1, 2}).when(mSubControllerMock).getActiveSubIdList(anyBoolean()); mMultiSimSettingControllerUT.notifySubscriptionInfoChanged(); mMultiSimSettingControllerUT.notifyCarrierConfigChanged(1, 2); waitABit(); // Intent should be broadcast to ask default data selection. Loading @@ -282,8 +293,10 @@ public class MultiSimSettingControllerTest extends TelephonyTest { doReturn(3).when(mPhoneMock2).getSubId(); infoList = Arrays.asList(mSubInfo1, mSubInfo3); doReturn(infoList).when(mSubControllerMock).getActiveSubscriptionInfoList(anyString()); doReturn(new int[]{1, 3}).when(mSubControllerMock).getActiveSubIdList(anyBoolean()); mMultiSimSettingControllerUT.notifySubscriptionInfoChanged(); mMultiSimSettingControllerUT.notifyCarrierConfigChanged(1, 3); waitABit(); // Intent should be broadcast to ask default data selection. Loading @@ -300,6 +313,8 @@ public class MultiSimSettingControllerTest extends TelephonyTest { doReturn(true).when(mPhoneMock2).isUserDataEnabled(); // After initialization, sub 2 should have mobile data off. mMultiSimSettingControllerUT.notifyAllSubscriptionLoaded(); mMultiSimSettingControllerUT.notifyCarrierConfigChanged(0, 1); mMultiSimSettingControllerUT.notifyCarrierConfigChanged(1, 2); waitABit(); verify(mDataEnabledSettingsMock2).setUserDataEnabled(false); Loading @@ -323,7 +338,10 @@ public class MultiSimSettingControllerTest extends TelephonyTest { doReturn(false).when(mSubControllerMock).isActiveSubId(1); List<SubscriptionInfo> infoList = Arrays.asList(mSubInfo2); doReturn(infoList).when(mSubControllerMock).getActiveSubscriptionInfoList(anyString()); doReturn(new int[]{2}).when(mSubControllerMock).getActiveSubIdList(anyBoolean()); mMultiSimSettingControllerUT.notifySubscriptionInfoChanged(); mMultiSimSettingControllerUT.notifyCarrierConfigChanged( 1, SubscriptionManager.INVALID_SUBSCRIPTION_ID); waitABit(); verify(mSubControllerMock).setDefaultDataSubId(SubscriptionManager.INVALID_SUBSCRIPTION_ID); verify(mSubControllerMock).setDefaultSmsSubId(SubscriptionManager.INVALID_SUBSCRIPTION_ID); Loading @@ -346,6 +364,8 @@ public class MultiSimSettingControllerTest extends TelephonyTest { GlobalSettingsHelper.setBoolean(mContext, Settings.Global.MOBILE_DATA, 2, true); GlobalSettingsHelper.setBoolean(mContext, Settings.Global.DATA_ROAMING, 2, false); mMultiSimSettingControllerUT.notifyAllSubscriptionLoaded(); mMultiSimSettingControllerUT.notifyCarrierConfigChanged(0, 1); mMultiSimSettingControllerUT.notifyCarrierConfigChanged(1, 2); waitABit(); // Create subscription grouping. Loading Loading @@ -391,8 +411,10 @@ public class MultiSimSettingControllerTest extends TelephonyTest { doReturn(1).when(mSubControllerMock).getDefaultVoiceSubId(); List<SubscriptionInfo> infoList = Arrays.asList(mSubInfo1, mSubInfo3); doReturn(infoList).when(mSubControllerMock).getActiveSubscriptionInfoList(anyString()); doReturn(new int[]{1, 3}).when(mSubControllerMock).getActiveSubIdList(anyBoolean()); mMultiSimSettingControllerUT.notifySubscriptionInfoChanged(); mMultiSimSettingControllerUT.notifyCarrierConfigChanged(1, 3); waitABit(); verify(mSubControllerMock).setDefaultDataSubId(3); Loading @@ -414,6 +436,8 @@ public class MultiSimSettingControllerTest extends TelephonyTest { // Notify subscriptions ready. Sub 2 should become the default. But shouldn't turn off // data of oppt sub 1. mMultiSimSettingControllerUT.notifyAllSubscriptionLoaded(); mMultiSimSettingControllerUT.notifyCarrierConfigChanged(0, 1); mMultiSimSettingControllerUT.notifyCarrierConfigChanged(1, 2); waitABit(); verify(mSubControllerMock).setDefaultDataSubId(2); verify(mDataEnabledSettingsMock1, never()).setUserDataEnabled(anyBoolean()); Loading Loading @@ -451,6 +475,8 @@ public class MultiSimSettingControllerTest extends TelephonyTest { // Notify subscriptions ready. Sub 2 should become the default, as sub 1 is opportunistic. mMultiSimSettingControllerUT.notifyAllSubscriptionLoaded(); mMultiSimSettingControllerUT.notifyCarrierConfigChanged(0, 1); mMultiSimSettingControllerUT.notifyCarrierConfigChanged(1, 2); waitABit(); verify(mSubControllerMock).setDefaultDataSubId(2); // No user selection needed, no intent should be sent. Loading Loading @@ -494,6 +520,8 @@ public class MultiSimSettingControllerTest extends TelephonyTest { GlobalSettingsHelper.setBoolean(mContext, Settings.Global.MOBILE_DATA, 1, true); GlobalSettingsHelper.setBoolean(mContext, Settings.Global.DATA_ROAMING, 1, false); mMultiSimSettingControllerUT.notifyAllSubscriptionLoaded(); mMultiSimSettingControllerUT.notifyCarrierConfigChanged(0, 1); mMultiSimSettingControllerUT.notifyCarrierConfigChanged(1, 2); waitABit(); // Create subscription grouping. Loading @@ -514,4 +542,46 @@ public class MultiSimSettingControllerTest extends TelephonyTest { waitABit(); verify(mDataEnabledSettingsMock2).setUserDataEnabled(false); } @Test @SmallTest public void testCarrierConfigLoading() throws Exception { doReturn(true).when(mPhoneMock1).isUserDataEnabled(); doReturn(true).when(mPhoneMock2).isUserDataEnabled(); // Sub 2 should have mobile data off, but it shouldn't happen until carrier configs are // loaded on both subscriptions. mMultiSimSettingControllerUT.notifyAllSubscriptionLoaded(); waitABit(); verify(mDataEnabledSettingsMock2, never()).setUserDataEnabled(false); mMultiSimSettingControllerUT.notifyCarrierConfigChanged(0, 1); waitABit(); verify(mDataEnabledSettingsMock2, never()).setUserDataEnabled(false); mMultiSimSettingControllerUT.notifyCarrierConfigChanged(1, 2); waitABit(); verify(mDataEnabledSettingsMock2).setUserDataEnabled(false); // Switch from sub 2 to sub 3 in phone[1]. clearInvocations(mSubControllerMock); doReturn(false).when(mSubControllerMock).isActiveSubId(2); doReturn(true).when(mSubControllerMock).isActiveSubId(3); doReturn(SubscriptionManager.INVALID_PHONE_INDEX).when(mSubControllerMock).getPhoneId(2); doReturn(1).when(mSubControllerMock).getPhoneId(3); doReturn(3).when(mPhoneMock2).getSubId(); List<SubscriptionInfo> infoList = Arrays.asList(mSubInfo1, mSubInfo3); doReturn(infoList).when(mSubControllerMock).getActiveSubscriptionInfoList(anyString()); doReturn(new int[]{1, 3}).when(mSubControllerMock).getActiveSubIdList(anyBoolean()); // Nothing should happen until carrier config change is notified on sub 3. mMultiSimSettingControllerUT.notifySubscriptionInfoChanged(); waitABit(); verify(mContext, never()).sendBroadcast(any()); mMultiSimSettingControllerUT.notifyCarrierConfigChanged(1, 3); waitABit(); // Intent should be broadcast to ask default data selection. Intent intent = captureBroadcastIntent(); assertEquals(ACTION_PRIMARY_SUBSCRIPTION_LIST_CHANGED, intent.getAction()); assertEquals(EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_DATA, intent.getIntExtra(EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE, -1)); } }