1 // Copyright 2012 The Chromium Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 package org.chromium.net; 6 7 import static android.net.ConnectivityManager.TYPE_VPN; 8 import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; 9 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN; 10 import static android.net.NetworkCapabilities.TRANSPORT_VPN; 11 12 import android.Manifest.permission; 13 import android.annotation.SuppressLint; 14 import android.content.BroadcastReceiver; 15 import android.content.Context; 16 import android.content.Intent; 17 import android.content.IntentFilter; 18 import android.content.pm.PackageManager; 19 import android.net.ConnectivityManager; 20 import android.net.ConnectivityManager.NetworkCallback; 21 import android.net.LinkProperties; 22 import android.net.Network; 23 import android.net.NetworkCapabilities; 24 import android.net.NetworkInfo; 25 import android.net.NetworkRequest; 26 import android.net.wifi.WifiInfo; 27 import android.net.wifi.WifiManager; 28 import android.os.Build; 29 import android.os.Handler; 30 import android.os.Looper; 31 import android.telephony.TelephonyManager; 32 33 import androidx.annotation.RequiresApi; 34 import androidx.annotation.VisibleForTesting; 35 36 import org.chromium.base.ApplicationState; 37 import org.chromium.base.ApplicationStatus; 38 import org.chromium.base.ContextUtils; 39 import org.chromium.base.Log; 40 import org.chromium.base.ResettersForTesting; 41 import org.chromium.base.StrictModeContext; 42 import org.chromium.base.TraceEvent; 43 import org.chromium.base.compat.ApiHelperForM; 44 import org.chromium.base.compat.ApiHelperForO; 45 import org.chromium.base.compat.ApiHelperForP; 46 import org.chromium.build.BuildConfig; 47 48 import java.io.IOException; 49 import java.net.Socket; 50 import java.util.Arrays; 51 52 import javax.annotation.concurrent.GuardedBy; 53 54 /** 55 * Used by the NetworkChangeNotifier to listens to platform changes in connectivity. 56 * Note that use of this class requires that the app have the platform 57 * ACCESS_NETWORK_STATE permission. 58 */ 59 // TODO(crbug.com/635567): Fix this properly. 60 @SuppressLint("NewApi") 61 public class NetworkChangeNotifierAutoDetect extends BroadcastReceiver { 62 /** Immutable class representing the state of a device's network. */ 63 public static class NetworkState { 64 private final boolean mConnected; 65 private final int mType; 66 private final int mSubtype; 67 private final boolean mIsMetered; 68 // WIFI SSID of the connection on pre-Marshmallow, NetID starting with Marshmallow. Always 69 // non-null (i.e. instead of null it'll be an empty string) to facilitate .equals(). 70 private final String mNetworkIdentifier; 71 // Indicates if this network is using DNS-over-TLS. 72 private final boolean mIsPrivateDnsActive; 73 // Indicates the DNS-over-TLS server in use, if specified. 74 private final String mPrivateDnsServerName; 75 NetworkState( boolean connected, int type, int subtype, boolean isMetered, String networkIdentifier, boolean isPrivateDnsActive, String privateDnsServerName)76 public NetworkState( 77 boolean connected, 78 int type, 79 int subtype, 80 boolean isMetered, 81 String networkIdentifier, 82 boolean isPrivateDnsActive, 83 String privateDnsServerName) { 84 mConnected = connected; 85 mType = type; 86 mSubtype = subtype; 87 mIsMetered = isMetered; 88 mNetworkIdentifier = networkIdentifier == null ? "" : networkIdentifier; 89 mIsPrivateDnsActive = isPrivateDnsActive; 90 mPrivateDnsServerName = privateDnsServerName == null ? "" : privateDnsServerName; 91 } 92 isConnected()93 public boolean isConnected() { 94 return mConnected; 95 } 96 getNetworkType()97 public int getNetworkType() { 98 return mType; 99 } 100 isMetered()101 public boolean isMetered() { 102 return mIsMetered; 103 } 104 getNetworkSubType()105 public int getNetworkSubType() { 106 return mSubtype; 107 } 108 109 // Always non-null to facilitate .equals(). getNetworkIdentifier()110 public String getNetworkIdentifier() { 111 return mNetworkIdentifier; 112 } 113 114 /** Returns the connection type for the given NetworkState. */ 115 @ConnectionType getConnectionType()116 public int getConnectionType() { 117 if (!isConnected()) { 118 return ConnectionType.CONNECTION_NONE; 119 } 120 return convertToConnectionType(getNetworkType(), getNetworkSubType()); 121 } 122 123 /** Returns the connection cost for the given NetworkState. */ 124 @ConnectionCost getConnectionCost()125 public int getConnectionCost() { 126 if (isMetered()) { 127 return ConnectionCost.METERED; 128 } 129 return ConnectionCost.UNMETERED; 130 } 131 132 /** Returns the connection subtype for the given NetworkState. */ getConnectionSubtype()133 public int getConnectionSubtype() { 134 if (!isConnected()) { 135 return ConnectionSubtype.SUBTYPE_NONE; 136 } 137 138 switch (getNetworkType()) { 139 case ConnectivityManager.TYPE_ETHERNET: 140 case ConnectivityManager.TYPE_WIFI: 141 case ConnectivityManager.TYPE_WIMAX: 142 case ConnectivityManager.TYPE_BLUETOOTH: 143 return ConnectionSubtype.SUBTYPE_UNKNOWN; 144 case ConnectivityManager.TYPE_MOBILE: 145 case ConnectivityManager.TYPE_MOBILE_DUN: 146 case ConnectivityManager.TYPE_MOBILE_HIPRI: 147 // Use information from TelephonyManager to classify the connection. 148 switch (getNetworkSubType()) { 149 case TelephonyManager.NETWORK_TYPE_GPRS: 150 return ConnectionSubtype.SUBTYPE_GPRS; 151 case TelephonyManager.NETWORK_TYPE_EDGE: 152 return ConnectionSubtype.SUBTYPE_EDGE; 153 case TelephonyManager.NETWORK_TYPE_CDMA: 154 return ConnectionSubtype.SUBTYPE_CDMA; 155 case TelephonyManager.NETWORK_TYPE_1xRTT: 156 return ConnectionSubtype.SUBTYPE_1XRTT; 157 case TelephonyManager.NETWORK_TYPE_IDEN: 158 return ConnectionSubtype.SUBTYPE_IDEN; 159 case TelephonyManager.NETWORK_TYPE_UMTS: 160 return ConnectionSubtype.SUBTYPE_UMTS; 161 case TelephonyManager.NETWORK_TYPE_EVDO_0: 162 return ConnectionSubtype.SUBTYPE_EVDO_REV_0; 163 case TelephonyManager.NETWORK_TYPE_EVDO_A: 164 return ConnectionSubtype.SUBTYPE_EVDO_REV_A; 165 case TelephonyManager.NETWORK_TYPE_HSDPA: 166 return ConnectionSubtype.SUBTYPE_HSDPA; 167 case TelephonyManager.NETWORK_TYPE_HSUPA: 168 return ConnectionSubtype.SUBTYPE_HSUPA; 169 case TelephonyManager.NETWORK_TYPE_HSPA: 170 return ConnectionSubtype.SUBTYPE_HSPA; 171 case TelephonyManager.NETWORK_TYPE_EVDO_B: 172 return ConnectionSubtype.SUBTYPE_EVDO_REV_B; 173 case TelephonyManager.NETWORK_TYPE_EHRPD: 174 return ConnectionSubtype.SUBTYPE_EHRPD; 175 case TelephonyManager.NETWORK_TYPE_HSPAP: 176 return ConnectionSubtype.SUBTYPE_HSPAP; 177 case TelephonyManager.NETWORK_TYPE_LTE: 178 return ConnectionSubtype.SUBTYPE_LTE; 179 default: 180 return ConnectionSubtype.SUBTYPE_UNKNOWN; 181 } 182 default: 183 return ConnectionSubtype.SUBTYPE_UNKNOWN; 184 } 185 } 186 187 /** Returns boolean indicating if this network uses DNS-over-TLS. */ isPrivateDnsActive()188 public boolean isPrivateDnsActive() { 189 return mIsPrivateDnsActive; 190 } 191 192 /** Returns the DNS-over-TLS server in use, if specified. */ getPrivateDnsServerName()193 public String getPrivateDnsServerName() { 194 return mPrivateDnsServerName; 195 } 196 } 197 198 /** Queries the ConnectivityManager for information about the current connection. */ 199 @VisibleForTesting 200 public static class ConnectivityManagerDelegate { 201 private final ConnectivityManager mConnectivityManager; 202 ConnectivityManagerDelegate(Context context)203 public ConnectivityManagerDelegate(Context context) { 204 mConnectivityManager = 205 (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); 206 } 207 208 // For testing. ConnectivityManagerDelegate()209 ConnectivityManagerDelegate() { 210 // All the methods below should be overridden. 211 mConnectivityManager = null; 212 } 213 214 /** 215 * @param networkInfo The NetworkInfo for the active network. 216 * @return the info of the network that is available to this app. 217 */ processActiveNetworkInfo(NetworkInfo networkInfo)218 private NetworkInfo processActiveNetworkInfo(NetworkInfo networkInfo) { 219 if (networkInfo == null) { 220 return null; 221 } 222 223 if (networkInfo.isConnected()) { 224 return networkInfo; 225 } 226 227 // If |networkInfo| is BLOCKED, but the app is in the foreground, then it's likely that 228 // Android hasn't finished updating the network access permissions as BLOCKED is only 229 // meant for apps in the background. See https://crbug.com/677365 for more details. 230 231 if (networkInfo.getDetailedState() != NetworkInfo.DetailedState.BLOCKED) { 232 // Network state is not blocked which implies that network access is 233 // unavailable (not just blocked to this app). 234 return null; 235 } 236 237 if (ApplicationStatus.getStateForApplication() 238 != ApplicationState.HAS_RUNNING_ACTIVITIES) { 239 // The app is not in the foreground. 240 return null; 241 } 242 return networkInfo; 243 } 244 245 /** 246 * Returns connection type and status information about the current 247 * default network. 248 */ getNetworkState(WifiManagerDelegate wifiManagerDelegate)249 NetworkState getNetworkState(WifiManagerDelegate wifiManagerDelegate) { 250 Network network = null; 251 NetworkInfo networkInfo; 252 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 253 network = getDefaultNetwork(); 254 networkInfo = getNetworkInfo(network); 255 } else { 256 networkInfo = mConnectivityManager.getActiveNetworkInfo(); 257 } 258 networkInfo = processActiveNetworkInfo(networkInfo); 259 if (networkInfo == null) { 260 return new NetworkState(false, -1, -1, false, null, false, ""); 261 } 262 263 if (network != null) { 264 final NetworkCapabilities capabilities = getNetworkCapabilities(network); 265 boolean isMetered = 266 (capabilities != null 267 && !capabilities.hasCapability( 268 NetworkCapabilities.NET_CAPABILITY_NOT_METERED)); 269 DnsStatus dnsStatus = AndroidNetworkLibrary.getDnsStatus(network); 270 if (dnsStatus == null) { 271 return new NetworkState( 272 true, 273 networkInfo.getType(), 274 networkInfo.getSubtype(), 275 isMetered, 276 String.valueOf(networkToNetId(network)), 277 false, 278 ""); 279 } else { 280 return new NetworkState( 281 true, 282 networkInfo.getType(), 283 networkInfo.getSubtype(), 284 isMetered, 285 String.valueOf(networkToNetId(network)), 286 dnsStatus.getPrivateDnsActive(), 287 dnsStatus.getPrivateDnsServerName()); 288 } 289 } 290 assert Build.VERSION.SDK_INT < Build.VERSION_CODES.M; 291 // If Wifi, then fetch SSID also 292 if (networkInfo.getType() == ConnectivityManager.TYPE_WIFI) { 293 // Since Android 4.2 the SSID can be retrieved from NetworkInfo.getExtraInfo(). 294 if (networkInfo.getExtraInfo() != null && !"".equals(networkInfo.getExtraInfo())) { 295 return new NetworkState( 296 true, 297 networkInfo.getType(), 298 networkInfo.getSubtype(), 299 false, 300 networkInfo.getExtraInfo(), 301 false, 302 ""); 303 } 304 // Fetch WiFi SSID directly from WifiManagerDelegate if not in NetworkInfo. 305 return new NetworkState( 306 true, 307 networkInfo.getType(), 308 networkInfo.getSubtype(), 309 false, 310 wifiManagerDelegate.getWifiSsid(), 311 false, 312 ""); 313 } 314 return new NetworkState( 315 true, networkInfo.getType(), networkInfo.getSubtype(), false, null, false, ""); 316 } 317 318 /** 319 * Fetches NetworkInfo for |network|. Does not account for underlying VPNs; see 320 * getNetworkInfo(Network) for a method that does. 321 */ 322 NetworkInfo getRawNetworkInfo(Network network) { 323 try { 324 return mConnectivityManager.getNetworkInfo(network); 325 } catch (NullPointerException firstException) { 326 // Rarely this unexpectedly throws. Retry or just return {@code null} if it fails. 327 try { 328 return mConnectivityManager.getNetworkInfo(network); 329 } catch (NullPointerException secondException) { 330 return null; 331 } 332 } 333 } 334 335 /** Fetches NetworkInfo for |network|. */ 336 NetworkInfo getNetworkInfo(Network network) { 337 NetworkInfo networkInfo = getRawNetworkInfo(network); 338 if (networkInfo != null && networkInfo.getType() == TYPE_VPN) { 339 // When a VPN is in place the underlying network type can be queried via 340 // getActiveNetworkInfo() thanks to 341 // https://android.googlesource.com/platform/frameworks/base/+/d6a7980d 342 networkInfo = mConnectivityManager.getActiveNetworkInfo(); 343 } 344 return networkInfo; 345 } 346 347 /** Returns connection type for |network|. */ 348 @ConnectionType 349 int getConnectionType(Network network) { 350 NetworkInfo networkInfo = getNetworkInfo(network); 351 if (networkInfo != null && networkInfo.isConnected()) { 352 return convertToConnectionType(networkInfo.getType(), networkInfo.getSubtype()); 353 } 354 return ConnectionType.CONNECTION_NONE; 355 } 356 357 /** 358 * Returns all connected networks. This may include networks that aren't useful 359 * to Chrome (e.g. MMS, IMS, FOTA etc) or aren't accessible to Chrome (e.g. a VPN for 360 * another user); use {@link getAllNetworks} for a filtered list. 361 */ 362 @VisibleForTesting 363 protected Network[] getAllNetworksUnfiltered() { 364 Network[] networks = mConnectivityManager.getAllNetworks(); 365 // Very rarely this API inexplicably returns {@code null}, crbug.com/721116. 366 return networks == null ? new Network[0] : networks; 367 } 368 369 /** 370 * Returns {@code true} if {@code network} applies to (and hence is accessible) to the 371 * current user. 372 */ 373 @VisibleForTesting 374 protected boolean vpnAccessible(Network network) { 375 // Determine if the VPN applies to the current user by seeing if a socket can be bound 376 // to the VPN. 377 Socket s = new Socket(); 378 // Disable detectUntaggedSockets StrictMode policy to avoid false positives, as |s| 379 // isn't used to send or receive traffic. https://crbug.com/946531 380 try (StrictModeContext ignored = StrictModeContext.allowAllVmPolicies()) { 381 // Avoid using network.getSocketFactory().createSocket() because it leaks. 382 // https://crbug.com/805424 383 network.bindSocket(s); 384 } catch (IOException e) { 385 // Failed to bind so this VPN isn't for the current user to use. 386 return false; 387 } finally { 388 try { 389 s.close(); 390 } catch (IOException e) { 391 // Not worth taking action on a failed close. 392 } 393 } 394 return true; 395 } 396 397 /** 398 * Return the NetworkCapabilities for {@code network}, or {@code null} if they cannot 399 * be retrieved (e.g. {@code network} has disconnected). 400 */ 401 @VisibleForTesting 402 protected NetworkCapabilities getNetworkCapabilities(Network network) { 403 final int retryCount = 2; 404 for (int i = 0; i < retryCount; ++i) { 405 // This try-catch is a workaround for https://crbug.com/1218536. We ignore 406 // the exception intentionally. 407 try { 408 return mConnectivityManager.getNetworkCapabilities(network); 409 } catch (SecurityException e) { 410 // Do nothing. 411 } 412 } 413 return null; 414 } 415 416 /** 417 * Registers networkCallback to receive notifications about networks 418 * that satisfy networkRequest. 419 */ 420 void registerNetworkCallback( 421 NetworkRequest networkRequest, NetworkCallback networkCallback, Handler handler) { 422 // Starting with Oreo specifying a Handler is allowed. Use this to avoid thread-hops. 423 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 424 try (StrictModeContext ignored = StrictModeContext.allowDiskReads()) { 425 // Samsung Android O devices aggressively trigger StrictMode violations. 426 // See https://crbug.com/1450175 for detail. 427 mConnectivityManager.registerNetworkCallback( 428 networkRequest, networkCallback, handler); 429 } 430 } else { 431 mConnectivityManager.registerNetworkCallback(networkRequest, networkCallback); 432 } 433 } 434 435 /** 436 * Registers networkCallback to receive notifications about default network. 437 * Only callable on P and newer releases. 438 */ 439 @RequiresApi(Build.VERSION_CODES.P) 440 void registerDefaultNetworkCallback(NetworkCallback networkCallback, Handler handler) { 441 ApiHelperForO.registerDefaultNetworkCallback( 442 mConnectivityManager, networkCallback, handler); 443 } 444 445 /** Unregisters networkCallback from receiving notifications. */ 446 void unregisterNetworkCallback(NetworkCallback networkCallback) { 447 mConnectivityManager.unregisterNetworkCallback(networkCallback); 448 } 449 450 /** 451 * Returns the current default {@link Network}, or {@code null} if disconnected. 452 */ 453 @VisibleForTesting 454 public Network getDefaultNetwork() { 455 Network defaultNetwork = null; 456 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 457 defaultNetwork = ApiHelperForM.getActiveNetwork(mConnectivityManager); 458 // getActiveNetwork() returning null cannot be trusted to indicate disconnected 459 // as it suffers from https://crbug.com/677365. 460 if (defaultNetwork != null) { 461 return defaultNetwork; 462 } 463 } 464 // Android Lollipop had no API to get the default network; only an 465 // API to return the NetworkInfo for the default network. To 466 // determine the default network one can find the network with 467 // type matching that of the default network. 468 final NetworkInfo defaultNetworkInfo = mConnectivityManager.getActiveNetworkInfo(); 469 if (defaultNetworkInfo == null) { 470 return null; 471 } 472 final Network[] networks = getAllNetworksFiltered(this, null); 473 for (Network network : networks) { 474 final NetworkInfo networkInfo = getRawNetworkInfo(network); 475 if (networkInfo != null 476 && (networkInfo.getType() == defaultNetworkInfo.getType() 477 // getActiveNetworkInfo() will not return TYPE_VPN types due to 478 // https://android.googlesource.com/platform/frameworks/base/+/d6a7980d 479 // so networkInfo.getType() can't be matched against 480 // defaultNetworkInfo.getType() but networkInfo.getType() should 481 // be TYPE_VPN. In the case of a VPN, getAllNetworks() will have 482 // returned just this VPN if it applies. 483 || networkInfo.getType() == TYPE_VPN)) { 484 // Android 10+ devices occasionally return multiple networks 485 // of the same type that are stuck in the CONNECTING state. 486 // Now that Java asserts are enabled, ignore these zombie 487 // networks here to avoid hitting the assert below. crbug.com/1361170 488 if (defaultNetwork != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { 489 // If `network` is CONNECTING, ignore it. 490 if (networkInfo.getDetailedState() 491 == NetworkInfo.DetailedState.CONNECTING) { 492 continue; 493 } 494 // If `defaultNetwork` is CONNECTING, ignore it. 495 NetworkInfo prevDefaultNetworkInfo = getRawNetworkInfo(defaultNetwork); 496 if (prevDefaultNetworkInfo != null 497 && prevDefaultNetworkInfo.getDetailedState() 498 == NetworkInfo.DetailedState.CONNECTING) { 499 defaultNetwork = null; 500 } 501 } 502 if (defaultNetwork != null) { 503 // TODO(https://crbug.com/1361170): Investigate why there are multiple 504 // connected networks. 505 Log.e( 506 TAG, 507 "There should not be multiple connected " 508 + "networks of the same type. At least as of Android " 509 + "Marshmallow this is not supported. If this becomes " 510 + "supported this assertion may trigger."); 511 } 512 defaultNetwork = network; 513 } 514 } 515 return defaultNetwork; 516 } 517 } 518 519 /** Queries the WifiManager for SSID of the current Wifi connection. */ 520 static class WifiManagerDelegate { 521 private final Context mContext; 522 // Lock all members below. 523 private final Object mLock = new Object(); 524 525 // Has mHasWifiPermission been calculated. 526 @GuardedBy("mLock") 527 private boolean mHasWifiPermissionComputed; 528 529 // Only valid when mHasWifiPermissionComputed is set. 530 @GuardedBy("mLock") 531 private boolean mHasWifiPermission; 532 533 // Only valid when mHasWifiPermission is set. 534 @GuardedBy("mLock") 535 private WifiManager mWifiManager; 536 WifiManagerDelegate(Context context)537 WifiManagerDelegate(Context context) { 538 // Getting SSID requires more permissions in later Android releases. 539 assert Build.VERSION.SDK_INT < Build.VERSION_CODES.M; 540 mContext = context; 541 } 542 543 // For testing. 544 WifiManagerDelegate() { 545 // All the methods below should be overridden. 546 mContext = null; 547 } 548 549 // Lazily determine if app has ACCESS_WIFI_STATE permission. 550 @GuardedBy("mLock") 551 @SuppressLint("WifiManagerPotentialLeak") 552 private boolean hasPermissionLocked() { 553 if (mHasWifiPermissionComputed) { 554 return mHasWifiPermission; 555 } 556 mHasWifiPermission = 557 mContext.getPackageManager() 558 .checkPermission( 559 permission.ACCESS_WIFI_STATE, mContext.getPackageName()) 560 == PackageManager.PERMISSION_GRANTED; 561 // TODO(crbug.com/635567): Fix lint properly. 562 mWifiManager = 563 mHasWifiPermission 564 ? (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE) 565 : null; 566 mHasWifiPermissionComputed = true; 567 return mHasWifiPermission; 568 } 569 570 String getWifiSsid() { 571 // Synchronized because this method can be called on multiple threads (e.g. mLooper 572 // from a private caller, and another thread calling a public API like 573 // getCurrentNetworkState) and is otherwise racy. 574 synchronized (mLock) { 575 // If app has permission it's faster to query WifiManager directly. 576 if (hasPermissionLocked()) { 577 WifiInfo wifiInfo = getWifiInfoLocked(); 578 if (wifiInfo != null) { 579 return wifiInfo.getSSID(); 580 } 581 return ""; 582 } 583 } 584 return AndroidNetworkLibrary.getWifiSSID(); 585 } 586 587 // Fetches WifiInfo and records UMA for NullPointerExceptions. 588 @GuardedBy("mLock") 589 private WifiInfo getWifiInfoLocked() { 590 try { 591 return mWifiManager.getConnectionInfo(); 592 } catch (NullPointerException firstException) { 593 // Rarely this unexpectedly throws. Retry or just return {@code null} if it fails. 594 try { 595 return mWifiManager.getConnectionInfo(); 596 } catch (NullPointerException secondException) { 597 return null; 598 } 599 } 600 } 601 } 602 603 // NetworkCallback used for listening for changes to the default network. 604 private class DefaultNetworkCallback extends NetworkCallback { 605 // If registered, notify connectionTypeChanged() to look for changes. 606 @Override 607 public void onAvailable(Network network) { 608 if (mRegistered) { 609 connectionTypeChanged(); 610 } 611 } 612 613 @Override 614 public void onLost(final Network network) { 615 onAvailable(null); 616 } 617 618 // LinkProperties changes include enabling/disabling DNS-over-TLS. 619 @Override 620 public void onLinkPropertiesChanged(Network network, LinkProperties linkProperties) { 621 onAvailable(null); 622 } 623 } 624 625 // NetworkCallback used for listening for changes to the default network. 626 // This version has two major bug fixes over the above DefaultNetworkCallback: 627 // 1. Avoids avoids calling synchronous ConnectivityManager methods which is prohibited inside 628 // NetworkCallbacks see "Do NOT call" here: 629 // 630 // https://developer.android.com/reference/android/net/ConnectivityManager.NetworkCallback#onAvailable(android.net.Network) 631 // 2. Catches onCapabilitiesChanged() which includes cellular connections transitioning to and 632 // from SUSPENDED states. Failing to catch this could leave the NetworkChangeNotifier in 633 // an incorrect disconnected state, see crbug.com/1120144. 634 @RequiresApi(Build.VERSION_CODES.P) 635 private class AndroidRDefaultNetworkCallback extends NetworkCallback { 636 LinkProperties mLinkProperties; 637 NetworkCapabilities mNetworkCapabilities; 638 639 @Override 640 public void onAvailable(Network network) { 641 // Clear accumulated state and wait for new state to be received. 642 // Android guarantees we receive onLinkPropertiesChanged and 643 // onNetworkCapabilities calls after onAvailable: 644 // https://developer.android.com/reference/android/net/ConnectivityManager.NetworkCallback#onCapabilitiesChanged(android.net.Network,%20android.net.NetworkCapabilities) 645 // so the call to connectionTypeChangedTo() is done when we have received the 646 // LinkProperties and NetworkCapabilities. 647 mLinkProperties = null; 648 mNetworkCapabilities = null; 649 } 650 651 @Override 652 public void onLost(final Network network) { 653 mLinkProperties = null; 654 mNetworkCapabilities = null; 655 if (mRegistered) { 656 connectionTypeChangedTo(new NetworkState(false, -1, -1, false, null, false, "")); 657 } 658 } 659 660 // LinkProperties changes include enabling/disabling DNS-over-TLS. 661 @Override 662 public void onLinkPropertiesChanged(Network network, LinkProperties linkProperties) { 663 mLinkProperties = linkProperties; 664 if (mRegistered && mLinkProperties != null && mNetworkCapabilities != null) { 665 connectionTypeChangedTo(getNetworkState(network)); 666 } 667 } 668 669 // CapabilitiesChanged includes cellular connections switching in and out of SUSPENDED. 670 @Override 671 public void onCapabilitiesChanged( 672 Network network, NetworkCapabilities networkCapabilities) { 673 mNetworkCapabilities = networkCapabilities; 674 if (mRegistered && mLinkProperties != null && mNetworkCapabilities != null) { 675 connectionTypeChangedTo(getNetworkState(network)); 676 } 677 } 678 679 // Calculate current NetworkState. Unlike getNetworkState(), this method avoids calling 680 // synchronous ConnectivityManager methods which is prohibited inside NetworkCallbacks see 681 // "Do NOT call" here: 682 // https://developer.android.com/reference/android/net/ConnectivityManager.NetworkCallback#onAvailable(android.net.Network) 683 private NetworkState getNetworkState(Network network) { 684 // Initialize to unknown values then extract more accurate info 685 int type = -1; 686 int subtype = -1; 687 if (mNetworkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) 688 || mNetworkCapabilities.hasTransport( 689 NetworkCapabilities.TRANSPORT_WIFI_AWARE)) { 690 type = ConnectivityManager.TYPE_WIFI; 691 } else if (mNetworkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) { 692 type = ConnectivityManager.TYPE_MOBILE; 693 // To get the subtype we need to make a synchronous ConnectivityManager call 694 // unfortunately. It's recommended to use TelephonyManager.getDataNetworkType() 695 // but that requires an additional permission. Worst case this might be inaccurate 696 // but getting the correct subtype is much much less important than getting the 697 // correct type. Incorrect type could make Chrome behave like it's offline, 698 // incorrect subtype will just make cellular bandwidth estimates incorrect. 699 NetworkInfo networkInfo = mConnectivityManagerDelegate.getRawNetworkInfo(network); 700 if (networkInfo != null) { 701 subtype = networkInfo.getSubtype(); 702 } 703 } else if (mNetworkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET)) { 704 type = ConnectivityManager.TYPE_ETHERNET; 705 } else if (mNetworkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_BLUETOOTH)) { 706 type = ConnectivityManager.TYPE_BLUETOOTH; 707 } else if (mNetworkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN)) { 708 // Use ConnectivityManagerDelegate.getNetworkInfo(network) to find underlying 709 // network which has a more useful transport type. crbug.com/1208022 710 NetworkInfo networkInfo = mConnectivityManagerDelegate.getNetworkInfo(network); 711 type = networkInfo != null ? networkInfo.getType() : ConnectivityManager.TYPE_VPN; 712 } 713 boolean isMetered = 714 !mNetworkCapabilities.hasCapability( 715 NetworkCapabilities.NET_CAPABILITY_NOT_METERED); 716 return new NetworkState( 717 true, 718 type, 719 subtype, 720 isMetered, 721 String.valueOf(networkToNetId(network)), 722 ApiHelperForP.isPrivateDnsActive(mLinkProperties), 723 ApiHelperForP.getPrivateDnsServerName(mLinkProperties)); 724 } 725 } 726 727 // This class gets called back by ConnectivityManager whenever networks come 728 // and go. It gets called back on a special handler thread 729 // ConnectivityManager creates for making the callbacks. The callbacks in 730 // turn post to mLooper where mObserver lives. 731 private class MyNetworkCallback extends NetworkCallback { 732 // If non-null, this indicates a VPN is in place for the current user, and no other 733 // networks are accessible. 734 private Network mVpnInPlace; 735 736 // Initialize mVpnInPlace. 737 void initializeVpnInPlace() { 738 final Network[] networks = getAllNetworksFiltered(mConnectivityManagerDelegate, null); 739 mVpnInPlace = null; 740 // If the filtered list of networks contains just a VPN, then that VPN is in place. 741 if (networks.length == 1) { 742 final NetworkCapabilities capabilities = 743 mConnectivityManagerDelegate.getNetworkCapabilities(networks[0]); 744 if (capabilities != null && capabilities.hasTransport(TRANSPORT_VPN)) { 745 mVpnInPlace = networks[0]; 746 } 747 } 748 } 749 750 /** 751 * Should changes to network {@code network} be ignored due to a VPN being in place 752 * and blocking direct access to {@code network}? 753 * @param network Network to possibly consider ignoring changes to. 754 */ 755 private boolean ignoreNetworkDueToVpn(Network network) { 756 return mVpnInPlace != null && !mVpnInPlace.equals(network); 757 } 758 759 /** 760 * Should changes to connected network {@code network} be ignored? 761 * @param network Network to possibly consider ignoring changes to. 762 * @param capabilities {@code NetworkCapabilities} for {@code network} if known, otherwise 763 * {@code null}. 764 * @return {@code true} when either: {@code network} is an inaccessible VPN, or has already 765 * disconnected. 766 */ 767 private boolean ignoreConnectedInaccessibleVpn( 768 Network network, NetworkCapabilities capabilities) { 769 // Fetch capabilities if not provided. 770 if (capabilities == null) { 771 capabilities = mConnectivityManagerDelegate.getNetworkCapabilities(network); 772 } 773 // Ignore inaccessible VPNs as they don't apply to Chrome. 774 return capabilities == null 775 || capabilities.hasTransport(TRANSPORT_VPN) 776 && !mConnectivityManagerDelegate.vpnAccessible(network); 777 } 778 779 /** 780 * Should changes to connected network {@code network} be ignored? 781 * @param network Network to possible consider ignoring changes to. 782 * @param capabilities {@code NetworkCapabilities} for {@code network} if known, otherwise 783 * {@code null}. 784 */ 785 private boolean ignoreConnectedNetwork(Network network, NetworkCapabilities capabilities) { 786 return ignoreNetworkDueToVpn(network) 787 || ignoreConnectedInaccessibleVpn(network, capabilities); 788 } 789 790 @Override 791 public void onAvailable(Network network) { 792 try (TraceEvent e = TraceEvent.scoped("NetworkChangeNotifierCallback::onAvailable")) { 793 final NetworkCapabilities capabilities = 794 mConnectivityManagerDelegate.getNetworkCapabilities(network); 795 if (ignoreConnectedNetwork(network, capabilities)) { 796 return; 797 } 798 final boolean makeVpnDefault = 799 capabilities.hasTransport(TRANSPORT_VPN) 800 && 801 // Only make the VPN the default if it isn't already. 802 (mVpnInPlace == null || !network.equals(mVpnInPlace)); 803 if (makeVpnDefault) { 804 mVpnInPlace = network; 805 } 806 final long netId = networkToNetId(network); 807 @ConnectionType 808 final int connectionType = mConnectivityManagerDelegate.getConnectionType(network); 809 runOnThread( 810 new Runnable() { 811 @Override 812 public void run() { 813 mObserver.onNetworkConnect(netId, connectionType); 814 if (makeVpnDefault) { 815 // Make VPN the default network. 816 mObserver.onConnectionTypeChanged(connectionType); 817 // Purge all other networks as they're inaccessible to Chrome 818 // now. 819 mObserver.purgeActiveNetworkList(new long[] {netId}); 820 } 821 } 822 }); 823 } 824 } 825 826 @Override 827 public void onCapabilitiesChanged( 828 Network network, NetworkCapabilities networkCapabilities) { 829 try (TraceEvent e = 830 TraceEvent.scoped("NetworkChangeNotifierCallback::onCapabilitiesChanged")) { 831 if (ignoreConnectedNetwork(network, networkCapabilities)) { 832 return; 833 } 834 // A capabilities change may indicate the ConnectionType has changed, 835 // so forward the new ConnectionType along to observer. 836 final long netId = networkToNetId(network); 837 final int connectionType = mConnectivityManagerDelegate.getConnectionType(network); 838 runOnThread( 839 new Runnable() { 840 @Override 841 public void run() { 842 mObserver.onNetworkConnect(netId, connectionType); 843 } 844 }); 845 } 846 } 847 848 @Override 849 public void onLosing(Network network, int maxMsToLive) { 850 try (TraceEvent e = TraceEvent.scoped("NetworkChangeNotifierCallback::onLosing")) { 851 if (ignoreConnectedNetwork(network, null)) { 852 return; 853 } 854 final long netId = networkToNetId(network); 855 runOnThread( 856 new Runnable() { 857 @Override 858 public void run() { 859 mObserver.onNetworkSoonToDisconnect(netId); 860 } 861 }); 862 } 863 } 864 865 @Override 866 public void onLost(final Network network) { 867 try (TraceEvent e = TraceEvent.scoped("NetworkChangeNotifierCallback::onLost")) { 868 if (ignoreNetworkDueToVpn(network)) { 869 return; 870 } 871 runOnThread( 872 new Runnable() { 873 @Override 874 public void run() { 875 mObserver.onNetworkDisconnect(networkToNetId(network)); 876 } 877 }); 878 // If the VPN is going away, inform observer that other networks that were 879 // previously hidden by ignoreNetworkDueToVpn() are now available for use, now that 880 // this user's traffic is not forced into the VPN. 881 if (mVpnInPlace != null) { 882 assert network.equals(mVpnInPlace); 883 mVpnInPlace = null; 884 for (Network newNetwork : 885 getAllNetworksFiltered(mConnectivityManagerDelegate, network)) { 886 onAvailable(newNetwork); 887 } 888 @ConnectionType 889 final int newConnectionType = getCurrentNetworkState().getConnectionType(); 890 runOnThread( 891 new Runnable() { 892 @Override 893 public void run() { 894 mObserver.onConnectionTypeChanged(newConnectionType); 895 } 896 }); 897 } 898 } 899 } 900 } 901 902 /** 903 * Abstract class for providing a policy regarding when the NetworkChangeNotifier 904 * should listen for network changes. 905 */ 906 public abstract static class RegistrationPolicy { 907 private NetworkChangeNotifierAutoDetect mNotifier; 908 909 /** Start listening for network changes. */ 910 protected final void register() { 911 assert mNotifier != null; 912 mNotifier.register(); 913 } 914 915 /** Stop listening for network changes. */ 916 protected final void unregister() { 917 assert mNotifier != null; 918 mNotifier.unregister(); 919 } 920 921 /** 922 * Initializes the policy with the notifier, overriding subclasses should always 923 * call this method. 924 */ 925 protected void init(NetworkChangeNotifierAutoDetect notifier) { 926 mNotifier = notifier; 927 } 928 929 protected abstract void destroy(); 930 } 931 932 private static final String TAG = NetworkChangeNotifierAutoDetect.class.getSimpleName(); 933 private static final int UNKNOWN_LINK_SPEED = -1; 934 935 // {@link Looper} for the thread this object lives on. 936 private final Looper mLooper; 937 // Used to post to the thread this object lives on. 938 private final Handler mHandler; 939 // {@link IntentFilter} for incoming global broadcast {@link Intent}s this object listens for. 940 private final NetworkConnectivityIntentFilter mIntentFilter; 941 // Notifications are sent to this {@link Observer}. 942 private final Observer mObserver; 943 private final RegistrationPolicy mRegistrationPolicy; 944 // Starting with Android Pie, used to detect changes in default network. 945 private NetworkCallback mDefaultNetworkCallback; 946 947 // mConnectivityManagerDelegates and mWifiManagerDelegate are only non-final for testing. 948 private ConnectivityManagerDelegate mConnectivityManagerDelegate; 949 private WifiManagerDelegate mWifiManagerDelegate; 950 // mNetworkCallback and mNetworkRequest are only non-null in Android L and above. 951 // mNetworkCallback will be null if ConnectivityManager.registerNetworkCallback() ever fails. 952 private MyNetworkCallback mNetworkCallback; 953 private NetworkRequest mNetworkRequest; 954 private boolean mRegistered; 955 private NetworkState mNetworkState; 956 // When a BroadcastReceiver is registered for a sticky broadcast that has been sent out at 957 // least once, onReceive() will immediately be called. mIgnoreNextBroadcast is set to true 958 // when this class is registered in such a circumstance, and indicates that the next 959 // invokation of onReceive() can be ignored as the state hasn't actually changed. Immediately 960 // prior to mIgnoreNextBroadcast being set, all internal state is updated to the current device 961 // state so were this initial onReceive() call not ignored, no signals would be passed to 962 // observers anyhow as the state hasn't changed. This is simply an optimization to avoid 963 // useless work. 964 private boolean mIgnoreNextBroadcast; 965 // mSignal is set to false when it's not worth calculating if signals to Observers should 966 // be sent out because this class is being constructed and the internal state has just 967 // been updated to the current device state, so no signals are necessary. This is simply an 968 // optimization to avoid useless work. 969 private boolean mShouldSignalObserver; 970 // Indicates if ConnectivityManager.registerNetworkRequest() ever failed. When true, no 971 // network-specific callbacks (e.g. Observer.onNetwork*() ) will be issued. 972 private boolean mRegisterNetworkCallbackFailed; 973 974 /** Observer interface by which observer is notified of network changes. */ 975 public static interface Observer { 976 /** Called when default network changes. */ 977 public void onConnectionTypeChanged(@ConnectionType int newConnectionType); 978 979 /** Called when connection cost of default network changes. */ 980 public void onConnectionCostChanged(int newConnectionCost); 981 982 /** Called when connection subtype of default network changes. */ 983 public void onConnectionSubtypeChanged(int newConnectionSubtype); 984 985 /** 986 * Called when device connects to network with NetID netId. For 987 * example device associates with a WiFi access point. 988 * connectionType is the type of the network; a member of 989 * ConnectionType. Only called on Android L and above. 990 */ 991 public void onNetworkConnect(long netId, int connectionType); 992 993 /** 994 * Called when device determines the connection to the network with 995 * NetID netId is no longer preferred, for example when a device 996 * transitions from cellular to WiFi it might deem the cellular 997 * connection no longer preferred. The device will disconnect from 998 * the network in 30s allowing network communications on that network 999 * to wrap up. Only called on Android L and above. 1000 */ 1001 public void onNetworkSoonToDisconnect(long netId); 1002 1003 /** 1004 * Called when device disconnects from network with NetID netId. 1005 * Only called on Android L and above. 1006 */ 1007 public void onNetworkDisconnect(long netId); 1008 1009 /** 1010 * Called to cause a purge of cached lists of active networks, of any 1011 * networks not in the accompanying list of active networks. This is 1012 * issued if a period elapsed where disconnected notifications may have 1013 * been missed, and acts to keep cached lists of active networks 1014 * accurate. Only called on Android L and above. 1015 */ 1016 public void purgeActiveNetworkList(long[] activeNetIds); 1017 } 1018 1019 /** 1020 * Constructs a NetworkChangeNotifierAutoDetect. Lives on calling thread, receives broadcast 1021 * notifications on the UI thread and forwards the notifications to be processed on the calling 1022 * thread. 1023 * @param policy The RegistrationPolicy which determines when this class should watch 1024 * for network changes (e.g. see (@link RegistrationPolicyAlwaysRegister} and 1025 * {@link RegistrationPolicyApplicationStatus}). 1026 */ 1027 public NetworkChangeNotifierAutoDetect(Observer observer, RegistrationPolicy policy) { 1028 mLooper = Looper.myLooper(); 1029 mHandler = new Handler(mLooper); 1030 mObserver = observer; 1031 mConnectivityManagerDelegate = 1032 new ConnectivityManagerDelegate(ContextUtils.getApplicationContext()); 1033 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { 1034 mWifiManagerDelegate = new WifiManagerDelegate(ContextUtils.getApplicationContext()); 1035 } 1036 mNetworkCallback = new MyNetworkCallback(); 1037 mNetworkRequest = 1038 new NetworkRequest.Builder() 1039 .addCapability(NET_CAPABILITY_INTERNET) 1040 // Need to hear about VPNs too. 1041 .removeCapability(NET_CAPABILITY_NOT_VPN) 1042 .build(); 1043 1044 // Use AndroidRDefaultNetworkCallback to fix Android R issue crbug.com/1120144. 1045 // This NetworkCallback could be used on O+ (where onCapabilitiesChanged and 1046 // onLinkProperties callbacks are guaranteed to be called after onAvailable) 1047 // but is only necessary on Android R+. For now it's only used on R+ to reduce 1048 // churn. 1049 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { 1050 mDefaultNetworkCallback = new AndroidRDefaultNetworkCallback(); 1051 } else { 1052 mDefaultNetworkCallback = 1053 Build.VERSION.SDK_INT >= Build.VERSION_CODES.P 1054 ? new DefaultNetworkCallback() 1055 : null; 1056 } 1057 mNetworkState = getCurrentNetworkState(); 1058 mIntentFilter = new NetworkConnectivityIntentFilter(); 1059 mIgnoreNextBroadcast = false; 1060 mShouldSignalObserver = false; 1061 mRegistrationPolicy = policy; mRegistrationPolicy.init(this)1062 mRegistrationPolicy.init(this); 1063 mShouldSignalObserver = true; 1064 } 1065 onThread()1066 private boolean onThread() { 1067 return mLooper == Looper.myLooper(); 1068 } 1069 assertOnThread()1070 private void assertOnThread() { 1071 if (BuildConfig.ENABLE_ASSERTS && !onThread()) { 1072 throw new IllegalStateException( 1073 "Must be called on NetworkChangeNotifierAutoDetect thread."); 1074 } 1075 } 1076 runOnThread(Runnable r)1077 private void runOnThread(Runnable r) { 1078 if (onThread()) { 1079 r.run(); 1080 } else { 1081 // Once execution begins on the correct thread, make sure unregister() hasn't 1082 // been called in the mean time. 1083 mHandler.post( 1084 () -> { 1085 if (mRegistered) r.run(); 1086 }); 1087 } 1088 } 1089 1090 /** Allows overriding the ConnectivityManagerDelegate for tests. */ setConnectivityManagerDelegateForTests(ConnectivityManagerDelegate delegate)1091 void setConnectivityManagerDelegateForTests(ConnectivityManagerDelegate delegate) { 1092 var oldValue = mConnectivityManagerDelegate; 1093 mConnectivityManagerDelegate = delegate; 1094 ResettersForTesting.register(() -> mConnectivityManagerDelegate = oldValue); 1095 } 1096 1097 /** Allows overriding the WifiManagerDelegate for tests. */ setWifiManagerDelegateForTests(WifiManagerDelegate delegate)1098 void setWifiManagerDelegateForTests(WifiManagerDelegate delegate) { 1099 var oldValue = mWifiManagerDelegate; 1100 mWifiManagerDelegate = delegate; 1101 ResettersForTesting.register(() -> mWifiManagerDelegate = oldValue); 1102 } 1103 1104 @VisibleForTesting getRegistrationPolicy()1105 RegistrationPolicy getRegistrationPolicy() { 1106 return mRegistrationPolicy; 1107 } 1108 1109 /** Returns whether the object has registered to receive network connectivity intents. */ isReceiverRegisteredForTesting()1110 boolean isReceiverRegisteredForTesting() { 1111 return mRegistered; 1112 } 1113 destroy()1114 public void destroy() { 1115 assertOnThread(); 1116 mRegistrationPolicy.destroy(); 1117 unregister(); 1118 } 1119 1120 /** Registers a BroadcastReceiver in the given context. */ register()1121 public void register() { 1122 assertOnThread(); 1123 if (mRegistered) { 1124 // Even when registered previously, Android may not send callbacks about change of 1125 // network state when the device screen is turned on from off. Get the most up-to-date 1126 // network state. See https://crbug.com/1007998 for more details. 1127 connectionTypeChanged(); 1128 return; 1129 } 1130 1131 if (mShouldSignalObserver) { 1132 connectionTypeChanged(); 1133 } 1134 if (mDefaultNetworkCallback != null) { 1135 try { 1136 mConnectivityManagerDelegate.registerDefaultNetworkCallback( 1137 mDefaultNetworkCallback, mHandler); 1138 } catch (RuntimeException e) { 1139 // If registering a default network callback failed, fallback to 1140 // listening for CONNECTIVITY_ACTION broadcast. 1141 mDefaultNetworkCallback = null; 1142 } 1143 } 1144 if (mDefaultNetworkCallback == null) { 1145 // When registering for a sticky broadcast, like CONNECTIVITY_ACTION, if 1146 // registerReceiver returns non-null, it means the broadcast was previously issued and 1147 // onReceive() will be immediately called with this previous Intent. Since this initial 1148 // callback doesn't actually indicate a network change, we can ignore it by setting 1149 // mIgnoreNextBroadcast. 1150 mIgnoreNextBroadcast = 1151 ContextUtils.registerProtectedBroadcastReceiver( 1152 ContextUtils.getApplicationContext(), this, mIntentFilter) 1153 != null; 1154 } 1155 mRegistered = true; 1156 1157 if (mNetworkCallback != null) { 1158 mNetworkCallback.initializeVpnInPlace(); 1159 try { 1160 mConnectivityManagerDelegate.registerNetworkCallback( 1161 mNetworkRequest, mNetworkCallback, mHandler); 1162 } catch (RuntimeException e) { 1163 mRegisterNetworkCallbackFailed = true; 1164 // If Android thinks this app has used up all available NetworkRequests, don't 1165 // bother trying to register any more callbacks as Android will still think 1166 // all available NetworkRequests are used up and fail again needlessly. 1167 // Also don't bother unregistering as this call didn't actually register. 1168 // See crbug.com/791025 for more info. 1169 mNetworkCallback = null; 1170 } 1171 if (!mRegisterNetworkCallbackFailed && mShouldSignalObserver) { 1172 // registerNetworkCallback() will rematch the NetworkRequest 1173 // against active networks, so a cached list of active networks 1174 // will be repopulated immediatly after this. However we need to 1175 // purge any cached networks as they may have been disconnected 1176 // while mNetworkCallback was unregistered. 1177 final Network[] networks = 1178 getAllNetworksFiltered(mConnectivityManagerDelegate, null); 1179 // Convert Networks to NetIDs. 1180 final long[] netIds = new long[networks.length]; 1181 for (int i = 0; i < networks.length; i++) { 1182 netIds[i] = networkToNetId(networks[i]); 1183 } 1184 mObserver.purgeActiveNetworkList(netIds); 1185 } 1186 } 1187 } 1188 1189 /** Unregisters a BroadcastReceiver in the given context. */ unregister()1190 public void unregister() { 1191 assertOnThread(); 1192 if (!mRegistered) return; 1193 mRegistered = false; 1194 if (mNetworkCallback != null) { 1195 mConnectivityManagerDelegate.unregisterNetworkCallback(mNetworkCallback); 1196 } 1197 if (mDefaultNetworkCallback != null) { 1198 mConnectivityManagerDelegate.unregisterNetworkCallback(mDefaultNetworkCallback); 1199 } else { 1200 ContextUtils.getApplicationContext().unregisterReceiver(this); 1201 } 1202 } 1203 getCurrentNetworkState()1204 public NetworkState getCurrentNetworkState() { 1205 return mConnectivityManagerDelegate.getNetworkState(mWifiManagerDelegate); 1206 } 1207 1208 /** 1209 * Returns all connected networks that are useful and accessible to Chrome. 1210 * @param ignoreNetwork ignore this network as if it is not connected. 1211 */ getAllNetworksFiltered( ConnectivityManagerDelegate connectivityManagerDelegate, Network ignoreNetwork)1212 private static Network[] getAllNetworksFiltered( 1213 ConnectivityManagerDelegate connectivityManagerDelegate, Network ignoreNetwork) { 1214 Network[] networks = connectivityManagerDelegate.getAllNetworksUnfiltered(); 1215 // Whittle down |networks| into just the list of networks useful to us. 1216 int filteredIndex = 0; 1217 for (Network network : networks) { 1218 if (network.equals(ignoreNetwork)) { 1219 continue; 1220 } 1221 final NetworkCapabilities capabilities = 1222 connectivityManagerDelegate.getNetworkCapabilities(network); 1223 if (capabilities == null || !capabilities.hasCapability(NET_CAPABILITY_INTERNET)) { 1224 continue; 1225 } 1226 if (capabilities.hasTransport(TRANSPORT_VPN)) { 1227 // If we can access the VPN then... 1228 if (connectivityManagerDelegate.vpnAccessible(network)) { 1229 // ...we cannot access any other network, so return just the VPN. 1230 return new Network[] {network}; 1231 } else { 1232 // ...otherwise ignore it as we cannot use it. 1233 continue; 1234 } 1235 } 1236 networks[filteredIndex++] = network; 1237 } 1238 return Arrays.copyOf(networks, filteredIndex); 1239 } 1240 1241 /** Returns all connected networks that are useful and accessible to Chrome. */ getNetworksForTesting()1242 public Network[] getNetworksForTesting() { 1243 return getAllNetworksFiltered(mConnectivityManagerDelegate, null); 1244 } 1245 1246 /** 1247 * Returns an array of all of the device's currently connected 1248 * networks and ConnectionTypes, including only those that are useful and accessible to Chrome. 1249 * Array elements are a repeated sequence of: 1250 * NetID of network 1251 * ConnectionType of network 1252 * Only available when auto-detection has been enabled. 1253 */ getNetworksAndTypes()1254 public long[] getNetworksAndTypes() { 1255 final Network networks[] = getAllNetworksFiltered(mConnectivityManagerDelegate, null); 1256 final long networksAndTypes[] = new long[networks.length * 2]; 1257 int index = 0; 1258 for (Network network : networks) { 1259 networksAndTypes[index++] = networkToNetId(network); 1260 networksAndTypes[index++] = mConnectivityManagerDelegate.getConnectionType(network); 1261 } 1262 return networksAndTypes; 1263 } 1264 1265 /** 1266 * Returns the device's current default connected network used for 1267 * communication. 1268 * Returns null when not implemented. 1269 */ getDefaultNetwork()1270 public Network getDefaultNetwork() { 1271 return mConnectivityManagerDelegate.getDefaultNetwork(); 1272 } 1273 1274 /** 1275 * Returns NetID of device's current default connected network used for 1276 * communication. 1277 * Returns NetId.INVALID when not implemented. 1278 */ getDefaultNetId()1279 public long getDefaultNetId() { 1280 Network network = getDefaultNetwork(); 1281 return network == null ? NetId.INVALID : networkToNetId(network); 1282 } 1283 1284 /** 1285 * Returns {@code true} if NetworkCallback failed to register, indicating that network-specific 1286 * callbacks will not be issued. 1287 */ registerNetworkCallbackFailed()1288 public boolean registerNetworkCallbackFailed() { 1289 return mRegisterNetworkCallbackFailed; 1290 } 1291 1292 /** Returns the connection type for the given ConnectivityManager type and subtype. */ 1293 @ConnectionType convertToConnectionType(int type, int subtype)1294 private static int convertToConnectionType(int type, int subtype) { 1295 switch (type) { 1296 case ConnectivityManager.TYPE_ETHERNET: 1297 return ConnectionType.CONNECTION_ETHERNET; 1298 case ConnectivityManager.TYPE_WIFI: 1299 return ConnectionType.CONNECTION_WIFI; 1300 case ConnectivityManager.TYPE_WIMAX: 1301 return ConnectionType.CONNECTION_4G; 1302 case ConnectivityManager.TYPE_BLUETOOTH: 1303 return ConnectionType.CONNECTION_BLUETOOTH; 1304 case ConnectivityManager.TYPE_MOBILE: 1305 case ConnectivityManager.TYPE_MOBILE_DUN: 1306 case ConnectivityManager.TYPE_MOBILE_HIPRI: 1307 // Use information from TelephonyManager to classify the connection. 1308 switch (subtype) { 1309 case TelephonyManager.NETWORK_TYPE_GPRS: 1310 case TelephonyManager.NETWORK_TYPE_EDGE: 1311 case TelephonyManager.NETWORK_TYPE_CDMA: 1312 case TelephonyManager.NETWORK_TYPE_1xRTT: 1313 case TelephonyManager.NETWORK_TYPE_IDEN: 1314 return ConnectionType.CONNECTION_2G; 1315 case TelephonyManager.NETWORK_TYPE_UMTS: 1316 case TelephonyManager.NETWORK_TYPE_EVDO_0: 1317 case TelephonyManager.NETWORK_TYPE_EVDO_A: 1318 case TelephonyManager.NETWORK_TYPE_HSDPA: 1319 case TelephonyManager.NETWORK_TYPE_HSUPA: 1320 case TelephonyManager.NETWORK_TYPE_HSPA: 1321 case TelephonyManager.NETWORK_TYPE_EVDO_B: 1322 case TelephonyManager.NETWORK_TYPE_EHRPD: 1323 case TelephonyManager.NETWORK_TYPE_HSPAP: 1324 return ConnectionType.CONNECTION_3G; 1325 case TelephonyManager.NETWORK_TYPE_LTE: 1326 return ConnectionType.CONNECTION_4G; 1327 case TelephonyManager.NETWORK_TYPE_NR: 1328 return ConnectionType.CONNECTION_5G; 1329 default: 1330 return ConnectionType.CONNECTION_UNKNOWN; 1331 } 1332 default: 1333 return ConnectionType.CONNECTION_UNKNOWN; 1334 } 1335 } 1336 1337 // BroadcastReceiver 1338 @Override onReceive(Context context, Intent intent)1339 public void onReceive(Context context, Intent intent) { 1340 runOnThread( 1341 new Runnable() { 1342 @Override 1343 public void run() { 1344 if (mIgnoreNextBroadcast) { 1345 mIgnoreNextBroadcast = false; 1346 return; 1347 } 1348 connectionTypeChanged(); 1349 } 1350 }); 1351 } 1352 connectionTypeChanged()1353 private void connectionTypeChanged() { 1354 connectionTypeChangedTo(getCurrentNetworkState()); 1355 } 1356 connectionTypeChangedTo(NetworkState networkState)1357 private void connectionTypeChangedTo(NetworkState networkState) { 1358 if (networkState.getConnectionType() != mNetworkState.getConnectionType() 1359 || !networkState.getNetworkIdentifier().equals(mNetworkState.getNetworkIdentifier()) 1360 || networkState.isPrivateDnsActive() != mNetworkState.isPrivateDnsActive() 1361 || !networkState 1362 .getPrivateDnsServerName() 1363 .equals(mNetworkState.getPrivateDnsServerName())) { 1364 mObserver.onConnectionTypeChanged(networkState.getConnectionType()); 1365 } 1366 if (networkState.getConnectionType() != mNetworkState.getConnectionType() 1367 || networkState.getConnectionSubtype() != mNetworkState.getConnectionSubtype()) { 1368 mObserver.onConnectionSubtypeChanged(networkState.getConnectionSubtype()); 1369 } 1370 if (networkState.getConnectionCost() != mNetworkState.getConnectionCost()) { 1371 mObserver.onConnectionCostChanged(networkState.getConnectionCost()); 1372 } 1373 mNetworkState = networkState; 1374 } 1375 1376 private static class NetworkConnectivityIntentFilter extends IntentFilter { NetworkConnectivityIntentFilter()1377 NetworkConnectivityIntentFilter() { 1378 addAction(ConnectivityManager.CONNECTIVITY_ACTION); 1379 } 1380 } 1381 1382 /** 1383 * Extracts NetID of Network on Lollipop and NetworkHandle (which is munged NetID) on 1384 * Marshmallow and newer releases. 1385 * TODO(crbug.com/1489183): Rename networkToNetId to something meaningful and update 1386 * javadoc comment. 1387 */ networkToNetId(Network network)1388 public static long networkToNetId(Network network) { 1389 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 1390 return ApiHelperForM.getNetworkHandle(network); 1391 } else { 1392 // NOTE(pauljensen): This depends on Android framework implementation details. These 1393 // details cannot change because Lollipop is long since released. 1394 // NetIDs are only 16-bit so use parseInt. This function returns a long because 1395 // getNetworkHandle() returns a long. 1396 return Integer.parseInt(network.toString()); 1397 } 1398 } 1399 } 1400