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

Commit 4e3372b3 authored by Holly Jiuyu Sun's avatar Holly Jiuyu Sun
Browse files

Run SubscriptionInfoUpdater on main thread.

Run GetEuiccProfileInfoList should be run on background thread. Post the
following operations back to main thread.

Bug: 131695762
Test: test on phone
Change-Id: I7d819d50f568561e0dff6250da61b8f157b315ca
parent 55357995
Loading
Loading
Loading
Loading
+104 −37
Original line number Diff line number Diff line
@@ -66,7 +66,6 @@ import com.android.internal.telephony.uicc.UiccSlot;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
@@ -113,12 +112,26 @@ public class SubscriptionInfoUpdater extends Handler {
    private EuiccManager mEuiccManager;
    @UnsupportedAppUsage
    private IPackageManager mPackageManager;
    private Handler mBackgroundHandler;

    // The current foreground user ID.
    @UnsupportedAppUsage
    private int mCurrentlyActiveUserId;
    private CarrierServiceBindHelper mCarrierServiceBindHelper;

    /**
     * Runnable with a boolean parameter. This is used in
     * updateEmbeddedSubscriptions(List<Integer> cardIds, @Nullable UpdateEmbeddedSubsCallback).
     */
    private interface UpdateEmbeddedSubsCallback {
        /**
         * Callback of the Runnable.
         * @param hasChanges Whether there is any subscription info change. If yes, we need to
         * notify the listeners.
         */
        void run(boolean hasChanges);
    }

    // TODO: The SubscriptionController instance should be passed in here from PhoneFactory
    // rather than invoking the static getter all over the place.
    public SubscriptionInfoUpdater(
@@ -130,8 +143,8 @@ public class SubscriptionInfoUpdater extends Handler {
    @VisibleForTesting public SubscriptionInfoUpdater(
            Looper looper, Context context, Phone[] phone,
            CommandsInterface[] ci, IPackageManager packageMgr) {
        super(looper);
        logd("Constructor invoked");
        mBackgroundHandler = new Handler(looper);

        mContext = context;
        mPhone = phone;
@@ -228,6 +241,7 @@ public class SubscriptionInfoUpdater extends Handler {

    @Override
    public void handleMessage(Message msg) {
        List<Integer> cardIds = new ArrayList<>();
        switch (msg.what) {
            case EVENT_GET_NETWORK_SELECTION_MODE_DONE: {
                AsyncResult ar = (AsyncResult)msg.obj;
@@ -279,6 +293,12 @@ public class SubscriptionInfoUpdater extends Handler {
                break;

            case EVENT_SIM_READY:
                cardIds.add(getCardIdFromPhoneId(msg.arg1));
                updateEmbeddedSubscriptions(cardIds, (hasChanges) -> {
                    if (hasChanges) {
                        SubscriptionController.getInstance().notifySubscriptionInfoChanged();
                    }
                });
                broadcastSimStateChanged(msg.arg1, IccCardConstants.INTENT_VALUE_ICC_READY, null);
                broadcastSimCardStateChanged(msg.arg1, TelephonyManager.SIM_STATE_PRESENT);
                broadcastSimApplicationStateChanged(msg.arg1, TelephonyManager.SIM_STATE_NOT_READY);
@@ -289,22 +309,27 @@ public class SubscriptionInfoUpdater extends Handler {
                break;

            case EVENT_SIM_NOT_READY:
                handleSimNotReady(msg.arg1);
                int cardId = getCardIdFromPhoneId(msg.arg1);
                // an eUICC with no active subscriptions never becomes ready, so we need to trigger
                // the embedded subscriptions update here
                if (updateEmbeddedSubscriptions(cardId)) {
                cardIds.add(getCardIdFromPhoneId(msg.arg1));
                updateEmbeddedSubscriptions(cardIds, (hasChanges) -> {
                    if (hasChanges) {
                        SubscriptionController.getInstance().notifySubscriptionInfoChanged();
                    }
                });
                handleSimNotReady(msg.arg1);
                break;

            case EVENT_REFRESH_EMBEDDED_SUBSCRIPTIONS:
                if (updateEmbeddedSubscriptions(msg.arg1)) {
                cardIds.add(msg.arg1);
                updateEmbeddedSubscriptions(cardIds, (hasChanges) -> {
                    if (hasChanges) {
                        SubscriptionController.getInstance().notifySubscriptionInfoChanged();
                    }
                    if (msg.obj != null) {
                        ((Runnable) msg.obj).run();
                    }
                });
                break;

            default:
@@ -353,7 +378,7 @@ public class SubscriptionInfoUpdater extends Handler {
            logd("NOT Querying IccId its already set sIccid[" + slotId + "]=" + iccId);
        }

        updateSubscriptionInfoByIccId(slotId);
        updateSubscriptionInfoByIccId(slotId, true /* updateEmbeddedSubs */);

        broadcastSimStateChanged(slotId, IccCardConstants.INTENT_VALUE_ICC_LOCKED, reason);
        broadcastSimCardStateChanged(slotId, TelephonyManager.SIM_STATE_PRESENT);
@@ -387,7 +412,7 @@ public class SubscriptionInfoUpdater extends Handler {
            // phase, the subscription list is accessible. Treating NOT_READY
            // as equivalent to ABSENT, once the rest of the system can handle it.
            mIccId[slotId] = ICCID_STRING_FOR_NO_SIM;
            updateSubscriptionInfoByIccId(slotId);
            updateSubscriptionInfoByIccId(slotId, false /* updateEmbeddedSubs */);
        }

        broadcastSimStateChanged(slotId, IccCardConstants.INTENT_VALUE_ICC_NOT_READY,
@@ -419,7 +444,7 @@ public class SubscriptionInfoUpdater extends Handler {
        }
        mIccId[slotId] = IccUtils.stripTrailingFs(records.getFullIccId());

        updateSubscriptionInfoByIccId(slotId);
        updateSubscriptionInfoByIccId(slotId, true /* updateEmbeddedSubs */);
        List<SubscriptionInfo> subscriptionInfos = SubscriptionController.getInstance()
                .getSubInfoUsingSlotIndexPrivileged(slotId);
        if (subscriptionInfos == null || subscriptionInfos.isEmpty()) {
@@ -541,7 +566,7 @@ public class SubscriptionInfoUpdater extends Handler {
            logd("SIM" + (slotId + 1) + " hot plug out, absentAndInactive=" + absentAndInactive);
        }
        mIccId[slotId] = ICCID_STRING_FOR_NO_SIM;
        updateSubscriptionInfoByIccId(slotId);
        updateSubscriptionInfoByIccId(slotId, true /* updateEmbeddedSubs */);
        // Do not broadcast if the SIM is absent and inactive, because the logical slotId here is
        // no longer correct
        if (absentAndInactive == 0) {
@@ -558,7 +583,7 @@ public class SubscriptionInfoUpdater extends Handler {
            logd("SIM" + (slotId + 1) + " Error ");
        }
        mIccId[slotId] = ICCID_STRING_FOR_NO_SIM;
        updateSubscriptionInfoByIccId(slotId);
        updateSubscriptionInfoByIccId(slotId, true /* updateEmbeddedSubs */);
        broadcastSimStateChanged(slotId, IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR,
                IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR);
        broadcastSimCardStateChanged(slotId, TelephonyManager.SIM_STATE_CARD_IO_ERROR);
@@ -567,7 +592,8 @@ public class SubscriptionInfoUpdater extends Handler {
        updateCarrierServices(slotId, IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR);
    }

    synchronized private void updateSubscriptionInfoByIccId(int slotIndex) {
    private synchronized void updateSubscriptionInfoByIccId(int slotIndex,
            boolean updateEmbeddedSubs) {
        logd("updateSubscriptionInfoByIccId:+ Start");
        if (!SubscriptionManager.isValidSlotIndex(slotIndex)) {
            loge("[updateSubscriptionInfoByIccId]- invalid slotIndex=" + slotIndex);
@@ -621,20 +647,30 @@ public class SubscriptionInfoUpdater extends Handler {
            } else {
                logd("bypass reset default data sub if inactive");
            }
            setSubInfoInitialized();
        }

        UiccController uiccController = UiccController.getInstance();
        UiccSlot[] uiccSlots = uiccController.getUiccSlots();
            if (uiccSlots != null) {
                Arrays.stream(uiccSlots)
                        .filter(uiccSlot -> uiccSlot != null && uiccSlot.getUiccCard() != null)
                        .map(uiccSlot -> uiccController.convertToPublicCardId(
                                uiccSlot.getUiccCard().getCardId()))
                        .forEach(cardId -> updateEmbeddedSubscriptions(cardId));
        if (uiccSlots != null && updateEmbeddedSubs) {
            List<Integer> cardIds = new ArrayList<>();
            for (UiccSlot uiccSlot : uiccSlots) {
                if (uiccSlot != null && uiccSlot.getUiccCard() != null) {
                    int cardId = uiccController.convertToPublicCardId(
                            uiccSlot.getUiccCard().getCardId());
                    cardIds.add(cardId);
                }
            setSubInfoInitialized();
            }
            updateEmbeddedSubscriptions(cardIds, (hasChanges) -> {
                if (hasChanges) {
                    SubscriptionController.getInstance().notifySubscriptionInfoChanged();
                }
                if (DBG) logd("updateSubscriptionInfoByIccId: SubscriptionInfo update complete");
            });
        }

        SubscriptionController.getInstance().notifySubscriptionInfoChanged();
        logd("updateSubscriptionInfoByIccId:- SubscriptionInfo update complete");
        if (DBG) logd("updateSubscriptionInfoByIccId: SubscriptionInfo update complete");
    }

    private static void setSubInfoInitialized() {
@@ -655,25 +691,56 @@ public class SubscriptionInfoUpdater extends Handler {
    }

    /**
     * Update the cached list of embedded subscription for the eUICC with the given card ID
     * {@code cardId}.
     * Updates the cached list of embedded subscription for the eUICC with the given list of card
     * IDs {@code cardIds}. The step of reading the embedded subscription list from eUICC card is
     * executed in background thread. The callback {@code callback} is executed after the cache is
     * refreshed. The callback is executed in main thread.
     */
    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
    public void updateEmbeddedSubscriptions(List<Integer> cardIds,
            @Nullable UpdateEmbeddedSubsCallback callback) {
        mBackgroundHandler.post(() -> {
            List<GetEuiccProfileInfoListResult> results = new ArrayList<>();
            for (int cardId : cardIds) {
                GetEuiccProfileInfoListResult result =
                        EuiccController.get().blockingGetEuiccProfileInfoList(cardId);
                if (DBG) logd("blockingGetEuiccProfileInfoList cardId " + cardId);
                results.add(result);
            }

            // The runnable will be executed in the main thread.
            this.post(() -> {
                boolean hasChanges = false;
                for (GetEuiccProfileInfoListResult result : results) {
                    if (updateEmbeddedSubscriptionsCache(result)) {
                        hasChanges = true;
                    }
                }
                // The latest state in the main thread may be changed when the callback is
                // triggered.
                if (callback != null) {
                    callback.run(hasChanges);
                }
            });
        });
    }

    /**
     * Update the cached list of embedded subscription based on the passed in
     * GetEuiccProfileInfoListResult {@code result}.
     *
     * @return true if changes may have been made. This is not a guarantee that changes were made,
     * but notifications about subscription changes may be skipped if this returns false as an
     * optimization to avoid spurious notifications.
     */
    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
    public boolean updateEmbeddedSubscriptions(int cardId) {
        if (DBG) logd("updateEmbeddedSubscriptions");
    private boolean updateEmbeddedSubscriptionsCache(GetEuiccProfileInfoListResult result) {
        if (DBG) logd("updateEmbeddedSubscriptionsCache");
        // Do nothing if eUICCs are disabled. (Previous entries may remain in the cache, but they
        // are filtered out of list calls as long as EuiccManager.isEnabled returns false).
        if (!mEuiccManager.isEnabled()) {
            return false;
        }

        GetEuiccProfileInfoListResult result =
                EuiccController.get().blockingGetEuiccProfileInfoList(cardId);
        if (DBG) logd("blockingGetEuiccProfileInfoList cardId " + cardId);
        if (result == null) {
            // IPC to the eUICC controller failed.
            return false;
+12 −3
Original line number Diff line number Diff line
@@ -507,7 +507,12 @@ public class SubscriptionInfoUpdaterTest extends TelephonyTest {
        when(mSubscriptionController.getSubscriptionInfoListForEmbeddedSubscriptionUpdate(
                new String[] { "1", "3"}, false /* removable */)).thenReturn(subInfoList);

        assertTrue(mUpdater.updateEmbeddedSubscriptions(FAKE_CARD_ID));
        List<Integer> cardIds = new ArrayList<>();
        cardIds.add(FAKE_CARD_ID);
        mUpdater.updateEmbeddedSubscriptions(cardIds, null /* callback */);

        // Wait for some time until the callback is triggered.
        waitForMs(100);

        // 3 is new and so a new entry should have been created.
        verify(mSubscriptionController).insertEmptySubInfoRecord(
@@ -558,7 +563,9 @@ public class SubscriptionInfoUpdaterTest extends TelephonyTest {
        when(mSubscriptionController.getSubscriptionInfoListForEmbeddedSubscriptionUpdate(
                new String[0], false /* removable */)).thenReturn(subInfoList);

        assertFalse(mUpdater.updateEmbeddedSubscriptions(FAKE_CARD_ID));
        ArrayList<Integer> cardIds = new ArrayList<>(1);
        cardIds.add(FAKE_CARD_ID);
        mUpdater.updateEmbeddedSubscriptions(cardIds, null /* callback */);

        // No new entries should be created.
        verify(mSubscriptionController, times(0)).clearSubInfo();
@@ -586,7 +593,9 @@ public class SubscriptionInfoUpdaterTest extends TelephonyTest {
        when(mSubscriptionController.getSubscriptionInfoListForEmbeddedSubscriptionUpdate(
                new String[0], false /* removable */)).thenReturn(subInfoList);

        assertFalse(mUpdater.updateEmbeddedSubscriptions(FAKE_CARD_ID));
        ArrayList<Integer> cardIds = new ArrayList<>(1);
        cardIds.add(FAKE_CARD_ID);
        mUpdater.updateEmbeddedSubscriptions(cardIds, null /* callback */);

        // No new entries should be created.
        verify(mSubscriptionController, never()).insertEmptySubInfoRecord(anyString(), anyInt());