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