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