Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 34f09f05 authored by Malcolm Chen's avatar Malcolm Chen Committed by Xiangyu/Malcolm Chen
Browse files

Have MultiSimSettingController listen to carrier config changes

When SIM is added or swapped, have MultiSimSettingController wait until
carrier config is loaded before evaluating whether to pop up SIM related
dialogs or disable grouped oppotunistic subscriptions. Because carrier
config may update opportunistic bit and groupUuid.

Bug: 136187220
Test: manual and unittest
Change-Id: Ife069855f1366c68e6359f7ecdcd92991546cf99
parent 20cba2d5
Loading
Loading
Loading
Loading
+107 −13
Original line number Diff line number Diff line
@@ -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;
@@ -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;

@@ -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_"},
@@ -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.
     */
@@ -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));
    }

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

@@ -261,9 +306,7 @@ public class MultiSimSettingController extends Handler {
    private void onAllSubscriptionsLoaded() {
        if (DBG) log("onAllSubscriptionsLoaded");
        mSubInfoInitialized = true;
        updateDefaults(/*init*/ true);
        disableDataForNonDefaultNonOpportunisticSubscriptions();
        deactivateGroupedOpportunisticSubscriptionIfNeeded();
        reEvaluateAll();
    }

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

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

@@ -554,7 +648,7 @@ public class MultiSimSettingController extends Handler {
    }

    private void disableDataForNonDefaultNonOpportunisticSubscriptions() {
        if (!mSubInfoInitialized) return;
        if (!isReadyToReevaluate()) return;

        int defaultDataSub = mSubController.getDefaultDataSubId();

+70 −0
Original line number Diff line number Diff line
@@ -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();
@@ -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();
@@ -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.
@@ -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.
@@ -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.
@@ -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.
@@ -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.
@@ -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.
@@ -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);

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