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 org.robolectric.RuntimeEnvironment.getApiLevel;
6 
7 import android.net.ConnectivityManager;
8 import android.net.Network;
9 import android.net.NetworkInfo;
10 import android.net.NetworkRequest;
11 import java.util.HashMap;
12 import java.util.HashSet;
13 import java.util.Map;
14 import java.util.Set;
15 import org.robolectric.Shadows;
16 import org.robolectric.annotation.HiddenApi;
17 import org.robolectric.annotation.Implementation;
18 import org.robolectric.annotation.Implements;
19 
20 @Implements(ConnectivityManager.class)
21 public class ShadowConnectivityManager {
22 
23   // Package-private for tests.
24   static final int NET_ID_WIFI = ConnectivityManager.TYPE_WIFI;
25   static final int NET_ID_MOBILE = ConnectivityManager.TYPE_MOBILE;
26 
27   private NetworkInfo activeNetworkInfo;
28   private boolean backgroundDataSetting;
29   private int networkPreference = ConnectivityManager.DEFAULT_NETWORK_PREFERENCE;
30   private final Map<Integer, NetworkInfo> networkTypeToNetworkInfo = new HashMap<>();
31 
32   private HashSet<ConnectivityManager.NetworkCallback> networkCallbacks = new HashSet<>();
33   private final Map<Integer, Network> netIdToNetwork = new HashMap<>();
34   private final Map<Integer, NetworkInfo> netIdToNetworkInfo = new HashMap<>();
35   private Network processBoundNetwork;
36   private boolean defaultNetworkActive;
37   private HashSet<ConnectivityManager.OnNetworkActiveListener> onNetworkActiveListeners =
38       new HashSet<>();
39 
ShadowConnectivityManager()40   public ShadowConnectivityManager() {
41     NetworkInfo wifi = ShadowNetworkInfo.newInstance(NetworkInfo.DetailedState.DISCONNECTED,
42         ConnectivityManager.TYPE_WIFI, 0, true, false);
43     networkTypeToNetworkInfo.put(ConnectivityManager.TYPE_WIFI, wifi);
44 
45     NetworkInfo mobile = ShadowNetworkInfo.newInstance(NetworkInfo.DetailedState.CONNECTED,
46         ConnectivityManager.TYPE_MOBILE, ConnectivityManager.TYPE_MOBILE_MMS, true, true);
47     networkTypeToNetworkInfo.put(ConnectivityManager.TYPE_MOBILE, mobile);
48 
49     this.activeNetworkInfo = mobile;
50 
51     if (getApiLevel() >= LOLLIPOP) {
52       netIdToNetwork.put(NET_ID_WIFI, ShadowNetwork.newInstance(NET_ID_WIFI));
53       netIdToNetwork.put(NET_ID_MOBILE, ShadowNetwork.newInstance(NET_ID_MOBILE));
54       netIdToNetworkInfo.put(NET_ID_WIFI, wifi);
55       netIdToNetworkInfo.put(NET_ID_MOBILE, mobile);
56     }
57     defaultNetworkActive = true;
58   }
59 
getNetworkCallbacks()60   public Set<ConnectivityManager.NetworkCallback> getNetworkCallbacks() {
61     return networkCallbacks;
62   }
63 
64   @Implementation(minSdk = LOLLIPOP)
registerNetworkCallback(NetworkRequest request, ConnectivityManager.NetworkCallback networkCallback)65   public void registerNetworkCallback(NetworkRequest request, ConnectivityManager.NetworkCallback networkCallback) {
66     networkCallbacks.add(networkCallback);
67   }
68 
69   @Implementation(minSdk = LOLLIPOP)
unregisterNetworkCallback(ConnectivityManager.NetworkCallback networkCallback)70   public void unregisterNetworkCallback (ConnectivityManager.NetworkCallback networkCallback) {
71     if (networkCallback == null) {
72       throw new IllegalArgumentException("Invalid NetworkCallback");
73     }
74     if (networkCallbacks.contains(networkCallback)) {
75       networkCallbacks.remove(networkCallback);
76     }
77   }
78 
79   @Implementation
getActiveNetworkInfo()80   public NetworkInfo getActiveNetworkInfo() {
81     return activeNetworkInfo;
82   }
83 
84   @Implementation(minSdk = M)
getActiveNetwork()85   public Network getActiveNetwork() {
86     if (defaultNetworkActive) {
87       return netIdToNetwork.get(getActiveNetworkInfo().getType());
88     }
89     return null;
90   }
91 
92   @Implementation
getAllNetworkInfo()93   public NetworkInfo[] getAllNetworkInfo() {
94     if (defaultNetworkActive) {
95       return networkTypeToNetworkInfo
96           .values()
97           .toArray(new NetworkInfo[networkTypeToNetworkInfo.size()]);
98     }
99     return null;
100   }
101 
102   @Implementation
getNetworkInfo(int networkType)103   public NetworkInfo getNetworkInfo(int networkType) {
104     return networkTypeToNetworkInfo.get(networkType);
105   }
106 
107   @Implementation(minSdk = LOLLIPOP)
getNetworkInfo(Network network)108   public NetworkInfo getNetworkInfo(Network network) {
109     ShadowNetwork shadowNetwork = Shadows.shadowOf(network);
110     return netIdToNetworkInfo.get(shadowNetwork.getNetId());
111   }
112 
113   @Implementation(minSdk = LOLLIPOP)
getAllNetworks()114   public Network[] getAllNetworks() {
115     return netIdToNetwork.values().toArray(new Network[netIdToNetwork.size()]);
116   }
117 
118   @Implementation
getBackgroundDataSetting()119   public boolean getBackgroundDataSetting() {
120     return backgroundDataSetting;
121   }
122 
123   @Implementation
setNetworkPreference(int preference)124   public void setNetworkPreference(int preference) {
125     networkPreference = preference;
126   }
127 
128   @Implementation
getNetworkPreference()129   public int getNetworkPreference() {
130     return networkPreference;
131   }
132 
133   /**
134    * Count {@link ConnectivityManager#TYPE_MOBILE} networks as metered.
135    * Other types will be considered unmetered.
136    *
137    * @return True if the active network is metered.
138    */
139   @Implementation
isActiveNetworkMetered()140   public boolean isActiveNetworkMetered() {
141     if (defaultNetworkActive && activeNetworkInfo != null) {
142       return activeNetworkInfo.getType() == ConnectivityManager.TYPE_MOBILE;
143     } else {
144       return false;
145     }
146   }
147 
148   @Implementation(minSdk = M)
bindProcessToNetwork(Network network)149   public boolean bindProcessToNetwork(Network network) {
150     processBoundNetwork = network;
151     return true;
152   }
153 
154   @Implementation(minSdk = M)
getBoundNetworkForProcess()155   public Network getBoundNetworkForProcess() {
156     return processBoundNetwork;
157   }
158 
setNetworkInfo(int networkType, NetworkInfo networkInfo)159   public void setNetworkInfo(int networkType, NetworkInfo networkInfo) {
160     networkTypeToNetworkInfo.put(networkType, networkInfo);
161   }
162 
163   @HiddenApi @Implementation
setBackgroundDataSetting(boolean b)164   public void setBackgroundDataSetting(boolean b) {
165     backgroundDataSetting = b;
166   }
167 
setActiveNetworkInfo(NetworkInfo info)168   public void setActiveNetworkInfo(NetworkInfo info) {
169     if (getApiLevel() >= LOLLIPOP) {
170       activeNetworkInfo = info;
171       if (info != null) {
172         networkTypeToNetworkInfo.put(info.getType(), info);
173         netIdToNetwork.put(info.getType(), ShadowNetwork.newInstance(info.getType()));
174       } else {
175         networkTypeToNetworkInfo.clear();
176         netIdToNetwork.clear();
177       }
178     } else {
179       activeNetworkInfo = info;
180       if (info != null) {
181         networkTypeToNetworkInfo.put(info.getType(), info);
182       } else {
183         networkTypeToNetworkInfo.clear();
184       }
185     }
186   }
187 
188   /**
189    * Adds new {@code network} to the list of all {@link android.net.Network}s.
190    *
191    * @param network The network.
192    * @param networkInfo The network info paired with the {@link android.net.Network}.
193    */
addNetwork(Network network, NetworkInfo networkInfo)194   public void addNetwork(Network network, NetworkInfo networkInfo) {
195     ShadowNetwork shadowNetwork = Shadows.shadowOf(network);
196     int netId = shadowNetwork.getNetId();
197     netIdToNetwork.put(netId, network);
198     netIdToNetworkInfo.put(netId, networkInfo);
199   }
200 
201   /**
202    * Removes the {@code network} from the list of all {@link android.net.Network}s.
203    * @param network The network.
204    */
removeNetwork(Network network)205   public void removeNetwork(Network network) {
206     ShadowNetwork shadowNetwork = Shadows.shadowOf(network);
207     int netId = shadowNetwork.getNetId();
208     netIdToNetwork.remove(netId);
209     netIdToNetworkInfo.remove(netId);
210   }
211 
212   /**
213    * Clears the list of all {@link android.net.Network}s.
214    */
clearAllNetworks()215   public void clearAllNetworks() {
216     netIdToNetwork.clear();
217     netIdToNetworkInfo.clear();
218   }
219 
220   /**
221    * Sets the active state of the default network.
222    *
223    * <p>By default this is true and controls the result of {@link
224    * ConnectivityManager#isActiveNetworkMetered()}, {@link
225    * ConnectivityManager#isDefaultNetworkActivite()}, {@link ConnectivityManager#getActiveNetwork()}
226    * and {@link ConnectivityManager#getAllNetworkInfo()}.
227    *
228    * <p>Calling this method with {@code true} after any listeners have been registered with {@link
229    * ConnectivityManager#addDefaultNetworkActiveListener()} will result in those listeners being
230    * fired.
231    *
232    * @param isActive The active state of the default network.
233    */
setDefaultNetworkActive(boolean isActive)234   public void setDefaultNetworkActive(boolean isActive) {
235     defaultNetworkActive = isActive;
236     if (defaultNetworkActive) {
237       for (ConnectivityManager.OnNetworkActiveListener l : onNetworkActiveListeners) {
238         if (l != null) {
239           l.onNetworkActive();
240         }
241       }
242     }
243   }
244 
245   @Implementation(minSdk = LOLLIPOP)
isDefaultNetworkActive()246   public boolean isDefaultNetworkActive() {
247     return defaultNetworkActive;
248   }
249 
250   @Implementation(minSdk = LOLLIPOP)
addDefaultNetworkActiveListener(final ConnectivityManager.OnNetworkActiveListener l)251   public void addDefaultNetworkActiveListener(final ConnectivityManager.OnNetworkActiveListener l) {
252     onNetworkActiveListeners.add(l);
253   }
254 
255   @Implementation(minSdk = LOLLIPOP)
removeDefaultNetworkActiveListener(ConnectivityManager.OnNetworkActiveListener l)256   public void removeDefaultNetworkActiveListener(ConnectivityManager.OnNetworkActiveListener l) {
257     if (l == null) {
258       throw new IllegalArgumentException("Invalid OnNetworkActiveListener");
259     }
260     if (onNetworkActiveListeners.contains(l)) {
261       onNetworkActiveListeners.remove(l);
262     }
263   }
264 }
265