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

Commit c260996d authored by Muralidhar Reddy's avatar Muralidhar Reddy
Browse files

[MEP] Add support for port index validation and resolving port index

Bug: 214055002
Test: manual, atest FrameworksTelephonyTests
Change-Id: Ib365834290518872d50054e621b61968025ed47b
parent 43a7fa17
Loading
Loading
Loading
Loading
+216 −13
Original line number Diff line number Diff line
@@ -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;
@@ -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);
@@ -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);
@@ -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,
@@ -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");
@@ -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,
@@ -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) {
@@ -1513,7 +1702,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) {
@@ -1527,6 +1719,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;
            }
@@ -1541,7 +1734,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.
@@ -1559,9 +1755,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;
                }
            }
@@ -1574,8 +1772,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);
                }
            }
+16 −0
Original line number Diff line number Diff line
@@ -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;
@@ -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;

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