386 lines
14 KiB
Java
386 lines
14 KiB
Java
/*
|
|
* 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 android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
|
|
import static android.net.NetworkCapabilities.TRANSPORT_TEST;
|
|
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
|
|
|
|
import static com.android.internal.annotations.VisibleForTesting.Visibility;
|
|
import static com.android.server.vcn.util.PersistableBundleUtils.INTEGER_DESERIALIZER;
|
|
import static com.android.server.vcn.util.PersistableBundleUtils.INTEGER_SERIALIZER;
|
|
|
|
import android.annotation.IntDef;
|
|
import android.annotation.NonNull;
|
|
import android.annotation.Nullable;
|
|
import android.content.Context;
|
|
import android.net.NetworkCapabilities;
|
|
import android.net.NetworkRequest;
|
|
import android.os.Parcel;
|
|
import android.os.Parcelable;
|
|
import android.os.PersistableBundle;
|
|
import android.util.ArraySet;
|
|
import android.util.Log;
|
|
|
|
import com.android.internal.annotations.VisibleForTesting;
|
|
import com.android.internal.util.Preconditions;
|
|
import com.android.server.vcn.util.PersistableBundleUtils;
|
|
|
|
import java.lang.annotation.ElementType;
|
|
import java.lang.annotation.Retention;
|
|
import java.lang.annotation.RetentionPolicy;
|
|
import java.lang.annotation.Target;
|
|
import java.util.ArrayList;
|
|
import java.util.Collections;
|
|
import java.util.Iterator;
|
|
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
|
|
*/
|
|
public final class VcnConfig implements Parcelable {
|
|
@NonNull private static final String TAG = VcnConfig.class.getSimpleName();
|
|
|
|
/** @hide */
|
|
@Retention(RetentionPolicy.SOURCE)
|
|
@IntDef(
|
|
prefix = {"TRANSPORT_"},
|
|
value = {
|
|
NetworkCapabilities.TRANSPORT_CELLULAR,
|
|
NetworkCapabilities.TRANSPORT_WIFI,
|
|
})
|
|
@Target({ElementType.TYPE_USE})
|
|
public @interface VcnUnderlyingNetworkTransport {}
|
|
|
|
private static final Set<Integer> ALLOWED_TRANSPORTS = new ArraySet<>();
|
|
|
|
static {
|
|
ALLOWED_TRANSPORTS.add(TRANSPORT_WIFI);
|
|
ALLOWED_TRANSPORTS.add(TRANSPORT_CELLULAR);
|
|
ALLOWED_TRANSPORTS.add(TRANSPORT_TEST);
|
|
}
|
|
|
|
private static final String PACKAGE_NAME_KEY = "mPackageName";
|
|
@NonNull private final String mPackageName;
|
|
|
|
private static final String GATEWAY_CONNECTION_CONFIGS_KEY = "mGatewayConnectionConfigs";
|
|
@NonNull private final Set<VcnGatewayConnectionConfig> mGatewayConnectionConfigs;
|
|
|
|
private static final Set<Integer> RESTRICTED_TRANSPORTS_DEFAULT =
|
|
Collections.singleton(TRANSPORT_WIFI);
|
|
private static final String RESTRICTED_TRANSPORTS_KEY = "mRestrictedTransports";
|
|
@NonNull private final Set<Integer> mRestrictedTransports;
|
|
|
|
private static final String IS_TEST_MODE_PROFILE_KEY = "mIsTestModeProfile";
|
|
private final boolean mIsTestModeProfile;
|
|
|
|
private VcnConfig(
|
|
@NonNull String packageName,
|
|
@NonNull Set<VcnGatewayConnectionConfig> gatewayConnectionConfigs,
|
|
@NonNull Set<Integer> restrictedTransports,
|
|
boolean isTestModeProfile) {
|
|
mPackageName = packageName;
|
|
mGatewayConnectionConfigs =
|
|
Collections.unmodifiableSet(new ArraySet<>(gatewayConnectionConfigs));
|
|
mRestrictedTransports = Collections.unmodifiableSet(new ArraySet<>(restrictedTransports));
|
|
mIsTestModeProfile = isTestModeProfile;
|
|
|
|
validate();
|
|
}
|
|
|
|
/**
|
|
* Deserializes a VcnConfig from a PersistableBundle.
|
|
*
|
|
* @hide
|
|
*/
|
|
@VisibleForTesting(visibility = Visibility.PRIVATE)
|
|
public VcnConfig(@NonNull PersistableBundle in) {
|
|
mPackageName = in.getString(PACKAGE_NAME_KEY);
|
|
|
|
final PersistableBundle gatewayConnectionConfigsBundle =
|
|
in.getPersistableBundle(GATEWAY_CONNECTION_CONFIGS_KEY);
|
|
mGatewayConnectionConfigs =
|
|
new ArraySet<>(
|
|
PersistableBundleUtils.toList(
|
|
gatewayConnectionConfigsBundle, VcnGatewayConnectionConfig::new));
|
|
|
|
final PersistableBundle restrictedTransportsBundle =
|
|
in.getPersistableBundle(RESTRICTED_TRANSPORTS_KEY);
|
|
if (restrictedTransportsBundle == null) {
|
|
// RESTRICTED_TRANSPORTS_KEY was added in U and does not exist in VcnConfigs created in
|
|
// older platforms
|
|
mRestrictedTransports = RESTRICTED_TRANSPORTS_DEFAULT;
|
|
} else {
|
|
mRestrictedTransports =
|
|
new ArraySet<Integer>(
|
|
PersistableBundleUtils.toList(
|
|
restrictedTransportsBundle, INTEGER_DESERIALIZER));
|
|
}
|
|
|
|
mIsTestModeProfile = in.getBoolean(IS_TEST_MODE_PROFILE_KEY);
|
|
|
|
validate();
|
|
}
|
|
|
|
private void validate() {
|
|
Objects.requireNonNull(mPackageName, "packageName was null");
|
|
Preconditions.checkCollectionNotEmpty(
|
|
mGatewayConnectionConfigs, "gatewayConnectionConfigs was empty");
|
|
|
|
final Iterator<Integer> iterator = mRestrictedTransports.iterator();
|
|
while (iterator.hasNext()) {
|
|
final int transport = iterator.next();
|
|
if (!ALLOWED_TRANSPORTS.contains(transport)) {
|
|
iterator.remove();
|
|
Log.w(
|
|
TAG,
|
|
"Found invalid transport "
|
|
+ transport
|
|
+ " which might be from a new version of VcnConfig");
|
|
}
|
|
|
|
if (transport == TRANSPORT_TEST && !mIsTestModeProfile) {
|
|
throw new IllegalArgumentException(
|
|
"Found TRANSPORT_TEST in a non-test-mode profile");
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Retrieve the package name of the provisioning app.
|
|
*
|
|
* @hide
|
|
*/
|
|
@NonNull
|
|
public String getProvisioningPackageName() {
|
|
return mPackageName;
|
|
}
|
|
|
|
/** Retrieves the set of configured GatewayConnection(s). */
|
|
@NonNull
|
|
public Set<VcnGatewayConnectionConfig> getGatewayConnectionConfigs() {
|
|
return Collections.unmodifiableSet(mGatewayConnectionConfigs);
|
|
}
|
|
|
|
/**
|
|
* Retrieve the transports that will be restricted by the VCN.
|
|
*
|
|
* @see Builder#setRestrictedUnderlyingNetworkTransports(Set)
|
|
*/
|
|
@NonNull
|
|
public Set<@VcnUnderlyingNetworkTransport Integer> getRestrictedUnderlyingNetworkTransports() {
|
|
return Collections.unmodifiableSet(mRestrictedTransports);
|
|
}
|
|
|
|
/**
|
|
* Returns whether or not this VcnConfig is restricted to test networks.
|
|
*
|
|
* @hide
|
|
*/
|
|
public boolean isTestModeProfile() {
|
|
return mIsTestModeProfile;
|
|
}
|
|
|
|
/**
|
|
* Serializes this object to a PersistableBundle.
|
|
*
|
|
* @hide
|
|
*/
|
|
@NonNull
|
|
public PersistableBundle toPersistableBundle() {
|
|
final PersistableBundle result = new PersistableBundle();
|
|
|
|
result.putString(PACKAGE_NAME_KEY, mPackageName);
|
|
|
|
final PersistableBundle gatewayConnectionConfigsBundle =
|
|
PersistableBundleUtils.fromList(
|
|
new ArrayList<>(mGatewayConnectionConfigs),
|
|
VcnGatewayConnectionConfig::toPersistableBundle);
|
|
result.putPersistableBundle(GATEWAY_CONNECTION_CONFIGS_KEY, gatewayConnectionConfigsBundle);
|
|
|
|
final PersistableBundle restrictedTransportsBundle =
|
|
PersistableBundleUtils.fromList(
|
|
new ArrayList<>(mRestrictedTransports), INTEGER_SERIALIZER);
|
|
result.putPersistableBundle(RESTRICTED_TRANSPORTS_KEY, restrictedTransportsBundle);
|
|
|
|
result.putBoolean(IS_TEST_MODE_PROFILE_KEY, mIsTestModeProfile);
|
|
|
|
return result;
|
|
}
|
|
|
|
@Override
|
|
public int hashCode() {
|
|
return Objects.hash(
|
|
mPackageName, mGatewayConnectionConfigs, mRestrictedTransports, mIsTestModeProfile);
|
|
}
|
|
|
|
@Override
|
|
public boolean equals(@Nullable Object other) {
|
|
if (!(other instanceof VcnConfig)) {
|
|
return false;
|
|
}
|
|
|
|
final VcnConfig rhs = (VcnConfig) other;
|
|
return mPackageName.equals(rhs.mPackageName)
|
|
&& mGatewayConnectionConfigs.equals(rhs.mGatewayConnectionConfigs)
|
|
&& mRestrictedTransports.equals(rhs.mRestrictedTransports)
|
|
&& mIsTestModeProfile == rhs.mIsTestModeProfile;
|
|
}
|
|
|
|
// Parcelable methods
|
|
|
|
@Override
|
|
public int describeContents() {
|
|
return 0;
|
|
}
|
|
|
|
@Override
|
|
public void writeToParcel(@NonNull 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) {
|
|
return new VcnConfig((PersistableBundle) in.readParcelable(null, android.os.PersistableBundle.class));
|
|
}
|
|
|
|
@NonNull
|
|
public VcnConfig[] newArray(int size) {
|
|
return new VcnConfig[size];
|
|
}
|
|
};
|
|
|
|
/** This class is used to incrementally build {@link VcnConfig} objects. */
|
|
public static final class Builder {
|
|
@NonNull private final String mPackageName;
|
|
|
|
@NonNull
|
|
private final Set<VcnGatewayConnectionConfig> mGatewayConnectionConfigs = new ArraySet<>();
|
|
|
|
@NonNull private final Set<Integer> mRestrictedTransports = new ArraySet<>();
|
|
|
|
private boolean mIsTestModeProfile = false;
|
|
|
|
public Builder(@NonNull Context context) {
|
|
Objects.requireNonNull(context, "context was null");
|
|
|
|
mPackageName = context.getOpPackageName();
|
|
mRestrictedTransports.addAll(RESTRICTED_TRANSPORTS_DEFAULT);
|
|
}
|
|
|
|
/**
|
|
* Adds a configuration for an individual gateway connection.
|
|
*
|
|
* @param gatewayConnectionConfig the configuration for an individual gateway connection
|
|
* @return this {@link Builder} instance, for chaining
|
|
* @throws IllegalArgumentException if a VcnGatewayConnectionConfig has already been set for
|
|
* this {@link VcnConfig} with the same GatewayConnection name (as returned via {@link
|
|
* VcnGatewayConnectionConfig#getGatewayConnectionName()}).
|
|
*/
|
|
@NonNull
|
|
public Builder addGatewayConnectionConfig(
|
|
@NonNull VcnGatewayConnectionConfig gatewayConnectionConfig) {
|
|
Objects.requireNonNull(gatewayConnectionConfig, "gatewayConnectionConfig was null");
|
|
|
|
for (final VcnGatewayConnectionConfig vcnGatewayConnectionConfig :
|
|
mGatewayConnectionConfigs) {
|
|
if (vcnGatewayConnectionConfig
|
|
.getGatewayConnectionName()
|
|
.equals(gatewayConnectionConfig.getGatewayConnectionName())) {
|
|
throw new IllegalArgumentException(
|
|
"GatewayConnection for specified name already exists");
|
|
}
|
|
}
|
|
|
|
mGatewayConnectionConfigs.add(gatewayConnectionConfig);
|
|
return this;
|
|
}
|
|
|
|
private void validateRestrictedTransportsOrThrow(Set<Integer> restrictedTransports) {
|
|
Objects.requireNonNull(restrictedTransports, "transports was null");
|
|
|
|
for (int transport : restrictedTransports) {
|
|
if (!ALLOWED_TRANSPORTS.contains(transport)) {
|
|
throw new IllegalArgumentException("Invalid transport " + transport);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sets transports that will be restricted by the VCN.
|
|
*
|
|
* <p>In general, apps will not be able to bind to, or use a restricted network. In other
|
|
* words, unless the network type is marked restricted, any app can opt to use underlying
|
|
* networks, instead of through the VCN.
|
|
*
|
|
* @param transports transports that will be restricted by VCN. Networks that include any of
|
|
* the transports will be marked as restricted. {@link
|
|
* NetworkCapabilities#TRANSPORT_WIFI} is marked restricted by default.
|
|
* @return this {@link Builder} instance, for chaining
|
|
* @throws IllegalArgumentException if the input contains unsupported transport types.
|
|
* @see NetworkCapabilities#NET_CAPABILITY_NOT_RESTRICTED
|
|
*/
|
|
@NonNull
|
|
public Builder setRestrictedUnderlyingNetworkTransports(
|
|
@NonNull Set<@VcnUnderlyingNetworkTransport Integer> transports) {
|
|
validateRestrictedTransportsOrThrow(transports);
|
|
|
|
mRestrictedTransports.clear();
|
|
mRestrictedTransports.addAll(transports);
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Restricts this VcnConfig to matching with test networks (only).
|
|
*
|
|
* <p>This method is for testing only, and must not be used by apps. Calling {@link
|
|
* VcnManager#setVcnConfig(ParcelUuid, VcnConfig)} with a VcnConfig where test-network usage
|
|
* is enabled will require the MANAGE_TEST_NETWORKS permission.
|
|
*
|
|
* @return this {@link Builder} instance, for chaining
|
|
* @hide
|
|
*/
|
|
@NonNull
|
|
public Builder setIsTestModeProfile() {
|
|
mIsTestModeProfile = true;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Builds and validates the VcnConfig.
|
|
*
|
|
* @return an immutable VcnConfig instance
|
|
*/
|
|
@NonNull
|
|
public VcnConfig build() {
|
|
return new VcnConfig(
|
|
mPackageName,
|
|
mGatewayConnectionConfigs,
|
|
mRestrictedTransports,
|
|
mIsTestModeProfile);
|
|
}
|
|
}
|
|
}
|