1 /*
2  * Copyright (C) 2008 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.net.wifi;
18 
19 import static android.Manifest.permission.ACCESS_FINE_LOCATION;
20 import static android.Manifest.permission.LOCATION_HARDWARE;
21 import static android.Manifest.permission.NEARBY_WIFI_DEVICES;
22 
23 import android.Manifest;
24 import android.annotation.CallbackExecutor;
25 import android.annotation.FlaggedApi;
26 import android.annotation.IntDef;
27 import android.annotation.NonNull;
28 import android.annotation.Nullable;
29 import android.annotation.RequiresPermission;
30 import android.annotation.SuppressLint;
31 import android.annotation.SystemApi;
32 import android.annotation.SystemService;
33 import android.content.Context;
34 import android.os.Binder;
35 import android.os.Build;
36 import android.os.Bundle;
37 import android.os.Looper;
38 import android.os.Parcel;
39 import android.os.Parcelable;
40 import android.os.Process;
41 import android.os.RemoteException;
42 import android.os.WorkSource;
43 import android.text.TextUtils;
44 import android.util.Log;
45 
46 import androidx.annotation.RequiresApi;
47 
48 import com.android.internal.util.Protocol;
49 import com.android.modules.utils.build.SdkLevel;
50 import com.android.wifi.flags.Flags;
51 
52 import java.lang.annotation.Retention;
53 import java.lang.annotation.RetentionPolicy;
54 import java.util.ArrayList;
55 import java.util.Arrays;
56 import java.util.HashMap;
57 import java.util.List;
58 import java.util.Map;
59 import java.util.Objects;
60 import java.util.concurrent.Executor;
61 import java.util.function.Consumer;
62 
63 /**
64  * This class provides a way to scan the Wifi universe around the device
65  * @hide
66  */
67 @SystemApi
68 @SystemService(Context.WIFI_SCANNING_SERVICE)
69 public class WifiScanner {
70 
71     /** @hide */
72     public static final int WIFI_BAND_INDEX_24_GHZ = 0;
73     /** @hide */
74     public static final int WIFI_BAND_INDEX_5_GHZ = 1;
75     /** @hide */
76     public static final int WIFI_BAND_INDEX_5_GHZ_DFS_ONLY = 2;
77     /** @hide */
78     public static final int WIFI_BAND_INDEX_6_GHZ = 3;
79     /** @hide */
80     public static final int WIFI_BAND_INDEX_60_GHZ = 4;
81     /** @hide */
82     public static final int WIFI_BAND_COUNT = 5;
83 
84     /**
85      * Reserved bit for Multi-internet connection only, not for scanning.
86      * @hide
87      */
88     public static final int WIFI_BAND_INDEX_5_GHZ_LOW = 29;
89 
90     /**
91      * Reserved bit for Multi-internet connection only, not for scanning.
92      * @hide
93      */
94     public static final int WIFI_BAND_INDEX_5_GHZ_HIGH = 30;
95 
96     /** @hide */
97     @Retention(RetentionPolicy.SOURCE)
98     @IntDef(prefix = {"WIFI_BAND_INDEX_"}, value = {
99             WIFI_BAND_INDEX_24_GHZ,
100             WIFI_BAND_INDEX_5_GHZ,
101             WIFI_BAND_INDEX_5_GHZ_DFS_ONLY,
102             WIFI_BAND_INDEX_6_GHZ,
103             WIFI_BAND_INDEX_60_GHZ})
104     public @interface WifiBandIndex {}
105 
106     /** no band specified; use channel list instead */
107     public static final int WIFI_BAND_UNSPECIFIED = 0;
108     /** 2.4 GHz band */
109     public static final int WIFI_BAND_24_GHZ = 1 << WIFI_BAND_INDEX_24_GHZ;
110     /** 5 GHz band excluding DFS channels */
111     public static final int WIFI_BAND_5_GHZ = 1 << WIFI_BAND_INDEX_5_GHZ;
112     /** DFS channels from 5 GHz band only */
113     public static final int WIFI_BAND_5_GHZ_DFS_ONLY  = 1 << WIFI_BAND_INDEX_5_GHZ_DFS_ONLY;
114     /** 6 GHz band */
115     public static final int WIFI_BAND_6_GHZ = 1 << WIFI_BAND_INDEX_6_GHZ;
116     /** 60 GHz band */
117     public static final int WIFI_BAND_60_GHZ = 1 << WIFI_BAND_INDEX_60_GHZ;
118 
119     /**
120      * Reserved for Multi-internet connection only, not for scanning.
121      * @hide
122      */
123     public static final int WIFI_BAND_5_GHZ_LOW = 1 << WIFI_BAND_INDEX_5_GHZ_LOW;
124     /**
125      * Reserved for Multi-internet connection only, not for scanning.
126      * @hide
127      */
128     public static final int WIFI_BAND_5_GHZ_HIGH = 1 << WIFI_BAND_INDEX_5_GHZ_HIGH;
129 
130     /**
131      * Combination of bands
132      * Note that those are only the common band combinations,
133      * other combinations can be created by combining any of the basic bands above
134      */
135     /** Both 2.4 GHz band and 5 GHz band; no DFS channels */
136     public static final int WIFI_BAND_BOTH = WIFI_BAND_24_GHZ | WIFI_BAND_5_GHZ;
137     /**
138      * 2.4Ghz band + DFS channels from 5 GHz band only
139      * @hide
140      */
141     public static final int WIFI_BAND_24_GHZ_WITH_5GHZ_DFS  =
142             WIFI_BAND_24_GHZ | WIFI_BAND_5_GHZ_DFS_ONLY;
143     /** 5 GHz band including DFS channels */
144     public static final int WIFI_BAND_5_GHZ_WITH_DFS  = WIFI_BAND_5_GHZ | WIFI_BAND_5_GHZ_DFS_ONLY;
145     /** Both 2.4 GHz band and 5 GHz band; with DFS channels */
146     public static final int WIFI_BAND_BOTH_WITH_DFS =
147             WIFI_BAND_24_GHZ | WIFI_BAND_5_GHZ | WIFI_BAND_5_GHZ_DFS_ONLY;
148     /** 2.4 GHz band and 5 GHz band (no DFS channels) and 6 GHz */
149     public static final int WIFI_BAND_24_5_6_GHZ = WIFI_BAND_BOTH | WIFI_BAND_6_GHZ;
150     /** 2.4 GHz band and 5 GHz band; with DFS channels and 6 GHz */
151     public static final int WIFI_BAND_24_5_WITH_DFS_6_GHZ =
152             WIFI_BAND_BOTH_WITH_DFS | WIFI_BAND_6_GHZ;
153     /** @hide */
154     public static final int WIFI_BAND_24_5_6_60_GHZ =
155             WIFI_BAND_24_5_6_GHZ | WIFI_BAND_60_GHZ;
156     /** @hide */
157     public static final int WIFI_BAND_24_5_WITH_DFS_6_60_GHZ =
158             WIFI_BAND_24_5_6_60_GHZ | WIFI_BAND_5_GHZ_DFS_ONLY;
159 
160     /** @hide */
161     @Retention(RetentionPolicy.SOURCE)
162     @IntDef(prefix = {"WIFI_BAND_"}, value = {
163             WIFI_BAND_UNSPECIFIED,
164             WIFI_BAND_24_GHZ,
165             WIFI_BAND_5_GHZ,
166             WIFI_BAND_BOTH,
167             WIFI_BAND_5_GHZ_DFS_ONLY,
168             WIFI_BAND_24_GHZ_WITH_5GHZ_DFS,
169             WIFI_BAND_5_GHZ_WITH_DFS,
170             WIFI_BAND_BOTH_WITH_DFS,
171             WIFI_BAND_6_GHZ,
172             WIFI_BAND_24_5_6_GHZ,
173             WIFI_BAND_24_5_WITH_DFS_6_GHZ,
174             WIFI_BAND_60_GHZ,
175             WIFI_BAND_24_5_6_60_GHZ,
176             WIFI_BAND_24_5_WITH_DFS_6_60_GHZ})
177     public @interface WifiBand {}
178 
179     /**
180      * All bands
181      * @hide
182      */
183     public static final int WIFI_BAND_ALL = (1 << WIFI_BAND_COUNT) - 1;
184 
185     /** Minimum supported scanning period */
186     public static final int MIN_SCAN_PERIOD_MS = 1000;
187     /** Maximum supported scanning period */
188     public static final int MAX_SCAN_PERIOD_MS = 1024000;
189 
190     /** No Error */
191     public static final int REASON_SUCCEEDED = 0;
192     /** Unknown error */
193     public static final int REASON_UNSPECIFIED = -1;
194     /** Invalid listener */
195     public static final int REASON_INVALID_LISTENER = -2;
196     /** Invalid request */
197     public static final int REASON_INVALID_REQUEST = -3;
198     /** Invalid request */
199     public static final int REASON_NOT_AUTHORIZED = -4;
200     /** An outstanding request with the same listener hasn't finished yet. */
201     public static final int REASON_DUPLICATE_REQEUST = -5;
202     /** Busy - Due to Connection in progress, processing another scan request etc. */
203     public static final int REASON_BUSY = -6;
204     /** Abort - Due to another high priority operation like roaming, offload scan etc. */
205     public static final int REASON_ABORT = -7;
206     /** No such device - Wrong interface or interface doesn't exist. */
207     public static final int REASON_NO_DEVICE = -8;
208     /** Invalid argument - Wrong/unsupported argument passed in scan params. */
209     public static final int REASON_INVALID_ARGS = -9;
210     /** Timeout - Device didn't respond back with scan results */
211     public static final int REASON_TIMEOUT = -10;
212 
213     /** @hide */
214     @Retention(RetentionPolicy.SOURCE)
215     @IntDef(prefix = { "REASON_" }, value = {
216             REASON_SUCCEEDED,
217             REASON_UNSPECIFIED,
218             REASON_INVALID_LISTENER,
219             REASON_INVALID_REQUEST,
220             REASON_NOT_AUTHORIZED,
221             REASON_DUPLICATE_REQEUST,
222             REASON_BUSY,
223             REASON_ABORT,
224             REASON_NO_DEVICE,
225             REASON_INVALID_ARGS,
226             REASON_TIMEOUT,
227     })
228     public @interface ScanStatusCode {}
229 
230     /** @hide */
231     public static final String GET_AVAILABLE_CHANNELS_EXTRA = "Channels";
232 
233     /**
234      * This constant is used for {@link ScanSettings#setRnrSetting(int)}.
235      * <p>
236      * Scan 6Ghz APs co-located with 2.4/5Ghz APs using Reduced Neighbor Report (RNR) if the 6Ghz
237      * band is explicitly requested to be scanned and the current country code supports scanning
238      * of at least one 6Ghz channel. The 6Ghz band is explicitly requested if the
239      * ScanSetting.band parameter is set to one of:
240      * <li> {@link #WIFI_BAND_6_GHZ} </li>
241      * <li> {@link #WIFI_BAND_24_5_6_GHZ} </li>
242      * <li> {@link #WIFI_BAND_24_5_WITH_DFS_6_GHZ} </li>
243      * <li> {@link #WIFI_BAND_24_5_6_60_GHZ} </li>
244      * <li> {@link #WIFI_BAND_24_5_WITH_DFS_6_60_GHZ} </li>
245      * <li> {@link #WIFI_BAND_ALL} </li>
246      **/
247     public static final int WIFI_RNR_ENABLED_IF_WIFI_BAND_6_GHZ_SCANNED = 0;
248     /**
249      * This constant is used for {@link ScanSettings#setRnrSetting(int)}.
250      * <p>
251      * Request to scan 6Ghz APs co-located with 2.4/5Ghz APs using Reduced Neighbor Report (RNR)
252      * when the current country code supports scanning of at least one 6Ghz channel.
253      **/
254     public static final int WIFI_RNR_ENABLED = 1;
255     /**
256      * This constant is used for {@link ScanSettings#setRnrSetting(int)}.
257      * <p>
258      * Do not request to scan 6Ghz APs co-located with 2.4/5Ghz APs using
259      * Reduced Neighbor Report (RNR)
260      **/
261     public static final int WIFI_RNR_NOT_NEEDED = 2;
262 
263     /** @hide */
264     @Retention(RetentionPolicy.SOURCE)
265     @IntDef(prefix = {"RNR_"}, value = {
266             WIFI_RNR_ENABLED_IF_WIFI_BAND_6_GHZ_SCANNED,
267             WIFI_RNR_ENABLED,
268             WIFI_RNR_NOT_NEEDED})
269     public @interface RnrSetting {}
270 
271     /**
272      * Maximum length in bytes of all vendor specific information elements (IEs) allowed to set.
273      * @hide
274      */
275     public static final int WIFI_SCANNER_SETTINGS_VENDOR_ELEMENTS_MAX_LEN = 512;
276 
277     /**
278      * Information Element head: id (1 byte) + length (1 byte)
279      * @hide
280      */
281     public static final int WIFI_IE_HEAD_LEN = 2;
282 
283     /**
284      * Generic action callback invocation interface
285      *  @hide
286      */
287     @SystemApi
288     public static interface ActionListener {
onSuccess()289         public void onSuccess();
onFailure(int reason, String description)290         public void onFailure(int reason, String description);
291     }
292 
293     /**
294      * Test if scan is a full scan. i.e. scanning all available bands.
295      * For backward compatibility, since some apps don't include 6GHz or 60Ghz in their requests
296      * yet, lacking 6GHz or 60Ghz band does not cause the result to be false.
297      *
298      * @param bandsScanned bands that are fully scanned
299      * @param excludeDfs when true, DFS band is excluded from the check
300      * @return true if all bands are scanned, false otherwise
301      *
302      * @hide
303      */
isFullBandScan(@ifiBand int bandsScanned, boolean excludeDfs)304     public static boolean isFullBandScan(@WifiBand int bandsScanned, boolean excludeDfs) {
305         return (bandsScanned | WIFI_BAND_6_GHZ | WIFI_BAND_60_GHZ
306                 | (excludeDfs ? WIFI_BAND_5_GHZ_DFS_ONLY : 0))
307                 == WIFI_BAND_ALL;
308     }
309 
310     /**
311      * Returns a list of all the possible channels for the given band(s).
312      *
313      * @param band one of the WifiScanner#WIFI_BAND_* constants, e.g. {@link #WIFI_BAND_24_GHZ}
314      * @return a list of all the frequencies, in MHz, for the given band(s) e.g. channel 1 is
315      * 2412, or null if an error occurred.
316      */
317     @NonNull
318     @RequiresPermission(NEARBY_WIFI_DEVICES)
getAvailableChannels(int band)319     public List<Integer> getAvailableChannels(int band) {
320         try {
321             Bundle extras = new Bundle();
322             if (SdkLevel.isAtLeastS()) {
323                 extras.putParcelable(WifiManager.EXTRA_PARAM_KEY_ATTRIBUTION_SOURCE,
324                         mContext.getAttributionSource());
325             }
326             Bundle bundle = mService.getAvailableChannels(band, mContext.getOpPackageName(),
327                     mContext.getAttributionTag(), extras);
328             List<Integer> channels = bundle.getIntegerArrayList(GET_AVAILABLE_CHANNELS_EXTRA);
329             return channels == null ? new ArrayList<>() : channels;
330         } catch (RemoteException e) {
331             throw e.rethrowFromSystemServer();
332         }
333     }
334 
335     private class ServiceListener extends IWifiScannerListener.Stub {
336         private ActionListener mActionListener;
337         private Executor mExecutor;
338 
ServiceListener(ActionListener listener, Executor executor)339         ServiceListener(ActionListener listener, Executor executor) {
340             mActionListener = listener;
341             mExecutor = executor;
342         }
343 
344         @Override
onSuccess()345         public void onSuccess() {
346             if (mActionListener == null) return;
347             Binder.clearCallingIdentity();
348             mExecutor.execute(mActionListener::onSuccess);
349         }
350 
351         @Override
onFailure(int reason, String description)352         public void onFailure(int reason, String description) {
353             if (mActionListener == null) return;
354             Binder.clearCallingIdentity();
355             mExecutor.execute(() ->
356                     mActionListener.onFailure(reason, description));
357             removeListener(mActionListener);
358         }
359 
360         /**
361          * reports results retrieved from background scan and single shot scans
362          */
363         @Override
onResults(WifiScanner.ScanData[] results)364         public void onResults(WifiScanner.ScanData[] results) {
365             if (mActionListener == null) return;
366             if (!(mActionListener instanceof ScanListener)) return;
367             ScanListener scanListener = (ScanListener) mActionListener;
368             Binder.clearCallingIdentity();
369             mExecutor.execute(
370                     () -> scanListener.onResults(results));
371         }
372 
373         /**
374          * reports full scan result for each access point found in scan
375          */
376         @Override
onFullResult(ScanResult fullScanResult)377         public void onFullResult(ScanResult fullScanResult) {
378             if (mActionListener == null) return;
379             if (!(mActionListener instanceof ScanListener)) return;
380             ScanListener scanListener = (ScanListener) mActionListener;
381             Binder.clearCallingIdentity();
382             mExecutor.execute(
383                     () -> scanListener.onFullResult(fullScanResult));
384         }
385 
386         @Override
onSingleScanCompleted()387         public void onSingleScanCompleted() {
388             if (DBG) Log.d(TAG, "single scan completed");
389             removeListener(mActionListener);
390         }
391 
392         /**
393          * Invoked when one of the PNO networks are found in scan results.
394          */
395         @Override
onPnoNetworkFound(ScanResult[] results)396         public void onPnoNetworkFound(ScanResult[] results) {
397             if (mActionListener == null) return;
398             if (!(mActionListener instanceof PnoScanListener)) return;
399             PnoScanListener pnoScanListener = (PnoScanListener) mActionListener;
400             Binder.clearCallingIdentity();
401             mExecutor.execute(
402                     () -> pnoScanListener.onPnoNetworkFound(results));
403         }
404     }
405 
406     /**
407      * provides channel specification for scanning
408      */
409     public static class ChannelSpec {
410         /**
411          * channel frequency in MHz; for example channel 1 is specified as 2412
412          */
413         public int frequency;
414         /**
415          * if true, scan this channel in passive fashion.
416          * This flag is ignored on DFS channel specification.
417          * @hide
418          */
419         public boolean passive;                                    /* ignored on DFS channels */
420         /**
421          * how long to dwell on this channel
422          * @hide
423          */
424         public int dwellTimeMS;                                    /* not supported for now */
425 
426         /**
427          * default constructor for channel spec
428          */
ChannelSpec(int frequency)429         public ChannelSpec(int frequency) {
430             this.frequency = frequency;
431             passive = false;
432             dwellTimeMS = 0;
433         }
434     }
435 
436     /**
437      * reports {@link ScanListener#onResults} when underlying buffers are full
438      * this is simply the lack of the {@link #REPORT_EVENT_AFTER_EACH_SCAN} flag
439      * @deprecated It is not supported anymore.
440      */
441     @Deprecated
442     public static final int REPORT_EVENT_AFTER_BUFFER_FULL = 0;
443     /**
444      * reports {@link ScanListener#onResults} after each scan
445      */
446     public static final int REPORT_EVENT_AFTER_EACH_SCAN = (1 << 0);
447     /**
448      * reports {@link ScanListener#onFullResult} whenever each beacon is discovered
449      */
450     public static final int REPORT_EVENT_FULL_SCAN_RESULT = (1 << 1);
451     /**
452      * Do not place scans in the chip's scan history buffer
453      */
454     public static final int REPORT_EVENT_NO_BATCH = (1 << 2);
455 
456     /**
457      * Optimize the scan for lower latency.
458      * @see ScanSettings#type
459      */
460     public static final int SCAN_TYPE_LOW_LATENCY = 0;
461     /**
462      * Optimize the scan for lower power usage.
463      * @see ScanSettings#type
464      */
465     public static final int SCAN_TYPE_LOW_POWER = 1;
466     /**
467      * Optimize the scan for higher accuracy.
468      * @see ScanSettings#type
469      */
470     public static final int SCAN_TYPE_HIGH_ACCURACY = 2;
471     /**
472      * Max valid value of SCAN_TYPE_
473      * @hide
474      */
475     public static final int SCAN_TYPE_MAX = 2;
476 
477     /** {@hide} */
478     public static final String SCAN_PARAMS_SCAN_SETTINGS_KEY = "ScanSettings";
479     /** {@hide} */
480     public static final String SCAN_PARAMS_WORK_SOURCE_KEY = "WorkSource";
481     /** {@hide} */
482     public static final String REQUEST_PACKAGE_NAME_KEY = "PackageName";
483     /** {@hide} */
484     public static final String REQUEST_FEATURE_ID_KEY = "FeatureId";
485 
486     /**
487      * scan configuration parameters to be sent to {@link #startBackgroundScan}
488      */
489     public static class ScanSettings implements Parcelable {
490         /** Hidden network to be scanned for. */
491         public static class HiddenNetwork {
492             /** SSID of the network */
493             @NonNull
494             public final String ssid;
495 
496             /** Default constructor for HiddenNetwork. */
HiddenNetwork(@onNull String ssid)497             public HiddenNetwork(@NonNull String ssid) {
498                 this.ssid = ssid;
499             }
500         }
501 
502         /** one of the WIFI_BAND values */
503         public int band;
504         /**
505          * one of the {@code WIFI_RNR_*} values.
506          */
507         private int mRnrSetting = WIFI_RNR_ENABLED_IF_WIFI_BAND_6_GHZ_SCANNED;
508 
509         /**
510          * See {@link #set6GhzPscOnlyEnabled}
511          */
512         private boolean mEnable6GhzPsc = false;
513 
514         /** list of channels; used when band is set to WIFI_BAND_UNSPECIFIED */
515         public ChannelSpec[] channels;
516         /**
517          * List of hidden networks to scan for. Explicit probe requests are sent out for such
518          * networks during scan. Only valid for single scan requests.
519          */
520         @NonNull
521         @RequiresPermission(android.Manifest.permission.NETWORK_STACK)
522         public final List<HiddenNetwork> hiddenNetworks = new ArrayList<>();
523 
524         /**
525          * vendor IEs -- list of ScanResult.InformationElement, configured by App
526          * see {@link #setVendorIes(List)}
527          */
528         @NonNull
529         private List<ScanResult.InformationElement> mVendorIes = new ArrayList<>();
530 
531         /**
532          * period of background scan; in millisecond, 0 => single shot scan
533          * @deprecated Background scan support has always been hardware vendor dependent. This
534          * support may not be present on newer devices. Use {@link #startScan(ScanSettings,
535          * ScanListener)} instead for single scans.
536          */
537         @Deprecated
538         public int periodInMs;
539         /**
540          * must have a valid REPORT_EVENT value
541          * @deprecated Background scan support has always been hardware vendor dependent. This
542          * support may not be present on newer devices. Use {@link #startScan(ScanSettings,
543          * ScanListener)} instead for single scans.
544          */
545         @Deprecated
546         public int reportEvents;
547         /**
548          * defines number of bssids to cache from each scan
549          * @deprecated Background scan support has always been hardware vendor dependent. This
550          * support may not be present on newer devices. Use {@link #startScan(ScanSettings,
551          * ScanListener)} instead for single scans.
552          */
553         @Deprecated
554         public int numBssidsPerScan;
555         /**
556          * defines number of scans to cache; use it with REPORT_EVENT_AFTER_BUFFER_FULL
557          * to wake up at fixed interval
558          * @deprecated Background scan support has always been hardware vendor dependent. This
559          * support may not be present on newer devices. Use {@link #startScan(ScanSettings,
560          * ScanListener)} instead for single scans.
561          */
562         @Deprecated
563         public int maxScansToCache;
564         /**
565          * if maxPeriodInMs is non zero or different than period, then this bucket is
566          * a truncated binary exponential backoff bucket and the scan period will grow
567          * exponentially as per formula: actual_period(N) = period * (2 ^ (N/stepCount))
568          * to maxPeriodInMs
569          * @deprecated Background scan support has always been hardware vendor dependent. This
570          * support may not be present on newer devices. Use {@link #startScan(ScanSettings,
571          * ScanListener)} instead for single scans.
572          */
573         @Deprecated
574         public int maxPeriodInMs;
575         /**
576          * for truncated binary exponential back off bucket, number of scans to perform
577          * for a given period
578          * @deprecated Background scan support has always been hardware vendor dependent. This
579          * support may not be present on newer devices. Use {@link #startScan(ScanSettings,
580          * ScanListener)} instead for single scans.
581          */
582         @Deprecated
583         public int stepCount;
584         /**
585          * Flag to indicate if the scan settings are targeted for PNO scan.
586          * {@hide}
587          */
588         public boolean isPnoScan;
589         /**
590          * Indicate the type of scan to be performed by the wifi chip.
591          *
592          * On devices with multiple hardware radio chains (and hence different modes of scan),
593          * this type serves as an indication to the hardware on what mode of scan to perform.
594          * Only apps holding {@link android.Manifest.permission.NETWORK_STACK} permission can set
595          * this value.
596          *
597          * Note: This serves as an intent and not as a stipulation, the wifi chip
598          * might honor or ignore the indication based on the current radio conditions. Always
599          * use the {@link ScanResult#radioChainInfos} to figure out the radio chain configuration
600          * used to receive the corresponding scan result.
601          *
602          * One of {@link #SCAN_TYPE_LOW_LATENCY}, {@link #SCAN_TYPE_LOW_POWER},
603          * {@link #SCAN_TYPE_HIGH_ACCURACY}.
604          * Default value: {@link #SCAN_TYPE_LOW_LATENCY}.
605          */
606         @WifiAnnotations.ScanType
607         @RequiresPermission(android.Manifest.permission.NETWORK_STACK)
608         public int type = SCAN_TYPE_LOW_LATENCY;
609         /**
610          * This scan request may ignore location settings while receiving scans. This should only
611          * be used in emergency situations.
612          * {@hide}
613          */
614         @SystemApi
615         public boolean ignoreLocationSettings;
616         /**
617          * This scan request will be hidden from app-ops noting for location information. This
618          * should only be used by FLP/NLP module on the device which is using the scan results to
619          * compute results for behalf on their clients. FLP/NLP module using this flag should ensure
620          * that they note in app-ops the eventual delivery of location information computed using
621          * these results to their client .
622          * {@hide}
623          */
624         @SystemApi
625         public boolean hideFromAppOps;
626 
627         /**
628          * Configure whether it is needed to scan 6Ghz non Preferred Scanning Channels when scanning
629          * {@link #WIFI_BAND_6_GHZ}. If set to true and a band that contains
630          * {@link #WIFI_BAND_6_GHZ} is configured for scanning, then only scan 6Ghz PSC channels in
631          * addition to any other bands configured for scanning. Note, 6Ghz non-PSC channels that
632          * are co-located with 2.4/5Ghz APs could still be scanned via the
633          * {@link #setRnrSetting(int)} API.
634          *
635          * <p>
636          * For example, given a ScanSettings with band set to {@link #WIFI_BAND_24_5_WITH_DFS_6_GHZ}
637          * If this API is set to "true" then the ScanSettings is configured to scan all of 2.4Ghz
638          * + all of 5Ghz(DFS and non-DFS) + 6Ghz PSC channels. If this API is set to "false", then
639          * the ScanSetting is configured to scan all of 2.4Ghz + all of 5Ghz(DFS and non_DFS)
640          * + all of 6Ghz channels.
641          * @param enable true to only scan 6Ghz PSC channels, false to scan all 6Ghz channels.
642          */
643         @RequiresApi(Build.VERSION_CODES.S)
set6GhzPscOnlyEnabled(boolean enable)644         public void set6GhzPscOnlyEnabled(boolean enable) {
645             if (!SdkLevel.isAtLeastS()) {
646                 throw new UnsupportedOperationException();
647             }
648             mEnable6GhzPsc = enable;
649         }
650 
651         /**
652          * See {@link #set6GhzPscOnlyEnabled}
653          */
654         @RequiresApi(Build.VERSION_CODES.S)
is6GhzPscOnlyEnabled()655         public boolean is6GhzPscOnlyEnabled() {
656             if (!SdkLevel.isAtLeastS()) {
657                 throw new UnsupportedOperationException();
658             }
659             return mEnable6GhzPsc;
660         }
661 
662         /**
663          * Configure when to scan 6Ghz APs co-located with 2.4/5Ghz APs using Reduced
664          * Neighbor Report (RNR).
665          * @param rnrSetting one of the {@code WIFI_RNR_*} values
666          */
667         @RequiresApi(Build.VERSION_CODES.S)
setRnrSetting(@nrSetting int rnrSetting)668         public void setRnrSetting(@RnrSetting int rnrSetting) {
669             if (!SdkLevel.isAtLeastS()) {
670                 throw new UnsupportedOperationException();
671             }
672             if (rnrSetting < WIFI_RNR_ENABLED_IF_WIFI_BAND_6_GHZ_SCANNED
673                     || rnrSetting > WIFI_RNR_NOT_NEEDED) {
674                 throw new IllegalArgumentException("Invalid rnrSetting");
675             }
676             mRnrSetting = rnrSetting;
677         }
678 
679         /**
680          * See {@link #setRnrSetting}
681          */
682         @RequiresApi(Build.VERSION_CODES.S)
getRnrSetting()683         public @RnrSetting int getRnrSetting() {
684             if (!SdkLevel.isAtLeastS()) {
685                 throw new UnsupportedOperationException();
686             }
687             return mRnrSetting;
688         }
689 
690         /**
691          * Set vendor IEs in scan probe req.
692          *
693          * @param vendorIes List of ScanResult.InformationElement configured by App.
694          */
695         @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
setVendorIes(@onNull List<ScanResult.InformationElement> vendorIes)696         public void setVendorIes(@NonNull List<ScanResult.InformationElement> vendorIes) {
697             if (!SdkLevel.isAtLeastU()) {
698                 throw new UnsupportedOperationException();
699             }
700 
701             mVendorIes.clear();
702             int totalBytes = 0;
703             for (ScanResult.InformationElement e : vendorIes) {
704                 if (e.id != ScanResult.InformationElement.EID_VSA) {
705                     throw new IllegalArgumentException("received InformationElement which is not "
706                             + "a Vendor Specific IE (VSIE). VSIEs have an ID = ScanResult"
707                             + ".InformationElement.EID_VSA.");
708                 }
709                 if (e.bytes == null || e.bytes.length > 0xff) {
710                     throw new IllegalArgumentException("received InformationElement whose payload "
711                             + "is null or size is greater than 255.");
712                 }
713                 // The total bytes of an IE is EID (1 byte) + length (1 byte) + payload length.
714                 totalBytes += WIFI_IE_HEAD_LEN + e.bytes.length;
715                 if (totalBytes > WIFI_SCANNER_SETTINGS_VENDOR_ELEMENTS_MAX_LEN) {
716                     throw new IllegalArgumentException(
717                             "received InformationElement whose total size is greater than "
718                                     + WIFI_SCANNER_SETTINGS_VENDOR_ELEMENTS_MAX_LEN + ".");
719                 }
720             }
721             mVendorIes.addAll(vendorIes);
722         }
723 
724         /**
725          * See {@link #setVendorIes(List)}
726          */
727         @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
getVendorIes()728         public @NonNull List<ScanResult.InformationElement> getVendorIes() {
729             if (!SdkLevel.isAtLeastU()) {
730                 throw new UnsupportedOperationException();
731             }
732             return mVendorIes;
733         }
734 
735         /** Implement the Parcelable interface {@hide} */
describeContents()736         public int describeContents() {
737             return 0;
738         }
739 
740         /** Implement the Parcelable interface {@hide} */
writeToParcel(Parcel dest, int flags)741         public void writeToParcel(Parcel dest, int flags) {
742             dest.writeInt(band);
743             dest.writeInt(periodInMs);
744             dest.writeInt(reportEvents);
745             dest.writeInt(numBssidsPerScan);
746             dest.writeInt(maxScansToCache);
747             dest.writeInt(maxPeriodInMs);
748             dest.writeInt(stepCount);
749             dest.writeInt(isPnoScan ? 1 : 0);
750             dest.writeInt(type);
751             dest.writeInt(ignoreLocationSettings ? 1 : 0);
752             dest.writeInt(hideFromAppOps ? 1 : 0);
753             dest.writeInt(mRnrSetting);
754             dest.writeBoolean(mEnable6GhzPsc);
755             if (channels != null) {
756                 dest.writeInt(channels.length);
757                 for (int i = 0; i < channels.length; i++) {
758                     dest.writeInt(channels[i].frequency);
759                     dest.writeInt(channels[i].dwellTimeMS);
760                     dest.writeInt(channels[i].passive ? 1 : 0);
761                 }
762             } else {
763                 dest.writeInt(0);
764             }
765             dest.writeInt(hiddenNetworks.size());
766             for (HiddenNetwork hiddenNetwork : hiddenNetworks) {
767                 dest.writeString(hiddenNetwork.ssid);
768             }
769             dest.writeTypedList(mVendorIes);
770         }
771 
772         /** Implement the Parcelable interface */
773         public static final @NonNull Creator<ScanSettings> CREATOR =
774                 new Creator<ScanSettings>() {
775                     public ScanSettings createFromParcel(Parcel in) {
776                         ScanSettings settings = new ScanSettings();
777                         settings.band = in.readInt();
778                         settings.periodInMs = in.readInt();
779                         settings.reportEvents = in.readInt();
780                         settings.numBssidsPerScan = in.readInt();
781                         settings.maxScansToCache = in.readInt();
782                         settings.maxPeriodInMs = in.readInt();
783                         settings.stepCount = in.readInt();
784                         settings.isPnoScan = in.readInt() == 1;
785                         settings.type = in.readInt();
786                         settings.ignoreLocationSettings = in.readInt() == 1;
787                         settings.hideFromAppOps = in.readInt() == 1;
788                         settings.mRnrSetting = in.readInt();
789                         settings.mEnable6GhzPsc = in.readBoolean();
790                         int num_channels = in.readInt();
791                         settings.channels = new ChannelSpec[num_channels];
792                         for (int i = 0; i < num_channels; i++) {
793                             int frequency = in.readInt();
794                             ChannelSpec spec = new ChannelSpec(frequency);
795                             spec.dwellTimeMS = in.readInt();
796                             spec.passive = in.readInt() == 1;
797                             settings.channels[i] = spec;
798                         }
799                         int numNetworks = in.readInt();
800                         settings.hiddenNetworks.clear();
801                         for (int i = 0; i < numNetworks; i++) {
802                             String ssid = in.readString();
803                             settings.hiddenNetworks.add(new HiddenNetwork(ssid));
804                         }
805                         in.readTypedList(settings.mVendorIes,
806                                 ScanResult.InformationElement.CREATOR);
807                         return settings;
808                     }
809 
810                     public ScanSettings[] newArray(int size) {
811                         return new ScanSettings[size];
812                     }
813                 };
814     }
815 
816     /**
817      * All the information garnered from a single scan
818      */
819     public static class ScanData implements Parcelable {
820         /** scan identifier */
821         private int mId;
822         /** additional information about scan
823          * 0 => no special issues encountered in the scan
824          * non-zero => scan was truncated, so results may not be complete
825          */
826         private int mFlags;
827         /**
828          * Indicates the buckets that were scanned to generate these results.
829          * This is not relevant to WifiScanner API users and is used internally.
830          * {@hide}
831          */
832         private int mBucketsScanned;
833         /**
834          * Bands scanned. One of the WIFI_BAND values.
835          * Will be {@link #WIFI_BAND_UNSPECIFIED} if the list of channels do not fully cover
836          * any of the bands.
837          * {@hide}
838          */
839         private int mScannedBands;
840         /** all scan results discovered in this scan, sorted by timestamp in ascending order */
841         private final List<ScanResult> mResults;
842 
843         /** {@hide} */
ScanData()844         public ScanData() {
845             mResults = new ArrayList<>();
846         }
847 
ScanData(int id, int flags, ScanResult[] results)848         public ScanData(int id, int flags, ScanResult[] results) {
849             mId = id;
850             mFlags = flags;
851             mResults = new ArrayList<>(Arrays.asList(results));
852         }
853 
854         /** {@hide} */
ScanData(int id, int flags, int bucketsScanned, int bandsScanned, ScanResult[] results)855         public ScanData(int id, int flags, int bucketsScanned, int bandsScanned,
856                         ScanResult[] results) {
857             this(id, flags, bucketsScanned, bandsScanned, new ArrayList<>(Arrays.asList(results)));
858         }
859 
860         /** {@hide} */
ScanData(int id, int flags, int bucketsScanned, int bandsScanned, List<ScanResult> results)861         public ScanData(int id, int flags, int bucketsScanned, int bandsScanned,
862                         List<ScanResult> results) {
863             mId = id;
864             mFlags = flags;
865             mBucketsScanned = bucketsScanned;
866             mScannedBands = bandsScanned;
867             mResults = results;
868         }
869 
ScanData(ScanData s)870         public ScanData(ScanData s) {
871             mId = s.mId;
872             mFlags = s.mFlags;
873             mBucketsScanned = s.mBucketsScanned;
874             mScannedBands = s.mScannedBands;
875             mResults = new ArrayList<>();
876             for (ScanResult scanResult : s.mResults) {
877                 mResults.add(new ScanResult(scanResult));
878             }
879         }
880 
getId()881         public int getId() {
882             return mId;
883         }
884 
getFlags()885         public int getFlags() {
886             return mFlags;
887         }
888 
889         /** {@hide} */
getBucketsScanned()890         public int getBucketsScanned() {
891             return mBucketsScanned;
892         }
893 
894         /**
895          * Retrieve the bands that were fully scanned for this ScanData instance. "fully" here
896          * refers to all the channels available in the band based on the current regulatory
897          * domain.
898          *
899          * @return Bitmask of {@link #WIFI_BAND_24_GHZ}, {@link #WIFI_BAND_5_GHZ},
900          * {@link #WIFI_BAND_5_GHZ_DFS_ONLY}, {@link #WIFI_BAND_6_GHZ} & {@link #WIFI_BAND_60_GHZ}
901          * values. Each bit is set only if all the channels in the corresponding band is scanned.
902          * Will be {@link #WIFI_BAND_UNSPECIFIED} if the list of channels do not fully cover
903          * any of the bands.
904          * <p>
905          * For ex:
906          * <li> Scenario 1:  Fully scanned 2.4Ghz band, partially scanned 5Ghz band
907          *      - Returns {@link #WIFI_BAND_24_GHZ}
908          * </li>
909          * <li> Scenario 2:  Partially scanned 2.4Ghz band and 5Ghz band
910          *      - Returns {@link #WIFI_BAND_UNSPECIFIED}
911          * </li>
912          * </p>
913          */
getScannedBands()914         public @WifiBand int getScannedBands() {
915             return getScannedBandsInternal();
916         }
917 
918         /**
919          * Same as {@link #getScannedBands()}. For use in the wifi stack without version check.
920          *
921          * {@hide}
922          */
getScannedBandsInternal()923         public @WifiBand int getScannedBandsInternal() {
924             return mScannedBands;
925         }
926 
getResults()927         public ScanResult[] getResults() {
928             return mResults.toArray(new ScanResult[0]);
929         }
930 
931         /** {@hide} */
addResults(@onNull ScanResult[] newResults)932         public void addResults(@NonNull ScanResult[] newResults) {
933             for (ScanResult result : newResults) {
934                 mResults.add(new ScanResult(result));
935             }
936         }
937 
938         /** {@hide} */
addResults(@onNull ScanData s)939         public void addResults(@NonNull ScanData s) {
940             mScannedBands |= s.mScannedBands;
941             mFlags |= s.mFlags;
942             addResults(s.getResults());
943         }
944 
945         /** {@hide} */
isFullBandScanResults()946         public boolean isFullBandScanResults() {
947             return (mScannedBands & WifiScanner.WIFI_BAND_24_GHZ) != 0
948                 && (mScannedBands & WifiScanner.WIFI_BAND_5_GHZ) != 0;
949         }
950 
951         /** Implement the Parcelable interface {@hide} */
describeContents()952         public int describeContents() {
953             return 0;
954         }
955 
956         /** Implement the Parcelable interface {@hide} */
writeToParcel(Parcel dest, int flags)957         public void writeToParcel(Parcel dest, int flags) {
958             dest.writeInt(mId);
959             dest.writeInt(mFlags);
960             dest.writeInt(mBucketsScanned);
961             dest.writeInt(mScannedBands);
962             dest.writeParcelableList(mResults, 0);
963         }
964 
965         /** Implement the Parcelable interface {@hide} */
966         public static final @NonNull Creator<ScanData> CREATOR =
967                 new Creator<ScanData>() {
968                     public ScanData createFromParcel(Parcel in) {
969                         int id = in.readInt();
970                         int flags = in.readInt();
971                         int bucketsScanned = in.readInt();
972                         int bandsScanned = in.readInt();
973                         List<ScanResult> results = new ArrayList<>();
974                         in.readParcelableList(results, ScanResult.class.getClassLoader());
975                         return new ScanData(id, flags, bucketsScanned, bandsScanned, results);
976                     }
977 
978                     public ScanData[] newArray(int size) {
979                         return new ScanData[size];
980                     }
981                 };
982     }
983 
984     public static class ParcelableScanData implements Parcelable {
985 
986         public ScanData mResults[];
987 
ParcelableScanData(ScanData[] results)988         public ParcelableScanData(ScanData[] results) {
989             mResults = results;
990         }
991 
getResults()992         public ScanData[] getResults() {
993             return mResults;
994         }
995 
996         /** Implement the Parcelable interface {@hide} */
describeContents()997         public int describeContents() {
998             return 0;
999         }
1000 
1001         /** Implement the Parcelable interface {@hide} */
writeToParcel(Parcel dest, int flags)1002         public void writeToParcel(Parcel dest, int flags) {
1003             if (mResults != null) {
1004                 dest.writeInt(mResults.length);
1005                 for (int i = 0; i < mResults.length; i++) {
1006                     ScanData result = mResults[i];
1007                     result.writeToParcel(dest, flags);
1008                 }
1009             } else {
1010                 dest.writeInt(0);
1011             }
1012         }
1013 
1014         /** Implement the Parcelable interface {@hide} */
1015         public static final @NonNull Creator<ParcelableScanData> CREATOR =
1016                 new Creator<ParcelableScanData>() {
1017                     public ParcelableScanData createFromParcel(Parcel in) {
1018                         int n = in.readInt();
1019                         ScanData results[] = new ScanData[n];
1020                         for (int i = 0; i < n; i++) {
1021                             results[i] = ScanData.CREATOR.createFromParcel(in);
1022                         }
1023                         return new ParcelableScanData(results);
1024                     }
1025 
1026                     public ParcelableScanData[] newArray(int size) {
1027                         return new ParcelableScanData[size];
1028                     }
1029                 };
1030     }
1031 
1032     public static class ParcelableScanResults implements Parcelable {
1033 
1034         public ScanResult mResults[];
1035 
ParcelableScanResults(ScanResult[] results)1036         public ParcelableScanResults(ScanResult[] results) {
1037             mResults = results;
1038         }
1039 
getResults()1040         public ScanResult[] getResults() {
1041             return mResults;
1042         }
1043 
1044         /** Implement the Parcelable interface {@hide} */
describeContents()1045         public int describeContents() {
1046             return 0;
1047         }
1048 
1049         /** Implement the Parcelable interface {@hide} */
writeToParcel(Parcel dest, int flags)1050         public void writeToParcel(Parcel dest, int flags) {
1051             if (mResults != null) {
1052                 dest.writeInt(mResults.length);
1053                 for (int i = 0; i < mResults.length; i++) {
1054                     ScanResult result = mResults[i];
1055                     result.writeToParcel(dest, flags);
1056                 }
1057             } else {
1058                 dest.writeInt(0);
1059             }
1060         }
1061 
1062         /** Implement the Parcelable interface {@hide} */
1063         public static final @NonNull Creator<ParcelableScanResults> CREATOR =
1064                 new Creator<ParcelableScanResults>() {
1065                     public ParcelableScanResults createFromParcel(Parcel in) {
1066                         int n = in.readInt();
1067                         ScanResult results[] = new ScanResult[n];
1068                         for (int i = 0; i < n; i++) {
1069                             results[i] = ScanResult.CREATOR.createFromParcel(in);
1070                         }
1071                         return new ParcelableScanResults(results);
1072                     }
1073 
1074                     public ParcelableScanResults[] newArray(int size) {
1075                         return new ParcelableScanResults[size];
1076                     }
1077                 };
1078     }
1079 
1080     /** {@hide} */
1081     public static final String PNO_PARAMS_PNO_SETTINGS_KEY = "PnoSettings";
1082     /** {@hide} */
1083     public static final String PNO_PARAMS_SCAN_SETTINGS_KEY = "ScanSettings";
1084     /**
1085      * PNO scan configuration parameters to be sent to {@link #startPnoScan}.
1086      * Note: This structure needs to be in sync with |wifi_epno_params| struct in gscan HAL API.
1087      * {@hide}
1088      */
1089     public static class PnoSettings implements Parcelable {
1090         /**
1091          * Pno network to be added to the PNO scan filtering.
1092          * {@hide}
1093          */
1094         public static class PnoNetwork {
1095             /*
1096              * Pno flags bitmask to be set in {@link #PnoNetwork.flags}
1097              */
1098             /** Whether directed scan needs to be performed (for hidden SSIDs) */
1099             public static final byte FLAG_DIRECTED_SCAN = (1 << 0);
1100             /** Whether PNO event shall be triggered if the network is found on A band */
1101             public static final byte FLAG_A_BAND = (1 << 1);
1102             /** Whether PNO event shall be triggered if the network is found on G band */
1103             public static final byte FLAG_G_BAND = (1 << 2);
1104             /**
1105              * Whether strict matching is required
1106              * If required then the firmware must store the network's SSID and not just a hash
1107              */
1108             public static final byte FLAG_STRICT_MATCH = (1 << 3);
1109             /**
1110              * If this SSID should be considered the same network as the currently connected
1111              * one for scoring.
1112              */
1113             public static final byte FLAG_SAME_NETWORK = (1 << 4);
1114 
1115             /*
1116              * Code for matching the beacon AUTH IE - additional codes. Bitmask to be set in
1117              * {@link #PnoNetwork.authBitField}
1118              */
1119             /** Open Network */
1120             public static final byte AUTH_CODE_OPEN = (1 << 0);
1121             /** WPA_PSK or WPA2PSK */
1122             public static final byte AUTH_CODE_PSK = (1 << 1);
1123             /** any EAPOL */
1124             public static final byte AUTH_CODE_EAPOL = (1 << 2);
1125 
1126             /** SSID of the network */
1127             public String ssid;
1128             /** Bitmask of the FLAG_XXX */
1129             public byte flags = 0;
1130             /** Bitmask of the ATUH_XXX */
1131             public byte authBitField = 0;
1132             /** frequencies on which the particular network needs to be scanned for */
1133             public int[] frequencies = {};
1134 
1135             /**
1136              * default constructor for PnoNetwork
1137              */
PnoNetwork(String ssid)1138             public PnoNetwork(String ssid) {
1139                 this.ssid = ssid;
1140             }
1141 
1142             @Override
hashCode()1143             public int hashCode() {
1144                 return Objects.hash(ssid, flags, authBitField);
1145             }
1146 
1147             @Override
equals(Object obj)1148             public boolean equals(Object obj) {
1149                 if (this == obj) {
1150                     return true;
1151                 }
1152                 if (!(obj instanceof PnoNetwork)) {
1153                     return false;
1154                 }
1155                 PnoNetwork lhs = (PnoNetwork) obj;
1156                 return TextUtils.equals(this.ssid, lhs.ssid)
1157                         && this.flags == lhs.flags
1158                         && this.authBitField == lhs.authBitField;
1159             }
1160         }
1161 
1162         /** Connected vs Disconnected PNO flag {@hide} */
1163         public boolean isConnected;
1164         /** Minimum 5GHz RSSI for a BSSID to be considered */
1165         public int min5GHzRssi;
1166         /** Minimum 2.4GHz RSSI for a BSSID to be considered */
1167         public int min24GHzRssi;
1168         /** Minimum 6GHz RSSI for a BSSID to be considered */
1169         public int min6GHzRssi;
1170         /** Iterations of Pno scan */
1171         public int scanIterations;
1172         /** Multiplier of Pno scan interval */
1173         public int scanIntervalMultiplier;
1174         /** Pno Network filter list */
1175         public PnoNetwork[] networkList;
1176 
1177         /** Implement the Parcelable interface {@hide} */
describeContents()1178         public int describeContents() {
1179             return 0;
1180         }
1181 
1182         /** Implement the Parcelable interface {@hide} */
writeToParcel(Parcel dest, int flags)1183         public void writeToParcel(Parcel dest, int flags) {
1184             dest.writeInt(isConnected ? 1 : 0);
1185             dest.writeInt(min5GHzRssi);
1186             dest.writeInt(min24GHzRssi);
1187             dest.writeInt(min6GHzRssi);
1188             dest.writeInt(scanIterations);
1189             dest.writeInt(scanIntervalMultiplier);
1190             if (networkList != null) {
1191                 dest.writeInt(networkList.length);
1192                 for (int i = 0; i < networkList.length; i++) {
1193                     dest.writeString(networkList[i].ssid);
1194                     dest.writeByte(networkList[i].flags);
1195                     dest.writeByte(networkList[i].authBitField);
1196                     dest.writeIntArray(networkList[i].frequencies);
1197                 }
1198             } else {
1199                 dest.writeInt(0);
1200             }
1201         }
1202 
1203         /** Implement the Parcelable interface {@hide} */
1204         public static final @NonNull Creator<PnoSettings> CREATOR =
1205                 new Creator<PnoSettings>() {
1206                     public PnoSettings createFromParcel(Parcel in) {
1207                         PnoSettings settings = new PnoSettings();
1208                         settings.isConnected = in.readInt() == 1;
1209                         settings.min5GHzRssi = in.readInt();
1210                         settings.min24GHzRssi = in.readInt();
1211                         settings.min6GHzRssi = in.readInt();
1212                         settings.scanIterations = in.readInt();
1213                         settings.scanIntervalMultiplier = in.readInt();
1214                         int numNetworks = in.readInt();
1215                         settings.networkList = new PnoNetwork[numNetworks];
1216                         for (int i = 0; i < numNetworks; i++) {
1217                             String ssid = in.readString();
1218                             PnoNetwork network = new PnoNetwork(ssid);
1219                             network.flags = in.readByte();
1220                             network.authBitField = in.readByte();
1221                             network.frequencies = in.createIntArray();
1222                             settings.networkList[i] = network;
1223                         }
1224                         return settings;
1225                     }
1226 
1227                     public PnoSettings[] newArray(int size) {
1228                         return new PnoSettings[size];
1229                     }
1230                 };
1231 
1232     }
1233 
1234     /**
1235      * interface to get scan events on; specify this on {@link #startBackgroundScan} or
1236      * {@link #startScan}
1237      */
1238     public interface ScanListener extends ActionListener {
1239         /**
1240          * Framework co-ordinates scans across multiple apps; so it may not give exactly the
1241          * same period requested. If period of a scan is changed; it is reported by this event.
1242          * @deprecated Background scan support has always been hardware vendor dependent. This
1243          * support may not be present on newer devices. Use {@link #startScan(ScanSettings,
1244          * ScanListener)} instead for single scans.
1245          */
1246         @Deprecated
onPeriodChanged(int periodInMs)1247         public void onPeriodChanged(int periodInMs);
1248         /**
1249          * reports results retrieved from background scan and single shot scans
1250          */
onResults(ScanData[] results)1251         public void onResults(ScanData[] results);
1252         /**
1253          * reports full scan result for each access point found in scan
1254          */
onFullResult(ScanResult fullScanResult)1255         public void onFullResult(ScanResult fullScanResult);
1256     }
1257 
1258     /**
1259      * interface to get PNO scan events on; specify this on {@link #startDisconnectedPnoScan} and
1260      * {@link #startConnectedPnoScan}.
1261      * {@hide}
1262      */
1263     public interface PnoScanListener extends ScanListener {
1264         /**
1265          * Invoked when one of the PNO networks are found in scan results.
1266          */
onPnoNetworkFound(ScanResult[] results)1267         void onPnoNetworkFound(ScanResult[] results);
1268     }
1269 
1270     /**
1271      * Enable/Disable wifi scanning.
1272      *
1273      * @param enable set to true to enable scanning, set to false to disable all types of scanning.
1274      *
1275      * @see WifiManager#ACTION_WIFI_SCAN_AVAILABILITY_CHANGED
1276      * {@hide}
1277      */
1278     @SystemApi
1279     @RequiresPermission(Manifest.permission.NETWORK_STACK)
setScanningEnabled(boolean enable)1280     public void setScanningEnabled(boolean enable) {
1281         try {
1282             mService.setScanningEnabled(enable, Process.myTid(), mContext.getOpPackageName());
1283         } catch (RemoteException e) {
1284             throw e.rethrowFromSystemServer();
1285         }
1286     }
1287 
1288     /**
1289      * Register a listener that will receive results from all single scans.
1290      * Either the {@link ScanListener#onSuccess()} or  {@link ScanListener#onFailure(int, String)}
1291      * method will be called once when the listener is registered.
1292      * Afterwards (assuming onSuccess was called), all subsequent single scan results will be
1293      * delivered to the listener. It is possible that onFullResult will not be called for all
1294      * results of the first scan if the listener was registered during the scan.
1295      * <p>
1296      * On {@link android.os.Build.VERSION_CODES#TIRAMISU} or above this API can be called by
1297      * an app with either {@link android.Manifest.permission#LOCATION_HARDWARE} or
1298      * {@link android.Manifest.permission#NETWORK_STACK}. On platform versions prior to
1299      * {@link android.os.Build.VERSION_CODES#TIRAMISU}, the caller must have
1300      * {@link android.Manifest.permission#NETWORK_STACK}.
1301      *
1302      * @param executor the Executor on which to run the callback.
1303      * @param listener specifies the object to report events to. This object is also treated as a
1304      *                 key for this request, and must also be specified to cancel the request.
1305      *                 Multiple requests should also not share this object.
1306      * @throws SecurityException if the caller does not have permission.
1307      */
1308     @RequiresPermission(anyOf = {
1309             Manifest.permission.LOCATION_HARDWARE,
1310             Manifest.permission.NETWORK_STACK})
registerScanListener(@onNull @allbackExecutor Executor executor, @NonNull ScanListener listener)1311     public void registerScanListener(@NonNull @CallbackExecutor Executor executor,
1312             @NonNull ScanListener listener) {
1313         Objects.requireNonNull(executor, "executor cannot be null");
1314         Objects.requireNonNull(listener, "listener cannot be null");
1315         ServiceListener serviceListener = new ServiceListener(listener, executor);
1316         if (!addListener(listener, serviceListener)) {
1317             Binder.clearCallingIdentity();
1318             executor.execute(() ->
1319                     // TODO: fix the typo in WifiScanner system API.
1320                     listener.onFailure(REASON_DUPLICATE_REQEUST, // NOTYPO
1321                             "Outstanding request with same key not stopped yet"));
1322             return;
1323         }
1324         try {
1325             mService.registerScanListener(serviceListener,
1326                     mContext.getOpPackageName(),
1327                     mContext.getAttributionTag());
1328         } catch (RemoteException e) {
1329             Log.e(TAG, "Failed to register listener " + listener);
1330             removeListener(listener);
1331             throw e.rethrowFromSystemServer();
1332         }
1333     }
1334 
1335     /**
1336      * Overload of {@link #registerScanListener(Executor, ScanListener)} that executes the callback
1337      * synchronously.
1338      * @hide
1339      */
1340     @RequiresPermission(Manifest.permission.NETWORK_STACK)
registerScanListener(@onNull ScanListener listener)1341     public void registerScanListener(@NonNull ScanListener listener) {
1342         registerScanListener(new SynchronousExecutor(), listener);
1343     }
1344 
1345     /**
1346      * Deregister a listener for ongoing single scans
1347      * @param listener specifies which scan to cancel; must be same object as passed in {@link
1348      *  #registerScanListener}
1349      */
unregisterScanListener(@onNull ScanListener listener)1350     public void unregisterScanListener(@NonNull ScanListener listener) {
1351         Objects.requireNonNull(listener, "listener cannot be null");
1352         ServiceListener serviceListener = getServiceListener(listener);
1353         if (serviceListener == null) {
1354             Log.e(TAG, "listener does not exist");
1355             return;
1356         }
1357         try {
1358             mService.unregisterScanListener(serviceListener, mContext.getOpPackageName(),
1359                     mContext.getAttributionTag());
1360         } catch (RemoteException e) {
1361             Log.e(TAG, "failed to unregister listener");
1362             throw e.rethrowFromSystemServer();
1363         } finally {
1364             removeListener(listener);
1365         }
1366     }
1367 
1368     /**
1369      * Check whether the Wi-Fi subsystem has started a scan and is waiting for scan results.
1370      * @return true if a scan initiated via
1371      *         {@link WifiScanner#startScan(ScanSettings, ScanListener)} or
1372      *         {@link WifiManager#startScan()} is in progress.
1373      *         false if there is currently no scanning initiated by {@link WifiScanner} or
1374      *         {@link WifiManager}, but it's still possible the wifi radio is scanning for
1375      *         another reason.
1376      * @hide
1377      */
1378     @SystemApi
1379     @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
isScanning()1380     public boolean isScanning() {
1381         try {
1382             return mService.isScanning();
1383         } catch (RemoteException e) {
1384             throw e.rethrowFromSystemServer();
1385         }
1386     }
1387 
1388     /** start wifi scan in background
1389      * @param settings specifies various parameters for the scan; for more information look at
1390      * {@link ScanSettings}
1391      * @param listener specifies the object to report events to. This object is also treated as a
1392      *                 key for this scan, and must also be specified to cancel the scan. Multiple
1393      *                 scans should also not share this object.
1394      */
1395     @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
startBackgroundScan(ScanSettings settings, ScanListener listener)1396     public void startBackgroundScan(ScanSettings settings, ScanListener listener) {
1397         startBackgroundScan(settings, listener, null);
1398     }
1399 
1400     /** start wifi scan in background
1401      * @param settings specifies various parameters for the scan; for more information look at
1402      * {@link ScanSettings}
1403      * @param workSource WorkSource to blame for power usage
1404      * @param listener specifies the object to report events to. This object is also treated as a
1405      *                 key for this scan, and must also be specified to cancel the scan. Multiple
1406      *                 scans should also not share this object.
1407      * @deprecated Background scan support has always been hardware vendor dependent. This support
1408      * may not be present on newer devices. Use {@link #startScan(ScanSettings, ScanListener)}
1409      * instead for single scans.
1410      */
1411     @Deprecated
1412     @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
startBackgroundScan(ScanSettings settings, ScanListener listener, WorkSource workSource)1413     public void startBackgroundScan(ScanSettings settings, ScanListener listener,
1414             WorkSource workSource) {
1415         Objects.requireNonNull(listener, "listener cannot be null");
1416         if (getServiceListener(listener) != null) return;
1417         ServiceListener serviceListener = new ServiceListener(listener, new SynchronousExecutor());
1418         if (!addListener(listener, serviceListener)) {
1419             Log.e(TAG, "listener already exist!");
1420             return;
1421         }
1422         try {
1423             mService.startBackgroundScan(serviceListener, settings, workSource,
1424                     mContext.getOpPackageName(), mContext.getAttributionTag());
1425         } catch (RemoteException e) {
1426             throw e.rethrowFromSystemServer();
1427         }
1428     }
1429 
1430     /**
1431      * stop an ongoing wifi scan
1432      * @param listener specifies which scan to cancel; must be same object as passed in {@link
1433      *  #startBackgroundScan}
1434      * @deprecated Background scan support has always been hardware vendor dependent. This support
1435      * may not be present on newer devices. Use {@link #startScan(ScanSettings, ScanListener)}
1436      * instead for single scans.
1437      */
1438     @Deprecated
1439     @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
stopBackgroundScan(ScanListener listener)1440     public void stopBackgroundScan(ScanListener listener) {
1441         Objects.requireNonNull(listener, "listener cannot be null");
1442         ServiceListener serviceListener = getServiceListener(listener);
1443         if (serviceListener == null) {
1444             Log.e(TAG, "listener does not exist");
1445             return;
1446         }
1447         try {
1448             mService.stopBackgroundScan(serviceListener, mContext.getOpPackageName(),
1449                     mContext.getAttributionTag());
1450         } catch (RemoteException e) {
1451             throw e.rethrowFromSystemServer();
1452         } finally {
1453             removeListener(listener);
1454         }
1455     }
1456 
1457     /**
1458      * reports currently available scan results on appropriate listeners
1459      * @return true if all scan results were reported correctly
1460      * @deprecated Background scan support has always been hardware vendor dependent. This support
1461      * may not be present on newer devices. Use {@link #startScan(ScanSettings, ScanListener)}
1462      * instead for single scans.
1463      */
1464     @Deprecated
1465     @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
getScanResults()1466     public boolean getScanResults() {
1467         try {
1468             return mService.getScanResults(mContext.getOpPackageName(),
1469                     mContext.getAttributionTag());
1470         } catch (RemoteException e) {
1471             throw e.rethrowFromSystemServer();
1472         }
1473     }
1474 
1475     /**
1476      * starts a single scan and reports results asynchronously
1477      * @param settings specifies various parameters for the scan; for more information look at
1478      * {@link ScanSettings}
1479      * @param listener specifies the object to report events to. This object is also treated as a
1480      *                 key for this scan, and must also be specified to cancel the scan. Multiple
1481      *                 scans should also not share this object.
1482      */
1483     @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
startScan(ScanSettings settings, ScanListener listener)1484     public void startScan(ScanSettings settings, ScanListener listener) {
1485         startScan(settings, listener, null);
1486     }
1487 
1488     /**
1489      * starts a single scan and reports results asynchronously
1490      * @param settings specifies various parameters for the scan; for more information look at
1491      * {@link ScanSettings}
1492      * @param listener specifies the object to report events to. This object is also treated as a
1493      *                 key for this scan, and must also be specified to cancel the scan. Multiple
1494      *                 scans should also not share this object.
1495      * @param workSource WorkSource to blame for power usage
1496      */
1497     @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
startScan(ScanSettings settings, ScanListener listener, WorkSource workSource)1498     public void startScan(ScanSettings settings, ScanListener listener, WorkSource workSource) {
1499         startScan(settings, new SynchronousExecutor(), listener, workSource);
1500     }
1501 
1502     /**
1503      * starts a single scan and reports results asynchronously
1504      * @param settings specifies various parameters for the scan; for more information look at
1505      * {@link ScanSettings}
1506      * @param executor the Executor on which to run the callback.
1507      * @param listener specifies the object to report events to. This object is also treated as a
1508      *                 key for this scan, and must also be specified to cancel the scan. Multiple
1509      *                 scans should also not share this object.
1510      * @param workSource WorkSource to blame for power usage
1511      * @hide
1512      */
1513     @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
startScan(ScanSettings settings, @Nullable @CallbackExecutor Executor executor, ScanListener listener, WorkSource workSource)1514     public void startScan(ScanSettings settings, @Nullable @CallbackExecutor Executor executor,
1515             ScanListener listener, WorkSource workSource) {
1516         Objects.requireNonNull(listener, "listener cannot be null");
1517         if (getServiceListener(listener) != null) return;
1518         ServiceListener serviceListener = new ServiceListener(listener, executor);
1519         if (!addListener(listener, serviceListener)) {
1520             Log.e(TAG, "listener already exist!");
1521             return;
1522         }
1523         try {
1524             mService.startScan(serviceListener, settings, workSource,
1525                     mContext.getOpPackageName(),
1526                     mContext.getAttributionTag());
1527         } catch (RemoteException e) {
1528             throw e.rethrowFromSystemServer();
1529         }
1530     }
1531 
1532     /**
1533      * stops an ongoing single shot scan; only useful after {@link #startScan} if onResults()
1534      * hasn't been called on the listener, ignored otherwise
1535      * @param listener
1536      */
1537     @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
stopScan(ScanListener listener)1538     public void stopScan(ScanListener listener) {
1539         Objects.requireNonNull(listener, "listener cannot be null");
1540         ServiceListener serviceListener = getServiceListener(listener);
1541         if (serviceListener == null) {
1542             Log.e(TAG, "listener does not exist");
1543             return;
1544         }
1545         try {
1546             mService.stopScan(serviceListener, mContext.getOpPackageName(),
1547                     mContext.getAttributionTag());
1548         } catch (RemoteException e) {
1549             throw e.rethrowFromSystemServer();
1550         } finally {
1551             removeListener(listener);
1552         }
1553     }
1554 
1555     /**
1556      * Retrieve the most recent scan results from a single scan request.
1557      *
1558      * <p>
1559      * When an Access Point’s beacon or probe response includes a Multi-BSSID Element, the
1560      * returned scan results should include separate scan result for each BSSID within the
1561      * Multi-BSSID Information Element. This includes both transmitted and non-transmitted BSSIDs.
1562      * Original Multi-BSSID Element will be included in the Information Elements attached to
1563      * each of the scan results.
1564      * Note: This is the expected behavior for devices supporting 11ax (WiFi-6) and above, and an
1565      * optional requirement for devices running with older WiFi generations.
1566      * </p>
1567      */
1568     @NonNull
1569     @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
getSingleScanResults()1570     public List<ScanResult> getSingleScanResults() {
1571         try {
1572             return mService.getSingleScanResults(mContext.getPackageName(),
1573                     mContext.getAttributionTag());
1574         } catch (RemoteException e) {
1575             throw e.rethrowFromSystemServer();
1576         }
1577     }
1578 
1579     /**
1580      * Retrieve the scan data cached by the hardware.
1581      *
1582      * <p>
1583      * When an Access Point’s beacon or probe response includes a Multi-BSSID Element, the
1584      * returned scan results should include separate scan result for each BSSID within the
1585      * Multi-BSSID Information Element. This includes both transmitted and non-transmitted BSSIDs.
1586      * Original Multi-BSSID Element will be included in the Information Elements attached to
1587      * each of the scan results.
1588      * Note: This is the expected behavior for devices supporting 11ax (WiFi-6) and above, and an
1589      * optional requirement for devices running with older WiFi generations.
1590      * </p>
1591      *
1592      * @param executor The executor on which callback will be invoked.
1593      * @param resultsCallback An asynchronous callback that will return the cached scan data.
1594      *
1595      * @throws UnsupportedOperationException if the API is not supported on this SDK version.
1596      * @throws SecurityException if the caller does not have permission.
1597      * @throws NullPointerException if the caller provided invalid inputs.
1598      */
1599     @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API)
1600     @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
1601     @RequiresPermission(allOf = {ACCESS_FINE_LOCATION, LOCATION_HARDWARE})
getCachedScanData(@onNull @allbackExecutor Executor executor, @NonNull Consumer<ScanData> resultsCallback)1602     public void getCachedScanData(@NonNull @CallbackExecutor Executor executor,
1603             @NonNull Consumer<ScanData> resultsCallback) {
1604         Objects.requireNonNull(executor, "executor cannot be null");
1605         Objects.requireNonNull(resultsCallback, "resultsCallback cannot be null");
1606         try {
1607             mService.getCachedScanData(mContext.getPackageName(),
1608                     mContext.getAttributionTag(),
1609                     new IScanDataListener.Stub() {
1610                         @Override
1611                         public void onResult(@NonNull ScanData scanData) {
1612                             Binder.clearCallingIdentity();
1613                             executor.execute(() -> {
1614                                 resultsCallback.accept(scanData);
1615                             });
1616                         }
1617                     });
1618         } catch (RemoteException e) {
1619             throw e.rethrowFromSystemServer();
1620         }
1621     }
1622 
1623 
startPnoScan(PnoScanListener listener, Executor executor, ScanSettings scanSettings, PnoSettings pnoSettings)1624     private void startPnoScan(PnoScanListener listener, Executor executor,
1625             ScanSettings scanSettings, PnoSettings pnoSettings) {
1626         // Set the PNO scan flag.
1627         scanSettings.isPnoScan = true;
1628         if (getServiceListener(listener) != null) return;
1629         ServiceListener serviceListener = new ServiceListener(listener, executor);
1630         if (!addListener(listener, serviceListener)) {
1631             Log.w(TAG, "listener already exist!");
1632         }
1633         try {
1634             mService.startPnoScan(serviceListener, scanSettings, pnoSettings,
1635                     mContext.getOpPackageName(),
1636                     mContext.getAttributionTag());
1637         } catch (RemoteException e) {
1638             throw e.rethrowFromSystemServer();
1639         }
1640     }
1641 
1642     /**
1643      * Start wifi connected PNO scan
1644      * @param scanSettings specifies various parameters for the scan; for more information look at
1645      * {@link ScanSettings}
1646      * @param pnoSettings specifies various parameters for PNO; for more information look at
1647      * {@link PnoSettings}
1648      * @param executor the Executor on which to run the callback.
1649      * @param listener specifies the object to report events to. This object is also treated as a
1650      *                 key for this scan, and must also be specified to cancel the scan. Multiple
1651      *                 scans should also not share this object.
1652      * {@hide}
1653      */
startConnectedPnoScan(ScanSettings scanSettings, PnoSettings pnoSettings, @NonNull @CallbackExecutor Executor executor, PnoScanListener listener)1654     public void startConnectedPnoScan(ScanSettings scanSettings, PnoSettings pnoSettings,
1655             @NonNull @CallbackExecutor Executor executor, PnoScanListener listener) {
1656         Objects.requireNonNull(listener, "listener cannot be null");
1657         Objects.requireNonNull(pnoSettings, "pnoSettings cannot be null");
1658         pnoSettings.isConnected = true;
1659         startPnoScan(listener, executor, scanSettings, pnoSettings);
1660     }
1661     /**
1662      * Start wifi disconnected PNO scan
1663      * @param scanSettings specifies various parameters for the scan; for more information look at
1664      * {@link ScanSettings}
1665      * @param pnoSettings specifies various parameters for PNO; for more information look at
1666      * {@link PnoSettings}
1667      * @param listener specifies the object to report events to. This object is also treated as a
1668      *                 key for this scan, and must also be specified to cancel the scan. Multiple
1669      *                 scans should also not share this object.
1670      * {@hide}
1671      */
1672     @RequiresPermission(android.Manifest.permission.NETWORK_STACK)
startDisconnectedPnoScan(ScanSettings scanSettings, PnoSettings pnoSettings, @NonNull @CallbackExecutor Executor executor, PnoScanListener listener)1673     public void startDisconnectedPnoScan(ScanSettings scanSettings, PnoSettings pnoSettings,
1674             @NonNull @CallbackExecutor Executor executor, PnoScanListener listener) {
1675         Objects.requireNonNull(listener, "listener cannot be null");
1676         Objects.requireNonNull(pnoSettings, "pnoSettings cannot be null");
1677         pnoSettings.isConnected = false;
1678         startPnoScan(listener, executor, scanSettings, pnoSettings);
1679     }
1680     /**
1681      * Stop an ongoing wifi PNO scan
1682      * @param listener specifies which scan to cancel; must be same object as passed in {@link
1683      *  #startPnoScan}
1684      * {@hide}
1685      */
1686     @RequiresPermission(android.Manifest.permission.NETWORK_STACK)
stopPnoScan(ScanListener listener)1687     public void stopPnoScan(ScanListener listener) {
1688         Objects.requireNonNull(listener, "listener cannot be null");
1689         ServiceListener serviceListener = getServiceListener(listener);
1690         if (serviceListener == null) {
1691             Log.e(TAG, "listener does not exist");
1692             return;
1693         }
1694         try {
1695             mService.stopPnoScan(serviceListener, mContext.getOpPackageName(),
1696                     mContext.getAttributionTag());
1697         } catch (RemoteException e) {
1698             throw e.rethrowFromSystemServer();
1699         } finally {
1700             removeListener(listener);
1701         }
1702     }
1703 
1704     /**
1705      * Enable verbose logging. For internal use by wifi framework only.
1706      * @param enabled whether verbose logging is enabled
1707      * @hide
1708      */
1709     @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
enableVerboseLogging(boolean enabled)1710     public void enableVerboseLogging(boolean enabled) {
1711         try {
1712             mService.enableVerboseLogging(enabled);
1713         } catch (RemoteException e) {
1714             throw e.rethrowFromSystemServer();
1715         }
1716     }
1717 
1718     /** specifies information about an access point of interest */
1719     @Deprecated
1720     public static class BssidInfo {
1721         /** bssid of the access point; in XX:XX:XX:XX:XX:XX format */
1722         public String bssid;
1723         /** low signal strength threshold; more information at {@link ScanResult#level} */
1724         public int low;                                            /* minimum RSSI */
1725         /** high signal threshold; more information at {@link ScanResult#level} */
1726         public int high;                                           /* maximum RSSI */
1727         /** channel frequency (in KHz) where you may find this BSSID */
1728         public int frequencyHint;
1729     }
1730 
1731     /** @hide */
1732     @SystemApi
1733     @Deprecated
1734     public static class WifiChangeSettings implements Parcelable {
1735         public int rssiSampleSize;                          /* sample size for RSSI averaging */
1736         public int lostApSampleSize;                        /* samples to confirm AP's loss */
1737         public int unchangedSampleSize;                     /* samples to confirm no change */
1738         public int minApsBreachingThreshold;                /* change threshold to trigger event */
1739         public int periodInMs;                              /* scan period in millisecond */
1740         public BssidInfo[] bssidInfos;
1741 
1742         /** Implement the Parcelable interface {@hide} */
describeContents()1743         public int describeContents() {
1744             return 0;
1745         }
1746 
1747         /** Implement the Parcelable interface {@hide} */
writeToParcel(Parcel dest, int flags)1748         public void writeToParcel(Parcel dest, int flags) {
1749         }
1750 
1751         /** Implement the Parcelable interface {@hide} */
1752         public static final @NonNull Creator<WifiChangeSettings> CREATOR =
1753                 new Creator<WifiChangeSettings>() {
1754                     public WifiChangeSettings createFromParcel(Parcel in) {
1755                         return new WifiChangeSettings();
1756                     }
1757 
1758                     public WifiChangeSettings[] newArray(int size) {
1759                         return new WifiChangeSettings[size];
1760                     }
1761                 };
1762 
1763     }
1764 
1765     /** configure WifiChange detection
1766      * @param rssiSampleSize number of samples used for RSSI averaging
1767      * @param lostApSampleSize number of samples to confirm an access point's loss
1768      * @param unchangedSampleSize number of samples to confirm there are no changes
1769      * @param minApsBreachingThreshold minimum number of access points that need to be
1770      *                                 out of range to detect WifiChange
1771      * @param periodInMs indicates period of scan to find changes
1772      * @param bssidInfos access points to watch
1773      */
1774     @Deprecated
1775     @SuppressLint("RequiresPermission")
configureWifiChange( int rssiSampleSize, int lostApSampleSize, int unchangedSampleSize, int minApsBreachingThreshold, int periodInMs, BssidInfo[] bssidInfos )1776     public void configureWifiChange(
1777             int rssiSampleSize,                             /* sample size for RSSI averaging */
1778             int lostApSampleSize,                           /* samples to confirm AP's loss */
1779             int unchangedSampleSize,                        /* samples to confirm no change */
1780             int minApsBreachingThreshold,                   /* change threshold to trigger event */
1781             int periodInMs,                                 /* period of scan */
1782             BssidInfo[] bssidInfos                          /* signal thresholds to cross */
1783             )
1784     {
1785         throw new UnsupportedOperationException();
1786     }
1787 
1788     /**
1789      * interface to get wifi change events on; use this on {@link #startTrackingWifiChange}
1790      */
1791     @Deprecated
1792     public interface WifiChangeListener extends ActionListener {
1793         /** indicates that changes were detected in wifi environment
1794          * @param results indicate the access points that exhibited change
1795          */
onChanging(ScanResult[] results)1796         public void onChanging(ScanResult[] results);           /* changes are found */
1797         /** indicates that no wifi changes are being detected for a while
1798          * @param results indicate the access points that are bing monitored for change
1799          */
onQuiescence(ScanResult[] results)1800         public void onQuiescence(ScanResult[] results);         /* changes settled down */
1801     }
1802 
1803     /**
1804      * track changes in wifi environment
1805      * @param listener object to report events on; this object must be unique and must also be
1806      *                 provided on {@link #stopTrackingWifiChange}
1807      */
1808     @Deprecated
1809     @SuppressLint("RequiresPermission")
startTrackingWifiChange(WifiChangeListener listener)1810     public void startTrackingWifiChange(WifiChangeListener listener) {
1811         throw new UnsupportedOperationException();
1812     }
1813 
1814     /**
1815      * stop tracking changes in wifi environment
1816      * @param listener object that was provided to report events on {@link
1817      * #stopTrackingWifiChange}
1818      */
1819     @Deprecated
1820     @SuppressLint("RequiresPermission")
stopTrackingWifiChange(WifiChangeListener listener)1821     public void stopTrackingWifiChange(WifiChangeListener listener) {
1822         throw new UnsupportedOperationException();
1823     }
1824 
1825     /** @hide */
1826     @SystemApi
1827     @Deprecated
1828     @SuppressLint("RequiresPermission")
configureWifiChange(WifiChangeSettings settings)1829     public void configureWifiChange(WifiChangeSettings settings) {
1830         throw new UnsupportedOperationException();
1831     }
1832 
1833     /** interface to receive hotlist events on; use this on {@link #setHotlist} */
1834     @Deprecated
1835     public static interface BssidListener extends ActionListener {
1836         /** indicates that access points were found by on going scans
1837          * @param results list of scan results, one for each access point visible currently
1838          */
onFound(ScanResult[] results)1839         public void onFound(ScanResult[] results);
1840         /** indicates that access points were missed by on going scans
1841          * @param results list of scan results, for each access point that is not visible anymore
1842          */
onLost(ScanResult[] results)1843         public void onLost(ScanResult[] results);
1844     }
1845 
1846     /** @hide */
1847     @SystemApi
1848     @Deprecated
1849     public static class HotlistSettings implements Parcelable {
1850         public BssidInfo[] bssidInfos;
1851         public int apLostThreshold;
1852 
1853         /** Implement the Parcelable interface {@hide} */
describeContents()1854         public int describeContents() {
1855             return 0;
1856         }
1857 
1858         /** Implement the Parcelable interface {@hide} */
writeToParcel(Parcel dest, int flags)1859         public void writeToParcel(Parcel dest, int flags) {
1860         }
1861 
1862         /** Implement the Parcelable interface {@hide} */
1863         public static final @NonNull Creator<HotlistSettings> CREATOR =
1864                 new Creator<HotlistSettings>() {
1865                     public HotlistSettings createFromParcel(Parcel in) {
1866                         HotlistSettings settings = new HotlistSettings();
1867                         return settings;
1868                     }
1869 
1870                     public HotlistSettings[] newArray(int size) {
1871                         return new HotlistSettings[size];
1872                     }
1873                 };
1874     }
1875 
1876     /**
1877      * set interesting access points to find
1878      * @param bssidInfos access points of interest
1879      * @param apLostThreshold number of scans needed to indicate that AP is lost
1880      * @param listener object provided to report events on; this object must be unique and must
1881      *                 also be provided on {@link #stopTrackingBssids}
1882      */
1883     @Deprecated
1884     @SuppressLint("RequiresPermission")
startTrackingBssids(BssidInfo[] bssidInfos, int apLostThreshold, BssidListener listener)1885     public void startTrackingBssids(BssidInfo[] bssidInfos,
1886                                     int apLostThreshold, BssidListener listener) {
1887         throw new UnsupportedOperationException();
1888     }
1889 
1890     /**
1891      * remove tracking of interesting access points
1892      * @param listener same object provided in {@link #startTrackingBssids}
1893      */
1894     @Deprecated
1895     @SuppressLint("RequiresPermission")
stopTrackingBssids(BssidListener listener)1896     public void stopTrackingBssids(BssidListener listener) {
1897         throw new UnsupportedOperationException();
1898     }
1899 
1900 
1901     /* private members and methods */
1902 
1903     private static final String TAG = "WifiScanner";
1904     private static final boolean DBG = false;
1905 
1906     /* commands for Wifi Service */
1907     private static final int BASE = Protocol.BASE_WIFI_SCANNER;
1908 
1909     /** @hide */
1910     public static final int CMD_START_BACKGROUND_SCAN       = BASE + 2;
1911     /** @hide */
1912     public static final int CMD_STOP_BACKGROUND_SCAN        = BASE + 3;
1913     /** @hide */
1914     public static final int CMD_GET_SCAN_RESULTS            = BASE + 4;
1915     /** @hide */
1916     public static final int CMD_SCAN_RESULT                 = BASE + 5;
1917     /** @hide */
1918     public static final int CMD_CACHED_SCAN_DATA            = BASE + 6;
1919     /** @hide */
1920     public static final int CMD_OP_SUCCEEDED                = BASE + 17;
1921     /** @hide */
1922     public static final int CMD_OP_FAILED                   = BASE + 18;
1923     /** @hide */
1924     public static final int CMD_FULL_SCAN_RESULT            = BASE + 20;
1925     /** @hide */
1926     public static final int CMD_START_SINGLE_SCAN           = BASE + 21;
1927     /** @hide */
1928     public static final int CMD_STOP_SINGLE_SCAN            = BASE + 22;
1929     /** @hide */
1930     public static final int CMD_SINGLE_SCAN_COMPLETED       = BASE + 23;
1931     /** @hide */
1932     public static final int CMD_START_PNO_SCAN              = BASE + 24;
1933     /** @hide */
1934     public static final int CMD_STOP_PNO_SCAN               = BASE + 25;
1935     /** @hide */
1936     public static final int CMD_PNO_NETWORK_FOUND           = BASE + 26;
1937     /** @hide */
1938     public static final int CMD_REGISTER_SCAN_LISTENER      = BASE + 27;
1939     /** @hide */
1940     public static final int CMD_DEREGISTER_SCAN_LISTENER    = BASE + 28;
1941     /** @hide */
1942     public static final int CMD_GET_SINGLE_SCAN_RESULTS     = BASE + 29;
1943     /** @hide */
1944     public static final int CMD_ENABLE                      = BASE + 30;
1945     /** @hide */
1946     public static final int CMD_DISABLE                     = BASE + 31;
1947 
1948     private Context mContext;
1949     private IWifiScanner mService;
1950 
1951     private final Object mListenerMapLock = new Object();
1952     private final Map<ActionListener, ServiceListener> mListenerMap = new HashMap<>();
1953 
1954     /**
1955      * Create a new WifiScanner instance.
1956      * Applications will almost always want to use
1957      * {@link android.content.Context#getSystemService Context.getSystemService()} to retrieve
1958      * the standard {@link android.content.Context#WIFI_SERVICE Context.WIFI_SERVICE}.
1959      *
1960      * @param context the application context
1961      * @param service the Binder interface for {@link Context#WIFI_SCANNING_SERVICE}
1962      * @param looper the Looper used to deliver callbacks
1963      *
1964      * @hide
1965      */
WifiScanner(@onNull Context context, @NonNull IWifiScanner service, @NonNull Looper looper)1966     public WifiScanner(@NonNull Context context, @NonNull IWifiScanner service,
1967             @NonNull Looper looper) {
1968         mContext = context;
1969         mService = service;
1970     }
1971 
1972     // Add a listener into listener map. If the listener already exists, return INVALID_KEY and
1973     // send an error message to internal handler; Otherwise add the listener to the listener map and
1974     // return the key of the listener.
addListener(ActionListener listener, ServiceListener serviceListener)1975     private boolean addListener(ActionListener listener, ServiceListener serviceListener) {
1976         synchronized (mListenerMapLock) {
1977             boolean keyExists = mListenerMap.containsKey(listener);
1978             // Note we need to put the listener into listener map even if it's a duplicate as the
1979             // internal handler will need the key to find the listener. In case of duplicates,
1980             // removing duplicate key logic will be handled in internal handler.
1981             if (keyExists) {
1982                 if (DBG) Log.d(TAG, "listener key already exists");
1983                 return false;
1984             }
1985             mListenerMap.put(listener, serviceListener);
1986             return true;
1987         }
1988     }
1989 
getServiceListener(ActionListener listener)1990     private ServiceListener getServiceListener(ActionListener listener) {
1991         if (listener == null) return null;
1992         synchronized (mListenerMapLock) {
1993             return mListenerMap.get(listener);
1994         }
1995     }
1996 
removeListener(ActionListener listener)1997     private void removeListener(ActionListener listener) {
1998         if (listener == null) return;
1999         synchronized (mListenerMapLock) {
2000             mListenerMap.remove(listener);
2001         }
2002     }
2003 
2004 }
2005