summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenedict Wong <benedictwong@google.com>2020-08-10 17:54:33 -0700
committerBenedict Wong <benedictwong@google.com>2020-12-16 17:51:58 -0800
commit264973663ce84e73a0a88aeff6b6ec1a7971afb0 (patch)
treed2cfae9d8cde431d1d7313554f95693a59f22510
parentad3271875a1e1c083bf390d2f8cf960f5b8f297c (diff)
Implement basic VcnConfig and VcnGatewayConnectionConfig
This change adds some of the basic fields in VcnConfig and VcnGatewayConnectionConfig, plus adds a persistability layer to ensure all VcnConfig(s) are disk-stable. Bug: 163602123 Bug: 163594033 Test: New tests added, passing Change-Id: I2e632532809e7768b284be376f2b0a77f634fef5
-rw-r--r--Android.bp1
-rw-r--r--core/java/android/net/vcn/VcnConfig.java109
-rw-r--r--core/java/android/net/vcn/VcnGatewayConnectionConfig.java401
-rw-r--r--services/core/java/com/android/server/vcn/Android.bp4
-rw-r--r--tests/vcn/java/android/net/vcn/VcnConfigTest.java83
-rw-r--r--tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java143
-rw-r--r--tests/vcn/java/com/android/server/VcnManagementServiceTest.java8
7 files changed, 726 insertions, 23 deletions
diff --git a/Android.bp b/Android.bp
index 26e716530426..4b3c22da5c7c 100644
--- a/Android.bp
+++ b/Android.bp
@@ -314,6 +314,7 @@ filegroup {
":framework-telecomm-sources",
":framework-telephony-common-sources",
":framework-telephony-sources",
+ ":framework-vcn-util-sources",
":framework-wifi-annotations",
":framework-wifi-non-updatable-sources",
":PacProcessor-aidl-sources",
diff --git a/core/java/android/net/vcn/VcnConfig.java b/core/java/android/net/vcn/VcnConfig.java
index 148acf130857..d4a3fa7411b1 100644
--- a/core/java/android/net/vcn/VcnConfig.java
+++ b/core/java/android/net/vcn/VcnConfig.java
@@ -15,30 +15,104 @@
*/
package android.net.vcn;
+import static com.android.internal.annotations.VisibleForTesting.Visibility;
+
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.PersistableBundle;
+import android.util.ArraySet;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
+import com.android.server.vcn.util.PersistableBundleUtils;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Objects;
+import java.util.Set;
/**
* This class represents a configuration for a Virtual Carrier Network.
*
+ * <p>Each {@link VcnGatewayConnectionConfig} instance added represents a connection that will be
+ * brought up on demand based on active {@link NetworkRequest}(s).
+ *
+ * @see VcnManager for more information on the Virtual Carrier Network feature
* @hide
*/
public final class VcnConfig implements Parcelable {
@NonNull private static final String TAG = VcnConfig.class.getSimpleName();
- private VcnConfig() {
+ private static final String GATEWAY_CONNECTION_CONFIGS_KEY = "mGatewayConnectionConfigs";
+ @NonNull private final Set<VcnGatewayConnectionConfig> mGatewayConnectionConfigs;
+
+ private VcnConfig(@NonNull Set<VcnGatewayConnectionConfig> tunnelConfigs) {
+ mGatewayConnectionConfigs = Collections.unmodifiableSet(tunnelConfigs);
+
validate();
}
- // TODO: Implement getters, validators, etc
/**
- * Validates this configuration.
+ * Deserializes a VcnConfig from a PersistableBundle.
*
* @hide
*/
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ public VcnConfig(@NonNull PersistableBundle in) {
+ final PersistableBundle gatewayConnectionConfigsBundle =
+ in.getPersistableBundle(GATEWAY_CONNECTION_CONFIGS_KEY);
+ mGatewayConnectionConfigs =
+ new ArraySet<>(
+ PersistableBundleUtils.toList(
+ gatewayConnectionConfigsBundle, VcnGatewayConnectionConfig::new));
+
+ validate();
+ }
+
private void validate() {
- // TODO: implement validation logic
+ Preconditions.checkCollectionNotEmpty(
+ mGatewayConnectionConfigs, "gatewayConnectionConfigs");
+ }
+
+ /** Retrieves the set of configured tunnels. */
+ @NonNull
+ public Set<VcnGatewayConnectionConfig> getGatewayConnectionConfigs() {
+ return Collections.unmodifiableSet(mGatewayConnectionConfigs);
+ }
+
+ /**
+ * Serializes this object to a PersistableBundle.
+ *
+ * @hide
+ */
+ @NonNull
+ public PersistableBundle toPersistableBundle() {
+ final PersistableBundle result = new PersistableBundle();
+
+ final PersistableBundle gatewayConnectionConfigsBundle =
+ PersistableBundleUtils.fromList(
+ new ArrayList<>(mGatewayConnectionConfigs),
+ VcnGatewayConnectionConfig::toPersistableBundle);
+ result.putPersistableBundle(GATEWAY_CONNECTION_CONFIGS_KEY, gatewayConnectionConfigsBundle);
+
+ return result;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mGatewayConnectionConfigs);
+ }
+
+ @Override
+ public boolean equals(@Nullable Object other) {
+ if (!(other instanceof VcnConfig)) {
+ return false;
+ }
+
+ final VcnConfig rhs = (VcnConfig) other;
+ return mGatewayConnectionConfigs.equals(rhs.mGatewayConnectionConfigs);
}
// Parcelable methods
@@ -49,15 +123,16 @@ public final class VcnConfig implements Parcelable {
}
@Override
- public void writeToParcel(Parcel out, int flags) {}
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeParcelable(toPersistableBundle(), flags);
+ }
@NonNull
public static final Parcelable.Creator<VcnConfig> CREATOR =
new Parcelable.Creator<VcnConfig>() {
@NonNull
public VcnConfig createFromParcel(Parcel in) {
- // TODO: Ensure all methods are pulled from the parcels
- return new VcnConfig();
+ return new VcnConfig((PersistableBundle) in.readParcelable(null));
}
@NonNull
@@ -68,7 +143,23 @@ public final class VcnConfig implements Parcelable {
/** This class is used to incrementally build {@link VcnConfig} objects. */
public static class Builder {
- // TODO: Implement this builder
+ @NonNull
+ private final Set<VcnGatewayConnectionConfig> mGatewayConnectionConfigs = new ArraySet<>();
+
+ /**
+ * Adds a configuration for an individual gateway connection.
+ *
+ * @param gatewayConnectionConfig the configuration for an individual gateway connection
+ * @return this {@link Builder} instance, for chaining
+ */
+ @NonNull
+ public Builder addGatewayConnectionConfig(
+ @NonNull VcnGatewayConnectionConfig gatewayConnectionConfig) {
+ Objects.requireNonNull(gatewayConnectionConfig, "gatewayConnectionConfig was null");
+
+ mGatewayConnectionConfigs.add(gatewayConnectionConfig);
+ return this;
+ }
/**
* Builds and validates the VcnConfig.
@@ -77,7 +168,7 @@ public final class VcnConfig implements Parcelable {
*/
@NonNull
public VcnConfig build() {
- return new VcnConfig();
+ return new VcnConfig(mGatewayConnectionConfigs);
}
}
}
diff --git a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
index 8160edc87440..039360a69a3a 100644
--- a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
+++ b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
@@ -15,7 +15,27 @@
*/
package android.net.vcn;
+import static android.net.NetworkCapabilities.NetCapability;
+
+import static com.android.internal.annotations.VisibleForTesting.Visibility;
+
+import android.annotation.IntRange;
import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.NetworkCapabilities;
+import android.os.PersistableBundle;
+import android.util.ArraySet;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
+import com.android.server.vcn.util.PersistableBundleUtils;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
/**
* This class represents a configuration for a connection to a Virtual Carrier Network gateway.
@@ -49,38 +69,399 @@ import android.annotation.NonNull;
* <li>{@link android.net.NetworkCapabilities.NET_CAPABILITY_MCX}
* </ul>
*
+ * <p>The meteredness and roaming of the VCN {@link Network} will be determined by that of the
+ * underlying Network(s).
+ *
* @hide
*/
public final class VcnGatewayConnectionConfig {
- private VcnGatewayConnectionConfig() {
+ // TODO: Use MIN_MTU_V6 once it is public, @hide
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ static final int MIN_MTU_V6 = 1280;
+
+ private static final Set<Integer> ALLOWED_CAPABILITIES;
+
+ static {
+ Set<Integer> allowedCaps = new ArraySet<>();
+ allowedCaps.add(NetworkCapabilities.NET_CAPABILITY_MMS);
+ allowedCaps.add(NetworkCapabilities.NET_CAPABILITY_SUPL);
+ allowedCaps.add(NetworkCapabilities.NET_CAPABILITY_DUN);
+ allowedCaps.add(NetworkCapabilities.NET_CAPABILITY_FOTA);
+ allowedCaps.add(NetworkCapabilities.NET_CAPABILITY_IMS);
+ allowedCaps.add(NetworkCapabilities.NET_CAPABILITY_CBS);
+ allowedCaps.add(NetworkCapabilities.NET_CAPABILITY_IA);
+ allowedCaps.add(NetworkCapabilities.NET_CAPABILITY_RCS);
+ allowedCaps.add(NetworkCapabilities.NET_CAPABILITY_XCAP);
+ allowedCaps.add(NetworkCapabilities.NET_CAPABILITY_EIMS);
+ allowedCaps.add(NetworkCapabilities.NET_CAPABILITY_INTERNET);
+ allowedCaps.add(NetworkCapabilities.NET_CAPABILITY_MCX);
+
+ ALLOWED_CAPABILITIES = Collections.unmodifiableSet(allowedCaps);
+ }
+
+ private static final int DEFAULT_MAX_MTU = 1500;
+
+ /**
+ * The maximum number of retry intervals that may be specified.
+ *
+ * <p>Limited to ensure an upper bound on config sizes.
+ */
+ private static final int MAX_RETRY_INTERVAL_COUNT = 10;
+
+ /**
+ * The minimum allowable repeating retry interval
+ *
+ * <p>To ensure the device is not constantly being woken up, this retry interval MUST be greater
+ * than this value.
+ *
+ * @see {@link Builder#setRetryInterval()}
+ */
+ private static final long MINIMUM_REPEATING_RETRY_INTERVAL_MS = TimeUnit.MINUTES.toMillis(15);
+
+ private static final long[] DEFAULT_RETRY_INTERVALS_MS =
+ new long[] {
+ TimeUnit.SECONDS.toMillis(1),
+ TimeUnit.SECONDS.toMillis(2),
+ TimeUnit.SECONDS.toMillis(5),
+ TimeUnit.SECONDS.toMillis(30),
+ TimeUnit.MINUTES.toMillis(1),
+ TimeUnit.MINUTES.toMillis(5),
+ TimeUnit.MINUTES.toMillis(15)
+ };
+
+ private static final String EXPOSED_CAPABILITIES_KEY = "mExposedCapabilities";
+ @NonNull private final Set<Integer> mExposedCapabilities;
+
+ private static final String UNDERLYING_CAPABILITIES_KEY = "mUnderlyingCapabilities";
+ @NonNull private final Set<Integer> mUnderlyingCapabilities;
+
+ // TODO: Add Ike/ChildSessionParams as a subclass - maybe VcnIkeGatewayConnectionConfig
+
+ private static final String MAX_MTU_KEY = "mMaxMtu";
+ private final int mMaxMtu;
+
+ private static final String RETRY_INTERVAL_MS_KEY = "mRetryIntervalsMs";
+ @NonNull private final long[] mRetryIntervalsMs;
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ public VcnGatewayConnectionConfig(
+ @NonNull Set<Integer> exposedCapabilities,
+ @NonNull Set<Integer> underlyingCapabilities,
+ @NonNull long[] retryIntervalsMs,
+ @IntRange(from = MIN_MTU_V6) int maxMtu) {
+ mExposedCapabilities = exposedCapabilities;
+ mUnderlyingCapabilities = underlyingCapabilities;
+ mRetryIntervalsMs = retryIntervalsMs;
+ mMaxMtu = maxMtu;
+
+ validate();
+ }
+
+ /** @hide */
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ public VcnGatewayConnectionConfig(@NonNull PersistableBundle in) {
+ final PersistableBundle exposedCapsBundle =
+ in.getPersistableBundle(EXPOSED_CAPABILITIES_KEY);
+ final PersistableBundle underlyingCapsBundle =
+ in.getPersistableBundle(UNDERLYING_CAPABILITIES_KEY);
+
+ mExposedCapabilities = new ArraySet<>(PersistableBundleUtils.toList(
+ exposedCapsBundle, PersistableBundleUtils.INTEGER_DESERIALIZER));
+ mUnderlyingCapabilities = new ArraySet<>(PersistableBundleUtils.toList(
+ underlyingCapsBundle, PersistableBundleUtils.INTEGER_DESERIALIZER));
+ mRetryIntervalsMs = in.getLongArray(RETRY_INTERVAL_MS_KEY);
+ mMaxMtu = in.getInt(MAX_MTU_KEY);
+
validate();
}
- // TODO: Implement getters, validators, etc
+ private void validate() {
+ Preconditions.checkArgument(
+ mExposedCapabilities != null && !mExposedCapabilities.isEmpty(),
+ "exposedCapsBundle was null or empty");
+ for (Integer cap : getAllExposedCapabilities()) {
+ checkValidCapability(cap);
+ }
+
+ Preconditions.checkArgument(
+ mUnderlyingCapabilities != null && !mUnderlyingCapabilities.isEmpty(),
+ "underlyingCapabilities was null or empty");
+ for (Integer cap : getAllUnderlyingCapabilities()) {
+ checkValidCapability(cap);
+ }
+
+ Objects.requireNonNull(mRetryIntervalsMs, "retryIntervalsMs was null");
+ validateRetryInterval(mRetryIntervalsMs);
+
+ Preconditions.checkArgument(
+ mMaxMtu >= MIN_MTU_V6, "maxMtu must be at least IPv6 min MTU (1280)");
+ }
+
+ private static void checkValidCapability(int capability) {
+ Preconditions.checkArgument(
+ ALLOWED_CAPABILITIES.contains(capability),
+ "NetworkCapability " + capability + "out of range");
+ }
+
+ private static void validateRetryInterval(@Nullable long[] retryIntervalsMs) {
+ Preconditions.checkArgument(
+ retryIntervalsMs != null
+ && retryIntervalsMs.length > 0
+ && retryIntervalsMs.length <= MAX_RETRY_INTERVAL_COUNT,
+ "retryIntervalsMs was null, empty or exceed max interval count");
+
+ final long repeatingInterval = retryIntervalsMs[retryIntervalsMs.length - 1];
+ if (repeatingInterval < MINIMUM_REPEATING_RETRY_INTERVAL_MS) {
+ throw new IllegalArgumentException(
+ "Repeating retry interval was too short, must be a minimum of 15 minutes: "
+ + repeatingInterval);
+ }
+ }
/**
- * Validates this configuration
+ * Returns all exposed capabilities.
*
* @hide
*/
- private void validate() {
- // TODO: implement validation logic
+ @NonNull
+ public Set<Integer> getAllExposedCapabilities() {
+ return Collections.unmodifiableSet(mExposedCapabilities);
+ }
+
+ /**
+ * Checks if this config is configured to support/expose a specific capability.
+ *
+ * @param capability the capability to check for
+ */
+ public boolean hasExposedCapability(@NetCapability int capability) {
+ checkValidCapability(capability);
+
+ return mExposedCapabilities.contains(capability);
+ }
+
+ /**
+ * Returns all capabilities required of underlying networks.
+ *
+ * @hide
+ */
+ @NonNull
+ public Set<Integer> getAllUnderlyingCapabilities() {
+ return Collections.unmodifiableSet(mUnderlyingCapabilities);
}
- // Parcelable methods
+ /**
+ * Checks if this config requires an underlying network to have the specified capability.
+ *
+ * @param capability the capability to check for
+ */
+ public boolean requiresUnderlyingCapability(@NetCapability int capability) {
+ checkValidCapability(capability);
+
+ return mUnderlyingCapabilities.contains(capability);
+ }
- /** This class is used to incrementally build {@link VcnGatewayConnectionConfig} objects */
+ /** Retrieves the configured retry intervals. */
+ @NonNull
+ public long[] getRetryIntervalsMs() {
+ return Arrays.copyOf(mRetryIntervalsMs, mRetryIntervalsMs.length);
+ }
+
+ /** Retrieves the maximum MTU allowed for this Gateway Connection. */
+ @IntRange(from = MIN_MTU_V6)
+ public int getMaxMtu() {
+ return mMaxMtu;
+ }
+
+ /**
+ * Converts this config to a PersistableBundle.
+ *
+ * @hide
+ */
+ @NonNull
+ @VisibleForTesting(visibility = Visibility.PROTECTED)
+ public PersistableBundle toPersistableBundle() {
+ final PersistableBundle result = new PersistableBundle();
+
+ final PersistableBundle exposedCapsBundle =
+ PersistableBundleUtils.fromList(
+ new ArrayList<>(mExposedCapabilities),
+ PersistableBundleUtils.INTEGER_SERIALIZER);
+ final PersistableBundle underlyingCapsBundle =
+ PersistableBundleUtils.fromList(
+ new ArrayList<>(mUnderlyingCapabilities),
+ PersistableBundleUtils.INTEGER_SERIALIZER);
+
+ result.putPersistableBundle(EXPOSED_CAPABILITIES_KEY, exposedCapsBundle);
+ result.putPersistableBundle(UNDERLYING_CAPABILITIES_KEY, underlyingCapsBundle);
+ result.putLongArray(RETRY_INTERVAL_MS_KEY, mRetryIntervalsMs);
+ result.putInt(MAX_MTU_KEY, mMaxMtu);
+
+ return result;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(
+ mExposedCapabilities,
+ mUnderlyingCapabilities,
+ Arrays.hashCode(mRetryIntervalsMs),
+ mMaxMtu);
+ }
+
+ @Override
+ public boolean equals(@Nullable Object other) {
+ if (!(other instanceof VcnGatewayConnectionConfig)) {
+ return false;
+ }
+
+ final VcnGatewayConnectionConfig rhs = (VcnGatewayConnectionConfig) other;
+ return mExposedCapabilities.equals(rhs.mExposedCapabilities)
+ && mUnderlyingCapabilities.equals(rhs.mUnderlyingCapabilities)
+ && Arrays.equals(mRetryIntervalsMs, rhs.mRetryIntervalsMs)
+ && mMaxMtu == rhs.mMaxMtu;
+ }
+
+ /** This class is used to incrementally build {@link VcnGatewayConnectionConfig} objects. */
public static class Builder {
- // TODO: Implement this builder
+ @NonNull private final Set<Integer> mExposedCapabilities = new ArraySet();
+ @NonNull private final Set<Integer> mUnderlyingCapabilities = new ArraySet();
+ @NonNull private long[] mRetryIntervalsMs = DEFAULT_RETRY_INTERVALS_MS;
+ private int mMaxMtu = DEFAULT_MAX_MTU;
+
+ // TODO: (b/175829816) Consider VCN-exposed capabilities that may be transport dependent.
+ // Consider the case where the VCN might only expose MMS on WiFi, but defer to MMS
+ // when on Cell.
+
+ /**
+ * Add a capability that this VCN Gateway Connection will support.
+ *
+ * @param exposedCapability the app-facing capability to be exposed by this VCN Gateway
+ * Connection (i.e., the capabilities that this VCN Gateway Connection will support).
+ * @return this {@link Builder} instance, for chaining
+ * @see VcnGatewayConnectionConfig for a list of capabilities may be exposed by a Gateway
+ * Connection
+ */
+ public Builder addExposedCapability(@NetCapability int exposedCapability) {
+ checkValidCapability(exposedCapability);
+
+ mExposedCapabilities.add(exposedCapability);
+ return this;
+ }
+
+ /**
+ * Remove a capability that this VCN Gateway Connection will support.
+ *
+ * @param exposedCapability the app-facing capability to not be exposed by this VCN Gateway
+ * Connection (i.e., the capabilities that this VCN Gateway Connection will support)
+ * @return this {@link Builder} instance, for chaining
+ * @see VcnGatewayConnectionConfig for a list of capabilities may be exposed by a Gateway
+ * Connection
+ */
+ public Builder removeExposedCapability(@NetCapability int exposedCapability) {
+ checkValidCapability(exposedCapability);
+
+ mExposedCapabilities.remove(exposedCapability);
+ return this;
+ }
+
+ /**
+ * Require a capability for Networks underlying this VCN Gateway Connection.
+ *
+ * @param underlyingCapability the capability that a network MUST have in order to be an
+ * underlying network for this VCN Gateway Connection.
+ * @return this {@link Builder} instance, for chaining
+ * @see VcnGatewayConnectionConfig for a list of capabilities may be required of underlying
+ * networks
+ */
+ public Builder addRequiredUnderlyingCapability(@NetCapability int underlyingCapability) {
+ checkValidCapability(underlyingCapability);
+
+ mUnderlyingCapabilities.add(underlyingCapability);
+ return this;
+ }
+
+ /**
+ * Remove a requirement of a capability for Networks underlying this VCN Gateway Connection.
+ *
+ * <p>Calling this method will allow Networks that do NOT have this capability to be
+ * selected as an underlying network for this VCN Gateway Connection. However, underlying
+ * networks MAY still have the removed capability.
+ *
+ * @param underlyingCapability the capability that a network DOES NOT need to have in order
+ * to be an underlying network for this VCN Gateway Connection.
+ * @return this {@link Builder} instance, for chaining
+ * @see VcnGatewayConnectionConfig for a list of capabilities may be required of underlying
+ * networks
+ */
+ public Builder removeRequiredUnderlyingCapability(@NetCapability int underlyingCapability) {
+ checkValidCapability(underlyingCapability);
+
+ mUnderlyingCapabilities.remove(underlyingCapability);
+ return this;
+ }
+
+ /**
+ * Set the retry interval between VCN establishment attempts upon successive failures.
+ *
+ * <p>The last retry interval will be repeated until safe mode is entered, or a connection
+ * is successfully established, at which point the retry timers will be reset. For power
+ * reasons, the last (repeated) retry interval MUST be at least 15 minutes.
+ *
+ * <p>Retry intervals MAY be subject to system power saving modes. That is to say that if
+ * the system enters a power saving mode, the retry may not occur until the device leaves
+ * the specified power saving mode. Intervals are sequential, and intervals will NOT be
+ * skipped if system power saving results in delaying retries (even if it exceed multiple
+ * retry intervals).
+ *
+ * <p>Each Gateway Connection will retry according to the retry intervals configured, but if
+ * safe mode is enabled, all Gateway Connection(s) will be disabled.
+ *
+ * @param retryIntervalsMs an array of between 1 and 10 millisecond intervals after which
+ * the VCN will attempt to retry a session initiation. The last (repeating) retry
+ * interval must be at least 15 minutes. Defaults to: {@code [1s, 2s, 5s, 30s, 1m, 5m,
+ * 15m]}
+ * @return this {@link Builder} instance, for chaining
+ * @see VcnManager for additional discussion on fail-safe mode
+ */
+ @NonNull
+ public Builder setRetryInterval(@NonNull long[] retryIntervalsMs) {
+ validateRetryInterval(retryIntervalsMs);
+
+ mRetryIntervalsMs = retryIntervalsMs;
+ return this;
+ }
+
+ /**
+ * Sets the maximum MTU allowed for this VCN Gateway Connection.
+ *
+ * <p>This MTU is applied to the VCN Gateway Connection exposed Networks, and represents the
+ * MTU of the virtualized network.
+ *
+ * <p>The system may reduce the MTU below the maximum specified based on signals such as the
+ * MTU of the underlying networks (and adjusted for Gateway Connection overhead).
+ *
+ * @param maxMtu the maximum MTU allowed for this Gateway Connection. Must be greater than
+ * the IPv6 minimum MTU of 1280. Defaults to 1500.
+ * @return this {@link Builder} instance, for chaining
+ */
+ @NonNull
+ public Builder setMaxMtu(@IntRange(from = MIN_MTU_V6) int maxMtu) {
+ Preconditions.checkArgument(
+ maxMtu >= MIN_MTU_V6, "maxMtu must be at least IPv6 min MTU (1280)");
+
+ mMaxMtu = maxMtu;
+ return this;
+ }
/**
- * Builds and validates the VcnGatewayConnectionConfig
+ * Builds and validates the VcnGatewayConnectionConfig.
*
* @return an immutable VcnGatewayConnectionConfig instance
*/
@NonNull
public VcnGatewayConnectionConfig build() {
- return new VcnGatewayConnectionConfig();
+ return new VcnGatewayConnectionConfig(
+ mExposedCapabilities, mUnderlyingCapabilities, mRetryIntervalsMs, mMaxMtu);
}
}
}
diff --git a/services/core/java/com/android/server/vcn/Android.bp b/services/core/java/com/android/server/vcn/Android.bp
new file mode 100644
index 000000000000..5ed204fd7640
--- /dev/null
+++ b/services/core/java/com/android/server/vcn/Android.bp
@@ -0,0 +1,4 @@
+filegroup {
+ name: "framework-vcn-util-sources",
+ srcs: ["util/**/*.java"],
+} \ No newline at end of file
diff --git a/tests/vcn/java/android/net/vcn/VcnConfigTest.java b/tests/vcn/java/android/net/vcn/VcnConfigTest.java
new file mode 100644
index 000000000000..77944deb26f1
--- /dev/null
+++ b/tests/vcn/java/android/net/vcn/VcnConfigTest.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.vcn;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import android.os.Parcel;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Collections;
+import java.util.Set;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class VcnConfigTest {
+ private static final Set<VcnGatewayConnectionConfig> GATEWAY_CONNECTION_CONFIGS =
+ Collections.singleton(VcnGatewayConnectionConfigTest.buildTestConfig());
+
+ // Public visibility for VcnManagementServiceTest
+ public static VcnConfig buildTestConfig() {
+ VcnConfig.Builder builder = new VcnConfig.Builder();
+
+ for (VcnGatewayConnectionConfig gatewayConnectionConfig : GATEWAY_CONNECTION_CONFIGS) {
+ builder.addGatewayConnectionConfig(gatewayConnectionConfig);
+ }
+
+ return builder.build();
+ }
+
+ @Test
+ public void testBuilderRequiresGatewayConnectionConfig() {
+ try {
+ new VcnConfig.Builder().build();
+ fail("Expected exception due to no VcnGatewayConnectionConfigs provided");
+ } catch (IllegalArgumentException e) {
+ }
+ }
+
+ @Test
+ public void testBuilderAndGetters() {
+ final VcnConfig config = buildTestConfig();
+
+ assertEquals(GATEWAY_CONNECTION_CONFIGS, config.getGatewayConnectionConfigs());
+ }
+
+ @Test
+ public void testPersistableBundle() {
+ final VcnConfig config = buildTestConfig();
+
+ assertEquals(config, new VcnConfig(config.toPersistableBundle()));
+ }
+
+ @Test
+ public void testParceling() {
+ final VcnConfig config = buildTestConfig();
+
+ Parcel parcel = Parcel.obtain();
+ config.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+
+ assertEquals(config, VcnConfig.CREATOR.createFromParcel(parcel));
+ }
+}
diff --git a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
new file mode 100644
index 000000000000..e98b6ef2b3a6
--- /dev/null
+++ b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.vcn;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import android.net.NetworkCapabilities;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.TimeUnit;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class VcnGatewayConnectionConfigTest {
+ private static final int[] EXPOSED_CAPS =
+ new int[] {
+ NetworkCapabilities.NET_CAPABILITY_INTERNET, NetworkCapabilities.NET_CAPABILITY_MMS
+ };
+ private static final int[] UNDERLYING_CAPS = new int[] {NetworkCapabilities.NET_CAPABILITY_DUN};
+ private static final long[] RETRY_INTERVALS_MS =
+ new long[] {
+ TimeUnit.SECONDS.toMillis(5),
+ TimeUnit.SECONDS.toMillis(30),
+ TimeUnit.MINUTES.toMillis(1),
+ TimeUnit.MINUTES.toMillis(5),
+ TimeUnit.MINUTES.toMillis(15),
+ TimeUnit.MINUTES.toMillis(30)
+ };
+ private static final int MAX_MTU = 1360;
+
+ // Package protected for use in VcnConfigTest
+ static VcnGatewayConnectionConfig buildTestConfig() {
+ final VcnGatewayConnectionConfig.Builder builder =
+ new VcnGatewayConnectionConfig.Builder()
+ .setRetryInterval(RETRY_INTERVALS_MS)
+ .setMaxMtu(MAX_MTU);
+
+ for (int caps : EXPOSED_CAPS) {
+ builder.addExposedCapability(caps);
+ }
+
+ for (int caps : UNDERLYING_CAPS) {
+ builder.addRequiredUnderlyingCapability(caps);
+ }
+
+ return builder.build();
+ }
+
+ @Test
+ public void testBuilderRequiresNonEmptyExposedCaps() {
+ try {
+ new VcnGatewayConnectionConfig.Builder()
+ .addRequiredUnderlyingCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+ .build();
+
+ fail("Expected exception due to invalid exposed capabilities");
+ } catch (IllegalArgumentException e) {
+ }
+ }
+
+ @Test
+ public void testBuilderRequiresNonEmptyUnderlyingCaps() {
+ try {
+ new VcnGatewayConnectionConfig.Builder()
+ .addExposedCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+ .build();
+
+ fail("Expected exception due to invalid required underlying capabilities");
+ } catch (IllegalArgumentException e) {
+ }
+ }
+
+ @Test
+ public void testBuilderRequiresNonNullRetryInterval() {
+ try {
+ new VcnGatewayConnectionConfig.Builder().setRetryInterval(null);
+ fail("Expected exception due to invalid retryIntervalMs");
+ } catch (IllegalArgumentException e) {
+ }
+ }
+
+ @Test
+ public void testBuilderRequiresNonEmptyRetryInterval() {
+ try {
+ new VcnGatewayConnectionConfig.Builder().setRetryInterval(new long[0]);
+ fail("Expected exception due to invalid retryIntervalMs");
+ } catch (IllegalArgumentException e) {
+ }
+ }
+
+ @Test
+ public void testBuilderRequiresValidMtu() {
+ try {
+ new VcnGatewayConnectionConfig.Builder()
+ .setMaxMtu(VcnGatewayConnectionConfig.MIN_MTU_V6 - 1);
+ fail("Expected exception due to invalid mtu");
+ } catch (IllegalArgumentException e) {
+ }
+ }
+
+ @Test
+ public void testBuilderAndGetters() {
+ final VcnGatewayConnectionConfig config = buildTestConfig();
+
+ for (int cap : EXPOSED_CAPS) {
+ config.hasExposedCapability(cap);
+ }
+ for (int cap : UNDERLYING_CAPS) {
+ config.requiresUnderlyingCapability(cap);
+ }
+
+ assertArrayEquals(RETRY_INTERVALS_MS, config.getRetryIntervalsMs());
+ assertEquals(MAX_MTU, config.getMaxMtu());
+ }
+
+ @Test
+ public void testPersistableBundle() {
+ final VcnGatewayConnectionConfig config = buildTestConfig();
+
+ assertEquals(config, new VcnGatewayConnectionConfig(config.toPersistableBundle()));
+ }
+}
diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
index 633cf64bc274..876e07fbce0f 100644
--- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
+++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
@@ -25,7 +25,7 @@ import static org.mockito.Mockito.verify;
import android.content.Context;
import android.net.ConnectivityManager;
-import android.net.vcn.VcnConfig;
+import android.net.vcn.VcnConfigTest;
import android.os.ParcelUuid;
import android.os.Process;
import android.os.UserHandle;
@@ -120,7 +120,7 @@ public class VcnManagementServiceTest {
doReturn(Process.SYSTEM_UID).when(mMockDeps).getBinderCallingUid();
try {
- mVcnMgmtSvc.setVcnConfig(TEST_UUID_1, new VcnConfig.Builder().build());
+ mVcnMgmtSvc.setVcnConfig(TEST_UUID_1, VcnConfigTest.buildTestConfig());
fail("Expected IllegalStateException exception for system server");
} catch (IllegalStateException expected) {
}
@@ -133,7 +133,7 @@ public class VcnManagementServiceTest {
.getBinderCallingUid();
try {
- mVcnMgmtSvc.setVcnConfig(TEST_UUID_1, new VcnConfig.Builder().build());
+ mVcnMgmtSvc.setVcnConfig(TEST_UUID_1, VcnConfigTest.buildTestConfig());
fail("Expected security exception for non system user");
} catch (SecurityException expected) {
}
@@ -144,7 +144,7 @@ public class VcnManagementServiceTest {
setupMockedCarrierPrivilege(false);
try {
- mVcnMgmtSvc.setVcnConfig(TEST_UUID_1, new VcnConfig.Builder().build());
+ mVcnMgmtSvc.setVcnConfig(TEST_UUID_1, VcnConfigTest.buildTestConfig());
fail("Expected security exception for missing carrier privileges");
} catch (SecurityException expected) {
}