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

Commit 12cd5129 authored by Junyu Lai's avatar Junyu Lai Committed by Automerger Merge Worker
Browse files

Merge "Support dynamically update IMSI" am: 5d2e89e0 am: 9b8855c0 am: 860b9660 am: 5537ae92

Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/1418348

Change-Id: I5e5fc106aaab1b55302c38717cad9dbf1281baec
parents 86592a6a 5537ae92
Loading
Loading
Loading
Loading
+30 −23
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.CollectionUtils;
@@ -94,41 +95,43 @@ public class NetworkStatsSubscriptionsMonitor extends
        // also needed to track CBRS.
        final List<Integer> newSubs = getActiveSubIdList(mSubscriptionManager);

        for (final int subId : newSubs) {
            final RatTypeListener match = CollectionUtils.find(mRatListeners,
                    it -> it.mSubId == subId);
            if (match != null) continue;

            // Create listener for every newly added sub. Also store subscriberId into it to
            // prevent binder call to telephony when querying RAT. If the subscriberId is empty
            // for any reason, such as SIM PIN locked, skip registration.
            // SubscriberId will be unavailable again if 1. modem crashed 2. reboot
            // 3. re-insert SIM. If that happens, the listeners will be eventually synchronized
            // with active sub list once all subscriberIds are ready.
        // IMSI is needed for every newly added sub. Listener stores subscriberId into it to
        // prevent binder call to telephony when querying RAT. Keep listener registration with empty
        // IMSI is meaningless since the RAT type changed is ambiguous for multi-SIM if reported
        // with empty IMSI. So filter the subs w/o a valid IMSI to prevent such registration.
        final List<Pair<Integer, String>> filteredNewSubs =
                CollectionUtils.mapNotNull(newSubs, subId -> {
                    final String subscriberId = mTeleManager.getSubscriberId(subId);
            if (TextUtils.isEmpty(subscriberId)) {
                Log.d(NetworkStatsService.TAG, "Empty subscriberId for newly added sub "
                        + subId + ", skip listener registration");
                    return TextUtils.isEmpty(subscriberId) ? null : new Pair(subId, subscriberId);
                });

        for (final Pair<Integer, String> sub : filteredNewSubs) {
            // Fully match listener with subId and IMSI, since in some rare cases, IMSI might be
            // suddenly change regardless of subId, such as switch IMSI feature in modem side.
            // If that happens, register new listener with new IMSI and remove old one later.
            if (CollectionUtils.find(mRatListeners,
                    it -> it.equalsKey(sub.first, sub.second)) != null) {
                continue;
            }

            final RatTypeListener listener =
                    new RatTypeListener(mExecutor, this, subId, subscriberId);
                    new RatTypeListener(mExecutor, this, sub.first, sub.second);
            mRatListeners.add(listener);

            // Register listener to the telephony manager that associated with specific sub.
            mTeleManager.createForSubscriptionId(subId)
            mTeleManager.createForSubscriptionId(sub.first)
                    .listen(listener, PhoneStateListener.LISTEN_SERVICE_STATE);
            Log.d(NetworkStatsService.TAG, "RAT type listener registered for sub " + subId);
            Log.d(NetworkStatsService.TAG, "RAT type listener registered for sub " + sub.first);
        }

        for (final RatTypeListener listener : new ArrayList<>(mRatListeners)) {
            // If the new list contains the subId of the listener, keeps it.
            final Integer match = CollectionUtils.find(newSubs, it -> it == listener.mSubId);
            if (match != null) continue;

            // If there is no subId and IMSI matched the listener, removes it.
            if (CollectionUtils.find(filteredNewSubs,
                    it -> listener.equalsKey(it.first, it.second)) == null) {
                handleRemoveRatTypeListener(listener);
            }
        }
    }

    @NonNull
    private List<Integer> getActiveSubIdList(@NonNull SubscriptionManager subscriptionManager) {
@@ -232,5 +235,9 @@ public class NetworkStatsSubscriptionsMonitor extends
        public int getSubId() {
            return mSubId;
        }

        boolean equalsKey(int subId, @NonNull String subscriberId) {
            return mSubId == subId && TextUtils.equals(mSubscriberId, subscriberId);
        }
    }
}
+70 −11
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -150,7 +151,7 @@ public final class NetworkStatsSubscriptionsMonitorTest {
    }

    private void assertRatTypeChangedForSub(String subscriberId, int ratType) {
        assertEquals(mMonitor.getRatTypeForSubscriberId(subscriberId), ratType);
        assertEquals(ratType, mMonitor.getRatTypeForSubscriberId(subscriberId));
        final ArgumentCaptor<Integer> typeCaptor = ArgumentCaptor.forClass(Integer.class);
        // Verify callback with the subscriberId and the RAT type should be as expected.
        // It will fail if get a callback with an unexpected RAT type.
@@ -302,26 +303,84 @@ public final class NetworkStatsSubscriptionsMonitorTest {
        reset(mDelegate);

        // Set IMSI to null again to simulate somehow IMSI is not available, such as
        // modem crash. Verify service should not unregister listener.
        // modem crash. Verify service should unregister listener.
        updateSubscriberIdForTestSub(TEST_SUBID1, null);
        verify(mTelephonyManager, never()).listen(any(), anyInt());
        assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UMTS);
        verify(mTelephonyManager, times(1)).listen(eq(ratTypeListenerCaptor.getValue()),
                eq(PhoneStateListener.LISTEN_NONE));
        assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN);
        reset(mDelegate);
        clearInvocations(mTelephonyManager);

        // Set RAT type of sim1 to LTE. Verify RAT type of sim1 is still changed even if the IMSI
        // is not available. The monitor keeps the listener even if the IMSI disappears because
        // the IMSI can never change for any given subId, therefore even if the IMSI is updated
        // to null, the monitor should continue accepting updates of the RAT type. However,
        // telephony is never actually supposed to do this, if the IMSI disappears there should
        // not be updates, but it's still the right thing to do theoretically.
        setRatTypeForSub(ratTypeListenerCaptor.getAllValues(), TEST_SUBID1,
        // Simulate somehow IMSI is back. Verify service will register with
        // another listener and fire callback accordingly.
        final ArgumentCaptor<RatTypeListener> ratTypeListenerCaptor2 =
                ArgumentCaptor.forClass(RatTypeListener.class);
        updateSubscriberIdForTestSub(TEST_SUBID1, TEST_IMSI1);
        verify(mTelephonyManager, times(1)).listen(ratTypeListenerCaptor2.capture(),
                eq(PhoneStateListener.LISTEN_SERVICE_STATE));
        assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN);
        reset(mDelegate);
        clearInvocations(mTelephonyManager);

        // Set RAT type of sim1 to LTE. Verify RAT type of sim1 still works.
        setRatTypeForSub(ratTypeListenerCaptor2.getAllValues(), TEST_SUBID1,
                TelephonyManager.NETWORK_TYPE_LTE);
        assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_LTE);
        reset(mDelegate);

        mMonitor.stop();
        verify(mTelephonyManager, times(1)).listen(eq(ratTypeListenerCaptor2.getValue()),
                eq(PhoneStateListener.LISTEN_NONE));
        assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN);
    }

    /**
     * Verify that when IMSI suddenly changed for a given subId, the service will register a new
     * listener and unregister the old one, and report changes on updated IMSI. This is for modem
     * feature that may be enabled for certain carrier, which changes to use a different IMSI while
     * roaming on certain networks for multi-IMSI SIM cards, but the subId stays the same.
     */
    @Test
    public void testSubscriberIdChanged() {
        mMonitor.start();
        // Insert sim1, verify RAT type is NETWORK_TYPE_UNKNOWN, and never get any callback
        // before changing RAT type.
        addTestSub(TEST_SUBID1, TEST_IMSI1);
        final ArgumentCaptor<RatTypeListener> ratTypeListenerCaptor =
                ArgumentCaptor.forClass(RatTypeListener.class);
        verify(mTelephonyManager, times(1)).listen(ratTypeListenerCaptor.capture(),
                eq(PhoneStateListener.LISTEN_SERVICE_STATE));
        assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN);

        // Set RAT type of sim1 to UMTS.
        // Verify RAT type of sim1 changes accordingly.
        setRatTypeForSub(ratTypeListenerCaptor.getAllValues(), TEST_SUBID1,
                TelephonyManager.NETWORK_TYPE_UMTS);
        assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UMTS);
        reset(mDelegate);
        clearInvocations(mTelephonyManager);

        // Simulate IMSI of sim1 changed to IMSI2. Verify the service will register with
        // another listener and remove the old one. The RAT type of new IMSI stays at
        // NETWORK_TYPE_UNKNOWN until received initial callback from telephony.
        final ArgumentCaptor<RatTypeListener> ratTypeListenerCaptor2 =
                ArgumentCaptor.forClass(RatTypeListener.class);
        updateSubscriberIdForTestSub(TEST_SUBID1, TEST_IMSI2);
        verify(mTelephonyManager, times(1)).listen(ratTypeListenerCaptor2.capture(),
                eq(PhoneStateListener.LISTEN_SERVICE_STATE));
        verify(mTelephonyManager, times(1)).listen(eq(ratTypeListenerCaptor.getValue()),
                eq(PhoneStateListener.LISTEN_NONE));
        assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN);
        assertRatTypeNotChangedForSub(TEST_IMSI2, TelephonyManager.NETWORK_TYPE_UNKNOWN);
        reset(mDelegate);

        // Set RAT type of sim1 to UMTS for new listener to simulate the initial callback received
        // from telephony after registration. Verify RAT type of sim1 changes with IMSI2
        // accordingly.
        setRatTypeForSub(ratTypeListenerCaptor2.getAllValues(), TEST_SUBID1,
                TelephonyManager.NETWORK_TYPE_UMTS);
        assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN);
        assertRatTypeChangedForSub(TEST_IMSI2, TelephonyManager.NETWORK_TYPE_UMTS);
        reset(mDelegate);
    }
}