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.net.ProxyInfo;
20 import android.os.Parcelable;
21 import android.os.Parcel;
22 import android.text.TextUtils;
23 
24 import java.net.InetAddress;
25 import java.net.Inet4Address;
26 import java.net.Inet6Address;
27 
28 import java.net.UnknownHostException;
29 import java.util.ArrayList;
30 import java.util.Collection;
31 import java.util.Collections;
32 import java.util.Hashtable;
33 import java.util.List;
34 
35 /**
36  * Describes the properties of a network link.
37  *
38  * A link represents a connection to a network.
39  * It may have multiple addresses and multiple gateways,
40  * multiple dns servers but only one http proxy and one
41  * network interface.
42  *
43  * Note that this is just a holder of data.  Modifying it
44  * does not affect live networks.
45  *
46  */
47 public class LinkProperties implements Parcelable {
48     // The interface described by the network link.
49     private String mIfaceName;
50     private ArrayList<LinkAddress> mLinkAddresses = new ArrayList<LinkAddress>();
51     private ArrayList<InetAddress> mDnses = new ArrayList<InetAddress>();
52     private String mDomains;
53     private ArrayList<RouteInfo> mRoutes = new ArrayList<RouteInfo>();
54     private ProxyInfo mHttpProxy;
55     private int mMtu;
56 
57     // Stores the properties of links that are "stacked" above this link.
58     // Indexed by interface name to allow modification and to prevent duplicates being added.
59     private Hashtable<String, LinkProperties> mStackedLinks =
60         new Hashtable<String, LinkProperties>();
61 
62     /**
63      * @hide
64      */
65     public static class CompareResult<T> {
66         public List<T> removed = new ArrayList<T>();
67         public List<T> added = new ArrayList<T>();
68 
69         @Override
70         public String toString() {
71             String retVal = "removed=[";
72             for (T addr : removed) retVal += addr.toString() + ",";
73             retVal += "] added=[";
74             for (T addr : added) retVal += addr.toString() + ",";
75             retVal += "]";
76             return retVal;
77         }
78     }
79 
80     /**
81      * @hide
82      */
83     public LinkProperties() {
84     }
85 
86     /**
87      * @hide
88      */
89     public LinkProperties(LinkProperties source) {
90         if (source != null) {
91             mIfaceName = source.getInterfaceName();
92             for (LinkAddress l : source.getLinkAddresses()) mLinkAddresses.add(l);
93             for (InetAddress i : source.getDnsServers()) mDnses.add(i);
94             mDomains = source.getDomains();
95             for (RouteInfo r : source.getRoutes()) mRoutes.add(r);
96             mHttpProxy = (source.getHttpProxy() == null)  ?
97                     null : new ProxyInfo(source.getHttpProxy());
98             for (LinkProperties l: source.mStackedLinks.values()) {
99                 addStackedLink(l);
100             }
101             setMtu(source.getMtu());
102         }
103     }
104 
105     /**
106      * Sets the interface name for this link.  All {@link RouteInfo} already set for this
107      * will have their interface changed to match this new value.
108      *
109      * @param iface The name of the network interface used for this link.
110      * @hide
111      */
112     public void setInterfaceName(String iface) {
113         mIfaceName = iface;
114         ArrayList<RouteInfo> newRoutes = new ArrayList<RouteInfo>(mRoutes.size());
115         for (RouteInfo route : mRoutes) {
116             newRoutes.add(routeWithInterface(route));
117         }
118         mRoutes = newRoutes;
119     }
120 
121     /**
122      * Gets the interface name for this link.  May be {@code null} if not set.
123      *
124      * @return The interface name set for this link or {@code null}.
125      */
126     public String getInterfaceName() {
127         return mIfaceName;
128     }
129 
130     /**
131      * @hide
132      */
133     public List<String> getAllInterfaceNames() {
134         List<String> interfaceNames = new ArrayList<String>(mStackedLinks.size() + 1);
135         if (mIfaceName != null) interfaceNames.add(new String(mIfaceName));
136         for (LinkProperties stacked: mStackedLinks.values()) {
137             interfaceNames.addAll(stacked.getAllInterfaceNames());
138         }
139         return interfaceNames;
140     }
141 
142     /**
143      * Returns all the addresses on this link.  We often think of a link having a single address,
144      * however, particularly with Ipv6 several addresses are typical.  Note that the
145      * {@code LinkProperties} actually contains {@link LinkAddress} objects which also include
146      * prefix lengths for each address.  This is a simplified utility alternative to
147      * {@link LinkProperties#getLinkAddresses}.
148      *
149      * @return An umodifiable {@link List} of {@link InetAddress} for this link.
150      * @hide
151      */
152     public List<InetAddress> getAddresses() {
153         List<InetAddress> addresses = new ArrayList<InetAddress>();
154         for (LinkAddress linkAddress : mLinkAddresses) {
155             addresses.add(linkAddress.getAddress());
156         }
157         return Collections.unmodifiableList(addresses);
158     }
159 
160     /**
161      * Returns all the addresses on this link and all the links stacked above it.
162      * @hide
163      */
164     public List<InetAddress> getAllAddresses() {
165         List<InetAddress> addresses = new ArrayList<InetAddress>();
166         for (LinkAddress linkAddress : mLinkAddresses) {
167             addresses.add(linkAddress.getAddress());
168         }
169         for (LinkProperties stacked: mStackedLinks.values()) {
170             addresses.addAll(stacked.getAllAddresses());
171         }
172         return addresses;
173     }
174 
175     private int findLinkAddressIndex(LinkAddress address) {
176         for (int i = 0; i < mLinkAddresses.size(); i++) {
177             if (mLinkAddresses.get(i).isSameAddressAs(address)) {
178                 return i;
179             }
180         }
181         return -1;
182     }
183 
184     /**
185      * Adds a {@link LinkAddress} to this {@code LinkProperties} if a {@link LinkAddress} of the
186      * same address/prefix does not already exist.  If it does exist it is replaced.
187      * @param address The {@code LinkAddress} to add.
188      * @return true if {@code address} was added or updated, false otherwise.
189      * @hide
190      */
191     public boolean addLinkAddress(LinkAddress address) {
192         if (address == null) {
193             return false;
194         }
195         int i = findLinkAddressIndex(address);
196         if (i < 0) {
197             // Address was not present. Add it.
198             mLinkAddresses.add(address);
199             return true;
200         } else if (mLinkAddresses.get(i).equals(address)) {
201             // Address was present and has same properties. Do nothing.
202             return false;
203         } else {
204             // Address was present and has different properties. Update it.
205             mLinkAddresses.set(i, address);
206             return true;
207         }
208     }
209 
210     /**
211      * Removes a {@link LinkAddress} from this {@code LinkProperties}.  Specifically, matches
212      * and {@link LinkAddress} with the same address and prefix.
213      *
214      * @param toRemove A {@link LinkAddress} specifying the address to remove.
215      * @return true if the address was removed, false if it did not exist.
216      * @hide
217      */
218     public boolean removeLinkAddress(LinkAddress toRemove) {
219         int i = findLinkAddressIndex(toRemove);
220         if (i >= 0) {
221             mLinkAddresses.remove(i);
222             return true;
223         }
224         return false;
225     }
226 
227     /**
228      * Returns all the {@link LinkAddress} on this link.  Typically a link will have
229      * one IPv4 address and one or more IPv6 addresses.
230      *
231      * @return An unmodifiable {@link List} of {@link LinkAddress} for this link.
232      */
233     public List<LinkAddress> getLinkAddresses() {
234         return Collections.unmodifiableList(mLinkAddresses);
235     }
236 
237     /**
238      * Returns all the addresses on this link and all the links stacked above it.
239      * @hide
240      */
241     public List<LinkAddress> getAllLinkAddresses() {
242         List<LinkAddress> addresses = new ArrayList<LinkAddress>();
243         addresses.addAll(mLinkAddresses);
244         for (LinkProperties stacked: mStackedLinks.values()) {
245             addresses.addAll(stacked.getAllLinkAddresses());
246         }
247         return addresses;
248     }
249 
250     /**
251      * Replaces the {@link LinkAddress} in this {@code LinkProperties} with
252      * the given {@link Collection} of {@link LinkAddress}.
253      *
254      * @param addresses The {@link Collection} of {@link LinkAddress} to set in this
255      *                  object.
256      * @hide
257      */
258     public void setLinkAddresses(Collection<LinkAddress> addresses) {
259         mLinkAddresses.clear();
260         for (LinkAddress address: addresses) {
261             addLinkAddress(address);
262         }
263     }
264 
265     /**
266      * Adds the given {@link InetAddress} to the list of DNS servers.
267      *
268      * @param dnsServer The {@link InetAddress} to add to the list of DNS servers.
269      * @hide
270      */
271     public void addDnsServer(InetAddress dnsServer) {
272         if (dnsServer != null) mDnses.add(dnsServer);
273     }
274 
275     /**
276      * Returns all the {@link InetAddress} for DNS servers on this link.
277      *
278      * @return An umodifiable {@link List} of {@link InetAddress} for DNS servers on
279      *         this link.
280      */
281     public List<InetAddress> getDnsServers() {
282         return Collections.unmodifiableList(mDnses);
283     }
284 
285     /**
286      * Sets the DNS domain search path used on this link.
287      *
288      * @param domains A {@link String} listing in priority order the comma separated
289      *                domains to search when resolving host names on this link.
290      * @hide
291      */
292     public void setDomains(String domains) {
293         mDomains = domains;
294     }
295 
296     /**
297      * Get the DNS domains search path set for this link.
298      *
299      * @return A {@link String} containing the comma separated domains to search when resolving
300      *         host names on this link.
301      */
302     public String getDomains() {
303         return mDomains;
304     }
305 
306     /**
307      * Sets the Maximum Transmission Unit size to use on this link.  This should not be used
308      * unless the system default (1500) is incorrect.  Values less than 68 or greater than
309      * 10000 will be ignored.
310      *
311      * @param mtu The MTU to use for this link.
312      * @hide
313      */
314     public void setMtu(int mtu) {
315         mMtu = mtu;
316     }
317 
318     /**
319      * Gets any non-default MTU size set for this link.  Note that if the default is being used
320      * this will return 0.
321      *
322      * @return The mtu value set for this link.
323      * @hide
324      */
325     public int getMtu() {
326         return mMtu;
327     }
328 
329     private RouteInfo routeWithInterface(RouteInfo route) {
330         return new RouteInfo(
331             route.getDestination(),
332             route.getGateway(),
333             mIfaceName);
334     }
335 
336     /**
337      * Adds a {@link RouteInfo} to this {@code LinkProperties}.  If the {@link RouteInfo}
338      * had an interface name set and that differs from the interface set for this
339      * {@code LinkProperties} an {@link IllegalArgumentException} will be thrown.  The
340      * proper course is to add either un-named or properly named {@link RouteInfo}.
341      *
342      * @param route A {@link RouteInfo} to add to this object.
343      * @hide
344      */
345     public void addRoute(RouteInfo route) {
346         if (route != null) {
347             String routeIface = route.getInterface();
348             if (routeIface != null && !routeIface.equals(mIfaceName)) {
349                 throw new IllegalArgumentException(
350                    "Route added with non-matching interface: " + routeIface +
351                    " vs. " + mIfaceName);
352             }
353             mRoutes.add(routeWithInterface(route));
354         }
355     }
356 
357     /**
358      * Returns all the {@link RouteInfo} set on this link.
359      *
360      * @return An unmodifiable {@link List} of {@link RouteInfo} for this link.
361      */
362     public List<RouteInfo> getRoutes() {
363         return Collections.unmodifiableList(mRoutes);
364     }
365 
366     /**
367      * Returns all the routes on this link and all the links stacked above it.
368      * @hide
369      */
370     public List<RouteInfo> getAllRoutes() {
371         List<RouteInfo> routes = new ArrayList();
372         routes.addAll(mRoutes);
373         for (LinkProperties stacked: mStackedLinks.values()) {
374             routes.addAll(stacked.getAllRoutes());
375         }
376         return routes;
377     }
378 
379     /**
380      * Sets the recommended {@link ProxyInfo} to use on this link, or {@code null} for none.
381      * Note that Http Proxies are only a hint - the system recommends their use, but it does
382      * not enforce it and applications may ignore them.
383      *
384      * @param proxy A {@link ProxyInfo} defining the Http Proxy to use on this link.
385      * @hide
386      */
387     public void setHttpProxy(ProxyInfo proxy) {
388         mHttpProxy = proxy;
389     }
390 
391     /**
392      * Gets the recommended {@link ProxyInfo} (or {@code null}) set on this link.
393      *
394      * @return The {@link ProxyInfo} set on this link
395      */
396     public ProxyInfo getHttpProxy() {
397         return mHttpProxy;
398     }
399 
400     /**
401      * Adds a stacked link.
402      *
403      * If there is already a stacked link with the same interfacename as link,
404      * that link is replaced with link. Otherwise, link is added to the list
405      * of stacked links. If link is null, nothing changes.
406      *
407      * @param link The link to add.
408      * @return true if the link was stacked, false otherwise.
409      * @hide
410      */
411     public boolean addStackedLink(LinkProperties link) {
412         if (link != null && link.getInterfaceName() != null) {
413             mStackedLinks.put(link.getInterfaceName(), link);
414             return true;
415         }
416         return false;
417     }
418 
419     /**
420      * Removes a stacked link.
421      *
422      * If there a stacked link with the same interfacename as link, it is
423      * removed. Otherwise, nothing changes.
424      *
425      * @param link The link to remove.
426      * @return true if the link was removed, false otherwise.
427      * @hide
428      */
429     public boolean removeStackedLink(LinkProperties link) {
430         if (link != null && link.getInterfaceName() != null) {
431             LinkProperties removed = mStackedLinks.remove(link.getInterfaceName());
432             return removed != null;
433         }
434         return false;
435     }
436 
437     /**
438      * Returns all the links stacked on top of this link.
439      * @hide
440      */
441     public List<LinkProperties> getStackedLinks() {
442         List<LinkProperties> stacked = new ArrayList<LinkProperties>();
443         for (LinkProperties link : mStackedLinks.values()) {
444           stacked.add(new LinkProperties(link));
445         }
446         return Collections.unmodifiableList(stacked);
447     }
448 
449     /**
450      * Clears this object to its initial state.
451      * @hide
452      */
453     public void clear() {
454         mIfaceName = null;
455         mLinkAddresses.clear();
456         mDnses.clear();
457         mDomains = null;
458         mRoutes.clear();
459         mHttpProxy = null;
460         mStackedLinks.clear();
461         mMtu = 0;
462     }
463 
464     /**
465      * Implement the Parcelable interface
466      * @hide
467      */
468     public int describeContents() {
469         return 0;
470     }
471 
472     @Override
473     public String toString() {
474         String ifaceName = (mIfaceName == null ? "" : "InterfaceName: " + mIfaceName + " ");
475 
476         String linkAddresses = "LinkAddresses: [";
477         for (LinkAddress addr : mLinkAddresses) linkAddresses += addr.toString() + ",";
478         linkAddresses += "] ";
479 
480         String dns = "DnsAddresses: [";
481         for (InetAddress addr : mDnses) dns += addr.getHostAddress() + ",";
482         dns += "] ";
483 
484         String domainName = "Domains: " + mDomains;
485 
486         String mtu = " MTU: " + mMtu;
487 
488         String routes = " Routes: [";
489         for (RouteInfo route : mRoutes) routes += route.toString() + ",";
490         routes += "] ";
491         String proxy = (mHttpProxy == null ? "" : " HttpProxy: " + mHttpProxy.toString() + " ");
492 
493         String stacked = "";
494         if (mStackedLinks.values().size() > 0) {
495             stacked += " Stacked: [";
496             for (LinkProperties link: mStackedLinks.values()) {
497                 stacked += " [" + link.toString() + " ],";
498             }
499             stacked += "] ";
500         }
501         return "{" + ifaceName + linkAddresses + routes + dns + domainName + mtu
502             + proxy + stacked + "}";
503     }
504 
505     /**
506      * Returns true if this link has an IPv4 address.
507      *
508      * @return {@code true} if there is an IPv4 address, {@code false} otherwise.
509      * @hide
510      */
511     public boolean hasIPv4Address() {
512         for (LinkAddress address : mLinkAddresses) {
513           if (address.getAddress() instanceof Inet4Address) {
514             return true;
515           }
516         }
517         return false;
518     }
519 
520     /**
521      * Returns true if this link has an IPv6 address.
522      *
523      * @return {@code true} if there is an IPv6 address, {@code false} otherwise.
524      * @hide
525      */
526     public boolean hasIPv6Address() {
527         for (LinkAddress address : mLinkAddresses) {
528           if (address.getAddress() instanceof Inet6Address) {
529             return true;
530           }
531         }
532         return false;
533     }
534 
535     /**
536      * Compares this {@code LinkProperties} interface name against the target
537      *
538      * @param target LinkProperties to compare.
539      * @return {@code true} if both are identical, {@code false} otherwise.
540      * @hide
541      */
542     public boolean isIdenticalInterfaceName(LinkProperties target) {
543         return TextUtils.equals(getInterfaceName(), target.getInterfaceName());
544     }
545 
546     /**
547      * Compares this {@code LinkProperties} interface addresses against the target
548      *
549      * @param target LinkProperties to compare.
550      * @return {@code true} if both are identical, {@code false} otherwise.
551      * @hide
552      */
553     public boolean isIdenticalAddresses(LinkProperties target) {
554         Collection<InetAddress> targetAddresses = target.getAddresses();
555         Collection<InetAddress> sourceAddresses = getAddresses();
556         return (sourceAddresses.size() == targetAddresses.size()) ?
557                     sourceAddresses.containsAll(targetAddresses) : false;
558     }
559 
560     /**
561      * Compares this {@code LinkProperties} DNS addresses against the target
562      *
563      * @param target LinkProperties to compare.
564      * @return {@code true} if both are identical, {@code false} otherwise.
565      * @hide
566      */
567     public boolean isIdenticalDnses(LinkProperties target) {
568         Collection<InetAddress> targetDnses = target.getDnsServers();
569         String targetDomains = target.getDomains();
570         if (mDomains == null) {
571             if (targetDomains != null) return false;
572         } else {
573             if (mDomains.equals(targetDomains) == false) return false;
574         }
575         return (mDnses.size() == targetDnses.size()) ?
576                     mDnses.containsAll(targetDnses) : false;
577     }
578 
579     /**
580      * Compares this {@code LinkProperties} Routes against the target
581      *
582      * @param target LinkProperties to compare.
583      * @return {@code true} if both are identical, {@code false} otherwise.
584      * @hide
585      */
586     public boolean isIdenticalRoutes(LinkProperties target) {
587         Collection<RouteInfo> targetRoutes = target.getRoutes();
588         return (mRoutes.size() == targetRoutes.size()) ?
589                     mRoutes.containsAll(targetRoutes) : false;
590     }
591 
592     /**
593      * Compares this {@code LinkProperties} HttpProxy against the target
594      *
595      * @param target LinkProperties to compare.
596      * @return {@code true} if both are identical, {@code false} otherwise.
597      * @hide
598      */
599     public boolean isIdenticalHttpProxy(LinkProperties target) {
600         return getHttpProxy() == null ? target.getHttpProxy() == null :
601                     getHttpProxy().equals(target.getHttpProxy());
602     }
603 
604     /**
605      * Compares this {@code LinkProperties} stacked links against the target
606      *
607      * @param target LinkProperties to compare.
608      * @return {@code true} if both are identical, {@code false} otherwise.
609      * @hide
610      */
611     public boolean isIdenticalStackedLinks(LinkProperties target) {
612         if (!mStackedLinks.keySet().equals(target.mStackedLinks.keySet())) {
613             return false;
614         }
615         for (LinkProperties stacked : mStackedLinks.values()) {
616             // Hashtable values can never be null.
617             String iface = stacked.getInterfaceName();
618             if (!stacked.equals(target.mStackedLinks.get(iface))) {
619                 return false;
620             }
621         }
622         return true;
623     }
624 
625     /**
626      * Compares this {@code LinkProperties} MTU against the target
627      *
628      * @param target LinkProperties to compare.
629      * @return {@code true} if both are identical, {@code false} otherwise.
630      * @hide
631      */
632     public boolean isIdenticalMtu(LinkProperties target) {
633         return getMtu() == target.getMtu();
634     }
635 
636     @Override
637     /**
638      * Compares this {@code LinkProperties} instance against the target
639      * LinkProperties in {@code obj}. Two LinkPropertieses are equal if
640      * all their fields are equal in values.
641      *
642      * For collection fields, such as mDnses, containsAll() is used to check
643      * if two collections contains the same elements, independent of order.
644      * There are two thoughts regarding containsAll()
645      * 1. Duplicated elements. eg, (A, B, B) and (A, A, B) are equal.
646      * 2. Worst case performance is O(n^2).
647      *
648      * @param obj the object to be tested for equality.
649      * @return {@code true} if both objects are equal, {@code false} otherwise.
650      */
651     public boolean equals(Object obj) {
652         if (this == obj) return true;
653 
654         if (!(obj instanceof LinkProperties)) return false;
655 
656         LinkProperties target = (LinkProperties) obj;
657         /**
658          * This method does not check that stacked interfaces are equal, because
659          * stacked interfaces are not so much a property of the link as a
660          * description of connections between links.
661          */
662         return isIdenticalInterfaceName(target) &&
663                 isIdenticalAddresses(target) &&
664                 isIdenticalDnses(target) &&
665                 isIdenticalRoutes(target) &&
666                 isIdenticalHttpProxy(target) &&
667                 isIdenticalStackedLinks(target) &&
668                 isIdenticalMtu(target);
669     }
670 
671     /**
672      * Compares the addresses in this LinkProperties with another
673      * LinkProperties, examining only addresses on the base link.
674      *
675      * @param target a LinkProperties with the new list of addresses
676      * @return the differences between the addresses.
677      * @hide
678      */
679     public CompareResult<LinkAddress> compareAddresses(LinkProperties target) {
680         /*
681          * Duplicate the LinkAddresses into removed, we will be removing
682          * address which are common between mLinkAddresses and target
683          * leaving the addresses that are different. And address which
684          * are in target but not in mLinkAddresses are placed in the
685          * addedAddresses.
686          */
687         CompareResult<LinkAddress> result = new CompareResult<LinkAddress>();
688         result.removed = new ArrayList<LinkAddress>(mLinkAddresses);
689         result.added.clear();
690         if (target != null) {
691             for (LinkAddress newAddress : target.getLinkAddresses()) {
692                 if (! result.removed.remove(newAddress)) {
693                     result.added.add(newAddress);
694                 }
695             }
696         }
697         return result;
698     }
699 
700     /**
701      * Compares the DNS addresses in this LinkProperties with another
702      * LinkProperties, examining only DNS addresses on the base link.
703      *
704      * @param target a LinkProperties with the new list of dns addresses
705      * @return the differences between the DNS addresses.
706      * @hide
707      */
708     public CompareResult<InetAddress> compareDnses(LinkProperties target) {
709         /*
710          * Duplicate the InetAddresses into removed, we will be removing
711          * dns address which are common between mDnses and target
712          * leaving the addresses that are different. And dns address which
713          * are in target but not in mDnses are placed in the
714          * addedAddresses.
715          */
716         CompareResult<InetAddress> result = new CompareResult<InetAddress>();
717 
718         result.removed = new ArrayList<InetAddress>(mDnses);
719         result.added.clear();
720         if (target != null) {
721             for (InetAddress newAddress : target.getDnsServers()) {
722                 if (! result.removed.remove(newAddress)) {
723                     result.added.add(newAddress);
724                 }
725             }
726         }
727         return result;
728     }
729 
730     /**
731      * Compares all routes in this LinkProperties with another LinkProperties,
732      * examining both the the base link and all stacked links.
733      *
734      * @param target a LinkProperties with the new list of routes
735      * @return the differences between the routes.
736      * @hide
737      */
738     public CompareResult<RouteInfo> compareAllRoutes(LinkProperties target) {
739         /*
740          * Duplicate the RouteInfos into removed, we will be removing
741          * routes which are common between mRoutes and target
742          * leaving the routes that are different. And route address which
743          * are in target but not in mRoutes are placed in added.
744          */
745         CompareResult<RouteInfo> result = new CompareResult<RouteInfo>();
746 
747         result.removed = getAllRoutes();
748         result.added.clear();
749         if (target != null) {
750             for (RouteInfo r : target.getAllRoutes()) {
751                 if (! result.removed.remove(r)) {
752                     result.added.add(r);
753                 }
754             }
755         }
756         return result;
757     }
758 
759     /**
760      * Compares all interface names in this LinkProperties with another
761      * LinkProperties, examining both the the base link and all stacked links.
762      *
763      * @param target a LinkProperties with the new list of interface names
764      * @return the differences between the interface names.
765      * @hide
766      */
767     public CompareResult<String> compareAllInterfaceNames(LinkProperties target) {
768         /*
769          * Duplicate the interface names into removed, we will be removing
770          * interface names which are common between this and target
771          * leaving the interface names that are different. And interface names which
772          * are in target but not in this are placed in added.
773          */
774         CompareResult<String> result = new CompareResult<String>();
775 
776         result.removed = getAllInterfaceNames();
777         result.added.clear();
778         if (target != null) {
779             for (String r : target.getAllInterfaceNames()) {
780                 if (! result.removed.remove(r)) {
781                     result.added.add(r);
782                 }
783             }
784         }
785         return result;
786     }
787 
788 
789     @Override
790     /**
791      * generate hashcode based on significant fields
792      * Equal objects must produce the same hash code, while unequal objects
793      * may have the same hash codes.
794      */
795     public int hashCode() {
796         return ((null == mIfaceName) ? 0 : mIfaceName.hashCode()
797                 + mLinkAddresses.size() * 31
798                 + mDnses.size() * 37
799                 + ((null == mDomains) ? 0 : mDomains.hashCode())
800                 + mRoutes.size() * 41
801                 + ((null == mHttpProxy) ? 0 : mHttpProxy.hashCode())
802                 + mStackedLinks.hashCode() * 47)
803                 + mMtu * 51;
804     }
805 
806     /**
807      * Implement the Parcelable interface.
808      */
809     public void writeToParcel(Parcel dest, int flags) {
810         dest.writeString(getInterfaceName());
811         dest.writeInt(mLinkAddresses.size());
812         for(LinkAddress linkAddress : mLinkAddresses) {
813             dest.writeParcelable(linkAddress, flags);
814         }
815 
816         dest.writeInt(mDnses.size());
817         for(InetAddress d : mDnses) {
818             dest.writeByteArray(d.getAddress());
819         }
820         dest.writeString(mDomains);
821         dest.writeInt(mMtu);
822         dest.writeInt(mRoutes.size());
823         for(RouteInfo route : mRoutes) {
824             dest.writeParcelable(route, flags);
825         }
826 
827         if (mHttpProxy != null) {
828             dest.writeByte((byte)1);
829             dest.writeParcelable(mHttpProxy, flags);
830         } else {
831             dest.writeByte((byte)0);
832         }
833         ArrayList<LinkProperties> stackedLinks = new ArrayList(mStackedLinks.values());
834         dest.writeList(stackedLinks);
835     }
836 
837     /**
838      * Implement the Parcelable interface.
839      */
840     public static final Creator<LinkProperties> CREATOR =
841         new Creator<LinkProperties>() {
842             public LinkProperties createFromParcel(Parcel in) {
843                 LinkProperties netProp = new LinkProperties();
844 
845                 String iface = in.readString();
846                 if (iface != null) {
847                     netProp.setInterfaceName(iface);
848                 }
849                 int addressCount = in.readInt();
850                 for (int i=0; i<addressCount; i++) {
851                     netProp.addLinkAddress((LinkAddress)in.readParcelable(null));
852                 }
853                 addressCount = in.readInt();
854                 for (int i=0; i<addressCount; i++) {
855                     try {
856                         netProp.addDnsServer(InetAddress.getByAddress(in.createByteArray()));
857                     } catch (UnknownHostException e) { }
858                 }
859                 netProp.setDomains(in.readString());
860                 netProp.setMtu(in.readInt());
861                 addressCount = in.readInt();
862                 for (int i=0; i<addressCount; i++) {
863                     netProp.addRoute((RouteInfo)in.readParcelable(null));
864                 }
865                 if (in.readByte() == 1) {
866                     netProp.setHttpProxy((ProxyInfo)in.readParcelable(null));
867                 }
868                 ArrayList<LinkProperties> stackedLinks = new ArrayList<LinkProperties>();
869                 in.readList(stackedLinks, LinkProperties.class.getClassLoader());
870                 for (LinkProperties stackedLink: stackedLinks) {
871                     netProp.addStackedLink(stackedLink);
872                 }
873                 return netProp;
874             }
875 
876             public LinkProperties[] newArray(int size) {
877                 return new LinkProperties[size];
878             }
879         };
880 }
881