switch SIM refactor to support MEP am: 37358798bc am: 483ff27a47

Original change: https://android-review.googlesource.com/c/platform/packages/apps/Settings/+/2043024

Change-Id: I6e35330168e2094e5bb7b9cbcf80574fcfe38773
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
SongFerngWang
2022-03-27 15:26:33 +00:00
committed by Automerger Merge Worker
11 changed files with 526 additions and 106 deletions

View File

@@ -7958,11 +7958,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] -->
@@ -12744,14 +12750,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] -->

View File

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

View File

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

View File

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

View File

@@ -19,8 +19,10 @@ package com.android.settings.network;
import android.annotation.IntDef;
import android.content.Context;
import android.provider.Settings;
import android.telephony.SubscriptionInfo;
import android.telephony.TelephonyManager;
import android.telephony.UiccSlotInfo;
import android.telephony.UiccSlotMapping;
import android.util.Log;
import com.android.settingslib.utils.ThreadUtils;
@@ -29,17 +31,21 @@ import com.google.common.collect.ImmutableList;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Collection;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
// ToDo: to do the refactor for renaming
public class UiccSlotUtil {
private static final String TAG = "UiccSlotUtil";
private static final long DEFAULT_WAIT_AFTER_SWITCH_TIMEOUT_MILLIS = 25 * 1000L;
;
public static final int INVALID_PHYSICAL_SLOT_ID = -1;
public static final int INVALID_PORT_ID = -1;
/**
* Mode for switching to eSIM slot which decides whether there is cleanup process, e.g.
@@ -47,9 +53,9 @@ public class UiccSlotUtil {
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef({
SwitchingEsimMode.NO_CLEANUP,
SwitchingEsimMode.ASYNC_CLEANUP,
SwitchingEsimMode.SYNC_CLEANUP
SwitchingEsimMode.NO_CLEANUP,
SwitchingEsimMode.ASYNC_CLEANUP,
SwitchingEsimMode.SYNC_CLEANUP
})
public @interface SwitchingEsimMode {
/** No cleanup process after switching to eSIM slot */
@@ -76,44 +82,108 @@ public class UiccSlotUtil {
* Switches to the removable slot. It waits for SIM_STATE_LOADED after switch. If slotId is
* INVALID_PHYSICAL_SLOT_ID, the method will use the first detected inactive removable slot.
*
* @param slotId the physical removable slot id.
* @param slotId the physical removable slot id.
* @param context the application context.
* @throws UiccSlotsException if there is an error.
*/
//ToDo: delete this api and refactor the related code.
public static synchronized void switchToRemovableSlot(int slotId, Context context)
throws UiccSlotsException {
switchToRemovableSlot(context, slotId, null);
}
/**
* Switches to the removable slot. It waits for SIM_STATE_LOADED after switch. If slotId is
* INVALID_PHYSICAL_SLOT_ID, the method will use the first detected inactive removable slot.
*
* @param slotId the physical removable slot id.
* @param context the application context.
* @param removedSubInfo In the DSDS+MEP mode, 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.
* @throws UiccSlotsException if there is an error.
*/
public static synchronized void switchToRemovableSlot(Context context, int slotId,
SubscriptionInfo removedSubInfo) throws UiccSlotsException {
if (ThreadUtils.isMainThread()) {
throw new IllegalThreadStateException(
"Do not call switchToRemovableSlot on the main thread.");
}
TelephonyManager telMgr = context.getSystemService(TelephonyManager.class);
if (telMgr.isMultiSimEnabled()) {
// If this device supports multiple active slots, don't mess with TelephonyManager.
Log.i(TAG, "Multiple active slots supported. Not calling switchSlots.");
return;
}
UiccSlotInfo[] slots = telMgr.getUiccSlotsInfo();
if (slotId == INVALID_PHYSICAL_SLOT_ID) {
for (int i = 0; i < slots.length; i++) {
if (slots[i].isRemovable()
&& !slots[i].getPorts().stream().findFirst().get().isActive()
&& slots[i].getCardStateInfo() != UiccSlotInfo.CARD_STATE_INFO_ERROR
&& slots[i].getCardStateInfo() != UiccSlotInfo.CARD_STATE_INFO_RESTRICTED) {
performSwitchToRemovableSlot(i, context);
return;
}
}
} else {
if (slotId >= slots.length || !slots[slotId].isRemovable()) {
throw new UiccSlotsException("The given slotId is not a removable slot: " + slotId);
}
if (!slots[slotId].getPorts().stream().findFirst().get().isActive()) {
performSwitchToRemovableSlot(slotId, context);
}
}
int inactiveRemovableSlot = getInactiveRemovableSlot(telMgr.getUiccSlotsInfo(), slotId);
performSwitchToSlot(telMgr,
prepareUiccSlotMappingsForRemovableSlot(telMgr.getSimSlotMapping(),
inactiveRemovableSlot, removedSubInfo, telMgr.isMultiSimEnabled()),
context);
}
private static void performSwitchToRemovableSlot(int slotId, Context context)
/**
* Switches to the Euicc slot. It waits for SIM_STATE_LOADED after switch.
*
* @param context the application context.
* @param slotId the Euicc slot id.
* @param port the Euicc slot port id.
* @param removedSubInfo In the DSDS+MEP mode, if the all of slots have sims, it should
* remove the one of active sim.
* If the removedSubInfo is null, then it uses the default value.
* The default value is the esim slot and portId 0.
* @throws UiccSlotsException if there is an error.
*/
public static synchronized void switchToEuiccSlot(Context context, int slotId, int port,
SubscriptionInfo removedSubInfo) throws UiccSlotsException {
if (ThreadUtils.isMainThread()) {
throw new IllegalThreadStateException(
"Do not call switchToRemovableSlot on the main thread.");
}
TelephonyManager telMgr = context.getSystemService(TelephonyManager.class);
Collection<UiccSlotMapping> uiccSlotMappings = telMgr.getSimSlotMapping();
Log.i(TAG, "The SimSlotMapping: " + uiccSlotMappings);
if (isTargetSlotActive(uiccSlotMappings, slotId, port)) {
Log.i(TAG, "The slot is active, then the sim can enable directly.");
return;
}
Collection<UiccSlotMapping> newUiccSlotMappings = new ArrayList<>();
if (!telMgr.isMultiSimEnabled()) {
// In the 'SS mode', the port is 0.
newUiccSlotMappings.add(new UiccSlotMapping(port, slotId, 0));
} else {
// DSDS+MEP
// The target slot+port is not active, but the all of logical slots are full. It
// needs to replace one of logical slots.
int removedSlot =
(removedSubInfo != null) ? removedSubInfo.getSimSlotIndex() : slotId;
int removedPort = (removedSubInfo != null) ? removedSubInfo.getPortIndex() : 0;
Log.i(TAG,
String.format("Start to set SimSlotMapping from slot%d-port%d to slot%d-port%d",
slotId, port, removedSlot, removedPort));
newUiccSlotMappings =
uiccSlotMappings.stream().map(uiccSlotMapping -> {
if (uiccSlotMapping.getPhysicalSlotIndex() == removedSlot
&& uiccSlotMapping.getPortIndex() == removedPort) {
return new UiccSlotMapping(port, slotId,
uiccSlotMapping.getLogicalSlotIndex());
}
return uiccSlotMapping;
}).collect(Collectors.toList());
}
Log.i(TAG, "The SimSlotMapping: " + newUiccSlotMappings);
performSwitchToSlot(telMgr, newUiccSlotMappings, context);
}
private static boolean isTargetSlotActive(Collection<UiccSlotMapping> uiccSlotMappings,
int slotId, int port) {
return uiccSlotMappings.stream()
.anyMatch(
uiccSlotMapping -> uiccSlotMapping.getPhysicalSlotIndex() == slotId
&& uiccSlotMapping.getPortIndex() == port);
}
private static void performSwitchToSlot(TelephonyManager telMgr,
Collection<UiccSlotMapping> uiccSlotMappings, Context context)
throws UiccSlotsException {
CarrierConfigChangedReceiver receiver = null;
long waitingTimeMillis =
@@ -125,7 +195,7 @@ public class UiccSlotUtil {
CountDownLatch latch = new CountDownLatch(1);
receiver = new CarrierConfigChangedReceiver(latch);
receiver.registerOn(context);
switchSlots(context, slotId);
telMgr.setSimSlotMapping(uiccSlotMappings);
latch.await(waitingTimeMillis, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
@@ -138,22 +208,80 @@ public class UiccSlotUtil {
}
/**
* Changes the logical slot to physical slot mapping. OEM should override this to provide
* device-specific implementation if the device supports switching slots.
*
* @param context the application context.
* @param physicalSlots List of physical slot ids in the order of logical slots.
* @param slots The UiccSlotInfo list.
* @param slotId The physical removable slot id.
* @return The inactive physical removable slot id. If the physical removable slot id is
* active, then return -1.
* @throws UiccSlotsException if there is an error.
*/
private static void switchSlots(Context context, int... physicalSlots)
private static int getInactiveRemovableSlot(UiccSlotInfo[] slots, int slotId)
throws UiccSlotsException {
TelephonyManager telMgr = context.getSystemService(TelephonyManager.class);
if (telMgr.isMultiSimEnabled()) {
// If this device supports multiple active slots, don't mess with TelephonyManager.
Log.i(TAG, "Multiple active slots supported. Not calling switchSlots.");
return;
if (slots == null) {
throw new UiccSlotsException("UiccSlotInfo is null");
}
if (!telMgr.switchSlots(physicalSlots)) {
throw new UiccSlotsException("Failed to switch slots");
if (slotId == INVALID_PHYSICAL_SLOT_ID) {
for (int i = 0; i < slots.length; i++) {
if (slots[i].isRemovable()
&& !slots[i].getPorts().stream().findFirst().get().isActive()
&& slots[i].getCardStateInfo() != UiccSlotInfo.CARD_STATE_INFO_ERROR
&& slots[i].getCardStateInfo() != UiccSlotInfo.CARD_STATE_INFO_RESTRICTED) {
return i;
}
}
} else {
if (slotId >= slots.length || !slots[slotId].isRemovable()) {
throw new UiccSlotsException("The given slotId is not a removable slot: " + slotId);
}
if (!slots[slotId].getPorts().stream().findFirst().get().isActive()) {
return slotId;
}
}
return INVALID_PHYSICAL_SLOT_ID;
}
private static Collection<UiccSlotMapping> prepareUiccSlotMappingsForRemovableSlot(
Collection<UiccSlotMapping> uiccSlotMappings, int slotId,
SubscriptionInfo removedSubInfo, boolean isMultiSimEnabled) {
if (slotId == INVALID_PHYSICAL_SLOT_ID
|| uiccSlotMappings.stream().anyMatch(uiccSlotMapping ->
uiccSlotMapping.getPhysicalSlotIndex() == slotId
&& uiccSlotMapping.getPortIndex() == 0)) {
// The slot is invalid slot id, then to skip this.
// The slot is active, then the sim can enable directly.
return uiccSlotMappings;
}
Collection<UiccSlotMapping> newUiccSlotMappings = new ArrayList<>();
if (!isMultiSimEnabled) {
// In the 'SS mode', the port is 0.
newUiccSlotMappings.add(new UiccSlotMapping(0, slotId, 0));
} else if (removedSubInfo != null) {
// DSDS+MEP
// The target slot+port is not active, but the all of logical slots are full. It
// needs to replace one of logical slots.
Log.i(TAG,
String.format("Start to set SimSlotMapping from slot%d-port%d to slot%d-port%d",
slotId, 0, removedSubInfo.getSimSlotIndex(),
removedSubInfo.getPortIndex()));
newUiccSlotMappings =
uiccSlotMappings.stream().map(uiccSlotMapping -> {
if (uiccSlotMapping.getPhysicalSlotIndex()
== removedSubInfo.getSimSlotIndex()
&& uiccSlotMapping.getPortIndex()
== removedSubInfo.getPortIndex()) {
return new UiccSlotMapping(0, slotId,
uiccSlotMapping.getLogicalSlotIndex());
}
return uiccSlotMapping;
}).collect(Collectors.toList());
} else {
// DSDS+no MEP
// The removable slot should be in UiccSlotMapping.
newUiccSlotMappings = uiccSlotMappings;
Log.i(TAG, "The removedSubInfo is null");
}
Log.i(TAG, "The SimSlotMapping: " + newUiccSlotMappings);
return newUiccSlotMappings;
}
}

View File

@@ -38,11 +38,15 @@ public class ConfirmDialogFragment extends BaseDialogFragment
*/
public interface OnConfirmListener {
/**
* @param tag The tag in the caller.
* @param confirmed True if the user has clicked the positive button. False if the user has
* clicked the negative button or cancel the dialog.
* @param tag The tag in the caller.
* @param confirmed True if the user has clicked the positive button. False if the
* user has
* clicked the negative button or cancel the dialog.
* @param itemPosition It is the position of item, if user selects one of the list item.
* If the user select "cancel" or the dialog does not have list, then
* the value is -1.
*/
void onConfirm(int tag, boolean confirmed);
void onConfirm(int tag, boolean confirmed, int itemPosition);
}
/** Displays a confirmation dialog which has confirm and cancel buttons. */
@@ -89,19 +93,21 @@ public class ConfirmDialogFragment extends BaseDialogFragment
@Override
public void onClick(DialogInterface dialog, int which) {
informCaller(which == DialogInterface.BUTTON_POSITIVE);
Log.i(TAG, "dialog onClick =" + which);
informCaller(which == DialogInterface.BUTTON_POSITIVE, -1);
}
@Override
public void onCancel(DialogInterface dialog) {
informCaller(false);
informCaller(false, -1);
}
private void informCaller(boolean confirmed) {
private void informCaller(boolean confirmed, int itemPosition) {
OnConfirmListener listener = getListener(OnConfirmListener.class);
if (listener == null) {
return;
}
listener.onConfirm(getTagInCaller(), confirmed);
listener.onConfirm(getTagInCaller(), confirmed, itemPosition);
}
}

View File

@@ -96,7 +96,7 @@ public class DeleteEuiccSubscriptionDialogActivity extends SubscriptionActionDia
}
@Override
public void onConfirm(int tag, boolean confirmed) {
public void onConfirm(int tag, boolean confirmed, int itemPosition) {
if (!confirmed) {
finish();
return;

View File

@@ -24,10 +24,12 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.os.SystemClock;
import android.telephony.TelephonyManager;
import android.telephony.euicc.EuiccManager;
import android.util.Log;
import com.android.settings.SidecarFragment;
import com.android.settings.network.SwitchSlotSidecar;
import java.util.concurrent.atomic.AtomicInteger;
@@ -37,7 +39,8 @@ import java.util.concurrent.atomic.AtomicInteger;
* should implement its own get() function to return an instance of that class, and implement the
* functional class like run() to actually trigger the function in EuiccManager.
*/
public abstract class EuiccOperationSidecar extends SidecarFragment {
public abstract class EuiccOperationSidecar extends SidecarFragment
implements SidecarFragment.Listener{
private static final String TAG = "EuiccOperationSidecar";
private static final int REQUEST_CODE = 0;
private static final String EXTRA_OP_ID = "op_id";
@@ -45,6 +48,9 @@ public abstract class EuiccOperationSidecar extends SidecarFragment {
new AtomicInteger((int) SystemClock.elapsedRealtime());
protected EuiccManager mEuiccManager;
protected TelephonyManager mTelephonyManager;
protected SwitchSlotSidecar mSwitchSlotSidecar;
private int mResultCode;
private int mDetailedCode;
@@ -107,6 +113,8 @@ public abstract class EuiccOperationSidecar extends SidecarFragment {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mEuiccManager = getContext().getSystemService(EuiccManager.class);
mTelephonyManager = getContext().getSystemService(TelephonyManager.class);
mSwitchSlotSidecar = SwitchSlotSidecar.get(getChildFragmentManager());
getContext()
.getApplicationContext()
@@ -117,12 +125,42 @@ public abstract class EuiccOperationSidecar extends SidecarFragment {
null);
}
@Override
public void onResume() {
super.onResume();
mSwitchSlotSidecar.addListener(this);
}
@Override
public void onPause() {
mSwitchSlotSidecar.removeListener(this);
super.onPause();
}
@Override
public void onDestroy() {
getContext().getApplicationContext().unregisterReceiver(mReceiver);
super.onDestroy();
}
@Override
public void onStateChange(SidecarFragment fragment) {
if (fragment == mSwitchSlotSidecar) {
switch (mSwitchSlotSidecar.getState()) {
case State.SUCCESS:
mSwitchSlotSidecar.reset();
Log.i(TAG, "mSwitchSlotSidecar SUCCESS");
break;
case State.ERROR:
mSwitchSlotSidecar.reset();
Log.i(TAG, "mSwitchSlotSidecar ERROR");
break;
}
} else {
Log.wtf(TAG, "Received state change from a sidecar not expected.");
}
}
public int getResultCode() {
return mResultCode;
}

View File

@@ -23,6 +23,7 @@ import android.os.UserManager;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.telephony.UiccCardInfo;
import android.telephony.UiccSlotInfo;
import android.text.TextUtils;
import android.util.Log;
@@ -40,7 +41,9 @@ import com.android.settings.sim.SimActivationNotifier;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/** This dialog activity handles both eSIM and pSIM subscriptions enabling and disabling. */
public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogActivity
@@ -55,6 +58,8 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
private static final int DIALOG_TAG_ENABLE_SIM_CONFIRMATION = 2;
private static final int DIALOG_TAG_ENABLE_DSDS_CONFIRMATION = 3;
private static final int DIALOG_TAG_ENABLE_DSDS_REBOOT_CONFIRMATION = 4;
private static final int DIALOG_TAG_ENABLE_SIM_CONFIRMATION_MEP = 5;
// Number of SIMs for DSDS
private static final int NUM_OF_SIMS_FOR_DSDS = 2;
// Support RTL mode
@@ -85,11 +90,11 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
private boolean mIsEsimOperation;
private TelephonyManager mTelMgr;
private boolean isRtlMode;
private List<SubscriptionInfo> mActiveSubInfos;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = getIntent();
int subId = intent.getIntExtra(ARG_SUB_ID, SubscriptionManager.INVALID_SUBSCRIPTION_ID);
mTelMgr = getSystemService(TelephonyManager.class);
@@ -107,6 +112,7 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
return;
}
mActiveSubInfos = SubscriptionUtil.getActiveSubscriptions(mSubscriptionManager);
mSubInfo = SubscriptionUtil.getSubById(mSubscriptionManager, subId);
mIsEsimOperation = mSubInfo != null && mSubInfo.isEmbedded();
mSwitchToEuiccSubscriptionSidecar =
@@ -116,6 +122,7 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
mEnable = intent.getBooleanExtra(ARG_enable, true);
isRtlMode = getResources().getConfiguration().getLayoutDirection()
== View.LAYOUT_DIRECTION_RTL;
Log.i(TAG, "isMultipleEnabledProfilesSupported():" + isMultipleEnabledProfilesSupported());
if (savedInstanceState == null) {
if (mEnable) {
@@ -154,7 +161,7 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
}
@Override
public void onConfirm(int tag, boolean confirmed) {
public void onConfirm(int tag, boolean confirmed, int itemPosition) {
if (!confirmed
&& tag != DIALOG_TAG_ENABLE_DSDS_CONFIRMATION
&& tag != DIALOG_TAG_ENABLE_DSDS_REBOOT_CONFIRMATION) {
@@ -162,14 +169,16 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
return;
}
SubscriptionInfo removedSubInfo = null;
switch (tag) {
case DIALOG_TAG_DISABLE_SIM_CONFIRMATION:
if (mIsEsimOperation) {
Log.i(TAG, "Disabling the eSIM profile.");
showProgressDialog(
getString(R.string.privileged_action_disable_sub_dialog_progress));
int port = mSubInfo != null ? mSubInfo.getPortIndex() : 0;
mSwitchToEuiccSubscriptionSidecar.run(
SubscriptionManager.INVALID_SUBSCRIPTION_ID);
SubscriptionManager.INVALID_SUBSCRIPTION_ID, port, null);
return;
}
Log.i(TAG, "Disabling the pSIM profile.");
@@ -201,6 +210,11 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
SimActivationNotifier.setShowSimSettingsNotification(this, true);
mTelMgr.switchMultiSimConfig(NUM_OF_SIMS_FOR_DSDS);
break;
case DIALOG_TAG_ENABLE_SIM_CONFIRMATION_MEP:
if (itemPosition != -1) {
removedSubInfo = (mActiveSubInfos != null) ? mActiveSubInfos.get(itemPosition)
: null;
}
case DIALOG_TAG_ENABLE_SIM_CONFIRMATION:
Log.i(TAG, "User confirmed to enable the subscription.");
if (mIsEsimOperation) {
@@ -209,12 +223,15 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
R.string.sim_action_switch_sub_dialog_progress,
SubscriptionUtil.getUniqueSubscriptionDisplayName(
mSubInfo, this)));
mSwitchToEuiccSubscriptionSidecar.run(mSubInfo.getSubscriptionId());
mSwitchToEuiccSubscriptionSidecar.run(mSubInfo.getSubscriptionId(),
UiccSlotUtil.INVALID_PORT_ID,
removedSubInfo);
return;
}
showProgressDialog(
getString(R.string.sim_action_enabling_sim_without_carrier_name));
mSwitchToRemovableSlotSidecar.run(UiccSlotUtil.INVALID_PHYSICAL_SLOT_ID);
mSwitchToRemovableSlotSidecar.run(UiccSlotUtil.INVALID_PHYSICAL_SLOT_ID,
removedSubInfo);
break;
default:
Log.e(TAG, "Unrecognized confirmation dialog tag: " + tag);
@@ -225,8 +242,7 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
private void handleSwitchToEuiccSubscriptionSidecarStateChange() {
switch (mSwitchToEuiccSubscriptionSidecar.getState()) {
case SidecarFragment.State.SUCCESS:
Log.i(
TAG,
Log.i(TAG,
String.format(
"Successfully %s the eSIM profile.",
mEnable ? "enable" : "disable"));
@@ -235,8 +251,7 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
finish();
break;
case SidecarFragment.State.ERROR:
Log.i(
TAG,
Log.i(TAG,
String.format(
"Failed to %s the eSIM profile.", mEnable ? "enable" : "disable"));
mSwitchToEuiccSubscriptionSidecar.reset();
@@ -290,7 +305,8 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
if (mIsEsimOperation) {
Log.i(TAG, "DSDS enabled, start to enable profile: " + mSubInfo.getSubscriptionId());
// For eSIM operations, we simply switch to the selected eSIM profile.
mSwitchToEuiccSubscriptionSidecar.run(mSubInfo.getSubscriptionId());
mSwitchToEuiccSubscriptionSidecar.run(mSubInfo.getSubscriptionId(),
UiccSlotUtil.INVALID_PORT_ID, null);
return;
}
@@ -305,10 +321,8 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
mSubscriptionManager.setUiccApplicationsEnabled(mSubInfo.getSubscriptionId(), mEnable);
finish();
} else {
Log.i(
TAG,
"The device does not support toggling pSIM. It is enough to just "
+ "enable the removable slot.");
Log.i(TAG, "The device does not support toggling pSIM. It is enough to just "
+ "enable the removable slot.");
}
}
@@ -319,7 +333,10 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
showEnableDsdsConfirmDialog();
return;
}
if (!mIsEsimOperation && mTelMgr.isMultiSimEnabled()) {
if (!mIsEsimOperation && mTelMgr.isMultiSimEnabled()
&& isRemovableSimEnabled()) {
// This case is for switching on psim when device is not multiple enable profile
// supported.
Log.i(TAG, "Toggle on pSIM, no dialog displayed.");
handleTogglePsimAction();
finish();
@@ -372,27 +389,55 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
}
private void showEnableSimConfirmDialog() {
List<SubscriptionInfo> activeSubs =
SubscriptionUtil.getActiveSubscriptions(mSubscriptionManager);
SubscriptionInfo activeSub = activeSubs.isEmpty() ? null : activeSubs.get(0);
if (activeSub == null) {
if (mActiveSubInfos == null || mActiveSubInfos.isEmpty()) {
Log.i(TAG, "No active subscriptions available.");
showNonSwitchSimConfirmDialog();
return;
}
Log.i(TAG, "Found active subscription.");
boolean isBetweenEsim = mIsEsimOperation && activeSub.isEmbedded();
if (mTelMgr.isMultiSimEnabled() && !isBetweenEsim) {
Log.i(TAG, "mActiveSubInfos:" + mActiveSubInfos);
boolean isSwitchingBetweenEsims = mIsEsimOperation
&& mActiveSubInfos.stream().anyMatch(activeSubInfo -> activeSubInfo.isEmbedded());
boolean isMultiSimEnabled = mTelMgr.isMultiSimEnabled();
if (isMultiSimEnabled
&& !isMultipleEnabledProfilesSupported()
&& !isSwitchingBetweenEsims) {
// Showing the "no switch dialog" for below cases.
// DSDS mode + no MEP +
// (there is the active psim -> esim switch on => active (psim + esim))
showNonSwitchSimConfirmDialog();
return;
}
if (isMultiSimEnabled && isMultipleEnabledProfilesSupported()) {
if (mActiveSubInfos.size() < NUM_OF_SIMS_FOR_DSDS) {
// The sim can add into device directly, so showing the "no switch dialog".
// DSDS + MEP + (active sim < NUM_OF_SIMS_FOR_DSDS)
showNonSwitchSimConfirmDialog();
} else {
// The all of slots have sim, it needs to show the "MEP switch dialog".
// DSDS + MEP + two active sims
showMepSwitchSimConfirmDialog();
}
return;
}
// Showing the "switch dialog" for below cases.
// case1: SS mode + psim switch on from esim.
// case2: SS mode + esim switch from psim.
// case3: DSDS mode + No MEP + esim switch on from another esim.
SubscriptionInfo activeSub =
(isMultiSimEnabled && isSwitchingBetweenEsims)
? mActiveSubInfos.stream()
.filter(activeSubInfo -> activeSubInfo.isEmbedded())
.findFirst().get()
: mActiveSubInfos.get(0);
ConfirmDialogFragment.show(
this,
ConfirmDialogFragment.OnConfirmListener.class,
DIALOG_TAG_ENABLE_SIM_CONFIRMATION,
getSwitchSubscriptionTitle(),
getSwitchDialogBodyMsg(activeSub, isBetweenEsim),
getSwitchDialogBodyMsg(activeSub, isSwitchingBetweenEsims),
getSwitchDialogPosBtnText(),
getString(R.string.sim_action_cancel));
}
@@ -408,6 +453,10 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
getString(R.string.sim_action_cancel));
}
private void showMepSwitchSimConfirmDialog() {
Log.i(TAG, "showMepSwitchSimConfirmDialog");
}
private String getSwitchDialogPosBtnText() {
return mIsEsimOperation
? getString(
@@ -468,6 +517,20 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
return switchDialogMsg.toString();
}
private ArrayList<String> getSwitchDialogBodyList() {
ArrayList<String> list = new ArrayList<String>(mActiveSubInfos.stream()
.map(subInfo -> {
CharSequence subInfoName = SubscriptionUtil.getUniqueSubscriptionDisplayName(
subInfo, this);
return getString(
R.string.sim_action_switch_sub_dialog_carrier_list_item_for_turning_off,
subInfoName);
})
.collect(Collectors.toList()));
list.add(getString(R.string.sim_action_cancel));
return list;
}
private boolean isDsdsConditionSatisfied() {
if (mTelMgr.isMultiSimEnabled()) {
Log.i(TAG, "DSDS is already enabled. Condition not satisfied.");
@@ -477,17 +540,7 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
Log.i(TAG, "Hardware does not support DSDS.");
return false;
}
ImmutableList<UiccSlotInfo> slotInfos = UiccSlotUtil.getSlotInfos(mTelMgr);
boolean isRemovableSimEnabled =
slotInfos.stream()
.anyMatch(
slot ->
slot != null
&& slot.isRemovable()
&& slot.getPorts().stream().anyMatch(
port -> port.isActive())
&& slot.getCardStateInfo()
== UiccSlotInfo.CARD_STATE_INFO_PRESENT);
boolean isRemovableSimEnabled = isRemovableSimEnabled();
if (mIsEsimOperation && isRemovableSimEnabled) {
Log.i(TAG, "eSIM operation and removable SIM is enabled. DSDS condition satisfied.");
return true;
@@ -496,13 +549,36 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
SubscriptionUtil.getActiveSubscriptions(mSubscriptionManager).stream()
.anyMatch(SubscriptionInfo::isEmbedded);
if (!mIsEsimOperation && isEsimProfileEnabled) {
Log.i(
TAG,
"Removable SIM operation and eSIM profile is enabled. DSDS condition"
+ " satisfied.");
Log.i(TAG, "Removable SIM operation and eSIM profile is enabled. DSDS condition"
+ " satisfied.");
return true;
}
Log.i(TAG, "DSDS condition not satisfied.");
return false;
}
private boolean isRemovableSimEnabled() {
ImmutableList<UiccSlotInfo> slotInfos = UiccSlotUtil.getSlotInfos(mTelMgr);
boolean isRemovableSimEnabled =
slotInfos.stream()
.anyMatch(
slot -> slot != null
&& slot.isRemovable()
&& slot.getPorts().stream().anyMatch(
port -> port.isActive())
&& slot.getCardStateInfo()
== UiccSlotInfo.CARD_STATE_INFO_PRESENT);
Log.i(TAG, "isRemovableSimEnabled: " + isRemovableSimEnabled);
return isRemovableSimEnabled;
}
private boolean isMultipleEnabledProfilesSupported() {
List<UiccCardInfo> cardInfos = mTelMgr.getUiccCardsInfo();
if (cardInfos == null) {
Log.w(TAG, "UICC cards info list is empty.");
return false;
}
return cardInfos.stream().anyMatch(
cardInfo -> cardInfo.isMultipleEnabledProfilesSupported());
}
}

View File

@@ -85,7 +85,7 @@ public class DsdsDialogActivity extends SubscriptionActionDialogActivity
}
@Override
public void onConfirm(int tag, boolean confirmed) {
public void onConfirm(int tag, boolean confirmed, int itemPosition) {
if (!confirmed) {
Log.i(TAG, "User cancel the dialog to enable DSDS.");
startChooseSimActivity();

View File

@@ -101,7 +101,7 @@ public class SwitchToEsimConfirmDialogActivity extends SubscriptionActionDialogA
}
@Override
public void onConfirm(int tag, boolean confirmed) {
public void onConfirm(int tag, boolean confirmed, int itemPosition) {
if (!confirmed) {
AlertDialogFragment.show(
this,