Loading src/java/com/android/internal/telephony/euicc/EuiccController.java +216 −13 Original line number Diff line number Diff line Loading @@ -57,6 +57,7 @@ import com.android.internal.telephony.SubscriptionController; import com.android.internal.telephony.euicc.EuiccConnector.OtaStatusChangedCallback; import com.android.internal.telephony.uicc.UiccController; import com.android.internal.telephony.uicc.UiccPort; import com.android.internal.telephony.uicc.UiccSlot; import com.android.internal.telephony.util.ArrayUtils; import java.io.FileDescriptor; Loading Loading @@ -180,12 +181,11 @@ public class EuiccController extends IEuiccController.Stub { PendingIntent callbackIntent = resolutionIntent.getParcelableExtra( EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_RESOLUTION_CALLBACK_INTENT); int portIndex = resolutionIntent.getIntExtra( EuiccService.EXTRA_RESOLUTION_PORT_INDEX, 0); resolutionExtras.putInt(EuiccService.EXTRA_RESOLUTION_PORT_INDEX, portIndex); boolean usePortIndex = resolutionIntent.getBooleanExtra( EuiccService.EXTRA_RESOLUTION_USE_PORT_INDEX, false); resolutionExtras.putBoolean(EuiccService.EXTRA_RESOLUTION_USE_PORT_INDEX, usePortIndex); Log.i(TAG, " continueOperation portIndex: " + resolutionExtras.getInt( EuiccService.EXTRA_RESOLUTION_PORT_INDEX) + " usePortIndex: " + usePortIndex); op.continueOperation(cardId, resolutionExtras, callbackIntent); } finally { Binder.restoreCallingIdentity(token); Loading @@ -211,7 +211,8 @@ public class EuiccController extends IEuiccController.Stub { long token = Binder.clearCallingIdentity(); try { if (!callerCanReadPhoneStatePrivileged && !canManageSubscriptionOnTargetSim(cardId, callingPackage)) { && !canManageSubscriptionOnTargetSim(cardId, callingPackage, false, TelephonyManager.INVALID_PORT_INDEX)) { throw new SecurityException( "Must have carrier privileges on subscription to read EID for cardId=" + cardId); Loading Loading @@ -564,7 +565,8 @@ public class EuiccController extends IEuiccController.Stub { // Otherwise, the user must provide consent. If it's a single-active SIM device, // determine whether the caller can manage the current profile; if so, we can perform // the download silently; if not, the user must provide consent. if (canManageSubscriptionOnTargetSim(cardId, callingPackage)) { if (canManageSubscriptionOnTargetSim(cardId, callingPackage, false, TelephonyManager.INVALID_PORT_INDEX)) { mConnector.getDownloadableSubscriptionMetadata(cardId, subscription, forceDeactivateSim, new DownloadSubscriptionGetMetadataCommandCallback(token, subscription, Loading Loading @@ -1006,9 +1008,11 @@ public class EuiccController extends IEuiccController.Stub { } final String iccid; boolean passConsent = false; boolean isConsentNeededToResolvePortIndex = false; if (subscriptionId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { if (callerCanWriteEmbeddedSubscriptions || canManageActiveSubscriptionOnTargetSim(cardId, callingPackage)) { || canManageActiveSubscriptionOnTargetSim(cardId, callingPackage, usePortIndex, portIndex)) { passConsent = true; } else { Log.e(TAG, "Not permitted to switch to empty subscription"); Loading @@ -1032,14 +1036,34 @@ public class EuiccController extends IEuiccController.Stub { return; } if (canManageSubscriptionOnTargetSim(cardId, callingPackage)) { if (canManageSubscriptionOnTargetSim(cardId, callingPackage, usePortIndex, portIndex)) { passConsent = true; } } iccid = sub.getIccId(); if (usePortIndex) { boolean hasValidPortIndex = isTargetPortIndexValid(cardId, portIndex); if (!hasValidPortIndex) { // Return permanent error. Log.e(TAG, "Not permitted to switch to invalid portIndex"); Intent extrasIntent = new Intent(); extrasIntent.putExtra(EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_ERROR_CODE, EuiccManager.ERROR_INVALID_PORT); sendResult(callbackIntent, ERROR, extrasIntent /* extrasIntent */); return; } if (!passConsent) { } else { // Resolve the portIndex internally if apps are calling switchToSubscription // API without portIndex. portIndex = getResolvedPortIndexForSubscriptionSwitch(cardId); isConsentNeededToResolvePortIndex = (portIndex == TelephonyManager.INVALID_PORT_INDEX); usePortIndex = true; Log.d(TAG, " Resolved portIndex: " + portIndex); } } if (!passConsent || isConsentNeededToResolvePortIndex) { // Switch needs consent. Intent extrasIntent = new Intent(); addResolutionIntent(extrasIntent, Loading @@ -1061,6 +1085,171 @@ public class EuiccController extends IEuiccController.Stub { } } /** * Returns the resolved portIndex or {@link TelephonyManager#INVALID_PORT_INDEX} if no port * is available without user consent. */ private int getResolvedPortIndexForSubscriptionSwitch(int cardId) { int slotIndex = getSlotIndexFromCardId(cardId); // Euicc Slot UiccSlot slot = UiccController.getInstance().getUiccSlot(slotIndex); if (!slot.isMultipleEnabledProfileSupported()) { Log.d(TAG, "Multiple enabled profiles is not supported, return default port index"); return TelephonyManager.DEFAULT_PORT_INDEX; } boolean isPsimHasActiveSub = isRemovalNonEuiccSlotHasActiveSubscription(); if (mTelephonyManager.getActiveModemCount() == 1) { // SS Mode if (isPsimHasActiveSub) { // In case of SS Mode and pSim has active subscription, return default port index // for two reasons. // 1. If psim and esim share the same carrier privilege, then users wouldn't need // to consent, the switch should be seamless. // 2. If psim and esim doesn't share the same carrier privilege, then permission // check dialog will be shown anyway. return TelephonyManager.DEFAULT_PORT_INDEX; } // If esim port is active, return the active portIndex irrespective of whether port is // empty or has active subscription. for (int portIndex : slot.getPortList()) { if (slot.isPortActive(portIndex)) { return portIndex; } } } else { // DSDS Mode for (int portIndex : slot.getPortList()) { if (slot.isPortActive(portIndex) && mSubscriptionManager.getActiveSubscriptionInfoForSimSlotIndex( slot.getPhoneIdFromPortIndex(portIndex)) == null) { // If the port is active and empty, return the portIndex. return portIndex; } } // Check whether the pSim is active and empty boolean isPsimEmpty = getRemovableNonEuiccSlot() != null && getRemovableNonEuiccSlot().isActive() && !isPsimHasActiveSub; if (isPsimEmpty) { // This logic will execute only if below two conditions are true. // 1. pSim is active and empty // 2. eSim has active subscription // Return the next available inactive eSim portIndex. return getNextAvailableInActivePortIndex(slot); } } return TelephonyManager.INVALID_PORT_INDEX; } /** * Returns true if the target port index is valid. * 1. Port index is valid if it is non-negative and less than the total port count. * 2. In SS Mode, port index is invalid if the embedded slot already has an active empty port * with different port index than the target port index. * 3. In DSDS mode, port index is invalid if the pSim slot is active and the embedded slot * already has an active empty port with different port index than the target port index. */ private boolean isTargetPortIndexValid(int cardId, int targetPortIndex) { if (targetPortIndex < 0) { Log.e(TAG, "Invalid portIndex: " + targetPortIndex); return false; } int slotIndex = getSlotIndexFromCardId(cardId); UiccSlot slot = UiccController.getInstance().getUiccSlot(slotIndex); if (slot == null || slot.getPortList().length == 0 || targetPortIndex >= slot.getPortList().length) { Log.e(TAG, "Invalid portIndex"); return false; } if (mTelephonyManager.getActiveModemCount() == 1) { // SS Mode for (int portIndex : slot.getPortList()) { if (slot.isPortActive(portIndex) && portIndex != targetPortIndex) { // if there is an active esim port, should not try to enable the // profile on other inactive port. Log.e(TAG, "In SS Mode, slot already has active port on portIndex " + portIndex + " , reject the switch request to portIndex " + targetPortIndex); return false; } } } else if (mTelephonyManager.getActiveModemCount() > 1) { // DSDS Mode // If physical slot has active subscription and eSim has active port (without active // subscription), should not try to enable the profile on other inactive port. boolean isPsimActive = isRemovalNonEuiccSlotHasActiveSubscription(); if (isPsimActive) { for (int portIndex : slot.getPortList()) { if (slot.isPortActive(portIndex) && mSubscriptionManager .getActiveSubscriptionInfoForSimSlotIndex( slot.getPhoneIdFromPortIndex(portIndex)) == null && portIndex != targetPortIndex) { Log.e(TAG, "In DSDS Mode, pSim has active subscription, eSim has empty" + " active port on portIndex " + portIndex + " , reject the switch request to portIndex " + targetPortIndex); return false; } } } } return true; } private int getNextAvailableInActivePortIndex(UiccSlot slot) { if (slot != null) { for (int portIndex : slot.getPortList()) { if (!slot.isPortActive(portIndex)) { return portIndex; } } } return TelephonyManager.INVALID_PORT_INDEX; } /** * Gets the slot index from the card ID. */ private int getSlotIndexFromCardId(int cardId) { List<UiccCardInfo> infos = mTelephonyManager.getUiccCardsInfo(); if (infos == null || infos.size() == 0) { return SubscriptionManager.INVALID_SIM_SLOT_INDEX; } for (UiccCardInfo info : infos) { if (info.getCardId() == cardId) { return info.getPhysicalSlotIndex(); } } return SubscriptionManager.INVALID_SIM_SLOT_INDEX; } private boolean isRemovalNonEuiccSlotHasActiveSubscription() { UiccSlot uiccSlot = getRemovableNonEuiccSlot(); if (uiccSlot != null) { for (int portIndex : uiccSlot.getPortList()) { if (uiccSlot.isPortActive(portIndex) && mSubscriptionManager.getActiveSubscriptionInfoForSimSlotIndex( uiccSlot.getPhoneIdFromPortIndex(portIndex)) != null) { return true; } } } return false; } private UiccSlot getRemovableNonEuiccSlot() { UiccSlot[] uiccSlots = UiccController.getInstance().getUiccSlots(); if (uiccSlots != null) { for (int i = 0; i < uiccSlots.length; i++) { if (uiccSlots[i] != null && uiccSlots[i].isRemovable() && !uiccSlots[i].isEuicc()) { return uiccSlots[i]; } } } return null; } void switchToSubscriptionPrivileged(int cardId, int portIndex, final long callingToken, int subscriptionId, boolean forceDeactivateSim, final String callingPackage, final PendingIntent callbackIntent, boolean usePortIndex) { Loading Loading @@ -1515,7 +1704,10 @@ public class EuiccController extends IEuiccController.Stub { // Checks whether the caller can manage the active embedded subscription on the SIM with the // given cardId. private boolean canManageActiveSubscriptionOnTargetSim(int cardId, String callingPackage) { // From Android T, if usePortIndex is true then should check if the calling app has carrier // privilege over the subscription on the target port index. private boolean canManageActiveSubscriptionOnTargetSim(int cardId, String callingPackage, boolean usePortIndex, int targetPortIndex) { List<SubscriptionInfo> subInfoList = mSubscriptionManager .getActiveSubscriptionInfoList(/* userVisibleOnly */false); if (subInfoList == null || subInfoList.size() == 0) { Loading @@ -1529,6 +1721,7 @@ public class EuiccController extends IEuiccController.Stub { // subscription on any eSIM. That's the best we can do here. if ((cardId == TelephonyManager.UNSUPPORTED_CARD_ID || subInfo.getCardId() == cardId) && subInfo.isEmbedded() && (!usePortIndex || subInfo.getPortIndex() == targetPortIndex) && mSubscriptionManager.canManageSubscription(subInfo, callingPackage)) { return true; } Loading @@ -1543,7 +1736,10 @@ public class EuiccController extends IEuiccController.Stub { // other SIM. The target SIM should be an eUICC. // For a single-active subscription phone, checks whether the caller can manage any active // embedded subscription. private boolean canManageSubscriptionOnTargetSim(int cardId, String callingPackage) { // From Android T, If embedded slot supports Multiple Enabled Profiles then should check if // the calling app has carrier privilege over the subscription on the target port index. private boolean canManageSubscriptionOnTargetSim(int cardId, String callingPackage, boolean usePortIndex, int targetPortIndex) { List<SubscriptionInfo> subInfoList = mSubscriptionManager .getActiveSubscriptionInfoList(false /* userVisibleonly */); // No active subscription on any SIM. Loading @@ -1561,9 +1757,11 @@ public class EuiccController extends IEuiccController.Stub { return false; } boolean isEuicc = false; boolean isMultipleEnabledProfilesSupported = false; for (UiccCardInfo info : cardInfos) { if (info != null && info.getCardId() == cardId && info.isEuicc()) { isEuicc = true; isMultipleEnabledProfilesSupported = info.isMultipleEnabledProfilesSupported(); break; } } Loading @@ -1576,8 +1774,13 @@ public class EuiccController extends IEuiccController.Stub { // false. If the caller can manage the active embedded subscription on the target SIM, // return true directly. for (SubscriptionInfo subInfo : subInfoList) { // subInfo.isEmbedded() can only be true for the target SIM. if (subInfo.isEmbedded() && subInfo.getCardId() == cardId) { // 1. subInfo.isEmbedded() can only be true for the target SIM. // 2. Check whether the caller can manage subscription on the target portIndex // (i.e. subInfo.getPortIndex() == targetPortIndex condition) only in case if // isMultipleEnabledProfilesSupported and usePortIndex both are true. if (subInfo.isEmbedded() && subInfo.getCardId() == cardId && (!isMultipleEnabledProfilesSupported || !usePortIndex || subInfo.getPortIndex() == targetPortIndex)) { return mSubscriptionManager.canManageSubscription(subInfo, callingPackage); } } Loading tests/telephonytests/src/com/android/internal/telephony/euicc/EuiccControllerTest.java +16 −0 Original line number Diff line number Diff line Loading @@ -66,6 +66,7 @@ import androidx.test.runner.AndroidJUnit4; import com.android.internal.telephony.TelephonyTest; import com.android.internal.telephony.euicc.EuiccConnector.GetOtaStatusCommandCallback; import com.android.internal.telephony.euicc.EuiccConnector.OtaStatusChangedCallback; import com.android.internal.telephony.uicc.UiccSlot; import org.junit.After; import org.junit.Before; Loading Loading @@ -121,6 +122,7 @@ public class EuiccControllerTest extends TelephonyTest { private static final int PORT_INDEX = 0; @Mock private EuiccConnector mMockConnector; @Mock private UiccSlot mUiccSlot; private TestEuiccController mController; private int mSavedEuiccProvisionedValue; Loading Loading @@ -786,6 +788,7 @@ public class EuiccControllerTest extends TelephonyTest { public void testSwitchToSubscription_serviceUnavailable() throws Exception { setHasWriteEmbeddedPermission(true); prepareOperationSubscription(false /* hasPrivileges */); setUpUiccSlotData(); callSwitchToSubscription( SUBSCRIPTION_ID, ICC_ID, false /* complete */, 0 /* result */, "whatever" /* callingPackage */); Loading @@ -799,6 +802,7 @@ public class EuiccControllerTest extends TelephonyTest { public void testSwitchToSubscription_error() throws Exception { setHasWriteEmbeddedPermission(true); prepareOperationSubscription(false /* hasPrivileges */); setUpUiccSlotData(); callSwitchToSubscription( SUBSCRIPTION_ID, ICC_ID, true /* complete */, 42 /* result */, "whatever" /* callingPackage */); Loading @@ -810,6 +814,7 @@ public class EuiccControllerTest extends TelephonyTest { public void testSwitchToSubscription_success() throws Exception { setHasWriteEmbeddedPermission(true); prepareOperationSubscription(false /* hasPrivileges */); setUpUiccSlotData(); callSwitchToSubscription( SUBSCRIPTION_ID, ICC_ID, true /* complete */, EuiccService.RESULT_OK, "whatever" /* callingPackage */); Loading Loading @@ -842,6 +847,7 @@ public class EuiccControllerTest extends TelephonyTest { public void testSwitchToSubscription_hasCarrierPrivileges() throws Exception { setHasWriteEmbeddedPermission(false); prepareOperationSubscription(true /* hasPrivileges */); setUpUiccSlotData(); when(mTelephonyManager.getPhoneCount()).thenReturn(1); setHasCarrierPrivilegesOnActiveSubscription(true); callSwitchToSubscription( Loading @@ -853,6 +859,7 @@ public class EuiccControllerTest extends TelephonyTest { public void testSwitchToSubscription_hasCarrierPrivileges_multiSim() throws Exception { setHasWriteEmbeddedPermission(false); prepareOperationSubscription(true /* hasPrivileges */); setUpUiccSlotData(); when(mTelephonyManager.getPhoneCount()).thenReturn(2); setCanManageSubscriptionOnTargetSim(true /* isTargetEuicc */, true /* hasPrivileges */); callSwitchToSubscription( Loading @@ -864,6 +871,7 @@ public class EuiccControllerTest extends TelephonyTest { public void testSwitchToSubscription_hasCarrierPrivileges_needsConsent() throws Exception { setHasWriteEmbeddedPermission(false); prepareOperationSubscription(true /* hasPrivileges */); setUpUiccSlotData(); setHasCarrierPrivilegesOnActiveSubscription(false); when(mTelephonyManager.getPhoneCount()).thenReturn(1); callSwitchToSubscription( Loading @@ -881,6 +889,7 @@ public class EuiccControllerTest extends TelephonyTest { throws Exception { setHasWriteEmbeddedPermission(false); prepareOperationSubscription(true /* hasPrivileges */); setUpUiccSlotData(); when(mTelephonyManager.getPhoneCount()).thenReturn(2); setCanManageSubscriptionOnTargetSim(true /* isTargetEuicc */, false /* hasPrivileges */); callSwitchToSubscription( Loading @@ -898,6 +907,7 @@ public class EuiccControllerTest extends TelephonyTest { throws Exception { setHasWriteEmbeddedPermission(false); prepareOperationSubscription(true /* hasPrivileges */); setUpUiccSlotData(); when(mTelephonyManager.getPhoneCount()).thenReturn(2); setCanManageSubscriptionOnTargetSim(false /* isTargetEuicc */, true /* hasPrivileges */); callSwitchToSubscription( Loading Loading @@ -1129,6 +1139,12 @@ public class EuiccControllerTest extends TelephonyTest { EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_ERROR_CODE, -1), 0x345678); } private void setUpUiccSlotData() { when(mUiccController.getUiccSlot(anyInt())).thenReturn(mUiccSlot); // TODO(b/199559633): Add test cases for isMultipleEnabledProfileSupported true case when(mUiccSlot.isMultipleEnabledProfileSupported()).thenReturn(false); } private void setGetEidPermissions( boolean hasPhoneStatePrivileged, boolean hasCarrierPrivileges) throws Exception { doReturn(hasPhoneStatePrivileged Loading Loading
src/java/com/android/internal/telephony/euicc/EuiccController.java +216 −13 Original line number Diff line number Diff line Loading @@ -57,6 +57,7 @@ import com.android.internal.telephony.SubscriptionController; import com.android.internal.telephony.euicc.EuiccConnector.OtaStatusChangedCallback; import com.android.internal.telephony.uicc.UiccController; import com.android.internal.telephony.uicc.UiccPort; import com.android.internal.telephony.uicc.UiccSlot; import com.android.internal.telephony.util.ArrayUtils; import java.io.FileDescriptor; Loading Loading @@ -180,12 +181,11 @@ public class EuiccController extends IEuiccController.Stub { PendingIntent callbackIntent = resolutionIntent.getParcelableExtra( EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_RESOLUTION_CALLBACK_INTENT); int portIndex = resolutionIntent.getIntExtra( EuiccService.EXTRA_RESOLUTION_PORT_INDEX, 0); resolutionExtras.putInt(EuiccService.EXTRA_RESOLUTION_PORT_INDEX, portIndex); boolean usePortIndex = resolutionIntent.getBooleanExtra( EuiccService.EXTRA_RESOLUTION_USE_PORT_INDEX, false); resolutionExtras.putBoolean(EuiccService.EXTRA_RESOLUTION_USE_PORT_INDEX, usePortIndex); Log.i(TAG, " continueOperation portIndex: " + resolutionExtras.getInt( EuiccService.EXTRA_RESOLUTION_PORT_INDEX) + " usePortIndex: " + usePortIndex); op.continueOperation(cardId, resolutionExtras, callbackIntent); } finally { Binder.restoreCallingIdentity(token); Loading @@ -211,7 +211,8 @@ public class EuiccController extends IEuiccController.Stub { long token = Binder.clearCallingIdentity(); try { if (!callerCanReadPhoneStatePrivileged && !canManageSubscriptionOnTargetSim(cardId, callingPackage)) { && !canManageSubscriptionOnTargetSim(cardId, callingPackage, false, TelephonyManager.INVALID_PORT_INDEX)) { throw new SecurityException( "Must have carrier privileges on subscription to read EID for cardId=" + cardId); Loading Loading @@ -564,7 +565,8 @@ public class EuiccController extends IEuiccController.Stub { // Otherwise, the user must provide consent. If it's a single-active SIM device, // determine whether the caller can manage the current profile; if so, we can perform // the download silently; if not, the user must provide consent. if (canManageSubscriptionOnTargetSim(cardId, callingPackage)) { if (canManageSubscriptionOnTargetSim(cardId, callingPackage, false, TelephonyManager.INVALID_PORT_INDEX)) { mConnector.getDownloadableSubscriptionMetadata(cardId, subscription, forceDeactivateSim, new DownloadSubscriptionGetMetadataCommandCallback(token, subscription, Loading Loading @@ -1006,9 +1008,11 @@ public class EuiccController extends IEuiccController.Stub { } final String iccid; boolean passConsent = false; boolean isConsentNeededToResolvePortIndex = false; if (subscriptionId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { if (callerCanWriteEmbeddedSubscriptions || canManageActiveSubscriptionOnTargetSim(cardId, callingPackage)) { || canManageActiveSubscriptionOnTargetSim(cardId, callingPackage, usePortIndex, portIndex)) { passConsent = true; } else { Log.e(TAG, "Not permitted to switch to empty subscription"); Loading @@ -1032,14 +1036,34 @@ public class EuiccController extends IEuiccController.Stub { return; } if (canManageSubscriptionOnTargetSim(cardId, callingPackage)) { if (canManageSubscriptionOnTargetSim(cardId, callingPackage, usePortIndex, portIndex)) { passConsent = true; } } iccid = sub.getIccId(); if (usePortIndex) { boolean hasValidPortIndex = isTargetPortIndexValid(cardId, portIndex); if (!hasValidPortIndex) { // Return permanent error. Log.e(TAG, "Not permitted to switch to invalid portIndex"); Intent extrasIntent = new Intent(); extrasIntent.putExtra(EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_ERROR_CODE, EuiccManager.ERROR_INVALID_PORT); sendResult(callbackIntent, ERROR, extrasIntent /* extrasIntent */); return; } if (!passConsent) { } else { // Resolve the portIndex internally if apps are calling switchToSubscription // API without portIndex. portIndex = getResolvedPortIndexForSubscriptionSwitch(cardId); isConsentNeededToResolvePortIndex = (portIndex == TelephonyManager.INVALID_PORT_INDEX); usePortIndex = true; Log.d(TAG, " Resolved portIndex: " + portIndex); } } if (!passConsent || isConsentNeededToResolvePortIndex) { // Switch needs consent. Intent extrasIntent = new Intent(); addResolutionIntent(extrasIntent, Loading @@ -1061,6 +1085,171 @@ public class EuiccController extends IEuiccController.Stub { } } /** * Returns the resolved portIndex or {@link TelephonyManager#INVALID_PORT_INDEX} if no port * is available without user consent. */ private int getResolvedPortIndexForSubscriptionSwitch(int cardId) { int slotIndex = getSlotIndexFromCardId(cardId); // Euicc Slot UiccSlot slot = UiccController.getInstance().getUiccSlot(slotIndex); if (!slot.isMultipleEnabledProfileSupported()) { Log.d(TAG, "Multiple enabled profiles is not supported, return default port index"); return TelephonyManager.DEFAULT_PORT_INDEX; } boolean isPsimHasActiveSub = isRemovalNonEuiccSlotHasActiveSubscription(); if (mTelephonyManager.getActiveModemCount() == 1) { // SS Mode if (isPsimHasActiveSub) { // In case of SS Mode and pSim has active subscription, return default port index // for two reasons. // 1. If psim and esim share the same carrier privilege, then users wouldn't need // to consent, the switch should be seamless. // 2. If psim and esim doesn't share the same carrier privilege, then permission // check dialog will be shown anyway. return TelephonyManager.DEFAULT_PORT_INDEX; } // If esim port is active, return the active portIndex irrespective of whether port is // empty or has active subscription. for (int portIndex : slot.getPortList()) { if (slot.isPortActive(portIndex)) { return portIndex; } } } else { // DSDS Mode for (int portIndex : slot.getPortList()) { if (slot.isPortActive(portIndex) && mSubscriptionManager.getActiveSubscriptionInfoForSimSlotIndex( slot.getPhoneIdFromPortIndex(portIndex)) == null) { // If the port is active and empty, return the portIndex. return portIndex; } } // Check whether the pSim is active and empty boolean isPsimEmpty = getRemovableNonEuiccSlot() != null && getRemovableNonEuiccSlot().isActive() && !isPsimHasActiveSub; if (isPsimEmpty) { // This logic will execute only if below two conditions are true. // 1. pSim is active and empty // 2. eSim has active subscription // Return the next available inactive eSim portIndex. return getNextAvailableInActivePortIndex(slot); } } return TelephonyManager.INVALID_PORT_INDEX; } /** * Returns true if the target port index is valid. * 1. Port index is valid if it is non-negative and less than the total port count. * 2. In SS Mode, port index is invalid if the embedded slot already has an active empty port * with different port index than the target port index. * 3. In DSDS mode, port index is invalid if the pSim slot is active and the embedded slot * already has an active empty port with different port index than the target port index. */ private boolean isTargetPortIndexValid(int cardId, int targetPortIndex) { if (targetPortIndex < 0) { Log.e(TAG, "Invalid portIndex: " + targetPortIndex); return false; } int slotIndex = getSlotIndexFromCardId(cardId); UiccSlot slot = UiccController.getInstance().getUiccSlot(slotIndex); if (slot == null || slot.getPortList().length == 0 || targetPortIndex >= slot.getPortList().length) { Log.e(TAG, "Invalid portIndex"); return false; } if (mTelephonyManager.getActiveModemCount() == 1) { // SS Mode for (int portIndex : slot.getPortList()) { if (slot.isPortActive(portIndex) && portIndex != targetPortIndex) { // if there is an active esim port, should not try to enable the // profile on other inactive port. Log.e(TAG, "In SS Mode, slot already has active port on portIndex " + portIndex + " , reject the switch request to portIndex " + targetPortIndex); return false; } } } else if (mTelephonyManager.getActiveModemCount() > 1) { // DSDS Mode // If physical slot has active subscription and eSim has active port (without active // subscription), should not try to enable the profile on other inactive port. boolean isPsimActive = isRemovalNonEuiccSlotHasActiveSubscription(); if (isPsimActive) { for (int portIndex : slot.getPortList()) { if (slot.isPortActive(portIndex) && mSubscriptionManager .getActiveSubscriptionInfoForSimSlotIndex( slot.getPhoneIdFromPortIndex(portIndex)) == null && portIndex != targetPortIndex) { Log.e(TAG, "In DSDS Mode, pSim has active subscription, eSim has empty" + " active port on portIndex " + portIndex + " , reject the switch request to portIndex " + targetPortIndex); return false; } } } } return true; } private int getNextAvailableInActivePortIndex(UiccSlot slot) { if (slot != null) { for (int portIndex : slot.getPortList()) { if (!slot.isPortActive(portIndex)) { return portIndex; } } } return TelephonyManager.INVALID_PORT_INDEX; } /** * Gets the slot index from the card ID. */ private int getSlotIndexFromCardId(int cardId) { List<UiccCardInfo> infos = mTelephonyManager.getUiccCardsInfo(); if (infos == null || infos.size() == 0) { return SubscriptionManager.INVALID_SIM_SLOT_INDEX; } for (UiccCardInfo info : infos) { if (info.getCardId() == cardId) { return info.getPhysicalSlotIndex(); } } return SubscriptionManager.INVALID_SIM_SLOT_INDEX; } private boolean isRemovalNonEuiccSlotHasActiveSubscription() { UiccSlot uiccSlot = getRemovableNonEuiccSlot(); if (uiccSlot != null) { for (int portIndex : uiccSlot.getPortList()) { if (uiccSlot.isPortActive(portIndex) && mSubscriptionManager.getActiveSubscriptionInfoForSimSlotIndex( uiccSlot.getPhoneIdFromPortIndex(portIndex)) != null) { return true; } } } return false; } private UiccSlot getRemovableNonEuiccSlot() { UiccSlot[] uiccSlots = UiccController.getInstance().getUiccSlots(); if (uiccSlots != null) { for (int i = 0; i < uiccSlots.length; i++) { if (uiccSlots[i] != null && uiccSlots[i].isRemovable() && !uiccSlots[i].isEuicc()) { return uiccSlots[i]; } } } return null; } void switchToSubscriptionPrivileged(int cardId, int portIndex, final long callingToken, int subscriptionId, boolean forceDeactivateSim, final String callingPackage, final PendingIntent callbackIntent, boolean usePortIndex) { Loading Loading @@ -1515,7 +1704,10 @@ public class EuiccController extends IEuiccController.Stub { // Checks whether the caller can manage the active embedded subscription on the SIM with the // given cardId. private boolean canManageActiveSubscriptionOnTargetSim(int cardId, String callingPackage) { // From Android T, if usePortIndex is true then should check if the calling app has carrier // privilege over the subscription on the target port index. private boolean canManageActiveSubscriptionOnTargetSim(int cardId, String callingPackage, boolean usePortIndex, int targetPortIndex) { List<SubscriptionInfo> subInfoList = mSubscriptionManager .getActiveSubscriptionInfoList(/* userVisibleOnly */false); if (subInfoList == null || subInfoList.size() == 0) { Loading @@ -1529,6 +1721,7 @@ public class EuiccController extends IEuiccController.Stub { // subscription on any eSIM. That's the best we can do here. if ((cardId == TelephonyManager.UNSUPPORTED_CARD_ID || subInfo.getCardId() == cardId) && subInfo.isEmbedded() && (!usePortIndex || subInfo.getPortIndex() == targetPortIndex) && mSubscriptionManager.canManageSubscription(subInfo, callingPackage)) { return true; } Loading @@ -1543,7 +1736,10 @@ public class EuiccController extends IEuiccController.Stub { // other SIM. The target SIM should be an eUICC. // For a single-active subscription phone, checks whether the caller can manage any active // embedded subscription. private boolean canManageSubscriptionOnTargetSim(int cardId, String callingPackage) { // From Android T, If embedded slot supports Multiple Enabled Profiles then should check if // the calling app has carrier privilege over the subscription on the target port index. private boolean canManageSubscriptionOnTargetSim(int cardId, String callingPackage, boolean usePortIndex, int targetPortIndex) { List<SubscriptionInfo> subInfoList = mSubscriptionManager .getActiveSubscriptionInfoList(false /* userVisibleonly */); // No active subscription on any SIM. Loading @@ -1561,9 +1757,11 @@ public class EuiccController extends IEuiccController.Stub { return false; } boolean isEuicc = false; boolean isMultipleEnabledProfilesSupported = false; for (UiccCardInfo info : cardInfos) { if (info != null && info.getCardId() == cardId && info.isEuicc()) { isEuicc = true; isMultipleEnabledProfilesSupported = info.isMultipleEnabledProfilesSupported(); break; } } Loading @@ -1576,8 +1774,13 @@ public class EuiccController extends IEuiccController.Stub { // false. If the caller can manage the active embedded subscription on the target SIM, // return true directly. for (SubscriptionInfo subInfo : subInfoList) { // subInfo.isEmbedded() can only be true for the target SIM. if (subInfo.isEmbedded() && subInfo.getCardId() == cardId) { // 1. subInfo.isEmbedded() can only be true for the target SIM. // 2. Check whether the caller can manage subscription on the target portIndex // (i.e. subInfo.getPortIndex() == targetPortIndex condition) only in case if // isMultipleEnabledProfilesSupported and usePortIndex both are true. if (subInfo.isEmbedded() && subInfo.getCardId() == cardId && (!isMultipleEnabledProfilesSupported || !usePortIndex || subInfo.getPortIndex() == targetPortIndex)) { return mSubscriptionManager.canManageSubscription(subInfo, callingPackage); } } Loading
tests/telephonytests/src/com/android/internal/telephony/euicc/EuiccControllerTest.java +16 −0 Original line number Diff line number Diff line Loading @@ -66,6 +66,7 @@ import androidx.test.runner.AndroidJUnit4; import com.android.internal.telephony.TelephonyTest; import com.android.internal.telephony.euicc.EuiccConnector.GetOtaStatusCommandCallback; import com.android.internal.telephony.euicc.EuiccConnector.OtaStatusChangedCallback; import com.android.internal.telephony.uicc.UiccSlot; import org.junit.After; import org.junit.Before; Loading Loading @@ -121,6 +122,7 @@ public class EuiccControllerTest extends TelephonyTest { private static final int PORT_INDEX = 0; @Mock private EuiccConnector mMockConnector; @Mock private UiccSlot mUiccSlot; private TestEuiccController mController; private int mSavedEuiccProvisionedValue; Loading Loading @@ -786,6 +788,7 @@ public class EuiccControllerTest extends TelephonyTest { public void testSwitchToSubscription_serviceUnavailable() throws Exception { setHasWriteEmbeddedPermission(true); prepareOperationSubscription(false /* hasPrivileges */); setUpUiccSlotData(); callSwitchToSubscription( SUBSCRIPTION_ID, ICC_ID, false /* complete */, 0 /* result */, "whatever" /* callingPackage */); Loading @@ -799,6 +802,7 @@ public class EuiccControllerTest extends TelephonyTest { public void testSwitchToSubscription_error() throws Exception { setHasWriteEmbeddedPermission(true); prepareOperationSubscription(false /* hasPrivileges */); setUpUiccSlotData(); callSwitchToSubscription( SUBSCRIPTION_ID, ICC_ID, true /* complete */, 42 /* result */, "whatever" /* callingPackage */); Loading @@ -810,6 +814,7 @@ public class EuiccControllerTest extends TelephonyTest { public void testSwitchToSubscription_success() throws Exception { setHasWriteEmbeddedPermission(true); prepareOperationSubscription(false /* hasPrivileges */); setUpUiccSlotData(); callSwitchToSubscription( SUBSCRIPTION_ID, ICC_ID, true /* complete */, EuiccService.RESULT_OK, "whatever" /* callingPackage */); Loading Loading @@ -842,6 +847,7 @@ public class EuiccControllerTest extends TelephonyTest { public void testSwitchToSubscription_hasCarrierPrivileges() throws Exception { setHasWriteEmbeddedPermission(false); prepareOperationSubscription(true /* hasPrivileges */); setUpUiccSlotData(); when(mTelephonyManager.getPhoneCount()).thenReturn(1); setHasCarrierPrivilegesOnActiveSubscription(true); callSwitchToSubscription( Loading @@ -853,6 +859,7 @@ public class EuiccControllerTest extends TelephonyTest { public void testSwitchToSubscription_hasCarrierPrivileges_multiSim() throws Exception { setHasWriteEmbeddedPermission(false); prepareOperationSubscription(true /* hasPrivileges */); setUpUiccSlotData(); when(mTelephonyManager.getPhoneCount()).thenReturn(2); setCanManageSubscriptionOnTargetSim(true /* isTargetEuicc */, true /* hasPrivileges */); callSwitchToSubscription( Loading @@ -864,6 +871,7 @@ public class EuiccControllerTest extends TelephonyTest { public void testSwitchToSubscription_hasCarrierPrivileges_needsConsent() throws Exception { setHasWriteEmbeddedPermission(false); prepareOperationSubscription(true /* hasPrivileges */); setUpUiccSlotData(); setHasCarrierPrivilegesOnActiveSubscription(false); when(mTelephonyManager.getPhoneCount()).thenReturn(1); callSwitchToSubscription( Loading @@ -881,6 +889,7 @@ public class EuiccControllerTest extends TelephonyTest { throws Exception { setHasWriteEmbeddedPermission(false); prepareOperationSubscription(true /* hasPrivileges */); setUpUiccSlotData(); when(mTelephonyManager.getPhoneCount()).thenReturn(2); setCanManageSubscriptionOnTargetSim(true /* isTargetEuicc */, false /* hasPrivileges */); callSwitchToSubscription( Loading @@ -898,6 +907,7 @@ public class EuiccControllerTest extends TelephonyTest { throws Exception { setHasWriteEmbeddedPermission(false); prepareOperationSubscription(true /* hasPrivileges */); setUpUiccSlotData(); when(mTelephonyManager.getPhoneCount()).thenReturn(2); setCanManageSubscriptionOnTargetSim(false /* isTargetEuicc */, true /* hasPrivileges */); callSwitchToSubscription( Loading Loading @@ -1129,6 +1139,12 @@ public class EuiccControllerTest extends TelephonyTest { EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_ERROR_CODE, -1), 0x345678); } private void setUpUiccSlotData() { when(mUiccController.getUiccSlot(anyInt())).thenReturn(mUiccSlot); // TODO(b/199559633): Add test cases for isMultipleEnabledProfileSupported true case when(mUiccSlot.isMultipleEnabledProfileSupported()).thenReturn(false); } private void setGetEidPermissions( boolean hasPhoneStatePrivileged, boolean hasCarrierPrivileges) throws Exception { doReturn(hasPhoneStatePrivileged Loading