• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package org.robolectric.shadows;
2 
3 import static android.os.Build.VERSION_CODES.KITKAT;
4 import static android.os.Build.VERSION_CODES.LOLLIPOP;
5 import static android.os.Build.VERSION_CODES.M;
6 import static android.os.Build.VERSION_CODES.N;
7 import static android.os.Build.VERSION_CODES.O;
8 import static org.robolectric.RuntimeEnvironment.getApiLevel;
9 
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.os.Handler;
18 import java.util.HashMap;
19 import java.util.HashSet;
20 import java.util.Map;
21 import java.util.Set;
22 import org.robolectric.annotation.HiddenApi;
23 import org.robolectric.annotation.Implementation;
24 import org.robolectric.annotation.Implements;
25 import org.robolectric.shadow.api.Shadow;
26 
27 @Implements(ConnectivityManager.class)
28 public class ShadowConnectivityManager {
29 
30   // Package-private for tests.
31   static final int NET_ID_WIFI = ConnectivityManager.TYPE_WIFI;
32   static final int NET_ID_MOBILE = ConnectivityManager.TYPE_MOBILE;
33 
34   private NetworkInfo activeNetworkInfo;
35   private boolean backgroundDataSetting;
36   private int networkPreference = ConnectivityManager.DEFAULT_NETWORK_PREFERENCE;
37   private final Map<Integer, NetworkInfo> networkTypeToNetworkInfo = new HashMap<>();
38 
39   private HashSet<ConnectivityManager.NetworkCallback> networkCallbacks = new HashSet<>();
40   private final Map<Integer, Network> netIdToNetwork = new HashMap<>();
41   private final Map<Integer, NetworkInfo> netIdToNetworkInfo = new HashMap<>();
42   private Network processBoundNetwork;
43   private boolean defaultNetworkActive;
44   private HashSet<ConnectivityManager.OnNetworkActiveListener> onNetworkActiveListeners =
45       new HashSet<>();
46   private Map<Network, Boolean> reportedNetworkConnectivity = new HashMap<>();
47   private Map<Network, NetworkCapabilities> networkCapabilitiesMap = new HashMap<>();
48   private String captivePortalServerUrl = "http://10.0.0.2";
49   private final Map<Network, LinkProperties> linkPropertiesMap = new HashMap<>();
50 
ShadowConnectivityManager()51   public ShadowConnectivityManager() {
52     NetworkInfo wifi = ShadowNetworkInfo.newInstance(NetworkInfo.DetailedState.DISCONNECTED,
53         ConnectivityManager.TYPE_WIFI, 0, true, false);
54     networkTypeToNetworkInfo.put(ConnectivityManager.TYPE_WIFI, wifi);
55 
56     NetworkInfo mobile = ShadowNetworkInfo.newInstance(NetworkInfo.DetailedState.CONNECTED,
57         ConnectivityManager.TYPE_MOBILE, ConnectivityManager.TYPE_MOBILE_MMS, true, true);
58     networkTypeToNetworkInfo.put(ConnectivityManager.TYPE_MOBILE, mobile);
59 
60     this.activeNetworkInfo = mobile;
61 
62     if (getApiLevel() >= LOLLIPOP) {
63       netIdToNetwork.put(NET_ID_WIFI, ShadowNetwork.newInstance(NET_ID_WIFI));
64       netIdToNetwork.put(NET_ID_MOBILE, ShadowNetwork.newInstance(NET_ID_MOBILE));
65       netIdToNetworkInfo.put(NET_ID_WIFI, wifi);
66       netIdToNetworkInfo.put(NET_ID_MOBILE, mobile);
67     }
68     defaultNetworkActive = true;
69   }
70 
getNetworkCallbacks()71   public Set<ConnectivityManager.NetworkCallback> getNetworkCallbacks() {
72     return networkCallbacks;
73   }
74 
75   /**
76    * @return networks and their connectivity status which was reported with {@link
77    *     #reportNetworkConnectivity}.
78    */
getReportedNetworkConnectivity()79   public Map<Network, Boolean> getReportedNetworkConnectivity() {
80     return new HashMap<>(reportedNetworkConnectivity);
81   }
82 
83   @Implementation(minSdk = LOLLIPOP)
registerNetworkCallback( NetworkRequest request, ConnectivityManager.NetworkCallback networkCallback)84   protected void registerNetworkCallback(
85       NetworkRequest request, ConnectivityManager.NetworkCallback networkCallback) {
86     registerNetworkCallback(request, networkCallback, null);
87   }
88 
89   @Implementation(minSdk = O)
registerNetworkCallback( NetworkRequest request, ConnectivityManager.NetworkCallback networkCallback, Handler handler)90   protected void registerNetworkCallback(
91       NetworkRequest request,
92       ConnectivityManager.NetworkCallback networkCallback,
93       Handler handler) {
94     networkCallbacks.add(networkCallback);
95   }
96 
97   @Implementation(minSdk = LOLLIPOP)
requestNetwork( NetworkRequest request, ConnectivityManager.NetworkCallback networkCallback)98   protected void requestNetwork(
99       NetworkRequest request, ConnectivityManager.NetworkCallback networkCallback) {
100     registerNetworkCallback(request, networkCallback);
101   }
102 
103   @Implementation(minSdk = LOLLIPOP)
unregisterNetworkCallback(ConnectivityManager.NetworkCallback networkCallback)104   protected void unregisterNetworkCallback(ConnectivityManager.NetworkCallback networkCallback) {
105     if (networkCallback == null) {
106       throw new IllegalArgumentException("Invalid NetworkCallback");
107     }
108     if (networkCallbacks.contains(networkCallback)) {
109       networkCallbacks.remove(networkCallback);
110     }
111   }
112 
113   @Implementation
getActiveNetworkInfo()114   protected NetworkInfo getActiveNetworkInfo() {
115     return activeNetworkInfo;
116   }
117 
118   /**
119    * @see #setActiveNetworkInfo(NetworkInfo)
120    * @see #setNetworkInfo(int, NetworkInfo)
121    */
122   @Implementation(minSdk = M)
getActiveNetwork()123   protected Network getActiveNetwork() {
124     if (defaultNetworkActive) {
125       return netIdToNetwork.get(getActiveNetworkInfo().getType());
126     }
127     return null;
128   }
129 
130   /**
131    * @see #setActiveNetworkInfo(NetworkInfo)
132    * @see #setNetworkInfo(int, NetworkInfo)
133    */
134   @Implementation
getAllNetworkInfo()135   protected NetworkInfo[] getAllNetworkInfo() {
136     // todo(xian): is `defaultNetworkActive` really relevant here?
137     if (defaultNetworkActive) {
138       return networkTypeToNetworkInfo
139           .values()
140           .toArray(new NetworkInfo[networkTypeToNetworkInfo.size()]);
141     }
142     return null;
143   }
144 
145   @Implementation
getNetworkInfo(int networkType)146   protected NetworkInfo getNetworkInfo(int networkType) {
147     return networkTypeToNetworkInfo.get(networkType);
148   }
149 
150   @Implementation(minSdk = LOLLIPOP)
getNetworkInfo(Network network)151   protected NetworkInfo getNetworkInfo(Network network) {
152     if (network == null) {
153       return null;
154     }
155     ShadowNetwork shadowNetwork = Shadow.extract(network);
156     return netIdToNetworkInfo.get(shadowNetwork.getNetId());
157   }
158 
159   @Implementation(minSdk = LOLLIPOP)
getAllNetworks()160   protected Network[] getAllNetworks() {
161     return netIdToNetwork.values().toArray(new Network[netIdToNetwork.size()]);
162   }
163 
164   @Implementation
getBackgroundDataSetting()165   protected boolean getBackgroundDataSetting() {
166     return backgroundDataSetting;
167   }
168 
169   @Implementation
setNetworkPreference(int preference)170   protected void setNetworkPreference(int preference) {
171     networkPreference = preference;
172   }
173 
174   @Implementation
getNetworkPreference()175   protected int getNetworkPreference() {
176     return networkPreference;
177   }
178 
179   /**
180    * Counts {@link ConnectivityManager#TYPE_MOBILE} networks as metered. Other types will be
181    * considered unmetered.
182    *
183    * @return `true` if the active network is metered, otherwise `false`.
184    * @see #setActiveNetworkInfo(NetworkInfo)
185    * @see #setDefaultNetworkActive(boolean)
186    */
187   @Implementation
isActiveNetworkMetered()188   protected boolean isActiveNetworkMetered() {
189     if (defaultNetworkActive && activeNetworkInfo != null) {
190       return activeNetworkInfo.getType() == ConnectivityManager.TYPE_MOBILE;
191     } else {
192       return false;
193     }
194   }
195 
196   @Implementation(minSdk = M)
bindProcessToNetwork(Network network)197   protected boolean bindProcessToNetwork(Network network) {
198     processBoundNetwork = network;
199     return true;
200   }
201 
202   @Implementation(minSdk = M)
getBoundNetworkForProcess()203   protected Network getBoundNetworkForProcess() {
204     return processBoundNetwork;
205   }
206 
setNetworkInfo(int networkType, NetworkInfo networkInfo)207   public void setNetworkInfo(int networkType, NetworkInfo networkInfo) {
208     networkTypeToNetworkInfo.put(networkType, networkInfo);
209   }
210 
211   /**
212    * Returns the captive portal URL previously set with {@link #setCaptivePortalServerUrl}.
213    */
214   @Implementation(minSdk = N)
getCaptivePortalServerUrl()215   protected String getCaptivePortalServerUrl() {
216     return captivePortalServerUrl;
217   }
218 
219   /**
220    * Sets the captive portal URL, which will be returned in {@link #getCaptivePortalServerUrl}.
221    *
222    * @param captivePortalServerUrl the url of captive portal.
223    */
setCaptivePortalServerUrl(String captivePortalServerUrl)224   public void setCaptivePortalServerUrl(String captivePortalServerUrl) {
225     this.captivePortalServerUrl = captivePortalServerUrl;
226   }
227 
228   @HiddenApi @Implementation
setBackgroundDataSetting(boolean b)229   public void setBackgroundDataSetting(boolean b) {
230     backgroundDataSetting = b;
231   }
232 
setActiveNetworkInfo(NetworkInfo info)233   public void setActiveNetworkInfo(NetworkInfo info) {
234     if (getApiLevel() >= LOLLIPOP) {
235       activeNetworkInfo = info;
236       if (info != null) {
237         networkTypeToNetworkInfo.put(info.getType(), info);
238         netIdToNetwork.put(info.getType(), ShadowNetwork.newInstance(info.getType()));
239         netIdToNetworkInfo.put(info.getType(), info);
240       } else {
241         networkTypeToNetworkInfo.clear();
242         netIdToNetwork.clear();
243       }
244     } else {
245       activeNetworkInfo = info;
246       if (info != null) {
247         networkTypeToNetworkInfo.put(info.getType(), info);
248       } else {
249         networkTypeToNetworkInfo.clear();
250       }
251     }
252   }
253 
254   /**
255    * Adds new {@code network} to the list of all {@link android.net.Network}s.
256    *
257    * @param network The network.
258    * @param networkInfo The network info paired with the {@link android.net.Network}.
259    */
addNetwork(Network network, NetworkInfo networkInfo)260   public void addNetwork(Network network, NetworkInfo networkInfo) {
261     ShadowNetwork shadowNetwork = Shadow.extract(network);
262     int netId = shadowNetwork.getNetId();
263     netIdToNetwork.put(netId, network);
264     netIdToNetworkInfo.put(netId, networkInfo);
265   }
266 
267   /**
268    * Removes the {@code network} from the list of all {@link android.net.Network}s.
269    * @param network The network.
270    */
removeNetwork(Network network)271   public void removeNetwork(Network network) {
272     ShadowNetwork shadowNetwork = Shadow.extract(network);
273     int netId = shadowNetwork.getNetId();
274     netIdToNetwork.remove(netId);
275     netIdToNetworkInfo.remove(netId);
276   }
277 
278   /**
279    * Clears the list of all {@link android.net.Network}s.
280    */
clearAllNetworks()281   public void clearAllNetworks() {
282     netIdToNetwork.clear();
283     netIdToNetworkInfo.clear();
284   }
285 
286   /**
287    * Sets the active state of the default network.
288    *
289    * By default this is true and affects the result of {@link
290    * ConnectivityManager#isActiveNetworkMetered()}, {@link
291    * ConnectivityManager#isDefaultNetworkActive()}, {@link ConnectivityManager#getActiveNetwork()}
292    * and {@link ConnectivityManager#getAllNetworkInfo()}.
293    *
294    * Calling this method with {@code true} after any listeners have been registered with {@link
295    * ConnectivityManager#addDefaultNetworkActiveListener(OnNetworkActiveListener)} will result in
296    * those listeners being fired.
297    *
298    * @param isActive The active state of the default network.
299    */
setDefaultNetworkActive(boolean isActive)300   public void setDefaultNetworkActive(boolean isActive) {
301     defaultNetworkActive = isActive;
302     if (defaultNetworkActive) {
303       for (ConnectivityManager.OnNetworkActiveListener l : onNetworkActiveListeners) {
304         if (l != null) {
305           l.onNetworkActive();
306         }
307       }
308     }
309   }
310 
311   /**
312    * @return `true` by default, or the value specifed via {@link #setDefaultNetworkActive(boolean)}
313    * @see #setDefaultNetworkActive(boolean)
314    */
315   @Implementation(minSdk = LOLLIPOP)
isDefaultNetworkActive()316   protected boolean isDefaultNetworkActive() {
317     return defaultNetworkActive;
318   }
319 
320   @Implementation(minSdk = LOLLIPOP)
addDefaultNetworkActiveListener(final ConnectivityManager.OnNetworkActiveListener l)321   protected void addDefaultNetworkActiveListener(final ConnectivityManager.OnNetworkActiveListener l) {
322     onNetworkActiveListeners.add(l);
323   }
324 
325   @Implementation(minSdk = LOLLIPOP)
removeDefaultNetworkActiveListener(ConnectivityManager.OnNetworkActiveListener l)326   protected void removeDefaultNetworkActiveListener(ConnectivityManager.OnNetworkActiveListener l) {
327     if (l == null) {
328       throw new IllegalArgumentException("Invalid OnNetworkActiveListener");
329     }
330     if (onNetworkActiveListeners.contains(l)) {
331       onNetworkActiveListeners.remove(l);
332     }
333   }
334 
335   @Implementation(minSdk = M)
reportNetworkConnectivity(Network network, boolean hasConnectivity)336   protected void reportNetworkConnectivity(Network network, boolean hasConnectivity) {
337     reportedNetworkConnectivity.put(network, hasConnectivity);
338   }
339 
340   /**
341    * Gets the network capabilities of a given {@link Network}.
342    *
343    * @param network The {@link Network} object identifying the network in question.
344    * @return The {@link android.net.NetworkCapabilities} for the network.
345    * @see #setNetworkCapabilities(Network, NetworkCapabilities)
346    */
347   @Implementation(minSdk = LOLLIPOP)
getNetworkCapabilities(Network network)348   protected NetworkCapabilities getNetworkCapabilities(Network network) {
349     return networkCapabilitiesMap.get(network);
350   }
351 
352   /**
353    * Sets network capability and affects the result of {@link
354    * ConnectivityManager#getNetworkCapabilities(Network)}
355    *
356    * @param network The {@link Network} object identifying the network in question.
357    * @param networkCapabilities The {@link android.net.NetworkCapabilities} for the network.
358    */
setNetworkCapabilities(Network network, NetworkCapabilities networkCapabilities)359   public void setNetworkCapabilities(Network network, NetworkCapabilities networkCapabilities) {
360     networkCapabilitiesMap.put(network, networkCapabilities);
361   }
362 
363   /**
364    * Sets the value for enabling/disabling airplane mode
365    *
366    * @param enable new status for airplane mode
367    */
368   @Implementation(minSdk = KITKAT)
setAirplaneMode(boolean enable)369   protected void setAirplaneMode(boolean enable) {
370     ShadowSettings.setAirplaneMode(enable);
371   }
372 
373   /** @see #setLinkProperties(Network, LinkProperties) */
374   @Implementation(minSdk = LOLLIPOP)
getLinkProperties(Network network)375   protected LinkProperties getLinkProperties(Network network) {
376     return linkPropertiesMap.get(network);
377   }
378 
379   /**
380    * Sets the LinkProperties for the given Network.
381    *
382    * <p>A LinkProperties can be constructed by
383    * `org.robolectric.util.ReflectionHelpers.callConstructor` in tests.
384    */
setLinkProperties(Network network, LinkProperties linkProperties)385   public void setLinkProperties(Network network, LinkProperties linkProperties) {
386     linkPropertiesMap.put(network, linkProperties);
387   }
388 }
389