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.net.ProxyInfo;
22 import android.os.Parcelable;
23 import android.os.Parcel;
24 import android.text.TextUtils;
25 
26 import java.net.InetAddress;
27 import java.net.Inet4Address;
28 import java.net.Inet6Address;
29 import java.net.UnknownHostException;
30 import java.util.ArrayList;
31 import java.util.Collection;
32 import java.util.Collections;
33 import java.util.Hashtable;
34 import java.util.List;
35 import java.util.Objects;
36 
37 /**
38  * Describes the properties of a network link.
39  *
40  * A link represents a connection to a network.
41  * It may have multiple addresses and multiple gateways,
42  * multiple dns servers but only one http proxy and one
43  * network interface.
44  *
45  * Note that this is just a holder of data.  Modifying it
46  * does not affect live networks.
47  *
48  */
49 public final class LinkProperties implements Parcelable {
50     // The interface described by the network link.
51     private String mIfaceName;
52     private ArrayList<LinkAddress> mLinkAddresses = new ArrayList<LinkAddress>();
53     private ArrayList<InetAddress> mDnses = new ArrayList<InetAddress>();
54     private String mDomains;
55     private ArrayList<RouteInfo> mRoutes = new ArrayList<RouteInfo>();
56     private ProxyInfo mHttpProxy;
57     private int mMtu;
58     // in the format "rmem_min,rmem_def,rmem_max,wmem_min,wmem_def,wmem_max"
59     private String mTcpBufferSizes;
60 
61     private static final int MIN_MTU    = 68;
62     private static final int MIN_MTU_V6 = 1280;
63     private static final int MAX_MTU    = 10000;
64 
65     // Stores the properties of links that are "stacked" above this link.
66     // Indexed by interface name to allow modification and to prevent duplicates being added.
67     private Hashtable<String, LinkProperties> mStackedLinks =
68         new Hashtable<String, LinkProperties>();
69 
70     /**
71      * @hide
72      */
73     public static class CompareResult<T> {
74         public List<T> removed = new ArrayList<T>();
75         public List<T> added = new ArrayList<T>();
76 
77         @Override
toString()78         public String toString() {
79             String retVal = "removed=[";
80             for (T addr : removed) retVal += addr.toString() + ",";
81             retVal += "] added=[";
82             for (T addr : added) retVal += addr.toString() + ",";
83             retVal += "]";
84             return retVal;
85         }
86     }
87 
88     /**
89      * @hide
90      */
91     public enum ProvisioningChange {
92         STILL_NOT_PROVISIONED,
93         LOST_PROVISIONING,
94         GAINED_PROVISIONING,
95         STILL_PROVISIONED,
96     }
97 
98     /**
99      * Compare the provisioning states of two LinkProperties instances.
100      *
101      * @hide
102      */
compareProvisioning( LinkProperties before, LinkProperties after)103     public static ProvisioningChange compareProvisioning(
104             LinkProperties before, LinkProperties after) {
105         if (before.isProvisioned() && after.isProvisioned()) {
106             // On dualstack networks, DHCPv4 renewals can occasionally fail.
107             // When this happens, IPv6-reachable services continue to function
108             // normally but IPv4-only services (naturally) fail.
109             //
110             // When an application using an IPv4-only service reports a bad
111             // network condition to the framework, attempts to re-validate
112             // the network succeed (since we support IPv6-only networks) and
113             // nothing is changed.
114             //
115             // For users, this is confusing and unexpected behaviour, and is
116             // not necessarily easy to diagnose.  Therefore, we treat changing
117             // from a dualstack network to an IPv6-only network equivalent to
118             // a total loss of provisioning.
119             //
120             // For one such example of this, see b/18867306.
121             //
122             // Additionally, losing IPv6 provisioning can result in TCP
123             // connections getting stuck until timeouts fire and other
124             // baffling failures. Therefore, loss of either IPv4 or IPv6 on a
125             // previously dualstack network is deemed a lost of provisioning.
126             if ((before.isIPv4Provisioned() && !after.isIPv4Provisioned()) ||
127                 (before.isIPv6Provisioned() && !after.isIPv6Provisioned())) {
128                 return ProvisioningChange.LOST_PROVISIONING;
129             }
130             return ProvisioningChange.STILL_PROVISIONED;
131         } else if (before.isProvisioned() && !after.isProvisioned()) {
132             return ProvisioningChange.LOST_PROVISIONING;
133         } else if (!before.isProvisioned() && after.isProvisioned()) {
134             return ProvisioningChange.GAINED_PROVISIONING;
135         } else {  // !before.isProvisioned() && !after.isProvisioned()
136             return ProvisioningChange.STILL_NOT_PROVISIONED;
137         }
138     }
139 
140     /**
141      * @hide
142      */
LinkProperties()143     public LinkProperties() {
144     }
145 
146     /**
147      * @hide
148      */
LinkProperties(LinkProperties source)149     public LinkProperties(LinkProperties source) {
150         if (source != null) {
151             mIfaceName = source.getInterfaceName();
152             for (LinkAddress l : source.getLinkAddresses()) mLinkAddresses.add(l);
153             for (InetAddress i : source.getDnsServers()) mDnses.add(i);
154             mDomains = source.getDomains();
155             for (RouteInfo r : source.getRoutes()) mRoutes.add(r);
156             mHttpProxy = (source.getHttpProxy() == null)  ?
157                     null : new ProxyInfo(source.getHttpProxy());
158             for (LinkProperties l: source.mStackedLinks.values()) {
159                 addStackedLink(l);
160             }
161             setMtu(source.getMtu());
162             mTcpBufferSizes = source.mTcpBufferSizes;
163         }
164     }
165 
166     /**
167      * Sets the interface name for this link.  All {@link RouteInfo} already set for this
168      * will have their interface changed to match this new value.
169      *
170      * @param iface The name of the network interface used for this link.
171      * @hide
172      */
setInterfaceName(String iface)173     public void setInterfaceName(String iface) {
174         mIfaceName = iface;
175         ArrayList<RouteInfo> newRoutes = new ArrayList<RouteInfo>(mRoutes.size());
176         for (RouteInfo route : mRoutes) {
177             newRoutes.add(routeWithInterface(route));
178         }
179         mRoutes = newRoutes;
180     }
181 
182     /**
183      * Gets the interface name for this link.  May be {@code null} if not set.
184      *
185      * @return The interface name set for this link or {@code null}.
186      */
getInterfaceName()187     public @Nullable String getInterfaceName() {
188         return mIfaceName;
189     }
190 
191     /**
192      * @hide
193      */
getAllInterfaceNames()194     public List<String> getAllInterfaceNames() {
195         List<String> interfaceNames = new ArrayList<String>(mStackedLinks.size() + 1);
196         if (mIfaceName != null) interfaceNames.add(new String(mIfaceName));
197         for (LinkProperties stacked: mStackedLinks.values()) {
198             interfaceNames.addAll(stacked.getAllInterfaceNames());
199         }
200         return interfaceNames;
201     }
202 
203     /**
204      * Returns all the addresses on this link.  We often think of a link having a single address,
205      * however, particularly with Ipv6 several addresses are typical.  Note that the
206      * {@code LinkProperties} actually contains {@link LinkAddress} objects which also include
207      * prefix lengths for each address.  This is a simplified utility alternative to
208      * {@link LinkProperties#getLinkAddresses}.
209      *
210      * @return An umodifiable {@link List} of {@link InetAddress} for this link.
211      * @hide
212      */
getAddresses()213     public List<InetAddress> getAddresses() {
214         List<InetAddress> addresses = new ArrayList<InetAddress>();
215         for (LinkAddress linkAddress : mLinkAddresses) {
216             addresses.add(linkAddress.getAddress());
217         }
218         return Collections.unmodifiableList(addresses);
219     }
220 
221     /**
222      * Returns all the addresses on this link and all the links stacked above it.
223      * @hide
224      */
getAllAddresses()225     public List<InetAddress> getAllAddresses() {
226         List<InetAddress> addresses = new ArrayList<InetAddress>();
227         for (LinkAddress linkAddress : mLinkAddresses) {
228             addresses.add(linkAddress.getAddress());
229         }
230         for (LinkProperties stacked: mStackedLinks.values()) {
231             addresses.addAll(stacked.getAllAddresses());
232         }
233         return addresses;
234     }
235 
findLinkAddressIndex(LinkAddress address)236     private int findLinkAddressIndex(LinkAddress address) {
237         for (int i = 0; i < mLinkAddresses.size(); i++) {
238             if (mLinkAddresses.get(i).isSameAddressAs(address)) {
239                 return i;
240             }
241         }
242         return -1;
243     }
244 
245     /**
246      * Adds a {@link LinkAddress} to this {@code LinkProperties} if a {@link LinkAddress} of the
247      * same address/prefix does not already exist.  If it does exist it is replaced.
248      * @param address The {@code LinkAddress} to add.
249      * @return true if {@code address} was added or updated, false otherwise.
250      * @hide
251      */
addLinkAddress(LinkAddress address)252     public boolean addLinkAddress(LinkAddress address) {
253         if (address == null) {
254             return false;
255         }
256         int i = findLinkAddressIndex(address);
257         if (i < 0) {
258             // Address was not present. Add it.
259             mLinkAddresses.add(address);
260             return true;
261         } else if (mLinkAddresses.get(i).equals(address)) {
262             // Address was present and has same properties. Do nothing.
263             return false;
264         } else {
265             // Address was present and has different properties. Update it.
266             mLinkAddresses.set(i, address);
267             return true;
268         }
269     }
270 
271     /**
272      * Removes a {@link LinkAddress} from this {@code LinkProperties}.  Specifically, matches
273      * and {@link LinkAddress} with the same address and prefix.
274      *
275      * @param toRemove A {@link LinkAddress} specifying the address to remove.
276      * @return true if the address was removed, false if it did not exist.
277      * @hide
278      */
removeLinkAddress(LinkAddress toRemove)279     public boolean removeLinkAddress(LinkAddress toRemove) {
280         int i = findLinkAddressIndex(toRemove);
281         if (i >= 0) {
282             mLinkAddresses.remove(i);
283             return true;
284         }
285         return false;
286     }
287 
288     /**
289      * Returns all the {@link LinkAddress} on this link.  Typically a link will have
290      * one IPv4 address and one or more IPv6 addresses.
291      *
292      * @return An unmodifiable {@link List} of {@link LinkAddress} for this link.
293      */
getLinkAddresses()294     public List<LinkAddress> getLinkAddresses() {
295         return Collections.unmodifiableList(mLinkAddresses);
296     }
297 
298     /**
299      * Returns all the addresses on this link and all the links stacked above it.
300      * @hide
301      */
getAllLinkAddresses()302     public List<LinkAddress> getAllLinkAddresses() {
303         List<LinkAddress> addresses = new ArrayList<LinkAddress>();
304         addresses.addAll(mLinkAddresses);
305         for (LinkProperties stacked: mStackedLinks.values()) {
306             addresses.addAll(stacked.getAllLinkAddresses());
307         }
308         return addresses;
309     }
310 
311     /**
312      * Replaces the {@link LinkAddress} in this {@code LinkProperties} with
313      * the given {@link Collection} of {@link LinkAddress}.
314      *
315      * @param addresses The {@link Collection} of {@link LinkAddress} to set in this
316      *                  object.
317      * @hide
318      */
setLinkAddresses(Collection<LinkAddress> addresses)319     public void setLinkAddresses(Collection<LinkAddress> addresses) {
320         mLinkAddresses.clear();
321         for (LinkAddress address: addresses) {
322             addLinkAddress(address);
323         }
324     }
325 
326     /**
327      * Adds the given {@link InetAddress} to the list of DNS servers, if not present.
328      *
329      * @param dnsServer The {@link InetAddress} to add to the list of DNS servers.
330      * @return true if the DNS server was added, false if it was already present.
331      * @hide
332      */
addDnsServer(InetAddress dnsServer)333     public boolean addDnsServer(InetAddress dnsServer) {
334         if (dnsServer != null && !mDnses.contains(dnsServer)) {
335             mDnses.add(dnsServer);
336             return true;
337         }
338         return false;
339     }
340 
341     /**
342      * Removes the given {@link InetAddress} from the list of DNS servers.
343      *
344      * @param dnsServer The {@link InetAddress} to remove from the list of DNS servers.
345      * @return true if the DNS server was removed, false if it did not exist.
346      * @hide
347      */
removeDnsServer(InetAddress dnsServer)348     public boolean removeDnsServer(InetAddress dnsServer) {
349         if (dnsServer != null) {
350             return mDnses.remove(dnsServer);
351         }
352         return false;
353     }
354 
355     /**
356      * Replaces the DNS servers in this {@code LinkProperties} with
357      * the given {@link Collection} of {@link InetAddress} objects.
358      *
359      * @param addresses The {@link Collection} of DNS servers to set in this object.
360      * @hide
361      */
setDnsServers(Collection<InetAddress> dnsServers)362     public void setDnsServers(Collection<InetAddress> dnsServers) {
363         mDnses.clear();
364         for (InetAddress dnsServer: dnsServers) {
365             addDnsServer(dnsServer);
366         }
367     }
368 
369     /**
370      * Returns all the {@link InetAddress} for DNS servers on this link.
371      *
372      * @return An umodifiable {@link List} of {@link InetAddress} for DNS servers on
373      *         this link.
374      */
getDnsServers()375     public List<InetAddress> getDnsServers() {
376         return Collections.unmodifiableList(mDnses);
377     }
378 
379     /**
380      * Sets the DNS domain search path used on this link.
381      *
382      * @param domains A {@link String} listing in priority order the comma separated
383      *                domains to search when resolving host names on this link.
384      * @hide
385      */
setDomains(String domains)386     public void setDomains(String domains) {
387         mDomains = domains;
388     }
389 
390     /**
391      * Get the DNS domains search path set for this link.
392      *
393      * @return A {@link String} containing the comma separated domains to search when resolving
394      *         host names on this link.
395      */
getDomains()396     public String getDomains() {
397         return mDomains;
398     }
399 
400     /**
401      * Sets the Maximum Transmission Unit size to use on this link.  This should not be used
402      * unless the system default (1500) is incorrect.  Values less than 68 or greater than
403      * 10000 will be ignored.
404      *
405      * @param mtu The MTU to use for this link.
406      * @hide
407      */
setMtu(int mtu)408     public void setMtu(int mtu) {
409         mMtu = mtu;
410     }
411 
412     /**
413      * Gets any non-default MTU size set for this link.  Note that if the default is being used
414      * this will return 0.
415      *
416      * @return The mtu value set for this link.
417      * @hide
418      */
getMtu()419     public int getMtu() {
420         return mMtu;
421     }
422 
423     /**
424      * Sets the tcp buffers sizes to be used when this link is the system default.
425      * Should be of the form "rmem_min,rmem_def,rmem_max,wmem_min,wmem_def,wmem_max".
426      *
427      * @param tcpBufferSizes The tcp buffers sizes to use.
428      *
429      * @hide
430      */
setTcpBufferSizes(String tcpBufferSizes)431     public void setTcpBufferSizes(String tcpBufferSizes) {
432         mTcpBufferSizes = tcpBufferSizes;
433     }
434 
435     /**
436      * Gets the tcp buffer sizes.
437      *
438      * @return the tcp buffer sizes to use when this link is the system default.
439      *
440      * @hide
441      */
getTcpBufferSizes()442     public String getTcpBufferSizes() {
443         return mTcpBufferSizes;
444     }
445 
routeWithInterface(RouteInfo route)446     private RouteInfo routeWithInterface(RouteInfo route) {
447         return new RouteInfo(
448             route.getDestination(),
449             route.getGateway(),
450             mIfaceName,
451             route.getType());
452     }
453 
454     /**
455      * Adds a {@link RouteInfo} to this {@code LinkProperties}, if not present. If the
456      * {@link RouteInfo} had an interface name set and that differs from the interface set for this
457      * {@code LinkProperties} an {@link IllegalArgumentException} will be thrown.  The proper
458      * course is to add either un-named or properly named {@link RouteInfo}.
459      *
460      * @param route A {@link RouteInfo} to add to this object.
461      * @return {@code false} if the route was already present, {@code true} if it was added.
462      *
463      * @hide
464      */
addRoute(RouteInfo route)465     public boolean addRoute(RouteInfo route) {
466         if (route != null) {
467             String routeIface = route.getInterface();
468             if (routeIface != null && !routeIface.equals(mIfaceName)) {
469                 throw new IllegalArgumentException(
470                    "Route added with non-matching interface: " + routeIface +
471                    " vs. " + mIfaceName);
472             }
473             route = routeWithInterface(route);
474             if (!mRoutes.contains(route)) {
475                 mRoutes.add(route);
476                 return true;
477             }
478         }
479         return false;
480     }
481 
482     /**
483      * Removes a {@link RouteInfo} from this {@code LinkProperties}, if present. The route must
484      * specify an interface and the interface must match the interface of this
485      * {@code LinkProperties}, or it will not be removed.
486      *
487      * @return {@code true} if the route was removed, {@code false} if it was not present.
488      *
489      * @hide
490      */
removeRoute(RouteInfo route)491     public boolean removeRoute(RouteInfo route) {
492         return route != null &&
493                 Objects.equals(mIfaceName, route.getInterface()) &&
494                 mRoutes.remove(route);
495     }
496 
497     /**
498      * Returns all the {@link RouteInfo} set on this link.
499      *
500      * @return An unmodifiable {@link List} of {@link RouteInfo} for this link.
501      */
getRoutes()502     public List<RouteInfo> getRoutes() {
503         return Collections.unmodifiableList(mRoutes);
504     }
505 
506     /**
507      * Returns all the routes on this link and all the links stacked above it.
508      * @hide
509      */
getAllRoutes()510     public List<RouteInfo> getAllRoutes() {
511         List<RouteInfo> routes = new ArrayList();
512         routes.addAll(mRoutes);
513         for (LinkProperties stacked: mStackedLinks.values()) {
514             routes.addAll(stacked.getAllRoutes());
515         }
516         return routes;
517     }
518 
519     /**
520      * Sets the recommended {@link ProxyInfo} to use on this link, or {@code null} for none.
521      * Note that Http Proxies are only a hint - the system recommends their use, but it does
522      * not enforce it and applications may ignore them.
523      *
524      * @param proxy A {@link ProxyInfo} defining the HTTP Proxy to use on this link.
525      * @hide
526      */
setHttpProxy(ProxyInfo proxy)527     public void setHttpProxy(ProxyInfo proxy) {
528         mHttpProxy = proxy;
529     }
530 
531     /**
532      * Gets the recommended {@link ProxyInfo} (or {@code null}) set on this link.
533      *
534      * @return The {@link ProxyInfo} set on this link
535      */
getHttpProxy()536     public ProxyInfo getHttpProxy() {
537         return mHttpProxy;
538     }
539 
540     /**
541      * Adds a stacked link.
542      *
543      * If there is already a stacked link with the same interfacename as link,
544      * that link is replaced with link. Otherwise, link is added to the list
545      * of stacked links. If link is null, nothing changes.
546      *
547      * @param link The link to add.
548      * @return true if the link was stacked, false otherwise.
549      * @hide
550      */
addStackedLink(LinkProperties link)551     public boolean addStackedLink(LinkProperties link) {
552         if (link != null && link.getInterfaceName() != null) {
553             mStackedLinks.put(link.getInterfaceName(), link);
554             return true;
555         }
556         return false;
557     }
558 
559     /**
560      * Removes a stacked link.
561      *
562      * If there is a stacked link with the given interface name, it is
563      * removed. Otherwise, nothing changes.
564      *
565      * @param iface The interface name of the link to remove.
566      * @return true if the link was removed, false otherwise.
567      * @hide
568      */
removeStackedLink(String iface)569     public boolean removeStackedLink(String iface) {
570         if (iface != null) {
571             LinkProperties removed = mStackedLinks.remove(iface);
572             return removed != null;
573         }
574         return false;
575     }
576 
577     /**
578      * Returns all the links stacked on top of this link.
579      * @hide
580      */
getStackedLinks()581     public @NonNull List<LinkProperties> getStackedLinks() {
582         if (mStackedLinks.isEmpty()) {
583             return Collections.EMPTY_LIST;
584         }
585         List<LinkProperties> stacked = new ArrayList<LinkProperties>();
586         for (LinkProperties link : mStackedLinks.values()) {
587             stacked.add(new LinkProperties(link));
588         }
589         return Collections.unmodifiableList(stacked);
590     }
591 
592     /**
593      * Clears this object to its initial state.
594      * @hide
595      */
clear()596     public void clear() {
597         mIfaceName = null;
598         mLinkAddresses.clear();
599         mDnses.clear();
600         mDomains = null;
601         mRoutes.clear();
602         mHttpProxy = null;
603         mStackedLinks.clear();
604         mMtu = 0;
605         mTcpBufferSizes = null;
606     }
607 
608     /**
609      * Implement the Parcelable interface
610      */
describeContents()611     public int describeContents() {
612         return 0;
613     }
614 
615     @Override
toString()616     public String toString() {
617         String ifaceName = (mIfaceName == null ? "" : "InterfaceName: " + mIfaceName + " ");
618 
619         String linkAddresses = "LinkAddresses: [";
620         for (LinkAddress addr : mLinkAddresses) linkAddresses += addr.toString() + ",";
621         linkAddresses += "] ";
622 
623         String dns = "DnsAddresses: [";
624         for (InetAddress addr : mDnses) dns += addr.getHostAddress() + ",";
625         dns += "] ";
626 
627         String domainName = "Domains: " + mDomains;
628 
629         String mtu = " MTU: " + mMtu;
630 
631         String tcpBuffSizes = "";
632         if (mTcpBufferSizes != null) {
633             tcpBuffSizes = " TcpBufferSizes: " + mTcpBufferSizes;
634         }
635 
636         String routes = " Routes: [";
637         for (RouteInfo route : mRoutes) routes += route.toString() + ",";
638         routes += "] ";
639         String proxy = (mHttpProxy == null ? "" : " HttpProxy: " + mHttpProxy.toString() + " ");
640 
641         String stacked = "";
642         if (mStackedLinks.values().size() > 0) {
643             stacked += " Stacked: [";
644             for (LinkProperties link: mStackedLinks.values()) {
645                 stacked += " [" + link.toString() + " ],";
646             }
647             stacked += "] ";
648         }
649         return "{" + ifaceName + linkAddresses + routes + dns + domainName + mtu
650             + tcpBuffSizes + proxy + stacked + "}";
651     }
652 
653     /**
654      * Returns true if this link has an IPv4 address.
655      *
656      * @return {@code true} if there is an IPv4 address, {@code false} otherwise.
657      * @hide
658      */
hasIPv4Address()659     public boolean hasIPv4Address() {
660         for (LinkAddress address : mLinkAddresses) {
661           if (address.getAddress() instanceof Inet4Address) {
662             return true;
663           }
664         }
665         return false;
666     }
667 
668     /**
669      * Returns true if this link or any of its stacked interfaces has an IPv4 address.
670      *
671      * @return {@code true} if there is an IPv4 address, {@code false} otherwise.
672      */
hasIPv4AddressOnInterface(String iface)673     private boolean hasIPv4AddressOnInterface(String iface) {
674         // mIfaceName can be null.
675         return (Objects.equals(iface, mIfaceName) && hasIPv4Address()) ||
676                 (iface != null && mStackedLinks.containsKey(iface) &&
677                         mStackedLinks.get(iface).hasIPv4Address());
678     }
679 
680     /**
681      * Returns true if this link has a global preferred IPv6 address.
682      *
683      * @return {@code true} if there is a global preferred IPv6 address, {@code false} otherwise.
684      * @hide
685      */
hasGlobalIPv6Address()686     public boolean hasGlobalIPv6Address() {
687         for (LinkAddress address : mLinkAddresses) {
688           if (address.getAddress() instanceof Inet6Address && address.isGlobalPreferred()) {
689             return true;
690           }
691         }
692         return false;
693     }
694 
695     /**
696      * Returns true if this link has an IPv4 default route.
697      *
698      * @return {@code true} if there is an IPv4 default route, {@code false} otherwise.
699      * @hide
700      */
hasIPv4DefaultRoute()701     public boolean hasIPv4DefaultRoute() {
702         for (RouteInfo r : mRoutes) {
703           if (r.isIPv4Default()) {
704             return true;
705           }
706         }
707         return false;
708     }
709 
710     /**
711      * Returns true if this link has an IPv6 default route.
712      *
713      * @return {@code true} if there is an IPv6 default route, {@code false} otherwise.
714      * @hide
715      */
hasIPv6DefaultRoute()716     public boolean hasIPv6DefaultRoute() {
717         for (RouteInfo r : mRoutes) {
718           if (r.isIPv6Default()) {
719             return true;
720           }
721         }
722         return false;
723     }
724 
725     /**
726      * Returns true if this link has an IPv4 DNS server.
727      *
728      * @return {@code true} if there is an IPv4 DNS server, {@code false} otherwise.
729      * @hide
730      */
hasIPv4DnsServer()731     public boolean hasIPv4DnsServer() {
732         for (InetAddress ia : mDnses) {
733           if (ia instanceof Inet4Address) {
734             return true;
735           }
736         }
737         return false;
738     }
739 
740     /**
741      * Returns true if this link has an IPv6 DNS server.
742      *
743      * @return {@code true} if there is an IPv6 DNS server, {@code false} otherwise.
744      * @hide
745      */
hasIPv6DnsServer()746     public boolean hasIPv6DnsServer() {
747         for (InetAddress ia : mDnses) {
748           if (ia instanceof Inet6Address) {
749             return true;
750           }
751         }
752         return false;
753     }
754 
755     /**
756      * Returns true if this link is provisioned for global IPv4 connectivity.
757      * This requires an IP address, default route, and DNS server.
758      *
759      * @return {@code true} if the link is provisioned, {@code false} otherwise.
760      * @hide
761      */
isIPv4Provisioned()762     public boolean isIPv4Provisioned() {
763         return (hasIPv4Address() &&
764                 hasIPv4DefaultRoute() &&
765                 hasIPv4DnsServer());
766     }
767 
768     /**
769      * Returns true if this link is provisioned for global IPv6 connectivity.
770      * This requires an IP address, default route, and DNS server.
771      *
772      * @return {@code true} if the link is provisioned, {@code false} otherwise.
773      * @hide
774      */
isIPv6Provisioned()775     public boolean isIPv6Provisioned() {
776         return (hasGlobalIPv6Address() &&
777                 hasIPv6DefaultRoute() &&
778                 hasIPv6DnsServer());
779     }
780 
781     /**
782      * Returns true if this link is provisioned for global connectivity,
783      * for at least one Internet Protocol family.
784      *
785      * @return {@code true} if the link is provisioned, {@code false} otherwise.
786      * @hide
787      */
isProvisioned()788     public boolean isProvisioned() {
789         return (isIPv4Provisioned() || isIPv6Provisioned());
790     }
791 
792     /**
793      * Evaluate whether the {@link InetAddress} is considered reachable.
794      *
795      * @return {@code true} if the given {@link InetAddress} is considered reachable,
796      *         {@code false} otherwise.
797      * @hide
798      */
isReachable(InetAddress ip)799     public boolean isReachable(InetAddress ip) {
800         final List<RouteInfo> allRoutes = getAllRoutes();
801         // If we don't have a route to this IP address, it's not reachable.
802         final RouteInfo bestRoute = RouteInfo.selectBestRoute(allRoutes, ip);
803         if (bestRoute == null) {
804             return false;
805         }
806 
807         // TODO: better source address evaluation for destination addresses.
808 
809         if (ip instanceof Inet4Address) {
810             // For IPv4, it suffices for now to simply have any address.
811             return hasIPv4AddressOnInterface(bestRoute.getInterface());
812         } else if (ip instanceof Inet6Address) {
813             if (ip.isLinkLocalAddress()) {
814                 // For now, just make sure link-local destinations have
815                 // scopedIds set, since transmits will generally fail otherwise.
816                 // TODO: verify it matches the ifindex of one of the interfaces.
817                 return (((Inet6Address)ip).getScopeId() != 0);
818             }  else {
819                 // For non-link-local destinations check that either the best route
820                 // is directly connected or that some global preferred address exists.
821                 // TODO: reconsider all cases (disconnected ULA networks, ...).
822                 return (!bestRoute.hasGateway() || hasGlobalIPv6Address());
823             }
824         }
825 
826         return false;
827     }
828 
829     /**
830      * Compares this {@code LinkProperties} interface name against the target
831      *
832      * @param target LinkProperties to compare.
833      * @return {@code true} if both are identical, {@code false} otherwise.
834      * @hide
835      */
isIdenticalInterfaceName(LinkProperties target)836     public boolean isIdenticalInterfaceName(LinkProperties target) {
837         return TextUtils.equals(getInterfaceName(), target.getInterfaceName());
838     }
839 
840     /**
841      * Compares this {@code LinkProperties} interface addresses against the target
842      *
843      * @param target LinkProperties to compare.
844      * @return {@code true} if both are identical, {@code false} otherwise.
845      * @hide
846      */
isIdenticalAddresses(LinkProperties target)847     public boolean isIdenticalAddresses(LinkProperties target) {
848         Collection<InetAddress> targetAddresses = target.getAddresses();
849         Collection<InetAddress> sourceAddresses = getAddresses();
850         return (sourceAddresses.size() == targetAddresses.size()) ?
851                     sourceAddresses.containsAll(targetAddresses) : false;
852     }
853 
854     /**
855      * Compares this {@code LinkProperties} DNS addresses against the target
856      *
857      * @param target LinkProperties to compare.
858      * @return {@code true} if both are identical, {@code false} otherwise.
859      * @hide
860      */
isIdenticalDnses(LinkProperties target)861     public boolean isIdenticalDnses(LinkProperties target) {
862         Collection<InetAddress> targetDnses = target.getDnsServers();
863         String targetDomains = target.getDomains();
864         if (mDomains == null) {
865             if (targetDomains != null) return false;
866         } else {
867             if (mDomains.equals(targetDomains) == false) return false;
868         }
869         return (mDnses.size() == targetDnses.size()) ?
870                     mDnses.containsAll(targetDnses) : false;
871     }
872 
873     /**
874      * Compares this {@code LinkProperties} Routes against the target
875      *
876      * @param target LinkProperties to compare.
877      * @return {@code true} if both are identical, {@code false} otherwise.
878      * @hide
879      */
isIdenticalRoutes(LinkProperties target)880     public boolean isIdenticalRoutes(LinkProperties target) {
881         Collection<RouteInfo> targetRoutes = target.getRoutes();
882         return (mRoutes.size() == targetRoutes.size()) ?
883                     mRoutes.containsAll(targetRoutes) : false;
884     }
885 
886     /**
887      * Compares this {@code LinkProperties} HttpProxy against the target
888      *
889      * @param target LinkProperties to compare.
890      * @return {@code true} if both are identical, {@code false} otherwise.
891      * @hide
892      */
isIdenticalHttpProxy(LinkProperties target)893     public boolean isIdenticalHttpProxy(LinkProperties target) {
894         return getHttpProxy() == null ? target.getHttpProxy() == null :
895                     getHttpProxy().equals(target.getHttpProxy());
896     }
897 
898     /**
899      * Compares this {@code LinkProperties} stacked links against the target
900      *
901      * @param target LinkProperties to compare.
902      * @return {@code true} if both are identical, {@code false} otherwise.
903      * @hide
904      */
isIdenticalStackedLinks(LinkProperties target)905     public boolean isIdenticalStackedLinks(LinkProperties target) {
906         if (!mStackedLinks.keySet().equals(target.mStackedLinks.keySet())) {
907             return false;
908         }
909         for (LinkProperties stacked : mStackedLinks.values()) {
910             // Hashtable values can never be null.
911             String iface = stacked.getInterfaceName();
912             if (!stacked.equals(target.mStackedLinks.get(iface))) {
913                 return false;
914             }
915         }
916         return true;
917     }
918 
919     /**
920      * Compares this {@code LinkProperties} MTU against the target
921      *
922      * @param target LinkProperties to compare.
923      * @return {@code true} if both are identical, {@code false} otherwise.
924      * @hide
925      */
isIdenticalMtu(LinkProperties target)926     public boolean isIdenticalMtu(LinkProperties target) {
927         return getMtu() == target.getMtu();
928     }
929 
930     /**
931      * Compares this {@code LinkProperties} Tcp buffer sizes against the target.
932      *
933      * @param target LinkProperties to compare.
934      * @return {@code true} if both are identical, {@code false} otherwise.
935      * @hide
936      */
isIdenticalTcpBufferSizes(LinkProperties target)937     public boolean isIdenticalTcpBufferSizes(LinkProperties target) {
938         return Objects.equals(mTcpBufferSizes, target.mTcpBufferSizes);
939     }
940 
941     @Override
942     /**
943      * Compares this {@code LinkProperties} instance against the target
944      * LinkProperties in {@code obj}. Two LinkPropertieses are equal if
945      * all their fields are equal in values.
946      *
947      * For collection fields, such as mDnses, containsAll() is used to check
948      * if two collections contains the same elements, independent of order.
949      * There are two thoughts regarding containsAll()
950      * 1. Duplicated elements. eg, (A, B, B) and (A, A, B) are equal.
951      * 2. Worst case performance is O(n^2).
952      *
953      * @param obj the object to be tested for equality.
954      * @return {@code true} if both objects are equal, {@code false} otherwise.
955      */
equals(Object obj)956     public boolean equals(Object obj) {
957         if (this == obj) return true;
958 
959         if (!(obj instanceof LinkProperties)) return false;
960 
961         LinkProperties target = (LinkProperties) obj;
962         /**
963          * This method does not check that stacked interfaces are equal, because
964          * stacked interfaces are not so much a property of the link as a
965          * description of connections between links.
966          */
967         return isIdenticalInterfaceName(target) &&
968                 isIdenticalAddresses(target) &&
969                 isIdenticalDnses(target) &&
970                 isIdenticalRoutes(target) &&
971                 isIdenticalHttpProxy(target) &&
972                 isIdenticalStackedLinks(target) &&
973                 isIdenticalMtu(target) &&
974                 isIdenticalTcpBufferSizes(target);
975     }
976 
977     /**
978      * Compares the addresses in this LinkProperties with another
979      * LinkProperties, examining only addresses on the base link.
980      *
981      * @param target a LinkProperties with the new list of addresses
982      * @return the differences between the addresses.
983      * @hide
984      */
compareAddresses(LinkProperties target)985     public CompareResult<LinkAddress> compareAddresses(LinkProperties target) {
986         /*
987          * Duplicate the LinkAddresses into removed, we will be removing
988          * address which are common between mLinkAddresses and target
989          * leaving the addresses that are different. And address which
990          * are in target but not in mLinkAddresses are placed in the
991          * addedAddresses.
992          */
993         CompareResult<LinkAddress> result = new CompareResult<LinkAddress>();
994         result.removed = new ArrayList<LinkAddress>(mLinkAddresses);
995         result.added.clear();
996         if (target != null) {
997             for (LinkAddress newAddress : target.getLinkAddresses()) {
998                 if (! result.removed.remove(newAddress)) {
999                     result.added.add(newAddress);
1000                 }
1001             }
1002         }
1003         return result;
1004     }
1005 
1006     /**
1007      * Compares the DNS addresses in this LinkProperties with another
1008      * LinkProperties, examining only DNS addresses on the base link.
1009      *
1010      * @param target a LinkProperties with the new list of dns addresses
1011      * @return the differences between the DNS addresses.
1012      * @hide
1013      */
compareDnses(LinkProperties target)1014     public CompareResult<InetAddress> compareDnses(LinkProperties target) {
1015         /*
1016          * Duplicate the InetAddresses into removed, we will be removing
1017          * dns address which are common between mDnses and target
1018          * leaving the addresses that are different. And dns address which
1019          * are in target but not in mDnses are placed in the
1020          * addedAddresses.
1021          */
1022         CompareResult<InetAddress> result = new CompareResult<InetAddress>();
1023 
1024         result.removed = new ArrayList<InetAddress>(mDnses);
1025         result.added.clear();
1026         if (target != null) {
1027             for (InetAddress newAddress : target.getDnsServers()) {
1028                 if (! result.removed.remove(newAddress)) {
1029                     result.added.add(newAddress);
1030                 }
1031             }
1032         }
1033         return result;
1034     }
1035 
1036     /**
1037      * Compares all routes in this LinkProperties with another LinkProperties,
1038      * examining both the the base link and all stacked links.
1039      *
1040      * @param target a LinkProperties with the new list of routes
1041      * @return the differences between the routes.
1042      * @hide
1043      */
compareAllRoutes(LinkProperties target)1044     public CompareResult<RouteInfo> compareAllRoutes(LinkProperties target) {
1045         /*
1046          * Duplicate the RouteInfos into removed, we will be removing
1047          * routes which are common between mRoutes and target
1048          * leaving the routes that are different. And route address which
1049          * are in target but not in mRoutes are placed in added.
1050          */
1051         CompareResult<RouteInfo> result = new CompareResult<RouteInfo>();
1052 
1053         result.removed = getAllRoutes();
1054         result.added.clear();
1055         if (target != null) {
1056             for (RouteInfo r : target.getAllRoutes()) {
1057                 if (! result.removed.remove(r)) {
1058                     result.added.add(r);
1059                 }
1060             }
1061         }
1062         return result;
1063     }
1064 
1065     /**
1066      * Compares all interface names in this LinkProperties with another
1067      * LinkProperties, examining both the the base link and all stacked links.
1068      *
1069      * @param target a LinkProperties with the new list of interface names
1070      * @return the differences between the interface names.
1071      * @hide
1072      */
compareAllInterfaceNames(LinkProperties target)1073     public CompareResult<String> compareAllInterfaceNames(LinkProperties target) {
1074         /*
1075          * Duplicate the interface names into removed, we will be removing
1076          * interface names which are common between this and target
1077          * leaving the interface names that are different. And interface names which
1078          * are in target but not in this are placed in added.
1079          */
1080         CompareResult<String> result = new CompareResult<String>();
1081 
1082         result.removed = getAllInterfaceNames();
1083         result.added.clear();
1084         if (target != null) {
1085             for (String r : target.getAllInterfaceNames()) {
1086                 if (! result.removed.remove(r)) {
1087                     result.added.add(r);
1088                 }
1089             }
1090         }
1091         return result;
1092     }
1093 
1094 
1095     @Override
1096     /**
1097      * generate hashcode based on significant fields
1098      * Equal objects must produce the same hash code, while unequal objects
1099      * may have the same hash codes.
1100      */
hashCode()1101     public int hashCode() {
1102         return ((null == mIfaceName) ? 0 : mIfaceName.hashCode()
1103                 + mLinkAddresses.size() * 31
1104                 + mDnses.size() * 37
1105                 + ((null == mDomains) ? 0 : mDomains.hashCode())
1106                 + mRoutes.size() * 41
1107                 + ((null == mHttpProxy) ? 0 : mHttpProxy.hashCode())
1108                 + mStackedLinks.hashCode() * 47)
1109                 + mMtu * 51
1110                 + ((null == mTcpBufferSizes) ? 0 : mTcpBufferSizes.hashCode());
1111     }
1112 
1113     /**
1114      * Implement the Parcelable interface.
1115      */
writeToParcel(Parcel dest, int flags)1116     public void writeToParcel(Parcel dest, int flags) {
1117         dest.writeString(getInterfaceName());
1118         dest.writeInt(mLinkAddresses.size());
1119         for(LinkAddress linkAddress : mLinkAddresses) {
1120             dest.writeParcelable(linkAddress, flags);
1121         }
1122 
1123         dest.writeInt(mDnses.size());
1124         for(InetAddress d : mDnses) {
1125             dest.writeByteArray(d.getAddress());
1126         }
1127         dest.writeString(mDomains);
1128         dest.writeInt(mMtu);
1129         dest.writeString(mTcpBufferSizes);
1130         dest.writeInt(mRoutes.size());
1131         for(RouteInfo route : mRoutes) {
1132             dest.writeParcelable(route, flags);
1133         }
1134 
1135         if (mHttpProxy != null) {
1136             dest.writeByte((byte)1);
1137             dest.writeParcelable(mHttpProxy, flags);
1138         } else {
1139             dest.writeByte((byte)0);
1140         }
1141         ArrayList<LinkProperties> stackedLinks = new ArrayList(mStackedLinks.values());
1142         dest.writeList(stackedLinks);
1143     }
1144 
1145     /**
1146      * Implement the Parcelable interface.
1147      */
1148     public static final Creator<LinkProperties> CREATOR =
1149         new Creator<LinkProperties>() {
1150             public LinkProperties createFromParcel(Parcel in) {
1151                 LinkProperties netProp = new LinkProperties();
1152 
1153                 String iface = in.readString();
1154                 if (iface != null) {
1155                     netProp.setInterfaceName(iface);
1156                 }
1157                 int addressCount = in.readInt();
1158                 for (int i=0; i<addressCount; i++) {
1159                     netProp.addLinkAddress((LinkAddress)in.readParcelable(null));
1160                 }
1161                 addressCount = in.readInt();
1162                 for (int i=0; i<addressCount; i++) {
1163                     try {
1164                         netProp.addDnsServer(InetAddress.getByAddress(in.createByteArray()));
1165                     } catch (UnknownHostException e) { }
1166                 }
1167                 netProp.setDomains(in.readString());
1168                 netProp.setMtu(in.readInt());
1169                 netProp.setTcpBufferSizes(in.readString());
1170                 addressCount = in.readInt();
1171                 for (int i=0; i<addressCount; i++) {
1172                     netProp.addRoute((RouteInfo)in.readParcelable(null));
1173                 }
1174                 if (in.readByte() == 1) {
1175                     netProp.setHttpProxy((ProxyInfo)in.readParcelable(null));
1176                 }
1177                 ArrayList<LinkProperties> stackedLinks = new ArrayList<LinkProperties>();
1178                 in.readList(stackedLinks, LinkProperties.class.getClassLoader());
1179                 for (LinkProperties stackedLink: stackedLinks) {
1180                     netProp.addStackedLink(stackedLink);
1181                 }
1182                 return netProp;
1183             }
1184 
1185             public LinkProperties[] newArray(int size) {
1186                 return new LinkProperties[size];
1187             }
1188         };
1189 
1190         /**
1191          * Check the valid MTU range based on IPv4 or IPv6.
1192          * @hide
1193          */
isValidMtu(int mtu, boolean ipv6)1194         public static boolean isValidMtu(int mtu, boolean ipv6) {
1195             if (ipv6) {
1196                 if ((mtu >= MIN_MTU_V6 && mtu <= MAX_MTU)) return true;
1197             } else {
1198                 if ((mtu >= MIN_MTU && mtu <= MAX_MTU)) return true;
1199             }
1200             return false;
1201         }
1202 }
1203