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

Commit 685c9097 authored by Benedict Wong's avatar Benedict Wong
Browse files

Notify Vcn of VcnGatewayConnection quits

This change updates the Vcn when any GatewayConnection StateMachine
quits, removing it from the map of active GatewayConnections, and
triggering a re-evaluation of all existing NetworkRequests. This may (in
the event of a updated policy) result in an immediate restart.

Bug: 179944275
Test: atest FrameworksVcnTests
Change-Id: I72abf1e270a772760a17a03173d5a110e15bd01d
parent 75a32b94
Loading
Loading
Loading
Loading
+40 −4
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package com.android.server.vcn;

import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED;

import static com.android.server.VcnManagementService.VDBG;

import android.annotation.NonNull;
@@ -84,6 +86,13 @@ public class Vcn extends Handler {
     */
    private static final int MSG_EVENT_SUBSCRIPTIONS_CHANGED = MSG_EVENT_BASE + 2;

    /**
     * A GatewayConnection owned by this VCN quit.
     *
     * @param obj VcnGatewayConnectionConfig
     */
    private static final int MSG_EVENT_GATEWAY_CONNECTION_QUIT = MSG_EVENT_BASE + 3;

    /** Triggers an immediate teardown of the entire Vcn, including GatewayConnections. */
    private static final int MSG_CMD_TEARDOWN = MSG_CMD_BASE;

@@ -208,6 +217,9 @@ public class Vcn extends Handler {
            case MSG_EVENT_SUBSCRIPTIONS_CHANGED:
                handleSubscriptionsChanged((TelephonySubscriptionSnapshot) msg.obj);
                break;
            case MSG_EVENT_GATEWAY_CONNECTION_QUIT:
                handleGatewayConnectionQuit((VcnGatewayConnectionConfig) msg.obj);
                break;
            case MSG_CMD_TEARDOWN:
                handleTeardown();
                break;
@@ -263,7 +275,7 @@ public class Vcn extends Handler {

        // If preexisting VcnGatewayConnection(s) satisfy request, return
        for (VcnGatewayConnectionConfig gatewayConnectionConfig : mVcnGatewayConnections.keySet()) {
            if (requestSatisfiedByGatewayConnectionConfig(request, gatewayConnectionConfig)) {
            if (isRequestSatisfiedByGatewayConnectionConfig(request, gatewayConnectionConfig)) {
                if (VDBG) {
                    Slog.v(
                            getLogTag(),
@@ -278,7 +290,7 @@ public class Vcn extends Handler {
        // up
        for (VcnGatewayConnectionConfig gatewayConnectionConfig :
                mConfig.getGatewayConnectionConfigs()) {
            if (requestSatisfiedByGatewayConnectionConfig(request, gatewayConnectionConfig)) {
            if (isRequestSatisfiedByGatewayConnectionConfig(request, gatewayConnectionConfig)) {
                Slog.v(
                        getLogTag(),
                        "Bringing up new VcnGatewayConnection for request " + request.requestId);
@@ -289,12 +301,21 @@ public class Vcn extends Handler {
                                mSubscriptionGroup,
                                mLastSnapshot,
                                gatewayConnectionConfig,
                                new VcnGatewayStatusCallbackImpl());
                                new VcnGatewayStatusCallbackImpl(gatewayConnectionConfig));
                mVcnGatewayConnections.put(gatewayConnectionConfig, vcnGatewayConnection);
            }
        }
    }

    private void handleGatewayConnectionQuit(VcnGatewayConnectionConfig config) {
        Slog.v(getLogTag(), "VcnGatewayConnection quit: " + config);
        mVcnGatewayConnections.remove(config);

        // Trigger a re-evaluation of all NetworkRequests (to make sure any that can be satisfied
        // start a new GatewayConnection)
        mVcnContext.getVcnNetworkProvider().resendAllRequests(mRequestListener);
    }

    private void handleSubscriptionsChanged(@NonNull TelephonySubscriptionSnapshot snapshot) {
        mLastSnapshot = snapshot;

@@ -305,9 +326,10 @@ public class Vcn extends Handler {
        }
    }

    private boolean requestSatisfiedByGatewayConnectionConfig(
    private boolean isRequestSatisfiedByGatewayConnectionConfig(
            @NonNull NetworkRequest request, @NonNull VcnGatewayConnectionConfig config) {
        final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder();
        builder.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
        for (int cap : config.getAllExposedCapabilities()) {
            builder.addCapability(cap);
        }
@@ -339,9 +361,23 @@ public class Vcn extends Handler {
                @VcnErrorCode int errorCode,
                @Nullable String exceptionClass,
                @Nullable String exceptionMessage);

        /** Called by a VcnGatewayConnection to indicate that it has fully torn down. */
        void onQuit();
    }

    private class VcnGatewayStatusCallbackImpl implements VcnGatewayStatusCallback {
        public final VcnGatewayConnectionConfig mGatewayConnectionConfig;

        VcnGatewayStatusCallbackImpl(VcnGatewayConnectionConfig gatewayConnectionConfig) {
            mGatewayConnectionConfig = gatewayConnectionConfig;
        }

        @Override
        public void onQuit() {
            sendMessage(obtainMessage(MSG_EVENT_GATEWAY_CONNECTION_QUIT, mGatewayConnectionConfig));
        }

        @Override
        public void onEnteredSafeMode() {
            sendMessage(obtainMessage(MSG_CMD_ENTER_SAFE_MODE));
+2 −0
Original line number Diff line number Diff line
@@ -649,6 +649,8 @@ public class VcnGatewayConnection extends StateMachine {
        cancelSafeModeAlarm();

        mUnderlyingNetworkTracker.teardown();

        mGatewayStatusCallback.onQuit();
    }

    /**
+9 −3
Original line number Diff line number Diff line
@@ -67,9 +67,7 @@ public class VcnNetworkProvider extends NetworkProvider {
        mListeners.add(listener);

        // Send listener all cached requests
        for (NetworkRequestEntry entry : mRequests.values()) {
            notifyListenerForEvent(listener, entry);
        }
        resendAllRequests(listener);
    }

    /** Unregisters the specified listener from receiving future NetworkRequests. */
@@ -78,6 +76,14 @@ public class VcnNetworkProvider extends NetworkProvider {
        mListeners.remove(listener);
    }

    /** Sends all cached NetworkRequest(s) to the specified listener. */
    @VisibleForTesting(visibility = Visibility.PACKAGE)
    public void resendAllRequests(@NonNull NetworkRequestListener listener) {
        for (NetworkRequestEntry entry : mRequests.values()) {
            notifyListenerForEvent(listener, entry);
        }
    }

    private void notifyListenerForEvent(
            @NonNull NetworkRequestListener listener, @NonNull NetworkRequestEntry entry) {
        listener.onNetworkRequested(entry.mRequest, entry.mScore, entry.mProviderId);
+3 −0
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;

import android.net.IpSecManager;
@@ -104,6 +105,7 @@ public class VcnGatewayConnectionDisconnectedStateTest extends VcnGatewayConnect
        verify(mIpSecSvc).deleteTunnelInterface(eq(TEST_IPSEC_TUNNEL_RESOURCE_ID), any());
        verifySafeModeTimeoutAlarmAndGetCallback(true /* expectCanceled */);
        assertFalse(mGatewayConnection.isRunning());
        verify(mGatewayStatusCallback).onQuit();
    }

    @Test
@@ -113,6 +115,7 @@ public class VcnGatewayConnectionDisconnectedStateTest extends VcnGatewayConnect

        assertEquals(mGatewayConnection.mDisconnectedState, mGatewayConnection.getCurrentState());
        assertTrue(mGatewayConnection.isRunning());
        verify(mGatewayStatusCallback, never()).onQuit();
        // No safe mode timer changes expected.
    }
}
+63 −10
Original line number Diff line number Diff line
@@ -16,6 +16,10 @@

package com.android.server.vcn;

import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN;
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
import static android.net.NetworkCapabilities.NET_CAPABILITY_MMS;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.mockito.Matchers.any;
@@ -33,6 +37,7 @@ import android.net.vcn.VcnGatewayConnectionConfig;
import android.net.vcn.VcnGatewayConnectionConfigTest;
import android.os.ParcelUuid;
import android.os.test.TestLooper;
import android.util.ArraySet;

import com.android.server.VcnManagementService.VcnCallback;
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
@@ -51,6 +56,11 @@ public class VcnTest {
    private static final ParcelUuid TEST_SUB_GROUP = new ParcelUuid(new UUID(0, 0));
    private static final int NETWORK_SCORE = 0;
    private static final int PROVIDER_ID = 5;
    private static final int[][] TEST_CAPS =
            new int[][] {
                new int[] {NET_CAPABILITY_INTERNET, NET_CAPABILITY_MMS},
                new int[] {NET_CAPABILITY_DUN}
            };

    private Context mContext;
    private VcnContext mVcnContext;
@@ -91,13 +101,12 @@ public class VcnTest {
        mGatewayStatusCallbackCaptor = ArgumentCaptor.forClass(VcnGatewayStatusCallback.class);

        final VcnConfig.Builder configBuilder = new VcnConfig.Builder(mContext);
        for (final int capability : VcnGatewayConnectionConfigTest.EXPOSED_CAPS) {
        for (final int[] caps : TEST_CAPS) {
            configBuilder.addGatewayConnectionConfig(
                    VcnGatewayConnectionConfigTest.buildTestConfigWithExposedCaps(capability));
                    VcnGatewayConnectionConfigTest.buildTestConfigWithExposedCaps(caps));
        }
        configBuilder.addGatewayConnectionConfig(VcnGatewayConnectionConfigTest.buildTestConfig());
        mConfig = configBuilder.build();

        mConfig = configBuilder.build();
        mVcn =
                new Vcn(
                        mVcnContext,
@@ -130,8 +139,7 @@ public class VcnTest {
    @Test
    public void testSubscriptionSnapshotUpdatesVcnGatewayConnections() {
        final NetworkRequestListener requestListener = verifyAndGetRequestListener();
        startVcnGatewayWithCapabilities(
                requestListener, VcnGatewayConnectionConfigTest.EXPOSED_CAPS);
        startVcnGatewayWithCapabilities(requestListener, TEST_CAPS[0]);

        final Set<VcnGatewayConnection> gatewayConnections = mVcn.getVcnGatewayConnections();
        assertFalse(gatewayConnections.isEmpty());
@@ -153,10 +161,19 @@ public class VcnTest {
        for (final int capability : VcnGatewayConnectionConfigTest.EXPOSED_CAPS) {
            startVcnGatewayWithCapabilities(requestListener, capability);
        }
    }

    private void triggerVcnRequestListeners(NetworkRequestListener requestListener) {
        for (final int[] caps : TEST_CAPS) {
            startVcnGatewayWithCapabilities(requestListener, caps);
        }
    }

        // Each Capability in EXPOSED_CAPS was split into a separate VcnGatewayConnection in #setUp.
        // Expect one VcnGatewayConnection per capability.
        final int numExpectedGateways = VcnGatewayConnectionConfigTest.EXPOSED_CAPS.length;
    public Set<VcnGatewayConnection> startGatewaysAndGetGatewayConnections(
            NetworkRequestListener requestListener) {
        triggerVcnRequestListeners(requestListener);

        final int numExpectedGateways = TEST_CAPS.length;

        final Set<VcnGatewayConnection> gatewayConnections = mVcn.getVcnGatewayConnections();
        assertEquals(numExpectedGateways, gatewayConnections.size());
@@ -168,7 +185,16 @@ public class VcnTest {
                        any(),
                        mGatewayStatusCallbackCaptor.capture());

        // Doesn't matter which callback this gets - any Gateway entering safe mode should shut down
        return gatewayConnections;
    }

    @Test
    public void testGatewayEnteringSafemodeNotifiesVcn() {
        final NetworkRequestListener requestListener = verifyAndGetRequestListener();
        final Set<VcnGatewayConnection> gatewayConnections =
                startGatewaysAndGetGatewayConnections(requestListener);

        // Doesn't matter which callback this gets - any Gateway entering Safemode should shut down
        // all Gateways
        final VcnGatewayStatusCallback statusCallback = mGatewayStatusCallbackCaptor.getValue();
        statusCallback.onEnteredSafeMode();
@@ -181,4 +207,31 @@ public class VcnTest {
        verify(mVcnNetworkProvider).unregisterListener(requestListener);
        verify(mVcnCallback).onEnteredSafeMode();
    }

    @Test
    public void testGatewayQuit() {
        final NetworkRequestListener requestListener = verifyAndGetRequestListener();
        final Set<VcnGatewayConnection> gatewayConnections =
                new ArraySet<>(startGatewaysAndGetGatewayConnections(requestListener));

        final VcnGatewayStatusCallback statusCallback = mGatewayStatusCallbackCaptor.getValue();
        statusCallback.onQuit();
        mTestLooper.dispatchAll();

        // Verify that the VCN requests the networkRequests be resent
        assertEquals(1, mVcn.getVcnGatewayConnections().size());
        verify(mVcnNetworkProvider).resendAllRequests(requestListener);

        // Verify that the VcnGatewayConnection is restarted
        triggerVcnRequestListeners(requestListener);
        mTestLooper.dispatchAll();
        assertEquals(2, mVcn.getVcnGatewayConnections().size());
        verify(mDeps, times(gatewayConnections.size() + 1))
                .newVcnGatewayConnection(
                        eq(mVcnContext),
                        eq(TEST_SUB_GROUP),
                        eq(mSubscriptionSnapshot),
                        any(),
                        mGatewayStatusCallbackCaptor.capture());
    }
}