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