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