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