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