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.annotation.SystemApi; 22 import android.annotation.TestApi; 23 import android.annotation.UnsupportedAppUsage; 24 import android.os.Build; 25 import android.os.Parcel; 26 import android.os.Parcelable; 27 import android.text.TextUtils; 28 29 import java.net.Inet4Address; 30 import java.net.Inet6Address; 31 import java.net.InetAddress; 32 import java.net.UnknownHostException; 33 import java.util.ArrayList; 34 import java.util.Collection; 35 import java.util.Collections; 36 import java.util.Hashtable; 37 import java.util.List; 38 import java.util.Objects; 39 import java.util.StringJoiner; 40 41 /** 42 * Describes the properties of a network link. 43 * 44 * A link represents a connection to a network. 45 * It may have multiple addresses and multiple gateways, 46 * multiple dns servers but only one http proxy and one 47 * network interface. 48 * 49 * Note that this is just a holder of data. Modifying it 50 * does not affect live networks. 51 * 52 */ 53 public final class LinkProperties implements Parcelable { 54 // The interface described by the network link. 55 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) 56 private String mIfaceName; 57 private final ArrayList<LinkAddress> mLinkAddresses = new ArrayList<>(); 58 private final ArrayList<InetAddress> mDnses = new ArrayList<>(); 59 // PCSCF addresses are addresses of SIP proxies that only exist for the IMS core service. 60 private final ArrayList<InetAddress> mPcscfs = new ArrayList<InetAddress>(); 61 private final ArrayList<InetAddress> mValidatedPrivateDnses = new ArrayList<>(); 62 private boolean mUsePrivateDns; 63 private String mPrivateDnsServerName; 64 private String mDomains; 65 private ArrayList<RouteInfo> mRoutes = new ArrayList<>(); 66 private ProxyInfo mHttpProxy; 67 private int mMtu; 68 // in the format "rmem_min,rmem_def,rmem_max,wmem_min,wmem_def,wmem_max" 69 private String mTcpBufferSizes; 70 private IpPrefix mNat64Prefix; 71 72 private static final int MIN_MTU = 68; 73 private static final int MIN_MTU_V6 = 1280; 74 private static final int MAX_MTU = 10000; 75 76 // Stores the properties of links that are "stacked" above this link. 77 // Indexed by interface name to allow modification and to prevent duplicates being added. 78 private Hashtable<String, LinkProperties> mStackedLinks = new Hashtable<>(); 79 80 /** 81 * @hide 82 */ 83 public static class CompareResult<T> { 84 public final List<T> removed = new ArrayList<>(); 85 public final List<T> added = new ArrayList<>(); 86 CompareResult()87 public CompareResult() {} 88 CompareResult(Collection<T> oldItems, Collection<T> newItems)89 public CompareResult(Collection<T> oldItems, Collection<T> newItems) { 90 if (oldItems != null) { 91 removed.addAll(oldItems); 92 } 93 if (newItems != null) { 94 for (T newItem : newItems) { 95 if (!removed.remove(newItem)) { 96 added.add(newItem); 97 } 98 } 99 } 100 } 101 102 @Override toString()103 public String toString() { 104 return "removed=[" + TextUtils.join(",", removed) 105 + "] added=[" + TextUtils.join(",", added) 106 + "]"; 107 } 108 } 109 110 /** 111 * @hide 112 */ 113 public enum ProvisioningChange { 114 @UnsupportedAppUsage 115 STILL_NOT_PROVISIONED, 116 @UnsupportedAppUsage 117 LOST_PROVISIONING, 118 @UnsupportedAppUsage 119 GAINED_PROVISIONING, 120 @UnsupportedAppUsage 121 STILL_PROVISIONED, 122 } 123 124 /** 125 * Compare the provisioning states of two LinkProperties instances. 126 * 127 * @hide 128 */ 129 @UnsupportedAppUsage compareProvisioning( LinkProperties before, LinkProperties after)130 public static ProvisioningChange compareProvisioning( 131 LinkProperties before, LinkProperties after) { 132 if (before.isProvisioned() && after.isProvisioned()) { 133 // On dual-stack networks, DHCPv4 renewals can occasionally fail. 134 // When this happens, IPv6-reachable services continue to function 135 // normally but IPv4-only services (naturally) fail. 136 // 137 // When an application using an IPv4-only service reports a bad 138 // network condition to the framework, attempts to re-validate 139 // the network succeed (since we support IPv6-only networks) and 140 // nothing is changed. 141 // 142 // For users, this is confusing and unexpected behaviour, and is 143 // not necessarily easy to diagnose. Therefore, we treat changing 144 // from a dual-stack network to an IPv6-only network equivalent to 145 // a total loss of provisioning. 146 // 147 // For one such example of this, see b/18867306. 148 // 149 // Additionally, losing IPv6 provisioning can result in TCP 150 // connections getting stuck until timeouts fire and other 151 // baffling failures. Therefore, loss of either IPv4 or IPv6 on a 152 // previously dual-stack network is deemed a lost of provisioning. 153 if ((before.isIpv4Provisioned() && !after.isIpv4Provisioned()) 154 || (before.isIpv6Provisioned() && !after.isIpv6Provisioned())) { 155 return ProvisioningChange.LOST_PROVISIONING; 156 } 157 return ProvisioningChange.STILL_PROVISIONED; 158 } else if (before.isProvisioned() && !after.isProvisioned()) { 159 return ProvisioningChange.LOST_PROVISIONING; 160 } else if (!before.isProvisioned() && after.isProvisioned()) { 161 return ProvisioningChange.GAINED_PROVISIONING; 162 } else { // !before.isProvisioned() && !after.isProvisioned() 163 return ProvisioningChange.STILL_NOT_PROVISIONED; 164 } 165 } 166 167 /** 168 * Constructs a new {@code LinkProperties} with default values. 169 */ LinkProperties()170 public LinkProperties() { 171 } 172 173 /** 174 * @hide 175 */ 176 @SystemApi 177 @TestApi LinkProperties(@ullable LinkProperties source)178 public LinkProperties(@Nullable LinkProperties source) { 179 if (source != null) { 180 mIfaceName = source.mIfaceName; 181 mLinkAddresses.addAll(source.mLinkAddresses); 182 mDnses.addAll(source.mDnses); 183 mValidatedPrivateDnses.addAll(source.mValidatedPrivateDnses); 184 mUsePrivateDns = source.mUsePrivateDns; 185 mPrivateDnsServerName = source.mPrivateDnsServerName; 186 mPcscfs.addAll(source.mPcscfs); 187 mDomains = source.mDomains; 188 mRoutes.addAll(source.mRoutes); 189 mHttpProxy = (source.mHttpProxy == null) ? null : new ProxyInfo(source.mHttpProxy); 190 for (LinkProperties l: source.mStackedLinks.values()) { 191 addStackedLink(l); 192 } 193 setMtu(source.mMtu); 194 mTcpBufferSizes = source.mTcpBufferSizes; 195 mNat64Prefix = source.mNat64Prefix; 196 } 197 } 198 199 /** 200 * Sets the interface name for this link. All {@link RouteInfo} already set for this 201 * will have their interface changed to match this new value. 202 * 203 * @param iface The name of the network interface used for this link. 204 */ setInterfaceName(@ullable String iface)205 public void setInterfaceName(@Nullable String iface) { 206 mIfaceName = iface; 207 ArrayList<RouteInfo> newRoutes = new ArrayList<>(mRoutes.size()); 208 for (RouteInfo route : mRoutes) { 209 newRoutes.add(routeWithInterface(route)); 210 } 211 mRoutes = newRoutes; 212 } 213 214 /** 215 * Gets the interface name for this link. May be {@code null} if not set. 216 * 217 * @return The interface name set for this link or {@code null}. 218 */ getInterfaceName()219 public @Nullable String getInterfaceName() { 220 return mIfaceName; 221 } 222 223 /** 224 * @hide 225 */ 226 @UnsupportedAppUsage getAllInterfaceNames()227 public @NonNull List<String> getAllInterfaceNames() { 228 List<String> interfaceNames = new ArrayList<>(mStackedLinks.size() + 1); 229 if (mIfaceName != null) interfaceNames.add(mIfaceName); 230 for (LinkProperties stacked: mStackedLinks.values()) { 231 interfaceNames.addAll(stacked.getAllInterfaceNames()); 232 } 233 return interfaceNames; 234 } 235 236 /** 237 * Returns all the addresses on this link. We often think of a link having a single address, 238 * however, particularly with Ipv6 several addresses are typical. Note that the 239 * {@code LinkProperties} actually contains {@link LinkAddress} objects which also include 240 * prefix lengths for each address. This is a simplified utility alternative to 241 * {@link LinkProperties#getLinkAddresses}. 242 * 243 * @return An unmodifiable {@link List} of {@link InetAddress} for this link. 244 * @hide 245 */ 246 @UnsupportedAppUsage getAddresses()247 public @NonNull List<InetAddress> getAddresses() { 248 final List<InetAddress> addresses = new ArrayList<>(); 249 for (LinkAddress linkAddress : mLinkAddresses) { 250 addresses.add(linkAddress.getAddress()); 251 } 252 return Collections.unmodifiableList(addresses); 253 } 254 255 /** 256 * Returns all the addresses on this link and all the links stacked above it. 257 * @hide 258 */ 259 @UnsupportedAppUsage getAllAddresses()260 public @NonNull List<InetAddress> getAllAddresses() { 261 List<InetAddress> addresses = new ArrayList<>(); 262 for (LinkAddress linkAddress : mLinkAddresses) { 263 addresses.add(linkAddress.getAddress()); 264 } 265 for (LinkProperties stacked: mStackedLinks.values()) { 266 addresses.addAll(stacked.getAllAddresses()); 267 } 268 return addresses; 269 } 270 findLinkAddressIndex(LinkAddress address)271 private int findLinkAddressIndex(LinkAddress address) { 272 for (int i = 0; i < mLinkAddresses.size(); i++) { 273 if (mLinkAddresses.get(i).isSameAddressAs(address)) { 274 return i; 275 } 276 } 277 return -1; 278 } 279 280 /** 281 * Adds a {@link LinkAddress} to this {@code LinkProperties} if a {@link LinkAddress} of the 282 * same address/prefix does not already exist. If it does exist it is replaced. 283 * @param address The {@code LinkAddress} to add. 284 * @return true if {@code address} was added or updated, false otherwise. 285 * @hide 286 */ 287 @SystemApi 288 @TestApi addLinkAddress(@onNull LinkAddress address)289 public boolean addLinkAddress(@NonNull LinkAddress address) { 290 if (address == null) { 291 return false; 292 } 293 int i = findLinkAddressIndex(address); 294 if (i < 0) { 295 // Address was not present. Add it. 296 mLinkAddresses.add(address); 297 return true; 298 } else if (mLinkAddresses.get(i).equals(address)) { 299 // Address was present and has same properties. Do nothing. 300 return false; 301 } else { 302 // Address was present and has different properties. Update it. 303 mLinkAddresses.set(i, address); 304 return true; 305 } 306 } 307 308 /** 309 * Removes a {@link LinkAddress} from this {@code LinkProperties}. Specifically, matches 310 * and {@link LinkAddress} with the same address and prefix. 311 * 312 * @param toRemove A {@link LinkAddress} specifying the address to remove. 313 * @return true if the address was removed, false if it did not exist. 314 * @hide 315 */ 316 @SystemApi 317 @TestApi removeLinkAddress(@onNull LinkAddress toRemove)318 public boolean removeLinkAddress(@NonNull LinkAddress toRemove) { 319 int i = findLinkAddressIndex(toRemove); 320 if (i >= 0) { 321 mLinkAddresses.remove(i); 322 return true; 323 } 324 return false; 325 } 326 327 /** 328 * Returns all the {@link LinkAddress} on this link. Typically a link will have 329 * one IPv4 address and one or more IPv6 addresses. 330 * 331 * @return An unmodifiable {@link List} of {@link LinkAddress} for this link. 332 */ getLinkAddresses()333 public @NonNull List<LinkAddress> getLinkAddresses() { 334 return Collections.unmodifiableList(mLinkAddresses); 335 } 336 337 /** 338 * Returns all the addresses on this link and all the links stacked above it. 339 * @hide 340 */ 341 @UnsupportedAppUsage getAllLinkAddresses()342 public List<LinkAddress> getAllLinkAddresses() { 343 List<LinkAddress> addresses = new ArrayList<>(mLinkAddresses); 344 for (LinkProperties stacked: mStackedLinks.values()) { 345 addresses.addAll(stacked.getAllLinkAddresses()); 346 } 347 return addresses; 348 } 349 350 /** 351 * Replaces the {@link LinkAddress} in this {@code LinkProperties} with 352 * the given {@link Collection} of {@link LinkAddress}. 353 * 354 * @param addresses The {@link Collection} of {@link LinkAddress} to set in this 355 * object. 356 */ setLinkAddresses(@onNull Collection<LinkAddress> addresses)357 public void setLinkAddresses(@NonNull Collection<LinkAddress> addresses) { 358 mLinkAddresses.clear(); 359 for (LinkAddress address: addresses) { 360 addLinkAddress(address); 361 } 362 } 363 364 /** 365 * Adds the given {@link InetAddress} to the list of DNS servers, if not present. 366 * 367 * @param dnsServer The {@link InetAddress} to add to the list of DNS servers. 368 * @return true if the DNS server was added, false if it was already present. 369 * @hide 370 */ 371 @TestApi 372 @SystemApi addDnsServer(@onNull InetAddress dnsServer)373 public boolean addDnsServer(@NonNull InetAddress dnsServer) { 374 if (dnsServer != null && !mDnses.contains(dnsServer)) { 375 mDnses.add(dnsServer); 376 return true; 377 } 378 return false; 379 } 380 381 /** 382 * Removes the given {@link InetAddress} from the list of DNS servers. 383 * 384 * @param dnsServer The {@link InetAddress} to remove from the list of DNS servers. 385 * @return true if the DNS server was removed, false if it did not exist. 386 * @hide 387 */ 388 @TestApi 389 @SystemApi removeDnsServer(@onNull InetAddress dnsServer)390 public boolean removeDnsServer(@NonNull InetAddress dnsServer) { 391 return mDnses.remove(dnsServer); 392 } 393 394 /** 395 * Replaces the DNS servers in this {@code LinkProperties} with 396 * the given {@link Collection} of {@link InetAddress} objects. 397 * 398 * @param dnsServers The {@link Collection} of DNS servers to set in this object. 399 */ setDnsServers(@onNull Collection<InetAddress> dnsServers)400 public void setDnsServers(@NonNull Collection<InetAddress> dnsServers) { 401 mDnses.clear(); 402 for (InetAddress dnsServer: dnsServers) { 403 addDnsServer(dnsServer); 404 } 405 } 406 407 /** 408 * Returns all the {@link InetAddress} for DNS servers on this link. 409 * 410 * @return An unmodifiable {@link List} of {@link InetAddress} for DNS servers on 411 * this link. 412 */ getDnsServers()413 public @NonNull List<InetAddress> getDnsServers() { 414 return Collections.unmodifiableList(mDnses); 415 } 416 417 /** 418 * Set whether private DNS is currently in use on this network. 419 * 420 * @param usePrivateDns The private DNS state. 421 * @hide 422 */ 423 @TestApi 424 @SystemApi setUsePrivateDns(boolean usePrivateDns)425 public void setUsePrivateDns(boolean usePrivateDns) { 426 mUsePrivateDns = usePrivateDns; 427 } 428 429 /** 430 * Returns whether private DNS is currently in use on this network. When 431 * private DNS is in use, applications must not send unencrypted DNS 432 * queries as doing so could reveal private user information. Furthermore, 433 * if private DNS is in use and {@link #getPrivateDnsServerName} is not 434 * {@code null}, DNS queries must be sent to the specified DNS server. 435 * 436 * @return {@code true} if private DNS is in use, {@code false} otherwise. 437 */ isPrivateDnsActive()438 public boolean isPrivateDnsActive() { 439 return mUsePrivateDns; 440 } 441 442 /** 443 * Set the name of the private DNS server to which private DNS queries 444 * should be sent when in strict mode. This value should be {@code null} 445 * when private DNS is off or in opportunistic mode. 446 * 447 * @param privateDnsServerName The private DNS server name. 448 * @hide 449 */ 450 @TestApi 451 @SystemApi setPrivateDnsServerName(@ullable String privateDnsServerName)452 public void setPrivateDnsServerName(@Nullable String privateDnsServerName) { 453 mPrivateDnsServerName = privateDnsServerName; 454 } 455 456 /** 457 * Returns the private DNS server name that is in use. If not {@code null}, 458 * private DNS is in strict mode. In this mode, applications should ensure 459 * that all DNS queries are encrypted and sent to this hostname and that 460 * queries are only sent if the hostname's certificate is valid. If 461 * {@code null} and {@link #isPrivateDnsActive} is {@code true}, private 462 * DNS is in opportunistic mode, and applications should ensure that DNS 463 * queries are encrypted and sent to a DNS server returned by 464 * {@link #getDnsServers}. System DNS will handle each of these cases 465 * correctly, but applications implementing their own DNS lookups must make 466 * sure to follow these requirements. 467 * 468 * @return The private DNS server name. 469 */ getPrivateDnsServerName()470 public @Nullable String getPrivateDnsServerName() { 471 return mPrivateDnsServerName; 472 } 473 474 /** 475 * Adds the given {@link InetAddress} to the list of validated private DNS servers, 476 * if not present. This is distinct from the server name in that these are actually 477 * resolved addresses. 478 * 479 * @param dnsServer The {@link InetAddress} to add to the list of validated private DNS servers. 480 * @return true if the DNS server was added, false if it was already present. 481 * @hide 482 */ addValidatedPrivateDnsServer(@onNull InetAddress dnsServer)483 public boolean addValidatedPrivateDnsServer(@NonNull InetAddress dnsServer) { 484 if (dnsServer != null && !mValidatedPrivateDnses.contains(dnsServer)) { 485 mValidatedPrivateDnses.add(dnsServer); 486 return true; 487 } 488 return false; 489 } 490 491 /** 492 * Removes the given {@link InetAddress} from the list of validated private DNS servers. 493 * 494 * @param dnsServer The {@link InetAddress} to remove from the list of validated private DNS 495 * servers. 496 * @return true if the DNS server was removed, false if it did not exist. 497 * @hide 498 */ removeValidatedPrivateDnsServer(@onNull InetAddress dnsServer)499 public boolean removeValidatedPrivateDnsServer(@NonNull InetAddress dnsServer) { 500 return mValidatedPrivateDnses.remove(dnsServer); 501 } 502 503 /** 504 * Replaces the validated private DNS servers in this {@code LinkProperties} with 505 * the given {@link Collection} of {@link InetAddress} objects. 506 * 507 * @param dnsServers The {@link Collection} of validated private DNS servers to set in this 508 * object. 509 * @hide 510 */ 511 @TestApi 512 @SystemApi setValidatedPrivateDnsServers(@onNull Collection<InetAddress> dnsServers)513 public void setValidatedPrivateDnsServers(@NonNull Collection<InetAddress> dnsServers) { 514 mValidatedPrivateDnses.clear(); 515 for (InetAddress dnsServer: dnsServers) { 516 addValidatedPrivateDnsServer(dnsServer); 517 } 518 } 519 520 /** 521 * Returns all the {@link InetAddress} for validated private DNS servers on this link. 522 * These are resolved from the private DNS server name. 523 * 524 * @return An unmodifiable {@link List} of {@link InetAddress} for validated private 525 * DNS servers on this link. 526 * @hide 527 */ 528 @TestApi 529 @SystemApi getValidatedPrivateDnsServers()530 public @NonNull List<InetAddress> getValidatedPrivateDnsServers() { 531 return Collections.unmodifiableList(mValidatedPrivateDnses); 532 } 533 534 /** 535 * Adds the given {@link InetAddress} to the list of PCSCF servers, if not present. 536 * 537 * @param pcscfServer The {@link InetAddress} to add to the list of PCSCF servers. 538 * @return true if the PCSCF server was added, false otherwise. 539 * @hide 540 */ addPcscfServer(@onNull InetAddress pcscfServer)541 public boolean addPcscfServer(@NonNull InetAddress pcscfServer) { 542 if (pcscfServer != null && !mPcscfs.contains(pcscfServer)) { 543 mPcscfs.add(pcscfServer); 544 return true; 545 } 546 return false; 547 } 548 549 /** 550 * Removes the given {@link InetAddress} from the list of PCSCF servers. 551 * 552 * @param pcscfServer The {@link InetAddress} to remove from the list of PCSCF servers. 553 * @return true if the PCSCF server was removed, false otherwise. 554 * @hide 555 */ removePcscfServer(@onNull InetAddress pcscfServer)556 public boolean removePcscfServer(@NonNull InetAddress pcscfServer) { 557 return mPcscfs.remove(pcscfServer); 558 } 559 560 /** 561 * Replaces the PCSCF servers in this {@code LinkProperties} with 562 * the given {@link Collection} of {@link InetAddress} objects. 563 * 564 * @param pcscfServers The {@link Collection} of PCSCF servers to set in this object. 565 * @hide 566 */ 567 @SystemApi 568 @TestApi setPcscfServers(@onNull Collection<InetAddress> pcscfServers)569 public void setPcscfServers(@NonNull Collection<InetAddress> pcscfServers) { 570 mPcscfs.clear(); 571 for (InetAddress pcscfServer: pcscfServers) { 572 addPcscfServer(pcscfServer); 573 } 574 } 575 576 /** 577 * Returns all the {@link InetAddress} for PCSCF servers on this link. 578 * 579 * @return An unmodifiable {@link List} of {@link InetAddress} for PCSCF servers on 580 * this link. 581 * @hide 582 */ 583 @SystemApi 584 @TestApi getPcscfServers()585 public @NonNull List<InetAddress> getPcscfServers() { 586 return Collections.unmodifiableList(mPcscfs); 587 } 588 589 /** 590 * Sets the DNS domain search path used on this link. 591 * 592 * @param domains A {@link String} listing in priority order the comma separated 593 * domains to search when resolving host names on this link. 594 */ setDomains(@ullable String domains)595 public void setDomains(@Nullable String domains) { 596 mDomains = domains; 597 } 598 599 /** 600 * Get the DNS domains search path set for this link. May be {@code null} if not set. 601 * 602 * @return A {@link String} containing the comma separated domains to search when resolving host 603 * names on this link or {@code null}. 604 */ getDomains()605 public @Nullable String getDomains() { 606 return mDomains; 607 } 608 609 /** 610 * Sets the Maximum Transmission Unit size to use on this link. This should not be used 611 * unless the system default (1500) is incorrect. Values less than 68 or greater than 612 * 10000 will be ignored. 613 * 614 * @param mtu The MTU to use for this link. 615 */ setMtu(int mtu)616 public void setMtu(int mtu) { 617 mMtu = mtu; 618 } 619 620 /** 621 * Gets any non-default MTU size set for this link. Note that if the default is being used 622 * this will return 0. 623 * 624 * @return The mtu value set for this link. 625 */ getMtu()626 public int getMtu() { 627 return mMtu; 628 } 629 630 /** 631 * Sets the tcp buffers sizes to be used when this link is the system default. 632 * Should be of the form "rmem_min,rmem_def,rmem_max,wmem_min,wmem_def,wmem_max". 633 * 634 * @param tcpBufferSizes The tcp buffers sizes to use. 635 * 636 * @hide 637 */ 638 @TestApi 639 @SystemApi setTcpBufferSizes(@ullable String tcpBufferSizes)640 public void setTcpBufferSizes(@Nullable String tcpBufferSizes) { 641 mTcpBufferSizes = tcpBufferSizes; 642 } 643 644 /** 645 * Gets the tcp buffer sizes. May be {@code null} if not set. 646 * 647 * @return the tcp buffer sizes to use when this link is the system default or {@code null}. 648 * 649 * @hide 650 */ 651 @TestApi 652 @SystemApi getTcpBufferSizes()653 public @Nullable String getTcpBufferSizes() { 654 return mTcpBufferSizes; 655 } 656 routeWithInterface(RouteInfo route)657 private RouteInfo routeWithInterface(RouteInfo route) { 658 return new RouteInfo( 659 route.getDestination(), 660 route.getGateway(), 661 mIfaceName, 662 route.getType()); 663 } 664 665 /** 666 * Adds a {@link RouteInfo} to this {@code LinkProperties}, if not present. If the 667 * {@link RouteInfo} had an interface name set and that differs from the interface set for this 668 * {@code LinkProperties} an {@link IllegalArgumentException} will be thrown. The proper 669 * course is to add either un-named or properly named {@link RouteInfo}. 670 * 671 * @param route A {@link RouteInfo} to add to this object. 672 * @return {@code false} if the route was already present, {@code true} if it was added. 673 */ addRoute(@onNull RouteInfo route)674 public boolean addRoute(@NonNull RouteInfo route) { 675 String routeIface = route.getInterface(); 676 if (routeIface != null && !routeIface.equals(mIfaceName)) { 677 throw new IllegalArgumentException( 678 "Route added with non-matching interface: " + routeIface 679 + " vs. " + mIfaceName); 680 } 681 route = routeWithInterface(route); 682 if (!mRoutes.contains(route)) { 683 mRoutes.add(route); 684 return true; 685 } 686 return false; 687 } 688 689 /** 690 * Removes a {@link RouteInfo} from this {@code LinkProperties}, if present. The route must 691 * specify an interface and the interface must match the interface of this 692 * {@code LinkProperties}, or it will not be removed. 693 * 694 * @return {@code true} if the route was removed, {@code false} if it was not present. 695 * 696 * @hide 697 */ 698 @TestApi 699 @SystemApi removeRoute(@onNull RouteInfo route)700 public boolean removeRoute(@NonNull RouteInfo route) { 701 return Objects.equals(mIfaceName, route.getInterface()) && mRoutes.remove(route); 702 } 703 704 /** 705 * Returns all the {@link RouteInfo} set on this link. 706 * 707 * @return An unmodifiable {@link List} of {@link RouteInfo} for this link. 708 */ getRoutes()709 public @NonNull List<RouteInfo> getRoutes() { 710 return Collections.unmodifiableList(mRoutes); 711 } 712 713 /** 714 * Make sure this LinkProperties instance contains routes that cover the local subnet 715 * of its link addresses. Add any route that is missing. 716 * @hide 717 */ ensureDirectlyConnectedRoutes()718 public void ensureDirectlyConnectedRoutes() { 719 for (LinkAddress addr : mLinkAddresses) { 720 addRoute(new RouteInfo(addr, null, mIfaceName)); 721 } 722 } 723 724 /** 725 * Returns all the routes on this link and all the links stacked above it. 726 * @hide 727 */ 728 @UnsupportedAppUsage getAllRoutes()729 public @NonNull List<RouteInfo> getAllRoutes() { 730 List<RouteInfo> routes = new ArrayList<>(mRoutes); 731 for (LinkProperties stacked: mStackedLinks.values()) { 732 routes.addAll(stacked.getAllRoutes()); 733 } 734 return routes; 735 } 736 737 /** 738 * Sets the recommended {@link ProxyInfo} to use on this link, or {@code null} for none. 739 * Note that Http Proxies are only a hint - the system recommends their use, but it does 740 * not enforce it and applications may ignore them. 741 * 742 * @param proxy A {@link ProxyInfo} defining the HTTP Proxy to use on this link. 743 */ setHttpProxy(@ullable ProxyInfo proxy)744 public void setHttpProxy(@Nullable ProxyInfo proxy) { 745 mHttpProxy = proxy; 746 } 747 748 /** 749 * Gets the recommended {@link ProxyInfo} (or {@code null}) set on this link. 750 * 751 * @return The {@link ProxyInfo} set on this link or {@code null}. 752 */ getHttpProxy()753 public @Nullable ProxyInfo getHttpProxy() { 754 return mHttpProxy; 755 } 756 757 /** 758 * Returns the NAT64 prefix in use on this link, if any. 759 * 760 * @return the NAT64 prefix or {@code null}. 761 * @hide 762 */ 763 @SystemApi 764 @TestApi getNat64Prefix()765 public @Nullable IpPrefix getNat64Prefix() { 766 return mNat64Prefix; 767 } 768 769 /** 770 * Sets the NAT64 prefix in use on this link. 771 * 772 * Currently, only 96-bit prefixes (i.e., where the 32-bit IPv4 address is at the end of the 773 * 128-bit IPv6 address) are supported or {@code null} for no prefix. 774 * 775 * @param prefix the NAT64 prefix. 776 * @hide 777 */ 778 @SystemApi 779 @TestApi setNat64Prefix(@ullable IpPrefix prefix)780 public void setNat64Prefix(@Nullable IpPrefix prefix) { 781 if (prefix != null && prefix.getPrefixLength() != 96) { 782 throw new IllegalArgumentException("Only 96-bit prefixes are supported: " + prefix); 783 } 784 mNat64Prefix = prefix; // IpPrefix objects are immutable. 785 } 786 787 /** 788 * Adds a stacked link. 789 * 790 * If there is already a stacked link with the same interface name as link, 791 * that link is replaced with link. Otherwise, link is added to the list 792 * of stacked links. 793 * 794 * @param link The link to add. 795 * @return true if the link was stacked, false otherwise. 796 * @hide 797 */ 798 @UnsupportedAppUsage addStackedLink(@onNull LinkProperties link)799 public boolean addStackedLink(@NonNull LinkProperties link) { 800 if (link.getInterfaceName() != null) { 801 mStackedLinks.put(link.getInterfaceName(), link); 802 return true; 803 } 804 return false; 805 } 806 807 /** 808 * Removes a stacked link. 809 * 810 * If there is a stacked link with the given interface name, it is 811 * removed. Otherwise, nothing changes. 812 * 813 * @param iface The interface name of the link to remove. 814 * @return true if the link was removed, false otherwise. 815 * @hide 816 */ removeStackedLink(@onNull String iface)817 public boolean removeStackedLink(@NonNull String iface) { 818 LinkProperties removed = mStackedLinks.remove(iface); 819 return removed != null; 820 } 821 822 /** 823 * Returns all the links stacked on top of this link. 824 * @hide 825 */ 826 @UnsupportedAppUsage getStackedLinks()827 public @NonNull List<LinkProperties> getStackedLinks() { 828 if (mStackedLinks.isEmpty()) { 829 return Collections.emptyList(); 830 } 831 final List<LinkProperties> stacked = new ArrayList<>(); 832 for (LinkProperties link : mStackedLinks.values()) { 833 stacked.add(new LinkProperties(link)); 834 } 835 return Collections.unmodifiableList(stacked); 836 } 837 838 /** 839 * Clears this object to its initial state. 840 */ clear()841 public void clear() { 842 mIfaceName = null; 843 mLinkAddresses.clear(); 844 mDnses.clear(); 845 mUsePrivateDns = false; 846 mPrivateDnsServerName = null; 847 mPcscfs.clear(); 848 mDomains = null; 849 mRoutes.clear(); 850 mHttpProxy = null; 851 mStackedLinks.clear(); 852 mMtu = 0; 853 mTcpBufferSizes = null; 854 mNat64Prefix = null; 855 } 856 857 /** 858 * Implement the Parcelable interface 859 */ describeContents()860 public int describeContents() { 861 return 0; 862 } 863 864 @Override toString()865 public String toString() { 866 // Space as a separator, so no need for spaces at start/end of the individual fragments. 867 final StringJoiner resultJoiner = new StringJoiner(" ", "{", "}"); 868 869 if (mIfaceName != null) { 870 resultJoiner.add("InterfaceName:"); 871 resultJoiner.add(mIfaceName); 872 } 873 874 resultJoiner.add("LinkAddresses: ["); 875 if (!mLinkAddresses.isEmpty()) { 876 resultJoiner.add(TextUtils.join(",", mLinkAddresses)); 877 } 878 resultJoiner.add("]"); 879 880 resultJoiner.add("DnsAddresses: ["); 881 if (!mDnses.isEmpty()) { 882 resultJoiner.add(TextUtils.join(",", mDnses)); 883 } 884 resultJoiner.add("]"); 885 886 if (mUsePrivateDns) { 887 resultJoiner.add("UsePrivateDns: true"); 888 } 889 890 if (mPrivateDnsServerName != null) { 891 resultJoiner.add("PrivateDnsServerName:"); 892 resultJoiner.add(mPrivateDnsServerName); 893 } 894 895 if (!mPcscfs.isEmpty()) { 896 resultJoiner.add("PcscfAddresses: ["); 897 resultJoiner.add(TextUtils.join(",", mPcscfs)); 898 resultJoiner.add("]"); 899 } 900 901 if (!mValidatedPrivateDnses.isEmpty()) { 902 final StringJoiner validatedPrivateDnsesJoiner = 903 new StringJoiner(",", "ValidatedPrivateDnsAddresses: [", "]"); 904 for (final InetAddress addr : mValidatedPrivateDnses) { 905 validatedPrivateDnsesJoiner.add(addr.getHostAddress()); 906 } 907 resultJoiner.add(validatedPrivateDnsesJoiner.toString()); 908 } 909 910 resultJoiner.add("Domains:"); 911 resultJoiner.add(mDomains); 912 913 resultJoiner.add("MTU:"); 914 resultJoiner.add(Integer.toString(mMtu)); 915 916 if (mTcpBufferSizes != null) { 917 resultJoiner.add("TcpBufferSizes:"); 918 resultJoiner.add(mTcpBufferSizes); 919 } 920 921 resultJoiner.add("Routes: ["); 922 if (!mRoutes.isEmpty()) { 923 resultJoiner.add(TextUtils.join(",", mRoutes)); 924 } 925 resultJoiner.add("]"); 926 927 if (mHttpProxy != null) { 928 resultJoiner.add("HttpProxy:"); 929 resultJoiner.add(mHttpProxy.toString()); 930 } 931 932 if (mNat64Prefix != null) { 933 resultJoiner.add("Nat64Prefix:"); 934 resultJoiner.add(mNat64Prefix.toString()); 935 } 936 937 final Collection<LinkProperties> stackedLinksValues = mStackedLinks.values(); 938 if (!stackedLinksValues.isEmpty()) { 939 final StringJoiner stackedLinksJoiner = new StringJoiner(",", "Stacked: [", "]"); 940 for (final LinkProperties lp : stackedLinksValues) { 941 stackedLinksJoiner.add("[ " + lp + " ]"); 942 } 943 resultJoiner.add(stackedLinksJoiner.toString()); 944 } 945 946 return resultJoiner.toString(); 947 } 948 949 /** 950 * Returns true if this link has an IPv4 address. 951 * 952 * @return {@code true} if there is an IPv4 address, {@code false} otherwise. 953 * @hide 954 */ 955 @TestApi 956 @SystemApi hasIpv4Address()957 public boolean hasIpv4Address() { 958 for (LinkAddress address : mLinkAddresses) { 959 if (address.getAddress() instanceof Inet4Address) { 960 return true; 961 } 962 } 963 return false; 964 } 965 966 /** 967 * For backward compatibility. 968 * This was annotated with @UnsupportedAppUsage in P, so we can't remove the method completely 969 * just yet. 970 * @return {@code true} if there is an IPv4 address, {@code false} otherwise. 971 * @hide 972 */ 973 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) hasIPv4Address()974 public boolean hasIPv4Address() { 975 return hasIpv4Address(); 976 } 977 978 /** 979 * Returns true if this link or any of its stacked interfaces has an IPv4 address. 980 * 981 * @return {@code true} if there is an IPv4 address, {@code false} otherwise. 982 */ hasIpv4AddressOnInterface(String iface)983 private boolean hasIpv4AddressOnInterface(String iface) { 984 // mIfaceName can be null. 985 return (Objects.equals(iface, mIfaceName) && hasIpv4Address()) 986 || (iface != null && mStackedLinks.containsKey(iface) 987 && mStackedLinks.get(iface).hasIpv4Address()); 988 } 989 990 /** 991 * Returns true if this link has a global preferred IPv6 address. 992 * 993 * @return {@code true} if there is a global preferred IPv6 address, {@code false} otherwise. 994 * @hide 995 */ 996 @TestApi 997 @SystemApi hasGlobalIpv6Address()998 public boolean hasGlobalIpv6Address() { 999 for (LinkAddress address : mLinkAddresses) { 1000 if (address.getAddress() instanceof Inet6Address && address.isGlobalPreferred()) { 1001 return true; 1002 } 1003 } 1004 return false; 1005 } 1006 1007 /** 1008 * For backward compatibility. 1009 * This was annotated with @UnsupportedAppUsage in P, so we can't remove the method completely 1010 * just yet. 1011 * @return {@code true} if there is a global preferred IPv6 address, {@code false} otherwise. 1012 * @hide 1013 */ 1014 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) hasGlobalIPv6Address()1015 public boolean hasGlobalIPv6Address() { 1016 return hasGlobalIpv6Address(); 1017 } 1018 1019 /** 1020 * Returns true if this link has an IPv4 default route. 1021 * 1022 * @return {@code true} if there is an IPv4 default route, {@code false} otherwise. 1023 * @hide 1024 */ 1025 @UnsupportedAppUsage hasIpv4DefaultRoute()1026 public boolean hasIpv4DefaultRoute() { 1027 for (RouteInfo r : mRoutes) { 1028 if (r.isIPv4Default()) { 1029 return true; 1030 } 1031 } 1032 return false; 1033 } 1034 1035 /** 1036 * For backward compatibility. 1037 * This was annotated with @UnsupportedAppUsage in P, so we can't remove the method completely 1038 * just yet. 1039 * @return {@code true} if there is an IPv4 default route, {@code false} otherwise. 1040 * @hide 1041 */ 1042 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) hasIPv4DefaultRoute()1043 public boolean hasIPv4DefaultRoute() { 1044 return hasIpv4DefaultRoute(); 1045 } 1046 1047 /** 1048 * Returns true if this link has an IPv6 default route. 1049 * 1050 * @return {@code true} if there is an IPv6 default route, {@code false} otherwise. 1051 * @hide 1052 */ 1053 @TestApi 1054 @SystemApi hasIpv6DefaultRoute()1055 public boolean hasIpv6DefaultRoute() { 1056 for (RouteInfo r : mRoutes) { 1057 if (r.isIPv6Default()) { 1058 return true; 1059 } 1060 } 1061 return false; 1062 } 1063 1064 /** 1065 * For backward compatibility. 1066 * This was annotated with @UnsupportedAppUsage in P, so we can't remove the method completely 1067 * just yet. 1068 * @return {@code true} if there is an IPv6 default route, {@code false} otherwise. 1069 * @hide 1070 */ 1071 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) hasIPv6DefaultRoute()1072 public boolean hasIPv6DefaultRoute() { 1073 return hasIpv6DefaultRoute(); 1074 } 1075 1076 /** 1077 * Returns true if this link has an IPv4 DNS server. 1078 * 1079 * @return {@code true} if there is an IPv4 DNS server, {@code false} otherwise. 1080 * @hide 1081 */ 1082 @UnsupportedAppUsage hasIpv4DnsServer()1083 public boolean hasIpv4DnsServer() { 1084 for (InetAddress ia : mDnses) { 1085 if (ia instanceof Inet4Address) { 1086 return true; 1087 } 1088 } 1089 return false; 1090 } 1091 1092 /** 1093 * For backward compatibility. 1094 * This was annotated with @UnsupportedAppUsage in P, so we can't remove the method completely 1095 * just yet. 1096 * @return {@code true} if there is an IPv4 DNS server, {@code false} otherwise. 1097 * @hide 1098 */ 1099 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) hasIPv4DnsServer()1100 public boolean hasIPv4DnsServer() { 1101 return hasIpv4DnsServer(); 1102 } 1103 1104 /** 1105 * Returns true if this link has an IPv6 DNS server. 1106 * 1107 * @return {@code true} if there is an IPv6 DNS server, {@code false} otherwise. 1108 * @hide 1109 */ 1110 @UnsupportedAppUsage hasIpv6DnsServer()1111 public boolean hasIpv6DnsServer() { 1112 for (InetAddress ia : mDnses) { 1113 if (ia instanceof Inet6Address) { 1114 return true; 1115 } 1116 } 1117 return false; 1118 } 1119 1120 /** 1121 * For backward compatibility. 1122 * This was annotated with @UnsupportedAppUsage in P, so we can't remove the method completely 1123 * just yet. 1124 * @return {@code true} if there is an IPv6 DNS server, {@code false} otherwise. 1125 * @hide 1126 */ 1127 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) hasIPv6DnsServer()1128 public boolean hasIPv6DnsServer() { 1129 return hasIpv6DnsServer(); 1130 } 1131 1132 /** 1133 * Returns true if this link has an IPv4 PCSCF server. 1134 * 1135 * @return {@code true} if there is an IPv4 PCSCF server, {@code false} otherwise. 1136 * @hide 1137 */ hasIpv4PcscfServer()1138 public boolean hasIpv4PcscfServer() { 1139 for (InetAddress ia : mPcscfs) { 1140 if (ia instanceof Inet4Address) { 1141 return true; 1142 } 1143 } 1144 return false; 1145 } 1146 1147 /** 1148 * Returns true if this link has an IPv6 PCSCF server. 1149 * 1150 * @return {@code true} if there is an IPv6 PCSCF server, {@code false} otherwise. 1151 * @hide 1152 */ hasIpv6PcscfServer()1153 public boolean hasIpv6PcscfServer() { 1154 for (InetAddress ia : mPcscfs) { 1155 if (ia instanceof Inet6Address) { 1156 return true; 1157 } 1158 } 1159 return false; 1160 } 1161 1162 /** 1163 * Returns true if this link is provisioned for global IPv4 connectivity. 1164 * This requires an IP address, default route, and DNS server. 1165 * 1166 * @return {@code true} if the link is provisioned, {@code false} otherwise. 1167 * @hide 1168 */ 1169 @TestApi 1170 @SystemApi isIpv4Provisioned()1171 public boolean isIpv4Provisioned() { 1172 return (hasIpv4Address() 1173 && hasIpv4DefaultRoute() 1174 && hasIpv4DnsServer()); 1175 } 1176 1177 /** 1178 * Returns true if this link is provisioned for global IPv6 connectivity. 1179 * This requires an IP address, default route, and DNS server. 1180 * 1181 * @return {@code true} if the link is provisioned, {@code false} otherwise. 1182 * @hide 1183 */ 1184 @TestApi 1185 @SystemApi isIpv6Provisioned()1186 public boolean isIpv6Provisioned() { 1187 return (hasGlobalIpv6Address() 1188 && hasIpv6DefaultRoute() 1189 && hasIpv6DnsServer()); 1190 } 1191 1192 /** 1193 * For backward compatibility. 1194 * This was annotated with @UnsupportedAppUsage in P, so we can't remove the method completely 1195 * just yet. 1196 * @return {@code true} if the link is provisioned, {@code false} otherwise. 1197 * @hide 1198 */ 1199 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) isIPv6Provisioned()1200 public boolean isIPv6Provisioned() { 1201 return isIpv6Provisioned(); 1202 } 1203 1204 1205 /** 1206 * Returns true if this link is provisioned for global connectivity, 1207 * for at least one Internet Protocol family. 1208 * 1209 * @return {@code true} if the link is provisioned, {@code false} otherwise. 1210 * @hide 1211 */ 1212 @TestApi 1213 @SystemApi isProvisioned()1214 public boolean isProvisioned() { 1215 return (isIpv4Provisioned() || isIpv6Provisioned()); 1216 } 1217 1218 /** 1219 * Evaluate whether the {@link InetAddress} is considered reachable. 1220 * 1221 * @return {@code true} if the given {@link InetAddress} is considered reachable, 1222 * {@code false} otherwise. 1223 * @hide 1224 */ 1225 @TestApi 1226 @SystemApi isReachable(@onNull InetAddress ip)1227 public boolean isReachable(@NonNull InetAddress ip) { 1228 final List<RouteInfo> allRoutes = getAllRoutes(); 1229 // If we don't have a route to this IP address, it's not reachable. 1230 final RouteInfo bestRoute = RouteInfo.selectBestRoute(allRoutes, ip); 1231 if (bestRoute == null) { 1232 return false; 1233 } 1234 1235 // TODO: better source address evaluation for destination addresses. 1236 1237 if (ip instanceof Inet4Address) { 1238 // For IPv4, it suffices for now to simply have any address. 1239 return hasIpv4AddressOnInterface(bestRoute.getInterface()); 1240 } else if (ip instanceof Inet6Address) { 1241 if (ip.isLinkLocalAddress()) { 1242 // For now, just make sure link-local destinations have 1243 // scopedIds set, since transmits will generally fail otherwise. 1244 // TODO: verify it matches the ifindex of one of the interfaces. 1245 return (((Inet6Address)ip).getScopeId() != 0); 1246 } else { 1247 // For non-link-local destinations check that either the best route 1248 // is directly connected or that some global preferred address exists. 1249 // TODO: reconsider all cases (disconnected ULA networks, ...). 1250 return (!bestRoute.hasGateway() || hasGlobalIpv6Address()); 1251 } 1252 } 1253 1254 return false; 1255 } 1256 1257 /** 1258 * Compares this {@code LinkProperties} interface name against the target 1259 * 1260 * @param target LinkProperties to compare. 1261 * @return {@code true} if both are identical, {@code false} otherwise. 1262 * @hide 1263 */ 1264 @UnsupportedAppUsage isIdenticalInterfaceName(@onNull LinkProperties target)1265 public boolean isIdenticalInterfaceName(@NonNull LinkProperties target) { 1266 return TextUtils.equals(getInterfaceName(), target.getInterfaceName()); 1267 } 1268 1269 /** 1270 * Compares this {@code LinkProperties} interface addresses against the target 1271 * 1272 * @param target LinkProperties to compare. 1273 * @return {@code true} if both are identical, {@code false} otherwise. 1274 * @hide 1275 */ 1276 @UnsupportedAppUsage isIdenticalAddresses(@onNull LinkProperties target)1277 public boolean isIdenticalAddresses(@NonNull LinkProperties target) { 1278 Collection<InetAddress> targetAddresses = target.getAddresses(); 1279 Collection<InetAddress> sourceAddresses = getAddresses(); 1280 return (sourceAddresses.size() == targetAddresses.size()) ? 1281 sourceAddresses.containsAll(targetAddresses) : false; 1282 } 1283 1284 /** 1285 * Compares this {@code LinkProperties} DNS addresses against the target 1286 * 1287 * @param target LinkProperties to compare. 1288 * @return {@code true} if both are identical, {@code false} otherwise. 1289 * @hide 1290 */ 1291 @UnsupportedAppUsage isIdenticalDnses(@onNull LinkProperties target)1292 public boolean isIdenticalDnses(@NonNull LinkProperties target) { 1293 Collection<InetAddress> targetDnses = target.getDnsServers(); 1294 String targetDomains = target.getDomains(); 1295 if (mDomains == null) { 1296 if (targetDomains != null) return false; 1297 } else { 1298 if (!mDomains.equals(targetDomains)) return false; 1299 } 1300 return (mDnses.size() == targetDnses.size()) ? 1301 mDnses.containsAll(targetDnses) : false; 1302 } 1303 1304 /** 1305 * Compares this {@code LinkProperties} private DNS settings against the 1306 * target. 1307 * 1308 * @param target LinkProperties to compare. 1309 * @return {@code true} if both are identical, {@code false} otherwise. 1310 * @hide 1311 */ isIdenticalPrivateDns(@onNull LinkProperties target)1312 public boolean isIdenticalPrivateDns(@NonNull LinkProperties target) { 1313 return (isPrivateDnsActive() == target.isPrivateDnsActive() 1314 && TextUtils.equals(getPrivateDnsServerName(), 1315 target.getPrivateDnsServerName())); 1316 } 1317 1318 /** 1319 * Compares this {@code LinkProperties} validated private DNS addresses against 1320 * the target 1321 * 1322 * @param target LinkProperties to compare. 1323 * @return {@code true} if both are identical, {@code false} otherwise. 1324 * @hide 1325 */ isIdenticalValidatedPrivateDnses(@onNull LinkProperties target)1326 public boolean isIdenticalValidatedPrivateDnses(@NonNull LinkProperties target) { 1327 Collection<InetAddress> targetDnses = target.getValidatedPrivateDnsServers(); 1328 return (mValidatedPrivateDnses.size() == targetDnses.size()) 1329 ? mValidatedPrivateDnses.containsAll(targetDnses) : false; 1330 } 1331 1332 /** 1333 * Compares this {@code LinkProperties} PCSCF addresses against the target 1334 * 1335 * @param target LinkProperties to compare. 1336 * @return {@code true} if both are identical, {@code false} otherwise. 1337 * @hide 1338 */ isIdenticalPcscfs(@onNull LinkProperties target)1339 public boolean isIdenticalPcscfs(@NonNull LinkProperties target) { 1340 Collection<InetAddress> targetPcscfs = target.getPcscfServers(); 1341 return (mPcscfs.size() == targetPcscfs.size()) ? 1342 mPcscfs.containsAll(targetPcscfs) : false; 1343 } 1344 1345 /** 1346 * Compares this {@code LinkProperties} Routes against the target 1347 * 1348 * @param target LinkProperties to compare. 1349 * @return {@code true} if both are identical, {@code false} otherwise. 1350 * @hide 1351 */ 1352 @UnsupportedAppUsage isIdenticalRoutes(@onNull LinkProperties target)1353 public boolean isIdenticalRoutes(@NonNull LinkProperties target) { 1354 Collection<RouteInfo> targetRoutes = target.getRoutes(); 1355 return (mRoutes.size() == targetRoutes.size()) ? 1356 mRoutes.containsAll(targetRoutes) : false; 1357 } 1358 1359 /** 1360 * Compares this {@code LinkProperties} HttpProxy against the target 1361 * 1362 * @param target LinkProperties to compare. 1363 * @return {@code true} if both are identical, {@code false} otherwise. 1364 * @hide 1365 */ 1366 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) isIdenticalHttpProxy(@onNull LinkProperties target)1367 public boolean isIdenticalHttpProxy(@NonNull LinkProperties target) { 1368 return getHttpProxy() == null ? target.getHttpProxy() == null : 1369 getHttpProxy().equals(target.getHttpProxy()); 1370 } 1371 1372 /** 1373 * Compares this {@code LinkProperties} stacked links against the target 1374 * 1375 * @param target LinkProperties to compare. 1376 * @return {@code true} if both are identical, {@code false} otherwise. 1377 * @hide 1378 */ 1379 @UnsupportedAppUsage isIdenticalStackedLinks(@onNull LinkProperties target)1380 public boolean isIdenticalStackedLinks(@NonNull LinkProperties target) { 1381 if (!mStackedLinks.keySet().equals(target.mStackedLinks.keySet())) { 1382 return false; 1383 } 1384 for (LinkProperties stacked : mStackedLinks.values()) { 1385 // Hashtable values can never be null. 1386 String iface = stacked.getInterfaceName(); 1387 if (!stacked.equals(target.mStackedLinks.get(iface))) { 1388 return false; 1389 } 1390 } 1391 return true; 1392 } 1393 1394 /** 1395 * Compares this {@code LinkProperties} MTU against the target 1396 * 1397 * @param target LinkProperties to compare. 1398 * @return {@code true} if both are identical, {@code false} otherwise. 1399 * @hide 1400 */ isIdenticalMtu(@onNull LinkProperties target)1401 public boolean isIdenticalMtu(@NonNull LinkProperties target) { 1402 return getMtu() == target.getMtu(); 1403 } 1404 1405 /** 1406 * Compares this {@code LinkProperties} Tcp buffer sizes against the target. 1407 * 1408 * @param target LinkProperties to compare. 1409 * @return {@code true} if both are identical, {@code false} otherwise. 1410 * @hide 1411 */ isIdenticalTcpBufferSizes(@onNull LinkProperties target)1412 public boolean isIdenticalTcpBufferSizes(@NonNull LinkProperties target) { 1413 return Objects.equals(mTcpBufferSizes, target.mTcpBufferSizes); 1414 } 1415 1416 /** 1417 * Compares this {@code LinkProperties} NAT64 prefix against the target. 1418 * 1419 * @param target LinkProperties to compare. 1420 * @return {@code true} if both are identical, {@code false} otherwise. 1421 * @hide 1422 */ isIdenticalNat64Prefix(@onNull LinkProperties target)1423 public boolean isIdenticalNat64Prefix(@NonNull LinkProperties target) { 1424 return Objects.equals(mNat64Prefix, target.mNat64Prefix); 1425 } 1426 1427 /** 1428 * Compares this {@code LinkProperties} instance against the target 1429 * LinkProperties in {@code obj}. Two LinkPropertieses are equal if 1430 * all their fields are equal in values. 1431 * 1432 * For collection fields, such as mDnses, containsAll() is used to check 1433 * if two collections contains the same elements, independent of order. 1434 * There are two thoughts regarding containsAll() 1435 * 1. Duplicated elements. eg, (A, B, B) and (A, A, B) are equal. 1436 * 2. Worst case performance is O(n^2). 1437 * 1438 * @param obj the object to be tested for equality. 1439 * @return {@code true} if both objects are equal, {@code false} otherwise. 1440 */ 1441 @Override equals(Object obj)1442 public boolean equals(Object obj) { 1443 if (this == obj) return true; 1444 1445 if (!(obj instanceof LinkProperties)) return false; 1446 1447 LinkProperties target = (LinkProperties) obj; 1448 /* 1449 * This method does not check that stacked interfaces are equal, because 1450 * stacked interfaces are not so much a property of the link as a 1451 * description of connections between links. 1452 */ 1453 return isIdenticalInterfaceName(target) 1454 && isIdenticalAddresses(target) 1455 && isIdenticalDnses(target) 1456 && isIdenticalPrivateDns(target) 1457 && isIdenticalValidatedPrivateDnses(target) 1458 && isIdenticalPcscfs(target) 1459 && isIdenticalRoutes(target) 1460 && isIdenticalHttpProxy(target) 1461 && isIdenticalStackedLinks(target) 1462 && isIdenticalMtu(target) 1463 && isIdenticalTcpBufferSizes(target) 1464 && isIdenticalNat64Prefix(target); 1465 } 1466 1467 /** 1468 * Compares the addresses in this LinkProperties with another 1469 * LinkProperties, examining only addresses on the base link. 1470 * 1471 * @param target a LinkProperties with the new list of addresses 1472 * @return the differences between the addresses. 1473 * @hide 1474 */ compareAddresses(@ullable LinkProperties target)1475 public @NonNull CompareResult<LinkAddress> compareAddresses(@Nullable LinkProperties target) { 1476 /* 1477 * Duplicate the LinkAddresses into removed, we will be removing 1478 * address which are common between mLinkAddresses and target 1479 * leaving the addresses that are different. And address which 1480 * are in target but not in mLinkAddresses are placed in the 1481 * addedAddresses. 1482 */ 1483 return new CompareResult<>(mLinkAddresses, 1484 target != null ? target.getLinkAddresses() : null); 1485 } 1486 1487 /** 1488 * Compares the DNS addresses in this LinkProperties with another 1489 * LinkProperties, examining only DNS addresses on the base link. 1490 * 1491 * @param target a LinkProperties with the new list of dns addresses 1492 * @return the differences between the DNS addresses. 1493 * @hide 1494 */ compareDnses(@ullable LinkProperties target)1495 public @NonNull CompareResult<InetAddress> compareDnses(@Nullable LinkProperties target) { 1496 /* 1497 * Duplicate the InetAddresses into removed, we will be removing 1498 * dns address which are common between mDnses and target 1499 * leaving the addresses that are different. And dns address which 1500 * are in target but not in mDnses are placed in the 1501 * addedAddresses. 1502 */ 1503 return new CompareResult<>(mDnses, target != null ? target.getDnsServers() : null); 1504 } 1505 1506 /** 1507 * Compares the validated private DNS addresses in this LinkProperties with another 1508 * LinkProperties. 1509 * 1510 * @param target a LinkProperties with the new list of validated private dns addresses 1511 * @return the differences between the DNS addresses. 1512 * @hide 1513 */ compareValidatedPrivateDnses( @ullable LinkProperties target)1514 public @NonNull CompareResult<InetAddress> compareValidatedPrivateDnses( 1515 @Nullable LinkProperties target) { 1516 return new CompareResult<>(mValidatedPrivateDnses, 1517 target != null ? target.getValidatedPrivateDnsServers() : null); 1518 } 1519 1520 /** 1521 * Compares all routes in this LinkProperties with another LinkProperties, 1522 * examining both the the base link and all stacked links. 1523 * 1524 * @param target a LinkProperties with the new list of routes 1525 * @return the differences between the routes. 1526 * @hide 1527 */ compareAllRoutes(@ullable LinkProperties target)1528 public @NonNull CompareResult<RouteInfo> compareAllRoutes(@Nullable LinkProperties target) { 1529 /* 1530 * Duplicate the RouteInfos into removed, we will be removing 1531 * routes which are common between mRoutes and target 1532 * leaving the routes that are different. And route address which 1533 * are in target but not in mRoutes are placed in added. 1534 */ 1535 return new CompareResult<>(getAllRoutes(), target != null ? target.getAllRoutes() : null); 1536 } 1537 1538 /** 1539 * Compares all interface names in this LinkProperties with another 1540 * LinkProperties, examining both the the base link and all stacked links. 1541 * 1542 * @param target a LinkProperties with the new list of interface names 1543 * @return the differences between the interface names. 1544 * @hide 1545 */ compareAllInterfaceNames( @ullable LinkProperties target)1546 public @NonNull CompareResult<String> compareAllInterfaceNames( 1547 @Nullable LinkProperties target) { 1548 /* 1549 * Duplicate the interface names into removed, we will be removing 1550 * interface names which are common between this and target 1551 * leaving the interface names that are different. And interface names which 1552 * are in target but not in this are placed in added. 1553 */ 1554 return new CompareResult<>(getAllInterfaceNames(), 1555 target != null ? target.getAllInterfaceNames() : null); 1556 } 1557 1558 1559 /** 1560 * Generate hashcode based on significant fields 1561 * 1562 * Equal objects must produce the same hash code, while unequal objects 1563 * may have the same hash codes. 1564 */ 1565 @Override hashCode()1566 public int hashCode() { 1567 return ((null == mIfaceName) ? 0 : mIfaceName.hashCode() 1568 + mLinkAddresses.size() * 31 1569 + mDnses.size() * 37 1570 + mValidatedPrivateDnses.size() * 61 1571 + ((null == mDomains) ? 0 : mDomains.hashCode()) 1572 + mRoutes.size() * 41 1573 + ((null == mHttpProxy) ? 0 : mHttpProxy.hashCode()) 1574 + mStackedLinks.hashCode() * 47) 1575 + mMtu * 51 1576 + ((null == mTcpBufferSizes) ? 0 : mTcpBufferSizes.hashCode()) 1577 + (mUsePrivateDns ? 57 : 0) 1578 + mPcscfs.size() * 67 1579 + ((null == mPrivateDnsServerName) ? 0 : mPrivateDnsServerName.hashCode()) 1580 + Objects.hash(mNat64Prefix); 1581 } 1582 1583 /** 1584 * Implement the Parcelable interface. 1585 */ writeToParcel(Parcel dest, int flags)1586 public void writeToParcel(Parcel dest, int flags) { 1587 dest.writeString(getInterfaceName()); 1588 dest.writeInt(mLinkAddresses.size()); 1589 for (LinkAddress linkAddress : mLinkAddresses) { 1590 dest.writeParcelable(linkAddress, flags); 1591 } 1592 1593 dest.writeInt(mDnses.size()); 1594 for (InetAddress d : mDnses) { 1595 dest.writeByteArray(d.getAddress()); 1596 } 1597 dest.writeInt(mValidatedPrivateDnses.size()); 1598 for (InetAddress d : mValidatedPrivateDnses) { 1599 dest.writeByteArray(d.getAddress()); 1600 } 1601 dest.writeBoolean(mUsePrivateDns); 1602 dest.writeString(mPrivateDnsServerName); 1603 dest.writeInt(mPcscfs.size()); 1604 for (InetAddress d : mPcscfs) { 1605 dest.writeByteArray(d.getAddress()); 1606 } 1607 dest.writeString(mDomains); 1608 dest.writeInt(mMtu); 1609 dest.writeString(mTcpBufferSizes); 1610 dest.writeInt(mRoutes.size()); 1611 for (RouteInfo route : mRoutes) { 1612 dest.writeParcelable(route, flags); 1613 } 1614 1615 if (mHttpProxy != null) { 1616 dest.writeByte((byte)1); 1617 dest.writeParcelable(mHttpProxy, flags); 1618 } else { 1619 dest.writeByte((byte)0); 1620 } 1621 dest.writeParcelable(mNat64Prefix, 0); 1622 1623 ArrayList<LinkProperties> stackedLinks = new ArrayList<>(mStackedLinks.values()); 1624 dest.writeList(stackedLinks); 1625 } 1626 1627 /** 1628 * Implement the Parcelable interface. 1629 */ 1630 public static final @android.annotation.NonNull Creator<LinkProperties> CREATOR = 1631 new Creator<LinkProperties>() { 1632 public LinkProperties createFromParcel(Parcel in) { 1633 LinkProperties netProp = new LinkProperties(); 1634 1635 String iface = in.readString(); 1636 if (iface != null) { 1637 netProp.setInterfaceName(iface); 1638 } 1639 int addressCount = in.readInt(); 1640 for (int i = 0; i < addressCount; i++) { 1641 netProp.addLinkAddress(in.readParcelable(null)); 1642 } 1643 addressCount = in.readInt(); 1644 for (int i = 0; i < addressCount; i++) { 1645 try { 1646 netProp.addDnsServer(InetAddress.getByAddress(in.createByteArray())); 1647 } catch (UnknownHostException e) { } 1648 } 1649 addressCount = in.readInt(); 1650 for (int i = 0; i < addressCount; i++) { 1651 try { 1652 netProp.addValidatedPrivateDnsServer( 1653 InetAddress.getByAddress(in.createByteArray())); 1654 } catch (UnknownHostException e) { } 1655 } 1656 netProp.setUsePrivateDns(in.readBoolean()); 1657 netProp.setPrivateDnsServerName(in.readString()); 1658 addressCount = in.readInt(); 1659 for (int i = 0; i < addressCount; i++) { 1660 try { 1661 netProp.addPcscfServer(InetAddress.getByAddress(in.createByteArray())); 1662 } catch (UnknownHostException e) { } 1663 } 1664 netProp.setDomains(in.readString()); 1665 netProp.setMtu(in.readInt()); 1666 netProp.setTcpBufferSizes(in.readString()); 1667 addressCount = in.readInt(); 1668 for (int i = 0; i < addressCount; i++) { 1669 netProp.addRoute(in.readParcelable(null)); 1670 } 1671 if (in.readByte() == 1) { 1672 netProp.setHttpProxy(in.readParcelable(null)); 1673 } 1674 netProp.setNat64Prefix(in.readParcelable(null)); 1675 ArrayList<LinkProperties> stackedLinks = new ArrayList<LinkProperties>(); 1676 in.readList(stackedLinks, LinkProperties.class.getClassLoader()); 1677 for (LinkProperties stackedLink: stackedLinks) { 1678 netProp.addStackedLink(stackedLink); 1679 } 1680 return netProp; 1681 } 1682 1683 public LinkProperties[] newArray(int size) { 1684 return new LinkProperties[size]; 1685 } 1686 }; 1687 1688 /** 1689 * Check the valid MTU range based on IPv4 or IPv6. 1690 * @hide 1691 */ isValidMtu(int mtu, boolean ipv6)1692 public static boolean isValidMtu(int mtu, boolean ipv6) { 1693 if (ipv6) { 1694 return mtu >= MIN_MTU_V6 && mtu <= MAX_MTU; 1695 } else { 1696 return mtu >= MIN_MTU && mtu <= MAX_MTU; 1697 } 1698 } 1699 } 1700