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

Commit 17433c9e authored by Jordan Liu's avatar Jordan Liu Committed by Gerrit Code Review
Browse files

Merge "Prefer non-removable eUICCs for default"

parents af6c6226 0471cb1d
Loading
Loading
Loading
Loading
+70 −19
Original line number Diff line number Diff line
@@ -155,6 +155,9 @@ public class UiccController extends Handler {
    // SharedPreference key for saving the known card strings (ICCIDs and EIDs) ordered by card ID
    private static final String CARD_STRINGS = "card_strings";

    // Whether the device has an eUICC built in.
    private boolean mHasBuiltInEuicc = false;

    // SharedPreferences key for saving the default euicc card ID
    private static final String DEFAULT_CARD = "default_card";

@@ -162,7 +165,8 @@ public class UiccController extends Handler {
    private static final Object mLock = new Object();
    @UnsupportedAppUsage
    private static UiccController mInstance;
    private static ArrayList<IccSlotStatus> sLastSlotStatus;
    @VisibleForTesting
    public static ArrayList<IccSlotStatus> sLastSlotStatus;

    @UnsupportedAppUsage
    @VisibleForTesting
@@ -230,6 +234,8 @@ public class UiccController extends Handler {
        mLauncher = new UiccStateChangedLauncher(c, this);
        mCardStrings = loadCardStrings();
        mDefaultEuiccCardId = UNINITIALIZED_CARD_ID;

        mHasBuiltInEuicc = hasBuiltInEuicc();
    }

    /**
@@ -789,7 +795,6 @@ public class UiccController extends Handler {
        int numActiveSlots = 0;
        boolean isDefaultEuiccCardIdSet = false;
        boolean anyEuiccIsActive = false;
        boolean hasEuicc = false;
        for (int i = 0; i < status.size(); i++) {
            IccSlotStatus iss = status.get(i);
            boolean isActive = (iss.slotState == IccSlotStatus.SlotState.SLOTSTATE_ACTIVE);
@@ -821,7 +826,6 @@ public class UiccController extends Handler {
            }

            if (mUiccSlots[i].isEuicc()) {
                hasEuicc = true;
                if (isActive) {
                    anyEuiccIsActive = true;
                }
@@ -833,11 +837,10 @@ public class UiccController extends Handler {

                addCardId(eid);

                // whenever slot status is received, set default card to the eUICC with the
                // lowest slot index.
                if (!isDefaultEuiccCardIdSet) {
                // whenever slot status is received, set default card to the non-removable eUICC
                // with the lowest slot index.
                if (!mUiccSlots[i].isRemovable() && !isDefaultEuiccCardIdSet) {
                    isDefaultEuiccCardIdSet = true;
                    // TODO(b/122738148) the default eUICC should not be removable
                    mDefaultEuiccCardId = convertToPublicCardId(eid);
                    String logStr = "Using eid=" + eid + " in slot=" + i
                            + " to set mDefaultEuiccCardId=" + mDefaultEuiccCardId;
@@ -847,11 +850,37 @@ public class UiccController extends Handler {
            }
        }

        if (hasEuicc && !anyEuiccIsActive && !isDefaultEuiccCardIdSet) {
        if (mHasBuiltInEuicc && !anyEuiccIsActive && !isDefaultEuiccCardIdSet) {
            log("onGetSlotStatusDone: setting TEMPORARILY_UNSUPPORTED_CARD_ID");
            isDefaultEuiccCardIdSet = true;
            mDefaultEuiccCardId = TEMPORARILY_UNSUPPORTED_CARD_ID;
        }

        if (!mHasBuiltInEuicc && !isDefaultEuiccCardIdSet) {
            // if there are no built-in eUICCs, then consider setting a removable eUICC to the
            // default.
            // Note that on HAL<1.2, it's possible that a built-in eUICC exists, but does not
            // correspond to any slot in mUiccSlots. This logic is still safe in that case because
            // SlotStatus is only for HAL >= 1.2
            for (int i = 0; i < status.size(); i++) {
                if (mUiccSlots[i].isEuicc()) {
                    isDefaultEuiccCardIdSet = true;
                    String eid = status.get(i).eid;
                    if (!TextUtils.isEmpty(eid)) {
                        mDefaultEuiccCardId = convertToPublicCardId(eid);
                        log("Using eid=" + eid + " from removable eUICC in slot="
                                + i + " to set mDefaultEuiccCardId=" + mDefaultEuiccCardId);
                        break;
                    }
                }
            }
        }

        if (!isDefaultEuiccCardIdSet) {
            // no eUICCs at all
            mDefaultEuiccCardId = UNINITIALIZED_CARD_ID;
        }

        if (VDBG) logPhoneIdToSlotIdMapping();

        // sanity check: number of active slots should be valid
@@ -954,8 +983,9 @@ public class UiccController extends Handler {
        mCis[index].getIccCardStatus(obtainMessage(EVENT_GET_ICC_STATUS_DONE, index));
    }

    // for RadioConfig 1.2 or higher, the EID comes with the IccSlotStatus
    // for RadioConfig<1.2 we register for EID ready set mCardStrings and mDefaultEuiccCardId here
    // for HAL 1.2-1.3 we register for EID ready, set mCardStrings and mDefaultEuiccCardId here.
    // Note that if there are multiple eUICCs on HAL 1.2-1.3, the default eUICC is the one whose EID
    // is first loaded
    private void onEidReady(AsyncResult ar, Integer index) {
        if (ar.exception != null) {
            Rlog.e(LOG_TAG, "onEidReady: exception: " + ar.exception);
@@ -967,25 +997,45 @@ public class UiccController extends Handler {
            return;
        }
        int slotId = mPhoneIdToSlotId[index];
        UiccCard card = mUiccSlots[slotId].getUiccCard();
        EuiccCard card = (EuiccCard) mUiccSlots[slotId].getUiccCard();
        if (card == null) {
            Rlog.e(LOG_TAG, "onEidReady: UiccCard in slot " + slotId + " is null");
            return;
        }

        // set mCardStrings and the defaultEuiccCardId using the now available EID
        String eid = ((EuiccCard) card).getEid();
        String eid = card.getEid();
        addCardId(eid);
        if (mDefaultEuiccCardId == UNINITIALIZED_CARD_ID
                || mDefaultEuiccCardId == TEMPORARILY_UNSUPPORTED_CARD_ID) {
            // TODO(b/122738148) the default eUICC should not be removable
            if (!mUiccSlots[slotId].isRemovable()) {
                mDefaultEuiccCardId = convertToPublicCardId(eid);
            String logStr = "onEidReady: eid=" + eid + " slot=" + slotId + " mDefaultEuiccCardId="
                    + mDefaultEuiccCardId;
                String logStr = "onEidReady: eid=" + eid + " slot=" + slotId
                        + " mDefaultEuiccCardId=" + mDefaultEuiccCardId;
                sLocalLog.log(logStr);
                log(logStr);
            } else if (!mHasBuiltInEuicc) {
                // we only set a removable eUICC to the default if there are no non-removable eUICCs
                mDefaultEuiccCardId = convertToPublicCardId(eid);
                String logStr = "onEidReady: eid=" + eid + " from removable eUICC in slot="
                        + slotId + " mDefaultEuiccCardId=" + mDefaultEuiccCardId;
                sLocalLog.log(logStr);
                log(logStr);
            }
        }
        ((EuiccCard) card).unregisterForEidReady(this);
        card.unregisterForEidReady(this);
    }

    // Return true if the device has at least one built in eUICC based on the resource overlay
    private boolean hasBuiltInEuicc() {
        int[] euiccSlots = mContext.getResources()
                .getIntArray(com.android.internal.R.array.non_removable_euicc_slots);
        if (euiccSlots == null || euiccSlots.length == 0) {
            log("hasBuiltInEuicc: non_removable_euicc_slots resource is empty; returning false.");
            return false;
        }
        log("hasBuiltInEuicc: non_removable_euicc_slots resource not empty, returning true");
        return true;
    }

    /**
@@ -1029,6 +1079,7 @@ public class UiccController extends Handler {
        pw.println();
        pw.flush();
        pw.println(" mIsCdmaSupported=" + isCdmaSupported(mContext));
        pw.println(" mHasBuiltInEuicc=" + mHasBuiltInEuicc);
        pw.println(" mUiccSlots: size=" + mUiccSlots.length);
        pw.println(" mCardStrings=" + mCardStrings);
        pw.println(" mDefaultEuiccCardId=" + mDefaultEuiccCardId);
+1 −0
Original line number Diff line number Diff line
@@ -423,6 +423,7 @@ public class UiccSlot extends Handler {
        pw.println(" mCi=" + mCi);
        pw.println(" mActive=" + mActive);
        pw.println(" mIsEuicc=" + mIsEuicc);
        pw.println(" mIsRemovable=" + mIsRemovable);
        pw.println(" mLastRadioState=" + mLastRadioState);
        pw.println(" mIccId=" + mIccId);
        pw.println(" mCardState=" + mCardState);
+157 −0
Original line number Diff line number Diff line
@@ -17,9 +17,12 @@ package com.android.internal.telephony.uicc;

import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;

import static junit.framework.Assert.fail;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.doReturn;
@@ -30,6 +33,7 @@ import android.os.AsyncResult;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.preference.PreferenceManager;
import android.telephony.TelephonyManager;
import android.telephony.UiccCardInfo;

@@ -62,6 +66,8 @@ public class UiccControllerTest extends TelephonyTest {
    @Mock
    private UiccSlot mMockSlot;
    @Mock
    private UiccSlot mMockRemovableEuiccSlot;
    @Mock
    private UiccCard mMockCard;
    @Mock
    private EuiccCard mMockEuiccCard;
@@ -95,6 +101,9 @@ public class UiccControllerTest extends TelephonyTest {
    @Before
    public void setUp() throws Exception {
        super.setUp(this.getClass().getSimpleName());
        // some tests use use the shared preferences in the runner context, so reset them here
        PreferenceManager.getDefaultSharedPreferences(InstrumentationRegistry.getContext())
                .edit().clear().commit();

        doReturn(PHONE_COUNT).when(mTelephonyManager).getPhoneCount();
        doReturn(PHONE_COUNT).when(mTelephonyManager).getSimCount();
@@ -115,6 +124,8 @@ public class UiccControllerTest extends TelephonyTest {
        mUiccControllerHandlerThread = new UiccControllerHandlerThread(TAG);
        mUiccControllerHandlerThread.start();
        waitUntilReady();
        // reset sLastSlotStatus so that onGetSlotStatusDone always sees a change in the slot status
        mUiccControllerUT.sLastSlotStatus = null;
        /* expected to get new UiccCards being created
        wait till the async result and message delay */
        waitForMs(100);
@@ -126,6 +137,23 @@ public class UiccControllerTest extends TelephonyTest {
        super.tearDown();
    }

    /**
     * Replace num slots and euicc slots resources and reinstantiate the UiccController
     */
    private void reconfigureSlots(int numSlots, int[] nonRemovableEuiccSlots) throws Exception {
        mContextFixture.putIntResource(com.android.internal.R.integer.config_num_physical_slots,
                numSlots);
        mContextFixture.putIntArrayResource(
                com.android.internal.R.array.non_removable_euicc_slots,
                nonRemovableEuiccSlots);
        replaceInstance(UiccController.class, "mInstance", null, null);
        mUiccControllerHandlerThread.quit();
        mUiccControllerHandlerThread = new UiccControllerHandlerThread(TAG);
        mUiccControllerHandlerThread.start();
        waitUntilReady();
        waitForMs(100);
    }

    @Test @SmallTest
    public void testSanity() {
        // radio power is expected to be on which should trigger icc card and slot status requests
@@ -395,4 +423,133 @@ public class UiccControllerTest extends TelephonyTest {
        assertEquals(TelephonyManager.UNSUPPORTED_CARD_ID,
                mUiccControllerUT.getCardIdForDefaultEuicc());
    }

    /**
     * The default eUICC should not be the removable slot if there is a built-in eUICC.
     */
    @Test
    public void testDefaultEuiccIsNotRemovable() {
        try {
            reconfigureSlots(2, new int[]{ 1 } /* non-removable slot */);
        } catch (Exception e) {
            fail("Unable to reconfigure slots.");
        }

        // Give UiccController a real context so it can use shared preferences
        mUiccControllerUT.mContext = InstrumentationRegistry.getContext();

        // Mock out UiccSlots so that [0] is a removable eUICC and [1] is built-in
        mUiccControllerUT.mUiccSlots[0] = mMockRemovableEuiccSlot;
        doReturn(true).when(mMockRemovableEuiccSlot).isEuicc();
        doReturn(true).when(mMockRemovableEuiccSlot).isRemovable();
        mUiccControllerUT.mUiccSlots[1] = mMockSlot;
        doReturn(true).when(mMockSlot).isEuicc();
        doReturn(false).when(mMockSlot).isRemovable();

        // simulate slot status loaded so that the UiccController sets the card ID
        IccSlotStatus iss1 = new IccSlotStatus();
        iss1.setSlotState(1 /* active */);
        iss1.eid = "AB123456";
        IccSlotStatus iss2 = new IccSlotStatus();
        iss2.setSlotState(1 /* active */);
        iss2.eid = "ZYW13094";
        ArrayList<IccSlotStatus> status = new ArrayList<IccSlotStatus>();
        status.add(iss1);
        status.add(iss2);
        AsyncResult ar = new AsyncResult(null, status, null);
        Message msg = Message.obtain(mUiccControllerUT, EVENT_GET_SLOT_STATUS_DONE, ar);
        mUiccControllerUT.handleMessage(msg);

        // assert that the default eUICC is the non-removable eUICC
        assertEquals(mUiccControllerUT.convertToPublicCardId(iss2.eid),
                mUiccControllerUT.getCardIdForDefaultEuicc());
        assertTrue(mUiccControllerUT.convertToPublicCardId(iss2.eid) >= 0);
    }

    /**
     * The default eUICC should not be the removable slot if there is a built-in eUICC. This should
     * not depend on the order of the slots.
     */
    @Test
    public void testDefaultEuiccIsNotRemovable_swapSlotOrder() {
        try {
            reconfigureSlots(2, new int[]{ 0 } /* non-removable slot */);
        } catch (Exception e) {
            fail("Unable to reconfigure slots.");
        }

        // Give UiccController a real context so it can use shared preferences
        mUiccControllerUT.mContext = InstrumentationRegistry.getContext();

        // Mock out UiccSlots so that [0] is a built-in eUICC and [1] is removable
        mUiccControllerUT.mUiccSlots[0] = mMockSlot;
        doReturn(true).when(mMockSlot).isEuicc();
        doReturn(false).when(mMockSlot).isRemovable();
        mUiccControllerUT.mUiccSlots[1] = mMockRemovableEuiccSlot;
        doReturn(true).when(mMockRemovableEuiccSlot).isEuicc();
        doReturn(true).when(mMockRemovableEuiccSlot).isRemovable();

        // simulate slot status loaded so that the UiccController sets the card ID
        IccSlotStatus iss1 = new IccSlotStatus();
        iss1.setSlotState(1 /* active */);
        iss1.eid = "AB123456";
        IccSlotStatus iss2 = new IccSlotStatus();
        iss2.setSlotState(1 /* active */);
        iss2.eid = "ZYW13094";
        ArrayList<IccSlotStatus> status = new ArrayList<IccSlotStatus>();
        status.add(iss1);
        status.add(iss2);
        AsyncResult ar = new AsyncResult(null, status, null);
        Message msg = Message.obtain(mUiccControllerUT, EVENT_GET_SLOT_STATUS_DONE, ar);
        mUiccControllerUT.handleMessage(msg);

        // assert that the default eUICC is the non-removable eUICC
        assertEquals(mUiccControllerUT.convertToPublicCardId(iss1.eid),
                mUiccControllerUT.getCardIdForDefaultEuicc());
        assertTrue(mUiccControllerUT.convertToPublicCardId(iss1.eid) >= 0);
    }

    /**
     * The default eUICC should not be the removable slot if there is a built-in eUICC, even if that
     * eUICC is inactive.
     * When there is a built-in eUICC which is inactive, we set the mDefaultEuiccCardId to
     * TEMPORARILY_UNSUPPORTED_CARD_ID.
     */
    @Test
    public void testDefaultEuiccIsNotRemovable_EuiccIsInactive() {
        try {
            reconfigureSlots(2, new int[]{ 1 } /* non-removable slot */);
        } catch (Exception e) {
            fail();
        }

        // Give UiccController a real context so it can use shared preferences
        mUiccControllerUT.mContext = InstrumentationRegistry.getContext();

        // Mock out UiccSlots. Slot 0 is inactive here.
        mUiccControllerUT.mUiccSlots[0] = mMockSlot;
        doReturn(true).when(mMockSlot).isEuicc();
        doReturn(false).when(mMockSlot).isRemovable();
        mUiccControllerUT.mUiccSlots[1] = mMockRemovableEuiccSlot;
        doReturn(true).when(mMockSlot).isEuicc();
        doReturn(true).when(mMockSlot).isRemovable();

        // simulate slot status loaded so that the UiccController sets the card ID
        IccSlotStatus iss1 = new IccSlotStatus();
        iss1.setSlotState(0 /* active */);
        iss1.eid = "AB123456";
        IccSlotStatus iss2 = new IccSlotStatus();
        iss2.setSlotState(1 /* active */);
        iss2.eid = "ZYW13094";
        ArrayList<IccSlotStatus> status = new ArrayList<IccSlotStatus>();
        status.add(iss1);
        status.add(iss2);
        AsyncResult ar = new AsyncResult(null, status, null);
        Message msg = Message.obtain(mUiccControllerUT, EVENT_GET_SLOT_STATUS_DONE, ar);
        mUiccControllerUT.handleMessage(msg);

        // assert that the default eUICC cardId is TEMPORARILY_UNSUPPORTED_CARD_ID
        assertEquals(TelephonyManager.UNSUPPORTED_CARD_ID,
                mUiccControllerUT.getCardIdForDefaultEuicc());
    }
}