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

Commit bd1085ef authored by Benedict Wong's avatar Benedict Wong
Browse files

Switch to using status codes for Vcn

This change adds status codes, and switches the Vcn class to use these
to signal safe mode status. Additionally, this allows a distinction
between a VCN that is quitting and one that is simply in safe mode,
providing potential for an opportunistic safe mode where the VCN will
continue to attempt to serve NetworkRequests while not restricting
underlying networks.

Bug: 183174340
Bug: 181789060
Test: atest FrameworksVcnTests
Change-Id: I9f2b2d0d8d7b3ade19ca5adcd48cc920171bf8c3
parent dedc39bc
Loading
Loading
Loading
Loading
+15 −12
Original line number Diff line number Diff line
@@ -40,7 +40,6 @@ import android.net.vcn.IVcnManagementService;
import android.net.vcn.IVcnStatusCallback;
import android.net.vcn.IVcnUnderlyingNetworkPolicyListener;
import android.net.vcn.VcnConfig;
import android.net.vcn.VcnManager;
import android.net.vcn.VcnManager.VcnErrorCode;
import android.net.vcn.VcnManager.VcnStatusCode;
import android.net.vcn.VcnUnderlyingNetworkPolicy;
@@ -532,11 +531,12 @@ public class VcnManagementService extends IVcnManagementService.Stub {

        if (mVcns.containsKey(subscriptionGroup)) {
            final Vcn vcn = mVcns.get(subscriptionGroup);
            final boolean isActive = vcn.isActive();
            final int status = vcn.getStatus();
            vcn.updateConfig(config);

            // TODO(b/183174340): Remove this once opportunistic-safe-mode is supported
            // Only notify VcnStatusCallbacks if this VCN was previously in Safe Mode
            if (!isActive) {
            if (status == VCN_STATUS_CODE_SAFE_MODE) {
                // TODO(b/181789060): invoke asynchronously after Vcn notifies through VcnCallback
                notifyAllPermissionedStatusCallbacksLocked(
                        subscriptionGroup, VCN_STATUS_CODE_ACTIVE);
@@ -769,7 +769,7 @@ public class VcnManagementService extends IVcnManagementService.Stub {
            synchronized (mLock) {
                final Vcn vcn = mVcns.get(subGrp);
                if (vcn != null) {
                    if (vcn.isActive()) {
                    if (vcn.getStatus() == VCN_STATUS_CODE_ACTIVE) {
                        isVcnManagedNetwork = true;
                    }

@@ -880,20 +880,23 @@ public class VcnManagementService extends IVcnManagementService.Stub {
                // now that callback is registered, send it the VCN's current status
                final VcnConfig vcnConfig = mConfigs.get(subGroup);
                final Vcn vcn = mVcns.get(subGroup);
                final int vcnStatus;
                final int vcnStatus =
                        vcn == null ? VCN_STATUS_CODE_NOT_CONFIGURED : vcn.getStatus();
                final int resultStatus;
                if (vcnConfig == null || !isCallbackPermissioned(cbInfo, subGroup)) {
                    vcnStatus = VcnManager.VCN_STATUS_CODE_NOT_CONFIGURED;
                    resultStatus = VCN_STATUS_CODE_NOT_CONFIGURED;
                } else if (vcn == null) {
                    vcnStatus = VcnManager.VCN_STATUS_CODE_INACTIVE;
                } else if (vcn.isActive()) {
                    vcnStatus = VcnManager.VCN_STATUS_CODE_ACTIVE;
                    resultStatus = VCN_STATUS_CODE_INACTIVE;
                } else if (vcnStatus == VCN_STATUS_CODE_ACTIVE
                        || vcnStatus == VCN_STATUS_CODE_SAFE_MODE) {
                    resultStatus = vcnStatus;
                } else {
                    // TODO(b/181789060): create Vcn.getStatus() and Log.WTF() for unknown status
                    vcnStatus = VcnManager.VCN_STATUS_CODE_SAFE_MODE;
                    Slog.wtf(TAG, "Unknown VCN status: " + vcnStatus);
                    resultStatus = VCN_STATUS_CODE_NOT_CONFIGURED;
                }

                try {
                    cbInfo.mCallback.onVcnStatusChanged(vcnStatus);
                    cbInfo.mCallback.onVcnStatusChanged(resultStatus);
                } catch (RemoteException e) {
                    Slog.d(TAG, "VcnStatusCallback threw on VCN status change", e);
                }
+32 −26
Original line number Diff line number Diff line
@@ -17,6 +17,9 @@
package com.android.server.vcn;

import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED;
import static android.net.vcn.VcnManager.VCN_STATUS_CODE_ACTIVE;
import static android.net.vcn.VcnManager.VCN_STATUS_CODE_INACTIVE;
import static android.net.vcn.VcnManager.VCN_STATUS_CODE_SAFE_MODE;

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

@@ -44,7 +47,6 @@ import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * Represents an single instance of a VCN.
@@ -137,17 +139,14 @@ public class Vcn extends Handler {
    @NonNull private TelephonySubscriptionSnapshot mLastSnapshot;

    /**
     * Whether this Vcn instance is active and running.
     * The current status of this Vcn instance
     *
     * <p>The value will be {@code true} while running. It will be {@code false} if the VCN has been
     * shut down or has entered safe mode.
     *
     * <p>This AtomicBoolean is required in order to ensure consistency and correctness across
     * multiple threads. Unlike the rest of the Vcn, this is queried synchronously on Binder threads
     * from VcnManagementService, and therefore cannot rely on guarantees of running on the VCN
     * Looper.
     * <p>The value will be {@link VCN_STATUS_CODE_ACTIVE} while all VcnGatewayConnections are in
     * good standing, {@link VCN_STATUS_CODE_SAFE_MODE} if any VcnGatewayConnections are in safe
     * mode, and {@link VCN_STATUS_CODE_INACTIVE} once a teardown has been commanded.
     */
    private final AtomicBoolean mIsActive = new AtomicBoolean(true);
    // Accessed from different threads, but always under lock in VcnManagementService
    private volatile int mCurrentStatus = VCN_STATUS_CODE_ACTIVE;

    public Vcn(
            @NonNull VcnContext vcnContext,
@@ -199,9 +198,15 @@ public class Vcn extends Handler {
        sendMessageAtFrontOfQueue(obtainMessage(MSG_CMD_TEARDOWN));
    }

    /** Synchronously checks whether this Vcn is active. */
    public boolean isActive() {
        return mIsActive.get();
    /** Synchronously retrieves the current status code. */
    public int getStatus() {
        return mCurrentStatus;
    }

    /** Sets the status of this VCN */
    @VisibleForTesting(visibility = Visibility.PRIVATE)
    public void setStatus(int status) {
        mCurrentStatus = status;
    }

    /** Get current Gateways for testing purposes */
@@ -217,12 +222,6 @@ public class Vcn extends Handler {
        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) {
@@ -264,7 +263,8 @@ public class Vcn extends Handler {

        mConfig = config;

        if (mIsActive.getAndSet(true)) {
        // TODO(b/183174340): Remove this once opportunistic safe mode is supported.
        if (mCurrentStatus == VCN_STATUS_CODE_ACTIVE) {
            // VCN is already active - teardown any GatewayConnections whose configs have been
            // removed and get all current requests
            for (final Entry<VcnGatewayConnectionConfig, VcnGatewayConnection> entry :
@@ -288,11 +288,15 @@ public class Vcn extends Handler {
            // Trigger a re-evaluation of all NetworkRequests (to make sure any that can be
            // satisfied start a new GatewayConnection)
            mVcnContext.getVcnNetworkProvider().resendAllRequests(mRequestListener);
        } else {
        } else if (mCurrentStatus == VCN_STATUS_CODE_SAFE_MODE) {
            // 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);
        } else {
            // Ignored; VCN was not active; config updates ignored.
            return;
        }
        mCurrentStatus = VCN_STATUS_CODE_ACTIVE;
    }

    private void handleTeardown() {
@@ -302,18 +306,20 @@ public class Vcn extends Handler {
            gatewayConnection.teardownAsynchronously();
        }

        mIsActive.set(false);
        mCurrentStatus = VCN_STATUS_CODE_INACTIVE;
    }

    private void handleEnterSafeMode() {
        // TODO(b/183174340): Remove this once opportunistic-safe-mode is supported
        handleTeardown();

        mCurrentStatus = VCN_STATUS_CODE_SAFE_MODE;
        mVcnCallback.onEnteredSafeMode();
    }

    private void handleNetworkRequested(
            @NonNull NetworkRequest request, int score, int providerId) {
        if (!isActive()) {
        if (mCurrentStatus != VCN_STATUS_CODE_ACTIVE) {
            Slog.v(getLogTag(), "Received NetworkRequest while inactive. Ignore for now");
            return;
        }
@@ -370,8 +376,8 @@ 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), but only if the Vcn is still active
        if (isActive()) {
        // start a new GatewayConnection), but only if the Vcn is still alive
        if (mCurrentStatus == VCN_STATUS_CODE_ACTIVE) {
            mVcnContext.getVcnNetworkProvider().resendAllRequests(mRequestListener);
        }
    }
@@ -379,7 +385,7 @@ public class Vcn extends Handler {
    private void handleSubscriptionsChanged(@NonNull TelephonySubscriptionSnapshot snapshot) {
        mLastSnapshot = snapshot;

        if (isActive()) {
        if (mCurrentStatus == VCN_STATUS_CODE_ACTIVE) {
            for (VcnGatewayConnection gatewayConnection : mVcnGatewayConnections.values()) {
                gatewayConnection.updateSubscriptionSnapshot(mLastSnapshot);
            }
+5 −1
Original line number Diff line number Diff line
@@ -19,6 +19,8 @@ package com.android.server;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.net.vcn.VcnManager.VCN_STATUS_CODE_ACTIVE;
import static android.net.vcn.VcnManager.VCN_STATUS_CODE_SAFE_MODE;
import static android.telephony.TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
import static android.telephony.TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS;

@@ -695,7 +697,9 @@ public class VcnManagementServiceTest {
                hasCarrierPrivileges);

        final Vcn vcn = startAndGetVcnInstance(subGrp);
        doReturn(isVcnActive).when(vcn).isActive();
        doReturn(isVcnActive ? VCN_STATUS_CODE_ACTIVE : VCN_STATUS_CODE_SAFE_MODE)
                .when(vcn)
                .getStatus();

        doReturn(true)
                .when(mLocationPermissionChecker)
+12 −10
Original line number Diff line number Diff line
@@ -19,10 +19,12 @@ 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 android.net.vcn.VcnManager.VCN_STATUS_CODE_ACTIVE;
import static android.net.vcn.VcnManager.VCN_STATUS_CODE_INACTIVE;
import static android.net.vcn.VcnManager.VCN_STATUS_CODE_SAFE_MODE;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.argThat;
import static org.mockito.Matchers.eq;
@@ -142,7 +144,7 @@ public class VcnTest {
        mTestLooper.dispatchAll();
    }

    private void verifyUpdateSubscriptionSnapshotNotifiesConnectionGateways(boolean isActive) {
    private void verifyUpdateSubscriptionSnapshotNotifiesGatewayConnections(int status) {
        final NetworkRequestListener requestListener = verifyAndGetRequestListener();
        startVcnGatewayWithCapabilities(requestListener, TEST_CAPS[0]);

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

        mVcn.setIsActive(isActive);
        mVcn.setStatus(status);

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

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

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

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

    private void triggerVcnRequestListeners(NetworkRequestListener requestListener) {
@@ -201,7 +203,7 @@ public class VcnTest {
    private void verifySafeMode(
            NetworkRequestListener requestListener,
            Set<VcnGatewayConnection> expectedGatewaysTornDown) {
        assertFalse(mVcn.isActive());
        assertEquals(VCN_STATUS_CODE_SAFE_MODE, mVcn.getStatus());
        for (final VcnGatewayConnection gatewayConnection : expectedGatewaysTornDown) {
            verify(gatewayConnection).teardownAsynchronously();
        }
@@ -319,7 +321,7 @@ public class VcnTest {

        // Registered on start, then re-registered with new configs
        verify(mVcnNetworkProvider, times(2)).registerListener(eq(requestListener));
        assertTrue(mVcn.isActive());
        assertEquals(VCN_STATUS_CODE_ACTIVE, mVcn.getStatus());
        for (final int[] caps : TEST_CAPS) {
            // Expect each gateway connection created only on initial startup
            verify(mDeps)
@@ -334,7 +336,7 @@ public class VcnTest {

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

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