Home | History | Annotate | Download | only in net
      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
     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      */
    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      */
    139     public LinkProperties() {
    140     }
    141 
    142     /**
    143      * @hide
    144      */
    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      */
    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      */
    183     public @Nullable String getInterfaceName() {
    184         return mIfaceName;
    185     }
    186 
    187     /**
    188      * @hide
    189      */
    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      */
    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      */
    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 
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    438     public String getTcpBufferSizes() {
    439         return mTcpBufferSizes;
    440     }
    441 
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    607     public int describeContents() {
    608         return 0;
    609     }
    610 
    611     @Override
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
   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      */
   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      */
   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      */
   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      */
   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          */
   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