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.UnknownHostException; 26 import java.util.ArrayList; 27 import java.util.Collection; 28 import java.util.Collections; 29 30 /** 31 * Describes the properties of a network link. 32 * 33 * A link represents a connection to a network. 34 * It may have multiple addresses and multiple gateways, 35 * multiple dns servers but only one http proxy. 36 * 37 * Because it's a single network, the dns's 38 * are interchangeable and don't need associating with 39 * particular addresses. The gateways similarly don't 40 * need associating with particular addresses. 41 * 42 * A dual stack interface works fine in this model: 43 * each address has it's own prefix length to describe 44 * the local network. The dns servers all return 45 * both v4 addresses and v6 addresses regardless of the 46 * address family of the server itself (rfc4213) and we 47 * don't care which is used. The gateways will be 48 * selected based on the destination address and the 49 * source address has no relavence. 50 * @hide 51 */ 52 public class LinkProperties implements Parcelable { 53 54 String mIfaceName; 55 private Collection<LinkAddress> mLinkAddresses = new ArrayList<LinkAddress>(); 56 private Collection<InetAddress> mDnses = new ArrayList<InetAddress>(); 57 private Collection<RouteInfo> mRoutes = new ArrayList<RouteInfo>(); 58 private ProxyProperties mHttpProxy; 59 60 public static class CompareResult<T> { 61 public Collection<T> removed = new ArrayList<T>(); 62 public Collection<T> added = new ArrayList<T>(); 63 64 @Override 65 public String toString() { 66 String retVal = "removed=["; 67 for (T addr : removed) retVal += addr.toString() + ","; 68 retVal += "] added=["; 69 for (T addr : added) retVal += addr.toString() + ","; 70 retVal += "]"; 71 return retVal; 72 } 73 } 74 75 public LinkProperties() { 76 clear(); 77 } 78 79 // copy constructor instead of clone 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 for (RouteInfo r : source.getRoutes()) mRoutes.add(r); 86 mHttpProxy = (source.getHttpProxy() == null) ? 87 null : new ProxyProperties(source.getHttpProxy()); 88 } 89 } 90 91 public void setInterfaceName(String iface) { 92 mIfaceName = iface; 93 } 94 95 public String getInterfaceName() { 96 return mIfaceName; 97 } 98 99 public Collection<InetAddress> getAddresses() { 100 Collection<InetAddress> addresses = new ArrayList<InetAddress>(); 101 for (LinkAddress linkAddress : mLinkAddresses) { 102 addresses.add(linkAddress.getAddress()); 103 } 104 return Collections.unmodifiableCollection(addresses); 105 } 106 107 public void addLinkAddress(LinkAddress address) { 108 if (address != null) mLinkAddresses.add(address); 109 } 110 111 public Collection<LinkAddress> getLinkAddresses() { 112 return Collections.unmodifiableCollection(mLinkAddresses); 113 } 114 115 public void addDns(InetAddress dns) { 116 if (dns != null) mDnses.add(dns); 117 } 118 119 public Collection<InetAddress> getDnses() { 120 return Collections.unmodifiableCollection(mDnses); 121 } 122 123 public void addRoute(RouteInfo route) { 124 if (route != null) mRoutes.add(route); 125 } 126 public Collection<RouteInfo> getRoutes() { 127 return Collections.unmodifiableCollection(mRoutes); 128 } 129 130 public void setHttpProxy(ProxyProperties proxy) { 131 mHttpProxy = proxy; 132 } 133 public ProxyProperties getHttpProxy() { 134 return mHttpProxy; 135 } 136 137 public void clear() { 138 mIfaceName = null; 139 mLinkAddresses.clear(); 140 mDnses.clear(); 141 mRoutes.clear(); 142 mHttpProxy = null; 143 } 144 145 /** 146 * Implement the Parcelable interface 147 * @hide 148 */ 149 public int describeContents() { 150 return 0; 151 } 152 153 @Override 154 public String toString() { 155 String ifaceName = (mIfaceName == null ? "" : "InterfaceName: " + mIfaceName + " "); 156 157 String linkAddresses = "LinkAddresses: ["; 158 for (LinkAddress addr : mLinkAddresses) linkAddresses += addr.toString() + ","; 159 linkAddresses += "] "; 160 161 String dns = "DnsAddresses: ["; 162 for (InetAddress addr : mDnses) dns += addr.getHostAddress() + ","; 163 dns += "] "; 164 165 String routes = "Routes: ["; 166 for (RouteInfo route : mRoutes) routes += route.toString() + ","; 167 routes += "] "; 168 String proxy = (mHttpProxy == null ? "" : "HttpProxy: " + mHttpProxy.toString() + " "); 169 170 return ifaceName + linkAddresses + routes + dns + proxy; 171 } 172 173 /** 174 * Compares this {@code LinkProperties} interface name against the target 175 * 176 * @param target LinkProperties to compare. 177 * @return {@code true} if both are identical, {@code false} otherwise. 178 */ 179 public boolean isIdenticalInterfaceName(LinkProperties target) { 180 return TextUtils.equals(getInterfaceName(), target.getInterfaceName()); 181 } 182 183 /** 184 * Compares this {@code LinkProperties} interface name against the target 185 * 186 * @param target LinkProperties to compare. 187 * @return {@code true} if both are identical, {@code false} otherwise. 188 */ 189 public boolean isIdenticalAddresses(LinkProperties target) { 190 Collection<InetAddress> targetAddresses = target.getAddresses(); 191 Collection<InetAddress> sourceAddresses = getAddresses(); 192 return (sourceAddresses.size() == targetAddresses.size()) ? 193 sourceAddresses.containsAll(targetAddresses) : false; 194 } 195 196 /** 197 * Compares this {@code LinkProperties} DNS addresses against the target 198 * 199 * @param target LinkProperties to compare. 200 * @return {@code true} if both are identical, {@code false} otherwise. 201 */ 202 public boolean isIdenticalDnses(LinkProperties target) { 203 Collection<InetAddress> targetDnses = target.getDnses(); 204 return (mDnses.size() == targetDnses.size()) ? 205 mDnses.containsAll(targetDnses) : false; 206 } 207 208 /** 209 * Compares this {@code LinkProperties} Routes against the target 210 * 211 * @param target LinkProperties to compare. 212 * @return {@code true} if both are identical, {@code false} otherwise. 213 */ 214 public boolean isIdenticalRoutes(LinkProperties target) { 215 Collection<RouteInfo> targetRoutes = target.getRoutes(); 216 return (mRoutes.size() == targetRoutes.size()) ? 217 mRoutes.containsAll(targetRoutes) : false; 218 } 219 220 /** 221 * Compares this {@code LinkProperties} HttpProxy against the target 222 * 223 * @param target LinkProperties to compare. 224 * @return {@code true} if both are identical, {@code false} otherwise. 225 */ 226 public boolean isIdenticalHttpProxy(LinkProperties target) { 227 return getHttpProxy() == null ? target.getHttpProxy() == null : 228 getHttpProxy().equals(target.getHttpProxy()); 229 } 230 231 @Override 232 /** 233 * Compares this {@code LinkProperties} instance against the target 234 * LinkProperties in {@code obj}. Two LinkPropertieses are equal if 235 * all their fields are equal in values. 236 * 237 * For collection fields, such as mDnses, containsAll() is used to check 238 * if two collections contains the same elements, independent of order. 239 * There are two thoughts regarding containsAll() 240 * 1. Duplicated elements. eg, (A, B, B) and (A, A, B) are equal. 241 * 2. Worst case performance is O(n^2). 242 * 243 * @param obj the object to be tested for equality. 244 * @return {@code true} if both objects are equal, {@code false} otherwise. 245 */ 246 public boolean equals(Object obj) { 247 if (this == obj) return true; 248 249 if (!(obj instanceof LinkProperties)) return false; 250 251 LinkProperties target = (LinkProperties) obj; 252 253 return isIdenticalInterfaceName(target) && 254 isIdenticalAddresses(target) && 255 isIdenticalDnses(target) && 256 isIdenticalRoutes(target) && 257 isIdenticalHttpProxy(target); 258 } 259 260 /** 261 * Return two lists, a list of addresses that would be removed from 262 * mLinkAddresses and a list of addresses that would be added to 263 * mLinkAddress which would then result in target and mLinkAddresses 264 * being the same list. 265 * 266 * @param target is a LinkProperties with the new list of addresses 267 * @return the removed and added lists. 268 */ 269 public CompareResult<LinkAddress> compareAddresses(LinkProperties target) { 270 /* 271 * Duplicate the LinkAddresses into removed, we will be removing 272 * address which are common between mLinkAddresses and target 273 * leaving the addresses that are different. And address which 274 * are in target but not in mLinkAddresses are placed in the 275 * addedAddresses. 276 */ 277 CompareResult<LinkAddress> result = new CompareResult<LinkAddress>(); 278 result.removed = new ArrayList<LinkAddress>(mLinkAddresses); 279 result.added.clear(); 280 if (target != null) { 281 for (LinkAddress newAddress : target.getLinkAddresses()) { 282 if (! result.removed.remove(newAddress)) { 283 result.added.add(newAddress); 284 } 285 } 286 } 287 return result; 288 } 289 290 /** 291 * Return two lists, a list of dns addresses that would be removed from 292 * mDnses and a list of addresses that would be added to 293 * mDnses which would then result in target and mDnses 294 * being the same list. 295 * 296 * @param target is a LinkProperties with the new list of dns addresses 297 * @return the removed and added lists. 298 */ 299 public CompareResult<InetAddress> compareDnses(LinkProperties target) { 300 /* 301 * Duplicate the InetAddresses into removed, we will be removing 302 * dns address which are common between mDnses and target 303 * leaving the addresses that are different. And dns address which 304 * are in target but not in mDnses are placed in the 305 * addedAddresses. 306 */ 307 CompareResult<InetAddress> result = new CompareResult<InetAddress>(); 308 309 result.removed = new ArrayList<InetAddress>(mDnses); 310 result.added.clear(); 311 if (target != null) { 312 for (InetAddress newAddress : target.getDnses()) { 313 if (! result.removed.remove(newAddress)) { 314 result.added.add(newAddress); 315 } 316 } 317 } 318 return result; 319 } 320 321 /** 322 * Return two lists, a list of routes that would be removed from 323 * mRoutes and a list of routes that would be added to 324 * mRoutes which would then result in target and mRoutes 325 * being the same list. 326 * 327 * @param target is a LinkProperties with the new list of routes 328 * @return the removed and added lists. 329 */ 330 public CompareResult<RouteInfo> compareRoutes(LinkProperties target) { 331 /* 332 * Duplicate the RouteInfos into removed, we will be removing 333 * routes which are common between mDnses and target 334 * leaving the routes that are different. And route address which 335 * are in target but not in mRoutes are placed in added. 336 */ 337 CompareResult<RouteInfo> result = new CompareResult<RouteInfo>(); 338 339 result.removed = new ArrayList<RouteInfo>(mRoutes); 340 result.added.clear(); 341 if (target != null) { 342 for (RouteInfo r : target.getRoutes()) { 343 if (! result.removed.remove(r)) { 344 result.added.add(r); 345 } 346 } 347 } 348 return result; 349 } 350 351 352 @Override 353 /** 354 * generate hashcode based on significant fields 355 * Equal objects must produce the same hash code, while unequal objects 356 * may have the same hash codes. 357 */ 358 public int hashCode() { 359 return ((null == mIfaceName) ? 0 : mIfaceName.hashCode() 360 + mLinkAddresses.size() * 31 361 + mDnses.size() * 37 362 + mRoutes.size() * 41 363 + ((null == mHttpProxy) ? 0 : mHttpProxy.hashCode())); 364 } 365 366 /** 367 * Implement the Parcelable interface. 368 * @hide 369 */ 370 public void writeToParcel(Parcel dest, int flags) { 371 dest.writeString(getInterfaceName()); 372 dest.writeInt(mLinkAddresses.size()); 373 for(LinkAddress linkAddress : mLinkAddresses) { 374 dest.writeParcelable(linkAddress, flags); 375 } 376 377 dest.writeInt(mDnses.size()); 378 for(InetAddress d : mDnses) { 379 dest.writeByteArray(d.getAddress()); 380 } 381 382 dest.writeInt(mRoutes.size()); 383 for(RouteInfo route : mRoutes) { 384 dest.writeParcelable(route, flags); 385 } 386 387 if (mHttpProxy != null) { 388 dest.writeByte((byte)1); 389 dest.writeParcelable(mHttpProxy, flags); 390 } else { 391 dest.writeByte((byte)0); 392 } 393 } 394 395 /** 396 * Implement the Parcelable interface. 397 * @hide 398 */ 399 public static final Creator<LinkProperties> CREATOR = 400 new Creator<LinkProperties>() { 401 public LinkProperties createFromParcel(Parcel in) { 402 LinkProperties netProp = new LinkProperties(); 403 String iface = in.readString(); 404 if (iface != null) { 405 try { 406 netProp.setInterfaceName(iface); 407 } catch (Exception e) { 408 return null; 409 } 410 } 411 int addressCount = in.readInt(); 412 for (int i=0; i<addressCount; i++) { 413 netProp.addLinkAddress((LinkAddress)in.readParcelable(null)); 414 } 415 addressCount = in.readInt(); 416 for (int i=0; i<addressCount; i++) { 417 try { 418 netProp.addDns(InetAddress.getByAddress(in.createByteArray())); 419 } catch (UnknownHostException e) { } 420 } 421 addressCount = in.readInt(); 422 for (int i=0; i<addressCount; i++) { 423 netProp.addRoute((RouteInfo)in.readParcelable(null)); 424 } 425 if (in.readByte() == 1) { 426 netProp.setHttpProxy((ProxyProperties)in.readParcelable(null)); 427 } 428 return netProp; 429 } 430 431 public LinkProperties[] newArray(int size) { 432 return new LinkProperties[size]; 433 } 434 }; 435 } 436