• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.net;
18 
19 import android.net.ProxyProperties;
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 
27 import java.net.UnknownHostException;
28 import java.util.ArrayList;
29 import java.util.Collection;
30 import java.util.Collections;
31 import java.util.Hashtable;
32 
33 /**
34  * Describes the properties of a network link.
35  *
36  * A link represents a connection to a network.
37  * It may have multiple addresses and multiple gateways,
38  * multiple dns servers but only one http proxy.
39  *
40  * Because it's a single network, the dns's
41  * are interchangeable and don't need associating with
42  * particular addresses.  The gateways similarly don't
43  * need associating with particular addresses.
44  *
45  * A dual stack interface works fine in this model:
46  * each address has it's own prefix length to describe
47  * the local network.  The dns servers all return
48  * both v4 addresses and v6 addresses regardless of the
49  * address family of the server itself (rfc4213) and we
50  * don't care which is used.  The gateways will be
51  * selected based on the destination address and the
52  * source address has no relavence.
53  *
54  * Links can also be stacked on top of each other.
55  * This can be used, for example, to represent a tunnel
56  * interface that runs on top of a physical interface.
57  *
58  * @hide
59  */
60 public class LinkProperties implements Parcelable {
61     // The interface described by the network link.
62     private String mIfaceName;
63     private Collection<LinkAddress> mLinkAddresses = new ArrayList<LinkAddress>();
64     private Collection<InetAddress> mDnses = new ArrayList<InetAddress>();
65     private String mDomains;
66     private Collection<RouteInfo> mRoutes = new ArrayList<RouteInfo>();
67     private ProxyProperties mHttpProxy;
68 
69     // Stores the properties of links that are "stacked" above this link.
70     // Indexed by interface name to allow modification and to prevent duplicates being added.
71     private Hashtable<String, LinkProperties> mStackedLinks =
72         new Hashtable<String, LinkProperties>();
73 
74     public static class CompareResult<T> {
75         public Collection<T> removed = new ArrayList<T>();
76         public Collection<T> added = new ArrayList<T>();
77 
78         @Override
toString()79         public String toString() {
80             String retVal = "removed=[";
81             for (T addr : removed) retVal += addr.toString() + ",";
82             retVal += "] added=[";
83             for (T addr : added) retVal += addr.toString() + ",";
84             retVal += "]";
85             return retVal;
86         }
87     }
88 
LinkProperties()89     public LinkProperties() {
90         clear();
91     }
92 
93     // copy constructor instead of clone
LinkProperties(LinkProperties source)94     public LinkProperties(LinkProperties source) {
95         if (source != null) {
96             mIfaceName = source.getInterfaceName();
97             for (LinkAddress l : source.getLinkAddresses()) mLinkAddresses.add(l);
98             for (InetAddress i : source.getDnses()) mDnses.add(i);
99             mDomains = source.getDomains();
100             for (RouteInfo r : source.getRoutes()) mRoutes.add(r);
101             mHttpProxy = (source.getHttpProxy() == null)  ?
102                     null : new ProxyProperties(source.getHttpProxy());
103             for (LinkProperties l: source.mStackedLinks.values()) {
104                 addStackedLink(l);
105             }
106         }
107     }
108 
setInterfaceName(String iface)109     public void setInterfaceName(String iface) {
110         mIfaceName = iface;
111         ArrayList<RouteInfo> newRoutes = new ArrayList<RouteInfo>(mRoutes.size());
112         for (RouteInfo route : mRoutes) {
113             newRoutes.add(routeWithInterface(route));
114         }
115         mRoutes = newRoutes;
116     }
117 
getInterfaceName()118     public String getInterfaceName() {
119         return mIfaceName;
120     }
121 
getAllInterfaceNames()122     public Collection<String> getAllInterfaceNames() {
123         Collection interfaceNames = new ArrayList<String>(mStackedLinks.size() + 1);
124         if (mIfaceName != null) interfaceNames.add(new String(mIfaceName));
125         for (LinkProperties stacked: mStackedLinks.values()) {
126             interfaceNames.addAll(stacked.getAllInterfaceNames());
127         }
128         return interfaceNames;
129     }
130 
getAddresses()131     public Collection<InetAddress> getAddresses() {
132         Collection<InetAddress> addresses = new ArrayList<InetAddress>();
133         for (LinkAddress linkAddress : mLinkAddresses) {
134             addresses.add(linkAddress.getAddress());
135         }
136         return Collections.unmodifiableCollection(addresses);
137     }
138 
addLinkAddress(LinkAddress address)139     public void addLinkAddress(LinkAddress address) {
140         if (address != null) mLinkAddresses.add(address);
141     }
142 
getLinkAddresses()143     public Collection<LinkAddress> getLinkAddresses() {
144         return Collections.unmodifiableCollection(mLinkAddresses);
145     }
146 
addDns(InetAddress dns)147     public void addDns(InetAddress dns) {
148         if (dns != null) mDnses.add(dns);
149     }
150 
getDnses()151     public Collection<InetAddress> getDnses() {
152         return Collections.unmodifiableCollection(mDnses);
153     }
154 
getDomains()155     public String getDomains() {
156         return mDomains;
157     }
158 
setDomains(String domains)159     public void setDomains(String domains) {
160         mDomains = domains;
161     }
162 
routeWithInterface(RouteInfo route)163     private RouteInfo routeWithInterface(RouteInfo route) {
164         return new RouteInfo(
165             route.getDestination(),
166             route.getGateway(),
167             mIfaceName);
168     }
169 
addRoute(RouteInfo route)170     public void addRoute(RouteInfo route) {
171         if (route != null) {
172             String routeIface = route.getInterface();
173             if (routeIface != null && !routeIface.equals(mIfaceName)) {
174                 throw new IllegalArgumentException(
175                    "Route added with non-matching interface: " + routeIface +
176                    " vs. " + mIfaceName);
177             }
178             mRoutes.add(routeWithInterface(route));
179         }
180     }
181 
182     /**
183      * Returns all the routes on this link.
184      */
getRoutes()185     public Collection<RouteInfo> getRoutes() {
186         return Collections.unmodifiableCollection(mRoutes);
187     }
188 
189     /**
190      * Returns all the routes on this link and all the links stacked above it.
191      */
getAllRoutes()192     public Collection<RouteInfo> getAllRoutes() {
193         Collection<RouteInfo> routes = new ArrayList();
194         routes.addAll(mRoutes);
195         for (LinkProperties stacked: mStackedLinks.values()) {
196             routes.addAll(stacked.getAllRoutes());
197         }
198         return routes;
199     }
200 
setHttpProxy(ProxyProperties proxy)201     public void setHttpProxy(ProxyProperties proxy) {
202         mHttpProxy = proxy;
203     }
getHttpProxy()204     public ProxyProperties getHttpProxy() {
205         return mHttpProxy;
206     }
207 
208     /**
209      * Adds a stacked link.
210      *
211      * If there is already a stacked link with the same interfacename as link,
212      * that link is replaced with link. Otherwise, link is added to the list
213      * of stacked links. If link is null, nothing changes.
214      *
215      * @param link The link to add.
216      */
addStackedLink(LinkProperties link)217     public void addStackedLink(LinkProperties link) {
218         if (link != null && link.getInterfaceName() != null) {
219             mStackedLinks.put(link.getInterfaceName(), link);
220         }
221     }
222 
223     /**
224      * Removes a stacked link.
225      *
226      * If there a stacked link with the same interfacename as link, it is
227      * removed. Otherwise, nothing changes.
228      *
229      * @param link The link to add.
230      */
removeStackedLink(LinkProperties link)231     public void removeStackedLink(LinkProperties link) {
232         if (link != null && link.getInterfaceName() != null) {
233             mStackedLinks.remove(link.getInterfaceName());
234         }
235     }
236 
237     /**
238      * Returns all the links stacked on top of this link.
239      */
getStackedLinks()240     public Collection<LinkProperties> getStackedLinks() {
241         Collection<LinkProperties> stacked = new ArrayList<LinkProperties>();
242         for (LinkProperties link : mStackedLinks.values()) {
243           stacked.add(new LinkProperties(link));
244         }
245         return Collections.unmodifiableCollection(stacked);
246     }
247 
clear()248     public void clear() {
249         mIfaceName = null;
250         mLinkAddresses.clear();
251         mDnses.clear();
252         mDomains = null;
253         mRoutes.clear();
254         mHttpProxy = null;
255         mStackedLinks.clear();
256     }
257 
258     /**
259      * Implement the Parcelable interface
260      * @hide
261      */
describeContents()262     public int describeContents() {
263         return 0;
264     }
265 
266     @Override
toString()267     public String toString() {
268         String ifaceName = (mIfaceName == null ? "" : "InterfaceName: " + mIfaceName + " ");
269 
270         String linkAddresses = "LinkAddresses: [";
271         for (LinkAddress addr : mLinkAddresses) linkAddresses += addr.toString() + ",";
272         linkAddresses += "] ";
273 
274         String dns = "DnsAddresses: [";
275         for (InetAddress addr : mDnses) dns += addr.getHostAddress() + ",";
276         dns += "] ";
277 
278         String domainName = "Domains: " + mDomains;
279 
280         String routes = " Routes: [";
281         for (RouteInfo route : mRoutes) routes += route.toString() + ",";
282         routes += "] ";
283         String proxy = (mHttpProxy == null ? "" : "HttpProxy: " + mHttpProxy.toString() + " ");
284 
285         String stacked = "";
286         if (mStackedLinks.values().size() > 0) {
287             stacked += " Stacked: [";
288             for (LinkProperties link: mStackedLinks.values()) {
289                 stacked += " [" + link.toString() + " ],";
290             }
291             stacked += "] ";
292         }
293         return "{" + ifaceName + linkAddresses + routes + dns + domainName + proxy + stacked + "}";
294     }
295 
296     /**
297      * Returns true if this link has an IPv4 address.
298      *
299      * @return {@code true} if there is an IPv4 address, {@code false} otherwise.
300      */
hasIPv4Address()301     public boolean hasIPv4Address() {
302         for (LinkAddress address : mLinkAddresses) {
303           if (address.getAddress() instanceof Inet4Address) {
304             return true;
305           }
306         }
307         return false;
308     }
309 
310     /**
311      * Compares this {@code LinkProperties} interface name against the target
312      *
313      * @param target LinkProperties to compare.
314      * @return {@code true} if both are identical, {@code false} otherwise.
315      */
isIdenticalInterfaceName(LinkProperties target)316     public boolean isIdenticalInterfaceName(LinkProperties target) {
317         return TextUtils.equals(getInterfaceName(), target.getInterfaceName());
318     }
319 
320     /**
321      * Compares this {@code LinkProperties} interface addresses against the target
322      *
323      * @param target LinkProperties to compare.
324      * @return {@code true} if both are identical, {@code false} otherwise.
325      */
isIdenticalAddresses(LinkProperties target)326     public boolean isIdenticalAddresses(LinkProperties target) {
327         Collection<InetAddress> targetAddresses = target.getAddresses();
328         Collection<InetAddress> sourceAddresses = getAddresses();
329         return (sourceAddresses.size() == targetAddresses.size()) ?
330                     sourceAddresses.containsAll(targetAddresses) : false;
331     }
332 
333     /**
334      * Compares this {@code LinkProperties} DNS addresses against the target
335      *
336      * @param target LinkProperties to compare.
337      * @return {@code true} if both are identical, {@code false} otherwise.
338      */
isIdenticalDnses(LinkProperties target)339     public boolean isIdenticalDnses(LinkProperties target) {
340         Collection<InetAddress> targetDnses = target.getDnses();
341         String targetDomains = target.getDomains();
342         if (mDomains == null) {
343             if (targetDomains != null) return false;
344         } else {
345             if (mDomains.equals(targetDomains) == false) return false;
346         }
347         return (mDnses.size() == targetDnses.size()) ?
348                     mDnses.containsAll(targetDnses) : false;
349     }
350 
351     /**
352      * Compares this {@code LinkProperties} Routes against the target
353      *
354      * @param target LinkProperties to compare.
355      * @return {@code true} if both are identical, {@code false} otherwise.
356      */
isIdenticalRoutes(LinkProperties target)357     public boolean isIdenticalRoutes(LinkProperties target) {
358         Collection<RouteInfo> targetRoutes = target.getRoutes();
359         return (mRoutes.size() == targetRoutes.size()) ?
360                     mRoutes.containsAll(targetRoutes) : false;
361     }
362 
363     /**
364      * Compares this {@code LinkProperties} HttpProxy against the target
365      *
366      * @param target LinkProperties to compare.
367      * @return {@code true} if both are identical, {@code false} otherwise.
368      */
isIdenticalHttpProxy(LinkProperties target)369     public boolean isIdenticalHttpProxy(LinkProperties target) {
370         return getHttpProxy() == null ? target.getHttpProxy() == null :
371                     getHttpProxy().equals(target.getHttpProxy());
372     }
373 
374     /**
375      * Compares this {@code LinkProperties} stacked links against the target
376      *
377      * @param target LinkProperties to compare.
378      * @return {@code true} if both are identical, {@code false} otherwise.
379      */
isIdenticalStackedLinks(LinkProperties target)380     public boolean isIdenticalStackedLinks(LinkProperties target) {
381         if (!mStackedLinks.keySet().equals(target.mStackedLinks.keySet())) {
382             return false;
383         }
384         for (LinkProperties stacked : mStackedLinks.values()) {
385             // Hashtable values can never be null.
386             String iface = stacked.getInterfaceName();
387             if (!stacked.equals(target.mStackedLinks.get(iface))) {
388                 return false;
389             }
390         }
391         return true;
392     }
393 
394     @Override
395     /**
396      * Compares this {@code LinkProperties} instance against the target
397      * LinkProperties in {@code obj}. Two LinkPropertieses are equal if
398      * all their fields are equal in values.
399      *
400      * For collection fields, such as mDnses, containsAll() is used to check
401      * if two collections contains the same elements, independent of order.
402      * There are two thoughts regarding containsAll()
403      * 1. Duplicated elements. eg, (A, B, B) and (A, A, B) are equal.
404      * 2. Worst case performance is O(n^2).
405      *
406      * This method does not check that stacked interfaces are equal, because
407      * stacked interfaces are not so much a property of the link as a
408      * description of connections between links.
409      *
410      * @param obj the object to be tested for equality.
411      * @return {@code true} if both objects are equal, {@code false} otherwise.
412      */
equals(Object obj)413     public boolean equals(Object obj) {
414         if (this == obj) return true;
415 
416         if (!(obj instanceof LinkProperties)) return false;
417 
418         LinkProperties target = (LinkProperties) obj;
419 
420         return isIdenticalInterfaceName(target) &&
421                 isIdenticalAddresses(target) &&
422                 isIdenticalDnses(target) &&
423                 isIdenticalRoutes(target) &&
424                 isIdenticalHttpProxy(target) &&
425                 isIdenticalStackedLinks(target);
426     }
427 
428     /**
429      * Return two lists, a list of addresses that would be removed from
430      * mLinkAddresses and a list of addresses that would be added to
431      * mLinkAddress which would then result in target and mLinkAddresses
432      * being the same list.
433      *
434      * @param target is a LinkProperties with the new list of addresses
435      * @return the removed and added lists.
436      */
compareAddresses(LinkProperties target)437     public CompareResult<LinkAddress> compareAddresses(LinkProperties target) {
438         /*
439          * Duplicate the LinkAddresses into removed, we will be removing
440          * address which are common between mLinkAddresses and target
441          * leaving the addresses that are different. And address which
442          * are in target but not in mLinkAddresses are placed in the
443          * addedAddresses.
444          */
445         CompareResult<LinkAddress> result = new CompareResult<LinkAddress>();
446         result.removed = new ArrayList<LinkAddress>(mLinkAddresses);
447         result.added.clear();
448         if (target != null) {
449             for (LinkAddress newAddress : target.getLinkAddresses()) {
450                 if (! result.removed.remove(newAddress)) {
451                     result.added.add(newAddress);
452                 }
453             }
454         }
455         return result;
456     }
457 
458     /**
459      * Return two lists, a list of dns addresses that would be removed from
460      * mDnses and a list of addresses that would be added to
461      * mDnses which would then result in target and mDnses
462      * being the same list.
463      *
464      * @param target is a LinkProperties with the new list of dns addresses
465      * @return the removed and added lists.
466      */
compareDnses(LinkProperties target)467     public CompareResult<InetAddress> compareDnses(LinkProperties target) {
468         /*
469          * Duplicate the InetAddresses into removed, we will be removing
470          * dns address which are common between mDnses and target
471          * leaving the addresses that are different. And dns address which
472          * are in target but not in mDnses are placed in the
473          * addedAddresses.
474          */
475         CompareResult<InetAddress> result = new CompareResult<InetAddress>();
476 
477         result.removed = new ArrayList<InetAddress>(mDnses);
478         result.added.clear();
479         if (target != null) {
480             for (InetAddress newAddress : target.getDnses()) {
481                 if (! result.removed.remove(newAddress)) {
482                     result.added.add(newAddress);
483                 }
484             }
485         }
486         return result;
487     }
488 
489     /**
490      * Return two lists, a list of routes that would be removed from
491      * mRoutes and a list of routes that would be added to
492      * mRoutes which would then result in target and mRoutes
493      * being the same list.
494      *
495      * @param target is a LinkProperties with the new list of routes
496      * @return the removed and added lists.
497      */
compareRoutes(LinkProperties target)498     public CompareResult<RouteInfo> compareRoutes(LinkProperties target) {
499         /*
500          * Duplicate the RouteInfos into removed, we will be removing
501          * routes which are common between mRoutes and target
502          * leaving the routes that are different. And route address which
503          * are in target but not in mRoutes are placed in added.
504          */
505         CompareResult<RouteInfo> result = new CompareResult<RouteInfo>();
506 
507         result.removed = getAllRoutes();
508         result.added.clear();
509         if (target != null) {
510             for (RouteInfo r : target.getAllRoutes()) {
511                 if (! result.removed.remove(r)) {
512                     result.added.add(r);
513                 }
514             }
515         }
516         return result;
517     }
518 
519 
520     @Override
521     /**
522      * generate hashcode based on significant fields
523      * Equal objects must produce the same hash code, while unequal objects
524      * may have the same hash codes.
525      */
hashCode()526     public int hashCode() {
527         return ((null == mIfaceName) ? 0 : mIfaceName.hashCode()
528                 + mLinkAddresses.size() * 31
529                 + mDnses.size() * 37
530                 + ((null == mDomains) ? 0 : mDomains.hashCode())
531                 + mRoutes.size() * 41
532                 + ((null == mHttpProxy) ? 0 : mHttpProxy.hashCode())
533                 + mStackedLinks.hashCode() * 47);
534     }
535 
536     /**
537      * Implement the Parcelable interface.
538      */
writeToParcel(Parcel dest, int flags)539     public void writeToParcel(Parcel dest, int flags) {
540         dest.writeString(getInterfaceName());
541         dest.writeInt(mLinkAddresses.size());
542         for(LinkAddress linkAddress : mLinkAddresses) {
543             dest.writeParcelable(linkAddress, flags);
544         }
545 
546         dest.writeInt(mDnses.size());
547         for(InetAddress d : mDnses) {
548             dest.writeByteArray(d.getAddress());
549         }
550         dest.writeString(mDomains);
551 
552         dest.writeInt(mRoutes.size());
553         for(RouteInfo route : mRoutes) {
554             dest.writeParcelable(route, flags);
555         }
556 
557         if (mHttpProxy != null) {
558             dest.writeByte((byte)1);
559             dest.writeParcelable(mHttpProxy, flags);
560         } else {
561             dest.writeByte((byte)0);
562         }
563         ArrayList<LinkProperties> stackedLinks = new ArrayList(mStackedLinks.values());
564         dest.writeList(stackedLinks);
565     }
566 
567     /**
568      * Implement the Parcelable interface.
569      */
570     public static final Creator<LinkProperties> CREATOR =
571         new Creator<LinkProperties>() {
572             public LinkProperties createFromParcel(Parcel in) {
573                 LinkProperties netProp = new LinkProperties();
574 
575                 String iface = in.readString();
576                 if (iface != null) {
577                     netProp.setInterfaceName(iface);
578                 }
579                 int addressCount = in.readInt();
580                 for (int i=0; i<addressCount; i++) {
581                     netProp.addLinkAddress((LinkAddress)in.readParcelable(null));
582                 }
583                 addressCount = in.readInt();
584                 for (int i=0; i<addressCount; i++) {
585                     try {
586                         netProp.addDns(InetAddress.getByAddress(in.createByteArray()));
587                     } catch (UnknownHostException e) { }
588                 }
589                 netProp.setDomains(in.readString());
590                 addressCount = in.readInt();
591                 for (int i=0; i<addressCount; i++) {
592                     netProp.addRoute((RouteInfo)in.readParcelable(null));
593                 }
594                 if (in.readByte() == 1) {
595                     netProp.setHttpProxy((ProxyProperties)in.readParcelable(null));
596                 }
597                 ArrayList<LinkProperties> stackedLinks = new ArrayList<LinkProperties>();
598                 in.readList(stackedLinks, LinkProperties.class.getClassLoader());
599                 for (LinkProperties stackedLink: stackedLinks) {
600                     netProp.addStackedLink(stackedLink);
601                 }
602                 return netProp;
603             }
604 
605             public LinkProperties[] newArray(int size) {
606                 return new LinkProperties[size];
607             }
608         };
609 }
610