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