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