• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package org.robolectric.shadows;
2 
3 import android.annotation.RequiresApi;
4 import android.net.wifi.ScanResult;
5 import android.net.wifi.WifiSsid;
6 import android.os.Build;
7 import android.os.Build.VERSION;
8 import android.os.Build.VERSION_CODES;
9 import com.google.errorprone.annotations.CanIgnoreReturnValue;
10 import java.time.Duration;
11 import java.util.concurrent.TimeUnit;
12 import javax.annotation.Nullable;
13 
14 /**
15  * Builder class for {@link ScanResult} allowing for more accurate construction of Wi-Fi scan
16  * results in test code.
17  */
18 public final class WifiScanResultBuilder {
19   private static final int UNSPECIFIED = -1;
20   private static final int NATIVE_BUILDER_MIN_SDK = VERSION_CODES.VANILLA_ICE_CREAM;
21   private static final boolean USE_NATIVE_BUILDER = Build.VERSION.SDK_INT >= NATIVE_BUILDER_MIN_SDK;
22 
23   // Available in or before API 21+
24   @Nullable private String ssid;
25   @Nullable private String bssid;
26   @Nullable private String capabilities;
27   private int rssi = UNSPECIFIED;
28   private int frequency = UNSPECIFIED;
29   private Duration timeSinceSeen = Duration.ZERO;
30 
31   // Added in API 23
32   private int channelWidth = ScanResult.CHANNEL_WIDTH_20MHZ;
33   private int centerFreq0 = UNSPECIFIED;
34   private int centerFreq1 = UNSPECIFIED;
35   private boolean is80211McRttResponder = false;
36 
37   // Added in API 33
38   @Nullable private WifiSsid wifiSsid;
39 
40   // Added in API 35. Past this API level all setters are delegated to the real ScanResult.Builder.
41   @Nullable private ScanResult.Builder realBuilder;
42 
WifiScanResultBuilder()43   public WifiScanResultBuilder() {}
44 
45   /**
46    * Sets the value of the {@link ScanResult#capabilities} field.
47    *
48    * @return this builder, for chaining
49    */
50   @CanIgnoreReturnValue
setCapabilities(@ullable String capabilities)51   public WifiScanResultBuilder setCapabilities(@Nullable String capabilities) {
52     if (USE_NATIVE_BUILDER) {
53       ensureRealBuilder().setCaps(capabilities);
54     } else {
55       this.capabilities = capabilities;
56     }
57     return this;
58   }
59 
60   /**
61    * Sets the value of the {@link ScanResult#BSSID} field.
62    *
63    * @return this builder, for chaining
64    */
65   @CanIgnoreReturnValue
setBssid(@ullable String bssid)66   public WifiScanResultBuilder setBssid(@Nullable String bssid) {
67     if (USE_NATIVE_BUILDER) {
68       ensureRealBuilder().setBssid(bssid);
69     } else {
70       this.bssid = bssid;
71     }
72     return this;
73   }
74 
75   /**
76    * Sets the value of the {@link ScanResult#level} field (aka RSSI).
77    *
78    * @return this builder, for chaining
79    */
80   @CanIgnoreReturnValue
setRssi(int rssi)81   public WifiScanResultBuilder setRssi(int rssi) {
82     if (USE_NATIVE_BUILDER) {
83       ensureRealBuilder().setRssi(rssi);
84     } else {
85       this.rssi = rssi;
86     }
87     return this;
88   }
89 
90   /**
91    * Sets the value of the frequency, in MHz, returned by the {@link ScanResult#frequency} field.
92    *
93    * @return this builder, for chaining
94    */
95   @CanIgnoreReturnValue
setFrequency(int frequency)96   public WifiScanResultBuilder setFrequency(int frequency) {
97     if (USE_NATIVE_BUILDER) {
98       ensureRealBuilder().setFrequency(frequency);
99     } else {
100       this.frequency = frequency;
101     }
102     return this;
103   }
104 
105   /**
106    * Sets the value of the {@link ScanResult#SSID} field. On API 33 and above, this also sets the
107    * return value of {@link ScanResult#getWifiSsid()}.
108    *
109    * @param ssid the name of the network, or null; this should be either a UTF-8 encoded string,
110    *     surrounded by quotes (e.g. '"Network name"'), or an unquoted hex-encoded string (e.g.
111    *     '0a8b2c1f').
112    * @return this builder, for chaining
113    */
114   @CanIgnoreReturnValue
setSsid(@ullable String ssid)115   public WifiScanResultBuilder setSsid(@Nullable String ssid) {
116     if (VERSION.SDK_INT >= VERSION_CODES.TIRAMISU) {
117       setWifiSsid(ssid != null ? WifiSsid.fromString(ssid) : null);
118     } else {
119       this.ssid = ssid;
120     }
121     return this;
122   }
123 
124   /**
125    * Sets the duration since boot since the result was seen.
126    *
127    * @see ScanResult#timestamp
128    * @return this builder, for chaining
129    */
130   @CanIgnoreReturnValue
setTimeSinceSeen(Duration timeSinceSeen)131   public WifiScanResultBuilder setTimeSinceSeen(Duration timeSinceSeen) {
132     if (USE_NATIVE_BUILDER) {
133       ensureRealBuilder().setTsf(TimeUnit.MILLISECONDS.toMicros(timeSinceSeen.toMillis()));
134     } else {
135       this.timeSinceSeen = timeSinceSeen;
136     }
137     return this;
138   }
139 
140   /**
141    * Sets the value returned by {@link ScanResult#getWifiSsid()}, additionally setting the
142    * deprecated {@link ScanResult#SSID} field.
143    *
144    * @return this builder, for chaining
145    */
146   @RequiresApi(VERSION_CODES.TIRAMISU)
147   @CanIgnoreReturnValue
setWifiSsid(@ullable WifiSsid wifiSsid)148   public WifiScanResultBuilder setWifiSsid(@Nullable WifiSsid wifiSsid) {
149     if (USE_NATIVE_BUILDER) {
150       ensureRealBuilder().setWifiSsid(wifiSsid);
151     } else {
152       this.wifiSsid = wifiSsid;
153       this.ssid = wifiSsid == null ? null : wifiSsid.toString();
154     }
155     return this;
156   }
157 
158   /**
159    * Sets the value returned by {@link ScanResult#channelWidth}. This should be one of the constants
160    * in {@link ScanResult} such as {@link ScanResult#CHANNEL_WIDTH_20MHZ}.
161    *
162    * @return this builder, for chaining
163    */
164   @RequiresApi(VERSION_CODES.M)
165   @CanIgnoreReturnValue
setChannelWidth(int channelWidth)166   public WifiScanResultBuilder setChannelWidth(int channelWidth) {
167     if (USE_NATIVE_BUILDER) {
168       ensureRealBuilder().setChannelWidth(channelWidth);
169     } else {
170       this.channelWidth = channelWidth;
171     }
172     return this;
173   }
174 
175   /**
176    * Sets the center frequency, in MHz, of the first segment (see {@link ScanResult#centerFreq0}).
177    *
178    * @return this builder, for chaining
179    */
180   @RequiresApi(VERSION_CODES.M)
181   @CanIgnoreReturnValue
setCenterFreq0(int centerFreq0)182   public WifiScanResultBuilder setCenterFreq0(int centerFreq0) {
183     if (USE_NATIVE_BUILDER) {
184       ensureRealBuilder().setCenterFreq0(centerFreq0);
185     } else {
186       this.centerFreq0 = centerFreq0;
187     }
188     return this;
189   }
190 
191   /**
192    * Sets the center frequency, in MHz, of the second segment (see {@link ScanResult#centerFreq1}).
193    *
194    * @return this builder, for chaining
195    */
196   @CanIgnoreReturnValue
197   @RequiresApi(VERSION_CODES.M)
setCenterFreq1(int centerFreq1)198   public WifiScanResultBuilder setCenterFreq1(int centerFreq1) {
199     if (USE_NATIVE_BUILDER) {
200       ensureRealBuilder().setCenterFreq1(centerFreq1);
201     } else {
202       this.centerFreq1 = centerFreq1;
203     }
204     return this;
205   }
206 
207   /**
208    * Sets the return value of {@link ScanResult#is80211mcResponder()}.
209    *
210    * @return this builder, for chaining
211    */
212   @CanIgnoreReturnValue
213   @RequiresApi(VERSION_CODES.M)
setIs80211McRttResponder(boolean is80211McRttResponder)214   public WifiScanResultBuilder setIs80211McRttResponder(boolean is80211McRttResponder) {
215     if (USE_NATIVE_BUILDER) {
216       ensureRealBuilder().setIs80211McRTTResponder(is80211McRttResponder);
217     } else {
218       this.is80211McRttResponder = is80211McRttResponder;
219     }
220     return this;
221   }
222 
223   /**
224    * Sets the return value of {@link ScanResult#is80211azNtbResponder()}.
225    *
226    * @return this builder, for chaining
227    */
228   @CanIgnoreReturnValue
229   @RequiresApi(VERSION_CODES.VANILLA_ICE_CREAM)
setIs80211azNtbRttResponder(boolean is80211azNtbRttResponder)230   public WifiScanResultBuilder setIs80211azNtbRttResponder(boolean is80211azNtbRttResponder) {
231     ensureRealBuilder().setIs80211azNtbRTTResponder(is80211azNtbRttResponder);
232     return this;
233   }
234 
235   /**
236    * Sets the return value of {@link ScanResult#isTwtResponder()}.
237    *
238    * @return this builder, for chaining
239    */
240   @CanIgnoreReturnValue
241   @RequiresApi(VERSION_CODES.VANILLA_ICE_CREAM)
setIsTwtResponder(boolean isTwtResponder)242   public WifiScanResultBuilder setIsTwtResponder(boolean isTwtResponder) {
243     ensureRealBuilder().setIsTwtResponder(isTwtResponder);
244     return this;
245   }
246 
247   /** Returns a new {@link ScanResult} instance as configured in this builder. */
build()248   public ScanResult build() {
249     if (USE_NATIVE_BUILDER) {
250       return ensureRealBuilder().build();
251     }
252 
253     long timestampMicros = TimeUnit.MILLISECONDS.toMicros(timeSinceSeen.toMillis());
254     ScanResult scanResult;
255     if (VERSION.SDK_INT >= VERSION_CODES.TIRAMISU) {
256       scanResult =
257           new ScanResult(
258               wifiSsid,
259               bssid,
260               /* hessid= */ UNSPECIFIED,
261               /* anqpDomainId= */ UNSPECIFIED,
262               /* osuProviders= */ null,
263               capabilities,
264               rssi,
265               frequency,
266               timestampMicros);
267     } else {
268       scanResult = new ScanResult();
269       scanResult.SSID = ssid;
270       scanResult.BSSID = bssid;
271       scanResult.capabilities = capabilities;
272       scanResult.level = rssi;
273       scanResult.frequency = frequency;
274       scanResult.timestamp = timestampMicros;
275     }
276 
277     if (VERSION.SDK_INT >= VERSION_CODES.M) {
278       scanResult.channelWidth = channelWidth;
279       scanResult.centerFreq0 = centerFreq0;
280       scanResult.centerFreq1 = centerFreq1;
281       if (is80211McRttResponder) {
282         scanResult.setFlag(ScanResult.FLAG_80211mc_RESPONDER);
283       }
284     }
285 
286     return scanResult;
287   }
288 
289   @RequiresApi(NATIVE_BUILDER_MIN_SDK)
ensureRealBuilder()290   private ScanResult.Builder ensureRealBuilder() {
291     if (realBuilder == null) {
292       realBuilder = new ScanResult.Builder();
293     }
294     return realBuilder;
295   }
296 }
297