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