1package org.robolectric.shadows;
2
3import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR2;
4import static android.os.Build.VERSION_CODES.LOLLIPOP;
5
6import android.net.DhcpInfo;
7import android.net.wifi.ScanResult;
8import android.net.wifi.WifiConfiguration;
9import android.net.wifi.WifiInfo;
10import android.net.wifi.WifiManager;
11import android.util.Pair;
12import java.util.ArrayList;
13import java.util.LinkedHashMap;
14import java.util.List;
15import java.util.Map;
16import org.robolectric.Shadows;
17import org.robolectric.annotation.Implementation;
18import org.robolectric.annotation.Implements;
19import org.robolectric.util.ReflectionHelpers;
20
21/**
22 * Shadow for {@link android.net.wifi.WifiManager}.
23 */
24@Implements(WifiManager.class)
25public class ShadowWifiManager {
26  private static float sSignalLevelInPercent = 1f;
27  private boolean accessWifiStatePermission = true;
28  private boolean wifiEnabled = true;
29  private boolean wasSaved = false;
30  private WifiInfo wifiInfo;
31  private List<ScanResult> scanResults;
32  private final Map<Integer, WifiConfiguration> networkIdToConfiguredNetworks = new LinkedHashMap<>();
33  private Pair<Integer, Boolean> lastEnabledNetwork;
34  private DhcpInfo dhcpInfo;
35  private boolean isScanAlwaysAvailable = true;
36
37  @Implementation
38  public boolean setWifiEnabled(boolean wifiEnabled) {
39    checkAccessWifiStatePermission();
40    this.wifiEnabled = wifiEnabled;
41    return true;
42  }
43
44  @Implementation
45  public boolean isWifiEnabled() {
46    checkAccessWifiStatePermission();
47    return wifiEnabled;
48  }
49
50  @Implementation
51  public int getWifiState() {
52    if (isWifiEnabled()) {
53      return WifiManager.WIFI_STATE_ENABLED;
54    } else {
55      return WifiManager.WIFI_STATE_DISABLED;
56    }
57  }
58
59  @Implementation
60  public WifiInfo getConnectionInfo() {
61    checkAccessWifiStatePermission();
62    if (wifiInfo == null) {
63      wifiInfo = ReflectionHelpers.callConstructor(WifiInfo.class);
64    }
65    return wifiInfo;
66  }
67
68  /**
69   * Sets the connection info as the provided {@link WifiInfo}.
70   */
71  public void setConnectionInfo(WifiInfo wifiInfo) {
72    this.wifiInfo = wifiInfo;
73  }
74
75  @Implementation
76  public List<ScanResult> getScanResults() {
77    return scanResults;
78  }
79
80  @Implementation
81  public List<WifiConfiguration> getConfiguredNetworks() {
82    final ArrayList<WifiConfiguration> wifiConfigurations = new ArrayList<>();
83    for (WifiConfiguration wifiConfiguration : networkIdToConfiguredNetworks.values()) {
84      wifiConfigurations.add(wifiConfiguration);
85    }
86    return wifiConfigurations;
87  }
88
89  @Implementation(minSdk = LOLLIPOP)
90  public List<WifiConfiguration> getPrivilegedConfiguredNetworks() {
91    return getConfiguredNetworks();
92  }
93
94  @Implementation
95  public int addNetwork(WifiConfiguration config) {
96    int networkId = networkIdToConfiguredNetworks.size();
97    config.networkId = -1;
98    networkIdToConfiguredNetworks.put(networkId, makeCopy(config, networkId));
99    return networkId;
100  }
101
102  @Implementation
103  public boolean removeNetwork(int netId) {
104    networkIdToConfiguredNetworks.remove(netId);
105    return true;
106  }
107
108  @Implementation
109  public int updateNetwork(WifiConfiguration config) {
110    if (config == null || config.networkId < 0) {
111      return -1;
112    }
113    networkIdToConfiguredNetworks.put(config.networkId, makeCopy(config, config.networkId));
114    return config.networkId;
115  }
116
117  @Implementation
118  public boolean saveConfiguration() {
119    wasSaved = true;
120    return true;
121  }
122
123  @Implementation
124  public boolean enableNetwork(int netId, boolean disableOthers) {
125    lastEnabledNetwork = new Pair<>(netId, disableOthers);
126    return true;
127  }
128
129  @Implementation
130  public WifiManager.WifiLock createWifiLock(int lockType, java.lang.String tag) {
131    return ReflectionHelpers.callConstructor(WifiManager.WifiLock.class);
132  }
133
134  @Implementation
135  public WifiManager.WifiLock createWifiLock(java.lang.String tag) {
136    return createWifiLock(WifiManager.WIFI_MODE_FULL, tag);
137  }
138
139  @Implementation
140  public static int calculateSignalLevel(int rssi, int numLevels) {
141    return (int) (sSignalLevelInPercent * (numLevels - 1));
142  }
143
144  @Implementation
145  public boolean startScan() {
146    return true;
147  }
148
149  @Implementation
150  public DhcpInfo getDhcpInfo() {
151    return dhcpInfo;
152  }
153
154  @Implementation(minSdk = JELLY_BEAN_MR2)
155  public boolean isScanAlwaysAvailable() {
156    return isScanAlwaysAvailable;
157  }
158
159  public static void setSignalLevelInPercent(float level) {
160    if (level < 0 || level > 1) {
161      throw new IllegalArgumentException("level needs to be between 0 and 1");
162    }
163    sSignalLevelInPercent = level;
164  }
165
166  public void setAccessWifiStatePermission(boolean accessWifiStatePermission) {
167    this.accessWifiStatePermission = accessWifiStatePermission;
168  }
169
170  public void setScanResults(List<ScanResult> scanResults) {
171    this.scanResults = scanResults;
172  }
173
174  public void setDhcpInfo(DhcpInfo dhcpInfo) {
175    this.dhcpInfo = dhcpInfo;
176  }
177
178  public Pair<Integer, Boolean> getLastEnabledNetwork() {
179    return lastEnabledNetwork;
180  }
181
182  public boolean wasConfigurationSaved() {
183    return wasSaved;
184  }
185
186  public void setIsScanAlwaysAvailable(boolean isScanAlwaysAvailable) {
187    this.isScanAlwaysAvailable = isScanAlwaysAvailable;
188  }
189
190  private void checkAccessWifiStatePermission() {
191    if (!accessWifiStatePermission) {
192      throw new SecurityException();
193    }
194  }
195
196  private WifiConfiguration makeCopy(WifiConfiguration config, int networkId) {
197    WifiConfiguration copy = Shadows.shadowOf(config).copy();
198    copy.networkId = networkId;
199    return copy;
200  }
201
202  @Implements(WifiManager.WifiLock.class)
203  public static class ShadowWifiLock {
204    private int refCount;
205    private boolean refCounted = true;
206    private boolean locked;
207    public static final int MAX_ACTIVE_LOCKS = 50;
208
209    @Implementation
210    public synchronized void acquire() {
211      if (refCounted) {
212        if (++refCount >= MAX_ACTIVE_LOCKS) throw new UnsupportedOperationException("Exceeded maximum number of wifi locks");
213      } else {
214        locked = true;
215      }
216    }
217
218    @Implementation
219    public synchronized void release() {
220      if (refCounted) {
221        if (--refCount < 0) throw new RuntimeException("WifiLock under-locked");
222      } else {
223        locked = false;
224      }
225    }
226
227    @Implementation
228    public synchronized boolean isHeld() {
229      return refCounted ? refCount > 0 : locked;
230    }
231
232    @Implementation
233    public void setReferenceCounted(boolean refCounted) {
234      this.refCounted = refCounted;
235    }
236  }
237}
238