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.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.net.ProxyInfo; 22 import android.os.Parcelable; 23 import android.os.Parcel; 24 import android.text.TextUtils; 25 26 import java.net.InetAddress; 27 import java.net.Inet4Address; 28 import java.net.Inet6Address; 29 import java.net.UnknownHostException; 30 import java.util.ArrayList; 31 import java.util.Collection; 32 import java.util.Collections; 33 import java.util.Hashtable; 34 import java.util.List; 35 import 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 */ 49 public 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 route.getType()); 386 } 387 388 /** 389 * Adds a {@link RouteInfo} to this {@code LinkProperties}, if not present. If the 390 * {@link RouteInfo} had an interface name set and that differs from the interface set for this 391 * {@code LinkProperties} an {@link IllegalArgumentException} will be thrown. The proper 392 * course is to add either un-named or properly named {@link RouteInfo}. 393 * 394 * @param route A {@link RouteInfo} to add to this object. 395 * @return {@code false} if the route was already present, {@code true} if it was added. 396 * 397 * @hide 398 */ 399 public boolean addRoute(RouteInfo route) { 400 if (route != null) { 401 String routeIface = route.getInterface(); 402 if (routeIface != null && !routeIface.equals(mIfaceName)) { 403 throw new IllegalArgumentException( 404 "Route added with non-matching interface: " + routeIface + 405 " vs. " + mIfaceName); 406 } 407 route = routeWithInterface(route); 408 if (!mRoutes.contains(route)) { 409 mRoutes.add(route); 410 return true; 411 } 412 } 413 return false; 414 } 415 416 /** 417 * Removes a {@link RouteInfo} from this {@code LinkProperties}, if present. The route must 418 * specify an interface and the interface must match the interface of this 419 * {@code LinkProperties}, or it will not be removed. 420 * 421 * @return {@code true} if the route was removed, {@code false} if it was not present. 422 * 423 * @hide 424 */ 425 public boolean removeRoute(RouteInfo route) { 426 return route != null && 427 Objects.equals(mIfaceName, route.getInterface()) && 428 mRoutes.remove(route); 429 } 430 431 /** 432 * Returns all the {@link RouteInfo} set on this link. 433 * 434 * @return An unmodifiable {@link List} of {@link RouteInfo} for this link. 435 */ 436 public List<RouteInfo> getRoutes() { 437 return Collections.unmodifiableList(mRoutes); 438 } 439 440 /** 441 * Returns all the routes on this link and all the links stacked above it. 442 * @hide 443 */ 444 public List<RouteInfo> getAllRoutes() { 445 List<RouteInfo> routes = new ArrayList(); 446 routes.addAll(mRoutes); 447 for (LinkProperties stacked: mStackedLinks.values()) { 448 routes.addAll(stacked.getAllRoutes()); 449 } 450 return routes; 451 } 452 453 /** 454 * Sets the recommended {@link ProxyInfo} to use on this link, or {@code null} for none. 455 * Note that Http Proxies are only a hint - the system recommends their use, but it does 456 * not enforce it and applications may ignore them. 457 * 458 * @param proxy A {@link ProxyInfo} defining the Http Proxy to use on this link. 459 * @hide 460 */ 461 public void setHttpProxy(ProxyInfo proxy) { 462 mHttpProxy = proxy; 463 } 464 465 /** 466 * Gets the recommended {@link ProxyInfo} (or {@code null}) set on this link. 467 * 468 * @return The {@link ProxyInfo} set on this link 469 */ 470 public ProxyInfo getHttpProxy() { 471 return mHttpProxy; 472 } 473 474 /** 475 * Adds a stacked link. 476 * 477 * If there is already a stacked link with the same interfacename as link, 478 * that link is replaced with link. Otherwise, link is added to the list 479 * of stacked links. If link is null, nothing changes. 480 * 481 * @param link The link to add. 482 * @return true if the link was stacked, false otherwise. 483 * @hide 484 */ 485 public boolean addStackedLink(LinkProperties link) { 486 if (link != null && link.getInterfaceName() != null) { 487 mStackedLinks.put(link.getInterfaceName(), link); 488 return true; 489 } 490 return false; 491 } 492 493 /** 494 * Removes a stacked link. 495 * 496 * If there a stacked link with the same interfacename as link, it is 497 * removed. Otherwise, nothing changes. 498 * 499 * @param link The link to remove. 500 * @return true if the link was removed, false otherwise. 501 * @hide 502 */ 503 public boolean removeStackedLink(LinkProperties link) { 504 if (link != null && link.getInterfaceName() != null) { 505 LinkProperties removed = mStackedLinks.remove(link.getInterfaceName()); 506 return removed != null; 507 } 508 return false; 509 } 510 511 /** 512 * Returns all the links stacked on top of this link. 513 * @hide 514 */ 515 public @NonNull List<LinkProperties> getStackedLinks() { 516 if (mStackedLinks.isEmpty()) { 517 return Collections.EMPTY_LIST; 518 } 519 List<LinkProperties> stacked = new ArrayList<LinkProperties>(); 520 for (LinkProperties link : mStackedLinks.values()) { 521 stacked.add(new LinkProperties(link)); 522 } 523 return Collections.unmodifiableList(stacked); 524 } 525 526 /** 527 * Clears this object to its initial state. 528 * @hide 529 */ 530 public void clear() { 531 mIfaceName = null; 532 mLinkAddresses.clear(); 533 mDnses.clear(); 534 mDomains = null; 535 mRoutes.clear(); 536 mHttpProxy = null; 537 mStackedLinks.clear(); 538 mMtu = 0; 539 mTcpBufferSizes = null; 540 } 541 542 /** 543 * Implement the Parcelable interface 544 */ 545 public int describeContents() { 546 return 0; 547 } 548 549 @Override 550 public String toString() { 551 String ifaceName = (mIfaceName == null ? "" : "InterfaceName: " + mIfaceName + " "); 552 553 String linkAddresses = "LinkAddresses: ["; 554 for (LinkAddress addr : mLinkAddresses) linkAddresses += addr.toString() + ","; 555 linkAddresses += "] "; 556 557 String dns = "DnsAddresses: ["; 558 for (InetAddress addr : mDnses) dns += addr.getHostAddress() + ","; 559 dns += "] "; 560 561 String domainName = "Domains: " + mDomains; 562 563 String mtu = " MTU: " + mMtu; 564 565 String tcpBuffSizes = ""; 566 if (mTcpBufferSizes != null) { 567 tcpBuffSizes = " TcpBufferSizes: " + mTcpBufferSizes; 568 } 569 570 String routes = " Routes: ["; 571 for (RouteInfo route : mRoutes) routes += route.toString() + ","; 572 routes += "] "; 573 String proxy = (mHttpProxy == null ? "" : " HttpProxy: " + mHttpProxy.toString() + " "); 574 575 String stacked = ""; 576 if (mStackedLinks.values().size() > 0) { 577 stacked += " Stacked: ["; 578 for (LinkProperties link: mStackedLinks.values()) { 579 stacked += " [" + link.toString() + " ],"; 580 } 581 stacked += "] "; 582 } 583 return "{" + ifaceName + linkAddresses + routes + dns + domainName + mtu 584 + tcpBuffSizes + proxy + stacked + "}"; 585 } 586 587 /** 588 * Returns true if this link has an IPv4 address. 589 * 590 * @return {@code true} if there is an IPv4 address, {@code false} otherwise. 591 * @hide 592 */ 593 public boolean hasIPv4Address() { 594 for (LinkAddress address : mLinkAddresses) { 595 if (address.getAddress() instanceof Inet4Address) { 596 return true; 597 } 598 } 599 return false; 600 } 601 602 /** 603 * Returns true if this link has a global preferred IPv6 address. 604 * 605 * @return {@code true} if there is a global preferred IPv6 address, {@code false} otherwise. 606 * @hide 607 */ 608 public boolean hasGlobalIPv6Address() { 609 for (LinkAddress address : mLinkAddresses) { 610 if (address.getAddress() instanceof Inet6Address && address.isGlobalPreferred()) { 611 return true; 612 } 613 } 614 return false; 615 } 616 617 /** 618 * Returns true if this link has an IPv4 default route. 619 * 620 * @return {@code true} if there is an IPv4 default route, {@code false} otherwise. 621 * @hide 622 */ 623 public boolean hasIPv4DefaultRoute() { 624 for (RouteInfo r : mRoutes) { 625 if (r.isIPv4Default()) { 626 return true; 627 } 628 } 629 return false; 630 } 631 632 /** 633 * Returns true if this link has an IPv6 default route. 634 * 635 * @return {@code true} if there is an IPv6 default route, {@code false} otherwise. 636 * @hide 637 */ 638 public boolean hasIPv6DefaultRoute() { 639 for (RouteInfo r : mRoutes) { 640 if (r.isIPv6Default()) { 641 return true; 642 } 643 } 644 return false; 645 } 646 647 /** 648 * Returns true if this link has an IPv4 DNS server. 649 * 650 * @return {@code true} if there is an IPv4 DNS server, {@code false} otherwise. 651 * @hide 652 */ 653 public boolean hasIPv4DnsServer() { 654 for (InetAddress ia : mDnses) { 655 if (ia instanceof Inet4Address) { 656 return true; 657 } 658 } 659 return false; 660 } 661 662 /** 663 * Returns true if this link has an IPv6 DNS server. 664 * 665 * @return {@code true} if there is an IPv6 DNS server, {@code false} otherwise. 666 * @hide 667 */ 668 public boolean hasIPv6DnsServer() { 669 for (InetAddress ia : mDnses) { 670 if (ia instanceof Inet6Address) { 671 return true; 672 } 673 } 674 return false; 675 } 676 677 /** 678 * Returns true if this link is provisioned for global connectivity. For IPv6, this requires an 679 * IP address, default route, and DNS server. For IPv4, this requires only an IPv4 address, 680 * because WifiStateMachine accepts static configurations that only specify an address but not 681 * DNS servers or a default route. 682 * 683 * @return {@code true} if the link is provisioned, {@code false} otherwise. 684 * @hide 685 */ 686 public boolean isProvisioned() { 687 return (hasIPv4Address() || 688 (hasGlobalIPv6Address() && hasIPv6DefaultRoute() && hasIPv6DnsServer())); 689 } 690 691 /** 692 * Compares this {@code LinkProperties} interface name against the target 693 * 694 * @param target LinkProperties to compare. 695 * @return {@code true} if both are identical, {@code false} otherwise. 696 * @hide 697 */ 698 public boolean isIdenticalInterfaceName(LinkProperties target) { 699 return TextUtils.equals(getInterfaceName(), target.getInterfaceName()); 700 } 701 702 /** 703 * Compares this {@code LinkProperties} interface addresses against the target 704 * 705 * @param target LinkProperties to compare. 706 * @return {@code true} if both are identical, {@code false} otherwise. 707 * @hide 708 */ 709 public boolean isIdenticalAddresses(LinkProperties target) { 710 Collection<InetAddress> targetAddresses = target.getAddresses(); 711 Collection<InetAddress> sourceAddresses = getAddresses(); 712 return (sourceAddresses.size() == targetAddresses.size()) ? 713 sourceAddresses.containsAll(targetAddresses) : false; 714 } 715 716 /** 717 * Compares this {@code LinkProperties} DNS addresses against the target 718 * 719 * @param target LinkProperties to compare. 720 * @return {@code true} if both are identical, {@code false} otherwise. 721 * @hide 722 */ 723 public boolean isIdenticalDnses(LinkProperties target) { 724 Collection<InetAddress> targetDnses = target.getDnsServers(); 725 String targetDomains = target.getDomains(); 726 if (mDomains == null) { 727 if (targetDomains != null) return false; 728 } else { 729 if (mDomains.equals(targetDomains) == false) return false; 730 } 731 return (mDnses.size() == targetDnses.size()) ? 732 mDnses.containsAll(targetDnses) : false; 733 } 734 735 /** 736 * Compares this {@code LinkProperties} Routes against the target 737 * 738 * @param target LinkProperties to compare. 739 * @return {@code true} if both are identical, {@code false} otherwise. 740 * @hide 741 */ 742 public boolean isIdenticalRoutes(LinkProperties target) { 743 Collection<RouteInfo> targetRoutes = target.getRoutes(); 744 return (mRoutes.size() == targetRoutes.size()) ? 745 mRoutes.containsAll(targetRoutes) : false; 746 } 747 748 /** 749 * Compares this {@code LinkProperties} HttpProxy against the target 750 * 751 * @param target LinkProperties to compare. 752 * @return {@code true} if both are identical, {@code false} otherwise. 753 * @hide 754 */ 755 public boolean isIdenticalHttpProxy(LinkProperties target) { 756 return getHttpProxy() == null ? target.getHttpProxy() == null : 757 getHttpProxy().equals(target.getHttpProxy()); 758 } 759 760 /** 761 * Compares this {@code LinkProperties} stacked links against the target 762 * 763 * @param target LinkProperties to compare. 764 * @return {@code true} if both are identical, {@code false} otherwise. 765 * @hide 766 */ 767 public boolean isIdenticalStackedLinks(LinkProperties target) { 768 if (!mStackedLinks.keySet().equals(target.mStackedLinks.keySet())) { 769 return false; 770 } 771 for (LinkProperties stacked : mStackedLinks.values()) { 772 // Hashtable values can never be null. 773 String iface = stacked.getInterfaceName(); 774 if (!stacked.equals(target.mStackedLinks.get(iface))) { 775 return false; 776 } 777 } 778 return true; 779 } 780 781 /** 782 * Compares this {@code LinkProperties} MTU against the target 783 * 784 * @param target LinkProperties to compare. 785 * @return {@code true} if both are identical, {@code false} otherwise. 786 * @hide 787 */ 788 public boolean isIdenticalMtu(LinkProperties target) { 789 return getMtu() == target.getMtu(); 790 } 791 792 /** 793 * Compares this {@code LinkProperties} Tcp buffer sizes against the target. 794 * 795 * @param target LinkProperties to compare. 796 * @return {@code true} if both are identical, {@code false} otherwise. 797 * @hide 798 */ 799 public boolean isIdenticalTcpBufferSizes(LinkProperties target) { 800 return Objects.equals(mTcpBufferSizes, target.mTcpBufferSizes); 801 } 802 803 @Override 804 /** 805 * Compares this {@code LinkProperties} instance against the target 806 * LinkProperties in {@code obj}. Two LinkPropertieses are equal if 807 * all their fields are equal in values. 808 * 809 * For collection fields, such as mDnses, containsAll() is used to check 810 * if two collections contains the same elements, independent of order. 811 * There are two thoughts regarding containsAll() 812 * 1. Duplicated elements. eg, (A, B, B) and (A, A, B) are equal. 813 * 2. Worst case performance is O(n^2). 814 * 815 * @param obj the object to be tested for equality. 816 * @return {@code true} if both objects are equal, {@code false} otherwise. 817 */ 818 public boolean equals(Object obj) { 819 if (this == obj) return true; 820 821 if (!(obj instanceof LinkProperties)) return false; 822 823 LinkProperties target = (LinkProperties) obj; 824 /** 825 * This method does not check that stacked interfaces are equal, because 826 * stacked interfaces are not so much a property of the link as a 827 * description of connections between links. 828 */ 829 return isIdenticalInterfaceName(target) && 830 isIdenticalAddresses(target) && 831 isIdenticalDnses(target) && 832 isIdenticalRoutes(target) && 833 isIdenticalHttpProxy(target) && 834 isIdenticalStackedLinks(target) && 835 isIdenticalMtu(target) && 836 isIdenticalTcpBufferSizes(target); 837 } 838 839 /** 840 * Compares the addresses in this LinkProperties with another 841 * LinkProperties, examining only addresses on the base link. 842 * 843 * @param target a LinkProperties with the new list of addresses 844 * @return the differences between the addresses. 845 * @hide 846 */ 847 public CompareResult<LinkAddress> compareAddresses(LinkProperties target) { 848 /* 849 * Duplicate the LinkAddresses into removed, we will be removing 850 * address which are common between mLinkAddresses and target 851 * leaving the addresses that are different. And address which 852 * are in target but not in mLinkAddresses are placed in the 853 * addedAddresses. 854 */ 855 CompareResult<LinkAddress> result = new CompareResult<LinkAddress>(); 856 result.removed = new ArrayList<LinkAddress>(mLinkAddresses); 857 result.added.clear(); 858 if (target != null) { 859 for (LinkAddress newAddress : target.getLinkAddresses()) { 860 if (! result.removed.remove(newAddress)) { 861 result.added.add(newAddress); 862 } 863 } 864 } 865 return result; 866 } 867 868 /** 869 * Compares the DNS addresses in this LinkProperties with another 870 * LinkProperties, examining only DNS addresses on the base link. 871 * 872 * @param target a LinkProperties with the new list of dns addresses 873 * @return the differences between the DNS addresses. 874 * @hide 875 */ 876 public CompareResult<InetAddress> compareDnses(LinkProperties target) { 877 /* 878 * Duplicate the InetAddresses into removed, we will be removing 879 * dns address which are common between mDnses and target 880 * leaving the addresses that are different. And dns address which 881 * are in target but not in mDnses are placed in the 882 * addedAddresses. 883 */ 884 CompareResult<InetAddress> result = new CompareResult<InetAddress>(); 885 886 result.removed = new ArrayList<InetAddress>(mDnses); 887 result.added.clear(); 888 if (target != null) { 889 for (InetAddress newAddress : target.getDnsServers()) { 890 if (! result.removed.remove(newAddress)) { 891 result.added.add(newAddress); 892 } 893 } 894 } 895 return result; 896 } 897 898 /** 899 * Compares all routes in this LinkProperties with another LinkProperties, 900 * examining both the the base link and all stacked links. 901 * 902 * @param target a LinkProperties with the new list of routes 903 * @return the differences between the routes. 904 * @hide 905 */ 906 public CompareResult<RouteInfo> compareAllRoutes(LinkProperties target) { 907 /* 908 * Duplicate the RouteInfos into removed, we will be removing 909 * routes which are common between mRoutes and target 910 * leaving the routes that are different. And route address which 911 * are in target but not in mRoutes are placed in added. 912 */ 913 CompareResult<RouteInfo> result = new CompareResult<RouteInfo>(); 914 915 result.removed = getAllRoutes(); 916 result.added.clear(); 917 if (target != null) { 918 for (RouteInfo r : target.getAllRoutes()) { 919 if (! result.removed.remove(r)) { 920 result.added.add(r); 921 } 922 } 923 } 924 return result; 925 } 926 927 /** 928 * Compares all interface names in this LinkProperties with another 929 * LinkProperties, examining both the the base link and all stacked links. 930 * 931 * @param target a LinkProperties with the new list of interface names 932 * @return the differences between the interface names. 933 * @hide 934 */ 935 public CompareResult<String> compareAllInterfaceNames(LinkProperties target) { 936 /* 937 * Duplicate the interface names into removed, we will be removing 938 * interface names which are common between this and target 939 * leaving the interface names that are different. And interface names which 940 * are in target but not in this are placed in added. 941 */ 942 CompareResult<String> result = new CompareResult<String>(); 943 944 result.removed = getAllInterfaceNames(); 945 result.added.clear(); 946 if (target != null) { 947 for (String r : target.getAllInterfaceNames()) { 948 if (! result.removed.remove(r)) { 949 result.added.add(r); 950 } 951 } 952 } 953 return result; 954 } 955 956 957 @Override 958 /** 959 * generate hashcode based on significant fields 960 * Equal objects must produce the same hash code, while unequal objects 961 * may have the same hash codes. 962 */ 963 public int hashCode() { 964 return ((null == mIfaceName) ? 0 : mIfaceName.hashCode() 965 + mLinkAddresses.size() * 31 966 + mDnses.size() * 37 967 + ((null == mDomains) ? 0 : mDomains.hashCode()) 968 + mRoutes.size() * 41 969 + ((null == mHttpProxy) ? 0 : mHttpProxy.hashCode()) 970 + mStackedLinks.hashCode() * 47) 971 + mMtu * 51 972 + ((null == mTcpBufferSizes) ? 0 : mTcpBufferSizes.hashCode()); 973 } 974 975 /** 976 * Implement the Parcelable interface. 977 */ 978 public void writeToParcel(Parcel dest, int flags) { 979 dest.writeString(getInterfaceName()); 980 dest.writeInt(mLinkAddresses.size()); 981 for(LinkAddress linkAddress : mLinkAddresses) { 982 dest.writeParcelable(linkAddress, flags); 983 } 984 985 dest.writeInt(mDnses.size()); 986 for(InetAddress d : mDnses) { 987 dest.writeByteArray(d.getAddress()); 988 } 989 dest.writeString(mDomains); 990 dest.writeInt(mMtu); 991 dest.writeString(mTcpBufferSizes); 992 dest.writeInt(mRoutes.size()); 993 for(RouteInfo route : mRoutes) { 994 dest.writeParcelable(route, flags); 995 } 996 997 if (mHttpProxy != null) { 998 dest.writeByte((byte)1); 999 dest.writeParcelable(mHttpProxy, flags); 1000 } else { 1001 dest.writeByte((byte)0); 1002 } 1003 ArrayList<LinkProperties> stackedLinks = new ArrayList(mStackedLinks.values()); 1004 dest.writeList(stackedLinks); 1005 } 1006 1007 /** 1008 * Implement the Parcelable interface. 1009 */ 1010 public static final Creator<LinkProperties> CREATOR = 1011 new Creator<LinkProperties>() { 1012 public LinkProperties createFromParcel(Parcel in) { 1013 LinkProperties netProp = new LinkProperties(); 1014 1015 String iface = in.readString(); 1016 if (iface != null) { 1017 netProp.setInterfaceName(iface); 1018 } 1019 int addressCount = in.readInt(); 1020 for (int i=0; i<addressCount; i++) { 1021 netProp.addLinkAddress((LinkAddress)in.readParcelable(null)); 1022 } 1023 addressCount = in.readInt(); 1024 for (int i=0; i<addressCount; i++) { 1025 try { 1026 netProp.addDnsServer(InetAddress.getByAddress(in.createByteArray())); 1027 } catch (UnknownHostException e) { } 1028 } 1029 netProp.setDomains(in.readString()); 1030 netProp.setMtu(in.readInt()); 1031 netProp.setTcpBufferSizes(in.readString()); 1032 addressCount = in.readInt(); 1033 for (int i=0; i<addressCount; i++) { 1034 netProp.addRoute((RouteInfo)in.readParcelable(null)); 1035 } 1036 if (in.readByte() == 1) { 1037 netProp.setHttpProxy((ProxyInfo)in.readParcelable(null)); 1038 } 1039 ArrayList<LinkProperties> stackedLinks = new ArrayList<LinkProperties>(); 1040 in.readList(stackedLinks, LinkProperties.class.getClassLoader()); 1041 for (LinkProperties stackedLink: stackedLinks) { 1042 netProp.addStackedLink(stackedLink); 1043 } 1044 return netProp; 1045 } 1046 1047 public LinkProperties[] newArray(int size) { 1048 return new LinkProperties[size]; 1049 } 1050 }; 1051 1052 /** 1053 * Check the valid MTU range based on IPv4 or IPv6. 1054 * @hide 1055 */ 1056 public static boolean isValidMtu(int mtu, boolean ipv6) { 1057 if (ipv6) { 1058 if ((mtu >= MIN_MTU_V6 && mtu <= MAX_MTU)) return true; 1059 } else { 1060 if ((mtu >= MIN_MTU && mtu <= MAX_MTU)) return true; 1061 } 1062 return false; 1063 } 1064 } 1065