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

Commit 7ab01bb4 authored by SongFerng Wang's avatar SongFerng Wang Committed by Android (Google) Code Review
Browse files

Merge "switch SIM refactor to support MEP"

parents 37af4dab 1e0e9021
Loading
Loading
Loading
Loading
+15 −1
Original line number Diff line number Diff line
@@ -8101,11 +8101,17 @@
    <!-- Checkbox to always use for calls.  [CHAR LIMIT=40] -->
    <string name="sim_calls_always_use">Always use this for calls</string>
    <!-- Message for selecting sim for data in settings.  [CHAR LIMIT=40] -->
    <string name="select_sim_for_data">Select a SIM for data</string>
    <string name="select_sim_for_data">Choose SIM for mobile data</string>
    <!-- Message for selecting sim for SMS in settings.  [CHAR LIMIT=40] -->
    <string name="select_sim_for_sms">Select a SIM for SMS</string>
    <!-- Message for switching data SIM; switching takes a while -->
    <string name="data_switch_started">Switching data SIM, this may take up to a minute\u2026</string>
    <!-- Title for selecting specific sim for data in settings.  [CHAR LIMIT=40] -->
    <string name="select_specific_sim_for_data_title">Use <xliff:g id="new_sim" example="carrierA">%1$s</xliff:g> for mobile data?</string>
    <!-- Message for selecting specific sim for data in settings.  [CHAR LIMIT=NONE] -->
    <string name="select_specific_sim_for_data_msg">If you switch to <xliff:g id="new_sim" example="carrierA">%1$s</xliff:g>, <xliff:g id="old_sim" example="carrierB">%2$s</xliff:g> will no longer be used for mobile data.</string>
    <!-- Button on a selecting specific sim dialog to confirm data in settings.  [CHAR LIMIT=40] -->
    <string name="select_specific_sim_for_data_button">Use <xliff:g id="new_sim" example="carrierA">%1$s</xliff:g></string>
    <!-- Message for selecting sim for call in settings.  [CHAR LIMIT=40] -->
    <string name="select_sim_for_calls">Call with</string>
    <!-- Title for selecting a SIM card.  [CHAR LIMIT=40] -->
@@ -12915,14 +12921,22 @@
    <string name="sim_action_switch_sub_dialog_title">Switch to <xliff:g id="carrier_name" example="Google Fi">%1$s</xliff:g>?</string>
    <!-- Title of confirmation dialog asking the user if they want to switch to the SIM card. [CHAR_LIMIT=NONE] -->
    <string name="sim_action_switch_psim_dialog_title">Switch to using SIM card?</string>
    <!-- Title of confirmation dialog asking the user if they want to switch subscription. [CHAR_LIMIT=NONE] -->
    <string name="sim_action_switch_sub_dialog_mep_title">Use <xliff:g id="carrier_name" example="Google Fi">%1$s</xliff:g>?</string>
    <!-- Body text of confirmation dialog for switching subscription that involves switching SIM slots. Indicates that only one SIM can be active at a time. Also that switching will not cancel the user's mobile service plan. [CHAR_LIMIT=NONE] -->
    <string name="sim_action_switch_sub_dialog_text">Only one SIM can be active at a time.\n\nSwitching to <xliff:g id="to_carrier_name" example="Google Fi">%1$s</xliff:g> won\u2019t cancel your <xliff:g id="from_carrier_name" example="Sprint">%2$s</xliff:g> service.</string>
    <!-- Body text of confirmation dialog for switching subscription between two eSIM profiles. Indicates that only one downloaded SIM can be active at a time. Also that switching will not cancel the user's mobile service plan. [CHAR_LIMIT=NONE] -->
    <string name="sim_action_switch_sub_dialog_text_downloaded">Only one downloaded SIM can be active at a time.\n\nSwitching to <xliff:g id="to_carrier_name" example="Google Fi">%1$s</xliff:g> won\u2019t cancel your <xliff:g id="from_carrier_name" example="Sprint">%2$s</xliff:g> service.</string>
    <!-- Body text of confirmation dialog for switching subscription between two eSIM profiles. Indicates that only one SIM can be active at a time. Also that switching will not cancel the user's mobile service plan. [CHAR_LIMIT=NONE] -->
    <string name="sim_action_switch_sub_dialog_text_single_sim">Only one SIM can be active at a time.\n\nSwitching won\u2019t cancel your <xliff:g id="to_carrier_name" example="Google Fi">%1$s</xliff:g> service.</string>
    <!-- Body text of confirmation dialog for switching subscription between two eSIM profiles. Indicates that only one downloaded SIM can be active at a time. Also that switching will not cancel the user's mobile service plan. [CHAR_LIMIT=NONE] -->
    <string name="sim_action_switch_sub_dialog_mep_text">You can use 2 SIMs at a time. To use <xliff:g id="carrier_name" example="Google Fi">%1$s</xliff:g>, turn off another SIM.</string>
    <!-- Text of confirm button in the confirmation dialog asking the user if they want to switch subscription. [CHAR_LIMIT=NONE] -->
    <string name="sim_action_switch_sub_dialog_confirm">Switch to <xliff:g id="carrier_name" example="Google Fi">%1$s</xliff:g></string>
    <!-- Text of carrier list item in the mep confirmation dialog asking the user if they want to turn off the carrier. [CHAR_LIMIT=NONE] -->
    <string name="sim_action_switch_sub_dialog_carrier_list_item_for_turning_off">Turn off <xliff:g id="carrier_name" example="Google Fi">%1$s</xliff:g></string>
    <!-- Text of carrier list item in the mep confirmation dialog asking the user if they want to turn off the carrier. [CHAR_LIMIT=NONE] -->
    <string name="sim_action_switch_sub_dialog_info_outline_for_turning_off">Turning off a SIM won\u2019t cancel your service</string>
    <!-- Status message indicating the device is in the process of disconnecting from one mobile network and immediately connecting to another. [CHAR_LIMIT=NONE] -->
    <string name="sim_action_enabling_sim_without_carrier_name">Connecting to network&#8230;</string>
    <!-- Text of progress dialog indicating the subscription switch is in progress. [CHAR_LIMIT=NONE] -->
+24 −2
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.settings.network;
import android.annotation.IntDef;
import android.app.FragmentManager;
import android.os.Bundle;
import android.telephony.SubscriptionInfo;
import android.util.Log;

import com.android.settings.AsyncTaskSidecar;
@@ -41,11 +42,14 @@ public class SwitchSlotSidecar
    })
    private @interface Command {
        int SWITCH_TO_REMOVABLE_SIM = 0;
        int SWITCH_TO_EUICC_SIM = 1;
    }

    static class Param {
        @Command int command;
        int slotId;
        int port;
        SubscriptionInfo removedSubInfo;
    }

    static class Result {
@@ -65,13 +69,24 @@ public class SwitchSlotSidecar
    }

    /** Starts switching to the removable slot. */
    public void runSwitchToRemovableSlot(int id) {
    public void runSwitchToRemovableSlot(int id, SubscriptionInfo removedSubInfo) {
        Param param = new Param();
        param.command = Command.SWITCH_TO_REMOVABLE_SIM;
        param.slotId = id;
        param.removedSubInfo = removedSubInfo;
        param.port = 0;
        super.run(param);
    }

    /** Starts switching to the removable slot. */
    public void runSwitchToEuiccSlot(int id, int port, SubscriptionInfo removedSubInfo) {
        Param param = new Param();
        param.command = Command.SWITCH_TO_EUICC_SIM;
        param.slotId = id;
        param.removedSubInfo = removedSubInfo;
        param.port = port;
        super.run(param);
    }
    /**
     * Returns the exception thrown during the execution of a command. Will be null in any state
     * other than {@link State#SUCCESS}, and may be null in that state if there was not an error.
@@ -91,7 +106,14 @@ public class SwitchSlotSidecar
        try {
            switch (param.command) {
                case Command.SWITCH_TO_REMOVABLE_SIM:
                    UiccSlotUtil.switchToRemovableSlot(param.slotId, getContext());
                    Log.i(TAG, "Start to switch to removable slot.");
                    UiccSlotUtil.switchToRemovableSlot(getContext(), param.slotId,
                            param.removedSubInfo);
                    break;
                case Command.SWITCH_TO_EUICC_SIM:
                    Log.i(TAG, "Start to switch to euicc slot.");
                    UiccSlotUtil.switchToEuiccSlot(getContext(), param.slotId, param.port,
                            param.removedSubInfo);
                    break;
                default:
                    Log.e(TAG, "Wrong command.");
+122 −0
Original line number Diff line number Diff line
@@ -18,17 +18,30 @@ package com.android.settings.network;

import android.app.FragmentManager;
import android.app.PendingIntent;
import android.content.Intent;
import android.telephony.SubscriptionInfo;
import android.telephony.UiccCardInfo;
import android.telephony.UiccSlotMapping;
import android.telephony.euicc.EuiccManager;
import android.util.Log;

import com.android.settings.SidecarFragment;
import com.android.settings.network.telephony.EuiccOperationSidecar;

import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;

/** A headless fragment encapsulating long-running eSIM enabling/disabling operations. */
public class SwitchToEuiccSubscriptionSidecar extends EuiccOperationSidecar {
    private static final String TAG = "SwitchToEuiccSubscriptionSidecar";
    private static final String ACTION_SWITCH_TO_SUBSCRIPTION =
            "com.android.settings.network.SWITCH_TO_SUBSCRIPTION";
    private static final int ESIM_SLOT_ID = 1;

    private PendingIntent mCallbackIntent;
    private int mSubId;
    private int mPort;

    /** Returns a SwitchToEuiccSubscriptionSidecar sidecar instance. */
    public static SwitchToEuiccSubscriptionSidecar get(FragmentManager fm) {
@@ -46,10 +59,119 @@ public class SwitchToEuiccSubscriptionSidecar extends EuiccOperationSidecar {
        return mCallbackIntent;
    }

    @Override
    public void onStateChange(SidecarFragment fragment) {
        if (fragment == mSwitchSlotSidecar) {
            onSwitchSlotSidecarStateChange();
        } else {
            Log.wtf(TAG, "Received state change from a sidecar not expected.");
        }
    }

    /** Starts calling EuiccManager#switchToSubscription to enable/disable the eSIM profile. */
    // ToDo: delete this api and refactor the related code.
    public void run(int subscriptionId) {
        setState(State.RUNNING, Substate.UNUSED);
        mCallbackIntent = createCallbackIntent();
        mEuiccManager.switchToSubscription(subscriptionId, mCallbackIntent);
    }

    /**
     * Starts calling EuiccManager#switchToSubscription to enable/disable the eSIM profile.
     *
     * @param subscriptionId the esim's subscriptionId.
     * @param port the esim's portId. If user wants to inactivate esim, then user must to assign the
     *             the port. If user wants to activate esim, then the port can be -1.
     * @param removedSubInfo if the all of slots have sims, it should remove the one of active sim.
     *                       If the removedSubInfo is null, then use the default value.
     *                       The default value is the esim slot and portId 0.
     */
    public void run(int subscriptionId, int port, SubscriptionInfo removedSubInfo) {
        setState(State.RUNNING, Substate.UNUSED);
        mCallbackIntent = createCallbackIntent();
        mSubId = subscriptionId;
        // To check whether the esim slot's port is active. If yes, skip setSlotMapping. If no,
        // set this slot+port into setSimSlotMapping.
        mPort = (port < 0) ? getTargetPortId(removedSubInfo) : port;
        Log.i(TAG, "The SubId is " + mSubId + "The port is " + mPort);

        mSwitchSlotSidecar.runSwitchToEuiccSlot(getTargetSlot(), mPort, removedSubInfo);
    }

    private int getTargetPortId(SubscriptionInfo removedSubInfo) {
        if (!mTelephonyManager.isMultiSimEnabled() || !isMultipleEnabledProfilesSupported()) {
            // In the 'SS mode' or 'DSDS+no MEP', the port is 0.
            return 0;
        }

        // In the 'DSDS+MEP', if the removedSubInfo is esim, then the port is
        // removedSubInfo's port.
        if (removedSubInfo != null && removedSubInfo.isEmbedded()) {
            return removedSubInfo.getPortIndex();
        }

        // In DSDS+MEP mode, the removedSubInfo is psim or is null, it means the this esim need
        // another port in the esim slot.
        // To find another esim's port and value is from 0;
        int port = 0;
        Collection<UiccSlotMapping> uiccSlotMappings = mTelephonyManager.getSimSlotMapping();
        for (UiccSlotMapping uiccSlotMapping :
                uiccSlotMappings.stream()
                        .filter(
                                uiccSlotMapping -> uiccSlotMapping.getPhysicalSlotIndex()
                                        == getTargetSlot())
                        .collect(Collectors.toList())) {
            if (uiccSlotMapping.getPortIndex() == port) {
                port++;
            }
        }
        return port;
    }

    private int getTargetSlot() {
        return ESIM_SLOT_ID;
    }

    private void onSwitchSlotSidecarStateChange() {
        switch (mSwitchSlotSidecar.getState()) {
            case State.SUCCESS:
                mSwitchSlotSidecar.reset();
                Log.i(TAG,
                        "Successfully SimSlotMapping. Start to enable/disable esim");
                switchToSubscription();
                break;
            case State.ERROR:
                mSwitchSlotSidecar.reset();
                Log.i(TAG, "Failed to set SimSlotMapping");
                setState(State.ERROR, Substate.UNUSED);
                break;
        }
    }

    private boolean isMultipleEnabledProfilesSupported() {
        List<UiccCardInfo> cardInfos = mTelephonyManager.getUiccCardsInfo();
        if (cardInfos == null) {
            Log.w(TAG, "UICC cards info list is empty.");
            return false;
        }
        return cardInfos.stream().anyMatch(
                cardInfo -> cardInfo.isMultipleEnabledProfilesSupported());
    }

    private void switchToSubscription() {
        // The SimSlotMapping is ready, then to execute activate/inactivate esim.
        EuiccManager.ResultListener callback = new EuiccManager.ResultListener() {
            @Override
            public void onComplete(int resultCode, Intent resultIntent) {
                Log.i(TAG, String.format("Result code : %d;", resultCode));
                if (resultCode == EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_OK) {
                    setState(State.SUCCESS, Substate.UNUSED);
                } else {
                    setState(State.ERROR, resultCode);
                }
            }
        };
        mEuiccManager.switchToSubscription(mSubId, mPort, getContext().getMainExecutor(),
                callback);
    }
}
+23 −9
Original line number Diff line number Diff line
@@ -38,8 +38,8 @@ public class SwitchToRemovableSlotSidecar extends EuiccOperationSidecar

    // Stateless members.
    private SwitchToEuiccSubscriptionSidecar mSwitchToSubscriptionSidecar;
    private SwitchSlotSidecar mSwitchSlotSidecar;
    private int mPhysicalSlotId;
    private SubscriptionInfo mRemovedSubInfo;

    /** Returns a SwitchToRemovableSlotSidecar sidecar instance. */
    public static SwitchToRemovableSlotSidecar get(FragmentManager fm) {
@@ -51,20 +51,17 @@ public class SwitchToRemovableSlotSidecar extends EuiccOperationSidecar
        super.onCreate(savedInstanceState);
        mSwitchToSubscriptionSidecar =
                SwitchToEuiccSubscriptionSidecar.get(getChildFragmentManager());
        mSwitchSlotSidecar = SwitchSlotSidecar.get(getChildFragmentManager());
    }

    @Override
    public void onResume() {
        super.onResume();
        mSwitchToSubscriptionSidecar.addListener(this);
        mSwitchSlotSidecar.addListener(this);
    }

    @Override
    public void onPause() {
        mSwitchToSubscriptionSidecar.removeListener(this);
        mSwitchSlotSidecar.removeListener(this);
        super.onPause();
    }

@@ -90,29 +87,46 @@ public class SwitchToRemovableSlotSidecar extends EuiccOperationSidecar
     *
     * @param physicalSlotId removable physical SIM slot ID.
     */
    // ToDo: delete this api and refactor the related code.
    public void run(int physicalSlotId) {
        mPhysicalSlotId = physicalSlotId;
        SubscriptionManager subscriptionManager =
                getContext().getSystemService(SubscriptionManager.class);
        if (SubscriptionUtil.getActiveSubscriptions(subscriptionManager).stream()
                .anyMatch(SubscriptionInfo::isEmbedded)) {
            // In SS mode, the esim is active, then inactivate the esim.
            Log.i(TAG, "There is an active eSIM profile. Disable the profile first.");
            // Use INVALID_SUBSCRIPTION_ID to disable the only active profile.
            mSwitchToSubscriptionSidecar.run(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
            mSwitchToSubscriptionSidecar.run(SubscriptionManager.INVALID_SUBSCRIPTION_ID, 0, null);
        } else {
            Log.i(TAG, "There is no active eSIM profiles. Start to switch to removable slot.");
            mSwitchSlotSidecar.runSwitchToRemovableSlot(mPhysicalSlotId);
            mSwitchSlotSidecar.runSwitchToRemovableSlot(mPhysicalSlotId, null);
        }
    }

    /**
     * Starts switching to the removable slot.
     *
     * @param physicalSlotId removable physical SIM slot ID.
     * @param removedSubInfo if the all of slots have sims, it should remove the one of active sim.
     *                       If the removedSubInfo is null, then use the default value.
     *                       The default value is the removable physical SIM slot and portId 0.
     */
    public void run(int physicalSlotId, SubscriptionInfo removedSubInfo) {
        mPhysicalSlotId = physicalSlotId;
        mRemovedSubInfo = removedSubInfo;

        Log.i(TAG, "Start to switch to removable slot.");
        mSwitchSlotSidecar.runSwitchToRemovableSlot(mPhysicalSlotId, mRemovedSubInfo);
    }

    private void onSwitchToSubscriptionSidecarStateChange() {
        switch (mSwitchToSubscriptionSidecar.getState()) {
            case State.SUCCESS:
                mSwitchToSubscriptionSidecar.reset();
                Log.i(
                        TAG,
                Log.i(TAG,
                        "Successfully disabled eSIM profile. Start to switch to Removable slot.");
                mSwitchSlotSidecar.runSwitchToRemovableSlot(mPhysicalSlotId);
                mSwitchSlotSidecar.runSwitchToRemovableSlot(mPhysicalSlotId, mRemovedSubInfo);
                break;
            case State.ERROR:
                mSwitchToSubscriptionSidecar.reset();
+168 −40

File changed.

Preview size limit exceeded, changes collapsed.

Loading