1 /*
2  * Copyright (C) 2010 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 android.net;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.annotation.SystemApi;
22 import android.annotation.TestApi;
23 import android.compat.annotation.UnsupportedAppUsage;
24 import android.net.util.LinkPropertiesUtils;
25 import android.net.util.LinkPropertiesUtils.CompareResult;
26 import android.os.Build;
27 import android.os.Parcel;
28 import android.os.Parcelable;
29 import android.text.TextUtils;
30 
31 import java.net.Inet4Address;
32 import java.net.Inet6Address;
33 import java.net.InetAddress;
34 import java.net.UnknownHostException;
35 import java.util.ArrayList;
36 import java.util.Collection;
37 import java.util.Collections;
38 import java.util.Hashtable;
39 import java.util.List;
40 import java.util.Objects;
41 import java.util.StringJoiner;
42 
43 /**
44  * Describes the properties of a network link.
45  *
46  * A link represents a connection to a network.
47  * It may have multiple addresses and multiple gateways,
48  * multiple dns servers but only one http proxy and one
49  * network interface.
50  *
51  * Note that this is just a holder of data.  Modifying it
52  * does not affect live networks.
53  *
54  */
55 public final class LinkProperties implements Parcelable {
56     // The interface described by the network link.
57     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
58     private String mIfaceName;
59     private final ArrayList<LinkAddress> mLinkAddresses = new ArrayList<>();
60     private final ArrayList<InetAddress> mDnses = new ArrayList<>();
61     // PCSCF addresses are addresses of SIP proxies that only exist for the IMS core service.
62     private final ArrayList<InetAddress> mPcscfs = new ArrayList<InetAddress>();
63     private final ArrayList<InetAddress> mValidatedPrivateDnses = new ArrayList<>();
64     private boolean mUsePrivateDns;
65     private String mPrivateDnsServerName;
66     private String mDomains;
67     private ArrayList<RouteInfo> mRoutes = new ArrayList<>();
68     private Inet4Address mDhcpServerAddress;
69     private ProxyInfo mHttpProxy;
70     private int mMtu;
71     // in the format "rmem_min,rmem_def,rmem_max,wmem_min,wmem_def,wmem_max"
72     private String mTcpBufferSizes;
73     private IpPrefix mNat64Prefix;
74     private boolean mWakeOnLanSupported;
75     private Uri mCaptivePortalApiUrl;
76     private CaptivePortalData mCaptivePortalData;
77 
78     /**
79      * Indicates whether parceling should preserve fields that are set based on permissions of
80      * the process receiving the {@link LinkProperties}.
81      */
82     private final transient boolean mParcelSensitiveFields;
83 
84     private static final int MIN_MTU    = 68;
85     /* package-visibility - Used in other files (such as Ikev2VpnProfile) as minimum iface MTU. */
86     static final int MIN_MTU_V6 = 1280;
87     private static final int MAX_MTU    = 10000;
88 
89     private static final int INET6_ADDR_LENGTH = 16;
90 
91     // Stores the properties of links that are "stacked" above this link.
92     // Indexed by interface name to allow modification and to prevent duplicates being added.
93     private Hashtable<String, LinkProperties> mStackedLinks = new Hashtable<>();
94 
95     /**
96      * @hide
97      */
98     @UnsupportedAppUsage(implicitMember =
99             "values()[Landroid/net/LinkProperties$ProvisioningChange;")
100     public enum ProvisioningChange {
101         @UnsupportedAppUsage
102         STILL_NOT_PROVISIONED,
103         @UnsupportedAppUsage
104         LOST_PROVISIONING,
105         @UnsupportedAppUsage
106         GAINED_PROVISIONING,
107         @UnsupportedAppUsage
108         STILL_PROVISIONED,
109     }
110 
111     /**
112      * Compare the provisioning states of two LinkProperties instances.
113      *
114      * @hide
115      */
116     @UnsupportedAppUsage
compareProvisioning( LinkProperties before, LinkProperties after)117     public static ProvisioningChange compareProvisioning(
118             LinkProperties before, LinkProperties after) {
119         if (before.isProvisioned() && after.isProvisioned()) {
120             // On dual-stack networks, DHCPv4 renewals can occasionally fail.
121             // When this happens, IPv6-reachable services continue to function
122             // normally but IPv4-only services (naturally) fail.
123             //
124             // When an application using an IPv4-only service reports a bad
125             // network condition to the framework, attempts to re-validate
126             // the network succeed (since we support IPv6-only networks) and
127             // nothing is changed.
128             //
129             // For users, this is confusing and unexpected behaviour, and is
130             // not necessarily easy to diagnose.  Therefore, we treat changing
131             // from a dual-stack network to an IPv6-only network equivalent to
132             // a total loss of provisioning.
133             //
134             // For one such example of this, see b/18867306.
135             //
136             // Additionally, losing IPv6 provisioning can result in TCP
137             // connections getting stuck until timeouts fire and other
138             // baffling failures. Therefore, loss of either IPv4 or IPv6 on a
139             // previously dual-stack network is deemed a lost of provisioning.
140             if ((before.isIpv4Provisioned() && !after.isIpv4Provisioned())
141                     || (before.isIpv6Provisioned() && !after.isIpv6Provisioned())) {
142                 return ProvisioningChange.LOST_PROVISIONING;
143             }
144             return ProvisioningChange.STILL_PROVISIONED;
145         } else if (before.isProvisioned() && !after.isProvisioned()) {
146             return ProvisioningChange.LOST_PROVISIONING;
147         } else if (!before.isProvisioned() && after.isProvisioned()) {
148             return ProvisioningChange.GAINED_PROVISIONING;
149         } else {  // !before.isProvisioned() && !after.isProvisioned()
150             return ProvisioningChange.STILL_NOT_PROVISIONED;
151         }
152     }
153 
154     /**
155      * Constructs a new {@code LinkProperties} with default values.
156      */
LinkProperties()157     public LinkProperties() {
158         mParcelSensitiveFields = false;
159     }
160 
161     /**
162      * @hide
163      */
164     @SystemApi
165     @TestApi
LinkProperties(@ullable LinkProperties source)166     public LinkProperties(@Nullable LinkProperties source) {
167         this(source, false /* parcelSensitiveFields */);
168     }
169 
170     /**
171      * Create a copy of a {@link LinkProperties} that may preserve fields that were set
172      * based on the permissions of the process that originally received it.
173      *
174      * <p>By default {@link LinkProperties} does not preserve such fields during parceling, as
175      * they should not be shared outside of the process that receives them without appropriate
176      * checks.
177      * @param parcelSensitiveFields Whether the sensitive fields should be kept when parceling
178      * @hide
179      */
180     @SystemApi
181     @TestApi
LinkProperties(@ullable LinkProperties source, boolean parcelSensitiveFields)182     public LinkProperties(@Nullable LinkProperties source, boolean parcelSensitiveFields) {
183         mParcelSensitiveFields = parcelSensitiveFields;
184         if (source == null) return;
185         mIfaceName = source.mIfaceName;
186         mLinkAddresses.addAll(source.mLinkAddresses);
187         mDnses.addAll(source.mDnses);
188         mValidatedPrivateDnses.addAll(source.mValidatedPrivateDnses);
189         mUsePrivateDns = source.mUsePrivateDns;
190         mPrivateDnsServerName = source.mPrivateDnsServerName;
191         mPcscfs.addAll(source.mPcscfs);
192         mDomains = source.mDomains;
193         mRoutes.addAll(source.mRoutes);
194         mHttpProxy = (source.mHttpProxy == null) ? null : new ProxyInfo(source.mHttpProxy);
195         for (LinkProperties l: source.mStackedLinks.values()) {
196             addStackedLink(l);
197         }
198         setMtu(source.mMtu);
199         setDhcpServerAddress(source.getDhcpServerAddress());
200         mTcpBufferSizes = source.mTcpBufferSizes;
201         mNat64Prefix = source.mNat64Prefix;
202         mWakeOnLanSupported = source.mWakeOnLanSupported;
203         mCaptivePortalApiUrl = source.mCaptivePortalApiUrl;
204         mCaptivePortalData = source.mCaptivePortalData;
205     }
206 
207     /**
208      * Sets the interface name for this link.  All {@link RouteInfo} already set for this
209      * will have their interface changed to match this new value.
210      *
211      * @param iface The name of the network interface used for this link.
212      */
setInterfaceName(@ullable String iface)213     public void setInterfaceName(@Nullable String iface) {
214         mIfaceName = iface;
215         ArrayList<RouteInfo> newRoutes = new ArrayList<>(mRoutes.size());
216         for (RouteInfo route : mRoutes) {
217             newRoutes.add(routeWithInterface(route));
218         }
219         mRoutes = newRoutes;
220     }
221 
222     /**
223      * Gets the interface name for this link.  May be {@code null} if not set.
224      *
225      * @return The interface name set for this link or {@code null}.
226      */
getInterfaceName()227     public @Nullable String getInterfaceName() {
228         return mIfaceName;
229     }
230 
231     /**
232      * @hide
233      */
234     @SystemApi
getAllInterfaceNames()235     public @NonNull List<String> getAllInterfaceNames() {
236         List<String> interfaceNames = new ArrayList<>(mStackedLinks.size() + 1);
237         if (mIfaceName != null) interfaceNames.add(mIfaceName);
238         for (LinkProperties stacked: mStackedLinks.values()) {
239             interfaceNames.addAll(stacked.getAllInterfaceNames());
240         }
241         return interfaceNames;
242     }
243 
244     /**
245      * Returns all the addresses on this link.  We often think of a link having a single address,
246      * however, particularly with Ipv6 several addresses are typical.  Note that the
247      * {@code LinkProperties} actually contains {@link LinkAddress} objects which also include
248      * prefix lengths for each address.  This is a simplified utility alternative to
249      * {@link LinkProperties#getLinkAddresses}.
250      *
251      * @return An unmodifiable {@link List} of {@link InetAddress} for this link.
252      * @hide
253      */
254     @SystemApi
getAddresses()255     public @NonNull List<InetAddress> getAddresses() {
256         final List<InetAddress> addresses = new ArrayList<>();
257         for (LinkAddress linkAddress : mLinkAddresses) {
258             addresses.add(linkAddress.getAddress());
259         }
260         return Collections.unmodifiableList(addresses);
261     }
262 
263     /**
264      * Returns all the addresses on this link and all the links stacked above it.
265      * @hide
266      */
267     @UnsupportedAppUsage
getAllAddresses()268     public @NonNull List<InetAddress> getAllAddresses() {
269         List<InetAddress> addresses = new ArrayList<>();
270         for (LinkAddress linkAddress : mLinkAddresses) {
271             addresses.add(linkAddress.getAddress());
272         }
273         for (LinkProperties stacked: mStackedLinks.values()) {
274             addresses.addAll(stacked.getAllAddresses());
275         }
276         return addresses;
277     }
278 
findLinkAddressIndex(LinkAddress address)279     private int findLinkAddressIndex(LinkAddress address) {
280         for (int i = 0; i < mLinkAddresses.size(); i++) {
281             if (mLinkAddresses.get(i).isSameAddressAs(address)) {
282                 return i;
283             }
284         }
285         return -1;
286     }
287 
288     /**
289      * Adds a {@link LinkAddress} to this {@code LinkProperties} if a {@link LinkAddress} of the
290      * same address/prefix does not already exist.  If it does exist it is replaced.
291      * @param address The {@code LinkAddress} to add.
292      * @return true if {@code address} was added or updated, false otherwise.
293      * @hide
294      */
295     @SystemApi
296     @TestApi
addLinkAddress(@onNull LinkAddress address)297     public boolean addLinkAddress(@NonNull LinkAddress address) {
298         if (address == null) {
299             return false;
300         }
301         int i = findLinkAddressIndex(address);
302         if (i < 0) {
303             // Address was not present. Add it.
304             mLinkAddresses.add(address);
305             return true;
306         } else if (mLinkAddresses.get(i).equals(address)) {
307             // Address was present and has same properties. Do nothing.
308             return false;
309         } else {
310             // Address was present and has different properties. Update it.
311             mLinkAddresses.set(i, address);
312             return true;
313         }
314     }
315 
316     /**
317      * Removes a {@link LinkAddress} from this {@code LinkProperties}.  Specifically, matches
318      * and {@link LinkAddress} with the same address and prefix.
319      *
320      * @param toRemove A {@link LinkAddress} specifying the address to remove.
321      * @return true if the address was removed, false if it did not exist.
322      * @hide
323      */
324     @SystemApi
325     @TestApi
removeLinkAddress(@onNull LinkAddress toRemove)326     public boolean removeLinkAddress(@NonNull LinkAddress toRemove) {
327         int i = findLinkAddressIndex(toRemove);
328         if (i >= 0) {
329             mLinkAddresses.remove(i);
330             return true;
331         }
332         return false;
333     }
334 
335     /**
336      * Returns all the {@link LinkAddress} on this link.  Typically a link will have
337      * one IPv4 address and one or more IPv6 addresses.
338      *
339      * @return An unmodifiable {@link List} of {@link LinkAddress} for this link.
340      */
getLinkAddresses()341     public @NonNull List<LinkAddress> getLinkAddresses() {
342         return Collections.unmodifiableList(mLinkAddresses);
343     }
344 
345     /**
346      * Returns all the addresses on this link and all the links stacked above it.
347      * @hide
348      */
349     @SystemApi
getAllLinkAddresses()350     public @NonNull List<LinkAddress> getAllLinkAddresses() {
351         List<LinkAddress> addresses = new ArrayList<>(mLinkAddresses);
352         for (LinkProperties stacked: mStackedLinks.values()) {
353             addresses.addAll(stacked.getAllLinkAddresses());
354         }
355         return addresses;
356     }
357 
358     /**
359      * Replaces the {@link LinkAddress} in this {@code LinkProperties} with
360      * the given {@link Collection} of {@link LinkAddress}.
361      *
362      * @param addresses The {@link Collection} of {@link LinkAddress} to set in this
363      *                  object.
364      */
setLinkAddresses(@onNull Collection<LinkAddress> addresses)365     public void setLinkAddresses(@NonNull Collection<LinkAddress> addresses) {
366         mLinkAddresses.clear();
367         for (LinkAddress address: addresses) {
368             addLinkAddress(address);
369         }
370     }
371 
372     /**
373      * Adds the given {@link InetAddress} to the list of DNS servers, if not present.
374      *
375      * @param dnsServer The {@link InetAddress} to add to the list of DNS servers.
376      * @return true if the DNS server was added, false if it was already present.
377      * @hide
378      */
379     @TestApi
380     @SystemApi
addDnsServer(@onNull InetAddress dnsServer)381     public boolean addDnsServer(@NonNull InetAddress dnsServer) {
382         if (dnsServer != null && !mDnses.contains(dnsServer)) {
383             mDnses.add(dnsServer);
384             return true;
385         }
386         return false;
387     }
388 
389     /**
390      * Removes the given {@link InetAddress} from the list of DNS servers.
391      *
392      * @param dnsServer The {@link InetAddress} to remove from the list of DNS servers.
393      * @return true if the DNS server was removed, false if it did not exist.
394      * @hide
395      */
396     @TestApi
397     @SystemApi
removeDnsServer(@onNull InetAddress dnsServer)398     public boolean removeDnsServer(@NonNull InetAddress dnsServer) {
399         return mDnses.remove(dnsServer);
400     }
401 
402     /**
403      * Replaces the DNS servers in this {@code LinkProperties} with
404      * the given {@link Collection} of {@link InetAddress} objects.
405      *
406      * @param dnsServers The {@link Collection} of DNS servers to set in this object.
407      */
setDnsServers(@onNull Collection<InetAddress> dnsServers)408     public void setDnsServers(@NonNull Collection<InetAddress> dnsServers) {
409         mDnses.clear();
410         for (InetAddress dnsServer: dnsServers) {
411             addDnsServer(dnsServer);
412         }
413     }
414 
415     /**
416      * Returns all the {@link InetAddress} for DNS servers on this link.
417      *
418      * @return An unmodifiable {@link List} of {@link InetAddress} for DNS servers on
419      *         this link.
420      */
getDnsServers()421     public @NonNull List<InetAddress> getDnsServers() {
422         return Collections.unmodifiableList(mDnses);
423     }
424 
425     /**
426      * Set whether private DNS is currently in use on this network.
427      *
428      * @param usePrivateDns The private DNS state.
429      * @hide
430      */
431     @TestApi
432     @SystemApi
setUsePrivateDns(boolean usePrivateDns)433     public void setUsePrivateDns(boolean usePrivateDns) {
434         mUsePrivateDns = usePrivateDns;
435     }
436 
437     /**
438      * Returns whether private DNS is currently in use on this network. When
439      * private DNS is in use, applications must not send unencrypted DNS
440      * queries as doing so could reveal private user information. Furthermore,
441      * if private DNS is in use and {@link #getPrivateDnsServerName} is not
442      * {@code null}, DNS queries must be sent to the specified DNS server.
443      *
444      * @return {@code true} if private DNS is in use, {@code false} otherwise.
445      */
isPrivateDnsActive()446     public boolean isPrivateDnsActive() {
447         return mUsePrivateDns;
448     }
449 
450     /**
451      * Set the name of the private DNS server to which private DNS queries
452      * should be sent when in strict mode. This value should be {@code null}
453      * when private DNS is off or in opportunistic mode.
454      *
455      * @param privateDnsServerName The private DNS server name.
456      * @hide
457      */
458     @TestApi
459     @SystemApi
setPrivateDnsServerName(@ullable String privateDnsServerName)460     public void setPrivateDnsServerName(@Nullable String privateDnsServerName) {
461         mPrivateDnsServerName = privateDnsServerName;
462     }
463 
464     /**
465      * Set DHCP server address.
466      *
467      * @param serverAddress the server address to set.
468      */
setDhcpServerAddress(@ullable Inet4Address serverAddress)469     public void setDhcpServerAddress(@Nullable Inet4Address serverAddress) {
470         mDhcpServerAddress = serverAddress;
471     }
472 
473      /**
474      * Get DHCP server address
475      *
476      * @return The current DHCP server address.
477      */
getDhcpServerAddress()478     public @Nullable Inet4Address getDhcpServerAddress() {
479         return mDhcpServerAddress;
480     }
481 
482     /**
483      * Returns the private DNS server name that is in use. If not {@code null},
484      * private DNS is in strict mode. In this mode, applications should ensure
485      * that all DNS queries are encrypted and sent to this hostname and that
486      * queries are only sent if the hostname's certificate is valid. If
487      * {@code null} and {@link #isPrivateDnsActive} is {@code true}, private
488      * DNS is in opportunistic mode, and applications should ensure that DNS
489      * queries are encrypted and sent to a DNS server returned by
490      * {@link #getDnsServers}. System DNS will handle each of these cases
491      * correctly, but applications implementing their own DNS lookups must make
492      * sure to follow these requirements.
493      *
494      * @return The private DNS server name.
495      */
getPrivateDnsServerName()496     public @Nullable String getPrivateDnsServerName() {
497         return mPrivateDnsServerName;
498     }
499 
500     /**
501      * Adds the given {@link InetAddress} to the list of validated private DNS servers,
502      * if not present. This is distinct from the server name in that these are actually
503      * resolved addresses.
504      *
505      * @param dnsServer The {@link InetAddress} to add to the list of validated private DNS servers.
506      * @return true if the DNS server was added, false if it was already present.
507      * @hide
508      */
addValidatedPrivateDnsServer(@onNull InetAddress dnsServer)509     public boolean addValidatedPrivateDnsServer(@NonNull InetAddress dnsServer) {
510         if (dnsServer != null && !mValidatedPrivateDnses.contains(dnsServer)) {
511             mValidatedPrivateDnses.add(dnsServer);
512             return true;
513         }
514         return false;
515     }
516 
517     /**
518      * Removes the given {@link InetAddress} from the list of validated private DNS servers.
519      *
520      * @param dnsServer The {@link InetAddress} to remove from the list of validated private DNS
521      *        servers.
522      * @return true if the DNS server was removed, false if it did not exist.
523      * @hide
524      */
removeValidatedPrivateDnsServer(@onNull InetAddress dnsServer)525     public boolean removeValidatedPrivateDnsServer(@NonNull InetAddress dnsServer) {
526         return mValidatedPrivateDnses.remove(dnsServer);
527     }
528 
529     /**
530      * Replaces the validated private DNS servers in this {@code LinkProperties} with
531      * the given {@link Collection} of {@link InetAddress} objects.
532      *
533      * @param dnsServers The {@link Collection} of validated private DNS servers to set in this
534      *        object.
535      * @hide
536      */
537     @TestApi
538     @SystemApi
setValidatedPrivateDnsServers(@onNull Collection<InetAddress> dnsServers)539     public void setValidatedPrivateDnsServers(@NonNull Collection<InetAddress> dnsServers) {
540         mValidatedPrivateDnses.clear();
541         for (InetAddress dnsServer: dnsServers) {
542             addValidatedPrivateDnsServer(dnsServer);
543         }
544     }
545 
546     /**
547      * Returns all the {@link InetAddress} for validated private DNS servers on this link.
548      * These are resolved from the private DNS server name.
549      *
550      * @return An unmodifiable {@link List} of {@link InetAddress} for validated private
551      *         DNS servers on this link.
552      * @hide
553      */
554     @TestApi
555     @SystemApi
getValidatedPrivateDnsServers()556     public @NonNull List<InetAddress> getValidatedPrivateDnsServers() {
557         return Collections.unmodifiableList(mValidatedPrivateDnses);
558     }
559 
560     /**
561      * Adds the given {@link InetAddress} to the list of PCSCF servers, if not present.
562      *
563      * @param pcscfServer The {@link InetAddress} to add to the list of PCSCF servers.
564      * @return true if the PCSCF server was added, false otherwise.
565      * @hide
566      */
567     @SystemApi
addPcscfServer(@onNull InetAddress pcscfServer)568     public boolean addPcscfServer(@NonNull InetAddress pcscfServer) {
569         if (pcscfServer != null && !mPcscfs.contains(pcscfServer)) {
570             mPcscfs.add(pcscfServer);
571             return true;
572         }
573         return false;
574     }
575 
576     /**
577      * Removes the given {@link InetAddress} from the list of PCSCF servers.
578      *
579      * @param pcscfServer The {@link InetAddress} to remove from the list of PCSCF servers.
580      * @return true if the PCSCF server was removed, false otherwise.
581      * @hide
582      */
removePcscfServer(@onNull InetAddress pcscfServer)583     public boolean removePcscfServer(@NonNull InetAddress pcscfServer) {
584         return mPcscfs.remove(pcscfServer);
585     }
586 
587     /**
588      * Replaces the PCSCF servers in this {@code LinkProperties} with
589      * the given {@link Collection} of {@link InetAddress} objects.
590      *
591      * @param pcscfServers The {@link Collection} of PCSCF servers to set in this object.
592      * @hide
593      */
594     @SystemApi
595     @TestApi
setPcscfServers(@onNull Collection<InetAddress> pcscfServers)596     public void setPcscfServers(@NonNull Collection<InetAddress> pcscfServers) {
597         mPcscfs.clear();
598         for (InetAddress pcscfServer: pcscfServers) {
599             addPcscfServer(pcscfServer);
600         }
601     }
602 
603     /**
604      * Returns all the {@link InetAddress} for PCSCF servers on this link.
605      *
606      * @return An unmodifiable {@link List} of {@link InetAddress} for PCSCF servers on
607      *         this link.
608      * @hide
609      */
610     @SystemApi
611     @TestApi
getPcscfServers()612     public @NonNull List<InetAddress> getPcscfServers() {
613         return Collections.unmodifiableList(mPcscfs);
614     }
615 
616     /**
617      * Sets the DNS domain search path used on this link.
618      *
619      * @param domains A {@link String} listing in priority order the comma separated
620      *                domains to search when resolving host names on this link.
621      */
setDomains(@ullable String domains)622     public void setDomains(@Nullable String domains) {
623         mDomains = domains;
624     }
625 
626     /**
627      * Get the DNS domains search path set for this link. May be {@code null} if not set.
628      *
629      * @return A {@link String} containing the comma separated domains to search when resolving host
630      *         names on this link or {@code null}.
631      */
getDomains()632     public @Nullable String getDomains() {
633         return mDomains;
634     }
635 
636     /**
637      * Sets the Maximum Transmission Unit size to use on this link.  This should not be used
638      * unless the system default (1500) is incorrect.  Values less than 68 or greater than
639      * 10000 will be ignored.
640      *
641      * @param mtu The MTU to use for this link.
642      */
setMtu(int mtu)643     public void setMtu(int mtu) {
644         mMtu = mtu;
645     }
646 
647     /**
648      * Gets any non-default MTU size set for this link.  Note that if the default is being used
649      * this will return 0.
650      *
651      * @return The mtu value set for this link.
652      */
getMtu()653     public int getMtu() {
654         return mMtu;
655     }
656 
657     /**
658      * Sets the tcp buffers sizes to be used when this link is the system default.
659      * Should be of the form "rmem_min,rmem_def,rmem_max,wmem_min,wmem_def,wmem_max".
660      *
661      * @param tcpBufferSizes The tcp buffers sizes to use.
662      *
663      * @hide
664      */
665     @TestApi
666     @SystemApi
setTcpBufferSizes(@ullable String tcpBufferSizes)667     public void setTcpBufferSizes(@Nullable String tcpBufferSizes) {
668         mTcpBufferSizes = tcpBufferSizes;
669     }
670 
671     /**
672      * Gets the tcp buffer sizes. May be {@code null} if not set.
673      *
674      * @return the tcp buffer sizes to use when this link is the system default or {@code null}.
675      *
676      * @hide
677      */
678     @TestApi
679     @SystemApi
getTcpBufferSizes()680     public @Nullable String getTcpBufferSizes() {
681         return mTcpBufferSizes;
682     }
683 
routeWithInterface(RouteInfo route)684     private RouteInfo routeWithInterface(RouteInfo route) {
685         return new RouteInfo(
686             route.getDestination(),
687             route.getGateway(),
688             mIfaceName,
689             route.getType(),
690             route.getMtu());
691     }
692 
findRouteIndexByRouteKey(RouteInfo route)693     private int findRouteIndexByRouteKey(RouteInfo route) {
694         for (int i = 0; i < mRoutes.size(); i++) {
695             if (mRoutes.get(i).getRouteKey().equals(route.getRouteKey())) {
696                 return i;
697             }
698         }
699         return -1;
700     }
701 
702     /**
703      * Adds a {@link RouteInfo} to this {@code LinkProperties}, if a {@link RouteInfo}
704      * with the same {@link RouteInfo.RouteKey} with different properties
705      * (e.g., different MTU), it will be updated. If the {@link RouteInfo} had an
706      * interface name set and that differs from the interface set for this
707      * {@code LinkProperties} an {@link IllegalArgumentException} will be thrown.
708      * The proper course is to add either un-named or properly named {@link RouteInfo}.
709      *
710      * @param route A {@link RouteInfo} to add to this object.
711      * @return {@code true} was added or updated, false otherwise.
712      */
addRoute(@onNull RouteInfo route)713     public boolean addRoute(@NonNull RouteInfo route) {
714         String routeIface = route.getInterface();
715         if (routeIface != null && !routeIface.equals(mIfaceName)) {
716             throw new IllegalArgumentException(
717                     "Route added with non-matching interface: " + routeIface
718                             + " vs. " + mIfaceName);
719         }
720         route = routeWithInterface(route);
721 
722         int i = findRouteIndexByRouteKey(route);
723         if (i == -1) {
724             // Route was not present. Add it.
725             mRoutes.add(route);
726             return true;
727         } else if (mRoutes.get(i).equals(route)) {
728             // Route was present and has same properties. Do nothing.
729             return false;
730         } else {
731             // Route was present and has different properties. Update it.
732             mRoutes.set(i, route);
733             return true;
734         }
735     }
736 
737     /**
738      * Removes a {@link RouteInfo} from this {@code LinkProperties}, if present. The route must
739      * specify an interface and the interface must match the interface of this
740      * {@code LinkProperties}, or it will not be removed.
741      *
742      * @param route A {@link RouteInfo} specifying the route to remove.
743      * @return {@code true} if the route was removed, {@code false} if it was not present.
744      *
745      * @hide
746      */
747     @TestApi
748     @SystemApi
removeRoute(@onNull RouteInfo route)749     public boolean removeRoute(@NonNull RouteInfo route) {
750         return Objects.equals(mIfaceName, route.getInterface()) && mRoutes.remove(route);
751     }
752 
753     /**
754      * Returns all the {@link RouteInfo} set on this link.
755      *
756      * @return An unmodifiable {@link List} of {@link RouteInfo} for this link.
757      */
getRoutes()758     public @NonNull List<RouteInfo> getRoutes() {
759         return Collections.unmodifiableList(mRoutes);
760     }
761 
762     /**
763      * Make sure this LinkProperties instance contains routes that cover the local subnet
764      * of its link addresses. Add any route that is missing.
765      * @hide
766      */
ensureDirectlyConnectedRoutes()767     public void ensureDirectlyConnectedRoutes() {
768         for (LinkAddress addr : mLinkAddresses) {
769             addRoute(new RouteInfo(addr, null, mIfaceName));
770         }
771     }
772 
773     /**
774      * Returns all the routes on this link and all the links stacked above it.
775      * @hide
776      */
777     @SystemApi
getAllRoutes()778     public @NonNull List<RouteInfo> getAllRoutes() {
779         List<RouteInfo> routes = new ArrayList<>(mRoutes);
780         for (LinkProperties stacked: mStackedLinks.values()) {
781             routes.addAll(stacked.getAllRoutes());
782         }
783         return routes;
784     }
785 
786     /**
787      * Sets the recommended {@link ProxyInfo} to use on this link, or {@code null} for none.
788      * Note that Http Proxies are only a hint - the system recommends their use, but it does
789      * not enforce it and applications may ignore them.
790      *
791      * @param proxy A {@link ProxyInfo} defining the HTTP Proxy to use on this link.
792      */
setHttpProxy(@ullable ProxyInfo proxy)793     public void setHttpProxy(@Nullable ProxyInfo proxy) {
794         mHttpProxy = proxy;
795     }
796 
797     /**
798      * Gets the recommended {@link ProxyInfo} (or {@code null}) set on this link.
799      *
800      * @return The {@link ProxyInfo} set on this link or {@code null}.
801      */
getHttpProxy()802     public @Nullable ProxyInfo getHttpProxy() {
803         return mHttpProxy;
804     }
805 
806     /**
807      * Returns the NAT64 prefix in use on this link, if any.
808      *
809      * @return the NAT64 prefix or {@code null}.
810      */
getNat64Prefix()811     public @Nullable IpPrefix getNat64Prefix() {
812         return mNat64Prefix;
813     }
814 
815     /**
816      * Sets the NAT64 prefix in use on this link.
817      *
818      * Currently, only 96-bit prefixes (i.e., where the 32-bit IPv4 address is at the end of the
819      * 128-bit IPv6 address) are supported or {@code null} for no prefix.
820      *
821      * @param prefix the NAT64 prefix.
822      */
setNat64Prefix(@ullable IpPrefix prefix)823     public void setNat64Prefix(@Nullable IpPrefix prefix) {
824         if (prefix != null && prefix.getPrefixLength() != 96) {
825             throw new IllegalArgumentException("Only 96-bit prefixes are supported: " + prefix);
826         }
827         mNat64Prefix = prefix;  // IpPrefix objects are immutable.
828     }
829 
830     /**
831      * Adds a stacked link.
832      *
833      * If there is already a stacked link with the same interface name as link,
834      * that link is replaced with link. Otherwise, link is added to the list
835      * of stacked links.
836      *
837      * @param link The link to add.
838      * @return true if the link was stacked, false otherwise.
839      * @hide
840      */
841     @UnsupportedAppUsage
addStackedLink(@onNull LinkProperties link)842     public boolean addStackedLink(@NonNull LinkProperties link) {
843         if (link.getInterfaceName() != null) {
844             mStackedLinks.put(link.getInterfaceName(), link);
845             return true;
846         }
847         return false;
848     }
849 
850     /**
851      * Removes a stacked link.
852      *
853      * If there is a stacked link with the given interface name, it is
854      * removed. Otherwise, nothing changes.
855      *
856      * @param iface The interface name of the link to remove.
857      * @return true if the link was removed, false otherwise.
858      * @hide
859      */
removeStackedLink(@onNull String iface)860     public boolean removeStackedLink(@NonNull String iface) {
861         LinkProperties removed = mStackedLinks.remove(iface);
862         return removed != null;
863     }
864 
865     /**
866      * Returns all the links stacked on top of this link.
867      * @hide
868      */
869     @UnsupportedAppUsage
getStackedLinks()870     public @NonNull List<LinkProperties> getStackedLinks() {
871         if (mStackedLinks.isEmpty()) {
872             return Collections.emptyList();
873         }
874         final List<LinkProperties> stacked = new ArrayList<>();
875         for (LinkProperties link : mStackedLinks.values()) {
876             stacked.add(new LinkProperties(link));
877         }
878         return Collections.unmodifiableList(stacked);
879     }
880 
881     /**
882      * Clears this object to its initial state.
883      */
clear()884     public void clear() {
885         if (mParcelSensitiveFields) {
886             throw new UnsupportedOperationException(
887                     "Cannot clear LinkProperties when parcelSensitiveFields is set");
888         }
889 
890         mIfaceName = null;
891         mLinkAddresses.clear();
892         mDnses.clear();
893         mUsePrivateDns = false;
894         mPrivateDnsServerName = null;
895         mPcscfs.clear();
896         mDomains = null;
897         mRoutes.clear();
898         mHttpProxy = null;
899         mStackedLinks.clear();
900         mMtu = 0;
901         mDhcpServerAddress = null;
902         mTcpBufferSizes = null;
903         mNat64Prefix = null;
904         mWakeOnLanSupported = false;
905         mCaptivePortalApiUrl = null;
906         mCaptivePortalData = null;
907     }
908 
909     /**
910      * Implement the Parcelable interface
911      */
describeContents()912     public int describeContents() {
913         return 0;
914     }
915 
916     @Override
toString()917     public String toString() {
918         // Space as a separator, so no need for spaces at start/end of the individual fragments.
919         final StringJoiner resultJoiner = new StringJoiner(" ", "{", "}");
920 
921         if (mIfaceName != null) {
922             resultJoiner.add("InterfaceName:");
923             resultJoiner.add(mIfaceName);
924         }
925 
926         resultJoiner.add("LinkAddresses: [");
927         if (!mLinkAddresses.isEmpty()) {
928             resultJoiner.add(TextUtils.join(",", mLinkAddresses));
929         }
930         resultJoiner.add("]");
931 
932         resultJoiner.add("DnsAddresses: [");
933         if (!mDnses.isEmpty()) {
934             resultJoiner.add(TextUtils.join(",", mDnses));
935         }
936         resultJoiner.add("]");
937 
938         if (mUsePrivateDns) {
939             resultJoiner.add("UsePrivateDns: true");
940         }
941 
942         if (mPrivateDnsServerName != null) {
943             resultJoiner.add("PrivateDnsServerName:");
944             resultJoiner.add(mPrivateDnsServerName);
945         }
946 
947         if (!mPcscfs.isEmpty()) {
948             resultJoiner.add("PcscfAddresses: [");
949             resultJoiner.add(TextUtils.join(",", mPcscfs));
950             resultJoiner.add("]");
951         }
952 
953         if (!mValidatedPrivateDnses.isEmpty()) {
954             final StringJoiner validatedPrivateDnsesJoiner =
955                     new StringJoiner(",", "ValidatedPrivateDnsAddresses: [", "]");
956             for (final InetAddress addr : mValidatedPrivateDnses) {
957                 validatedPrivateDnsesJoiner.add(addr.getHostAddress());
958             }
959             resultJoiner.add(validatedPrivateDnsesJoiner.toString());
960         }
961 
962         resultJoiner.add("Domains:");
963         resultJoiner.add(mDomains);
964 
965         resultJoiner.add("MTU:");
966         resultJoiner.add(Integer.toString(mMtu));
967 
968         if (mWakeOnLanSupported) {
969             resultJoiner.add("WakeOnLanSupported: true");
970         }
971 
972         if (mDhcpServerAddress != null) {
973             resultJoiner.add("ServerAddress:");
974             resultJoiner.add(mDhcpServerAddress.toString());
975         }
976 
977         if (mCaptivePortalApiUrl != null) {
978             resultJoiner.add("CaptivePortalApiUrl: " + mCaptivePortalApiUrl);
979         }
980 
981         if (mCaptivePortalData != null) {
982             resultJoiner.add("CaptivePortalData: " + mCaptivePortalData);
983         }
984 
985         if (mTcpBufferSizes != null) {
986             resultJoiner.add("TcpBufferSizes:");
987             resultJoiner.add(mTcpBufferSizes);
988         }
989 
990         resultJoiner.add("Routes: [");
991         if (!mRoutes.isEmpty()) {
992             resultJoiner.add(TextUtils.join(",", mRoutes));
993         }
994         resultJoiner.add("]");
995 
996         if (mHttpProxy != null) {
997             resultJoiner.add("HttpProxy:");
998             resultJoiner.add(mHttpProxy.toString());
999         }
1000 
1001         if (mNat64Prefix != null) {
1002             resultJoiner.add("Nat64Prefix:");
1003             resultJoiner.add(mNat64Prefix.toString());
1004         }
1005 
1006         final Collection<LinkProperties> stackedLinksValues = mStackedLinks.values();
1007         if (!stackedLinksValues.isEmpty()) {
1008             final StringJoiner stackedLinksJoiner = new StringJoiner(",", "Stacked: [", "]");
1009             for (final LinkProperties lp : stackedLinksValues) {
1010                 stackedLinksJoiner.add("[ " + lp + " ]");
1011             }
1012             resultJoiner.add(stackedLinksJoiner.toString());
1013         }
1014 
1015         return resultJoiner.toString();
1016     }
1017 
1018     /**
1019      * Returns true if this link has an IPv4 address.
1020      *
1021      * @return {@code true} if there is an IPv4 address, {@code false} otherwise.
1022      * @hide
1023      */
1024     @TestApi
1025     @SystemApi
hasIpv4Address()1026     public boolean hasIpv4Address() {
1027         for (LinkAddress address : mLinkAddresses) {
1028             if (address.getAddress() instanceof Inet4Address) {
1029                 return true;
1030             }
1031         }
1032         return false;
1033     }
1034 
1035     /**
1036      * For backward compatibility.
1037      * This was annotated with @UnsupportedAppUsage in P, so we can't remove the method completely
1038      * just yet.
1039      * @return {@code true} if there is an IPv4 address, {@code false} otherwise.
1040      * @hide
1041      */
1042     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
hasIPv4Address()1043     public boolean hasIPv4Address() {
1044         return hasIpv4Address();
1045     }
1046 
1047     /**
1048      * Returns true if this link or any of its stacked interfaces has an IPv4 address.
1049      *
1050      * @return {@code true} if there is an IPv4 address, {@code false} otherwise.
1051      */
hasIpv4AddressOnInterface(String iface)1052     private boolean hasIpv4AddressOnInterface(String iface) {
1053         // mIfaceName can be null.
1054         return (Objects.equals(iface, mIfaceName) && hasIpv4Address())
1055                 || (iface != null && mStackedLinks.containsKey(iface)
1056                         && mStackedLinks.get(iface).hasIpv4Address());
1057     }
1058 
1059     /**
1060      * Returns true if this link has a global preferred IPv6 address.
1061      *
1062      * @return {@code true} if there is a global preferred IPv6 address, {@code false} otherwise.
1063      * @hide
1064      */
1065     @TestApi
1066     @SystemApi
hasGlobalIpv6Address()1067     public boolean hasGlobalIpv6Address() {
1068         for (LinkAddress address : mLinkAddresses) {
1069           if (address.getAddress() instanceof Inet6Address && address.isGlobalPreferred()) {
1070             return true;
1071           }
1072         }
1073         return false;
1074     }
1075 
1076     /**
1077      * Returns true if this link has an IPv4 unreachable default route.
1078      *
1079      * @return {@code true} if there is an IPv4 unreachable default route, {@code false} otherwise.
1080      * @hide
1081      */
hasIpv4UnreachableDefaultRoute()1082     public boolean hasIpv4UnreachableDefaultRoute() {
1083         for (RouteInfo r : mRoutes) {
1084             if (r.isIPv4UnreachableDefault()) {
1085                 return true;
1086             }
1087         }
1088         return false;
1089     }
1090 
1091     /**
1092      * For backward compatibility.
1093      * This was annotated with @UnsupportedAppUsage in P, so we can't remove the method completely
1094      * just yet.
1095      * @return {@code true} if there is a global preferred IPv6 address, {@code false} otherwise.
1096      * @hide
1097      */
1098     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
hasGlobalIPv6Address()1099     public boolean hasGlobalIPv6Address() {
1100         return hasGlobalIpv6Address();
1101     }
1102 
1103     /**
1104      * Returns true if this link has an IPv4 default route.
1105      *
1106      * @return {@code true} if there is an IPv4 default route, {@code false} otherwise.
1107      * @hide
1108      */
1109     @SystemApi
hasIpv4DefaultRoute()1110     public boolean hasIpv4DefaultRoute() {
1111         for (RouteInfo r : mRoutes) {
1112             if (r.isIPv4Default()) {
1113                 return true;
1114             }
1115         }
1116         return false;
1117     }
1118 
1119     /**
1120      * Returns true if this link has an IPv6 unreachable default route.
1121      *
1122      * @return {@code true} if there is an IPv6 unreachable default route, {@code false} otherwise.
1123      * @hide
1124      */
hasIpv6UnreachableDefaultRoute()1125     public boolean hasIpv6UnreachableDefaultRoute() {
1126         for (RouteInfo r : mRoutes) {
1127             if (r.isIPv6UnreachableDefault()) {
1128                 return true;
1129             }
1130         }
1131         return false;
1132     }
1133 
1134     /**
1135      * For backward compatibility.
1136      * This was annotated with @UnsupportedAppUsage in P, so we can't remove the method completely
1137      * just yet.
1138      * @return {@code true} if there is an IPv4 default route, {@code false} otherwise.
1139      * @hide
1140      */
1141     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
hasIPv4DefaultRoute()1142     public boolean hasIPv4DefaultRoute() {
1143         return hasIpv4DefaultRoute();
1144     }
1145 
1146     /**
1147      * Returns true if this link has an IPv6 default route.
1148      *
1149      * @return {@code true} if there is an IPv6 default route, {@code false} otherwise.
1150      * @hide
1151      */
1152     @TestApi
1153     @SystemApi
hasIpv6DefaultRoute()1154     public boolean hasIpv6DefaultRoute() {
1155         for (RouteInfo r : mRoutes) {
1156             if (r.isIPv6Default()) {
1157                 return true;
1158             }
1159         }
1160         return false;
1161     }
1162 
1163     /**
1164      * For backward compatibility.
1165      * This was annotated with @UnsupportedAppUsage in P, so we can't remove the method completely
1166      * just yet.
1167      * @return {@code true} if there is an IPv6 default route, {@code false} otherwise.
1168      * @hide
1169      */
1170     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
hasIPv6DefaultRoute()1171     public boolean hasIPv6DefaultRoute() {
1172         return hasIpv6DefaultRoute();
1173     }
1174 
1175     /**
1176      * Returns true if this link has an IPv4 DNS server.
1177      *
1178      * @return {@code true} if there is an IPv4 DNS server, {@code false} otherwise.
1179      * @hide
1180      */
1181     @SystemApi
hasIpv4DnsServer()1182     public boolean hasIpv4DnsServer() {
1183         for (InetAddress ia : mDnses) {
1184             if (ia instanceof Inet4Address) {
1185                 return true;
1186             }
1187         }
1188         return false;
1189     }
1190 
1191     /**
1192      * For backward compatibility.
1193      * This was annotated with @UnsupportedAppUsage in P, so we can't remove the method completely
1194      * just yet.
1195      * @return {@code true} if there is an IPv4 DNS server, {@code false} otherwise.
1196      * @hide
1197      */
1198     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
hasIPv4DnsServer()1199     public boolean hasIPv4DnsServer() {
1200         return hasIpv4DnsServer();
1201     }
1202 
1203     /**
1204      * Returns true if this link has an IPv6 DNS server.
1205      *
1206      * @return {@code true} if there is an IPv6 DNS server, {@code false} otherwise.
1207      * @hide
1208      */
1209     @SystemApi
hasIpv6DnsServer()1210     public boolean hasIpv6DnsServer() {
1211         for (InetAddress ia : mDnses) {
1212             if (ia instanceof Inet6Address) {
1213                 return true;
1214             }
1215         }
1216         return false;
1217     }
1218 
1219     /**
1220      * For backward compatibility.
1221      * This was annotated with @UnsupportedAppUsage in P, so we can't remove the method completely
1222      * just yet.
1223      * @return {@code true} if there is an IPv6 DNS server, {@code false} otherwise.
1224      * @hide
1225      */
1226     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
hasIPv6DnsServer()1227     public boolean hasIPv6DnsServer() {
1228         return hasIpv6DnsServer();
1229     }
1230 
1231     /**
1232      * Returns true if this link has an IPv4 PCSCF server.
1233      *
1234      * @return {@code true} if there is an IPv4 PCSCF server, {@code false} otherwise.
1235      * @hide
1236      */
hasIpv4PcscfServer()1237     public boolean hasIpv4PcscfServer() {
1238         for (InetAddress ia : mPcscfs) {
1239           if (ia instanceof Inet4Address) {
1240             return true;
1241           }
1242         }
1243         return false;
1244     }
1245 
1246     /**
1247      * Returns true if this link has an IPv6 PCSCF server.
1248      *
1249      * @return {@code true} if there is an IPv6 PCSCF server, {@code false} otherwise.
1250      * @hide
1251      */
hasIpv6PcscfServer()1252     public boolean hasIpv6PcscfServer() {
1253         for (InetAddress ia : mPcscfs) {
1254           if (ia instanceof Inet6Address) {
1255             return true;
1256           }
1257         }
1258         return false;
1259     }
1260 
1261     /**
1262      * Returns true if this link is provisioned for global IPv4 connectivity.
1263      * This requires an IP address, default route, and DNS server.
1264      *
1265      * @return {@code true} if the link is provisioned, {@code false} otherwise.
1266      * @hide
1267      */
1268     @TestApi
1269     @SystemApi
isIpv4Provisioned()1270     public boolean isIpv4Provisioned() {
1271         return (hasIpv4Address()
1272                 && hasIpv4DefaultRoute()
1273                 && hasIpv4DnsServer());
1274     }
1275 
1276     /**
1277      * Returns true if this link is provisioned for global IPv6 connectivity.
1278      * This requires an IP address, default route, and DNS server.
1279      *
1280      * @return {@code true} if the link is provisioned, {@code false} otherwise.
1281      * @hide
1282      */
1283     @TestApi
1284     @SystemApi
isIpv6Provisioned()1285     public boolean isIpv6Provisioned() {
1286         return (hasGlobalIpv6Address()
1287                 && hasIpv6DefaultRoute()
1288                 && hasIpv6DnsServer());
1289     }
1290 
1291     /**
1292      * For backward compatibility.
1293      * This was annotated with @UnsupportedAppUsage in P, so we can't remove the method completely
1294      * just yet.
1295      * @return {@code true} if the link is provisioned, {@code false} otherwise.
1296      * @hide
1297      */
1298     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
isIPv6Provisioned()1299     public boolean isIPv6Provisioned() {
1300         return isIpv6Provisioned();
1301     }
1302 
1303 
1304     /**
1305      * Returns true if this link is provisioned for global connectivity,
1306      * for at least one Internet Protocol family.
1307      *
1308      * @return {@code true} if the link is provisioned, {@code false} otherwise.
1309      * @hide
1310      */
1311     @TestApi
1312     @SystemApi
isProvisioned()1313     public boolean isProvisioned() {
1314         return (isIpv4Provisioned() || isIpv6Provisioned());
1315     }
1316 
1317     /**
1318      * Evaluate whether the {@link InetAddress} is considered reachable.
1319      *
1320      * @return {@code true} if the given {@link InetAddress} is considered reachable,
1321      *         {@code false} otherwise.
1322      * @hide
1323      */
1324     @TestApi
1325     @SystemApi
isReachable(@onNull InetAddress ip)1326     public boolean isReachable(@NonNull InetAddress ip) {
1327         final List<RouteInfo> allRoutes = getAllRoutes();
1328         // If we don't have a route to this IP address, it's not reachable.
1329         final RouteInfo bestRoute = RouteInfo.selectBestRoute(allRoutes, ip);
1330         if (bestRoute == null) {
1331             return false;
1332         }
1333 
1334         // TODO: better source address evaluation for destination addresses.
1335 
1336         if (ip instanceof Inet4Address) {
1337             // For IPv4, it suffices for now to simply have any address.
1338             return hasIpv4AddressOnInterface(bestRoute.getInterface());
1339         } else if (ip instanceof Inet6Address) {
1340             if (ip.isLinkLocalAddress()) {
1341                 // For now, just make sure link-local destinations have
1342                 // scopedIds set, since transmits will generally fail otherwise.
1343                 // TODO: verify it matches the ifindex of one of the interfaces.
1344                 return (((Inet6Address)ip).getScopeId() != 0);
1345             }  else {
1346                 // For non-link-local destinations check that either the best route
1347                 // is directly connected or that some global preferred address exists.
1348                 // TODO: reconsider all cases (disconnected ULA networks, ...).
1349                 return (!bestRoute.hasGateway() || hasGlobalIpv6Address());
1350             }
1351         }
1352 
1353         return false;
1354     }
1355 
1356     /**
1357      * Compares this {@code LinkProperties} interface name against the target
1358      *
1359      * @param target LinkProperties to compare.
1360      * @return {@code true} if both are identical, {@code false} otherwise.
1361      * @hide
1362      */
1363     @UnsupportedAppUsage
isIdenticalInterfaceName(@onNull LinkProperties target)1364     public boolean isIdenticalInterfaceName(@NonNull LinkProperties target) {
1365         return LinkPropertiesUtils.isIdenticalInterfaceName(target, this);
1366     }
1367 
1368     /**
1369      * Compares this {@code LinkProperties} DHCP server address against the target
1370      *
1371      * @param target LinkProperties to compare.
1372      * @return {@code true} if both are identical, {@code false} otherwise.
1373      * @hide
1374      */
isIdenticalDhcpServerAddress(@onNull LinkProperties target)1375     public boolean isIdenticalDhcpServerAddress(@NonNull LinkProperties target) {
1376         return Objects.equals(mDhcpServerAddress, target.mDhcpServerAddress);
1377     }
1378 
1379     /**
1380      * Compares this {@code LinkProperties} interface addresses against the target
1381      *
1382      * @param target LinkProperties to compare.
1383      * @return {@code true} if both are identical, {@code false} otherwise.
1384      * @hide
1385      */
1386     @UnsupportedAppUsage
isIdenticalAddresses(@onNull LinkProperties target)1387     public boolean isIdenticalAddresses(@NonNull LinkProperties target) {
1388         return LinkPropertiesUtils.isIdenticalAddresses(target, this);
1389     }
1390 
1391     /**
1392      * Compares this {@code LinkProperties} DNS addresses against the target
1393      *
1394      * @param target LinkProperties to compare.
1395      * @return {@code true} if both are identical, {@code false} otherwise.
1396      * @hide
1397      */
1398     @UnsupportedAppUsage
isIdenticalDnses(@onNull LinkProperties target)1399     public boolean isIdenticalDnses(@NonNull LinkProperties target) {
1400         return LinkPropertiesUtils.isIdenticalDnses(target, this);
1401     }
1402 
1403     /**
1404      * Compares this {@code LinkProperties} private DNS settings against the
1405      * target.
1406      *
1407      * @param target LinkProperties to compare.
1408      * @return {@code true} if both are identical, {@code false} otherwise.
1409      * @hide
1410      */
isIdenticalPrivateDns(@onNull LinkProperties target)1411     public boolean isIdenticalPrivateDns(@NonNull LinkProperties target) {
1412         return (isPrivateDnsActive() == target.isPrivateDnsActive()
1413                 && TextUtils.equals(getPrivateDnsServerName(),
1414                 target.getPrivateDnsServerName()));
1415     }
1416 
1417     /**
1418      * Compares this {@code LinkProperties} validated private DNS addresses against
1419      * the target
1420      *
1421      * @param target LinkProperties to compare.
1422      * @return {@code true} if both are identical, {@code false} otherwise.
1423      * @hide
1424      */
isIdenticalValidatedPrivateDnses(@onNull LinkProperties target)1425     public boolean isIdenticalValidatedPrivateDnses(@NonNull LinkProperties target) {
1426         Collection<InetAddress> targetDnses = target.getValidatedPrivateDnsServers();
1427         return (mValidatedPrivateDnses.size() == targetDnses.size())
1428                 ? mValidatedPrivateDnses.containsAll(targetDnses) : false;
1429     }
1430 
1431     /**
1432      * Compares this {@code LinkProperties} PCSCF addresses against the target
1433      *
1434      * @param target LinkProperties to compare.
1435      * @return {@code true} if both are identical, {@code false} otherwise.
1436      * @hide
1437      */
isIdenticalPcscfs(@onNull LinkProperties target)1438     public boolean isIdenticalPcscfs(@NonNull LinkProperties target) {
1439         Collection<InetAddress> targetPcscfs = target.getPcscfServers();
1440         return (mPcscfs.size() == targetPcscfs.size()) ?
1441                     mPcscfs.containsAll(targetPcscfs) : false;
1442     }
1443 
1444     /**
1445      * Compares this {@code LinkProperties} Routes against the target
1446      *
1447      * @param target LinkProperties to compare.
1448      * @return {@code true} if both are identical, {@code false} otherwise.
1449      * @hide
1450      */
1451     @UnsupportedAppUsage
isIdenticalRoutes(@onNull LinkProperties target)1452     public boolean isIdenticalRoutes(@NonNull LinkProperties target) {
1453         return LinkPropertiesUtils.isIdenticalRoutes(target, this);
1454     }
1455 
1456     /**
1457      * Compares this {@code LinkProperties} HttpProxy against the target
1458      *
1459      * @param target LinkProperties to compare.
1460      * @return {@code true} if both are identical, {@code false} otherwise.
1461      * @hide
1462      */
1463     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
isIdenticalHttpProxy(@onNull LinkProperties target)1464     public boolean isIdenticalHttpProxy(@NonNull LinkProperties target) {
1465         return LinkPropertiesUtils.isIdenticalHttpProxy(target, this);
1466     }
1467 
1468     /**
1469      * Compares this {@code LinkProperties} stacked links against the target
1470      *
1471      * @param target LinkProperties to compare.
1472      * @return {@code true} if both are identical, {@code false} otherwise.
1473      * @hide
1474      */
1475     @UnsupportedAppUsage
isIdenticalStackedLinks(@onNull LinkProperties target)1476     public boolean isIdenticalStackedLinks(@NonNull LinkProperties target) {
1477         if (!mStackedLinks.keySet().equals(target.mStackedLinks.keySet())) {
1478             return false;
1479         }
1480         for (LinkProperties stacked : mStackedLinks.values()) {
1481             // Hashtable values can never be null.
1482             String iface = stacked.getInterfaceName();
1483             if (!stacked.equals(target.mStackedLinks.get(iface))) {
1484                 return false;
1485             }
1486         }
1487         return true;
1488     }
1489 
1490     /**
1491      * Compares this {@code LinkProperties} MTU against the target
1492      *
1493      * @param target LinkProperties to compare.
1494      * @return {@code true} if both are identical, {@code false} otherwise.
1495      * @hide
1496      */
isIdenticalMtu(@onNull LinkProperties target)1497     public boolean isIdenticalMtu(@NonNull LinkProperties target) {
1498         return getMtu() == target.getMtu();
1499     }
1500 
1501     /**
1502      * Compares this {@code LinkProperties} Tcp buffer sizes against the target.
1503      *
1504      * @param target LinkProperties to compare.
1505      * @return {@code true} if both are identical, {@code false} otherwise.
1506      * @hide
1507      */
isIdenticalTcpBufferSizes(@onNull LinkProperties target)1508     public boolean isIdenticalTcpBufferSizes(@NonNull LinkProperties target) {
1509         return Objects.equals(mTcpBufferSizes, target.mTcpBufferSizes);
1510     }
1511 
1512     /**
1513      * Compares this {@code LinkProperties} NAT64 prefix against the target.
1514      *
1515      * @param target LinkProperties to compare.
1516      * @return {@code true} if both are identical, {@code false} otherwise.
1517      * @hide
1518      */
isIdenticalNat64Prefix(@onNull LinkProperties target)1519     public boolean isIdenticalNat64Prefix(@NonNull LinkProperties target) {
1520         return Objects.equals(mNat64Prefix, target.mNat64Prefix);
1521     }
1522 
1523     /**
1524      * Compares this {@code LinkProperties} WakeOnLan supported against the target.
1525      *
1526      * @param target LinkProperties to compare.
1527      * @return {@code true} if both are identical, {@code false} otherwise.
1528      * @hide
1529      */
isIdenticalWakeOnLan(LinkProperties target)1530     public boolean isIdenticalWakeOnLan(LinkProperties target) {
1531         return isWakeOnLanSupported() == target.isWakeOnLanSupported();
1532     }
1533 
1534     /**
1535      * Compares this {@code LinkProperties}'s CaptivePortalApiUrl against the target.
1536      *
1537      * @param target LinkProperties to compare.
1538      * @return {@code true} if both are identical, {@code false} otherwise.
1539      * @hide
1540      */
isIdenticalCaptivePortalApiUrl(LinkProperties target)1541     public boolean isIdenticalCaptivePortalApiUrl(LinkProperties target) {
1542         return Objects.equals(mCaptivePortalApiUrl, target.mCaptivePortalApiUrl);
1543     }
1544 
1545     /**
1546      * Compares this {@code LinkProperties}'s CaptivePortalData against the target.
1547      *
1548      * @param target LinkProperties to compare.
1549      * @return {@code true} if both are identical, {@code false} otherwise.
1550      * @hide
1551      */
isIdenticalCaptivePortalData(LinkProperties target)1552     public boolean isIdenticalCaptivePortalData(LinkProperties target) {
1553         return Objects.equals(mCaptivePortalData, target.mCaptivePortalData);
1554     }
1555 
1556     /**
1557      * Set whether the network interface supports WakeOnLAN
1558      *
1559      * @param supported WakeOnLAN supported value
1560      *
1561      * @hide
1562      */
setWakeOnLanSupported(boolean supported)1563     public void setWakeOnLanSupported(boolean supported) {
1564         mWakeOnLanSupported = supported;
1565     }
1566 
1567     /**
1568      * Returns whether the network interface supports WakeOnLAN
1569      *
1570      * @return {@code true} if interface supports WakeOnLAN, {@code false} otherwise.
1571      */
isWakeOnLanSupported()1572     public boolean isWakeOnLanSupported() {
1573         return mWakeOnLanSupported;
1574     }
1575 
1576     /**
1577      * Set the URL of the captive portal API endpoint to get more information about the network.
1578      * @hide
1579      */
1580     @SystemApi
1581     @TestApi
setCaptivePortalApiUrl(@ullable Uri url)1582     public void setCaptivePortalApiUrl(@Nullable Uri url) {
1583         mCaptivePortalApiUrl = url;
1584     }
1585 
1586     /**
1587      * Get the URL of the captive portal API endpoint to get more information about the network.
1588      *
1589      * <p>This is null unless the application has
1590      * {@link android.Manifest.permission.NETWORK_SETTINGS} or
1591      * {@link NetworkStack#PERMISSION_MAINLINE_NETWORK_STACK} permissions, and the network provided
1592      * the URL.
1593      * @hide
1594      */
1595     @SystemApi
1596     @TestApi
1597     @Nullable
getCaptivePortalApiUrl()1598     public Uri getCaptivePortalApiUrl() {
1599         return mCaptivePortalApiUrl;
1600     }
1601 
1602     /**
1603      * Set the CaptivePortalData obtained from the captive portal API (RFC7710bis).
1604      * @hide
1605      */
1606     @SystemApi
1607     @TestApi
setCaptivePortalData(@ullable CaptivePortalData data)1608     public void setCaptivePortalData(@Nullable CaptivePortalData data) {
1609         mCaptivePortalData = data;
1610     }
1611 
1612     /**
1613      * Get the CaptivePortalData obtained from the captive portal API (RFC7710bis).
1614      *
1615      * <p>This is null unless the application has
1616      * {@link android.Manifest.permission.NETWORK_SETTINGS} or
1617      * {@link NetworkStack#PERMISSION_MAINLINE_NETWORK_STACK} permissions.
1618      * @hide
1619      */
1620     @SystemApi
1621     @TestApi
1622     @Nullable
getCaptivePortalData()1623     public CaptivePortalData getCaptivePortalData() {
1624         return mCaptivePortalData;
1625     }
1626 
1627     /**
1628      * Compares this {@code LinkProperties} instance against the target
1629      * LinkProperties in {@code obj}. Two LinkPropertieses are equal if
1630      * all their fields are equal in values.
1631      *
1632      * For collection fields, such as mDnses, containsAll() is used to check
1633      * if two collections contains the same elements, independent of order.
1634      * There are two thoughts regarding containsAll()
1635      * 1. Duplicated elements. eg, (A, B, B) and (A, A, B) are equal.
1636      * 2. Worst case performance is O(n^2).
1637      *
1638      * @param obj the object to be tested for equality.
1639      * @return {@code true} if both objects are equal, {@code false} otherwise.
1640      */
1641     @Override
equals(Object obj)1642     public boolean equals(Object obj) {
1643         if (this == obj) return true;
1644 
1645         if (!(obj instanceof LinkProperties)) return false;
1646 
1647         LinkProperties target = (LinkProperties) obj;
1648         /*
1649          * This method does not check that stacked interfaces are equal, because
1650          * stacked interfaces are not so much a property of the link as a
1651          * description of connections between links.
1652          */
1653         return isIdenticalInterfaceName(target)
1654                 && isIdenticalAddresses(target)
1655                 && isIdenticalDhcpServerAddress(target)
1656                 && isIdenticalDnses(target)
1657                 && isIdenticalPrivateDns(target)
1658                 && isIdenticalValidatedPrivateDnses(target)
1659                 && isIdenticalPcscfs(target)
1660                 && isIdenticalRoutes(target)
1661                 && isIdenticalHttpProxy(target)
1662                 && isIdenticalStackedLinks(target)
1663                 && isIdenticalMtu(target)
1664                 && isIdenticalTcpBufferSizes(target)
1665                 && isIdenticalNat64Prefix(target)
1666                 && isIdenticalWakeOnLan(target)
1667                 && isIdenticalCaptivePortalApiUrl(target)
1668                 && isIdenticalCaptivePortalData(target);
1669     }
1670 
1671     /**
1672      * Compares the DNS addresses in this LinkProperties with another
1673      * LinkProperties, examining only DNS addresses on the base link.
1674      *
1675      * @param target a LinkProperties with the new list of dns addresses
1676      * @return the differences between the DNS addresses.
1677      * @hide
1678      */
compareDnses(@ullable LinkProperties target)1679     public @NonNull CompareResult<InetAddress> compareDnses(@Nullable LinkProperties target) {
1680         /*
1681          * Duplicate the InetAddresses into removed, we will be removing
1682          * dns address which are common between mDnses and target
1683          * leaving the addresses that are different. And dns address which
1684          * are in target but not in mDnses are placed in the
1685          * addedAddresses.
1686          */
1687         return new CompareResult<>(mDnses, target != null ? target.getDnsServers() : null);
1688     }
1689 
1690     /**
1691      * Compares the validated private DNS addresses in this LinkProperties with another
1692      * LinkProperties.
1693      *
1694      * @param target a LinkProperties with the new list of validated private dns addresses
1695      * @return the differences between the DNS addresses.
1696      * @hide
1697      */
compareValidatedPrivateDnses( @ullable LinkProperties target)1698     public @NonNull CompareResult<InetAddress> compareValidatedPrivateDnses(
1699             @Nullable LinkProperties target) {
1700         return new CompareResult<>(mValidatedPrivateDnses,
1701                 target != null ? target.getValidatedPrivateDnsServers() : null);
1702     }
1703 
1704     /**
1705      * Compares all routes in this LinkProperties with another LinkProperties,
1706      * examining both the the base link and all stacked links.
1707      *
1708      * @param target a LinkProperties with the new list of routes
1709      * @return the differences between the routes.
1710      * @hide
1711      */
compareAllRoutes(@ullable LinkProperties target)1712     public @NonNull CompareResult<RouteInfo> compareAllRoutes(@Nullable LinkProperties target) {
1713         /*
1714          * Duplicate the RouteInfos into removed, we will be removing
1715          * routes which are common between mRoutes and target
1716          * leaving the routes that are different. And route address which
1717          * are in target but not in mRoutes are placed in added.
1718          */
1719         return new CompareResult<>(getAllRoutes(), target != null ? target.getAllRoutes() : null);
1720     }
1721 
1722     /**
1723      * Compares all interface names in this LinkProperties with another
1724      * LinkProperties, examining both the the base link and all stacked links.
1725      *
1726      * @param target a LinkProperties with the new list of interface names
1727      * @return the differences between the interface names.
1728      * @hide
1729      */
compareAllInterfaceNames( @ullable LinkProperties target)1730     public @NonNull CompareResult<String> compareAllInterfaceNames(
1731             @Nullable LinkProperties target) {
1732         /*
1733          * Duplicate the interface names into removed, we will be removing
1734          * interface names which are common between this and target
1735          * leaving the interface names that are different. And interface names which
1736          * are in target but not in this are placed in added.
1737          */
1738         return new CompareResult<>(getAllInterfaceNames(),
1739                 target != null ? target.getAllInterfaceNames() : null);
1740     }
1741 
1742 
1743     /**
1744      * Generate hashcode based on significant fields
1745      *
1746      * Equal objects must produce the same hash code, while unequal objects
1747      * may have the same hash codes.
1748      */
1749     @Override
hashCode()1750     public int hashCode() {
1751         return ((null == mIfaceName) ? 0 : mIfaceName.hashCode()
1752                 + mLinkAddresses.size() * 31
1753                 + mDnses.size() * 37
1754                 + mValidatedPrivateDnses.size() * 61
1755                 + ((null == mDomains) ? 0 : mDomains.hashCode())
1756                 + mRoutes.size() * 41
1757                 + ((null == mHttpProxy) ? 0 : mHttpProxy.hashCode())
1758                 + mStackedLinks.hashCode() * 47)
1759                 + mMtu * 51
1760                 + ((null == mTcpBufferSizes) ? 0 : mTcpBufferSizes.hashCode())
1761                 + (mUsePrivateDns ? 57 : 0)
1762                 + ((null == mDhcpServerAddress) ? 0 : mDhcpServerAddress.hashCode())
1763                 + mPcscfs.size() * 67
1764                 + ((null == mPrivateDnsServerName) ? 0 : mPrivateDnsServerName.hashCode())
1765                 + Objects.hash(mNat64Prefix)
1766                 + (mWakeOnLanSupported ? 71 : 0)
1767                 + Objects.hash(mCaptivePortalApiUrl, mCaptivePortalData);
1768     }
1769 
1770     /**
1771      * Implement the Parcelable interface.
1772      */
writeToParcel(Parcel dest, int flags)1773     public void writeToParcel(Parcel dest, int flags) {
1774         dest.writeString(getInterfaceName());
1775         dest.writeInt(mLinkAddresses.size());
1776         for (LinkAddress linkAddress : mLinkAddresses) {
1777             dest.writeParcelable(linkAddress, flags);
1778         }
1779 
1780         writeAddresses(dest, mDnses);
1781         writeAddresses(dest, mValidatedPrivateDnses);
1782         dest.writeBoolean(mUsePrivateDns);
1783         dest.writeString(mPrivateDnsServerName);
1784         writeAddresses(dest, mPcscfs);
1785         dest.writeString(mDomains);
1786         writeAddress(dest, mDhcpServerAddress);
1787         dest.writeInt(mMtu);
1788         dest.writeString(mTcpBufferSizes);
1789         dest.writeInt(mRoutes.size());
1790         for (RouteInfo route : mRoutes) {
1791             dest.writeParcelable(route, flags);
1792         }
1793 
1794         if (mHttpProxy != null) {
1795             dest.writeByte((byte)1);
1796             dest.writeParcelable(mHttpProxy, flags);
1797         } else {
1798             dest.writeByte((byte)0);
1799         }
1800         dest.writeParcelable(mNat64Prefix, 0);
1801 
1802         ArrayList<LinkProperties> stackedLinks = new ArrayList<>(mStackedLinks.values());
1803         dest.writeList(stackedLinks);
1804 
1805         dest.writeBoolean(mWakeOnLanSupported);
1806         dest.writeParcelable(mParcelSensitiveFields ? mCaptivePortalApiUrl : null, 0);
1807         dest.writeParcelable(mParcelSensitiveFields ? mCaptivePortalData : null, 0);
1808     }
1809 
writeAddresses(@onNull Parcel dest, @NonNull List<InetAddress> list)1810     private static void writeAddresses(@NonNull Parcel dest, @NonNull List<InetAddress> list) {
1811         dest.writeInt(list.size());
1812         for (InetAddress d : list) {
1813             writeAddress(dest, d);
1814         }
1815     }
1816 
writeAddress(@onNull Parcel dest, @Nullable InetAddress addr)1817     private static void writeAddress(@NonNull Parcel dest, @Nullable InetAddress addr) {
1818         byte[] addressBytes = (addr == null ? null : addr.getAddress());
1819         dest.writeByteArray(addressBytes);
1820         if (addr instanceof Inet6Address) {
1821             final Inet6Address v6Addr = (Inet6Address) addr;
1822             final boolean hasScopeId = v6Addr.getScopeId() != 0;
1823             dest.writeBoolean(hasScopeId);
1824             if (hasScopeId) dest.writeInt(v6Addr.getScopeId());
1825         }
1826     }
1827 
1828     @Nullable
readAddress(@onNull Parcel p)1829     private static InetAddress readAddress(@NonNull Parcel p) throws UnknownHostException {
1830         final byte[] addr = p.createByteArray();
1831         if (addr == null) return null;
1832 
1833         if (addr.length == INET6_ADDR_LENGTH) {
1834             final boolean hasScopeId = p.readBoolean();
1835             final int scopeId = hasScopeId ? p.readInt() : 0;
1836             return Inet6Address.getByAddress(null /* host */, addr, scopeId);
1837         }
1838 
1839         return InetAddress.getByAddress(addr);
1840     }
1841 
1842     /**
1843      * Implement the Parcelable interface.
1844      */
1845     public static final @android.annotation.NonNull Creator<LinkProperties> CREATOR =
1846         new Creator<LinkProperties>() {
1847             public LinkProperties createFromParcel(Parcel in) {
1848                 LinkProperties netProp = new LinkProperties();
1849 
1850                 String iface = in.readString();
1851                 if (iface != null) {
1852                     netProp.setInterfaceName(iface);
1853                 }
1854                 int addressCount = in.readInt();
1855                 for (int i = 0; i < addressCount; i++) {
1856                     netProp.addLinkAddress(in.readParcelable(null));
1857                 }
1858                 addressCount = in.readInt();
1859                 for (int i = 0; i < addressCount; i++) {
1860                     try {
1861                         netProp.addDnsServer(readAddress(in));
1862                     } catch (UnknownHostException e) { }
1863                 }
1864                 addressCount = in.readInt();
1865                 for (int i = 0; i < addressCount; i++) {
1866                     try {
1867                         netProp.addValidatedPrivateDnsServer(readAddress(in));
1868                     } catch (UnknownHostException e) { }
1869                 }
1870                 netProp.setUsePrivateDns(in.readBoolean());
1871                 netProp.setPrivateDnsServerName(in.readString());
1872                 addressCount = in.readInt();
1873                 for (int i = 0; i < addressCount; i++) {
1874                     try {
1875                         netProp.addPcscfServer(readAddress(in));
1876                     } catch (UnknownHostException e) { }
1877                 }
1878                 netProp.setDomains(in.readString());
1879                 try {
1880                     netProp.setDhcpServerAddress((Inet4Address) InetAddress
1881                             .getByAddress(in.createByteArray()));
1882                 } catch (UnknownHostException e) { }
1883                 netProp.setMtu(in.readInt());
1884                 netProp.setTcpBufferSizes(in.readString());
1885                 addressCount = in.readInt();
1886                 for (int i = 0; i < addressCount; i++) {
1887                     netProp.addRoute(in.readParcelable(null));
1888                 }
1889                 if (in.readByte() == 1) {
1890                     netProp.setHttpProxy(in.readParcelable(null));
1891                 }
1892                 netProp.setNat64Prefix(in.readParcelable(null));
1893                 ArrayList<LinkProperties> stackedLinks = new ArrayList<LinkProperties>();
1894                 in.readList(stackedLinks, LinkProperties.class.getClassLoader());
1895                 for (LinkProperties stackedLink: stackedLinks) {
1896                     netProp.addStackedLink(stackedLink);
1897                 }
1898                 netProp.setWakeOnLanSupported(in.readBoolean());
1899 
1900                 netProp.setCaptivePortalApiUrl(in.readParcelable(null));
1901                 netProp.setCaptivePortalData(in.readParcelable(null));
1902                 return netProp;
1903             }
1904 
1905             public LinkProperties[] newArray(int size) {
1906                 return new LinkProperties[size];
1907             }
1908         };
1909 
1910     /**
1911      * Check the valid MTU range based on IPv4 or IPv6.
1912      * @hide
1913      */
isValidMtu(int mtu, boolean ipv6)1914     public static boolean isValidMtu(int mtu, boolean ipv6) {
1915         if (ipv6) {
1916             return mtu >= MIN_MTU_V6 && mtu <= MAX_MTU;
1917         } else {
1918             return mtu >= MIN_MTU && mtu <= MAX_MTU;
1919         }
1920     }
1921 }
1922