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

Commit fe84f243 authored by Benedict Wong's avatar Benedict Wong Committed by Android (Google) Code Review
Browse files

Merge "Listen for individual subscription mobile data toggles" into sc-v2-dev

parents 04ce06bb 5f57cc83
Loading
Loading
Loading
Loading
+65 −4
Original line number Original line Diff line number Diff line
@@ -39,10 +39,13 @@ import android.net.vcn.VcnConfig;
import android.net.vcn.VcnGatewayConnectionConfig;
import android.net.vcn.VcnGatewayConnectionConfig;
import android.net.vcn.VcnManager.VcnErrorCode;
import android.net.vcn.VcnManager.VcnErrorCode;
import android.os.Handler;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.Message;
import android.os.Message;
import android.os.ParcelUuid;
import android.os.ParcelUuid;
import android.provider.Settings;
import android.provider.Settings;
import android.telephony.TelephonyCallback;
import android.telephony.TelephonyManager;
import android.telephony.TelephonyManager;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.ArraySet;
import android.util.Slog;
import android.util.Slog;


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


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

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


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

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


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

        mCurrentStatus = VCN_STATUS_CODE_INACTIVE;
        mCurrentStatus = VCN_STATUS_CODE_INACTIVE;
    }
    }


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


        updateMobileDataStateListeners();

        // Update the mobile data state after updating the subscription snapshot as a change in
        // Update the mobile data state after updating the subscription snapshot as a change in
        // subIds for a subGroup may affect the mobile data state.
        // subIds for a subGroup may affect the mobile data state.
        handleMobileDataToggled();
        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() {
    private void handleMobileDataToggled() {
        final boolean oldMobileDataEnabledStatus = mIsMobileDataEnabled;
        final boolean oldMobileDataEnabledStatus = mIsMobileDataEnabled;
        mIsMobileDataEnabled = getMobileDataStatus();
        mIsMobileDataEnabled = getMobileDataStatus();
@@ -493,11 +539,8 @@ public class Vcn extends Handler {
    }
    }


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

        for (int subId : mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup)) {
        for (int subId : mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup)) {
            if (genericTelMan.createForSubscriptionId(subId).isDataEnabled()) {
            if (getTelephonyManagerForSubid(subId).isDataEnabled()) {
                return true;
                return true;
            }
            }
        }
        }
@@ -517,6 +560,14 @@ public class Vcn extends Handler {
        return request.canBeSatisfiedBy(builder.build());
        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() {
    private String getLogPrefix() {
        return "["
        return "["
                + LogUtils.getHashedSubscriptionGroup(mSubscriptionGroup)
                + 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 */
    /** External dependencies used by Vcn, for injection in tests */
    @VisibleForTesting(visibility = Visibility.PRIVATE)
    @VisibleForTesting(visibility = Visibility.PRIVATE)
    public static class Dependencies {
    public static class Dependencies {
+109 −21
Original line number Original line Diff line number Diff line
@@ -58,6 +58,7 @@ import android.util.ArraySet;
import com.android.server.VcnManagementService.VcnCallback;
import com.android.server.VcnManagementService.VcnCallback;
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
import com.android.server.vcn.Vcn.VcnGatewayStatusCallback;
import com.android.server.vcn.Vcn.VcnGatewayStatusCallback;
import com.android.server.vcn.Vcn.VcnUserMobileDataStateListener;
import com.android.server.vcn.VcnNetworkProvider.NetworkRequestListener;
import com.android.server.vcn.VcnNetworkProvider.NetworkRequestListener;


import org.junit.Before;
import org.junit.Before;
@@ -207,6 +208,13 @@ public class VcnTest {
                .registerContentObserver(eq(uri), eq(true), any(ContentObserver.class));
                .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
    @Test
    public void testMobileDataStateCheckedOnInitialization_enabled() {
    public void testMobileDataStateCheckedOnInitialization_enabled() {
        // Validate state from setUp()
        // Validate state from setUp()
@@ -263,6 +271,24 @@ public class VcnTest {
        assertFalse(mVcn.isMobileDataEnabled());
        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) {
    private void triggerVcnRequestListeners(NetworkRequestListener requestListener) {
        for (final int[] caps : TEST_CAPS) {
        for (final int[] caps : TEST_CAPS) {
            startVcnGatewayWithCapabilities(requestListener, caps);
            startVcnGatewayWithCapabilities(requestListener, caps);
@@ -402,24 +428,17 @@ public class VcnTest {
        verify(mVcnNetworkProvider).resendAllRequests(requestListener);
        verify(mVcnNetworkProvider).resendAllRequests(requestListener);
    }
    }


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

        // Start VcnGatewayConnections
        // Start VcnGatewayConnections
        final NetworkRequestListener requestListener = verifyAndGetRequestListener();
        final NetworkRequestListener requestListener = verifyAndGetRequestListener();
        mVcn.setMobileDataEnabled(startingToggleState);
        mVcn.setMobileDataEnabled(startingToggleState);
        triggerVcnRequestListeners(requestListener);
        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
        // Verify that data toggle changes restart ONLY INTERNET or DUN networks, and only if the
        // toggle state changed.
        // toggle state changed.
        for (Entry<VcnGatewayConnectionConfig, VcnGatewayConnection> entry : gateways.entrySet()) {
        for (Entry<VcnGatewayConnectionConfig, VcnGatewayConnection> entry : gateways.entrySet()) {
@@ -433,29 +452,98 @@ public class VcnTest {
            }
            }
        }
        }


        final NetworkRequestListener requestListener = verifyAndGetRequestListener();
        if (startingToggleState != endingToggleState) {
        if (startingToggleState != endingToggleState) {
            verify(mVcnNetworkProvider).resendAllRequests(requestListener);
            verify(mVcnNetworkProvider).resendAllRequests(requestListener);
        }
        }
        assertEquals(endingToggleState, mVcn.isMobileDataEnabled());
        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
    @Test
    public void testMobileDataEnabled() {
    public void testSubscriptionMobileDataEnabled() {
        verifyMobileDataToggled(false /* startingToggleState */, true /* endingToggleState */);
        verifyGlobalMobileDataToggled(
                false /* startingToggleState */, true /* endingToggleState */);
    }
    }


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


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


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