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