Home | History | Annotate | Download | only in net
      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
     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 
     89     public LinkProperties() {
     90         clear();
     91     }
     92 
     93     // copy constructor instead of clone
     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 
    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 
    118     public String getInterfaceName() {
    119         return mIfaceName;
    120     }
    121 
    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 
    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 
    139     public void addLinkAddress(LinkAddress address) {
    140         if (address != null) mLinkAddresses.add(address);
    141     }
    142 
    143     public Collection<LinkAddress> getLinkAddresses() {
    144         return Collections.unmodifiableCollection(mLinkAddresses);
    145     }
    146 
    147     public void addDns(InetAddress dns) {
    148         if (dns != null) mDnses.add(dns);
    149     }
    150 
    151     public Collection<InetAddress> getDnses() {
    152         return Collections.unmodifiableCollection(mDnses);
    153     }
    154 
    155     public String getDomains() {
    156         return mDomains;
    157     }
    158 
    159     public void setDomains(String domains) {
    160         mDomains = domains;
    161     }
    162 
    163     private RouteInfo routeWithInterface(RouteInfo route) {
    164         return new RouteInfo(
    165             route.getDestination(),
    166             route.getGateway(),
    167             mIfaceName);
    168     }
    169 
    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      */
    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      */
    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 
    201     public void setHttpProxy(ProxyProperties proxy) {
    202         mHttpProxy = proxy;
    203     }
    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      */
    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      */
    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      */
    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 
    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      */
    262     public int describeContents() {
    263         return 0;
    264     }
    265 
    266     @Override
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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