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