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

Commit 7ad5f5a2 authored by Benedict Wong's avatar Benedict Wong Committed by Automerger Merge Worker
Browse files

Merge "Listen for individual subscription mobile data toggles" am: 92890dc5...

Merge "Listen for individual subscription mobile data toggles" am: 92890dc5 am: c5d9e812 am: ed637843 am: 7e746c43

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

Change-Id: Ie2a8632466dfcf6834009c9567435534389dbd11
parents 7f994312 7e746c43
Loading
Loading
Loading
Loading
+65 −4
Original line number Diff line number Diff line
@@ -39,10 +39,13 @@ import android.net.vcn.VcnConfig;
import android.net.vcn.VcnGatewayConnectionConfig;
import android.net.vcn.VcnManager.VcnErrorCode;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.Message;
import android.os.ParcelUuid;
import android.provider.Settings;
import android.telephony.TelephonyCallback;
import android.telephony.TelephonyManager;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;

@@ -57,6 +60,7 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
@@ -148,6 +152,10 @@ public class Vcn extends Handler {
    @NonNull private final VcnContentResolver mContentResolver;
    @NonNull private final ContentObserver mMobileDataSettingsObserver;

    @NonNull
    private final Map<Integer, VcnUserMobileDataStateListener> mMobileDataStateListeners =
            new ArrayMap<>();

    /**
     * Map containing all VcnGatewayConnections and their VcnGatewayConnectionConfigs.
     *
@@ -221,6 +229,9 @@ public class Vcn extends Handler {
        // Update mIsMobileDataEnabled before starting handling of NetworkRequests.
        mIsMobileDataEnabled = getMobileDataStatus();

        // Register mobile data state listeners.
        updateMobileDataStateListeners();

        // Register to receive cached and future NetworkRequests
        mVcnContext.getVcnNetworkProvider().registerListener(mRequestListener);
    }
@@ -348,6 +359,12 @@ public class Vcn extends Handler {
            gatewayConnection.teardownAsynchronously();
        }

        // Unregister MobileDataStateListeners
        for (VcnUserMobileDataStateListener listener : mMobileDataStateListeners.values()) {
            getTelephonyManager().unregisterTelephonyCallback(listener);
        }
        mMobileDataStateListeners.clear();

        mCurrentStatus = VCN_STATUS_CODE_INACTIVE;
    }

@@ -454,11 +471,40 @@ public class Vcn extends Handler {
            gatewayConnection.updateSubscriptionSnapshot(mLastSnapshot);
        }

        updateMobileDataStateListeners();

        // Update the mobile data state after updating the subscription snapshot as a change in
        // subIds for a subGroup may affect the mobile data state.
        handleMobileDataToggled();
    }

    private void updateMobileDataStateListeners() {
        final Set<Integer> subIdsInGroup = mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup);
        final HandlerExecutor executor = new HandlerExecutor(this);

        // Register new callbacks
        for (int subId : subIdsInGroup) {
            if (!mMobileDataStateListeners.containsKey(subId)) {
                final VcnUserMobileDataStateListener listener =
                        new VcnUserMobileDataStateListener();

                getTelephonyManagerForSubid(subId).registerTelephonyCallback(executor, listener);
                mMobileDataStateListeners.put(subId, listener);
            }
        }

        // Unregister old callbacks
        Iterator<Entry<Integer, VcnUserMobileDataStateListener>> iterator =
                mMobileDataStateListeners.entrySet().iterator();
        while (iterator.hasNext()) {
            final Entry<Integer, VcnUserMobileDataStateListener> entry = iterator.next();
            if (!subIdsInGroup.contains(entry.getKey())) {
                getTelephonyManager().unregisterTelephonyCallback(entry.getValue());
                iterator.remove();
            }
        }
    }

    private void handleMobileDataToggled() {
        final boolean oldMobileDataEnabledStatus = mIsMobileDataEnabled;
        mIsMobileDataEnabled = getMobileDataStatus();
@@ -493,11 +539,8 @@ public class Vcn extends Handler {
    }

    private boolean getMobileDataStatus() {
        final TelephonyManager genericTelMan =
                mVcnContext.getContext().getSystemService(TelephonyManager.class);

        for (int subId : mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup)) {
            if (genericTelMan.createForSubscriptionId(subId).isDataEnabled()) {
            if (getTelephonyManagerForSubid(subId).isDataEnabled()) {
                return true;
            }
        }
@@ -517,6 +560,14 @@ public class Vcn extends Handler {
        return request.canBeSatisfiedBy(builder.build());
    }

    private TelephonyManager getTelephonyManager() {
        return mVcnContext.getContext().getSystemService(TelephonyManager.class);
    }

    private TelephonyManager getTelephonyManagerForSubid(int subid) {
        return getTelephonyManager().createForSubscriptionId(subid);
    }

    private String getLogPrefix() {
        return "["
                + LogUtils.getHashedSubscriptionGroup(mSubscriptionGroup)
@@ -670,6 +721,16 @@ public class Vcn extends Handler {
        }
    }

    @VisibleForTesting(visibility = Visibility.PRIVATE)
    class VcnUserMobileDataStateListener extends TelephonyCallback
            implements TelephonyCallback.UserMobileDataStateListener {

        @Override
        public void onUserMobileDataStateChanged(boolean enabled) {
            sendMessage(obtainMessage(MSG_EVENT_MOBILE_DATA_TOGGLED));
        }
    }

    /** External dependencies used by Vcn, for injection in tests */
    @VisibleForTesting(visibility = Visibility.PRIVATE)
    public static class Dependencies {
+109 −21
Original line number Diff line number Diff line
@@ -58,6 +58,7 @@ import android.util.ArraySet;
import com.android.server.VcnManagementService.VcnCallback;
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
import com.android.server.vcn.Vcn.VcnGatewayStatusCallback;
import com.android.server.vcn.Vcn.VcnUserMobileDataStateListener;
import com.android.server.vcn.VcnNetworkProvider.NetworkRequestListener;

import org.junit.Before;
@@ -207,6 +208,13 @@ public class VcnTest {
                .registerContentObserver(eq(uri), eq(true), any(ContentObserver.class));
    }

    @Test
    public void testMobileDataStateListenersRegistered() {
        // Validate state from setUp()
        verify(mTelephonyManager, times(3))
                .registerTelephonyCallback(any(), any(VcnUserMobileDataStateListener.class));
    }

    @Test
    public void testMobileDataStateCheckedOnInitialization_enabled() {
        // Validate state from setUp()
@@ -263,6 +271,24 @@ public class VcnTest {
        assertFalse(mVcn.isMobileDataEnabled());
    }

    @Test
    public void testSubscriptionSnapshotUpdatesMobileDataStateListeners() {
        final TelephonySubscriptionSnapshot updatedSnapshot =
                mock(TelephonySubscriptionSnapshot.class);

        doReturn(new ArraySet<>(Arrays.asList(2, 4)))
                .when(updatedSnapshot)
                .getAllSubIdsInGroup(any());

        mVcn.updateSubscriptionSnapshot(updatedSnapshot);
        mTestLooper.dispatchAll();

        verify(mTelephonyManager, times(4))
                .registerTelephonyCallback(any(), any(VcnUserMobileDataStateListener.class));
        verify(mTelephonyManager, times(2))
                .unregisterTelephonyCallback(any(VcnUserMobileDataStateListener.class));
    }

    private void triggerVcnRequestListeners(NetworkRequestListener requestListener) {
        for (final int[] caps : TEST_CAPS) {
            startVcnGatewayWithCapabilities(requestListener, caps);
@@ -402,24 +428,17 @@ public class VcnTest {
        verify(mVcnNetworkProvider).resendAllRequests(requestListener);
    }

    private void verifyMobileDataToggled(boolean startingToggleState, boolean endingToggleState) {
        final ArgumentCaptor<ContentObserver> captor =
                ArgumentCaptor.forClass(ContentObserver.class);
        verify(mContentResolver).registerContentObserver(any(), anyBoolean(), captor.capture());
        final ContentObserver contentObserver = captor.getValue();

    private void setupForMobileDataTest(boolean startingToggleState) {
        // Start VcnGatewayConnections
        final NetworkRequestListener requestListener = verifyAndGetRequestListener();
        mVcn.setMobileDataEnabled(startingToggleState);
        triggerVcnRequestListeners(requestListener);
        final Map<VcnGatewayConnectionConfig, VcnGatewayConnection> gateways =
                mVcn.getVcnGatewayConnectionConfigMap();

        // Trigger data toggle change.
        doReturn(endingToggleState).when(mTelephonyManager).isDataEnabled();
        contentObserver.onChange(false /* selfChange, ignored */);
        mTestLooper.dispatchAll();
    }

    private void verifyMobileDataToggledUpdatesGatewayConnections(
            boolean startingToggleState,
            boolean endingToggleState,
            Map<VcnGatewayConnectionConfig, VcnGatewayConnection> gateways) {
        // Verify that data toggle changes restart ONLY INTERNET or DUN networks, and only if the
        // toggle state changed.
        for (Entry<VcnGatewayConnectionConfig, VcnGatewayConnection> entry : gateways.entrySet()) {
@@ -433,29 +452,98 @@ public class VcnTest {
            }
        }

        final NetworkRequestListener requestListener = verifyAndGetRequestListener();
        if (startingToggleState != endingToggleState) {
            verify(mVcnNetworkProvider).resendAllRequests(requestListener);
        }
        assertEquals(endingToggleState, mVcn.isMobileDataEnabled());
    }

    private void verifyGlobalMobileDataToggled(
            boolean startingToggleState, boolean endingToggleState) {
        setupForMobileDataTest(startingToggleState);
        final Map<VcnGatewayConnectionConfig, VcnGatewayConnection> gateways =
                mVcn.getVcnGatewayConnectionConfigMap();

        // Trigger data toggle change
        final ArgumentCaptor<ContentObserver> captor =
                ArgumentCaptor.forClass(ContentObserver.class);
        verify(mContentResolver).registerContentObserver(any(), anyBoolean(), captor.capture());
        final ContentObserver contentObserver = captor.getValue();

        doReturn(endingToggleState).when(mTelephonyManager).isDataEnabled();
        contentObserver.onChange(false /* selfChange, ignored */);
        mTestLooper.dispatchAll();

        // Verify resultant behavior
        verifyMobileDataToggledUpdatesGatewayConnections(
                startingToggleState, endingToggleState, gateways);
    }

    @Test
    public void testGlobalMobileDataEnabled() {
        verifyGlobalMobileDataToggled(
                false /* startingToggleState */, true /* endingToggleState */);
    }

    @Test
    public void testGlobalMobileDataDisabled() {
        verifyGlobalMobileDataToggled(
                true /* startingToggleState */, false /* endingToggleState */);
    }

    @Test
    public void testGlobalMobileDataObserverFiredWithoutChanges_dataEnabled() {
        verifyGlobalMobileDataToggled(
                false /* startingToggleState */, false /* endingToggleState */);
    }

    @Test
    public void testGlobalMobileDataObserverFiredWithoutChanges_dataDisabled() {
        verifyGlobalMobileDataToggled(true /* startingToggleState */, true /* endingToggleState */);
    }

    private void verifySubscriptionMobileDataToggled(
            boolean startingToggleState, boolean endingToggleState) {
        setupForMobileDataTest(startingToggleState);
        final Map<VcnGatewayConnectionConfig, VcnGatewayConnection> gateways =
                mVcn.getVcnGatewayConnectionConfigMap();

        // Trigger data toggle change.
        final ArgumentCaptor<VcnUserMobileDataStateListener> captor =
                ArgumentCaptor.forClass(VcnUserMobileDataStateListener.class);
        verify(mTelephonyManager, times(3)).registerTelephonyCallback(any(), captor.capture());
        final VcnUserMobileDataStateListener listener = captor.getValue();

        doReturn(endingToggleState).when(mTelephonyManager).isDataEnabled();
        listener.onUserMobileDataStateChanged(false /* enabled, ignored */);
        mTestLooper.dispatchAll();

        // Verify resultant behavior
        verifyMobileDataToggledUpdatesGatewayConnections(
                startingToggleState, endingToggleState, gateways);
    }

    @Test
    public void testMobileDataEnabled() {
        verifyMobileDataToggled(false /* startingToggleState */, true /* endingToggleState */);
    public void testSubscriptionMobileDataEnabled() {
        verifyGlobalMobileDataToggled(
                false /* startingToggleState */, true /* endingToggleState */);
    }

    @Test
    public void testMobileDataDisabled() {
        verifyMobileDataToggled(true /* startingToggleState */, false /* endingToggleState */);
    public void testSubscriptionMobileDataDisabled() {
        verifyGlobalMobileDataToggled(
                true /* startingToggleState */, false /* endingToggleState */);
    }

    @Test
    public void testMobileDataObserverFiredWithoutChanges_dataEnabled() {
        verifyMobileDataToggled(false /* startingToggleState */, false /* endingToggleState */);
    public void testSubscriptionMobileDataListenerFiredWithoutChanges_dataEnabled() {
        verifyGlobalMobileDataToggled(
                false /* startingToggleState */, false /* endingToggleState */);
    }

    @Test
    public void testMobileDataObserverFiredWithoutChanges_dataDisabled() {
        verifyMobileDataToggled(true /* startingToggleState */, true /* endingToggleState */);
    public void testSubscriptionMobileDataListenerFiredWithoutChanges_dataDisabled() {
        verifyGlobalMobileDataToggled(true /* startingToggleState */, true /* endingToggleState */);
    }
}