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