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