1 /* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.internal.net; 18 19 import android.annotation.NonNull; 20 import android.compat.annotation.UnsupportedAppUsage; 21 import android.net.Ikev2VpnProfile; 22 import android.net.PlatformVpnProfile; 23 import android.net.ProxyInfo; 24 import android.net.Uri; 25 import android.net.ipsec.ike.IkeTunnelConnectionParams; 26 import android.net.vcn.persistablebundleutils.TunnelConnectionParamsUtils; 27 import android.os.Build; 28 import android.os.Parcel; 29 import android.os.Parcelable; 30 import android.os.PersistableBundle; 31 import android.text.TextUtils; 32 import android.util.Log; 33 34 import com.android.internal.annotations.VisibleForTesting; 35 import com.android.internal.util.HexDump; 36 import com.android.net.module.util.ProxyUtils; 37 38 import java.io.UnsupportedEncodingException; 39 import java.net.InetAddress; 40 import java.net.URLDecoder; 41 import java.net.URLEncoder; 42 import java.nio.charset.StandardCharsets; 43 import java.util.ArrayList; 44 import java.util.Arrays; 45 import java.util.Collections; 46 import java.util.List; 47 import java.util.Objects; 48 49 /** 50 * Profile storage class for a platform VPN. 51 * 52 * <p>This class supports both the Legacy VPN, as well as application-configurable platform VPNs 53 * (such as IKEv2/IPsec). 54 * 55 * <p>This class is serialized and deserialized via the {@link #encode()} and {@link #decode()} 56 * functions for persistent storage in the Android Keystore. The encoding is entirely custom, but 57 * must be kept for backward compatibility for devices upgrading between Android versions. 58 * 59 * @hide 60 */ 61 public final class VpnProfile implements Cloneable, Parcelable { 62 private static final String TAG = "VpnProfile"; 63 64 @VisibleForTesting static final String VALUE_DELIMITER = "\0"; 65 @VisibleForTesting static final String LIST_DELIMITER = ","; 66 67 // Match these constants with R.array.vpn_types. 68 public static final int TYPE_PPTP = 0; 69 public static final int TYPE_L2TP_IPSEC_PSK = 1; 70 public static final int TYPE_L2TP_IPSEC_RSA = 2; 71 public static final int TYPE_IPSEC_XAUTH_PSK = 3; 72 public static final int TYPE_IPSEC_XAUTH_RSA = 4; 73 public static final int TYPE_IPSEC_HYBRID_RSA = 5; 74 public static final int TYPE_IKEV2_IPSEC_USER_PASS = 6; 75 public static final int TYPE_IKEV2_IPSEC_PSK = 7; 76 public static final int TYPE_IKEV2_IPSEC_RSA = 8; 77 public static final int TYPE_IKEV2_FROM_IKE_TUN_CONN_PARAMS = 9; 78 public static final int TYPE_MAX = 9; 79 80 // Match these constants with R.array.vpn_proxy_settings. 81 public static final int PROXY_NONE = 0; 82 public static final int PROXY_MANUAL = 1; 83 84 private static final String ENCODED_NULL_PROXY_INFO = "\0\0\0\0"; 85 86 /** Default URL encoding. */ 87 private static final String DEFAULT_ENCODING = StandardCharsets.UTF_8.name(); 88 89 // Entity fields. 90 @UnsupportedAppUsage 91 public final String key; // -1 92 93 @UnsupportedAppUsage 94 public String name = ""; // 0 95 96 @UnsupportedAppUsage 97 public int type = TYPE_PPTP; // 1 98 99 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) 100 public String server = ""; // 2 101 102 @UnsupportedAppUsage 103 public String username = ""; // 3 104 public String password = ""; // 4 105 public String dnsServers = ""; // 5 106 public String searchDomains = ""; // 6 107 public String routes = ""; // 7 108 public boolean mppe = true; // 8 109 public String l2tpSecret = ""; // 9 110 public String ipsecIdentifier = ""; // 10 111 112 /** 113 * The RSA private key or pre-shared key used for authentication. 114 * 115 * <p>If areAuthParamsInline is {@code true}, this String will be either: 116 * 117 * <ul> 118 * <li>If this is an IKEv2 RSA profile: a PKCS#8 encoded {@link java.security.PrivateKey} 119 * <li>If this is an IKEv2 PSK profile: a string value representing the PSK. 120 * </ul> 121 */ 122 public String ipsecSecret = ""; // 11 123 124 /** 125 * The RSA certificate to be used for digital signature authentication. 126 * 127 * <p>If areAuthParamsInline is {@code true}, this String will be a pem-encoded {@link 128 * java.security.X509Certificate} 129 */ 130 public String ipsecUserCert = ""; // 12 131 132 /** 133 * The RSA certificate that should be used to verify the server's end/target certificate. 134 * 135 * <p>If areAuthParamsInline is {@code true}, this String will be a pem-encoded {@link 136 * java.security.X509Certificate} 137 */ 138 public String ipsecCaCert = ""; // 13 139 public String ipsecServerCert = ""; // 14 140 public ProxyInfo proxy = null; // 15~18 141 142 /** 143 * The list of allowable algorithms. 144 */ 145 private List<String> mAllowedAlgorithms = new ArrayList<>(); // 19 146 public boolean isBypassable = false; // 20 147 public boolean isMetered = false; // 21 148 public int maxMtu = PlatformVpnProfile.MAX_MTU_DEFAULT; // 22 149 public boolean areAuthParamsInline = false; // 23 150 public final boolean isRestrictedToTestNetworks; // 24 151 152 public final boolean excludeLocalRoutes; // 25 153 public final boolean requiresInternetValidation; // 26 154 public final IkeTunnelConnectionParams ikeTunConnParams; // 27 155 public final boolean automaticNattKeepaliveTimerEnabled; // 28 156 public final boolean automaticIpVersionSelectionEnabled; // 29 157 158 // Helper fields. 159 @UnsupportedAppUsage 160 public transient boolean saveLogin = false; 161 VpnProfile(String key)162 public VpnProfile(String key) { 163 this(key, false, false, false, null); 164 } 165 VpnProfile(String key, boolean isRestrictedToTestNetworks)166 public VpnProfile(String key, boolean isRestrictedToTestNetworks) { 167 this(key, isRestrictedToTestNetworks, false, false, null); 168 } 169 VpnProfile(String key, boolean isRestrictedToTestNetworks, boolean excludeLocalRoutes, boolean requiresInternetValidation, IkeTunnelConnectionParams ikeTunConnParams)170 public VpnProfile(String key, boolean isRestrictedToTestNetworks, boolean excludeLocalRoutes, 171 boolean requiresInternetValidation, IkeTunnelConnectionParams ikeTunConnParams) { 172 this(key, isRestrictedToTestNetworks, excludeLocalRoutes, requiresInternetValidation, 173 ikeTunConnParams, false, false); 174 } 175 VpnProfile(String key, boolean isRestrictedToTestNetworks, boolean excludeLocalRoutes, boolean requiresInternetValidation, IkeTunnelConnectionParams ikeTunConnParams, boolean automaticNattKeepaliveTimerEnabled, boolean automaticIpVersionSelectionEnabled)176 public VpnProfile(String key, boolean isRestrictedToTestNetworks, boolean excludeLocalRoutes, 177 boolean requiresInternetValidation, IkeTunnelConnectionParams ikeTunConnParams, 178 boolean automaticNattKeepaliveTimerEnabled, 179 boolean automaticIpVersionSelectionEnabled) { 180 this.key = key; 181 this.isRestrictedToTestNetworks = isRestrictedToTestNetworks; 182 this.excludeLocalRoutes = excludeLocalRoutes; 183 this.requiresInternetValidation = requiresInternetValidation; 184 this.ikeTunConnParams = ikeTunConnParams; 185 this.automaticNattKeepaliveTimerEnabled = automaticNattKeepaliveTimerEnabled; 186 this.automaticIpVersionSelectionEnabled = automaticIpVersionSelectionEnabled; 187 } 188 189 @UnsupportedAppUsage VpnProfile(Parcel in)190 public VpnProfile(Parcel in) { 191 key = in.readString(); 192 name = in.readString(); 193 type = in.readInt(); 194 server = in.readString(); 195 username = in.readString(); 196 password = in.readString(); 197 dnsServers = in.readString(); 198 searchDomains = in.readString(); 199 routes = in.readString(); 200 mppe = in.readInt() != 0; 201 l2tpSecret = in.readString(); 202 ipsecIdentifier = in.readString(); 203 ipsecSecret = in.readString(); 204 ipsecUserCert = in.readString(); 205 ipsecCaCert = in.readString(); 206 ipsecServerCert = in.readString(); 207 saveLogin = in.readInt() != 0; 208 proxy = in.readParcelable(null, android.net.ProxyInfo.class); 209 mAllowedAlgorithms = new ArrayList<>(); 210 in.readList(mAllowedAlgorithms, null, java.lang.String.class); 211 isBypassable = in.readBoolean(); 212 isMetered = in.readBoolean(); 213 maxMtu = in.readInt(); 214 areAuthParamsInline = in.readBoolean(); 215 isRestrictedToTestNetworks = in.readBoolean(); 216 excludeLocalRoutes = in.readBoolean(); 217 requiresInternetValidation = in.readBoolean(); 218 final PersistableBundle bundle = 219 in.readParcelable(PersistableBundle.class.getClassLoader(), android.os.PersistableBundle.class); 220 ikeTunConnParams = (bundle == null) ? null 221 : TunnelConnectionParamsUtils.fromPersistableBundle(bundle); 222 automaticNattKeepaliveTimerEnabled = in.readBoolean(); 223 automaticIpVersionSelectionEnabled = in.readBoolean(); 224 } 225 226 /** 227 * Retrieves the list of allowed algorithms. 228 * 229 * <p>The contained elements are as listed in {@link IpSecAlgorithm} 230 */ getAllowedAlgorithms()231 public List<String> getAllowedAlgorithms() { 232 return Collections.unmodifiableList(mAllowedAlgorithms); 233 } 234 235 /** 236 * Validates and sets the list of algorithms that can be used for the IPsec transforms. 237 * 238 * @param allowedAlgorithms the list of allowable algorithms, as listed in {@link 239 * IpSecAlgorithm}. 240 */ setAllowedAlgorithms(List<String> allowedAlgorithms)241 public void setAllowedAlgorithms(List<String> allowedAlgorithms) { 242 mAllowedAlgorithms = allowedAlgorithms; 243 } 244 245 @Override writeToParcel(Parcel out, int flags)246 public void writeToParcel(Parcel out, int flags) { 247 out.writeString(key); 248 out.writeString(name); 249 out.writeInt(type); 250 out.writeString(server); 251 out.writeString(username); 252 out.writeString(password); 253 out.writeString(dnsServers); 254 out.writeString(searchDomains); 255 out.writeString(routes); 256 out.writeInt(mppe ? 1 : 0); 257 out.writeString(l2tpSecret); 258 out.writeString(ipsecIdentifier); 259 out.writeString(ipsecSecret); 260 out.writeString(ipsecUserCert); 261 out.writeString(ipsecCaCert); 262 out.writeString(ipsecServerCert); 263 out.writeInt(saveLogin ? 1 : 0); 264 out.writeParcelable(proxy, flags); 265 out.writeList(mAllowedAlgorithms); 266 out.writeBoolean(isBypassable); 267 out.writeBoolean(isMetered); 268 out.writeInt(maxMtu); 269 out.writeBoolean(areAuthParamsInline); 270 out.writeBoolean(isRestrictedToTestNetworks); 271 out.writeBoolean(excludeLocalRoutes); 272 out.writeBoolean(requiresInternetValidation); 273 out.writeParcelable(ikeTunConnParams == null ? null 274 : TunnelConnectionParamsUtils.toPersistableBundle(ikeTunConnParams), flags); 275 out.writeBoolean(automaticNattKeepaliveTimerEnabled); 276 out.writeBoolean(automaticIpVersionSelectionEnabled); 277 } 278 279 /** 280 * Decodes a VpnProfile instance from the encoded byte array. 281 * 282 * <p>See {@link #encode()} 283 */ 284 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) decode(String key, byte[] value)285 public static VpnProfile decode(String key, byte[] value) { 286 try { 287 if (key == null) { 288 return null; 289 } 290 291 String[] values = new String(value, StandardCharsets.UTF_8).split(VALUE_DELIMITER, -1); 292 293 // Acceptable numbers of values are: 294 // 14-19: Standard profile, with option for serverCert, proxy 295 // 24: Standard profile with serverCert, proxy and platform-VPN parameters 296 // 25: Standard profile with platform-VPN parameters and isRestrictedToTestNetworks 297 // 26: ...and excludeLocalRoutes 298 // 27: ...and requiresInternetValidation 299 // (26,27 can only be found on dogfood devices) 300 // 28: ...and ikeTunConnParams 301 // 29-30: ...and automatic NATT/IP version 302 if ((values.length < 14 || (values.length > 19 && values.length < 24) 303 || (values.length > 28 && values.length < 30) || values.length > 30)) { 304 return null; 305 } 306 307 final boolean isRestrictedToTestNetworks; 308 if (values.length >= 25) { 309 isRestrictedToTestNetworks = Boolean.parseBoolean(values[24]); 310 } else { 311 isRestrictedToTestNetworks = false; 312 } 313 314 final boolean excludeLocalRoutes; 315 if (values.length >= 26) { 316 excludeLocalRoutes = Boolean.parseBoolean(values[25]); 317 } else { 318 excludeLocalRoutes = false; 319 } 320 321 final boolean requiresInternetValidation; 322 if (values.length >= 27) { 323 requiresInternetValidation = Boolean.parseBoolean(values[26]); 324 } else { 325 requiresInternetValidation = false; 326 } 327 328 final IkeTunnelConnectionParams tempIkeTunConnParams; 329 // Assign null directly if the ikeTunConParams field is empty. 330 if (values.length >= 28 && values[27].length() != 0) { 331 final Parcel parcel = Parcel.obtain(); 332 final byte[] bytes = HexDump.hexStringToByteArray(values[27]); 333 parcel.unmarshall(bytes, 0, bytes.length); 334 parcel.setDataPosition(0); 335 final PersistableBundle bundle = (PersistableBundle) parcel.readValue( 336 PersistableBundle.class.getClassLoader()); 337 tempIkeTunConnParams = TunnelConnectionParamsUtils.fromPersistableBundle(bundle); 338 } else { 339 tempIkeTunConnParams = null; 340 } 341 342 final boolean automaticNattKeepaliveTimerEnabled; 343 final boolean automaticIpVersionSelectionEnabled; 344 if (values.length >= 30) { 345 automaticNattKeepaliveTimerEnabled = Boolean.parseBoolean(values[28]); 346 automaticIpVersionSelectionEnabled = Boolean.parseBoolean(values[29]); 347 } else { 348 automaticNattKeepaliveTimerEnabled = false; 349 automaticIpVersionSelectionEnabled = false; 350 } 351 352 VpnProfile profile = new VpnProfile(key, isRestrictedToTestNetworks, 353 excludeLocalRoutes, requiresInternetValidation, tempIkeTunConnParams, 354 automaticNattKeepaliveTimerEnabled, automaticIpVersionSelectionEnabled); 355 profile.name = values[0]; 356 profile.type = Integer.parseInt(values[1]); 357 if (profile.type < 0 || profile.type > TYPE_MAX) { 358 return null; 359 } 360 profile.server = values[2]; 361 profile.username = values[3]; 362 profile.password = values[4]; 363 profile.dnsServers = values[5]; 364 profile.searchDomains = values[6]; 365 profile.routes = values[7]; 366 profile.mppe = Boolean.parseBoolean(values[8]); 367 profile.l2tpSecret = values[9]; 368 profile.ipsecIdentifier = values[10]; 369 profile.ipsecSecret = values[11]; 370 profile.ipsecUserCert = values[12]; 371 profile.ipsecCaCert = values[13]; 372 profile.ipsecServerCert = (values.length > 14) ? values[14] : ""; 373 if (values.length > 15) { 374 String host = (values.length > 15) ? values[15] : ""; 375 String port = (values.length > 16) ? values[16] : ""; 376 String exclList = (values.length > 17) ? values[17] : ""; 377 String pacFileUrl = (values.length > 18) ? values[18] : ""; 378 if (!host.isEmpty() || !port.isEmpty() || !exclList.isEmpty()) { 379 profile.proxy = 380 ProxyInfo.buildDirectProxy(host, port.isEmpty() ? 381 0 : Integer.parseInt(port), 382 ProxyUtils.exclusionStringAsList(exclList)); 383 } else if (!pacFileUrl.isEmpty()) { 384 profile.proxy = ProxyInfo.buildPacProxy(Uri.parse(pacFileUrl)); 385 } 386 } // else profile.proxy = null 387 388 // Either all must be present, or none must be. 389 if (values.length >= 24) { 390 profile.mAllowedAlgorithms = new ArrayList<>(); 391 for (String algo : Arrays.asList(values[19].split(LIST_DELIMITER))) { 392 profile.mAllowedAlgorithms.add(URLDecoder.decode(algo, DEFAULT_ENCODING)); 393 } 394 395 profile.isBypassable = Boolean.parseBoolean(values[20]); 396 profile.isMetered = Boolean.parseBoolean(values[21]); 397 profile.maxMtu = Integer.parseInt(values[22]); 398 profile.areAuthParamsInline = Boolean.parseBoolean(values[23]); 399 } 400 401 // isRestrictedToTestNetworks (values[24]) assigned as part of the constructor 402 403 profile.saveLogin = !profile.username.isEmpty() || !profile.password.isEmpty(); 404 return profile; 405 } catch (Exception e) { 406 Log.d(TAG, "Got exception in decode.", e); 407 // ignore 408 } 409 return null; 410 } 411 412 /** 413 * Encodes a VpnProfile instance to a byte array for storage. 414 * 415 * <p>See {@link #decode(String, byte[])} 416 */ encode()417 public byte[] encode() { 418 StringBuilder builder = new StringBuilder(name); 419 builder.append(VALUE_DELIMITER).append(type); 420 builder.append(VALUE_DELIMITER).append(server); 421 builder.append(VALUE_DELIMITER).append(saveLogin ? username : ""); 422 builder.append(VALUE_DELIMITER).append(saveLogin ? password : ""); 423 builder.append(VALUE_DELIMITER).append(dnsServers); 424 builder.append(VALUE_DELIMITER).append(searchDomains); 425 builder.append(VALUE_DELIMITER).append(routes); 426 builder.append(VALUE_DELIMITER).append(mppe); 427 builder.append(VALUE_DELIMITER).append(l2tpSecret); 428 builder.append(VALUE_DELIMITER).append(ipsecIdentifier); 429 builder.append(VALUE_DELIMITER).append(ipsecSecret); 430 builder.append(VALUE_DELIMITER).append(ipsecUserCert); 431 builder.append(VALUE_DELIMITER).append(ipsecCaCert); 432 builder.append(VALUE_DELIMITER).append(ipsecServerCert); 433 if (proxy != null) { 434 builder.append(VALUE_DELIMITER).append(proxy.getHost() != null ? proxy.getHost() : ""); 435 builder.append(VALUE_DELIMITER).append(proxy.getPort()); 436 builder.append(VALUE_DELIMITER) 437 .append( 438 ProxyUtils.exclusionListAsString(proxy.getExclusionList()) != null 439 ? ProxyUtils.exclusionListAsString(proxy.getExclusionList()) 440 : ""); 441 builder.append(VALUE_DELIMITER).append(proxy.getPacFileUrl().toString()); 442 } else { 443 builder.append(ENCODED_NULL_PROXY_INFO); 444 } 445 446 final List<String> encodedAlgoNames = new ArrayList<>(); 447 448 try { 449 for (String algo : mAllowedAlgorithms) { 450 encodedAlgoNames.add(URLEncoder.encode(algo, DEFAULT_ENCODING)); 451 } 452 } catch (UnsupportedEncodingException e) { 453 // Unexpected error 454 throw new IllegalStateException("Failed to encode algorithms.", e); 455 } 456 457 builder.append(VALUE_DELIMITER).append(String.join(LIST_DELIMITER, encodedAlgoNames)); 458 459 builder.append(VALUE_DELIMITER).append(isBypassable); 460 builder.append(VALUE_DELIMITER).append(isMetered); 461 builder.append(VALUE_DELIMITER).append(maxMtu); 462 builder.append(VALUE_DELIMITER).append(areAuthParamsInline); 463 builder.append(VALUE_DELIMITER).append(isRestrictedToTestNetworks); 464 465 builder.append(VALUE_DELIMITER).append(excludeLocalRoutes); 466 builder.append(VALUE_DELIMITER).append(requiresInternetValidation); 467 468 if (ikeTunConnParams != null) { 469 final PersistableBundle bundle = 470 TunnelConnectionParamsUtils.toPersistableBundle(ikeTunConnParams); 471 final Parcel parcel = Parcel.obtain(); 472 parcel.writeValue(bundle); 473 final byte[] bytes = parcel.marshall(); 474 builder.append(VALUE_DELIMITER).append(HexDump.toHexString(bytes)); 475 } else { 476 builder.append(VALUE_DELIMITER).append(""); 477 } 478 builder.append(VALUE_DELIMITER).append(automaticNattKeepaliveTimerEnabled); 479 builder.append(VALUE_DELIMITER).append(automaticIpVersionSelectionEnabled); 480 481 return builder.toString().getBytes(StandardCharsets.UTF_8); 482 } 483 484 /** Checks if this profile specifies a LegacyVpn type. */ isLegacyType(int type)485 public static boolean isLegacyType(int type) { 486 switch (type) { 487 case VpnProfile.TYPE_PPTP: 488 case VpnProfile.TYPE_L2TP_IPSEC_PSK: 489 case VpnProfile.TYPE_L2TP_IPSEC_RSA: 490 case VpnProfile.TYPE_IPSEC_XAUTH_PSK: 491 case VpnProfile.TYPE_IPSEC_XAUTH_RSA: 492 case VpnProfile.TYPE_IPSEC_HYBRID_RSA: 493 return true; 494 default: 495 return false; 496 } 497 } 498 isValidLockdownLegacyVpnProfile()499 private boolean isValidLockdownLegacyVpnProfile() { 500 return isLegacyType(type) && isServerAddressNumeric() && hasDns() 501 && areDnsAddressesNumeric(); 502 } 503 isValidLockdownPlatformVpnProfile()504 private boolean isValidLockdownPlatformVpnProfile() { 505 return Ikev2VpnProfile.isValidVpnProfile(this); 506 } 507 508 /** 509 * Tests if profile is valid for lockdown. 510 * 511 * <p>For LegacyVpn profiles, this requires an IPv4 address for both the server and DNS. 512 * 513 * <p>For PlatformVpn profiles, this requires a server, an identifier and the relevant fields to 514 * be non-null. 515 */ isValidLockdownProfile()516 public boolean isValidLockdownProfile() { 517 return isTypeValidForLockdown() 518 && (isValidLockdownLegacyVpnProfile() || isValidLockdownPlatformVpnProfile()); 519 } 520 521 /** Returns {@code true} if the VPN type is valid for lockdown. */ isTypeValidForLockdown()522 public boolean isTypeValidForLockdown() { 523 // b/7064069: lockdown firewall blocks ports used for PPTP 524 return type != TYPE_PPTP; 525 } 526 527 /** Returns {@code true} if the server address is numeric, e.g. 8.8.8.8 */ isServerAddressNumeric()528 public boolean isServerAddressNumeric() { 529 try { 530 InetAddress.parseNumericAddress(server); 531 } catch (IllegalArgumentException e) { 532 return false; 533 } 534 return true; 535 } 536 537 /** Returns {@code true} if one or more DNS servers are specified. */ hasDns()538 public boolean hasDns() { 539 return !TextUtils.isEmpty(dnsServers); 540 } 541 542 /** Returns {@code true} if all DNS servers have numeric addresses, e.g. 8.8.8.8 */ areDnsAddressesNumeric()543 public boolean areDnsAddressesNumeric() { 544 try { 545 for (String dnsServer : dnsServers.split(" +")) { 546 InetAddress.parseNumericAddress(dnsServer); 547 } 548 } catch (IllegalArgumentException e) { 549 return false; 550 } 551 return true; 552 } 553 554 /** Generates a hashcode over the VpnProfile. */ 555 @Override hashCode()556 public int hashCode() { 557 return Objects.hash( 558 key, type, server, username, password, dnsServers, searchDomains, routes, mppe, 559 l2tpSecret, ipsecIdentifier, ipsecSecret, ipsecUserCert, ipsecCaCert, ipsecServerCert, 560 proxy, mAllowedAlgorithms, isBypassable, isMetered, maxMtu, areAuthParamsInline, 561 isRestrictedToTestNetworks, excludeLocalRoutes, requiresInternetValidation, 562 ikeTunConnParams, automaticNattKeepaliveTimerEnabled, 563 automaticIpVersionSelectionEnabled); 564 } 565 566 /** Checks VPN profiles for interior equality. */ 567 @Override equals(Object obj)568 public boolean equals(Object obj) { 569 if (!(obj instanceof VpnProfile)) { 570 return false; 571 } 572 573 final VpnProfile other = (VpnProfile) obj; 574 return Objects.equals(key, other.key) 575 && Objects.equals(name, other.name) 576 && type == other.type 577 && Objects.equals(server, other.server) 578 && Objects.equals(username, other.username) 579 && Objects.equals(password, other.password) 580 && Objects.equals(dnsServers, other.dnsServers) 581 && Objects.equals(searchDomains, other.searchDomains) 582 && Objects.equals(routes, other.routes) 583 && mppe == other.mppe 584 && Objects.equals(l2tpSecret, other.l2tpSecret) 585 && Objects.equals(ipsecIdentifier, other.ipsecIdentifier) 586 && Objects.equals(ipsecSecret, other.ipsecSecret) 587 && Objects.equals(ipsecUserCert, other.ipsecUserCert) 588 && Objects.equals(ipsecCaCert, other.ipsecCaCert) 589 && Objects.equals(ipsecServerCert, other.ipsecServerCert) 590 && Objects.equals(proxy, other.proxy) 591 && Objects.equals(mAllowedAlgorithms, other.mAllowedAlgorithms) 592 && isBypassable == other.isBypassable 593 && isMetered == other.isMetered 594 && maxMtu == other.maxMtu 595 && areAuthParamsInline == other.areAuthParamsInline 596 && isRestrictedToTestNetworks == other.isRestrictedToTestNetworks 597 && excludeLocalRoutes == other.excludeLocalRoutes 598 && requiresInternetValidation == other.requiresInternetValidation 599 && Objects.equals(ikeTunConnParams, other.ikeTunConnParams) 600 && automaticNattKeepaliveTimerEnabled == other.automaticNattKeepaliveTimerEnabled 601 && automaticIpVersionSelectionEnabled == other.automaticIpVersionSelectionEnabled; 602 } 603 604 @NonNull 605 public static final Creator<VpnProfile> CREATOR = new Creator<>() { 606 @Override 607 public VpnProfile createFromParcel(Parcel in) { 608 return new VpnProfile(in); 609 } 610 611 @Override 612 public VpnProfile[] newArray(int size) { 613 return new VpnProfile[size]; 614 } 615 }; 616 617 @Override describeContents()618 public int describeContents() { 619 return 0; 620 } 621 622 @Override clone()623 public VpnProfile clone() { 624 try { 625 return (VpnProfile) super.clone(); 626 } catch (CloneNotSupportedException e) { 627 Log.wtf(TAG, e); 628 return null; 629 } 630 } 631 } 632