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

Commit e7cca9c4 authored by Amit Mahajan's avatar Amit Mahajan
Browse files

Handle race condition between SIM app deactivation and SIM removal.

SIM app deactivation results in NOT_READY state, but before that
SIM can be removed resulting in ABSENT state. In that case
SubscriptionInfoUpdater would not get a chance to update
sInactiveIccIds.

Test: atest SubscriptionInfoUpdaterTest#testSimRemovedWhileDisablingUiccApps
Bug: 159777258
Change-Id: Ia6d07848d9a019b4cff55d5a051d9c278d4714ee
parent 2fd7bb6b
Loading
Loading
Loading
Loading
+27 −7
Original line number Diff line number Diff line
@@ -641,7 +641,7 @@ public class SubscriptionInfoUpdater extends Handler {
            if (sIccId[phoneId] != null && !sIccId[phoneId].equals(ICCID_STRING_FOR_NO_SIM)) {
                logd("Slot of SIM" + (phoneId + 1) + " becomes inactive");
            }
            cleanSubscriptionInPhone(phoneId);
            cleanSubscriptionInPhone(phoneId, false);
        }
        if (!TextUtils.isEmpty(iccId)) {
            // If iccId is new, add a subscription record in the db.
@@ -654,19 +654,39 @@ public class SubscriptionInfoUpdater extends Handler {
        }
    }

    private void cleanSubscriptionInPhone(int phoneId) {
        sIccId[phoneId] = ICCID_STRING_FOR_NO_SIM;
        if (sInactiveIccIds[phoneId] != null) {
    /**
     * Clean subscription info when sim state becomes ABSENT. There are 2 scenarios for this:
     * 1. SIM is actually removed
     * 2. Slot becomes inactive, which results in SIM being treated as ABSENT, but SIM may not
     * have been removed.
     * @param phoneId phoneId for which the cleanup needs to be done
     * @param isSimAbsent boolean to indicate if the SIM is actually ABSENT (case 1 above)
     */
    private void cleanSubscriptionInPhone(int phoneId, boolean isSimAbsent) {
        if (sInactiveIccIds[phoneId] != null || (isSimAbsent && sIccId[phoneId] != null
                && !sIccId[phoneId].equals(ICCID_STRING_FOR_NO_SIM))) {
            // When a SIM is unplugged, mark uicc applications enabled. This is to make sure when
            // user unplugs and re-inserts the SIM card, we re-enable it.
            logd("cleanSubscriptionInPhone " + phoneId + " inactive iccid "
            // In certain cases this can happen before sInactiveIccIds is updated, which is why we
            // check for sIccId as well (in case of isSimAbsent). The scenario is: after SIM
            // deactivate request is sent to RIL, SIM is removed before SIM state is updated to
            // NOT_READY. We do not need to check if this exact scenario is hit, because marking
            // uicc applications enabled when SIM is removed should be okay to do regardless.
            logd("cleanSubscriptionInPhone: " + phoneId + ", inactive iccid "
                    + sInactiveIccIds[phoneId]);
            if (sInactiveIccIds[phoneId] == null) {
                logd("cleanSubscriptionInPhone: " + phoneId + ", isSimAbsent=" + isSimAbsent
                        + ", iccid=" + sIccId[phoneId]);
            }
            String iccId = sInactiveIccIds[phoneId] != null
                    ? sInactiveIccIds[phoneId] : sIccId[phoneId];
            ContentValues value = new ContentValues(1);
            value.put(SubscriptionManager.UICC_APPLICATIONS_ENABLED, true);
            sContext.getContentResolver().update(SubscriptionManager.CONTENT_URI, value,
                    SubscriptionManager.ICC_ID + "=\'" + sInactiveIccIds[phoneId] + "\'", null);
                    SubscriptionManager.ICC_ID + "=\'" + iccId + "\'", null);
            sInactiveIccIds[phoneId] = null;
        }
        sIccId[phoneId] = ICCID_STRING_FOR_NO_SIM;
        updateSubscriptionInfoByIccId(phoneId, true /* updateEmbeddedSubs */);
    }

@@ -678,7 +698,7 @@ public class SubscriptionInfoUpdater extends Handler {
        if (sIccId[phoneId] != null && !sIccId[phoneId].equals(ICCID_STRING_FOR_NO_SIM)) {
            logd("SIM" + (phoneId + 1) + " hot plug out");
        }
        cleanSubscriptionInPhone(phoneId);
        cleanSubscriptionInPhone(phoneId, true);

        broadcastSimStateChanged(phoneId, IccCardConstants.INTENT_VALUE_ICC_ABSENT, null);
        broadcastSimCardStateChanged(phoneId, TelephonyManager.SIM_STATE_ABSENT);
+38 −10
Original line number Diff line number Diff line
@@ -84,6 +84,7 @@ public class SubscriptionInfoUpdaterTest extends TelephonyTest {
    private static final int FAKE_SUB_ID_2 = 1;
    private static final int FAKE_CARD_ID = 0;
    private static final String FAKE_EID = "89049032000001000000031328322874";
    private static final String FAKE_ICCID_1 = "89012604200000000000";
    private static final String FAKE_MCC_MNC_1 = "123456";
    private static final String FAKE_MCC_MNC_2 = "456789";

@@ -309,6 +310,24 @@ public class SubscriptionInfoUpdaterTest extends TelephonyTest {
                UICC_APPLICATIONS_ENABLED));
    }

    @Test
    @SmallTest
    public void testSimRemovedWhileDisablingUiccApps() throws Exception {
        loadSim();

        mUpdater.updateInternalIccState(
                IccCardConstants.INTENT_VALUE_ICC_ABSENT, null, FAKE_SUB_ID_1);
        processAllMessages();

        // UICC_APPLICATIONS_ENABLED should be reset to true.
        ArgumentCaptor<ContentValues> valueCapture = ArgumentCaptor.forClass(ContentValues.class);
        verify(mContentProvider).update(eq(SubscriptionManager.CONTENT_URI), valueCapture.capture(),
                eq(SubscriptionManager.ICC_ID + "=\'" + FAKE_ICCID_1 + "\'"), eq(null));
        ContentValues contentValues = valueCapture.getValue();
        assertTrue(contentValues != null && contentValues.getAsBoolean(
                UICC_APPLICATIONS_ENABLED));
    }

    @Test
    @SmallTest
    public void testSimError() throws Exception {
@@ -343,13 +362,11 @@ public class SubscriptionInfoUpdaterTest extends TelephonyTest {
        verify(mSubscriptionController, times(0)).notifySubscriptionInfoChanged();
    }

    @Test
    @SmallTest
    public void testSimLoaded() throws Exception {
    private void loadSim() {
        doReturn(FAKE_SUB_ID_1).when(mSubInfo).getSubscriptionId();
        doReturn(Arrays.asList(mSubInfo)).when(mSubscriptionController)
                .getSubInfoUsingSlotIndexPrivileged(eq(FAKE_SUB_ID_1));
        doReturn("89012604200000000000").when(mIccRecord).getFullIccId();
        doReturn(FAKE_ICCID_1).when(mIccRecord).getFullIccId();
        doReturn(FAKE_MCC_MNC_1).when(mTelephonyManager).getSimOperatorNumeric(FAKE_SUB_ID_1);
        when(mActivityManager.updateMccMncConfiguration(anyString(), anyString())).thenReturn(
                true);
@@ -360,6 +377,17 @@ public class SubscriptionInfoUpdaterTest extends TelephonyTest {
        processAllMessages();
        assertTrue(mUpdater.isSubInfoInitialized());

        CarrierConfigManager mConfigManager = (CarrierConfigManager)
                mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
        verify(mConfigManager).updateConfigForPhoneId(eq(FAKE_SUB_ID_1),
                eq(IccCardConstants.INTENT_VALUE_ICC_LOADED));
    }

    @Test
    @SmallTest
    public void testSimLoaded() throws Exception {
        loadSim();

        // verify SIM_STATE_CHANGED broadcast. It should be broadcast twice, once for
        // READ_PHONE_STATE and once for READ_PRIVILEGED_PHONE_STATE
        /* todo: cannot verify as intent is sent using ActivityManagerNative.broadcastStickyIntent()
@@ -381,7 +409,7 @@ public class SubscriptionInfoUpdaterTest extends TelephonyTest {
        SubscriptionManager mSubscriptionManager = SubscriptionManager.from(mContext);
        verify(mTelephonyManager).getSimOperatorNumeric(FAKE_SUB_ID_1);
        verify(mSubscriptionManager, times(1)).addSubscriptionInfoRecord(
                eq("89012604200000000000"), eq(FAKE_SUB_ID_1));
                eq(FAKE_ICCID_1), eq(FAKE_SUB_ID_1));
        verify(mSubscriptionController, times(1)).notifySubscriptionInfoChanged();
        verify(mSubscriptionController, times(1)).setMccMnc(FAKE_MCC_MNC_1, FAKE_SUB_ID_1);
        verify(mSubscriptionController, times(0)).clearSubInfo();
@@ -414,7 +442,7 @@ public class SubscriptionInfoUpdaterTest extends TelephonyTest {
    @Test
    @SmallTest
    public void testSimLoadedEmptyOperatorNumeric() throws Exception {
        doReturn("89012604200000000000").when(mIccRecord).getFullIccId();
        doReturn(FAKE_ICCID_1).when(mIccRecord).getFullIccId();
        // operator numeric is empty
        doReturn("").when(mTelephonyManager).getSimOperatorNumeric(FAKE_SUB_ID_1);
        doReturn(FAKE_SUB_ID_1).when(mSubInfo).getSubscriptionId();
@@ -428,7 +456,7 @@ public class SubscriptionInfoUpdaterTest extends TelephonyTest {
        SubscriptionManager mSubscriptionManager = SubscriptionManager.from(mContext);
        verify(mTelephonyManager).getSimOperatorNumeric(FAKE_SUB_ID_1);
        verify(mSubscriptionManager, times(1)).addSubscriptionInfoRecord(
                eq("89012604200000000000"), eq(FAKE_SUB_ID_1));
                eq(FAKE_ICCID_1), eq(FAKE_SUB_ID_1));
        verify(mSubscriptionController, times(1)).notifySubscriptionInfoChanged();
        verify(mSubscriptionController, times(0)).setMccMnc(anyString(), anyInt());
        verify(mSubscriptionController, times(0)).clearSubInfo();
@@ -488,7 +516,7 @@ public class SubscriptionInfoUpdaterTest extends TelephonyTest {
        doReturn(FAKE_MCC_MNC_1).when(mTelephonyManager).getSimOperatorNumeric(eq(FAKE_SUB_ID_1));
        doReturn(FAKE_MCC_MNC_2).when(mTelephonyManager).getSimOperatorNumeric(eq(FAKE_SUB_ID_2));
        verify(mSubscriptionController, times(0)).clearSubInfo();
        doReturn("89012604200000000000").when(mIccRecord).getFullIccId();
        doReturn(FAKE_ICCID_1).when(mIccRecord).getFullIccId();
        SubscriptionManager mSubscriptionManager = SubscriptionManager.from(mContext);
        verify(mSubscriptionManager, times(0)).addSubscriptionInfoRecord(anyString(), anyInt());
        verify(mSubscriptionController, times(0)).notifySubscriptionInfoChanged();
@@ -516,7 +544,7 @@ public class SubscriptionInfoUpdaterTest extends TelephonyTest {
                IccCardConstants.INTENT_VALUE_ICC_LOADED, null, FAKE_SUB_ID_2);

        processAllMessages();
        verify(mSubscriptionManager, times(1)).addSubscriptionInfoRecord(eq("89012604200000000000"),
        verify(mSubscriptionManager, times(1)).addSubscriptionInfoRecord(eq(FAKE_ICCID_1),
                eq(FAKE_SUB_ID_1));
        verify(mSubscriptionManager, times(1)).addSubscriptionInfoRecord(eq("89012604200000000001"),
                eq(FAKE_SUB_ID_2));
@@ -533,7 +561,7 @@ public class SubscriptionInfoUpdaterTest extends TelephonyTest {
        doReturn("98106240020000000000").when(mIccRecord).getFullIccId();

        replaceInstance(SubscriptionInfoUpdater.class, "sIccId", null,
                new String[]{"89012604200000000000"});
                new String[]{FAKE_ICCID_1});

        mUpdater.updateInternalIccState(
                IccCardConstants.INTENT_VALUE_ICC_LOCKED, "TESTING", FAKE_SUB_ID_1);