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