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