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