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