• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
156     // Helper fields.
157     @UnsupportedAppUsage
158     public transient boolean saveLogin = false;
159 
VpnProfile(String key)160     public VpnProfile(String key) {
161         this(key, false, false, false, null);
162     }
163 
VpnProfile(String key, boolean isRestrictedToTestNetworks)164     public VpnProfile(String key, boolean isRestrictedToTestNetworks) {
165         this(key, isRestrictedToTestNetworks, false, false, null);
166     }
167 
VpnProfile(String key, boolean isRestrictedToTestNetworks, boolean excludeLocalRoutes, boolean requiresInternetValidation, IkeTunnelConnectionParams ikeTunConnParams)168     public VpnProfile(String key, boolean isRestrictedToTestNetworks, boolean excludeLocalRoutes,
169             boolean requiresInternetValidation, IkeTunnelConnectionParams ikeTunConnParams) {
170         this.key = key;
171         this.isRestrictedToTestNetworks = isRestrictedToTestNetworks;
172         this.excludeLocalRoutes = excludeLocalRoutes;
173         this.requiresInternetValidation = requiresInternetValidation;
174         this.ikeTunConnParams = ikeTunConnParams;
175     }
176 
177     @UnsupportedAppUsage
VpnProfile(Parcel in)178     public VpnProfile(Parcel in) {
179         key = in.readString();
180         name = in.readString();
181         type = in.readInt();
182         server = in.readString();
183         username = in.readString();
184         password = in.readString();
185         dnsServers = in.readString();
186         searchDomains = in.readString();
187         routes = in.readString();
188         mppe = in.readInt() != 0;
189         l2tpSecret = in.readString();
190         ipsecIdentifier = in.readString();
191         ipsecSecret = in.readString();
192         ipsecUserCert = in.readString();
193         ipsecCaCert = in.readString();
194         ipsecServerCert = in.readString();
195         saveLogin = in.readInt() != 0;
196         proxy = in.readParcelable(null, android.net.ProxyInfo.class);
197         mAllowedAlgorithms = new ArrayList<>();
198         in.readList(mAllowedAlgorithms, null, java.lang.String.class);
199         isBypassable = in.readBoolean();
200         isMetered = in.readBoolean();
201         maxMtu = in.readInt();
202         areAuthParamsInline = in.readBoolean();
203         isRestrictedToTestNetworks = in.readBoolean();
204         excludeLocalRoutes = in.readBoolean();
205         requiresInternetValidation = in.readBoolean();
206         final PersistableBundle bundle =
207                 in.readParcelable(PersistableBundle.class.getClassLoader());
208         ikeTunConnParams = (bundle == null) ? null
209                 : TunnelConnectionParamsUtils.fromPersistableBundle(bundle);
210     }
211 
212     /**
213      * Retrieves the list of allowed algorithms.
214      *
215      * <p>The contained elements are as listed in {@link IpSecAlgorithm}
216      */
getAllowedAlgorithms()217     public List<String> getAllowedAlgorithms() {
218         return Collections.unmodifiableList(mAllowedAlgorithms);
219     }
220 
221     /**
222      * Validates and sets the list of algorithms that can be used for the IPsec transforms.
223      *
224      * @param allowedAlgorithms the list of allowable algorithms, as listed in {@link
225      *     IpSecAlgorithm}.
226      */
setAllowedAlgorithms(List<String> allowedAlgorithms)227     public void setAllowedAlgorithms(List<String> allowedAlgorithms) {
228         mAllowedAlgorithms = allowedAlgorithms;
229     }
230 
231     @Override
writeToParcel(Parcel out, int flags)232     public void writeToParcel(Parcel out, int flags) {
233         out.writeString(key);
234         out.writeString(name);
235         out.writeInt(type);
236         out.writeString(server);
237         out.writeString(username);
238         out.writeString(password);
239         out.writeString(dnsServers);
240         out.writeString(searchDomains);
241         out.writeString(routes);
242         out.writeInt(mppe ? 1 : 0);
243         out.writeString(l2tpSecret);
244         out.writeString(ipsecIdentifier);
245         out.writeString(ipsecSecret);
246         out.writeString(ipsecUserCert);
247         out.writeString(ipsecCaCert);
248         out.writeString(ipsecServerCert);
249         out.writeInt(saveLogin ? 1 : 0);
250         out.writeParcelable(proxy, flags);
251         out.writeList(mAllowedAlgorithms);
252         out.writeBoolean(isBypassable);
253         out.writeBoolean(isMetered);
254         out.writeInt(maxMtu);
255         out.writeBoolean(areAuthParamsInline);
256         out.writeBoolean(isRestrictedToTestNetworks);
257         out.writeBoolean(excludeLocalRoutes);
258         out.writeBoolean(requiresInternetValidation);
259         out.writeParcelable(ikeTunConnParams == null ? null
260                 : TunnelConnectionParamsUtils.toPersistableBundle(ikeTunConnParams), flags);
261     }
262 
263     /**
264      * Decodes a VpnProfile instance from the encoded byte array.
265      *
266      * <p>See {@link #encode()}
267      */
268     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
decode(String key, byte[] value)269     public static VpnProfile decode(String key, byte[] value) {
270         try {
271             if (key == null) {
272                 return null;
273             }
274 
275             String[] values = new String(value, StandardCharsets.UTF_8).split(VALUE_DELIMITER, -1);
276 
277             // Acceptable numbers of values are:
278             // 14-19: Standard profile, with option for serverCert, proxy
279             // 24: Standard profile with serverCert, proxy and platform-VPN parameters
280             // 25: Standard profile with platform-VPN parameters and isRestrictedToTestNetworks
281             // 26:                                            ...and excludeLocalRoutes
282             // 27:                                            ...and requiresInternetValidation
283             //     (26,27 can only be found on dogfood devices)
284             // 28:                                            ...and ikeTunConnParams
285             if ((values.length < 14 || (values.length > 19 && values.length < 24)
286                     || values.length > 28)) {
287                 return null;
288             }
289 
290             final boolean isRestrictedToTestNetworks;
291             if (values.length >= 25) {
292                 isRestrictedToTestNetworks = Boolean.parseBoolean(values[24]);
293             } else {
294                 isRestrictedToTestNetworks = false;
295             }
296 
297             final boolean excludeLocalRoutes;
298             if (values.length >= 26) {
299                 excludeLocalRoutes = Boolean.parseBoolean(values[25]);
300             } else {
301                 excludeLocalRoutes = false;
302             }
303 
304             final boolean requiresInternetValidation;
305             if (values.length >= 27) {
306                 requiresInternetValidation = Boolean.parseBoolean(values[26]);
307             } else {
308                 requiresInternetValidation = false;
309             }
310 
311             final IkeTunnelConnectionParams tempIkeTunConnParams;
312             // Assign null directly if the ikeTunConParams field is empty.
313             if (values.length >= 28 && values[27].length() != 0) {
314                 final Parcel parcel = Parcel.obtain();
315                 final byte[] bytes = HexDump.hexStringToByteArray(values[27]);
316                 parcel.unmarshall(bytes, 0, bytes.length);
317                 parcel.setDataPosition(0);
318                 final PersistableBundle bundle = (PersistableBundle) parcel.readValue(
319                         PersistableBundle.class.getClassLoader());
320                 tempIkeTunConnParams = TunnelConnectionParamsUtils.fromPersistableBundle(bundle);
321             } else {
322                 tempIkeTunConnParams = null;
323             }
324 
325             VpnProfile profile = new VpnProfile(key, isRestrictedToTestNetworks,
326                     excludeLocalRoutes, requiresInternetValidation, tempIkeTunConnParams);
327             profile.name = values[0];
328             profile.type = Integer.parseInt(values[1]);
329             if (profile.type < 0 || profile.type > TYPE_MAX) {
330                 return null;
331             }
332             profile.server = values[2];
333             profile.username = values[3];
334             profile.password = values[4];
335             profile.dnsServers = values[5];
336             profile.searchDomains = values[6];
337             profile.routes = values[7];
338             profile.mppe = Boolean.parseBoolean(values[8]);
339             profile.l2tpSecret = values[9];
340             profile.ipsecIdentifier = values[10];
341             profile.ipsecSecret = values[11];
342             profile.ipsecUserCert = values[12];
343             profile.ipsecCaCert = values[13];
344             profile.ipsecServerCert = (values.length > 14) ? values[14] : "";
345             if (values.length > 15) {
346                 String host = (values.length > 15) ? values[15] : "";
347                 String port = (values.length > 16) ? values[16] : "";
348                 String exclList = (values.length > 17) ? values[17] : "";
349                 String pacFileUrl = (values.length > 18) ? values[18] : "";
350                 if (!host.isEmpty() || !port.isEmpty() || !exclList.isEmpty()) {
351                     profile.proxy =
352                             ProxyInfo.buildDirectProxy(host, port.isEmpty() ?
353                                     0 : Integer.parseInt(port),
354                                     ProxyUtils.exclusionStringAsList(exclList));
355                 } else if (!pacFileUrl.isEmpty()) {
356                     profile.proxy = ProxyInfo.buildPacProxy(Uri.parse(pacFileUrl));
357                 }
358             } // else profile.proxy = null
359 
360             // Either all must be present, or none must be.
361             if (values.length >= 24) {
362                 profile.mAllowedAlgorithms = new ArrayList<>();
363                 for (String algo : Arrays.asList(values[19].split(LIST_DELIMITER))) {
364                     profile.mAllowedAlgorithms.add(URLDecoder.decode(algo, DEFAULT_ENCODING));
365                 }
366 
367                 profile.isBypassable = Boolean.parseBoolean(values[20]);
368                 profile.isMetered = Boolean.parseBoolean(values[21]);
369                 profile.maxMtu = Integer.parseInt(values[22]);
370                 profile.areAuthParamsInline = Boolean.parseBoolean(values[23]);
371             }
372 
373             // isRestrictedToTestNetworks (values[24]) assigned as part of the constructor
374 
375             profile.saveLogin = !profile.username.isEmpty() || !profile.password.isEmpty();
376             return profile;
377         } catch (Exception e) {
378             Log.d(TAG, "Got exception in decode.", e);
379             // ignore
380         }
381         return null;
382     }
383 
384     /**
385      * Encodes a VpnProfile instance to a byte array for storage.
386      *
387      * <p>See {@link #decode(String, byte[])}
388      */
encode()389     public byte[] encode() {
390         StringBuilder builder = new StringBuilder(name);
391         builder.append(VALUE_DELIMITER).append(type);
392         builder.append(VALUE_DELIMITER).append(server);
393         builder.append(VALUE_DELIMITER).append(saveLogin ? username : "");
394         builder.append(VALUE_DELIMITER).append(saveLogin ? password : "");
395         builder.append(VALUE_DELIMITER).append(dnsServers);
396         builder.append(VALUE_DELIMITER).append(searchDomains);
397         builder.append(VALUE_DELIMITER).append(routes);
398         builder.append(VALUE_DELIMITER).append(mppe);
399         builder.append(VALUE_DELIMITER).append(l2tpSecret);
400         builder.append(VALUE_DELIMITER).append(ipsecIdentifier);
401         builder.append(VALUE_DELIMITER).append(ipsecSecret);
402         builder.append(VALUE_DELIMITER).append(ipsecUserCert);
403         builder.append(VALUE_DELIMITER).append(ipsecCaCert);
404         builder.append(VALUE_DELIMITER).append(ipsecServerCert);
405         if (proxy != null) {
406             builder.append(VALUE_DELIMITER).append(proxy.getHost() != null ? proxy.getHost() : "");
407             builder.append(VALUE_DELIMITER).append(proxy.getPort());
408             builder.append(VALUE_DELIMITER)
409                     .append(
410                             ProxyUtils.exclusionListAsString(proxy.getExclusionList()) != null
411                                     ? ProxyUtils.exclusionListAsString(proxy.getExclusionList())
412                                     : "");
413             builder.append(VALUE_DELIMITER).append(proxy.getPacFileUrl().toString());
414         } else {
415             builder.append(ENCODED_NULL_PROXY_INFO);
416         }
417 
418         final List<String> encodedAlgoNames = new ArrayList<>();
419 
420         try {
421             for (String algo : mAllowedAlgorithms) {
422                 encodedAlgoNames.add(URLEncoder.encode(algo, DEFAULT_ENCODING));
423             }
424         } catch (UnsupportedEncodingException e) {
425             // Unexpected error
426             throw new IllegalStateException("Failed to encode algorithms.", e);
427         }
428 
429         builder.append(VALUE_DELIMITER).append(String.join(LIST_DELIMITER, encodedAlgoNames));
430 
431         builder.append(VALUE_DELIMITER).append(isBypassable);
432         builder.append(VALUE_DELIMITER).append(isMetered);
433         builder.append(VALUE_DELIMITER).append(maxMtu);
434         builder.append(VALUE_DELIMITER).append(areAuthParamsInline);
435         builder.append(VALUE_DELIMITER).append(isRestrictedToTestNetworks);
436 
437         builder.append(VALUE_DELIMITER).append(excludeLocalRoutes);
438         builder.append(VALUE_DELIMITER).append(requiresInternetValidation);
439 
440         if (ikeTunConnParams != null) {
441             final PersistableBundle bundle =
442                     TunnelConnectionParamsUtils.toPersistableBundle(ikeTunConnParams);
443             final Parcel parcel = Parcel.obtain();
444             parcel.writeValue(bundle);
445             final byte[] bytes = parcel.marshall();
446             builder.append(VALUE_DELIMITER).append(HexDump.toHexString(bytes));
447         } else {
448             builder.append(VALUE_DELIMITER).append("");
449         }
450 
451         return builder.toString().getBytes(StandardCharsets.UTF_8);
452     }
453 
454     /** Checks if this profile specifies a LegacyVpn type. */
isLegacyType(int type)455     public static boolean isLegacyType(int type) {
456         switch (type) {
457             case VpnProfile.TYPE_PPTP:
458             case VpnProfile.TYPE_L2TP_IPSEC_PSK:
459             case VpnProfile.TYPE_L2TP_IPSEC_RSA:
460             case VpnProfile.TYPE_IPSEC_XAUTH_PSK:
461             case VpnProfile.TYPE_IPSEC_XAUTH_RSA:
462             case VpnProfile.TYPE_IPSEC_HYBRID_RSA:
463                 return true;
464             default:
465                 return false;
466         }
467     }
468 
isValidLockdownLegacyVpnProfile()469     private boolean isValidLockdownLegacyVpnProfile() {
470         return isLegacyType(type) && isServerAddressNumeric() && hasDns()
471                 && areDnsAddressesNumeric();
472     }
473 
isValidLockdownPlatformVpnProfile()474     private boolean isValidLockdownPlatformVpnProfile() {
475         return Ikev2VpnProfile.isValidVpnProfile(this);
476     }
477 
478     /**
479      * Tests if profile is valid for lockdown.
480      *
481      * <p>For LegacyVpn profiles, this requires an IPv4 address for both the server and DNS.
482      *
483      * <p>For PlatformVpn profiles, this requires a server, an identifier and the relevant fields to
484      * be non-null.
485      */
isValidLockdownProfile()486     public boolean isValidLockdownProfile() {
487         return isTypeValidForLockdown()
488                 && (isValidLockdownLegacyVpnProfile() || isValidLockdownPlatformVpnProfile());
489     }
490 
491     /** Returns {@code true} if the VPN type is valid for lockdown. */
isTypeValidForLockdown()492     public boolean isTypeValidForLockdown() {
493         // b/7064069: lockdown firewall blocks ports used for PPTP
494         return type != TYPE_PPTP;
495     }
496 
497     /** Returns {@code true} if the server address is numeric, e.g. 8.8.8.8 */
isServerAddressNumeric()498     public boolean isServerAddressNumeric() {
499         try {
500             InetAddress.parseNumericAddress(server);
501         } catch (IllegalArgumentException e) {
502             return false;
503         }
504         return true;
505     }
506 
507     /** Returns {@code true} if one or more DNS servers are specified. */
hasDns()508     public boolean hasDns() {
509         return !TextUtils.isEmpty(dnsServers);
510     }
511 
512     /** Returns {@code true} if all DNS servers have numeric addresses, e.g. 8.8.8.8 */
areDnsAddressesNumeric()513     public boolean areDnsAddressesNumeric() {
514         try {
515             for (String dnsServer : dnsServers.split(" +")) {
516                 InetAddress.parseNumericAddress(dnsServer);
517             }
518         } catch (IllegalArgumentException e) {
519             return false;
520         }
521         return true;
522     }
523 
524     /** Generates a hashcode over the VpnProfile. */
525     @Override
hashCode()526     public int hashCode() {
527         return Objects.hash(
528             key, type, server, username, password, dnsServers, searchDomains, routes, mppe,
529             l2tpSecret, ipsecIdentifier, ipsecSecret, ipsecUserCert, ipsecCaCert, ipsecServerCert,
530             proxy, mAllowedAlgorithms, isBypassable, isMetered, maxMtu, areAuthParamsInline,
531             isRestrictedToTestNetworks, excludeLocalRoutes, requiresInternetValidation,
532             ikeTunConnParams);
533     }
534 
535     /** Checks VPN profiles for interior equality. */
536     @Override
equals(Object obj)537     public boolean equals(Object obj) {
538         if (!(obj instanceof VpnProfile)) {
539             return false;
540         }
541 
542         final VpnProfile other = (VpnProfile) obj;
543         return Objects.equals(key, other.key)
544                 && Objects.equals(name, other.name)
545                 && type == other.type
546                 && Objects.equals(server, other.server)
547                 && Objects.equals(username, other.username)
548                 && Objects.equals(password, other.password)
549                 && Objects.equals(dnsServers, other.dnsServers)
550                 && Objects.equals(searchDomains, other.searchDomains)
551                 && Objects.equals(routes, other.routes)
552                 && mppe == other.mppe
553                 && Objects.equals(l2tpSecret, other.l2tpSecret)
554                 && Objects.equals(ipsecIdentifier, other.ipsecIdentifier)
555                 && Objects.equals(ipsecSecret, other.ipsecSecret)
556                 && Objects.equals(ipsecUserCert, other.ipsecUserCert)
557                 && Objects.equals(ipsecCaCert, other.ipsecCaCert)
558                 && Objects.equals(ipsecServerCert, other.ipsecServerCert)
559                 && Objects.equals(proxy, other.proxy)
560                 && Objects.equals(mAllowedAlgorithms, other.mAllowedAlgorithms)
561                 && isBypassable == other.isBypassable
562                 && isMetered == other.isMetered
563                 && maxMtu == other.maxMtu
564                 && areAuthParamsInline == other.areAuthParamsInline
565                 && isRestrictedToTestNetworks == other.isRestrictedToTestNetworks
566                 && excludeLocalRoutes == other.excludeLocalRoutes
567                 && requiresInternetValidation == other.requiresInternetValidation
568                 && Objects.equals(ikeTunConnParams, other.ikeTunConnParams);
569     }
570 
571     @NonNull
572     public static final Creator<VpnProfile> CREATOR = new Creator<>() {
573         @Override
574         public VpnProfile createFromParcel(Parcel in) {
575             return new VpnProfile(in);
576         }
577 
578         @Override
579         public VpnProfile[] newArray(int size) {
580             return new VpnProfile[size];
581         }
582     };
583 
584     @Override
describeContents()585     public int describeContents() {
586         return 0;
587     }
588 }
589