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

Commit 8c077691 authored by Xiangyu/Malcolm Chen's avatar Xiangyu/Malcolm Chen Committed by android-build-merger
Browse files

Merge changes Ie01bcb94,Ifb49dcab,I9bcd4424

am: fd773f8e

Change-Id: I09a29b47199ce35eb8c1275c4d1400d6124a64b6
parents cbc08a56 fd773f8e
Loading
Loading
Loading
Loading
+133 −64
Original line number Original line Diff line number Diff line
@@ -24,6 +24,8 @@ import static android.telephony.TelephonyManager.EXTRA_DEFAULT_SUBSCRIPTION_SELE
import static android.telephony.TelephonyManager.EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_NONE;
import static android.telephony.TelephonyManager.EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_NONE;
import static android.telephony.TelephonyManager.EXTRA_SUBSCRIPTION_ID;
import static android.telephony.TelephonyManager.EXTRA_SUBSCRIPTION_ID;


import android.annotation.IntDef;
import android.annotation.NonNull;
import android.content.Context;
import android.content.Context;
import android.content.Intent;
import android.content.Intent;
import android.os.Handler;
import android.os.Handler;
@@ -37,7 +39,11 @@ import android.telephony.TelephonyManager;
import android.util.Log;
import android.util.Log;


import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;


import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Collectors;


@@ -61,9 +67,38 @@ public class MultiSimSettingController extends Handler {
    private static final int EVENT_SUBSCRIPTION_GROUP_CHANGED        = 5;
    private static final int EVENT_SUBSCRIPTION_GROUP_CHANGED        = 5;
    private static final int EVENT_DEFAULT_DATA_SUBSCRIPTION_CHANGED = 6;
    private static final int EVENT_DEFAULT_DATA_SUBSCRIPTION_CHANGED = 6;


    @Retention(RetentionPolicy.SOURCE)
    @IntDef(prefix = {"PRIMARY_SUB_"},
            value = {
                    PRIMARY_SUB_NO_CHANGE,
                    PRIMARY_SUB_ADDED,
                    PRIMARY_SUB_REMOVED,
                    PRIMARY_SUB_SWAPPED,
                    PRIMARY_SUB_SWAPPED_IN_GROUP,
                    PRIMARY_SUB_MARKED_OPPT,
                    PRIMARY_SUB_INITIALIZED
    })
    private @interface PrimarySubChangeType {}

    // Primary subscription not change.
    private static final int PRIMARY_SUB_NO_CHANGE              = 0;
    // One or more primary subscriptions are activated.
    private static final int PRIMARY_SUB_ADDED                  = 1;
    // One or more primary subscriptions are deactivated.
    private static final int PRIMARY_SUB_REMOVED                = 2;
    // One or more primary subscriptions are swapped.
    private static final int PRIMARY_SUB_SWAPPED                = 3;
    // One or more primary subscriptions are swapped but within same sub group.
    private static final int PRIMARY_SUB_SWAPPED_IN_GROUP       = 4;
    // One or more primary subscriptions are marked as opportunistic.
    private static final int PRIMARY_SUB_MARKED_OPPT            = 5;
    // Subscription information is initially loaded.
    private static final int PRIMARY_SUB_INITIALIZED            = 6;

    private final Context mContext;
    private final Context mContext;
    private final SubscriptionController mSubController;
    private final SubscriptionController mSubController;
    private List<SubscriptionInfo> mPrimarySubList;
    // Keep a record of active primary (non-opportunistic) subscription list.
    @NonNull private List<Integer> mPrimarySubList = new ArrayList<>();


    /** The singleton instance. */
    /** The singleton instance. */
    private static MultiSimSettingController sInstance = null;
    private static MultiSimSettingController sInstance = null;
@@ -181,8 +216,7 @@ public class MultiSimSettingController extends Handler {
     * If user is enabling a non-default non-opportunistic subscription, make it default
     * If user is enabling a non-default non-opportunistic subscription, make it default
     * data subscription.
     * data subscription.
     */
     */
    @VisibleForTesting
    private void onUserDataEnabled(int subId, boolean enable) {
    public void onUserDataEnabled(int subId, boolean enable) {
        if (DBG) log("onUserDataEnabled");
        if (DBG) log("onUserDataEnabled");
        // Make sure MOBILE_DATA of subscriptions in same group are synced.
        // Make sure MOBILE_DATA of subscriptions in same group are synced.
        setUserDataEnabledForGroup(subId, enable);
        setUserDataEnabledForGroup(subId, enable);
@@ -197,8 +231,7 @@ public class MultiSimSettingController extends Handler {
    /**
    /**
     * Make sure DATA_ROAMING of subscriptions in same group are synced.
     * Make sure DATA_ROAMING of subscriptions in same group are synced.
     */
     */
    @VisibleForTesting
    private void onRoamingDataEnabled(int subId, boolean enable) {
    public void onRoamingDataEnabled(int subId, boolean enable) {
        if (DBG) log("onRoamingDataEnabled");
        if (DBG) log("onRoamingDataEnabled");
        setRoamingDataEnabledForGroup(subId, enable);
        setRoamingDataEnabledForGroup(subId, enable);


@@ -210,10 +243,9 @@ public class MultiSimSettingController extends Handler {
     * Upon initialization, update defaults and mobile data enabling.
     * Upon initialization, update defaults and mobile data enabling.
     * Should only be triggered once.
     * Should only be triggered once.
     */
     */
    @VisibleForTesting
    private void onAllSubscriptionsLoaded() {
    public void onAllSubscriptionsLoaded() {
        if (DBG) log("onAllSubscriptionsLoaded");
        if (DBG) log("onAllSubscriptionsLoaded");
        updateDefaults();
        updateDefaults(/*init*/ true);
        disableDataForNonDefaultNonOpportunisticSubscriptions();
        disableDataForNonDefaultNonOpportunisticSubscriptions();
    }
    }


@@ -222,19 +254,17 @@ public class MultiSimSettingController extends Handler {
     *
     *
     * Make sure non-default non-opportunistic subscriptions has data off.
     * Make sure non-default non-opportunistic subscriptions has data off.
     */
     */
    @VisibleForTesting
    private void onSubscriptionsChanged() {
    public void onSubscriptionsChanged() {
        if (DBG) log("onSubscriptionsChanged");
        if (DBG) log("onSubscriptionsChanged");
        if (!SubscriptionInfoUpdater.isSubInfoInitialized()) return;
        if (!SubscriptionInfoUpdater.isSubInfoInitialized()) return;
        updateDefaults();
        updateDefaults(/*init*/ false);
        disableDataForNonDefaultNonOpportunisticSubscriptions();
        disableDataForNonDefaultNonOpportunisticSubscriptions();
    }
    }


    /**
    /**
     * Make sure non-default non-opportunistic subscriptions has data disabled.
     * Make sure non-default non-opportunistic subscriptions has data disabled.
     */
     */
    @VisibleForTesting
    private void onDefaultDataSettingChanged() {
    public void onDefaultDataSettingChanged() {
        if (DBG) log("onDefaultDataSettingChanged");
        if (DBG) log("onDefaultDataSettingChanged");
        disableDataForNonDefaultNonOpportunisticSubscriptions();
        disableDataForNonDefaultNonOpportunisticSubscriptions();
    }
    }
@@ -245,8 +275,7 @@ public class MultiSimSettingController extends Handler {
     * TODO: b/130258159 have a separate database table for grouped subscriptions so we don't
     * TODO: b/130258159 have a separate database table for grouped subscriptions so we don't
     * manually sync each setting.
     * manually sync each setting.
     */
     */
    @VisibleForTesting
    private void onSubscriptionGroupChanged(ParcelUuid groupUuid) {
    public void onSubscriptionGroupChanged(ParcelUuid groupUuid) {
        if (DBG) log("onSubscriptionGroupChanged");
        if (DBG) log("onSubscriptionGroupChanged");


        List<SubscriptionInfo> infoList = mSubController.getSubscriptionsInGroup(
        List<SubscriptionInfo> infoList = mSubController.getSubscriptionsInGroup(
@@ -305,32 +334,37 @@ public class MultiSimSettingController extends Handler {
     *    data subscription on it. Because default data in Android Q is an internal value,
     *    data subscription on it. Because default data in Android Q is an internal value,
     *    not a user settable value anymore.
     *    not a user settable value anymore.
     * 4) If non above is met, clear the default value to INVALID.
     * 4) If non above is met, clear the default value to INVALID.
     *
     * @param init whether the subscriptions are just initialized.
     */
     */
    @VisibleForTesting
    private void updateDefaults(boolean init) {
    public void updateDefaults() {
        if (DBG) log("updateDefaults");
        if (DBG) log("updateDefaults");


        if (!SubscriptionInfoUpdater.isSubInfoInitialized()) return;
        if (!SubscriptionInfoUpdater.isSubInfoInitialized()) return;


        List<SubscriptionInfo> activeSubInfos = mSubController
        List<SubscriptionInfo> activeSubInfos = mSubController
                .getActiveSubscriptionInfoList(mContext.getOpPackageName());
                .getActiveSubscriptionInfoList(mContext.getOpPackageName());
        // If subscription controller is not ready, do nothing.
        if (activeSubInfos == null) return;


        List<SubscriptionInfo> prevPrimarySubList = mPrimarySubList;
        if (ArrayUtils.isEmpty(activeSubInfos)) {
            mPrimarySubList.clear();
            if (DBG) log("[updateDefaultValues] No active sub. Setting default to INVALID sub.");
            mSubController.setDefaultDataSubId(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
            mSubController.setDefaultVoiceSubId(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
            mSubController.setDefaultSmsSubId(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
            return;
        }


        // Opportunistic subscriptions can't be default data / voice / sms subscription.
        int change = updatePrimarySubListAndGetChangeType(activeSubInfos, init);
        mPrimarySubList = activeSubInfos.stream().filter(info -> !info.isOpportunistic())
        if (DBG) log("[updateDefaultValues] change: " + change);
                .collect(Collectors.toList());
        if (change == PRIMARY_SUB_NO_CHANGE) return;


        // If there's only one primary subscription active, we trigger PREFERRED_PICK_DIALOG
        // If there's only one primary subscription active, we trigger PREFERRED_PICK_DIALOG
        // dialog if and only if there were multiple primary SIM cards and one is removed.
        // dialog if and only if there were multiple primary SIM cards and one is removed.
        // Otherwise, if user just inserted their first SIM, or there's one primary and one
        // Otherwise, if user just inserted their first SIM, or there's one primary and one
        // opportunistic subscription active (activeSubInfos.size() > 1), we automatically
        // opportunistic subscription active (activeSubInfos.size() > 1), we automatically
        // set the primary to be default SIM and return.
        // set the primary to be default SIM and return.
        if (mPrimarySubList.size() == 1 && (activeSubInfos.size() > 1
        if (mPrimarySubList.size() == 1 && change != PRIMARY_SUB_REMOVED) {
                || (prevPrimarySubList != null && prevPrimarySubList.isEmpty()))) {
            int subId = mPrimarySubList.get(0);
            int subId = mPrimarySubList.get(0).getSubscriptionId();
            if (DBG) log("[updateDefaultValues] to only primary sub " + subId);
            if (DBG) log("[updateDefaultValues] to only primary sub " + subId);
            mSubController.setDefaultDataSubId(subId);
            mSubController.setDefaultDataSubId(subId);
            mSubController.setDefaultVoiceSubId(subId);
            mSubController.setDefaultVoiceSubId(subId);
@@ -340,7 +374,6 @@ public class MultiSimSettingController extends Handler {


        if (DBG) log("[updateDefaultValues] records: " + mPrimarySubList);
        if (DBG) log("[updateDefaultValues] records: " + mPrimarySubList);


        // TODO: b/121394765 update logic once confirmed the behaviors.
        // Update default data subscription.
        // Update default data subscription.
        if (DBG) log("[updateDefaultValues] Update default data subscription");
        if (DBG) log("[updateDefaultValues] Update default data subscription");
        boolean dataSelected = updateDefaultValue(mPrimarySubList,
        boolean dataSelected = updateDefaultValue(mPrimarySubList,
@@ -359,32 +392,76 @@ public class MultiSimSettingController extends Handler {
                mSubController.getDefaultSmsSubId(),
                mSubController.getDefaultSmsSubId(),
                (newValue -> mSubController.setDefaultSmsSubId(newValue)));
                (newValue -> mSubController.setDefaultSmsSubId(newValue)));


        showSimSelectDialogIfNeeded(prevPrimarySubList, dataSelected, voiceSelected, smsSelected);
        showSimSelectDialogIfNeeded(change, dataSelected, voiceSelected, smsSelected);
    }

    @PrimarySubChangeType
    private int updatePrimarySubListAndGetChangeType(List<SubscriptionInfo> activeSubList,
            boolean init) {
        // Update mPrimarySubList. Opportunistic subscriptions can't be default
        // data / voice / sms subscription.
        List<Integer> prevPrimarySubList = mPrimarySubList;
        mPrimarySubList = activeSubList.stream().filter(info -> !info.isOpportunistic())
                .map(info -> info.getSubscriptionId())
                .collect(Collectors.toList());

        if (init) return PRIMARY_SUB_INITIALIZED;
        if (mPrimarySubList.equals(prevPrimarySubList)) return PRIMARY_SUB_NO_CHANGE;
        if (mPrimarySubList.size() > prevPrimarySubList.size()) return PRIMARY_SUB_ADDED;

        if (mPrimarySubList.size() == prevPrimarySubList.size()) {
            // We need to differentiate PRIMARY_SUB_SWAPPED and PRIMARY_SUB_SWAPPED_IN_GROUP:
            // For SWAPPED_IN_GROUP, we never pop up dialog to ask data sub selection again.
            for (int subId : mPrimarySubList) {
                boolean swappedInSameGroup = false;
                for (int prevSubId : prevPrimarySubList) {
                    if (areSubscriptionsInSameGroup(subId, prevSubId)) {
                        swappedInSameGroup = true;
                        break;
                    }
                }
                if (!swappedInSameGroup) return PRIMARY_SUB_SWAPPED;
            }
            return PRIMARY_SUB_SWAPPED_IN_GROUP;
        } else /* mPrimarySubList.size() < prevPrimarySubList.size() */ {
            // We need to differentiate whether the missing subscription is removed or marked as
            // opportunistic. Usually only one subscription may change at a time, But to be safe, if
            // any previous primary subscription becomes inactive, we consider it
            for (int subId : prevPrimarySubList) {
                if (mPrimarySubList.contains(subId)) continue;
                if (!mSubController.isActiveSubId(subId)) return PRIMARY_SUB_REMOVED;
                if (!mSubController.isOpportunistic(subId)) {
                    // Should never happen.
                    loge("[updatePrimarySubListAndGetChangeType]: missing active primary subId "
                            + subId);
                }
            }
            return PRIMARY_SUB_MARKED_OPPT;
        }
    }
    }


    private void showSimSelectDialogIfNeeded(List<SubscriptionInfo> prevPrimarySubs,
    private void showSimSelectDialogIfNeeded(int change, boolean dataSelected,
            boolean dataSelected, boolean voiceSelected, boolean smsSelected) {
            boolean voiceSelected, boolean smsSelected) {
        @TelephonyManager.DefaultSubscriptionSelectType
        @TelephonyManager.DefaultSubscriptionSelectType
        int dialogType = EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_NONE;
        int dialogType = EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_NONE;
        int preferredSubId = INVALID_SUBSCRIPTION_ID;
        int preferredSubId = INVALID_SUBSCRIPTION_ID;
        boolean primarySubRemoved = prevPrimarySubs != null
                && mPrimarySubList.size() < prevPrimarySubs.size();
        boolean primarySubAdded = prevPrimarySubs != null
                && mPrimarySubList.size() > prevPrimarySubs.size();


        // If a primary subscription is removed and only one is left active, ask user
        // If a primary subscription is removed and only one is left active, ask user
        // for preferred sub selection if any default setting is not set.
        // for preferred sub selection if any default setting is not set.
        // If another primary subscription is added or default data is not selected, ask
        // If another primary subscription is added or default data is not selected, ask
        // user to select default for data as it's most important.
        // user to select default for data as it's most important.
        if (mPrimarySubList.size() == 1 && primarySubRemoved
        if (mPrimarySubList.size() == 1 && change == PRIMARY_SUB_REMOVED
                && (!dataSelected || !smsSelected || !voiceSelected)) {
                && (!dataSelected || !smsSelected || !voiceSelected)) {
            dialogType = EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_ALL;
            dialogType = EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_ALL;
            preferredSubId = mPrimarySubList.get(0).getSubscriptionId();
            preferredSubId = mPrimarySubList.get(0);
        } else if (mPrimarySubList.size() > 1 && (!dataSelected || primarySubAdded)) {
        } else if (mPrimarySubList.size() > 1 && (change == PRIMARY_SUB_REMOVED
                || change == PRIMARY_SUB_ADDED || change == PRIMARY_SUB_SWAPPED)) {
            // If change is SWAPPED_IN_GROUP or MARKED_OPPT orINITIALIZED, don't ask user again.
            dialogType = EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_DATA;
            dialogType = EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_DATA;
        }
        }


        if (dialogType != EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_NONE) {
        if (dialogType != EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_NONE) {
            log("[showSimSelectDialogIfNeeded] showing dialog type " + dialogType);
            Intent intent = new Intent();
            Intent intent = new Intent();
            intent.setAction(ACTION_PRIMARY_SUBSCRIPTION_LIST_CHANGED);
            intent.setAction(ACTION_PRIMARY_SUBSCRIPTION_LIST_CHANGED);
            intent.setClassName("com.android.settings",
            intent.setClassName("com.android.settings",
@@ -402,23 +479,29 @@ public class MultiSimSettingController extends Handler {
        if (!SubscriptionInfoUpdater.isSubInfoInitialized()) return;
        if (!SubscriptionInfoUpdater.isSubInfoInitialized()) return;


        int defaultDataSub = mSubController.getDefaultDataSubId();
        int defaultDataSub = mSubController.getDefaultDataSubId();
        // Only disable data for non-default subscription if default sub is active.
        if (!mSubController.isActiveSubId(defaultDataSub)) {
            log("default data sub is inactive, skip disabling data for non-default subs");
            return;
        }


        for (Phone phone : PhoneFactory.getPhones()) {
        for (Phone phone : PhoneFactory.getPhones()) {
            if (phone.getSubId() != defaultDataSub
            if (phone.getSubId() != defaultDataSub
                    && SubscriptionManager.isValidSubscriptionId(phone.getSubId())
                    && SubscriptionManager.isValidSubscriptionId(phone.getSubId())
                    && !mSubController.isOpportunistic(phone.getSubId())
                    && !mSubController.isOpportunistic(phone.getSubId())
                    && phone.isUserDataEnabled()) {
                    && phone.isUserDataEnabled()
                    && !areSubscriptionsInSameGroup(defaultDataSub, phone.getSubId())) {
                log("setting data to false on " + phone.getSubId());
                log("setting data to false on " + phone.getSubId());
                phone.getDataEnabledSettings().setUserDataEnabled(false);
                phone.getDataEnabledSettings().setUserDataEnabled(false);
            }
            }
        }
        }
    }
    }


    private boolean areSubscriptionsInSameGroup(int subId1, int subId2) {
        if (!SubscriptionManager.isUsableSubscriptionId(subId1)
                || !SubscriptionManager.isUsableSubscriptionId(subId2)) return false;
        if (subId1 == subId2) return true;

        ParcelUuid groupUuid1 = mSubController.getGroupUuid(subId1);
        ParcelUuid groupUuid2 = mSubController.getGroupUuid(subId2);
        return groupUuid1 != null && groupUuid1.equals(groupUuid2);
    }

    /**
    /**
     * Make sure MOBILE_DATA of subscriptions in the same group with the subId
     * Make sure MOBILE_DATA of subscriptions in the same group with the subId
     * are synced.
     * are synced.
@@ -434,15 +517,6 @@ public class MultiSimSettingController extends Handler {
            int currentSubId = info.getSubscriptionId();
            int currentSubId = info.getSubscriptionId();
            // TODO: simplify when setUserDataEnabled becomes singleton
            // TODO: simplify when setUserDataEnabled becomes singleton
            if (mSubController.isActiveSubId(currentSubId)) {
            if (mSubController.isActiveSubId(currentSubId)) {
                // If we end up enabling two active primary subscriptions, don't enable the
                // non-default data sub. This will only happen if two primary subscriptions
                // in a group are both active. This is not a valid use-case now, but we are
                // handling it just in case.
                if (enable && !mSubController.isOpportunistic(currentSubId)
                        && currentSubId != mSubController.getDefaultSubId()) {
                    loge("Can not enable two active primary subscriptions.");
                    continue;
                }
                // For active subscription, call setUserDataEnabled through DataEnabledSettings.
                // For active subscription, call setUserDataEnabled through DataEnabledSettings.
                Phone phone = PhoneFactory.getPhone(mSubController.getPhoneId(currentSubId));
                Phone phone = PhoneFactory.getPhone(mSubController.getPhoneId(currentSubId));
                // If enable is true and it's not opportunistic subscription, we don't enable it,
                // If enable is true and it's not opportunistic subscription, we don't enable it,
@@ -481,23 +555,18 @@ public class MultiSimSettingController extends Handler {
    }
    }


    // Returns whether the new default value is valid.
    // Returns whether the new default value is valid.
    private boolean updateDefaultValue(List<SubscriptionInfo> subInfos, int oldValue,
    private boolean updateDefaultValue(List<Integer> primarySubList, int oldValue,
            UpdateDefaultAction action) {
            UpdateDefaultAction action) {
        int newValue = INVALID_SUBSCRIPTION_ID;
        int newValue = INVALID_SUBSCRIPTION_ID;


        if (subInfos.size() > 0) {
        if (primarySubList.size() > 0) {
            // Get groupUuid of old
            for (int subId : primarySubList) {
            ParcelUuid groupUuid = mSubController.getGroupUuid(oldValue);
                if (DBG) log("[updateDefaultValue] Record.id: " + subId);

            for (SubscriptionInfo info : subInfos) {
                int id = info.getSubscriptionId();
                if (DBG) log("[updateDefaultValue] Record.id: " + id);
                // If the old subId is still active, or there's another active primary subscription
                // If the old subId is still active, or there's another active primary subscription
                // that is in the same group, that should become the new default subscription.
                // that is in the same group, that should become the new default subscription.
                if (id == oldValue || (groupUuid != null && groupUuid.equals(
                if (areSubscriptionsInSameGroup(subId, oldValue)) {
                        info.getGroupUuid()))) {
                    newValue = subId;
                    log("[updateDefaultValue] updates to subId=" + id);
                    log("[updateDefaultValue] updates to subId=" + newValue);
                    newValue = id;
                    break;
                    break;
                }
                }
            }
            }