Merge changes Ifabf5e2b,I2e632532 am: c0e2e1c9bd am: 0ed3d9cb2f
Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/1501053 MUST ONLY BE SUBMITTED BY AUTOMERGER Change-Id: I646ce9b020e3ad1c799065bfd69e1528d05e1277
This commit is contained in:
@@ -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",
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
// TODO: Implement getters, validators, etc
|
||||
/** @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();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// Parcelable methods
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/** This class is used to incrementally build {@link VcnGatewayConnectionConfig} objects */
|
||||
return mExposedCapabilities.contains(capability);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all capabilities required of underlying networks.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@NonNull
|
||||
public Set<Integer> getAllUnderlyingCapabilities() {
|
||||
return Collections.unmodifiableSet(mUnderlyingCapabilities);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
/** 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.
|
||||
|
||||
/**
|
||||
* Builds and validates the VcnGatewayConnectionConfig
|
||||
* 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.
|
||||
*
|
||||
* @return an immutable VcnGatewayConnectionConfig instance
|
||||
*/
|
||||
@NonNull
|
||||
public VcnGatewayConnectionConfig build() {
|
||||
return new VcnGatewayConnectionConfig();
|
||||
return new VcnGatewayConnectionConfig(
|
||||
mExposedCapabilities, mUnderlyingCapabilities, mRetryIntervalsMs, mMaxMtu);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,9 @@ import android.annotation.SystemService;
|
||||
import android.content.Context;
|
||||
import android.os.ParcelUuid;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceSpecificException;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* VcnManager publishes APIs for applications to configure and manage Virtual Carrier Networks.
|
||||
@@ -63,15 +66,20 @@ public final class VcnManager {
|
||||
* @param config the configuration parameters for the VCN
|
||||
* @throws SecurityException if the caller does not have carrier privileges, or is not running
|
||||
* as the primary user
|
||||
* @throws IOException if the configuration failed to be persisted. A caller encountering this
|
||||
* exception should attempt to retry (possibly after a delay).
|
||||
* @hide
|
||||
*/
|
||||
@RequiresPermission("carrier privileges") // TODO (b/72967236): Define a system-wide constant
|
||||
public void setVcnConfig(@NonNull ParcelUuid subscriptionGroup, @NonNull VcnConfig config) {
|
||||
public void setVcnConfig(@NonNull ParcelUuid subscriptionGroup, @NonNull VcnConfig config)
|
||||
throws IOException {
|
||||
requireNonNull(subscriptionGroup, "subscriptionGroup was null");
|
||||
requireNonNull(config, "config was null");
|
||||
|
||||
try {
|
||||
mService.setVcnConfig(subscriptionGroup, config);
|
||||
} catch (ServiceSpecificException e) {
|
||||
throw new IOException(e);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
@@ -88,14 +96,18 @@ public final class VcnManager {
|
||||
* @param subscriptionGroup the subscription group that the configuration should be applied to
|
||||
* @throws SecurityException if the caller does not have carrier privileges, or is not running
|
||||
* as the primary user
|
||||
* @throws IOException if the configuration failed to be cleared. A caller encountering this
|
||||
* exception should attempt to retry (possibly after a delay).
|
||||
* @hide
|
||||
*/
|
||||
@RequiresPermission("carrier privileges") // TODO (b/72967236): Define a system-wide constant
|
||||
public void clearVcnConfig(@NonNull ParcelUuid subscriptionGroup) {
|
||||
public void clearVcnConfig(@NonNull ParcelUuid subscriptionGroup) throws IOException {
|
||||
requireNonNull(subscriptionGroup, "subscriptionGroup was null");
|
||||
|
||||
try {
|
||||
mService.clearVcnConfig(subscriptionGroup);
|
||||
} catch (ServiceSpecificException e) {
|
||||
throw new IOException(e);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
|
||||
@@ -26,20 +26,31 @@ import android.net.NetworkRequest;
|
||||
import android.net.vcn.IVcnManagementService;
|
||||
import android.net.vcn.VcnConfig;
|
||||
import android.os.Binder;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.Looper;
|
||||
import android.os.ParcelUuid;
|
||||
import android.os.PersistableBundle;
|
||||
import android.os.Process;
|
||||
import android.os.ServiceSpecificException;
|
||||
import android.os.UserHandle;
|
||||
import android.telephony.SubscriptionInfo;
|
||||
import android.telephony.SubscriptionManager;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.Slog;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.annotations.VisibleForTesting.Visibility;
|
||||
import com.android.server.vcn.util.PersistableBundleUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
/**
|
||||
* VcnManagementService manages Virtual Carrier Network profiles and lifecycles.
|
||||
@@ -101,20 +112,72 @@ public class VcnManagementService extends IVcnManagementService.Stub {
|
||||
|
||||
public static final boolean VDBG = false; // STOPSHIP: if true
|
||||
|
||||
@VisibleForTesting(visibility = Visibility.PRIVATE)
|
||||
static final String VCN_CONFIG_FILE = "/data/system/vcn/configs.xml";
|
||||
|
||||
/* Binder context for this service */
|
||||
@NonNull private final Context mContext;
|
||||
@NonNull private final Dependencies mDeps;
|
||||
|
||||
@NonNull private final Looper mLooper;
|
||||
@NonNull private final Handler mHandler;
|
||||
@NonNull private final VcnNetworkProvider mNetworkProvider;
|
||||
|
||||
@GuardedBy("mLock")
|
||||
@NonNull
|
||||
private final Map<ParcelUuid, VcnConfig> mConfigs = new ArrayMap<>();
|
||||
|
||||
@NonNull private final Object mLock = new Object();
|
||||
|
||||
@NonNull private final PersistableBundleUtils.LockingReadWriteHelper mConfigDiskRwHelper;
|
||||
|
||||
@VisibleForTesting(visibility = Visibility.PRIVATE)
|
||||
VcnManagementService(@NonNull Context context, @NonNull Dependencies deps) {
|
||||
mContext = requireNonNull(context, "Missing context");
|
||||
mDeps = requireNonNull(deps, "Missing dependencies");
|
||||
|
||||
mLooper = mDeps.getLooper();
|
||||
mHandler = new Handler(mLooper);
|
||||
mNetworkProvider = new VcnNetworkProvider(mContext, mLooper);
|
||||
|
||||
mConfigDiskRwHelper = mDeps.newPersistableBundleLockingReadWriteHelper(VCN_CONFIG_FILE);
|
||||
|
||||
// Run on handler to ensure I/O does not block system server startup
|
||||
mHandler.post(() -> {
|
||||
PersistableBundle configBundle = null;
|
||||
try {
|
||||
configBundle = mConfigDiskRwHelper.readFromDisk();
|
||||
} catch (IOException e1) {
|
||||
Slog.e(TAG, "Failed to read configs from disk; retrying", e1);
|
||||
|
||||
// Retry immediately. The IOException may have been transient.
|
||||
try {
|
||||
configBundle = mConfigDiskRwHelper.readFromDisk();
|
||||
} catch (IOException e2) {
|
||||
Slog.wtf(TAG, "Failed to read configs from disk", e2);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (configBundle != null) {
|
||||
final Map<ParcelUuid, VcnConfig> configs =
|
||||
PersistableBundleUtils.toMap(
|
||||
configBundle,
|
||||
PersistableBundleUtils::toParcelUuid,
|
||||
VcnConfig::new);
|
||||
|
||||
synchronized (mLock) {
|
||||
for (Entry<ParcelUuid, VcnConfig> entry : configs.entrySet()) {
|
||||
// Ensure no new configs are overwritten; a carrier app may have added a new
|
||||
// config.
|
||||
if (!mConfigs.containsKey(entry.getKey())) {
|
||||
mConfigs.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
// TODO: Trigger re-evaluation of active VCNs; start/stop VCNs as needed.
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Package-visibility for SystemServer to create instances.
|
||||
@@ -151,12 +214,21 @@ public class VcnManagementService extends IVcnManagementService.Stub {
|
||||
public int getBinderCallingUid() {
|
||||
return Binder.getCallingUid();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and returns a new {@link PersistableBundle.LockingReadWriteHelper}
|
||||
*
|
||||
* @param path the file path to read/write from/to.
|
||||
* @return the {@link PersistableBundleUtils.LockingReadWriteHelper} instance
|
||||
*/
|
||||
public PersistableBundleUtils.LockingReadWriteHelper
|
||||
newPersistableBundleLockingReadWriteHelper(@NonNull String path) {
|
||||
return new PersistableBundleUtils.LockingReadWriteHelper(path);
|
||||
}
|
||||
}
|
||||
|
||||
/** Notifies the VcnManagementService that external dependencies can be set up. */
|
||||
public void systemReady() {
|
||||
// TODO: Retrieve existing profiles from KeyStore
|
||||
|
||||
mContext.getSystemService(ConnectivityManager.class)
|
||||
.registerNetworkProvider(mNetworkProvider);
|
||||
}
|
||||
@@ -217,9 +289,15 @@ public class VcnManagementService extends IVcnManagementService.Stub {
|
||||
|
||||
enforceCallingUserAndCarrierPrivilege(subscriptionGroup);
|
||||
|
||||
// TODO: Clear Binder calling identity
|
||||
synchronized (mLock) {
|
||||
mConfigs.put(subscriptionGroup, config);
|
||||
|
||||
// TODO: Store VCN configuration, trigger startup as necessary
|
||||
// Must be done synchronously to ensure that writes do not happen out-of-order.
|
||||
writeConfigsToDiskLocked();
|
||||
}
|
||||
|
||||
// TODO: Clear Binder calling identity
|
||||
// TODO: Trigger startup as necessary
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -233,9 +311,38 @@ public class VcnManagementService extends IVcnManagementService.Stub {
|
||||
|
||||
enforceCallingUserAndCarrierPrivilege(subscriptionGroup);
|
||||
|
||||
// TODO: Clear Binder calling identity
|
||||
synchronized (mLock) {
|
||||
mConfigs.remove(subscriptionGroup);
|
||||
|
||||
// TODO: Clear VCN configuration, trigger teardown as necessary
|
||||
// Must be done synchronously to ensure that writes do not happen out-of-order.
|
||||
writeConfigsToDiskLocked();
|
||||
}
|
||||
|
||||
// TODO: Clear Binder calling identity
|
||||
// TODO: Trigger teardown as necessary
|
||||
}
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private void writeConfigsToDiskLocked() {
|
||||
try {
|
||||
PersistableBundle bundle =
|
||||
PersistableBundleUtils.fromMap(
|
||||
mConfigs,
|
||||
PersistableBundleUtils::fromParcelUuid,
|
||||
VcnConfig::toPersistableBundle);
|
||||
mConfigDiskRwHelper.writeToDisk(bundle);
|
||||
} catch (IOException e) {
|
||||
Slog.e(TAG, "Failed to save configs to disk", e);
|
||||
throw new ServiceSpecificException(0, "Failed to save configs");
|
||||
}
|
||||
}
|
||||
|
||||
/** Get current configuration list for testing purposes */
|
||||
@VisibleForTesting(visibility = Visibility.PRIVATE)
|
||||
Map<ParcelUuid, VcnConfig> getConfigs() {
|
||||
synchronized (mLock) {
|
||||
return Collections.unmodifiableMap(mConfigs);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
4
services/core/java/com/android/server/vcn/Android.bp
Normal file
4
services/core/java/com/android/server/vcn/Android.bp
Normal file
@@ -0,0 +1,4 @@
|
||||
filegroup {
|
||||
name: "framework-vcn-util-sources",
|
||||
srcs: ["util/**/*.java"],
|
||||
}
|
||||
83
tests/vcn/java/android/net/vcn/VcnConfigTest.java
Normal file
83
tests/vcn/java/android/net/vcn/VcnConfigTest.java
Normal file
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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()));
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,9 @@
|
||||
|
||||
package com.android.server;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.Mockito.any;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
@@ -26,7 +29,9 @@ 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.PersistableBundle;
|
||||
import android.os.Process;
|
||||
import android.os.UserHandle;
|
||||
import android.os.test.TestLooper;
|
||||
@@ -37,10 +42,14 @@ import android.telephony.TelephonyManager;
|
||||
import androidx.test.filters.SmallTest;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import com.android.server.vcn.util.PersistableBundleUtils;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/** Tests for {@link VcnManagementService}. */
|
||||
@@ -48,6 +57,11 @@ import java.util.UUID;
|
||||
@SmallTest
|
||||
public class VcnManagementServiceTest {
|
||||
private static final ParcelUuid TEST_UUID_1 = new ParcelUuid(new UUID(0, 0));
|
||||
private static final ParcelUuid TEST_UUID_2 = new ParcelUuid(new UUID(1, 1));
|
||||
private static final VcnConfig TEST_VCN_CONFIG = VcnConfigTest.buildTestConfig();
|
||||
private static final Map<ParcelUuid, VcnConfig> TEST_VCN_CONFIG_MAP =
|
||||
Collections.unmodifiableMap(Collections.singletonMap(TEST_UUID_1, TEST_VCN_CONFIG));
|
||||
|
||||
private static final SubscriptionInfo TEST_SUBSCRIPTION_INFO =
|
||||
new SubscriptionInfo(
|
||||
1 /* id */,
|
||||
@@ -79,6 +93,8 @@ public class VcnManagementServiceTest {
|
||||
private final TelephonyManager mTelMgr = mock(TelephonyManager.class);
|
||||
private final SubscriptionManager mSubMgr = mock(SubscriptionManager.class);
|
||||
private final VcnManagementService mVcnMgmtSvc;
|
||||
private final PersistableBundleUtils.LockingReadWriteHelper mConfigReadWriteHelper =
|
||||
mock(PersistableBundleUtils.LockingReadWriteHelper.class);
|
||||
|
||||
public VcnManagementServiceTest() throws Exception {
|
||||
setupSystemService(mConnMgr, Context.CONNECTIVITY_SERVICE, ConnectivityManager.class);
|
||||
@@ -88,6 +104,16 @@ public class VcnManagementServiceTest {
|
||||
|
||||
doReturn(mTestLooper.getLooper()).when(mMockDeps).getLooper();
|
||||
doReturn(Process.FIRST_APPLICATION_UID).when(mMockDeps).getBinderCallingUid();
|
||||
doReturn(mConfigReadWriteHelper)
|
||||
.when(mMockDeps)
|
||||
.newPersistableBundleLockingReadWriteHelper(any());
|
||||
|
||||
final PersistableBundle bundle =
|
||||
PersistableBundleUtils.fromMap(
|
||||
TEST_VCN_CONFIG_MAP,
|
||||
PersistableBundleUtils::fromParcelUuid,
|
||||
VcnConfig::toPersistableBundle);
|
||||
doReturn(bundle).when(mConfigReadWriteHelper).readFromDisk();
|
||||
|
||||
setupMockedCarrierPrivilege(true);
|
||||
mVcnMgmtSvc = new VcnManagementService(mMockContext, mMockDeps);
|
||||
@@ -115,12 +141,42 @@ public class VcnManagementServiceTest {
|
||||
.registerNetworkProvider(any(VcnManagementService.VcnNetworkProvider.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNonSystemServerRealConfigFileAccessPermission() throws Exception {
|
||||
// Attempt to build a real instance of the dependencies, and verify we cannot write to the
|
||||
// file.
|
||||
VcnManagementService.Dependencies deps = new VcnManagementService.Dependencies();
|
||||
PersistableBundleUtils.LockingReadWriteHelper configReadWriteHelper =
|
||||
deps.newPersistableBundleLockingReadWriteHelper(
|
||||
VcnManagementService.VCN_CONFIG_FILE);
|
||||
|
||||
// Even tests should not be able to read/write configs from disk; SELinux policies restrict
|
||||
// it to only the system server.
|
||||
// Reading config should always return null since the file "does not exist", and writing
|
||||
// should throw an IOException.
|
||||
assertNull(configReadWriteHelper.readFromDisk());
|
||||
|
||||
try {
|
||||
configReadWriteHelper.writeToDisk(new PersistableBundle());
|
||||
fail("Expected IOException due to SELinux policy");
|
||||
} catch (FileNotFoundException expected) {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadVcnConfigsOnStartup() throws Exception {
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
assertEquals(TEST_VCN_CONFIG_MAP, mVcnMgmtSvc.getConfigs());
|
||||
verify(mConfigReadWriteHelper).readFromDisk();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetVcnConfigRequiresNonSystemServer() throws Exception {
|
||||
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 +189,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,12 +200,20 @@ 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) {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetVcnConfig() throws Exception {
|
||||
// Use a different UUID to simulate a new VCN config.
|
||||
mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, TEST_VCN_CONFIG);
|
||||
assertEquals(TEST_VCN_CONFIG, mVcnMgmtSvc.getConfigs().get(TEST_UUID_2));
|
||||
verify(mConfigReadWriteHelper).writeToDisk(any(PersistableBundle.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClearVcnConfigRequiresNonSystemServer() throws Exception {
|
||||
doReturn(Process.SYSTEM_UID).when(mMockDeps).getBinderCallingUid();
|
||||
@@ -184,4 +248,11 @@ public class VcnManagementServiceTest {
|
||||
} catch (SecurityException expected) {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClearVcnConfig() throws Exception {
|
||||
mVcnMgmtSvc.clearVcnConfig(TEST_UUID_1);
|
||||
assertTrue(mVcnMgmtSvc.getConfigs().isEmpty());
|
||||
verify(mConfigReadWriteHelper).writeToDisk(any(PersistableBundle.class));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user