Merge changes I6871db6c,Ia261e3b4,I1588bd4a,I1b81faf0,I9a596440, ...

* changes:
  [MEP] sort the simSlotMapping by logcal slot id
  In E+E, the user can't enable the PSIM
  [MEP] psim's logical slot index is 0
  [MEP] The condition of "null point check" is wrong
  Fix the settings crash when SimDialogActivity is null
  [MEP] The subscriptionInfo's getSimSlotIndex is logical slotId
  [MEP] the port id is wrong
  [MEP] Inserting a pSIM while user has 2 esims, showing the MEP dialog
  [MEP] Refactor SlotSidecar API for all of sim page.
  [MEP]The Esim's PhysicalSlotIndex is wrong
  Lost code for setting the list as visible
  The list does not follow the UX dialog design doc in alert dialog.
  The carrier name is wrong in the dialog
  Refine the mobile data selection UI
This commit is contained in:
SongFerng Wang
2022-04-14 07:08:41 +00:00
committed by Gerrit Code Review
21 changed files with 1176 additions and 197 deletions

View File

@@ -78,11 +78,19 @@ public class SwitchSlotSidecar
super.run(param);
}
/** Starts switching to the removable slot. */
public void runSwitchToEuiccSlot(int id, int port, SubscriptionInfo removedSubInfo) {
/**
* Start the SimSlotMapping process if the euicc slot is not in SimSlotMapping list.
* @param physicalSlotId The physical slot id.
* @param port The port id.
* @param removedSubInfo The subscriptionInfo which is selected by the user to disable when all
* of sim slots are full in the device. If all of slots are not full in
* the device, then this is null.
*/
public void runSwitchToEuiccSlot(int physicalSlotId, int port,
SubscriptionInfo removedSubInfo) {
Param param = new Param();
param.command = Command.SWITCH_TO_EUICC_SIM;
param.slotId = id;
param.slotId = physicalSlotId;
param.removedSubInfo = removedSubInfo;
param.port = port;
super.run(param);

View File

@@ -21,14 +21,13 @@ import android.app.PendingIntent;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
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.Comparator;
import java.util.List;
import java.util.stream.Collectors;
@@ -37,7 +36,6 @@ public class SwitchToEuiccSubscriptionSidecar extends EuiccOperationSidecar {
private static final String TAG = "SwitchToEuiccSidecar";
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;
@@ -70,20 +68,15 @@ public class SwitchToEuiccSubscriptionSidecar extends EuiccOperationSidecar {
}
}
/** 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 port the esim's portId. If user wants to inactivate esim, then user must to assign
* the corresponding port. If user wants to activate esim, then the port can be
* {@link UiccSlotUtil#INVALID_PORT_ID}. When it is
* {@link UiccSlotUtil#INVALID_PORT_ID}, the system will reassign a corresponding
* port 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 esim slot and portId 0.
@@ -92,11 +85,20 @@ public class SwitchToEuiccSubscriptionSidecar extends EuiccOperationSidecar {
setState(State.RUNNING, Substate.UNUSED);
mCallbackIntent = createCallbackIntent();
mSubId = subscriptionId;
int targetSlot = getTargetSlot();
if (targetSlot < 0) {
Log.d(TAG, "There is no esim, the TargetSlot is " + targetSlot);
setState(State.ERROR, Substate.UNUSED);
return;
}
// 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;
mRemovedSubInfo = removedSubInfo;
Log.i(TAG, "The SubId is " + mSubId + ". The port is " + mPort);
Log.d(TAG,
String.format("set esim into the SubId%d Slot%d:Port%d",
mSubId, targetSlot, mPort));
if (mTelephonyManager.isMultiSimEnabled() && removedSubInfo != null
&& removedSubInfo.isEmbedded()) {
@@ -108,7 +110,7 @@ public class SwitchToEuiccSubscriptionSidecar extends EuiccOperationSidecar {
mEuiccManager.switchToSubscription(SubscriptionManager.INVALID_SUBSCRIPTION_ID, mPort,
mCallbackIntent);
} else {
mSwitchSlotSidecar.runSwitchToEuiccSlot(getTargetSlot(), mPort, removedSubInfo);
mSwitchSlotSidecar.runSwitchToEuiccSlot(targetSlot, mPort, removedSubInfo);
}
}
@@ -124,18 +126,26 @@ public class SwitchToEuiccSubscriptionSidecar extends EuiccOperationSidecar {
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;
// In DSDS+MEP mode, the removedSubInfo is psim or is null, it means this esim needs
// a new corresponding port in the esim slot.
// For example:
// 1) If there is no enabled esim and the user add new esim. This new esim's port is 0.
// 2) If there is one enabled esim in port0 and the user add new esim. This new esim's
// port is 1.
// 3) If there is one enabled esim in port1 and the user add new esim. This new esim's
// port is 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) {
SubscriptionManager subscriptionManager = getContext().getSystemService(
SubscriptionManager.class);
List<SubscriptionInfo> activeEsimSubInfos =
SubscriptionUtil.getActiveSubscriptions(subscriptionManager)
.stream()
.filter(i -> i.isEmbedded())
.sorted(Comparator.comparingInt(SubscriptionInfo::getPortIndex))
.collect(Collectors.toList());
for (SubscriptionInfo subscriptionInfo : activeEsimSubInfos) {
if (subscriptionInfo.getPortIndex() == port) {
port++;
}
}
@@ -143,7 +153,7 @@ public class SwitchToEuiccSubscriptionSidecar extends EuiccOperationSidecar {
}
private int getTargetSlot() {
return ESIM_SLOT_ID;
return UiccSlotUtil.getEsimSlotId(getContext());
}
private void onSwitchSlotSidecarStateChange() {

View File

@@ -80,29 +80,6 @@ public class SwitchToRemovableSlotSidecar extends EuiccOperationSidecar
}
}
/**
* Starts switching to the removable slot. It disables the active eSIM profile before switching
* if there is one.
*
* @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, 0, null);
} else {
Log.i(TAG, "There is no active eSIM profiles. Start to switch to removable slot.");
mSwitchSlotSidecar.runSwitchToRemovableSlot(mPhysicalSlotId, null);
}
}
/**
* Starts switching to the removable slot.
*
@@ -124,7 +101,7 @@ public class SwitchToRemovableSlotSidecar extends EuiccOperationSidecar
// Use INVALID_SUBSCRIPTION_ID to disable the only active profile.
mSwitchToSubscriptionSidecar.run(SubscriptionManager.INVALID_SUBSCRIPTION_ID, 0, null);
} else if (mTelephonyManager.isMultiSimEnabled() && mRemovedSubInfo != null) {
// In DSDS mode+MEP, if the replaced esim is active, then it should be disabled esim
// In DSDS mode+MEP, if the replaced esim is active, then it should disable that esim
// profile before changing SimSlotMapping process.
// Use INVALID_SUBSCRIPTION_ID to disable the esim profile.
mSwitchToSubscriptionSidecar.run(SubscriptionManager.INVALID_SUBSCRIPTION_ID,

View File

@@ -20,11 +20,13 @@ import android.annotation.IntDef;
import android.content.Context;
import android.provider.Settings;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.telephony.UiccSlotInfo;
import android.telephony.UiccSlotMapping;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.settingslib.utils.ThreadUtils;
import com.google.common.collect.ImmutableList;
@@ -33,9 +35,11 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
// ToDo: to do the refactor for renaming
public class UiccSlotUtil {
@@ -44,6 +48,7 @@ public class UiccSlotUtil {
private static final long DEFAULT_WAIT_AFTER_SWITCH_TIMEOUT_MILLIS = 25 * 1000L;
public static final int INVALID_LOGICAL_SLOT_ID = -1;
public static final int INVALID_PHYSICAL_SLOT_ID = -1;
public static final int INVALID_PORT_ID = -1;
@@ -112,9 +117,27 @@ public class UiccSlotUtil {
}
TelephonyManager telMgr = context.getSystemService(TelephonyManager.class);
int inactiveRemovableSlot = getInactiveRemovableSlot(telMgr.getUiccSlotsInfo(), slotId);
Log.d(TAG, "The InactiveRemovableSlot: " + inactiveRemovableSlot);
if (inactiveRemovableSlot == INVALID_PHYSICAL_SLOT_ID) {
// The slot is invalid slot id, then to skip this.
// The slot is active, then the sim can enable directly.
return;
}
Collection<UiccSlotMapping> uiccSlotMappings = telMgr.getSimSlotMapping();
Log.d(TAG, "The SimSlotMapping: " + uiccSlotMappings);
SubscriptionManager subscriptionManager = context.getSystemService(
SubscriptionManager.class);
int excludedLogicalSlotIndex = getExcludedLogicalSlotIndex(uiccSlotMappings,
SubscriptionUtil.getActiveSubscriptions(subscriptionManager), removedSubInfo,
telMgr.isMultiSimEnabled());
performSwitchToSlot(telMgr,
prepareUiccSlotMappingsForRemovableSlot(telMgr.getSimSlotMapping(),
inactiveRemovableSlot, removedSubInfo, telMgr.isMultiSimEnabled()),
prepareUiccSlotMappings(uiccSlotMappings,
/*slot is psim*/ true,
inactiveRemovableSlot,
/*removable sim's port Id*/ TelephonyManager.DEFAULT_PORT_INDEX,
excludedLogicalSlotIndex),
context);
}
@@ -122,7 +145,7 @@ public class UiccSlotUtil {
* 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 physicalSlotId 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.
@@ -130,7 +153,7 @@ public class UiccSlotUtil {
* 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,
public static synchronized void switchToEuiccSlot(Context context, int physicalSlotId, int port,
SubscriptionInfo removedSubInfo) throws UiccSlotsException {
if (ThreadUtils.isMainThread()) {
throw new IllegalThreadStateException(
@@ -138,47 +161,51 @@ public class UiccSlotUtil {
}
TelephonyManager telMgr = context.getSystemService(TelephonyManager.class);
Collection<UiccSlotMapping> uiccSlotMappings = telMgr.getSimSlotMapping();
Log.i(TAG, "The SimSlotMapping: " + uiccSlotMappings);
Log.d(TAG, "The SimSlotMapping: " + uiccSlotMappings);
if (isTargetSlotActive(uiccSlotMappings, slotId, port)) {
Log.i(TAG, "The slot is active, then the sim can enable directly.");
if (isTargetSlotActive(uiccSlotMappings, physicalSlotId, port)) {
Log.d(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());
}
SubscriptionManager subscriptionManager = context.getSystemService(
SubscriptionManager.class);
int excludedLogicalSlotIndex = getExcludedLogicalSlotIndex(uiccSlotMappings,
SubscriptionUtil.getActiveSubscriptions(subscriptionManager), removedSubInfo,
telMgr.isMultiSimEnabled());
performSwitchToSlot(telMgr,
prepareUiccSlotMappings(uiccSlotMappings, /*slot is not psim*/ false,
physicalSlotId, port, excludedLogicalSlotIndex),
context);
}
Log.i(TAG, "The SimSlotMapping: " + newUiccSlotMappings);
performSwitchToSlot(telMgr, newUiccSlotMappings, context);
/**
* @param context the application context.
* @return the esim slot. If the value is -1, there is not the esim.
*/
public static int getEsimSlotId(Context context) {
TelephonyManager telMgr = context.getSystemService(TelephonyManager.class);
ImmutableList<UiccSlotInfo> slotInfos = UiccSlotUtil.getSlotInfos(telMgr);
int firstEsimSlot = IntStream.range(0, slotInfos.size())
.filter(
index -> {
UiccSlotInfo slotInfo = slotInfos.get(index);
if (slotInfo == null) {
return false;
}
return !slotInfo.isRemovable();
})
.findFirst().orElse(-1);
Log.i(TAG, "firstEsimSlot: " + firstEsimSlot);
return firstEsimSlot;
}
private static boolean isTargetSlotActive(Collection<UiccSlotMapping> uiccSlotMappings,
int slotId, int port) {
int physicalSlotId, int port) {
return uiccSlotMappings.stream()
.anyMatch(
uiccSlotMapping -> uiccSlotMapping.getPhysicalSlotIndex() == slotId
uiccSlotMapping -> uiccSlotMapping.getPhysicalSlotIndex() == physicalSlotId
&& uiccSlotMapping.getPortIndex() == port);
}
@@ -239,49 +266,112 @@ public class UiccSlotUtil {
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.
// Device | |Slot |
// Working| |Mapping|
// State |Type |Mode |Friendly name
//--------------------------------------------------------------------------
// Single |SIM pSIM [RIL 0] |1 |pSIM active
// Single |SIM MEP Port #0 [RIL0] |2 |eSIM Port0 active
// Single |SIM MEP Port #1 [RIL0] |2.1 |eSIM Port1 active
// DSDS |pSIM [RIL 0] + MEP Port #0 [RIL 1] |3 |pSIM+Port0
// DSDS |pSIM [RIL 0] + MEP Port #1 [RIL 1] |3.1 |pSIM+Port1
// DSDS |MEP Port #0 [RIL 0] + MEP Port #1 [RIL1]|3.2 |Dual-Ports-A
// DSDS |MEP Port #1 [RIL 0] + MEP Port #0 [RIL1]|4 |Dual-Ports-B
//
// The rules are:
// 1. pSIM's logical slots always is [RIL 0].
// 2. assign the new active port to the same stack that will be de-activated
// For example: mode#3->mode#4
@VisibleForTesting
static Collection<UiccSlotMapping> prepareUiccSlotMappings(
Collection<UiccSlotMapping> uiccSlotMappings, boolean isPsim, int physicalSlotId,
int port, int removedLogicalSlotId) {
if (removedLogicalSlotId == INVALID_LOGICAL_SLOT_ID) {
Log.d(TAG, "There is no removedLogicalSlotId. Do nothing.");
return uiccSlotMappings;
}
Log.d(TAG,
String.format(
"Create new SimSlotMapping. Remove the UiccSlotMapping of logicalSlot%d"
+ ", and insert PhysicalSlotId%d-Port%d",
removedLogicalSlotId, physicalSlotId, port));
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");
int logicalSlotIndex = 0;
if (isPsim) {
// The target slot is psim. The psim is always the first index at LogicalSlot.
newUiccSlotMappings.add(
new UiccSlotMapping(port, physicalSlotId, logicalSlotIndex++));
}
Collection<UiccSlotMapping> tempUiccSlotMappings =
uiccSlotMappings.stream()
.sorted(Comparator.comparingInt(UiccSlotMapping::getLogicalSlotIndex))
.collect(Collectors.toList());
for (UiccSlotMapping uiccSlotMapping : tempUiccSlotMappings) {
if (uiccSlotMapping.getLogicalSlotIndex() == removedLogicalSlotId) {
if (!isPsim) {
// Replace this uiccSlotMapping
newUiccSlotMappings.add(new UiccSlotMapping(port, physicalSlotId,
uiccSlotMapping.getLogicalSlotIndex()));
}
continue;
}
// If the psim is inserted, then change the logicalSlotIndex for another
// uiccSlotMappings.
newUiccSlotMappings.add(isPsim
? new UiccSlotMapping(uiccSlotMapping.getPortIndex(),
uiccSlotMapping.getPhysicalSlotIndex(), logicalSlotIndex++)
: uiccSlotMapping);
}
Log.i(TAG, "The SimSlotMapping: " + newUiccSlotMappings);
Log.d(TAG, "The new SimSlotMapping: " + newUiccSlotMappings);
return newUiccSlotMappings;
}
/**
* To get the excluded logical slot index from uiccSlotMapping list. If the sim which is
* enabled by user does not have the corresponding slot, then it needs to do the
* SimSlotMapping changed. This method can find the logical slot index of the corresponding slot
* before the Frameworks do the SimSlotMapping changed.
*
* @param uiccSlotMappings The uiccSlotMapping list from the Telephony Frameworks.
* @param activeSubInfos The active subscriptionInfo list.
* @param removedSubInfo The removed sim card which is selected by the user. If the user
* don't select removed sim , then the value is null.
* @param isMultiSimEnabled whether the device is in the DSDS mode or not.
* @return The logical slot index of removed slot. If it can't find the removed slot, it
* returns {@link #INVALID_LOGICAL_SLOT_ID}.
*/
@VisibleForTesting
static int getExcludedLogicalSlotIndex(Collection<UiccSlotMapping> uiccSlotMappings,
Collection<SubscriptionInfo> activeSubInfos, SubscriptionInfo removedSubInfo,
boolean isMultiSimEnabled) {
if (!isMultiSimEnabled) {
Log.i(TAG, "In the ss mode.");
return 0;
}
if (removedSubInfo != null) {
// Use removedSubInfo's logicalSlotIndex
Log.i(TAG, "The removedSubInfo is not null");
return removedSubInfo.getSimSlotIndex();
}
// If it needs to do simSlotMapping when user enables sim and there is an empty slot which
// there is no enabled sim in this slot, then the empty slot can be removed.
Log.i(TAG, "The removedSubInfo is null");
return uiccSlotMappings.stream()
.filter(uiccSlotMapping -> {
// find the empty slots.
for (SubscriptionInfo subInfo : activeSubInfos) {
if (subInfo.getSimSlotIndex() == uiccSlotMapping.getLogicalSlotIndex()) {
return false;
}
}
return true;
})
.sorted(Comparator.comparingInt(UiccSlotMapping::getLogicalSlotIndex))
.mapToInt(uiccSlotMapping -> uiccSlotMapping.getLogicalSlotIndex())
.findFirst()
.orElse(INVALID_LOGICAL_SLOT_ID);
}
}

View File

@@ -25,6 +25,7 @@ import android.view.LayoutInflater;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
@@ -115,13 +116,12 @@ public class ConfirmDialogFragment extends BaseDialogFragment
AlertDialog.Builder builder = new AlertDialog.Builder(getContext())
.setPositiveButton(posBtnString, this)
.setNegativeButton(negBtnString, this);
View content = LayoutInflater.from(getContext()).inflate(
R.layout.sim_confirm_dialog_multiple_enabled_profiles_supported, null);
if (list != null && !list.isEmpty()) {
if (list != null && !list.isEmpty() && content != null) {
Log.i(TAG, "list =" + list.toString());
View content = LayoutInflater.from(getContext()).inflate(
R.layout.sim_confirm_dialog_multiple_enabled_profiles_supported, null);
if (!TextUtils.isEmpty(title)) {
View titleView = LayoutInflater.from(getContext()).inflate(
R.layout.sim_confirm_dialog_title_multiple_enabled_profiles_supported,
@@ -133,6 +133,7 @@ public class ConfirmDialogFragment extends BaseDialogFragment
TextView dialogMessage = content.findViewById(R.id.msg);
if (!TextUtils.isEmpty(message) && dialogMessage != null) {
dialogMessage.setText(message);
dialogMessage.setVisibility(View.VISIBLE);
}
final ArrayAdapter<String> arrayAdapterItems = new ArrayAdapter<String>(
@@ -140,8 +141,8 @@ public class ConfirmDialogFragment extends BaseDialogFragment
R.layout.sim_confirm_dialog_item_multiple_enabled_profiles_supported, list);
final ListView lvItems = content.findViewById(R.id.carrier_list);
if (lvItems != null) {
lvItems.setVisibility(View.VISIBLE);
lvItems.setAdapter(arrayAdapterItems);
lvItems.setChoiceMode(ListView.CHOICE_MODE_NONE);
lvItems.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position,
@@ -158,6 +159,10 @@ public class ConfirmDialogFragment extends BaseDialogFragment
}
});
}
final LinearLayout infoOutline = content.findViewById(R.id.info_outline_layout);
if (infoOutline != null) {
infoOutline.setVisibility(View.VISIBLE);
}
builder.setView(content);
} else {
if (!TextUtils.isEmpty(title)) {

View File

@@ -159,11 +159,12 @@ public class ChooseSimActivity extends Activity
mSelectedItemIndex = subItem.getId();
if (mSelectedItemIndex == INDEX_PSIM) {
Log.i(TAG, "Ready to switch to pSIM slot.");
mSwitchToRemovableSlotSidecar.run(UiccSlotUtil.INVALID_PHYSICAL_SLOT_ID);
mSwitchToRemovableSlotSidecar.run(UiccSlotUtil.INVALID_PHYSICAL_SLOT_ID, null);
} else {
Log.i(TAG, "Ready to switch to eSIM subscription with index: " + mSelectedItemIndex);
mSwitchToEuiccSubscriptionSidecar.run(
mEmbeddedSubscriptions.get(mSelectedItemIndex).getSubscriptionId());
mEmbeddedSubscriptions.get(mSelectedItemIndex).getSubscriptionId(),
UiccSlotUtil.INVALID_PORT_ID, null);
}
}

View File

@@ -84,10 +84,11 @@ public class PreferredSimDialogFragment extends SimDialogFragment implements
private void updateDialog(AlertDialog dialog) {
Log.d(TAG, "Dialog updated, dismiss status: " + mWasDismissed);
final SubscriptionInfo info = getPreferredSubscription();
if (mWasDismissed) {
return;
}
final SubscriptionInfo info = getPreferredSubscription();
if (info == null) {
dismiss();
return;

View File

@@ -26,8 +26,6 @@ import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
import androidx.annotation.NonNull;
@@ -88,10 +86,11 @@ public class SelectSpecificDataSimDialogFragment extends SimDialogFragment imple
if (subInfos == null || dds == null) {
return null;
}
return subInfos.stream().filter(subinfo -> subinfo != dds).findFirst().orElse(null);
return subInfos.stream().filter(subinfo -> subinfo.getSubscriptionId()
!= dds.getSubscriptionId()).findFirst().orElse(null);
}
private SubscriptionInfo getDefaultDataSubId() {
private SubscriptionInfo getDefaultDataSubInfo() {
return getSubscriptionManager().getDefaultDataSubscriptionInfo();
}
@@ -101,20 +100,22 @@ public class SelectSpecificDataSimDialogFragment extends SimDialogFragment imple
return;
}
SubscriptionInfo activeSubInfo = getDefaultDataSubId();
SubscriptionInfo newSubInfo = getNonDefaultDataSubscriptionInfo(activeSubInfo);
SubscriptionInfo currentDataSubInfo = getDefaultDataSubInfo();
SubscriptionInfo newSubInfo = getNonDefaultDataSubscriptionInfo(currentDataSubInfo);
if (newSubInfo == null || activeSubInfo == null) {
if (newSubInfo == null || currentDataSubInfo == null) {
Log.d(TAG, "one of target SubscriptionInfos is null");
dismiss();
return;
}
Log.d(TAG, "newSubId: " + newSubInfo.getSubscriptionId()
+ "currentDataSubID: " + currentDataSubInfo.getSubscriptionId());
setTargetSubscriptionInfo(newSubInfo);
CharSequence newDataCarrierName = SubscriptionUtil.getUniqueSubscriptionDisplayName(
newSubInfo, getContext());
CharSequence currentDataCarrierName = SubscriptionUtil.getUniqueSubscriptionDisplayName(
activeSubInfo, getContext());
currentDataSubInfo, getContext());
String positive = getContext().getString(
R.string.select_specific_sim_for_data_button, newDataCarrierName);
@@ -123,18 +124,10 @@ public class SelectSpecificDataSimDialogFragment extends SimDialogFragment imple
View content = LayoutInflater.from(getContext()).inflate(
R.layout.sim_confirm_dialog_multiple_enabled_profiles_supported, null);
TextView dialogMessage = content.findViewById(R.id.msg);
TextView dialogMessage = content != null ? content.findViewById(R.id.msg) : null;
if (!TextUtils.isEmpty(message) && dialogMessage != null) {
dialogMessage.setText(message);
}
final ListView lvItems = content.findViewById(R.id.carrier_list);
if (lvItems != null) {
lvItems.setVisibility(View.GONE);
}
final LinearLayout infoOutline = content.findViewById(R.id.info_outline_layout);
if (infoOutline != null) {
infoOutline.setVisibility(View.GONE);
dialogMessage.setVisibility(View.VISIBLE);
}
dialog.setView(content);

View File

@@ -83,6 +83,12 @@ public abstract class SimDialogFragment extends InstrumentedDialogFragment imple
}
}
@Override
public void dismiss() {
mChangeListener.stop();
super.dismiss();
}
public abstract void updateDialog();
@Override

View File

@@ -19,7 +19,6 @@ package com.android.settings.sim;
import android.app.Dialog;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
@@ -28,6 +27,7 @@ import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.TextView;
@@ -47,12 +47,10 @@ import java.util.List;
* Shows a dialog consisting of a list of SIMs (aka subscriptions), possibly including an additional
* entry indicating "ask me every time".
*/
public class SimListDialogFragment extends SimDialogFragment implements
DialogInterface.OnClickListener {
public class SimListDialogFragment extends SimDialogFragment {
private static final String TAG = "SimListDialogFragment";
protected static final String KEY_INCLUDE_ASK_EVERY_TIME = "include_ask_every_time";
protected static final String KEY_SHOW_CANCEL_ITEM = "show_cancel_item";
private static final int LIST_VIEW_DIVIDER_LINE_WEIGHT = 2;
protected SelectSubscriptionAdapter mAdapter;
@VisibleForTesting
@@ -79,21 +77,36 @@ public class SimListDialogFragment extends SimDialogFragment implements
TextView titleTextView = titleView.findViewById(R.id.title);
titleTextView.setText(getContext().getString(getTitleResId()));
builder.setCustomTitle(titleTextView);
mAdapter = new SelectSubscriptionAdapter(builder.getContext(), mSubscriptions);
setAdapter(builder);
final AlertDialog dialog = builder.create();
ListView listView = dialog.getListView();
if (listView != null) {
listView.setDividerHeight(LIST_VIEW_DIVIDER_LINE_WEIGHT);
View content = LayoutInflater.from(getContext()).inflate(
R.layout.sim_confirm_dialog_multiple_enabled_profiles_supported, null);
final ListView lvItems = content != null ? content.findViewById(R.id.carrier_list) : null;
if (lvItems != null) {
setAdapter(lvItems);
lvItems.setVisibility(View.VISIBLE);
lvItems.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
onClick(position);
}
});
}
dialog.setView(content);
updateDialog();
return dialog;
}
@Override
public void onClick(DialogInterface dialog, int selectionIndex) {
/**
* If the user click the item at the list, then it sends the callback.
* @param selectionIndex the index of item in the list.
*/
public void onClick(int selectionIndex) {
if (selectionIndex >= 0 && selectionIndex < mSubscriptions.size()) {
int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
final SubscriptionInfo subscription = mSubscriptions.get(selectionIndex);
@@ -103,6 +116,7 @@ public class SimListDialogFragment extends SimDialogFragment implements
final SimDialogActivity activity = (SimDialogActivity) getActivity();
activity.onSubscriptionSelected(getDialogType(), subId);
}
dismiss();
}
protected List<SubscriptionInfo> getCurrentSubscriptions() {
@@ -114,12 +128,13 @@ public class SimListDialogFragment extends SimDialogFragment implements
@Override
public void updateDialog() {
Log.d(TAG, "Dialog updated, dismiss status: " + mWasDismissed);
if (mWasDismissed) {
return;
}
List<SubscriptionInfo> currentSubscriptions = getCurrentSubscriptions();
if (currentSubscriptions == null) {
if (!mWasDismissed) {
dismiss();
}
dismiss();
return;
}
boolean includeAskEveryTime = getArguments().getBoolean(KEY_INCLUDE_ASK_EVERY_TIME);
@@ -143,14 +158,15 @@ public class SimListDialogFragment extends SimDialogFragment implements
if (currentSubscriptions.equals(mSubscriptions)) {
return;
}
mSubscriptions.clear();
mSubscriptions.addAll(currentSubscriptions);
mAdapter.notifyDataSetChanged();
}
@VisibleForTesting
void setAdapter(AlertDialog.Builder builder) {
builder.setAdapter(mAdapter, this);
void setAdapter(ListView lvItems) {
lvItems.setAdapter(mAdapter);
}
@Override
@@ -200,6 +216,16 @@ public class SimListDialogFragment extends SimDialogFragment implements
final TextView title = convertView.findViewById(R.id.title);
final TextView summary = convertView.findViewById(R.id.summary);
ViewGroup.MarginLayoutParams lp =
(ViewGroup.MarginLayoutParams) parent.getLayoutParams();
if (lp != null) {
lp.setMargins(0, mContext.getResources().getDimensionPixelSize(
R.dimen.sims_select_margin_top), 0,
mContext.getResources().getDimensionPixelSize(
R.dimen.sims_select_margin_bottom));
convertView.setLayoutParams(lp);
}
if (sub == null) {
if (position == 0) {
title.setText(R.string.sim_calls_ask_first_prefs_title);

View File

@@ -23,6 +23,7 @@ import android.util.Log;
import com.android.settings.R;
import com.android.settings.SidecarFragment;
import com.android.settings.network.SwitchToEuiccSubscriptionSidecar;
import com.android.settings.network.UiccSlotUtil;
import com.android.settings.network.telephony.AlertDialogFragment;
import com.android.settings.network.telephony.ConfirmDialogFragment;
import com.android.settings.network.telephony.SubscriptionActionDialogActivity;
@@ -110,7 +111,8 @@ public class SwitchToEsimConfirmDialogActivity extends SubscriptionActionDialogA
return;
}
Log.i(TAG, "User confirmed to switch to embedded slot.");
mSwitchToEuiccSubscriptionSidecar.run(mSubToEnabled.getSubscriptionId());
mSwitchToEuiccSubscriptionSidecar.run(mSubToEnabled.getSubscriptionId(),
UiccSlotUtil.INVALID_PORT_ID, null);
showProgressDialog(
getString(
R.string.sim_action_switch_sub_dialog_progress,

View File

@@ -26,12 +26,14 @@ import android.provider.Settings;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.telephony.UiccCardInfo;
import android.telephony.UiccSlotInfo;
import android.util.Log;
import com.android.settings.network.SubscriptionUtil;
import com.android.settings.network.UiccSlotUtil;
import com.android.settings.network.UiccSlotsException;
import com.android.settings.network.telephony.ToggleSubscriptionDialogActivity;
import com.android.settings.sim.ChooseSimActivity;
import com.android.settings.sim.DsdsDialogActivity;
import com.android.settings.sim.SimActivationNotifier;
@@ -40,6 +42,7 @@ import com.android.settings.sim.SwitchToEsimConfirmDialogActivity;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
@@ -83,8 +86,8 @@ public class SimSlotChangeHandler {
throw new IllegalStateException("Cannot be called from main thread.");
}
if (mTelMgr.getActiveModemCount() > 1) {
Log.i(TAG, "The device is already in DSDS mode. Do nothing.");
if (mTelMgr.getActiveModemCount() > 1 && !isMultipleEnabledProfilesSupported()) {
Log.i(TAG, "The device is already in DSDS mode and no MEP. Do nothing.");
return;
}
@@ -96,17 +99,30 @@ public class SimSlotChangeHandler {
int lastRemovableSlotState = getLastRemovableSimSlotState(mContext);
int currentRemovableSlotState = removableSlotInfo.getCardStateInfo();
boolean isRemovableSimInserted =
lastRemovableSlotState == UiccSlotInfo.CARD_STATE_INFO_ABSENT
&& currentRemovableSlotState == UiccSlotInfo.CARD_STATE_INFO_PRESENT;
boolean isRemovableSimRemoved =
lastRemovableSlotState == UiccSlotInfo.CARD_STATE_INFO_PRESENT
&& currentRemovableSlotState == UiccSlotInfo.CARD_STATE_INFO_ABSENT;
// Sets the current removable slot state.
setRemovableSimSlotState(mContext, currentRemovableSlotState);
if (lastRemovableSlotState == UiccSlotInfo.CARD_STATE_INFO_ABSENT
&& currentRemovableSlotState == UiccSlotInfo.CARD_STATE_INFO_PRESENT) {
if (mTelMgr.getActiveModemCount() > 1 && isMultipleEnabledProfilesSupported()) {
if(!isRemovableSimInserted) {
Log.i(TAG, "Removable Sim is not inserted in DSDS mode and MEP. Do nothing.");
return;
}
handleRemovableSimInsertUnderDsdsMep(removableSlotInfo);
return;
}
if (isRemovableSimInserted) {
handleSimInsert(removableSlotInfo);
return;
}
if (lastRemovableSlotState == UiccSlotInfo.CARD_STATE_INFO_PRESENT
&& currentRemovableSlotState == UiccSlotInfo.CARD_STATE_INFO_ABSENT) {
if (isRemovableSimRemoved) {
handleSimRemove(removableSlotInfo);
return;
}
@@ -210,10 +226,11 @@ public class SimSlotChangeHandler {
}
List<SubscriptionInfo> groupedEmbeddedSubscriptions = getGroupedEmbeddedSubscriptions();
if (groupedEmbeddedSubscriptions.size() == 0 || !removableSlotInfo.getPorts().stream()
.findFirst().get().isActive()) {
Log.i(TAG, "eSIM slot is active or no subscriptions exist. Do nothing.");
Log.i(TAG, "eSIM slot is active or no subscriptions exist. Do nothing."
+ " The removableSlotInfo: " + removableSlotInfo
+ ", groupedEmbeddedSubscriptions: " + groupedEmbeddedSubscriptions);
return;
}
@@ -231,6 +248,24 @@ public class SimSlotChangeHandler {
startChooseSimActivity(false);
}
private void handleRemovableSimInsertUnderDsdsMep(UiccSlotInfo removableSlotInfo) {
Log.i(TAG, "Handle Removable SIM inserted under DSDS+Mep.");
if (removableSlotInfo.getPorts().stream().findFirst().get().isActive()) {
Log.i(TAG, "The removable slot is already active. Do nothing. removableSlotInfo: "
+ removableSlotInfo);
return;
}
List<SubscriptionInfo> subscriptionInfos = getAvailableRemovableSubscription();
if (subscriptionInfos == null || subscriptionInfos.get(0) == null) {
Log.e(TAG, "Unable to find the removable subscriptionInfo. Do nothing.");
return;
}
Log.d(TAG, "getAvailableRemovableSubscription:" + subscriptionInfos);
startSimConfirmDialogActivity(subscriptionInfos.get(0).getSubscriptionId());
}
private int getLastRemovableSimSlotState(Context context) {
final SharedPreferences prefs = context.getSharedPreferences(EUICC_PREFS, MODE_PRIVATE);
return prefs.getInt(KEY_REMOVABLE_SLOT_STATE, UiccSlotInfo.CARD_STATE_INFO_ABSENT);
@@ -260,7 +295,6 @@ public class SimSlotChangeHandler {
}
for (UiccSlotInfo slotInfo : slotInfos) {
if (slotInfo != null && slotInfo.isRemovable()) {
return slotInfo;
}
}
@@ -296,6 +330,16 @@ public class SimSlotChangeHandler {
.collect(Collectors.toList()));
}
protected List<SubscriptionInfo> getAvailableRemovableSubscription() {
List<SubscriptionInfo> subList = new ArrayList<>();
for (SubscriptionInfo info : SubscriptionUtil.getAvailableSubscriptions(mContext)) {
if (!info.isEmbedded()) {
subList.add(info);
}
}
return subList;
}
private void startChooseSimActivity(boolean psimInserted) {
Intent intent = ChooseSimActivity.getIntent(mContext);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
@@ -316,5 +360,26 @@ public class SimSlotChangeHandler {
mContext.startActivity(intent);
}
private void startSimConfirmDialogActivity(int subId) {
if (!SubscriptionManager.isUsableSubscriptionId(subId)) {
Log.i(TAG, "Unable to enable subscription due to invalid subscription ID.");
return;
}
Log.d(TAG, "Start ToggleSubscriptionDialogActivity with " + subId + " under DSDS+Mep.");
Intent intent = ToggleSubscriptionDialogActivity.getIntent(mContext, subId, true);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(intent);
}
private boolean isMultipleEnabledProfilesSupported() {
List<UiccCardInfo> cardInfos = mTelMgr.getUiccCardsInfo();
if (cardInfos == null) {
Log.d(TAG, "UICC cards info list is empty.");
return false;
}
return cardInfos.stream().anyMatch(
cardInfo -> cardInfo.isMultipleEnabledProfilesSupported());
}
private SimSlotChangeHandler() {}
}