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

Commit 9dfb804a authored by Benedict Wong's avatar Benedict Wong
Browse files

Convert VcnNetworkProvider to use NetworkOffers

This change brings VcnNetworkProvider to full functionality in the new
NetworkProvider paradigm, where NetworkProviders offer networks, and are
notified based on NetworkOfferCallbacks.

Bug: 185204197
Test: atest FrameworksVcnTests
Change-Id: I88c69c0be9f6fd81839fb1595ed00341001694a5
parent 183576c8
Loading
Loading
Loading
Loading
+6 −1
Original line number Diff line number Diff line
@@ -79,7 +79,12 @@ public final class VcnGatewayConnectionConfig {
    @VisibleForTesting(visibility = Visibility.PRIVATE)
    static final int MIN_MTU_V6 = 1280;

    private static final Set<Integer> ALLOWED_CAPABILITIES;
    /**
     * The set of allowed capabilities for exposed capabilities.
     *
     * @hide
     */
    public static final Set<Integer> ALLOWED_CAPABILITIES;

    static {
        Set<Integer> allowedCaps = new ArraySet<>();
+1 −2
Original line number Diff line number Diff line
@@ -362,8 +362,7 @@ public class VcnManagementService extends IVcnManagementService.Stub {

    /** Notifies the VcnManagementService that external dependencies can be set up. */
    public void systemReady() {
        mContext.getSystemService(ConnectivityManager.class)
                .registerNetworkProvider(mNetworkProvider);
        mNetworkProvider.register();
        mContext.getSystemService(ConnectivityManager.class)
                .registerNetworkCallback(
                        new NetworkRequest.Builder().clearCapabilities().build(),
+87 −6
Original line number Diff line number Diff line
@@ -16,12 +16,24 @@

package com.android.server.vcn;

import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
import static android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;

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

import android.annotation.NonNull;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkCapabilities;
import android.net.NetworkProvider;
import android.net.NetworkRequest;
import android.net.NetworkScore;
import android.net.vcn.VcnGatewayConnectionConfig;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.Looper;
import android.util.ArraySet;
import android.util.Slog;
@@ -30,7 +42,9 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting.Visibility;
import com.android.internal.util.IndentingPrintWriter;

import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Executor;

/**
 * VCN Network Provider routes NetworkRequests to listeners to bring up tunnels as needed.
@@ -45,6 +59,10 @@ public class VcnNetworkProvider extends NetworkProvider {

    private final Set<NetworkRequestListener> mListeners = new ArraySet<>();

    private final Context mContext;
    private final Handler mHandler;
    private final Dependencies mDeps;

    /**
     * Cache of NetworkRequest(s).
     *
@@ -52,8 +70,59 @@ public class VcnNetworkProvider extends NetworkProvider {
     */
    private final Set<NetworkRequest> mRequests = new ArraySet<>();

    public VcnNetworkProvider(Context context, Looper looper) {
        super(context, looper, VcnNetworkProvider.class.getSimpleName());
    public VcnNetworkProvider(@NonNull Context context, @NonNull Looper looper) {
        this(context, looper, new Dependencies());
    }

    @VisibleForTesting(visibility = Visibility.PRIVATE)
    public VcnNetworkProvider(
            @NonNull Context context, @NonNull Looper looper, @NonNull Dependencies dependencies) {
        super(
                Objects.requireNonNull(context, "Missing context"),
                Objects.requireNonNull(looper, "Missing looper"),
                TAG);

        mContext = context;
        mHandler = new Handler(looper);
        mDeps = Objects.requireNonNull(dependencies, "Missing dependencies");
    }

    /** Registers this VcnNetworkProvider and a generic network offer with ConnectivityService. */
    public void register() {
        mContext.getSystemService(ConnectivityManager.class).registerNetworkProvider(this);
        mDeps.registerNetworkOffer(
                this,
                Vcn.getNetworkScore(), // score filter
                buildCapabilityFilter(),
                new HandlerExecutor(mHandler),
                new NetworkOfferCallback() {
                    @Override
                    public void onNetworkNeeded(@NonNull NetworkRequest request) {
                        handleNetworkRequested(request);
                    }

                    @Override
                    public void onNetworkUnneeded(@NonNull NetworkRequest request) {
                        handleNetworkRequestWithdrawn(request);
                    }
                });
    }

    /** Builds the filter for NetworkRequests that can be served by the VcnNetworkProvider. */
    private NetworkCapabilities buildCapabilityFilter() {
        final NetworkCapabilities.Builder builder =
                new NetworkCapabilities.Builder()
                        .addTransportType(TRANSPORT_CELLULAR)
                        .addCapability(NET_CAPABILITY_TRUSTED)
                        .addCapability(NET_CAPABILITY_NOT_RESTRICTED)
                        .addCapability(NET_CAPABILITY_NOT_VPN)
                        .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);

        for (int cap : VcnGatewayConnectionConfig.ALLOWED_CAPABILITIES) {
            builder.addCapability(cap);
        }

        return builder.build();
    }

    /**
@@ -88,8 +157,7 @@ public class VcnNetworkProvider extends NetworkProvider {
        listener.onNetworkRequested(request);
    }

    @Override
    public void onNetworkRequested(@NonNull NetworkRequest request, int score, int providerId) {
    private void handleNetworkRequested(@NonNull NetworkRequest request) {
        if (VDBG) {
            Slog.v(TAG, "Network requested: Request = " + request);
        }
@@ -103,8 +171,7 @@ public class VcnNetworkProvider extends NetworkProvider {
        }
    }

    @Override
    public void onNetworkRequestWithdrawn(@NonNull NetworkRequest request) {
    private void handleNetworkRequestWithdrawn(@NonNull NetworkRequest request) {
        mRequests.remove(request);
    }

@@ -140,4 +207,18 @@ public class VcnNetworkProvider extends NetworkProvider {

        pw.decreaseIndent();
    }

    /** Proxy class for dependencies used for testing. */
    @VisibleForTesting(visibility = Visibility.PRIVATE)
    public static class Dependencies {
        /** Registers a given network offer for the given provider. */
        public void registerNetworkOffer(
                @NonNull VcnNetworkProvider provider,
                @NonNull NetworkScore score,
                @NonNull NetworkCapabilities capabilitiesFilter,
                @NonNull Executor executor,
                @NonNull NetworkOfferCallback callback) {
            provider.registerNetworkOffer(score, capabilitiesFilter, executor, callback);
        }
    }
}
+46 −5
Original line number Diff line number Diff line
@@ -16,12 +16,18 @@

package com.android.server.vcn;

import static android.net.NetworkProvider.NetworkOfferCallback;

import static org.mockito.Matchers.any;
import static org.mockito.Matchers.argThat;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;

import android.annotation.NonNull;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkRequest;
import android.os.test.TestLooper;

@@ -33,6 +39,7 @@ import com.android.server.vcn.VcnNetworkProvider.NetworkRequestListener;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;

import java.util.ArrayList;
import java.util.List;
@@ -47,6 +54,8 @@ public class VcnNetworkProviderTest {
    @NonNull private final Context mContext;
    @NonNull private final TestLooper mTestLooper;

    @NonNull private VcnNetworkProvider.Dependencies mDeps;
    @NonNull private ConnectivityManager mConnMgr;
    @NonNull private VcnNetworkProvider mVcnNetworkProvider;
    @NonNull private NetworkRequestListener mListener;

@@ -57,16 +66,47 @@ public class VcnNetworkProviderTest {

    @Before
    public void setUp() throws Exception {
        mVcnNetworkProvider = new VcnNetworkProvider(mContext, mTestLooper.getLooper());
        mDeps = mock(VcnNetworkProvider.Dependencies.class);
        mConnMgr = mock(ConnectivityManager.class);
        VcnTestUtils.setupSystemService(
                mContext, mConnMgr, Context.CONNECTIVITY_SERVICE, ConnectivityManager.class);

        mVcnNetworkProvider = new VcnNetworkProvider(mContext, mTestLooper.getLooper(), mDeps);
        mListener = mock(NetworkRequestListener.class);
    }

    private NetworkOfferCallback verifyRegisterAndGetOfferCallback() throws Exception {
        mVcnNetworkProvider.register();

        final ArgumentCaptor<NetworkOfferCallback> cbCaptor =
                ArgumentCaptor.forClass(NetworkOfferCallback.class);

        verify(mConnMgr).registerNetworkProvider(eq(mVcnNetworkProvider));
        verify(mDeps)
                .registerNetworkOffer(
                        eq(mVcnNetworkProvider),
                        argThat(
                                score ->
                                        score.getLegacyInt()
                                                == Vcn.getNetworkScore().getLegacyInt()),
                        any(),
                        any(),
                        cbCaptor.capture());

        return cbCaptor.getValue();
    }

    @Test
    public void testRegister() throws Exception {
        verifyRegisterAndGetOfferCallback();
    }

    @Test
    public void testRequestsPassedToRegisteredListeners() throws Exception {
        mVcnNetworkProvider.registerListener(mListener);

        final NetworkRequest request = mock(NetworkRequest.class);
        mVcnNetworkProvider.onNetworkRequested(request, TEST_SCORE_UNSATISFIED, TEST_PROVIDER_ID);
        verifyRegisterAndGetOfferCallback().onNetworkNeeded(request);
        verify(mListener).onNetworkRequested(request);
    }

@@ -76,13 +116,14 @@ public class VcnNetworkProviderTest {
        mVcnNetworkProvider.unregisterListener(mListener);

        final NetworkRequest request = mock(NetworkRequest.class);
        mVcnNetworkProvider.onNetworkRequested(request, TEST_SCORE_UNSATISFIED, TEST_PROVIDER_ID);
        verifyRegisterAndGetOfferCallback().onNetworkNeeded(request);
        verifyNoMoreInteractions(mListener);
    }

    @Test
    public void testCachedRequestsPassedOnRegister() throws Exception {
        final List<NetworkRequest> requests = new ArrayList<>();
        final NetworkOfferCallback offerCb = verifyRegisterAndGetOfferCallback();

        for (int i = 0; i < 10; i++) {
            // Build unique network requests; in this case, iterate down the capabilities as a way
@@ -91,12 +132,12 @@ public class VcnNetworkProviderTest {
                    new NetworkRequest.Builder().clearCapabilities().addCapability(i).build();

            requests.add(request);
            mVcnNetworkProvider.onNetworkRequested(request, i, i + 1);
            offerCb.onNetworkNeeded(request);
        }

        // Remove one, and verify that it is never sent to the listeners.
        final NetworkRequest removed = requests.remove(0);
        mVcnNetworkProvider.onNetworkRequestWithdrawn(removed);
        offerCb.onNetworkUnneeded(removed);

        mVcnNetworkProvider.registerListener(mListener);
        for (NetworkRequest request : requests) {