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