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