• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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