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

Commit c749220d authored by Xiangyu/Malcolm Chen's avatar Xiangyu/Malcolm Chen Committed by Gerrit Code Review
Browse files

Merge changes from topics "118348832", "120945564"

* changes:
  Adding validationBeforeSwitchSupported in phoneCapability.
  Adding unittest for enable / disable subscription.
  Add API to handle user's subscription selection changes.
parents 4d5d10c2 da02d1d7
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -40,10 +40,10 @@ class PhoneConfigurationModels {
        List<ModemInfo> logicalModemList = new ArrayList<>();
        logicalModemList.add(modemInfo1);
        logicalModemList.add(modemInfo2);
        DSDS_CAPABILITY = new PhoneCapability(1, 2, 0, logicalModemList);
        DSDS_CAPABILITY = new PhoneCapability(1, 2, 0, logicalModemList, false);

        logicalModemList = new ArrayList<>();
        logicalModemList.add(modemInfo1);
        SSSS_CAPABILITY = new PhoneCapability(1, 1, 0, logicalModemList);
        SSSS_CAPABILITY = new PhoneCapability(1, 1, 0, logicalModemList, false);
    }
}
+3 −1
Original line number Diff line number Diff line
@@ -121,6 +121,7 @@ public class RadioConfigResponse extends IRadioConfigResponse.Stub {
        int maxActiveVoiceCalls = 0;
        int maxActiveData = phoneCapability.maxActiveData;
        int max5G = 0;
        boolean validationBeforeSwitchSupported = phoneCapability.isInternetLingeringSupported;
        List<ModemInfo> logicalModemList = new ArrayList();

        for (android.hardware.radio.config.V1_1.ModemInfo
@@ -128,7 +129,8 @@ public class RadioConfigResponse extends IRadioConfigResponse.Stub {
            logicalModemList.add(new ModemInfo(modemInfo.modemId));
        }

        return new PhoneCapability(maxActiveVoiceCalls, maxActiveData, max5G, logicalModemList);
        return new PhoneCapability(maxActiveVoiceCalls, maxActiveData, max5G, logicalModemList,
                validationBeforeSwitchSupported);
    }
    /**
     * Response function for IRadioConfig.getPhoneCapability().
+229 −0
Original line number Diff line number Diff line
@@ -41,6 +41,7 @@ import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.telephony.UiccAccessRule;
import android.telephony.UiccSlotInfo;
import android.telephony.euicc.EuiccManager;
import android.text.TextUtils;
import android.text.format.Time;
@@ -2940,6 +2941,234 @@ public class SubscriptionController extends ISub.Stub {
        }
    }

    @Override
    public boolean setSubscriptionEnabled(boolean enable, int subId) {
        // TODO: b/123314365 support multi-eSIM and removable eSIM.
        enforceModifyPhoneState("setSubscriptionEnabled");

        long identity = Binder.clearCallingIdentity();
        try {
            // Error checking.
            if (!SubscriptionManager.isUsableSubIdValue(subId)) {
                throw new IllegalArgumentException(
                        "setUserSelectedSubscription with invalid subId " + subId);
            }
            if (mTelephonyManager.getPhoneCount() <= 1) {
                loge("setSubscriptionEnabled not supported in single SIM modem.");
                return false;
            }
            List<SubscriptionInfo> infoList = getSubInfo(
                    SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" + subId, null);
            if (infoList == null || infoList.isEmpty()) {
                loge("setUserSelectedSubscription can't find subId " + subId);
                return false;
            }

            // For eSIM, inactive can still be enabled. Because user maybe enabled an embedded
            // subscription while system switched to another one temporarily.
            // But for pSIM, inactive means it's unplugged. So it can no longer be enabled or
            // disabled.
            if (!infoList.get(0).isEmbedded() && !isActiveSubId(subId)) {
                return false;
            }

            // Error checking done.
            return setSubscriptionEnabledInternal(infoList.get(0), enable);
        } finally {
            Binder.restoreCallingIdentity(identity);
        }
    }

    private boolean setSubscriptionEnabledInternal(SubscriptionInfo info, boolean enable) {
        int physicalSlotIndex = getPhysicalSlotIndex(info.isEmbedded(),
                info.getSubscriptionId());
        logd("setSubscriptionEnabledInternal setting subId " + info.getSubscriptionId()
                + " at physicalSlotIndex " + physicalSlotIndex
                + (enable ? " enabled." : " disabled."));

        int subId = info.getSubscriptionId();

        if (info.isEmbedded()) {
            return setSubscriptionOnEmbeddedSlot(subId, enable, physicalSlotIndex);
        } else {
            return setSubscriptionOnPhysicalSlot(subId, enable, physicalSlotIndex);
        }
    }

    private boolean setSubscriptionOnEmbeddedSlot(int subId, boolean enable,
            int physicalSlotIndex) {
        subId = enable ? subId : SubscriptionManager.INVALID_SUBSCRIPTION_ID;
        // Switch eSIM profile and keep it in global settings.
        EuiccManager euiccManager = (EuiccManager)
                mContext.getSystemService(Context.EUICC_SERVICE);
        euiccManager.switchToSubscription(subId, PendingIntent.getService(
                mContext, 0, new Intent(), 0));
        updateEnabledSubscriptionGlobalSetting(subId, physicalSlotIndex);
        refreshCachedActiveSubscriptionInfoList();
        return true;
    }

    private boolean setSubscriptionOnPhysicalSlot(int subId, boolean enable,
            int physicalSlotIndex) {
        // Enable / disable pSIM modem and keep the value in global settings.
        if (mTelephonyManager.enableModemForSlot(getPhoneId(subId), enable)) {
            // For physical slot, in addition to which subscription is enabled in the slot,
            // we also store whether the modem stack is enabled or not.
            updateEnabledSubscriptionGlobalSetting(
                    enable ? subId : SubscriptionManager.INVALID_SUBSCRIPTION_ID,
                    physicalSlotIndex);
            updateModemStackEnabledGlobalSetting(enable, physicalSlotIndex);
            refreshCachedActiveSubscriptionInfoList();
            return true;
        } else {
            // Operation failed. Do nothing.
            return false;
        }
    }

    private void updateEnabledSubscriptionGlobalSetting(int subId, int physicalSlotIndex) {
        // Write the value which subscription is enabled into global setting.
        Settings.Global.putInt(mContext.getContentResolver(),
                Settings.Global.ENABLED_SUBSCRIPTION_FOR_SLOT + physicalSlotIndex, subId);
    }

    private void updateModemStackEnabledGlobalSetting(boolean enabled, int physicalSlotIndex) {
        // Write the whether a modem stack is disabled into global setting.
        Settings.Global.putInt(mContext.getContentResolver(),
                Settings.Global.MODEM_STACK_ENABLED_FOR_SLOT
                        + physicalSlotIndex, enabled ? 1 : 0);
    }

    private int getPhysicalSlotIndex(boolean isEmbedded, int subId) {
        UiccSlotInfo[] slotInfos = mTelephonyManager.getUiccSlotsInfo();
        int logicalSlotIndex = getSlotIndex(subId);
        int physicalSlotIndex = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
        boolean isLogicalSlotIndexValid = SubscriptionManager.isValidSlotIndex(logicalSlotIndex);

        for (int i = 0; i < slotInfos.length; i++) {
            // If we can know the logicalSlotIndex from subId, we should find the exact matching
            // physicalSlotIndex. However for some cases like inactive eSIM, the logicalSlotIndex
            // will be -1. In this case, we assume there's only one eSIM, and return the
            // physicalSlotIndex of that eSIM.
            if ((isLogicalSlotIndexValid && slotInfos[i].getLogicalSlotIdx() == logicalSlotIndex)
                    || (!isLogicalSlotIndexValid && slotInfos[i].getIsEuicc() && isEmbedded)) {
                physicalSlotIndex = i;
                break;
            }
        }

        return physicalSlotIndex;
    }

    private int getPhysicalSlotIndexFromLogicalSlotIndex(int logicalSlotIndex) {
        int physicalSlotIndex = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
        UiccSlotInfo[] slotInfos = mTelephonyManager.getUiccSlotsInfo();
        for (int i = 0; i < slotInfos.length; i++) {
            if (slotInfos[i].getLogicalSlotIdx() == logicalSlotIndex) {
                physicalSlotIndex = i;
                break;
            }
        }

        return physicalSlotIndex;
    }

    @Override
    public boolean isSubscriptionEnabled(int subId) {
        // TODO: b/123314365 support multi-eSIM and removable eSIM.
        enforceReadPrivilegedPhoneState("isSubscriptionEnabled");

        long identity = Binder.clearCallingIdentity();
        try {
            // Subscription enable / disable shouldn't matter for single SIM devices.
            if (mTelephonyManager.getPhoneCount() <= 1) {
                return isActiveSubId(subId);
            }
            // Error checking.
            if (!SubscriptionManager.isUsableSubIdValue(subId)) {
                throw new IllegalArgumentException(
                        "setUserSelectedSubscription with invalid subId " + subId);
            }
            List<SubscriptionInfo> infoList = getSubInfo(
                    SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" + subId, null);
            if (infoList == null || infoList.isEmpty()) {
                loge("setUserSelectedSubscription can't find subId " + subId);
                return false;
            }

            boolean isEmbedded = infoList.get(0).isEmbedded();
            boolean isActiveSub = isActiveSubId(subId);
            int physicalSlotIndex = getPhysicalSlotIndex(isEmbedded, subId);
            // DEFAULT_SUBSCRIPTION_ID if not set.
            int enabledSubId = Settings.Global.getInt(mContext.getContentResolver(),
                    Settings.Global.ENABLED_SUBSCRIPTION_FOR_SLOT + physicalSlotIndex,
                    SubscriptionManager.DEFAULT_SUBSCRIPTION_ID);
            boolean modemStackEnabled = Settings.Global.getInt(mContext.getContentResolver(),
                    Settings.Global.MODEM_STACK_ENABLED_FOR_SLOT
                            + physicalSlotIndex, 1) == 1;
            boolean enabledSubIdNotSet =
                    (enabledSubId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID);
            boolean enabledSubIdMatch = (enabledSubId == subId);

            if (isEmbedded) {
                // For eSIM, enabled means means modem stack is enabled and it either 1) matches
                // what was set enabled in global setting, or 2) global setting is never set but
                // it's the currently active subscription.
                return modemStackEnabled && (enabledSubIdMatch
                        || (enabledSubIdNotSet && isActiveSub));
            } else {
                // For pSIM, enabled means modem stack is enabled and it's the currently active
                // subscription.
                return modemStackEnabled && isActiveSub;
            }

        } finally {
            Binder.restoreCallingIdentity(identity);
        }
    }

    @Override
    public int getEnabledSubscriptionId(int logicalSlotIndex) {
        // TODO: b/123314365 support multi-eSIM and removable eSIM.
        enforceReadPrivilegedPhoneState("getEnabledSubscriptionId");

        long identity = Binder.clearCallingIdentity();
        try {
            if (!SubscriptionManager.isValidPhoneId(logicalSlotIndex)) {
                throw new IllegalArgumentException(
                        "getEnabledSubscriptionId with invalid logicalSlotIndex "
                                + logicalSlotIndex);
            }

            // Getting physicalSlotIndex
            int physicalSlotIndex = getPhysicalSlotIndexFromLogicalSlotIndex(logicalSlotIndex);
            if (!SubscriptionManager.isValidSlotIndex(physicalSlotIndex)) {
                return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
            }

            // if modem stack is disabled, return INVALID_SUBSCRIPTION_ID without reading
            // Settings.Global.ENABLED_SUBSCRIPTION_FOR_SLOT.
            int modemStackEnabled = Settings.Global.getInt(mContext.getContentResolver(),
                    Settings.Global.MODEM_STACK_ENABLED_FOR_SLOT + physicalSlotIndex, 1);
            if (modemStackEnabled != 1) {
                return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
            }

            int subId;
            try {
                subId = Settings.Global.getInt(mContext.getContentResolver(),
                        Settings.Global.ENABLED_SUBSCRIPTION_FOR_SLOT + physicalSlotIndex);
            } catch (Settings.SettingNotFoundException e) {
                // Value never set. Return whether it's currently active.
                subId = getSubIdUsingPhoneId(logicalSlotIndex);
            }

            return subId;
        } finally {
            Binder.restoreCallingIdentity(identity);
        }
    }

    // Helper function of getOpportunisticSubscriptions and getActiveSubscriptionInfoList.
    // They are doing similar things except operating on different cache.
    private List<SubscriptionInfo> getSubscriptionInfoListFromCacheHelper(
+4 −4
Original line number Diff line number Diff line
@@ -41,7 +41,7 @@ public class PhoneCapabilityTest {
        logicalModemList.add(modemInfo);

        PhoneCapability capability = new PhoneCapability(maxActiveVoiceCalls, maxActiveData, max5G,
                logicalModemList);
                logicalModemList, false);

        assertEquals(maxActiveVoiceCalls, capability.maxActiveVoiceCalls);
        assertEquals(maxActiveData, capability.maxActiveData);
@@ -49,9 +49,9 @@ public class PhoneCapabilityTest {
        assertEquals(1, capability.logicalModemList.size());
        assertEquals(modemInfo, capability.logicalModemList.get(0));
        PhoneCapability toCompare = new PhoneCapability(
                maxActiveVoiceCalls + 1, maxActiveData - 1, max5G, logicalModemList);
                maxActiveVoiceCalls + 1, maxActiveData - 1, max5G, logicalModemList, false);
        assertEquals(capability, new PhoneCapability(
                maxActiveVoiceCalls, maxActiveData, max5G, logicalModemList));
                maxActiveVoiceCalls, maxActiveData, max5G, logicalModemList, false));
        assertNotEquals(capability, toCompare);
    }

@@ -66,7 +66,7 @@ public class PhoneCapabilityTest {
        logicalModemList.add(modemInfo);

        PhoneCapability capability = new PhoneCapability(maxActiveVoiceCalls, maxActiveData, max5G,
                logicalModemList);
                logicalModemList, false);

        Parcel parcel = Parcel.obtain();
        capability.writeToParcel(parcel, 0);
+41 −0
Original line number Diff line number Diff line
@@ -37,6 +37,7 @@ import android.os.UserHandle;
import android.provider.Settings;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.UiccSlotInfo;
import android.test.mock.MockContentResolver;
import android.test.suitebuilder.annotation.SmallTest;

@@ -54,6 +55,7 @@ import java.util.Map;

public class SubscriptionControllerTest extends TelephonyTest {
    private static final int SINGLE_SIM = 1;
    private static final int DUAL_SIM = 2;
    private String mCallingPackage;
    private SubscriptionController mSubscriptionControllerUT;
    private MockContentResolver mMockContentResolver;
@@ -754,6 +756,45 @@ public class SubscriptionControllerTest extends TelephonyTest {
                .queryLocalInterface(anyString());
    }

    @Test
    @SmallTest
    public void testEnableDisableSubscriptionSanity() throws Exception {
        testInsertSim();

        // Non existing subId.
        assertFalse(mSubscriptionControllerUT.isSubscriptionEnabled(2));
        assertFalse(mSubscriptionControllerUT.isSubscriptionEnabled(-1));

        // Test invalid arguments.
        try {
            mSubscriptionControllerUT.getEnabledSubscriptionId(3);
            fail("Should throw IllegalArgumentException with invalid subId.");
        } catch (IllegalArgumentException exception) {
            // Expected.
        }
    }

    @Test
    @SmallTest
    public void testEnableDisableSubscriptionSingleSim() throws Exception {
        testInsertSim();
        // UiccSlotInfo that maps logicalSlotIndex 0 to physicalSlotIndex 0.
        UiccSlotInfo slotInfo = new UiccSlotInfo(true, false, "", 0, 0, false);
        UiccSlotInfo[] slotInfos = new UiccSlotInfo[] {slotInfo};
        doReturn(slotInfos).when(mTelephonyManager).getUiccSlotsInfo();

        // Current active subscription should be the enabled one.
        assertTrue(mSubscriptionControllerUT.isSubscriptionEnabled(1));
        assertEquals(1, mSubscriptionControllerUT.getEnabledSubscriptionId(0));
        // SetSubscriptionEnabled should fail (no-op) on single SIM device.
        assertFalse(mSubscriptionControllerUT.setSubscriptionEnabled(false, 1));

        // Current active subscription should be the enabled one.
        assertTrue(mSubscriptionControllerUT.isSubscriptionEnabled(1));
        assertEquals(1, mSubscriptionControllerUT.getEnabledSubscriptionId(0));
        // TODO: test dual SIM case when SubscriptionControllerTest supports dual SIM config.
    }

    @Test
    @SmallTest
    public void testGetActiveSubIdList() throws Exception {
Loading