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