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