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