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