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