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 toString()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 */ LinkProperties()91 public LinkProperties() { 92 } 93 94 /** 95 * @hide 96 */ LinkProperties(LinkProperties source)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 */ setInterfaceName(String iface)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 */ getInterfaceName()135 public @Nullable String getInterfaceName() { 136 return mIfaceName; 137 } 138 139 /** 140 * @hide 141 */ getAllInterfaceNames()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 */ getAddresses()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 */ getAllAddresses()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 findLinkAddressIndex(LinkAddress address)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 */ addLinkAddress(LinkAddress address)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 */ removeLinkAddress(LinkAddress toRemove)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 */ getLinkAddresses()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 */ getAllLinkAddresses()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 */ setLinkAddresses(Collection<LinkAddress> addresses)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 */ addDnsServer(InetAddress dnsServer)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 */ setDnsServers(Collection<InetAddress> dnsServers)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 */ getDnsServers()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 */ setDomains(String domains)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 */ getDomains()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 */ setMtu(int mtu)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 */ getMtu()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 */ setTcpBufferSizes(String tcpBufferSizes)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 */ getTcpBufferSizes()376 public String getTcpBufferSizes() { 377 return mTcpBufferSizes; 378 } 379 routeWithInterface(RouteInfo route)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 */ addRoute(RouteInfo route)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 */ removeRoute(RouteInfo route)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 */ getRoutes()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 */ getAllRoutes()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 */ setHttpProxy(ProxyInfo proxy)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 */ getHttpProxy()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 */ addStackedLink(LinkProperties link)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 is a stacked link with the given interface name, it is 497 * removed. Otherwise, nothing changes. 498 * 499 * @param iface The interface name of the link to remove. 500 * @return true if the link was removed, false otherwise. 501 * @hide 502 */ removeStackedLink(String iface)503 public boolean removeStackedLink(String iface) { 504 if (iface != null) { 505 LinkProperties removed = mStackedLinks.remove(iface); 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 */ getStackedLinks()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 */ clear()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 */ describeContents()545 public int describeContents() { 546 return 0; 547 } 548 549 @Override toString()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 */ hasIPv4Address()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 */ hasGlobalIPv6Address()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 */ hasIPv4DefaultRoute()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 */ hasIPv6DefaultRoute()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 */ hasIPv4DnsServer()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 */ hasIPv6DnsServer()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 IPv4 connectivity. 679 * This requires an IP address, default route, and DNS server. 680 * 681 * @return {@code true} if the link is provisioned, {@code false} otherwise. 682 */ hasIPv4()683 private boolean hasIPv4() { 684 return (hasIPv4Address() && 685 hasIPv4DefaultRoute() && 686 hasIPv4DnsServer()); 687 } 688 689 /** 690 * Returns true if this link is provisioned for global IPv6 connectivity. 691 * This requires an IP address, default route, and DNS server. 692 * 693 * @return {@code true} if the link is provisioned, {@code false} otherwise. 694 */ hasIPv6()695 private boolean hasIPv6() { 696 return (hasGlobalIPv6Address() && 697 hasIPv6DefaultRoute() && 698 hasIPv6DnsServer()); 699 } 700 701 /** 702 * Returns true if this link is provisioned for global connectivity, 703 * for at least one Internet Protocol family. 704 * 705 * @return {@code true} if the link is provisioned, {@code false} otherwise. 706 * @hide 707 */ isProvisioned()708 public boolean isProvisioned() { 709 return (hasIPv4() || hasIPv6()); 710 } 711 712 /** 713 * Compares this {@code LinkProperties} interface name against the target 714 * 715 * @param target LinkProperties to compare. 716 * @return {@code true} if both are identical, {@code false} otherwise. 717 * @hide 718 */ isIdenticalInterfaceName(LinkProperties target)719 public boolean isIdenticalInterfaceName(LinkProperties target) { 720 return TextUtils.equals(getInterfaceName(), target.getInterfaceName()); 721 } 722 723 /** 724 * Compares this {@code LinkProperties} interface addresses against the target 725 * 726 * @param target LinkProperties to compare. 727 * @return {@code true} if both are identical, {@code false} otherwise. 728 * @hide 729 */ isIdenticalAddresses(LinkProperties target)730 public boolean isIdenticalAddresses(LinkProperties target) { 731 Collection<InetAddress> targetAddresses = target.getAddresses(); 732 Collection<InetAddress> sourceAddresses = getAddresses(); 733 return (sourceAddresses.size() == targetAddresses.size()) ? 734 sourceAddresses.containsAll(targetAddresses) : false; 735 } 736 737 /** 738 * Compares this {@code LinkProperties} DNS addresses against the target 739 * 740 * @param target LinkProperties to compare. 741 * @return {@code true} if both are identical, {@code false} otherwise. 742 * @hide 743 */ isIdenticalDnses(LinkProperties target)744 public boolean isIdenticalDnses(LinkProperties target) { 745 Collection<InetAddress> targetDnses = target.getDnsServers(); 746 String targetDomains = target.getDomains(); 747 if (mDomains == null) { 748 if (targetDomains != null) return false; 749 } else { 750 if (mDomains.equals(targetDomains) == false) return false; 751 } 752 return (mDnses.size() == targetDnses.size()) ? 753 mDnses.containsAll(targetDnses) : false; 754 } 755 756 /** 757 * Compares this {@code LinkProperties} Routes against the target 758 * 759 * @param target LinkProperties to compare. 760 * @return {@code true} if both are identical, {@code false} otherwise. 761 * @hide 762 */ isIdenticalRoutes(LinkProperties target)763 public boolean isIdenticalRoutes(LinkProperties target) { 764 Collection<RouteInfo> targetRoutes = target.getRoutes(); 765 return (mRoutes.size() == targetRoutes.size()) ? 766 mRoutes.containsAll(targetRoutes) : false; 767 } 768 769 /** 770 * Compares this {@code LinkProperties} HttpProxy against the target 771 * 772 * @param target LinkProperties to compare. 773 * @return {@code true} if both are identical, {@code false} otherwise. 774 * @hide 775 */ isIdenticalHttpProxy(LinkProperties target)776 public boolean isIdenticalHttpProxy(LinkProperties target) { 777 return getHttpProxy() == null ? target.getHttpProxy() == null : 778 getHttpProxy().equals(target.getHttpProxy()); 779 } 780 781 /** 782 * Compares this {@code LinkProperties} stacked links against the target 783 * 784 * @param target LinkProperties to compare. 785 * @return {@code true} if both are identical, {@code false} otherwise. 786 * @hide 787 */ isIdenticalStackedLinks(LinkProperties target)788 public boolean isIdenticalStackedLinks(LinkProperties target) { 789 if (!mStackedLinks.keySet().equals(target.mStackedLinks.keySet())) { 790 return false; 791 } 792 for (LinkProperties stacked : mStackedLinks.values()) { 793 // Hashtable values can never be null. 794 String iface = stacked.getInterfaceName(); 795 if (!stacked.equals(target.mStackedLinks.get(iface))) { 796 return false; 797 } 798 } 799 return true; 800 } 801 802 /** 803 * Compares this {@code LinkProperties} MTU against the target 804 * 805 * @param target LinkProperties to compare. 806 * @return {@code true} if both are identical, {@code false} otherwise. 807 * @hide 808 */ isIdenticalMtu(LinkProperties target)809 public boolean isIdenticalMtu(LinkProperties target) { 810 return getMtu() == target.getMtu(); 811 } 812 813 /** 814 * Compares this {@code LinkProperties} Tcp buffer sizes against the target. 815 * 816 * @param target LinkProperties to compare. 817 * @return {@code true} if both are identical, {@code false} otherwise. 818 * @hide 819 */ isIdenticalTcpBufferSizes(LinkProperties target)820 public boolean isIdenticalTcpBufferSizes(LinkProperties target) { 821 return Objects.equals(mTcpBufferSizes, target.mTcpBufferSizes); 822 } 823 824 @Override 825 /** 826 * Compares this {@code LinkProperties} instance against the target 827 * LinkProperties in {@code obj}. Two LinkPropertieses are equal if 828 * all their fields are equal in values. 829 * 830 * For collection fields, such as mDnses, containsAll() is used to check 831 * if two collections contains the same elements, independent of order. 832 * There are two thoughts regarding containsAll() 833 * 1. Duplicated elements. eg, (A, B, B) and (A, A, B) are equal. 834 * 2. Worst case performance is O(n^2). 835 * 836 * @param obj the object to be tested for equality. 837 * @return {@code true} if both objects are equal, {@code false} otherwise. 838 */ equals(Object obj)839 public boolean equals(Object obj) { 840 if (this == obj) return true; 841 842 if (!(obj instanceof LinkProperties)) return false; 843 844 LinkProperties target = (LinkProperties) obj; 845 /** 846 * This method does not check that stacked interfaces are equal, because 847 * stacked interfaces are not so much a property of the link as a 848 * description of connections between links. 849 */ 850 return isIdenticalInterfaceName(target) && 851 isIdenticalAddresses(target) && 852 isIdenticalDnses(target) && 853 isIdenticalRoutes(target) && 854 isIdenticalHttpProxy(target) && 855 isIdenticalStackedLinks(target) && 856 isIdenticalMtu(target) && 857 isIdenticalTcpBufferSizes(target); 858 } 859 860 /** 861 * Compares the addresses in this LinkProperties with another 862 * LinkProperties, examining only addresses on the base link. 863 * 864 * @param target a LinkProperties with the new list of addresses 865 * @return the differences between the addresses. 866 * @hide 867 */ compareAddresses(LinkProperties target)868 public CompareResult<LinkAddress> compareAddresses(LinkProperties target) { 869 /* 870 * Duplicate the LinkAddresses into removed, we will be removing 871 * address which are common between mLinkAddresses and target 872 * leaving the addresses that are different. And address which 873 * are in target but not in mLinkAddresses are placed in the 874 * addedAddresses. 875 */ 876 CompareResult<LinkAddress> result = new CompareResult<LinkAddress>(); 877 result.removed = new ArrayList<LinkAddress>(mLinkAddresses); 878 result.added.clear(); 879 if (target != null) { 880 for (LinkAddress newAddress : target.getLinkAddresses()) { 881 if (! result.removed.remove(newAddress)) { 882 result.added.add(newAddress); 883 } 884 } 885 } 886 return result; 887 } 888 889 /** 890 * Compares the DNS addresses in this LinkProperties with another 891 * LinkProperties, examining only DNS addresses on the base link. 892 * 893 * @param target a LinkProperties with the new list of dns addresses 894 * @return the differences between the DNS addresses. 895 * @hide 896 */ compareDnses(LinkProperties target)897 public CompareResult<InetAddress> compareDnses(LinkProperties target) { 898 /* 899 * Duplicate the InetAddresses into removed, we will be removing 900 * dns address which are common between mDnses and target 901 * leaving the addresses that are different. And dns address which 902 * are in target but not in mDnses are placed in the 903 * addedAddresses. 904 */ 905 CompareResult<InetAddress> result = new CompareResult<InetAddress>(); 906 907 result.removed = new ArrayList<InetAddress>(mDnses); 908 result.added.clear(); 909 if (target != null) { 910 for (InetAddress newAddress : target.getDnsServers()) { 911 if (! result.removed.remove(newAddress)) { 912 result.added.add(newAddress); 913 } 914 } 915 } 916 return result; 917 } 918 919 /** 920 * Compares all routes in this LinkProperties with another LinkProperties, 921 * examining both the the base link and all stacked links. 922 * 923 * @param target a LinkProperties with the new list of routes 924 * @return the differences between the routes. 925 * @hide 926 */ compareAllRoutes(LinkProperties target)927 public CompareResult<RouteInfo> compareAllRoutes(LinkProperties target) { 928 /* 929 * Duplicate the RouteInfos into removed, we will be removing 930 * routes which are common between mRoutes and target 931 * leaving the routes that are different. And route address which 932 * are in target but not in mRoutes are placed in added. 933 */ 934 CompareResult<RouteInfo> result = new CompareResult<RouteInfo>(); 935 936 result.removed = getAllRoutes(); 937 result.added.clear(); 938 if (target != null) { 939 for (RouteInfo r : target.getAllRoutes()) { 940 if (! result.removed.remove(r)) { 941 result.added.add(r); 942 } 943 } 944 } 945 return result; 946 } 947 948 /** 949 * Compares all interface names in this LinkProperties with another 950 * LinkProperties, examining both the the base link and all stacked links. 951 * 952 * @param target a LinkProperties with the new list of interface names 953 * @return the differences between the interface names. 954 * @hide 955 */ compareAllInterfaceNames(LinkProperties target)956 public CompareResult<String> compareAllInterfaceNames(LinkProperties target) { 957 /* 958 * Duplicate the interface names into removed, we will be removing 959 * interface names which are common between this and target 960 * leaving the interface names that are different. And interface names which 961 * are in target but not in this are placed in added. 962 */ 963 CompareResult<String> result = new CompareResult<String>(); 964 965 result.removed = getAllInterfaceNames(); 966 result.added.clear(); 967 if (target != null) { 968 for (String r : target.getAllInterfaceNames()) { 969 if (! result.removed.remove(r)) { 970 result.added.add(r); 971 } 972 } 973 } 974 return result; 975 } 976 977 978 @Override 979 /** 980 * generate hashcode based on significant fields 981 * Equal objects must produce the same hash code, while unequal objects 982 * may have the same hash codes. 983 */ hashCode()984 public int hashCode() { 985 return ((null == mIfaceName) ? 0 : mIfaceName.hashCode() 986 + mLinkAddresses.size() * 31 987 + mDnses.size() * 37 988 + ((null == mDomains) ? 0 : mDomains.hashCode()) 989 + mRoutes.size() * 41 990 + ((null == mHttpProxy) ? 0 : mHttpProxy.hashCode()) 991 + mStackedLinks.hashCode() * 47) 992 + mMtu * 51 993 + ((null == mTcpBufferSizes) ? 0 : mTcpBufferSizes.hashCode()); 994 } 995 996 /** 997 * Implement the Parcelable interface. 998 */ writeToParcel(Parcel dest, int flags)999 public void writeToParcel(Parcel dest, int flags) { 1000 dest.writeString(getInterfaceName()); 1001 dest.writeInt(mLinkAddresses.size()); 1002 for(LinkAddress linkAddress : mLinkAddresses) { 1003 dest.writeParcelable(linkAddress, flags); 1004 } 1005 1006 dest.writeInt(mDnses.size()); 1007 for(InetAddress d : mDnses) { 1008 dest.writeByteArray(d.getAddress()); 1009 } 1010 dest.writeString(mDomains); 1011 dest.writeInt(mMtu); 1012 dest.writeString(mTcpBufferSizes); 1013 dest.writeInt(mRoutes.size()); 1014 for(RouteInfo route : mRoutes) { 1015 dest.writeParcelable(route, flags); 1016 } 1017 1018 if (mHttpProxy != null) { 1019 dest.writeByte((byte)1); 1020 dest.writeParcelable(mHttpProxy, flags); 1021 } else { 1022 dest.writeByte((byte)0); 1023 } 1024 ArrayList<LinkProperties> stackedLinks = new ArrayList(mStackedLinks.values()); 1025 dest.writeList(stackedLinks); 1026 } 1027 1028 /** 1029 * Implement the Parcelable interface. 1030 */ 1031 public static final Creator<LinkProperties> CREATOR = 1032 new Creator<LinkProperties>() { 1033 public LinkProperties createFromParcel(Parcel in) { 1034 LinkProperties netProp = new LinkProperties(); 1035 1036 String iface = in.readString(); 1037 if (iface != null) { 1038 netProp.setInterfaceName(iface); 1039 } 1040 int addressCount = in.readInt(); 1041 for (int i=0; i<addressCount; i++) { 1042 netProp.addLinkAddress((LinkAddress)in.readParcelable(null)); 1043 } 1044 addressCount = in.readInt(); 1045 for (int i=0; i<addressCount; i++) { 1046 try { 1047 netProp.addDnsServer(InetAddress.getByAddress(in.createByteArray())); 1048 } catch (UnknownHostException e) { } 1049 } 1050 netProp.setDomains(in.readString()); 1051 netProp.setMtu(in.readInt()); 1052 netProp.setTcpBufferSizes(in.readString()); 1053 addressCount = in.readInt(); 1054 for (int i=0; i<addressCount; i++) { 1055 netProp.addRoute((RouteInfo)in.readParcelable(null)); 1056 } 1057 if (in.readByte() == 1) { 1058 netProp.setHttpProxy((ProxyInfo)in.readParcelable(null)); 1059 } 1060 ArrayList<LinkProperties> stackedLinks = new ArrayList<LinkProperties>(); 1061 in.readList(stackedLinks, LinkProperties.class.getClassLoader()); 1062 for (LinkProperties stackedLink: stackedLinks) { 1063 netProp.addStackedLink(stackedLink); 1064 } 1065 return netProp; 1066 } 1067 1068 public LinkProperties[] newArray(int size) { 1069 return new LinkProperties[size]; 1070 } 1071 }; 1072 1073 /** 1074 * Check the valid MTU range based on IPv4 or IPv6. 1075 * @hide 1076 */ isValidMtu(int mtu, boolean ipv6)1077 public static boolean isValidMtu(int mtu, boolean ipv6) { 1078 if (ipv6) { 1079 if ((mtu >= MIN_MTU_V6 && mtu <= MAX_MTU)) return true; 1080 } else { 1081 if ((mtu >= MIN_MTU && mtu <= MAX_MTU)) return true; 1082 } 1083 return false; 1084 } 1085 } 1086