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