/* * Copyright (C) 2019 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; import static android.net.IpSecAlgorithm.AUTH_AES_CMAC; import static android.net.IpSecAlgorithm.AUTH_AES_XCBC; import static android.net.IpSecAlgorithm.AUTH_CRYPT_AES_GCM; import static android.net.IpSecAlgorithm.AUTH_CRYPT_CHACHA20_POLY1305; import static android.net.IpSecAlgorithm.AUTH_HMAC_SHA256; import static android.net.IpSecAlgorithm.AUTH_HMAC_SHA384; import static android.net.IpSecAlgorithm.AUTH_HMAC_SHA512; import static android.net.IpSecAlgorithm.CRYPT_AES_CBC; import static android.net.IpSecAlgorithm.CRYPT_AES_CTR; import static com.android.internal.annotations.VisibleForTesting.Visibility; import static com.android.internal.util.Preconditions.checkStringNotEmpty; import static com.android.net.module.util.NetworkStackConstants.IPV6_MIN_MTU; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresFeature; import android.content.pm.PackageManager; import android.net.ipsec.ike.IkeDerAsn1DnIdentification; import android.net.ipsec.ike.IkeFqdnIdentification; import android.net.ipsec.ike.IkeIdentification; import android.net.ipsec.ike.IkeIpv4AddrIdentification; import android.net.ipsec.ike.IkeIpv6AddrIdentification; import android.net.ipsec.ike.IkeKeyIdIdentification; import android.net.ipsec.ike.IkeRfc822AddrIdentification; import android.net.ipsec.ike.IkeSessionParams; import android.net.ipsec.ike.IkeTunnelConnectionParams; import android.security.Credentials; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.net.VpnProfile; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.security.GeneralSecurityException; import java.security.Key; import java.security.KeyFactory; import java.security.KeyStore; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; import java.util.ArrayList; import java.util.Arrays; import java.util.Base64; import java.util.Collections; import java.util.List; import java.util.Objects; /** * The Ikev2VpnProfile is a configuration for the platform setup of IKEv2/IPsec VPNs. * *
Together with VpnManager, this allows apps to provision IKEv2/IPsec VPNs that do not require
* the VPN app to constantly run in the background.
*
* @see VpnManager
* @see RFC 7296 - Internet Key
* Exchange, Version 2 (IKEv2)
*/
public final class Ikev2VpnProfile extends PlatformVpnProfile {
private static final String TAG = Ikev2VpnProfile.class.getSimpleName();
/** Prefix for when a Private Key is an alias to look for in KeyStore @hide */
public static final String PREFIX_KEYSTORE_ALIAS = "KEYSTORE_ALIAS:";
/** Prefix for when a Private Key is stored directly in the profile @hide */
public static final String PREFIX_INLINE = "INLINE:";
private static final String ANDROID_KEYSTORE_PROVIDER = "AndroidKeyStore";
private static final String MISSING_PARAM_MSG_TMPL = "Required parameter was not provided: %s";
private static final String EMPTY_CERT = "";
/** @hide */
public static final List In order for the algorithm list to be a valid set, it must contain at least one algorithm
* that provides Authentication, and one that provides Encryption. Authenticated Encryption with
* Associated Data (AEAD) algorithms are counted as providing Authentication and Encryption.
*
* @param algorithmNames The list to be validated
*/
private static void validateAllowedAlgorithms(@NonNull List May be null if the profile is not using Pre-shared key authentication, or the profile is
* built from an {@link IkeTunnelConnectionParams}.
*/
@Nullable
public byte[] getPresharedKey() {
if (mIkeTunConnParams != null) return null;
return mPresharedKey == null ? null : Arrays.copyOf(mPresharedKey, mPresharedKey.length);
}
/**
* Retrieves the certificate for the server's root CA.
*
* May be null if the profile is not using RSA Digital Signature Authentication or
* Username/Password authentication, or the profile is built from an
* {@link IkeTunnelConnectionParams}.
*/
@Nullable
public X509Certificate getServerRootCaCert() {
if (mIkeTunConnParams != null) return null;
return mServerRootCaCert;
}
/**
* Retrieves the username.
*
* May be null if the profile is not using Username/Password authentication, or the profile
* is built from an {@link IkeTunnelConnectionParams}.
*/
@Nullable
public String getUsername() {
if (mIkeTunConnParams != null) return null;
return mUsername;
}
/**
* Retrieves the password.
*
* May be null if the profile is not using Username/Password authentication, or the profile
* is built from an {@link IkeTunnelConnectionParams}.
*/
@Nullable
public String getPassword() {
if (mIkeTunConnParams != null) return null;
return mPassword;
}
/**
* Retrieves the RSA private key.
*
* May be null if the profile is not using RSA Digital Signature authentication, or the
* profile is built from an {@link IkeTunnelConnectionParams}.
*/
@Nullable
public PrivateKey getRsaPrivateKey() {
if (mIkeTunConnParams != null) return null;
return mRsaPrivateKey;
}
/** Retrieves the user certificate, if any was set.
*
* May be null if the profile is built from an {@link IkeTunnelConnectionParams}.
*/
@Nullable
public X509Certificate getUserCert() {
if (mIkeTunConnParams != null) return null;
return mUserCert;
}
/** Retrieves the proxy information if any was set */
@Nullable
public ProxyInfo getProxyInfo() {
return mProxyInfo;
}
/** Returns all the algorithms allowed by this VPN profile.
*
* May be an empty list if the profile is built from an {@link IkeTunnelConnectionParams}.
*/
@NonNull
public List Redundant authentication information (from previous calls to other setAuth* methods) will
* be discarded.
*
* @hide
*/
@NonNull
public VpnProfile toVpnProfile() throws IOException, GeneralSecurityException {
final VpnProfile profile = new VpnProfile("" /* Key; value unused by IKEv2VpnProfile(s) */,
mIsRestrictedToTestNetworks, mExcludeLocalRoutes, mRequiresInternetValidation,
mIkeTunConnParams, mAutomaticNattKeepaliveTimerEnabled,
mAutomaticIpVersionSelectionEnabled);
profile.proxy = mProxyInfo;
profile.isBypassable = mIsBypassable;
profile.isMetered = mIsMetered;
profile.maxMtu = mMaxMtu;
profile.areAuthParamsInline = true;
profile.saveLogin = true;
// The other fields should come from mIkeTunConnParams if it's available.
if (mIkeTunConnParams != null) {
profile.type = VpnProfile.TYPE_IKEV2_FROM_IKE_TUN_CONN_PARAMS;
return profile;
}
profile.type = mType;
profile.server = getServerAddr();
profile.ipsecIdentifier = getUserIdentity();
profile.setAllowedAlgorithms(mAllowedAlgorithms);
switch (mType) {
case TYPE_IKEV2_IPSEC_USER_PASS:
profile.username = mUsername;
profile.password = mPassword;
profile.ipsecCaCert =
mServerRootCaCert == null ? "" : certificateToPemString(mServerRootCaCert);
break;
case TYPE_IKEV2_IPSEC_PSK:
profile.ipsecSecret = encodeForIpsecSecret(mPresharedKey);
break;
case TYPE_IKEV2_IPSEC_RSA:
profile.ipsecUserCert = certificateToPemString(mUserCert);
profile.ipsecSecret =
PREFIX_INLINE + encodeForIpsecSecret(mRsaPrivateKey.getEncoded());
profile.ipsecCaCert =
mServerRootCaCert == null ? "" : certificateToPemString(mServerRootCaCert);
break;
default:
throw new IllegalArgumentException("Invalid auth method set");
}
return profile;
}
private static PrivateKey getPrivateKeyFromAndroidKeystore(String alias) {
try {
final KeyStore keystore = KeyStore.getInstance(ANDROID_KEYSTORE_PROVIDER);
keystore.load(null);
final Key key = keystore.getKey(alias, null);
if (!(key instanceof PrivateKey)) {
throw new IllegalStateException(
"Unexpected key type returned from android keystore.");
}
return (PrivateKey) key;
} catch (Exception e) {
throw new IllegalStateException("Failed to load key from android keystore.", e);
}
}
/**
* Builds the Ikev2VpnProfile from the given profile.
*
* @param profile the source VpnProfile to build from
* @return The IKEv2/IPsec VPN profile
* @hide
*/
@NonNull
public static Ikev2VpnProfile fromVpnProfile(@NonNull VpnProfile profile)
throws GeneralSecurityException {
final Builder builder;
if (profile.ikeTunConnParams == null) {
builder = new Builder(profile.server, profile.ipsecIdentifier);
builder.setAllowedAlgorithms(profile.getAllowedAlgorithms());
switch (profile.type) {
case TYPE_IKEV2_IPSEC_USER_PASS:
builder.setAuthUsernamePassword(
profile.username,
profile.password,
certificateFromPemString(profile.ipsecCaCert));
break;
case TYPE_IKEV2_IPSEC_PSK:
builder.setAuthPsk(decodeFromIpsecSecret(profile.ipsecSecret));
break;
case TYPE_IKEV2_IPSEC_RSA:
final PrivateKey key;
if (profile.ipsecSecret.startsWith(PREFIX_KEYSTORE_ALIAS)) {
final String alias =
profile.ipsecSecret.substring(PREFIX_KEYSTORE_ALIAS.length());
key = getPrivateKeyFromAndroidKeystore(alias);
} else if (profile.ipsecSecret.startsWith(PREFIX_INLINE)) {
key = getPrivateKey(profile.ipsecSecret.substring(PREFIX_INLINE.length()));
} else {
throw new IllegalArgumentException("Invalid RSA private key prefix");
}
final X509Certificate userCert =
certificateFromPemString(profile.ipsecUserCert);
final X509Certificate serverRootCa =
certificateFromPemString(profile.ipsecCaCert);
builder.setAuthDigitalSignature(userCert, key, serverRootCa);
break;
default:
throw new IllegalArgumentException("Invalid auth method set");
}
} else {
builder = new Builder(profile.ikeTunConnParams);
}
builder.setProxy(profile.proxy);
builder.setBypassable(profile.isBypassable);
builder.setMetered(profile.isMetered);
builder.setMaxMtu(profile.maxMtu);
if (profile.isRestrictedToTestNetworks) {
builder.restrictToTestNetworks();
}
if (profile.excludeLocalRoutes && !profile.isBypassable) {
Log.w(TAG, "ExcludeLocalRoutes should only be set in the bypassable VPN");
}
builder.setLocalRoutesExcluded(profile.excludeLocalRoutes && profile.isBypassable);
builder.setRequiresInternetValidation(profile.requiresInternetValidation);
builder.setAutomaticNattKeepaliveTimerEnabled(profile.automaticNattKeepaliveTimerEnabled);
builder.setAutomaticIpVersionSelectionEnabled(profile.automaticIpVersionSelectionEnabled);
return builder.build();
}
/**
* Validates that the VpnProfile is acceptable for the purposes of an Ikev2VpnProfile.
*
* @hide
*/
public static boolean isValidVpnProfile(@NonNull VpnProfile profile) {
if (profile.server.isEmpty() || profile.ipsecIdentifier.isEmpty()) {
return false;
}
switch (profile.type) {
case TYPE_IKEV2_IPSEC_USER_PASS:
if (profile.username.isEmpty() || profile.password.isEmpty()) {
return false;
}
break;
case TYPE_IKEV2_IPSEC_PSK:
if (profile.ipsecSecret.isEmpty()) {
return false;
}
break;
case TYPE_IKEV2_IPSEC_RSA:
if (profile.ipsecSecret.isEmpty() || profile.ipsecUserCert.isEmpty()) {
return false;
}
break;
default:
return false;
}
return true;
}
/**
* Converts a X509 Certificate to a PEM-formatted string.
*
* Must be public due to runtime-package restrictions.
*
* @hide
*/
@NonNull
@VisibleForTesting(visibility = Visibility.PRIVATE)
public static String certificateToPemString(@Nullable X509Certificate cert)
throws IOException, CertificateEncodingException {
if (cert == null) {
return EMPTY_CERT;
}
// Credentials.convertToPem outputs ASCII bytes.
return new String(Credentials.convertToPem(cert), StandardCharsets.US_ASCII);
}
/**
* Decodes the provided Certificate(s).
*
* Will use the first one if the certStr encodes more than one certificate.
*/
@Nullable
private static X509Certificate certificateFromPemString(@Nullable String certStr)
throws CertificateException {
if (certStr == null || EMPTY_CERT.equals(certStr)) {
return null;
}
try {
final List Setting this will configure IKEv2 authentication using EAP-MSCHAPv2. Only one
* authentication method may be set. This method will overwrite any previously set
* authentication method.
*
* If this {@link Builder} is constructed with an {@link IkeTunnelConnectionParams},
* authentication details should be configured there, and calling this method will result
* in an exception being thrown.
*
* @param user the username to be used for EAP-MSCHAPv2 authentication
* @param pass the password to be used for EAP-MSCHAPv2 authentication
* @param serverRootCa the root certificate to be used for verifying the identity of the
* server
* @return this {@link Builder} object to facilitate chaining of method calls
* @throws IllegalArgumentException if any of the certificates were invalid or of an
* unrecognized format
*/
@NonNull
@RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
public Builder setAuthUsernamePassword(
@NonNull String user,
@NonNull String pass,
@Nullable X509Certificate serverRootCa) {
checkNotNull(user, MISSING_PARAM_MSG_TMPL, "user");
checkNotNull(pass, MISSING_PARAM_MSG_TMPL, "pass");
checkBuilderSetter(mIkeTunConnParams != null, "authUsernamePassword");
// Test to make sure all auth params can be encoded safely.
if (serverRootCa != null) checkCert(serverRootCa);
resetAuthParams();
mUsername = user;
mPassword = pass;
mServerRootCaCert = serverRootCa;
mType = VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS;
return this;
}
/**
* Set the IKEv2 authentication to use Digital Signature Authentication with the given key.
*
* Setting this will configure IKEv2 authentication using a Digital Signature scheme.
* Only one authentication method may be set. This method will overwrite any previously set
* authentication method.
*
* If this {@link Builder} is constructed with an {@link IkeTunnelConnectionParams},
* authentication details should be configured there, and calling this method will result in
* an exception being thrown.
*
* @param userCert the username to be used for RSA Digital signiture authentication
* @param key the PrivateKey instance associated with the user ceritificate, used for
* constructing the signature
* @param serverRootCa the root certificate to be used for verifying the identity of the
* server
* @return this {@link Builder} object to facilitate chaining of method calls
* @throws IllegalArgumentException if any of the certificates were invalid or of an
* unrecognized format
*/
@NonNull
@RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
public Builder setAuthDigitalSignature(
@NonNull X509Certificate userCert,
@NonNull PrivateKey key,
@Nullable X509Certificate serverRootCa) {
checkNotNull(userCert, MISSING_PARAM_MSG_TMPL, "userCert");
checkNotNull(key, MISSING_PARAM_MSG_TMPL, "key");
checkBuilderSetter(mIkeTunConnParams != null, "authDigitalSignature");
// Test to make sure all auth params can be encoded safely.
checkCert(userCert);
if (serverRootCa != null) checkCert(serverRootCa);
resetAuthParams();
mUserCert = userCert;
mRsaPrivateKey = key;
mServerRootCaCert = serverRootCa;
mType = VpnProfile.TYPE_IKEV2_IPSEC_RSA;
return this;
}
/**
* Set the IKEv2 authentication to use Preshared keys.
*
* Setting this will configure IKEv2 authentication using a Preshared Key. Only one
* authentication method may be set. This method will overwrite any previously set
* authentication method.
*
* If this {@link Builder} is constructed with an {@link IkeTunnelConnectionParams},
* authentication details should be configured there, and calling this method will result in
* an exception being thrown.
*
* @param psk the key to be used for Pre-Shared Key authentication
* @return this {@link Builder} object to facilitate chaining of method calls
*/
@NonNull
@RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
public Builder setAuthPsk(@NonNull byte[] psk) {
checkNotNull(psk, MISSING_PARAM_MSG_TMPL, "psk");
checkBuilderSetter(mIkeTunConnParams != null, "authPsk");
resetAuthParams();
mPresharedKey = psk;
mType = VpnProfile.TYPE_IKEV2_IPSEC_PSK;
return this;
}
/**
* Sets whether apps can bypass this VPN connection.
*
* By default, all traffic from apps are forwarded through the VPN interface and it is
* not possible for unprivileged apps to side-step the VPN. If a VPN is set to bypassable,
* apps may use methods such as {@link Network#getSocketFactory} or {@link
* Network#openConnection} to instead send/receive directly over the underlying network or
* any other network they have permissions for.
*
* @param isBypassable Whether or not the VPN should be considered bypassable. Defaults to
* {@code false}.
* @return this {@link Builder} object to facilitate chaining of method calls
*/
@NonNull
@RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
public Builder setBypassable(boolean isBypassable) {
mIsBypassable = isBypassable;
return this;
}
/**
* Sets a proxy for the VPN network.
*
* Note that this proxy is only a recommendation and it may be ignored by apps.
*
* @param proxy the ProxyInfo to be set for the VPN network
* @return this {@link Builder} object to facilitate chaining of method calls
*/
@NonNull
@RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
public Builder setProxy(@Nullable ProxyInfo proxy) {
mProxyInfo = proxy;
return this;
}
/**
* Set the upper bound of the maximum transmission unit (MTU) of the VPN interface.
*
* If it is not set, a safe value will be used. Additionally, the actual link MTU will be
* dynamically calculated/updated based on the underlying link's mtu.
*
* @param mtu the MTU (in bytes) of the VPN interface
* @return this {@link Builder} object to facilitate chaining of method calls
* @throws IllegalArgumentException if the value is not at least the minimum IPv6 MTU (1280)
*/
@NonNull
@RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
public Builder setMaxMtu(int mtu) {
// IPv6 MTU is greater; since profiles may be started by the system on IPv4 and IPv6
// networks, the VPN must provide a link fulfilling the stricter of the two conditions
// (at least that of the IPv6 MTU).
if (mtu < IPV6_MIN_MTU) {
throw new IllegalArgumentException("Max MTU must be at least " + IPV6_MIN_MTU);
}
mMaxMtu = mtu;
return this;
}
/**
* Request that this VPN undergoes Internet validation.
*
* If this is true, the platform will perform basic validation checks for Internet
* connectivity over this VPN. If and when they succeed, the VPN network capabilities will
* reflect this by gaining the {@link NetworkCapabilities#NET_CAPABILITY_VALIDATED}
* capability.
*
* If this is false, the platform assumes the VPN either is always capable of reaching the
* Internet or intends not to. In this case, the VPN network capabilities will
* always gain the {@link NetworkCapabilities#NET_CAPABILITY_VALIDATED} capability
* immediately after it connects, whether it can reach public Internet destinations or not.
*
* @param requiresInternetValidation {@code true} if the framework should attempt to
* validate this VPN for Internet connectivity. Defaults
* to {@code false}.
*/
@NonNull
@RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
public Builder setRequiresInternetValidation(boolean requiresInternetValidation) {
mRequiresInternetValidation = requiresInternetValidation;
return this;
}
/**
* Marks the VPN network as metered.
*
* A VPN network is classified as metered when the user is sensitive to heavy data usage
* due to monetary costs and/or data limitations. In such cases, you should set this to
* {@code true} so that apps on the system can avoid doing large data transfers. Otherwise,
* set this to {@code false}. Doing so would cause VPN network to inherit its meteredness
* from the underlying network.
*
* @param isMetered {@code true} if the VPN network should be treated as metered regardless
* of underlying network meteredness. Defaults to {@code true}.
* @return this {@link Builder} object to facilitate chaining of method calls
* @see NetworkCapabilities#NET_CAPABILITY_NOT_METERED
*/
@NonNull
@RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
public Builder setMetered(boolean isMetered) {
mIsMetered = isMetered;
return this;
}
/**
* Sets the allowable set of IPsec algorithms
*
* If set, this will constrain the set of algorithms that the IPsec tunnel will use for
* integrity verification and encryption to the provided list.
*
* The set of allowed IPsec algorithms is defined in {@link IpSecAlgorithm}. Adding of
* algorithms that are considered insecure (such as AUTH_HMAC_MD5 and AUTH_HMAC_SHA1) is not
* permitted, and will result in an IllegalArgumentException being thrown.
*
* The provided algorithm list must contain at least one algorithm that provides
* Authentication, and one that provides Encryption. Authenticated Encryption with
* Associated Data (AEAD) algorithms provide both Authentication and Encryption.
*
* If this {@link Builder} is constructed with an {@link IkeTunnelConnectionParams},
* authentication details should be configured there, and calling this method will result in
* an exception being thrown.
*
* By default, this profile will use any algorithm defined in {@link IpSecAlgorithm},
* with the exception of those considered insecure (as described above).
*
* @param algorithmNames the list of supported IPsec algorithms
* @return this {@link Builder} object to facilitate chaining of method calls
* @see IpSecAlgorithm
*/
@NonNull
@RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
public Builder setAllowedAlgorithms(@NonNull List This method is for testing only, and must not be used by apps. Calling
* provisionVpnProfile() with a profile where test-network usage is enabled will require the
* MANAGE_TEST_NETWORKS permission.
*
* @hide
*/
@NonNull
@RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
public Builder restrictToTestNetworks() {
mIsRestrictedToTestNetworks = true;
return this;
}
/**
* Sets the enabled state of the automatic NAT-T keepalive timers
*
* Note that if this builder was constructed with a {@link IkeTunnelConnectionParams},
* but this is called with {@code true}, the framework will automatically choose the
* appropriate keepalive timer and ignore the settings in the session params embedded
* in the connection params.
*
* @param isEnabled {@code true} to enable automatic keepalive timers, based on internal
* platform signals. Defaults to {@code false}.
* @return this {@link Builder} object to facilitate chaining of method calls
*/
@NonNull
@RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
public Builder setAutomaticNattKeepaliveTimerEnabled(boolean isEnabled) {
mAutomaticNattKeepaliveTimerEnabled = isEnabled;
return this;
}
/**
* Sets the enabled state of the automatic IP version selection
*
* @param isEnabled {@code true} to enable automatic IP version selection, based on internal
* platform signals. Defaults to {@code false}.
* @return this {@link Builder} object to facilitate chaining of method calls
*/
@NonNull
@RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
public Builder setAutomaticIpVersionSelectionEnabled(boolean isEnabled) {
mAutomaticIpVersionSelectionEnabled = isEnabled;
return this;
}
/**
* Sets whether the local traffic is exempted from the VPN.
*
* When this is set, the system will not use the VPN network when an app
* tries to send traffic for an IP address that is on a local network.
*
* Note that there are important security implications. In particular, the
* networks that the device connects to typically decides what IP addresses
* are part of the local network. This means that for VPNs setting this
* flag, it is possible for anybody to set up a public network in such a
* way that traffic to arbitrary IP addresses will bypass the VPN, including
* traffic to services like DNS. When using this API, please consider the
* security implications for your particular case.
*
* Note that because the local traffic will always bypass the VPN,
* it is not possible to set this flag on a non-bypassable VPN.
*/
@NonNull
@RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
public Builder setLocalRoutesExcluded(boolean excludeLocalRoutes) {
mExcludeLocalRoutes = excludeLocalRoutes;
return this;
}
/**
* Validates, builds and provisions the VpnProfile.
*
* @throws IllegalArgumentException if any of the required keys or values were invalid
*/
@NonNull
@RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
public Ikev2VpnProfile build() {
return new Ikev2VpnProfile(
mType,
mServerAddr,
mUserIdentity,
mPresharedKey,
mServerRootCaCert,
mUsername,
mPassword,
mRsaPrivateKey,
mUserCert,
mProxyInfo,
mAllowedAlgorithms,
mIsBypassable,
mIsMetered,
mMaxMtu,
mIsRestrictedToTestNetworks,
mExcludeLocalRoutes,
mRequiresInternetValidation,
mIkeTunConnParams,
mAutomaticNattKeepaliveTimerEnabled,
mAutomaticIpVersionSelectionEnabled);
}
}
}