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