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

Commit 46c10cbc authored by Cody Kesting's avatar Cody Kesting Committed by Gerrit Code Review
Browse files

Merge changes from topic "vcn-status-changed"

* changes:
  Rename VcnStatusCallback#onVcnStatusChanged.
  Reevaluate VcnGatewayConnections on receiving new configs.
parents 9446a523 c5728150
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -25723,7 +25723,7 @@ package android.net.vcn {
  public abstract static class VcnManager.VcnStatusCallback {
    ctor public VcnManager.VcnStatusCallback();
    method public abstract void onGatewayConnectionError(@NonNull int[], int, @Nullable Throwable);
    method public abstract void onVcnStatusChanged(int);
    method public abstract void onStatusChanged(int);
  }
}
+3 −3
Original line number Diff line number Diff line
@@ -439,7 +439,7 @@ public class VcnManager {
         * @param statusCode the code for the status change encountered by this {@link
         *     VcnStatusCallback}'s subscription group.
         */
        public abstract void onVcnStatusChanged(@VcnStatusCode int statusCode);
        public abstract void onStatusChanged(@VcnStatusCode int statusCode);

        /**
         * Invoked when a VCN Gateway Connection corresponding to this callback's subscription group
@@ -476,7 +476,7 @@ public class VcnManager {
     * and there is a VCN active for its specified subscription group (this may happen after the
     * callback is registered).
     *
     * <p>{@link VcnStatusCallback#onVcnStatusChanged(int)} will be invoked on registration with the
     * <p>{@link VcnStatusCallback#onStatusChanged(int)} will be invoked on registration with the
     * current status for the specified subscription group's VCN. If the registrant is not
     * privileged for this subscription group, {@link #VCN_STATUS_CODE_NOT_CONFIGURED} will be
     * returned.
@@ -580,7 +580,7 @@ public class VcnManager {
        @Override
        public void onVcnStatusChanged(@VcnStatusCode int statusCode) {
            Binder.withCleanCallingIdentity(
                    () -> mExecutor.execute(() -> mCallback.onVcnStatusChanged(statusCode)));
                    () -> mExecutor.execute(() -> mCallback.onStatusChanged(statusCode)));
        }

        // TODO(b/180521637): use ServiceSpecificException for safer Exception 'parceling'
+65 −11
Original line number Diff line number Diff line
@@ -41,6 +41,7 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -110,6 +111,24 @@ public class Vcn extends Handler {
    @NonNull private final VcnNetworkRequestListener mRequestListener;
    @NonNull private final VcnCallback mVcnCallback;

    /**
     * Map containing all VcnGatewayConnections and their VcnGatewayConnectionConfigs.
     *
     * <p>Due to potential for race conditions, VcnGatewayConnections MUST only be created and added
     * to this map in {@link #handleNetworkRequested(NetworkRequest, int, int)}, when a VCN receives
     * a NetworkRequest that matches a VcnGatewayConnectionConfig for this VCN's VcnConfig.
     *
     * <p>A VcnGatewayConnection instance MUST NEVER overwrite an existing instance - otherwise
     * there is potential for a orphaned VcnGatewayConnection instance that does not get properly
     * shut down.
     *
     * <p>Due to potential for race conditions, VcnGatewayConnections MUST only be removed from this
     * map once they have finished tearing down, which is reported to this VCN via {@link
     * VcnGatewayStatusCallback#onQuit()}. Once this is done, all NetworkRequests are retrieved from
     * the NetworkProvider so that another VcnGatewayConnectionConfig can match the
     * previously-matched request.
     */
    // TODO(b/182533200): remove the invariant on VcnGatewayConnection lifecycles
    @NonNull
    private final Map<VcnGatewayConnectionConfig, VcnGatewayConnection> mVcnGatewayConnections =
            new HashMap<>();
@@ -191,6 +210,19 @@ public class Vcn extends Handler {
        return Collections.unmodifiableSet(new HashSet<>(mVcnGatewayConnections.values()));
    }

    /** Get current Configs and Gateways for testing purposes */
    @VisibleForTesting(visibility = Visibility.PRIVATE)
    public Map<VcnGatewayConnectionConfig, VcnGatewayConnection>
            getVcnGatewayConnectionConfigMap() {
        return Collections.unmodifiableMap(new HashMap<>(mVcnGatewayConnections));
    }

    /** Set whether this Vcn is active for testing purposes */
    @VisibleForTesting(visibility = Visibility.PRIVATE)
    public void setIsActive(boolean isActive) {
        mIsActive.set(isActive);
    }

    private class VcnNetworkRequestListener implements VcnNetworkProvider.NetworkRequestListener {
        @Override
        public void onNetworkRequested(@NonNull NetworkRequest request, int score, int providerId) {
@@ -202,11 +234,6 @@ public class Vcn extends Handler {

    @Override
    public void handleMessage(@NonNull Message msg) {
        // Ignore if this Vcn is not active and we're not receiving new configs
        if (!isActive() && msg.what != MSG_EVENT_CONFIG_UPDATED) {
            return;
        }

        switch (msg.what) {
            case MSG_EVENT_CONFIG_UPDATED:
                handleConfigUpdated((VcnConfig) msg.obj);
@@ -237,9 +264,31 @@ public class Vcn extends Handler {

        mConfig = config;

        // TODO(b/181815405): Reevaluate active VcnGatewayConnection(s)
        if (mIsActive.getAndSet(true)) {
            // VCN is already active - teardown any GatewayConnections whose configs have been
            // removed and get all current requests
            for (final Entry<VcnGatewayConnectionConfig, VcnGatewayConnection> entry :
                    mVcnGatewayConnections.entrySet()) {
                final VcnGatewayConnectionConfig gatewayConnectionConfig = entry.getKey();
                final VcnGatewayConnection gatewayConnection = entry.getValue();

                // GatewayConnectionConfigs must match exactly (otherwise authentication or
                // connection details may have changed).
                if (!mConfig.getGatewayConnectionConfigs().contains(gatewayConnectionConfig)) {
                    if (gatewayConnection == null) {
                        Slog.wtf(
                                getLogTag(),
                                "Found gatewayConnectionConfig without GatewayConnection");
                    } else {
                        gatewayConnection.teardownAsynchronously();
                    }
                }
            }

        if (!mIsActive.getAndSet(true)) {
            // Trigger a re-evaluation of all NetworkRequests (to make sure any that can be
            // satisfied start a new GatewayConnection)
            mVcnContext.getVcnNetworkProvider().resendAllRequests(mRequestListener);
        } else {
            // If this VCN was not previously active, it is exiting Safe Mode. Re-register the
            // request listener to get NetworkRequests again (and all cached requests).
            mVcnContext.getVcnNetworkProvider().registerListener(mRequestListener);
@@ -259,13 +308,16 @@ public class Vcn extends Handler {
    private void handleEnterSafeMode() {
        handleTeardown();

        mVcnGatewayConnections.clear();

        mVcnCallback.onEnteredSafeMode();
    }

    private void handleNetworkRequested(
            @NonNull NetworkRequest request, int score, int providerId) {
        if (!isActive()) {
            Slog.v(getLogTag(), "Received NetworkRequest while inactive. Ignore for now");
            return;
        }

        if (score > getNetworkScore()) {
            if (VDBG) {
                Slog.v(
@@ -318,9 +370,11 @@ public class Vcn extends Handler {
        mVcnGatewayConnections.remove(config);

        // Trigger a re-evaluation of all NetworkRequests (to make sure any that can be satisfied
        // start a new GatewayConnection)
        // start a new GatewayConnection), but only if the Vcn is still active
        if (isActive()) {
            mVcnContext.getVcnNetworkProvider().resendAllRequests(mRequestListener);
        }
    }

    private void handleSubscriptionsChanged(@NonNull TelephonySubscriptionSnapshot snapshot) {
        mLastSnapshot = snapshot;
+1 −1
Original line number Diff line number Diff line
@@ -204,7 +204,7 @@ public class VcnManagerTest {
                new VcnStatusCallbackBinder(INLINE_EXECUTOR, mMockStatusCallback);

        cbBinder.onVcnStatusChanged(VCN_STATUS_CODE_ACTIVE);
        verify(mMockStatusCallback).onVcnStatusChanged(VCN_STATUS_CODE_ACTIVE);
        verify(mMockStatusCallback).onStatusChanged(VCN_STATUS_CODE_ACTIVE);

        cbBinder.onGatewayConnectionError(
                UNDERLYING_NETWORK_CAPABILITIES,
+73 −6
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@ import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;

@@ -139,8 +140,7 @@ public class VcnTest {
        mTestLooper.dispatchAll();
    }

    @Test
    public void testSubscriptionSnapshotUpdatesVcnGatewayConnections() {
    private void verifyUpdateSubscriptionSnapshotNotifiesConnectionGateways(boolean isActive) {
        final NetworkRequestListener requestListener = verifyAndGetRequestListener();
        startVcnGatewayWithCapabilities(requestListener, TEST_CAPS[0]);

@@ -150,12 +150,25 @@ public class VcnTest {
        final TelephonySubscriptionSnapshot updatedSnapshot =
                mock(TelephonySubscriptionSnapshot.class);

        mVcn.setIsActive(isActive);

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

        for (final VcnGatewayConnection gateway : gatewayConnections) {
            verify(gateway).updateSubscriptionSnapshot(eq(updatedSnapshot));
            verify(gateway, isActive ? times(1) : never())
                    .updateSubscriptionSnapshot(eq(updatedSnapshot));
        }
    }

    @Test
    public void testSubscriptionSnapshotUpdatesVcnGatewayConnections() {
        verifyUpdateSubscriptionSnapshotNotifiesConnectionGateways(true /* isActive */);
    }

    @Test
    public void testSubscriptionSnapshotUpdatesVcnGatewayConnectionsWhileInactive() {
        verifyUpdateSubscriptionSnapshotNotifiesConnectionGateways(false /* isActive */);
    }

    private void triggerVcnRequestListeners(NetworkRequestListener requestListener) {
@@ -187,7 +200,6 @@ public class VcnTest {
            NetworkRequestListener requestListener,
            Set<VcnGatewayConnection> expectedGatewaysTornDown) {
        assertFalse(mVcn.isActive());
        assertTrue(mVcn.getVcnGatewayConnections().isEmpty());
        for (final VcnGatewayConnection gatewayConnection : expectedGatewaysTornDown) {
            verify(gatewayConnection).teardownAsynchronously();
        }
@@ -237,6 +249,51 @@ public class VcnTest {
                        mGatewayStatusCallbackCaptor.capture());
    }

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

        mVcn.teardownAsynchronously();
        mTestLooper.dispatchAll();

        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, never()).resendAllRequests(requestListener);
    }

    @Test
    public void testUpdateConfigReevaluatesGatewayConnections() {
        final NetworkRequestListener requestListener = verifyAndGetRequestListener();
        startGatewaysAndGetGatewayConnections(requestListener);
        assertEquals(2, mVcn.getVcnGatewayConnectionConfigMap().size());

        // Create VcnConfig with only one VcnGatewayConnectionConfig so a gateway connection is torn
        // down
        final VcnGatewayConnectionConfig activeConfig =
                VcnGatewayConnectionConfigTest.buildTestConfigWithExposedCaps(TEST_CAPS[0]);
        final VcnGatewayConnectionConfig removedConfig =
                VcnGatewayConnectionConfigTest.buildTestConfigWithExposedCaps(TEST_CAPS[1]);
        final VcnConfig updatedConfig =
                new VcnConfig.Builder(mContext).addGatewayConnectionConfig(activeConfig).build();

        mVcn.updateConfig(updatedConfig);
        mTestLooper.dispatchAll();

        final VcnGatewayConnection activeGatewayConnection =
                mVcn.getVcnGatewayConnectionConfigMap().get(activeConfig);
        final VcnGatewayConnection removedGatewayConnection =
                mVcn.getVcnGatewayConnectionConfigMap().get(removedConfig);
        verify(activeGatewayConnection, never()).teardownAsynchronously();
        verify(removedGatewayConnection).teardownAsynchronously();
        verify(mVcnNetworkProvider).resendAllRequests(requestListener);
    }

    @Test
    public void testUpdateConfigExitsSafeMode() {
        final NetworkRequestListener requestListener = verifyAndGetRequestListener();
@@ -261,8 +318,8 @@ public class VcnTest {
        verify(mVcnNetworkProvider, times(2)).registerListener(eq(requestListener));
        assertTrue(mVcn.isActive());
        for (final int[] caps : TEST_CAPS) {
            // Expect each gateway connection created on initial startup, and again with new configs
            verify(mDeps, times(2))
            // Expect each gateway connection created only on initial startup
            verify(mDeps)
                    .newVcnGatewayConnection(
                            eq(mVcnContext),
                            eq(TEST_SUB_GROUP),
@@ -271,4 +328,14 @@ public class VcnTest {
                            any());
        }
    }

    @Test
    public void testIgnoreNetworkRequestWhileInactive() {
        mVcn.setIsActive(false /* isActive */);

        final NetworkRequestListener requestListener = verifyAndGetRequestListener();
        triggerVcnRequestListeners(requestListener);

        verify(mDeps, never()).newVcnGatewayConnection(any(), any(), any(), any(), any());
    }
}