diff --git a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java index d59ad6f888641..86cd23d1e9427 100644 --- a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java +++ b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java @@ -83,7 +83,12 @@ public final class VcnGatewayConnectionConfig { @VisibleForTesting(visibility = Visibility.PRIVATE) static final int MIN_MTU_V6 = 1280; - private static final Set ALLOWED_CAPABILITIES; + /** + * The set of allowed capabilities for exposed capabilities. + * + * @hide + */ + public static final Set ALLOWED_CAPABILITIES; static { Set allowedCaps = new ArraySet<>(); diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java index 171805245e45a..70b0fc126ed19 100644 --- a/services/core/java/com/android/server/VcnManagementService.java +++ b/services/core/java/com/android/server/VcnManagementService.java @@ -372,8 +372,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(), diff --git a/services/core/java/com/android/server/vcn/VcnNetworkProvider.java b/services/core/java/com/android/server/vcn/VcnNetworkProvider.java index ebabe2a66491f..31ee2477fa64c 100644 --- a/services/core/java/com/android/server/vcn/VcnNetworkProvider.java +++ b/services/core/java/com/android/server/vcn/VcnNetworkProvider.java @@ -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 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 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) { if (VDBG) { Slog.v(TAG, "Network request withdrawn: Request = " + request); } @@ -144,4 +211,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); + } + } } diff --git a/tests/vcn/java/com/android/server/vcn/VcnNetworkProviderTest.java b/tests/vcn/java/com/android/server/vcn/VcnNetworkProviderTest.java index 79cf746f105fa..72db55b3f4c57 100644 --- a/tests/vcn/java/com/android/server/vcn/VcnNetworkProviderTest.java +++ b/tests/vcn/java/com/android/server/vcn/VcnNetworkProviderTest.java @@ -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 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 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) {