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