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

Commit 31d56f82 authored by Benedict Wong's avatar Benedict Wong
Browse files

Update TelephonySubscriptionTracker to use CarrierPrivilegesListener

This change adds use for CarrierPrivilegesListener to use the callbacks
as triggers to re-check carrier privileges. For simplicity, the carrier
privileges returned by the callback are not used, as it requires an
additional layer of caching. Instead, a query is performed, which
should hit a cache in the CarrierPrivilegesTracker, thus not be
performance-sensitive.

Bug: 183554244
Test: atest FrameworksVcnTests
Change-Id: Ia4529d087ee7f0deca7101c6849183ba7267c067
parent 525fc0dc
Loading
Loading
Loading
Loading
+93 −10
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import static android.telephony.CarrierConfigManager.EXTRA_SLOT_INDEX;
import static android.telephony.CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX;
import static android.telephony.SubscriptionManager.INVALID_SIM_SLOT_INDEX;
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
import static android.telephony.TelephonyManager.ACTION_MULTI_SIM_CONFIG_CHANGED;

import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -38,15 +39,19 @@ import android.telephony.SubscriptionManager;
import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
import android.telephony.TelephonyCallback;
import android.telephony.TelephonyManager;
import android.telephony.TelephonyManager.CarrierPrivilegesListener;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting.Visibility;
import com.android.internal.util.IndentingPrintWriter;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
@@ -92,6 +97,10 @@ public class TelephonySubscriptionTracker extends BroadcastReceiver {
    @NonNull private final Map<Integer, Integer> mReadySubIdsBySlotId = new HashMap<>();
    @NonNull private final OnSubscriptionsChangedListener mSubscriptionChangedListener;

    @NonNull
    private final List<CarrierPrivilegesListener> mCarrierPrivilegesChangedListeners =
            new ArrayList<>();

    @NonNull private TelephonySubscriptionSnapshot mCurrentSnapshot;

    public TelephonySubscriptionTracker(
@@ -126,22 +135,71 @@ public class TelephonySubscriptionTracker extends BroadcastReceiver {
                };
    }

    /** Registers the receivers, and starts tracking subscriptions. */
    /**
     * Registers the receivers, and starts tracking subscriptions.
     *
     * <p>Must always be run on the VcnManagementService thread.
     */
    public void register() {
        final HandlerExecutor executor = new HandlerExecutor(mHandler);
        final IntentFilter filter = new IntentFilter();
        filter.addAction(ACTION_CARRIER_CONFIG_CHANGED);
        filter.addAction(ACTION_MULTI_SIM_CONFIG_CHANGED);

        mContext.registerReceiver(
                this, new IntentFilter(ACTION_CARRIER_CONFIG_CHANGED), null, mHandler);
        mContext.registerReceiver(this, filter, null, mHandler);
        mSubscriptionManager.addOnSubscriptionsChangedListener(
                executor, mSubscriptionChangedListener);
        mTelephonyManager.registerTelephonyCallback(executor, mActiveDataSubIdListener);

        registerCarrierPrivilegesListeners();
    }

    /** Unregisters the receivers, and stops tracking subscriptions. */
    private void registerCarrierPrivilegesListeners() {
        final HandlerExecutor executor = new HandlerExecutor(mHandler);
        final int modemCount = mTelephonyManager.getActiveModemCount();
        try {
            for (int i = 0; i < modemCount; i++) {
                CarrierPrivilegesListener carrierPrivilegesListener =
                        new CarrierPrivilegesListener() {
                            @Override
                            public void onCarrierPrivilegesChanged(
                                    @NonNull List<String> privilegedPackageNames,
                                    @NonNull int[] privilegedUids) {
                                // Re-trigger the synchronous check (which is also very cheap due
                                // to caching in CarrierPrivilegesTracker). This allows consistency
                                // with the onSubscriptionsChangedListener and broadcasts.
                                handleSubscriptionsChanged();
                            }
                        };

                mTelephonyManager.addCarrierPrivilegesListener(
                        i, executor, carrierPrivilegesListener);
                mCarrierPrivilegesChangedListeners.add(carrierPrivilegesListener);
            }
        } catch (IllegalArgumentException e) {
            Slog.wtf(TAG, "Encounted exception registering carrier privileges listeners", e);
        }
    }

    /**
     * Unregisters the receivers, and stops tracking subscriptions.
     *
     * <p>Must always be run on the VcnManagementService thread.
     */
    public void unregister() {
        mContext.unregisterReceiver(this);
        mSubscriptionManager.removeOnSubscriptionsChangedListener(mSubscriptionChangedListener);
        mTelephonyManager.unregisterTelephonyCallback(mActiveDataSubIdListener);

        unregisterCarrierPrivilegesListeners();
    }

    private void unregisterCarrierPrivilegesListeners() {
        for (CarrierPrivilegesListener carrierPrivilegesListener :
                mCarrierPrivilegesChangedListeners) {
            mTelephonyManager.removeCarrierPrivilegesListener(carrierPrivilegesListener);
        }
        mCarrierPrivilegesChangedListeners.clear();
    }

    /**
@@ -178,8 +236,6 @@ public class TelephonySubscriptionTracker extends BroadcastReceiver {
            // group.
            if (subInfo.getSimSlotIndex() != INVALID_SIM_SLOT_INDEX
                    && mReadySubIdsBySlotId.values().contains(subInfo.getSubscriptionId())) {
                // TODO (b/172619301): Cache based on callbacks from CarrierPrivilegesTracker

                final TelephonyManager subIdSpecificTelephonyManager =
                        mTelephonyManager.createForSubscriptionId(subInfo.getSubscriptionId());

@@ -214,12 +270,39 @@ public class TelephonySubscriptionTracker extends BroadcastReceiver {
     */
    @Override
    public void onReceive(Context context, Intent intent) {
        // Accept sticky broadcasts; if CARRIER_CONFIG_CHANGED was previously broadcast and it
        // already was for an identified carrier, we can stop waiting for initial load to complete
        if (!ACTION_CARRIER_CONFIG_CHANGED.equals(intent.getAction())) {
            return;
        switch (intent.getAction()) {
            case ACTION_CARRIER_CONFIG_CHANGED:
                handleActionCarrierConfigChanged(context, intent);
                break;
            case ACTION_MULTI_SIM_CONFIG_CHANGED:
                handleActionMultiSimConfigChanged(context, intent);
                break;
            default:
                Slog.v(TAG, "Unknown intent received with action: " + intent.getAction());
        }
    }

    private void handleActionMultiSimConfigChanged(Context context, Intent intent) {
        unregisterCarrierPrivilegesListeners();

        // Clear invalid slotIds from the mReadySubIdsBySlotId map.
        final int modemCount = mTelephonyManager.getActiveModemCount();
        final Iterator<Integer> slotIdIterator = mReadySubIdsBySlotId.keySet().iterator();
        while (slotIdIterator.hasNext()) {
            final int slotId = slotIdIterator.next();

            if (slotId >= modemCount) {
                slotIdIterator.remove();
            }
        }

        registerCarrierPrivilegesListeners();
        handleSubscriptionsChanged();
    }

    private void handleActionCarrierConfigChanged(Context context, Intent intent) {
        // Accept sticky broadcasts; if CARRIER_CONFIG_CHANGED was previously broadcast and it
        // already was for an identified carrier, we can stop waiting for initial load to complete
        final int subId = intent.getIntExtra(EXTRA_SUBSCRIPTION_INDEX, INVALID_SUBSCRIPTION_ID);
        final int slotId = intent.getIntExtra(EXTRA_SLOT_INDEX, INVALID_SIM_SLOT_INDEX);

+86 −2
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import static android.telephony.CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX;
import static android.telephony.SubscriptionManager.INVALID_SIM_SLOT_INDEX;
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
import static android.telephony.TelephonyCallback.ActiveDataSubscriptionIdListener;
import static android.telephony.TelephonyManager.ACTION_MULTI_SIM_CONFIG_CHANGED;

import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionTrackerCallback;
@@ -34,8 +35,10 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;

@@ -57,6 +60,8 @@ import android.telephony.SubscriptionManager;
import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
import android.telephony.TelephonyCallback;
import android.telephony.TelephonyManager;
import android.telephony.TelephonyManager.CarrierPrivilegesListener;
import android.util.ArrayMap;
import android.util.ArraySet;

import androidx.test.filters.SmallTest;
@@ -83,7 +88,7 @@ public class TelephonySubscriptionTrackerTest {
    private static final String PACKAGE_NAME =
            TelephonySubscriptionTrackerTest.class.getPackage().getName();
    private static final ParcelUuid TEST_PARCEL_UUID = new ParcelUuid(UUID.randomUUID());
    private static final int TEST_SIM_SLOT_INDEX = 1;
    private static final int TEST_SIM_SLOT_INDEX = 0;
    private static final int TEST_SUBSCRIPTION_ID_1 = 2;
    private static final SubscriptionInfo TEST_SUBINFO_1 = mock(SubscriptionInfo.class);
    private static final int TEST_SUBSCRIPTION_ID_2 = 3;
@@ -151,6 +156,8 @@ public class TelephonySubscriptionTrackerTest {

    @Before
    public void setUp() throws Exception {
        doReturn(2).when(mTelephonyManager).getActiveModemCount();

        mCallback = mock(TelephonySubscriptionTrackerCallback.class);
        mTelephonySubscriptionTracker =
                new TelephonySubscriptionTracker(mContext, mHandler, mCallback, mDeps);
@@ -180,6 +187,15 @@ public class TelephonySubscriptionTrackerTest {
        return captor.getValue();
    }

    private List<CarrierPrivilegesListener> getCarrierPrivilegesListeners() {
        final ArgumentCaptor<CarrierPrivilegesListener> captor =
                ArgumentCaptor.forClass(CarrierPrivilegesListener.class);
        verify(mTelephonyManager, atLeastOnce())
                .addCarrierPrivilegesListener(anyInt(), any(), captor.capture());

        return captor.getAllValues();
    }

    private ActiveDataSubscriptionIdListener getActiveDataSubscriptionIdListener() {
        final ArgumentCaptor<TelephonyCallback> captor =
                ArgumentCaptor.forClass(TelephonyCallback.class);
@@ -188,6 +204,11 @@ public class TelephonySubscriptionTrackerTest {
        return (ActiveDataSubscriptionIdListener) captor.getValue();
    }

    private Intent buildTestMultiSimConfigBroadcastIntent() {
        Intent intent = new Intent(ACTION_MULTI_SIM_CONFIG_CHANGED);
        return intent;
    }

    private Intent buildTestBroadcastIntent(boolean hasValidSubscription) {
        Intent intent = new Intent(ACTION_CARRIER_CONFIG_CHANGED);
        intent.putExtra(EXTRA_SLOT_INDEX, TEST_SIM_SLOT_INDEX);
@@ -239,12 +260,21 @@ public class TelephonySubscriptionTrackerTest {
                        any(),
                        eq(mHandler));
        final IntentFilter filter = getIntentFilter();
        assertEquals(1, filter.countActions());
        assertEquals(2, filter.countActions());
        assertTrue(filter.hasAction(ACTION_CARRIER_CONFIG_CHANGED));
        assertTrue(filter.hasAction(ACTION_MULTI_SIM_CONFIG_CHANGED));

        verify(mSubscriptionManager)
                .addOnSubscriptionsChangedListener(any(HandlerExecutor.class), any());
        assertNotNull(getOnSubscriptionsChangedListener());

        verify(mTelephonyManager, times(2))
                .addCarrierPrivilegesListener(anyInt(), any(HandlerExecutor.class), any());
        verify(mTelephonyManager)
                .addCarrierPrivilegesListener(eq(0), any(HandlerExecutor.class), any());
        verify(mTelephonyManager)
                .addCarrierPrivilegesListener(eq(1), any(HandlerExecutor.class), any());
        assertEquals(2, getCarrierPrivilegesListeners().size());
    }

    @Test
@@ -255,6 +285,49 @@ public class TelephonySubscriptionTrackerTest {

        final OnSubscriptionsChangedListener listener = getOnSubscriptionsChangedListener();
        verify(mSubscriptionManager).removeOnSubscriptionsChangedListener(eq(listener));

        for (CarrierPrivilegesListener carrierPrivilegesListener :
                getCarrierPrivilegesListeners()) {
            verify(mTelephonyManager)
                    .removeCarrierPrivilegesListener(eq(carrierPrivilegesListener));
        }
    }

    @Test
    public void testMultiSimConfigChanged() throws Exception {
        final ArrayMap<Integer, Integer> readySubIdsBySlotId = new ArrayMap<>();
        readySubIdsBySlotId.put(TEST_SIM_SLOT_INDEX, TEST_SUBSCRIPTION_ID_1);
        readySubIdsBySlotId.put(TEST_SIM_SLOT_INDEX + 1, TEST_SUBSCRIPTION_ID_1);

        mTelephonySubscriptionTracker.setReadySubIdsBySlotId(readySubIdsBySlotId);
        doReturn(1).when(mTelephonyManager).getActiveModemCount();

        List<CarrierPrivilegesListener> carrierPrivilegesListeners =
                getCarrierPrivilegesListeners();

        mTelephonySubscriptionTracker.onReceive(mContext, buildTestMultiSimConfigBroadcastIntent());
        mTestLooper.dispatchAll();

        for (CarrierPrivilegesListener carrierPrivilegesListener : carrierPrivilegesListeners) {
            verify(mTelephonyManager)
                    .removeCarrierPrivilegesListener(eq(carrierPrivilegesListener));
        }

        // Expect cache cleared for inactive slots.
        assertNull(
                mTelephonySubscriptionTracker
                        .getReadySubIdsBySlotId()
                        .get(TEST_SIM_SLOT_INDEX + 1));

        // Expect a new CarrierPrivilegesListener to have been registered for slot 0, and none other
        // (2 previously registered during startup, for slots 0 & 1)
        verify(mTelephonyManager, times(3))
                .addCarrierPrivilegesListener(anyInt(), any(HandlerExecutor.class), any());
        verify(mTelephonyManager, times(2))
                .addCarrierPrivilegesListener(eq(0), any(HandlerExecutor.class), any());

        // Verify that this triggers a re-evaluation
        verify(mCallback).onNewSnapshot(eq(buildExpectedSnapshot(TEST_PRIVILEGED_PACKAGES)));
    }

    @Test
@@ -313,6 +386,17 @@ public class TelephonySubscriptionTrackerTest {
        verify(mCallback).onNewSnapshot(eq(buildExpectedSnapshot(TEST_PRIVILEGED_PACKAGES)));
    }

    @Test
    public void testOnCarrierPrivilegesChanged() throws Exception {
        setupReadySubIds();

        final CarrierPrivilegesListener listener = getCarrierPrivilegesListeners().get(0);
        listener.onCarrierPrivilegesChanged(Collections.emptyList(), new int[] {});
        mTestLooper.dispatchAll();

        verify(mCallback).onNewSnapshot(eq(buildExpectedSnapshot(TEST_PRIVILEGED_PACKAGES)));
    }

    @Test
    public void testReceiveBroadcast_ConfigReadyWithSubscriptions() throws Exception {
        mTelephonySubscriptionTracker.onReceive(mContext, buildTestBroadcastIntent(true));