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