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