1 package org.robolectric.shadows; 2 3 import static android.os.Build.VERSION_CODES.M; 4 import static android.os.Build.VERSION_CODES.N; 5 import static android.os.Build.VERSION_CODES.O; 6 import static android.os.Build.VERSION_CODES.S; 7 import static org.robolectric.Shadows.shadowOf; 8 9 import android.app.PendingIntent; 10 import android.net.ConnectivityManager; 11 import android.net.ConnectivityManager.OnNetworkActiveListener; 12 import android.net.LinkProperties; 13 import android.net.Network; 14 import android.net.NetworkCapabilities; 15 import android.net.NetworkInfo; 16 import android.net.NetworkRequest; 17 import android.net.ProxyInfo; 18 import android.os.Handler; 19 import java.util.HashMap; 20 import java.util.HashSet; 21 import java.util.Map; 22 import java.util.Set; 23 import org.robolectric.annotation.HiddenApi; 24 import org.robolectric.annotation.Implementation; 25 import org.robolectric.annotation.Implements; 26 import org.robolectric.annotation.Resetter; 27 import org.robolectric.shadow.api.Shadow; 28 29 @Implements(ConnectivityManager.class) 30 public class ShadowConnectivityManager { 31 32 // Package-private for tests. 33 static final int NET_ID_WIFI = ConnectivityManager.TYPE_WIFI; 34 static final int NET_ID_MOBILE = ConnectivityManager.TYPE_MOBILE; 35 36 private static NetworkInfo activeNetworkInfo; 37 private static boolean backgroundDataSetting; 38 private static boolean networkCallbacksEnabled = true; 39 private static int restrictBackgroundStatus = 40 ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED; 41 private static int networkPreference = ConnectivityManager.DEFAULT_NETWORK_PREFERENCE; 42 private static final Map<Integer, NetworkInfo> networkTypeToNetworkInfo = new HashMap<>(); 43 44 private static HashSet<ConnectivityManager.NetworkCallback> networkCallbacks = new HashSet<>(); 45 private static final HashSet<PendingIntent> networkCallbackPendingIntents = new HashSet<>(); 46 47 private static final Map<Integer, Network> netIdToNetwork = new HashMap<>(); 48 private static final Map<Integer, NetworkInfo> netIdToNetworkInfo = new HashMap<>(); 49 private static Network processBoundNetwork; 50 private static boolean defaultNetworkActive = true; 51 private static HashSet<ConnectivityManager.OnNetworkActiveListener> onNetworkActiveListeners = 52 new HashSet<>(); 53 private static Map<Network, Boolean> reportedNetworkConnectivity = new HashMap<>(); 54 private static Map<Network, NetworkCapabilities> networkCapabilitiesMap = new HashMap<>(); 55 private static String captivePortalServerUrl = "http://10.0.0.2"; 56 private static final Map<Network, LinkProperties> linkPropertiesMap = new HashMap<>(); 57 private static final Map<Network, ProxyInfo> proxyInfoMap = new HashMap<>(); 58 59 static { resetNetworkDefaults()60 resetNetworkDefaults(); 61 } 62 resetNetworkDefaults()63 private static void resetNetworkDefaults() { 64 networkTypeToNetworkInfo.clear(); 65 NetworkInfo wifi = 66 ShadowNetworkInfo.newInstance( 67 NetworkInfo.DetailedState.DISCONNECTED, ConnectivityManager.TYPE_WIFI, 0, true, false); 68 networkTypeToNetworkInfo.put(ConnectivityManager.TYPE_WIFI, wifi); 69 70 NetworkInfo mobile = 71 ShadowNetworkInfo.newInstance( 72 NetworkInfo.DetailedState.CONNECTED, 73 ConnectivityManager.TYPE_MOBILE, 74 ConnectivityManager.TYPE_MOBILE_MMS, 75 true, 76 true); 77 networkTypeToNetworkInfo.put(ConnectivityManager.TYPE_MOBILE, mobile); 78 79 activeNetworkInfo = mobile; 80 81 netIdToNetwork.clear(); 82 netIdToNetwork.put(NET_ID_WIFI, ShadowNetwork.newInstance(NET_ID_WIFI)); 83 netIdToNetwork.put(NET_ID_MOBILE, ShadowNetwork.newInstance(NET_ID_MOBILE)); 84 netIdToNetworkInfo.clear(); 85 netIdToNetworkInfo.put(NET_ID_WIFI, wifi); 86 netIdToNetworkInfo.put(NET_ID_MOBILE, mobile); 87 88 NetworkCapabilities wifiNetworkCapabilities = ShadowNetworkCapabilities.newInstance(); 89 shadowOf(wifiNetworkCapabilities).addTransportType(NetworkCapabilities.TRANSPORT_WIFI); 90 NetworkCapabilities mobileNetworkCapabilities = ShadowNetworkCapabilities.newInstance(); 91 shadowOf(mobileNetworkCapabilities).addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR); 92 93 networkCapabilitiesMap.clear(); 94 networkCapabilitiesMap.put(netIdToNetwork.get(NET_ID_WIFI), wifiNetworkCapabilities); 95 networkCapabilitiesMap.put(netIdToNetwork.get(NET_ID_MOBILE), mobileNetworkCapabilities); 96 97 backgroundDataSetting = false; 98 networkCallbacksEnabled = true; 99 restrictBackgroundStatus = ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED; 100 networkPreference = ConnectivityManager.DEFAULT_NETWORK_PREFERENCE; 101 102 defaultNetworkActive = true; 103 104 networkCallbacks.clear(); 105 networkCallbackPendingIntents.clear(); 106 onNetworkActiveListeners.clear(); 107 reportedNetworkConnectivity.clear(); 108 linkPropertiesMap.clear(); 109 proxyInfoMap.clear(); 110 processBoundNetwork = null; 111 captivePortalServerUrl = "http://10.0.0.2"; 112 } 113 114 @Resetter reset()115 public static void reset() { 116 resetNetworkDefaults(); 117 } 118 119 /** 120 * Sets whether {@link #setDefaultNetworkActive(boolean)} triggers registered any {@link 121 * ConnectivityManager.NetworkCallback}. 122 */ setNetworkCallbacksEnabled(boolean enabled)123 public void setNetworkCallbacksEnabled(boolean enabled) { 124 networkCallbacksEnabled = enabled; 125 } 126 getNetworkCallbacks()127 public Set<ConnectivityManager.NetworkCallback> getNetworkCallbacks() { 128 return networkCallbacks; 129 } 130 getNetworkCallbackPendingIntents()131 public Set<PendingIntent> getNetworkCallbackPendingIntents() { 132 return networkCallbackPendingIntents; 133 } 134 135 /** 136 * @return networks and their connectivity status which was reported with {@link 137 * #reportNetworkConnectivity}. 138 */ getReportedNetworkConnectivity()139 public Map<Network, Boolean> getReportedNetworkConnectivity() { 140 return new HashMap<>(reportedNetworkConnectivity); 141 } 142 143 @Implementation registerNetworkCallback( NetworkRequest request, ConnectivityManager.NetworkCallback networkCallback)144 protected void registerNetworkCallback( 145 NetworkRequest request, ConnectivityManager.NetworkCallback networkCallback) { 146 registerNetworkCallback(request, networkCallback, null); 147 } 148 149 @Implementation(minSdk = O) registerNetworkCallback( NetworkRequest request, ConnectivityManager.NetworkCallback networkCallback, Handler handler)150 protected void registerNetworkCallback( 151 NetworkRequest request, 152 ConnectivityManager.NetworkCallback networkCallback, 153 Handler handler) { 154 networkCallbacks.add(networkCallback); 155 } 156 157 @Implementation(minSdk = M) registerNetworkCallback(NetworkRequest request, PendingIntent pendingIntent)158 protected void registerNetworkCallback(NetworkRequest request, PendingIntent pendingIntent) { 159 networkCallbackPendingIntents.add(pendingIntent); 160 } 161 162 @Implementation requestNetwork( NetworkRequest request, ConnectivityManager.NetworkCallback networkCallback)163 protected void requestNetwork( 164 NetworkRequest request, ConnectivityManager.NetworkCallback networkCallback) { 165 registerNetworkCallback(request, networkCallback); 166 } 167 168 @Implementation(minSdk = O) requestNetwork( NetworkRequest request, ConnectivityManager.NetworkCallback networkCallback, int timeoutMs)169 protected void requestNetwork( 170 NetworkRequest request, ConnectivityManager.NetworkCallback networkCallback, int timeoutMs) { 171 registerNetworkCallback(request, networkCallback); 172 } 173 174 @Implementation(minSdk = O) requestNetwork( NetworkRequest request, ConnectivityManager.NetworkCallback networkCallback, Handler handler)175 protected void requestNetwork( 176 NetworkRequest request, 177 ConnectivityManager.NetworkCallback networkCallback, 178 Handler handler) { 179 registerNetworkCallback(request, networkCallback); 180 } 181 182 @Implementation(minSdk = O) requestNetwork( NetworkRequest request, ConnectivityManager.NetworkCallback networkCallback, Handler handler, int timeoutMs)183 protected void requestNetwork( 184 NetworkRequest request, 185 ConnectivityManager.NetworkCallback networkCallback, 186 Handler handler, 187 int timeoutMs) { 188 registerNetworkCallback(request, networkCallback); 189 } 190 191 @Implementation(minSdk = N) registerDefaultNetworkCallback( ConnectivityManager.NetworkCallback networkCallback)192 protected void registerDefaultNetworkCallback( 193 ConnectivityManager.NetworkCallback networkCallback) { 194 networkCallbacks.add(networkCallback); 195 } 196 197 @Implementation(minSdk = O) registerDefaultNetworkCallback( ConnectivityManager.NetworkCallback networkCallback, Handler handler)198 protected void registerDefaultNetworkCallback( 199 ConnectivityManager.NetworkCallback networkCallback, Handler handler) { 200 networkCallbacks.add(networkCallback); 201 } 202 203 @Implementation(minSdk = S) registerBestMatchingNetworkCallback( NetworkRequest request, ConnectivityManager.NetworkCallback networkCallback, Handler handler)204 protected void registerBestMatchingNetworkCallback( 205 NetworkRequest request, 206 ConnectivityManager.NetworkCallback networkCallback, 207 Handler handler) { 208 networkCallbacks.add(networkCallback); 209 } 210 211 @Implementation unregisterNetworkCallback(ConnectivityManager.NetworkCallback networkCallback)212 protected void unregisterNetworkCallback(ConnectivityManager.NetworkCallback networkCallback) { 213 if (networkCallback == null) { 214 throw new IllegalArgumentException("Invalid NetworkCallback"); 215 } 216 if (networkCallbacks.contains(networkCallback)) { 217 networkCallbacks.remove(networkCallback); 218 } 219 } 220 221 @Implementation(minSdk = M) unregisterNetworkCallback(PendingIntent pendingIntent)222 protected void unregisterNetworkCallback(PendingIntent pendingIntent) { 223 if (pendingIntent == null) { 224 throw new IllegalArgumentException("Invalid NetworkCallback"); 225 } 226 if (networkCallbackPendingIntents.contains(pendingIntent)) { 227 networkCallbackPendingIntents.remove(pendingIntent); 228 } 229 } 230 231 @Implementation getActiveNetworkInfo()232 protected NetworkInfo getActiveNetworkInfo() { 233 return activeNetworkInfo; 234 } 235 236 /** 237 * @see #setActiveNetworkInfo(NetworkInfo) 238 * @see #setNetworkInfo(int, NetworkInfo) 239 */ 240 @Implementation(minSdk = M) getActiveNetwork()241 protected Network getActiveNetwork() { 242 if (defaultNetworkActive && activeNetworkInfo != null) { 243 return netIdToNetwork.get(activeNetworkInfo.getType()); 244 } 245 return null; 246 } 247 248 /** 249 * @see #setActiveNetworkInfo(NetworkInfo) 250 * @see #setNetworkInfo(int, NetworkInfo) 251 */ 252 @Implementation getAllNetworkInfo()253 protected NetworkInfo[] getAllNetworkInfo() { 254 // todo(xian): is `defaultNetworkActive` really relevant here? 255 if (defaultNetworkActive) { 256 return networkTypeToNetworkInfo 257 .values() 258 .toArray(new NetworkInfo[networkTypeToNetworkInfo.size()]); 259 } 260 return null; 261 } 262 263 @Implementation getNetworkInfo(int networkType)264 protected NetworkInfo getNetworkInfo(int networkType) { 265 return networkTypeToNetworkInfo.get(networkType); 266 } 267 268 @Implementation getNetworkInfo(Network network)269 protected NetworkInfo getNetworkInfo(Network network) { 270 if (network == null) { 271 return null; 272 } 273 ShadowNetwork shadowNetwork = Shadow.extract(network); 274 return netIdToNetworkInfo.get(shadowNetwork.getNetId()); 275 } 276 277 @Implementation getAllNetworks()278 protected Network[] getAllNetworks() { 279 return netIdToNetwork.values().toArray(new Network[netIdToNetwork.size()]); 280 } 281 282 @Implementation getBackgroundDataSetting()283 protected boolean getBackgroundDataSetting() { 284 return backgroundDataSetting; 285 } 286 287 @Implementation setNetworkPreference(int preference)288 protected void setNetworkPreference(int preference) { 289 networkPreference = preference; 290 } 291 292 @Implementation getNetworkPreference()293 protected int getNetworkPreference() { 294 return networkPreference; 295 } 296 297 /** 298 * Counts {@link ConnectivityManager#TYPE_MOBILE} networks as metered. Other types will be 299 * considered unmetered. 300 * 301 * @return true if the active network is metered, otherwise false. 302 * @see #setActiveNetworkInfo(NetworkInfo) 303 * @see #setDefaultNetworkActive(boolean) 304 */ 305 @Implementation isActiveNetworkMetered()306 protected boolean isActiveNetworkMetered() { 307 if (defaultNetworkActive && activeNetworkInfo != null) { 308 return activeNetworkInfo.getType() == ConnectivityManager.TYPE_MOBILE; 309 } else { 310 return false; 311 } 312 } 313 314 @Implementation(minSdk = M) bindProcessToNetwork(Network network)315 protected boolean bindProcessToNetwork(Network network) { 316 processBoundNetwork = network; 317 return true; 318 } 319 320 @Implementation(minSdk = M) getBoundNetworkForProcess()321 protected Network getBoundNetworkForProcess() { 322 return processBoundNetwork; 323 } 324 setNetworkInfo(int networkType, NetworkInfo networkInfo)325 public void setNetworkInfo(int networkType, NetworkInfo networkInfo) { 326 networkTypeToNetworkInfo.put(networkType, networkInfo); 327 } 328 329 /** Returns the captive portal URL previously set with {@link #setCaptivePortalServerUrl}. */ 330 @Implementation(minSdk = N) getCaptivePortalServerUrl()331 protected String getCaptivePortalServerUrl() { 332 return captivePortalServerUrl; 333 } 334 335 /** 336 * Sets the captive portal URL, which will be returned in {@link #getCaptivePortalServerUrl}. 337 * 338 * @param captivePortalServerUrl the url of captive portal. 339 */ setCaptivePortalServerUrl(String captivePortalServerUrl)340 public void setCaptivePortalServerUrl(String captivePortalServerUrl) { 341 this.captivePortalServerUrl = captivePortalServerUrl; 342 } 343 344 @HiddenApi 345 @Implementation setBackgroundDataSetting(boolean b)346 public void setBackgroundDataSetting(boolean b) { 347 backgroundDataSetting = b; 348 } 349 setActiveNetworkInfo(NetworkInfo info)350 public void setActiveNetworkInfo(NetworkInfo info) { 351 activeNetworkInfo = info; 352 if (info != null) { 353 networkTypeToNetworkInfo.put(info.getType(), info); 354 netIdToNetwork.put(info.getType(), ShadowNetwork.newInstance(info.getType())); 355 netIdToNetworkInfo.put(info.getType(), info); 356 } else { 357 networkTypeToNetworkInfo.clear(); 358 netIdToNetwork.clear(); 359 } 360 } 361 362 /** 363 * Adds new {@code network} to the list of all {@link android.net.Network}s. 364 * 365 * @param network The network. 366 * @param networkInfo The network info paired with the {@link android.net.Network}. 367 */ addNetwork(Network network, NetworkInfo networkInfo)368 public void addNetwork(Network network, NetworkInfo networkInfo) { 369 ShadowNetwork shadowNetwork = Shadow.extract(network); 370 int netId = shadowNetwork.getNetId(); 371 netIdToNetwork.put(netId, network); 372 netIdToNetworkInfo.put(netId, networkInfo); 373 } 374 375 /** 376 * Removes the {@code network} from the list of all {@link android.net.Network}s. 377 * 378 * @param network The network. 379 */ removeNetwork(Network network)380 public void removeNetwork(Network network) { 381 ShadowNetwork shadowNetwork = Shadow.extract(network); 382 int netId = shadowNetwork.getNetId(); 383 netIdToNetwork.remove(netId); 384 netIdToNetworkInfo.remove(netId); 385 } 386 387 /** Clears the list of all {@link android.net.Network}s. */ clearAllNetworks()388 public void clearAllNetworks() { 389 netIdToNetwork.clear(); 390 netIdToNetworkInfo.clear(); 391 } 392 393 /** 394 * Sets the active state of the default network. 395 * 396 * <p>By default this is true and affects the result of {@link 397 * ConnectivityManager#isActiveNetworkMetered()}, {@link 398 * ConnectivityManager#isDefaultNetworkActive()}, {@link ConnectivityManager#getActiveNetwork()} 399 * and {@link ConnectivityManager#getAllNetworkInfo()}. 400 * 401 * <p>Calling this method with {@code true} after any listeners have been registered with {@link 402 * ConnectivityManager#addDefaultNetworkActiveListener(OnNetworkActiveListener)} will result in 403 * those listeners being fired. 404 * 405 * <p>Calling this method after any {@link ConnectivityManager.NetworkCallback} have been 406 * registered will result in those callbacks being called unless {@link 407 * #setNetworkCallbacksEnabled(boolean)} has been called with a false value. 408 * 409 * @param isActive The active state of the default network. 410 */ setDefaultNetworkActive(boolean isActive)411 public void setDefaultNetworkActive(boolean isActive) { 412 defaultNetworkActive = isActive; 413 if (defaultNetworkActive) { 414 for (ConnectivityManager.OnNetworkActiveListener l : onNetworkActiveListeners) { 415 if (l != null) { 416 l.onNetworkActive(); 417 } 418 } 419 } 420 421 if (!networkCallbacksEnabled) { 422 return; 423 } 424 425 NetworkInfo activeNetworkInfo = getActiveNetworkInfo(); 426 427 if (activeNetworkInfo == null) { 428 return; 429 } 430 431 Network defaultNetwork = netIdToNetwork.get(activeNetworkInfo.getType()); 432 433 if (defaultNetwork == null) { 434 return; 435 } 436 437 HashSet<ConnectivityManager.NetworkCallback> stableNetworkCallbacks = 438 new HashSet<>(networkCallbacks); 439 for (ConnectivityManager.NetworkCallback c : stableNetworkCallbacks) { 440 if (c != null) { 441 if (defaultNetworkActive) { 442 c.onAvailable(defaultNetwork); 443 } else { 444 c.onLost(defaultNetwork); 445 } 446 } 447 } 448 } 449 450 /** 451 * @return true by default, or the value specified via {@link #setDefaultNetworkActive(boolean)} 452 * @see #setDefaultNetworkActive(boolean) 453 */ 454 @Implementation isDefaultNetworkActive()455 protected boolean isDefaultNetworkActive() { 456 return defaultNetworkActive; 457 } 458 459 @Implementation addDefaultNetworkActiveListener( final ConnectivityManager.OnNetworkActiveListener l)460 protected void addDefaultNetworkActiveListener( 461 final ConnectivityManager.OnNetworkActiveListener l) { 462 onNetworkActiveListeners.add(l); 463 } 464 465 @Implementation removeDefaultNetworkActiveListener(ConnectivityManager.OnNetworkActiveListener l)466 protected void removeDefaultNetworkActiveListener(ConnectivityManager.OnNetworkActiveListener l) { 467 if (l == null) { 468 throw new IllegalArgumentException("Invalid OnNetworkActiveListener"); 469 } 470 if (onNetworkActiveListeners.contains(l)) { 471 onNetworkActiveListeners.remove(l); 472 } 473 } 474 475 @Implementation(minSdk = M) reportNetworkConnectivity(Network network, boolean hasConnectivity)476 protected void reportNetworkConnectivity(Network network, boolean hasConnectivity) { 477 reportedNetworkConnectivity.put(network, hasConnectivity); 478 } 479 480 /** 481 * Gets the network capabilities of a given {@link Network}. 482 * 483 * @param network The {@link Network} object identifying the network in question. 484 * @return The {@link android.net.NetworkCapabilities} for the network. 485 * @see #setNetworkCapabilities(Network, NetworkCapabilities) 486 */ 487 @Implementation getNetworkCapabilities(Network network)488 protected NetworkCapabilities getNetworkCapabilities(Network network) { 489 return networkCapabilitiesMap.get(network); 490 } 491 492 /** 493 * Sets network capability and affects the result of {@link 494 * ConnectivityManager#getNetworkCapabilities(Network)} 495 * 496 * @param network The {@link Network} object identifying the network in question. 497 * @param networkCapabilities The {@link android.net.NetworkCapabilities} for the network. 498 */ setNetworkCapabilities(Network network, NetworkCapabilities networkCapabilities)499 public void setNetworkCapabilities(Network network, NetworkCapabilities networkCapabilities) { 500 networkCapabilitiesMap.put(network, networkCapabilities); 501 } 502 503 /** 504 * Sets the value for enabling/disabling airplane mode 505 * 506 * @param enable new status for airplane mode 507 */ 508 @Implementation setAirplaneMode(boolean enable)509 protected void setAirplaneMode(boolean enable) { 510 ShadowSettings.setAirplaneMode(enable); 511 } 512 513 /** 514 * @see #setLinkProperties(Network, LinkProperties) 515 */ 516 @Implementation getLinkProperties(Network network)517 protected LinkProperties getLinkProperties(Network network) { 518 return linkPropertiesMap.get(network); 519 } 520 521 /** 522 * Sets the LinkProperties for the given Network. 523 * 524 * <p>A LinkProperties can be constructed by {@code 525 * org.robolectric.util.ReflectionHelpers.callConstructor} in tests. 526 */ setLinkProperties(Network network, LinkProperties linkProperties)527 public void setLinkProperties(Network network, LinkProperties linkProperties) { 528 linkPropertiesMap.put(network, linkProperties); 529 } 530 531 /** 532 * Gets the RESTRICT_BACKGROUND_STATUS value. Default value is 1 533 * (RESTRICT_BACKGROUND_STATUS_DISABLED). 534 */ 535 @Implementation(minSdk = N) getRestrictBackgroundStatus()536 protected int getRestrictBackgroundStatus() { 537 return restrictBackgroundStatus; 538 } 539 540 /** Sets the next return value for {@link ConnectivityManager#getRestrictBackgroundStatus()}. */ setRestrictBackgroundStatus(int status)541 public void setRestrictBackgroundStatus(int status) { 542 if (status <= 0 || status >= 4) { 543 throw new IllegalArgumentException("Invalid RESTRICT_BACKGROUND_STATUS value."); 544 } 545 restrictBackgroundStatus = status; 546 } 547 548 /** 549 * Sets a proxy for a given {@link Network}. 550 * 551 * @param network The network. 552 * @param proxyInfo The proxy info. 553 */ setProxyForNetwork(Network network, ProxyInfo proxyInfo)554 public void setProxyForNetwork(Network network, ProxyInfo proxyInfo) { 555 proxyInfoMap.put(network, proxyInfo); 556 } 557 558 /** 559 * Returns a proxy for a given {@link Network}. 560 * 561 * <p>In order {@link ConnectivityManager#getDefaultProxy()} to work the default network should be 562 * set using {@link ConnectivityManager#bindProcessToNetwork(Network)}. 563 */ 564 @Implementation(minSdk = M) getProxyForNetwork(Network network)565 protected ProxyInfo getProxyForNetwork(Network network) { 566 return proxyInfoMap.get(network); 567 } 568 } 569