Home | History | Annotate | Download | only in wifi
      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 {
     95         public void onSuccess();
     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      */
    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          */
    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              */
    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} */
    228         public int describeContents() {
    229             return 0;
    230         }
    231 
    232         /** Implement the Parcelable interface {@hide} */
    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 
    326         ScanData() {}
    327 
    328         public ScanData(int id, int flags, ScanResult[] results) {
    329             mId = id;
    330             mFlags = flags;
    331             mResults = results;
    332         }
    333 
    334         /** {@hide} */
    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 
    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 
    357         public int getId() {
    358             return mId;
    359         }
    360 
    361         public int getFlags() {
    362             return mFlags;
    363         }
    364 
    365         /** {@hide} */
    366         public int getBucketsScanned() {
    367             return mBucketsScanned;
    368         }
    369 
    370         /** {@hide} */
    371         public boolean isAllChannelsScanned() {
    372             return mAllChannelsScanned;
    373         }
    374 
    375         public ScanResult[] getResults() {
    376             return mResults;
    377         }
    378 
    379         /** Implement the Parcelable interface {@hide} */
    380         public int describeContents() {
    381             return 0;
    382         }
    383 
    384         /** Implement the Parcelable interface {@hide} */
    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 
    427         public ParcelableScanData(ScanData[] results) {
    428             mResults = results;
    429         }
    430 
    431         public ScanData[] getResults() {
    432             return mResults;
    433         }
    434 
    435         /** Implement the Parcelable interface {@hide} */
    436         public int describeContents() {
    437             return 0;
    438         }
    439 
    440         /** Implement the Parcelable interface {@hide} */
    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 
    475         public ParcelableScanResults(ScanResult[] results) {
    476             mResults = results;
    477         }
    478 
    479         public ScanResult[] getResults() {
    480             return mResults;
    481         }
    482 
    483         /** Implement the Parcelable interface {@hide} */
    484         public int describeContents() {
    485             return 0;
    486         }
    487 
    488         /** Implement the Parcelable interface {@hide} */
    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              */
    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} */
    605         public int describeContents() {
    606             return 0;
    607         }
    608 
    609         /** Implement the Parcelable interface {@hide} */
    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          */
    672         public void onPeriodChanged(int periodInMs);
    673         /**
    674          * reports results retrieved from background scan and single shot scans
    675          */
    676         public void onResults(ScanData[] results);
    677         /**
    678          * reports full scan result for each access point found in scan
    679          */
    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          */
    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      */
    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      */
    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)
    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)
    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)
    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)
    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)
    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)
    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)
    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      */
    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 
    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      */
    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      */
    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      */
    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} */
    941         public int describeContents() {
    942             return 0;
    943         }
    944 
    945         /** Implement the Parcelable interface {@hide} */
    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")
    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          */
    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          */
    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")
   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")
   1019     public void stopTrackingWifiChange(WifiChangeListener listener) {
   1020         throw new UnsupportedOperationException();
   1021     }
   1022 
   1023     /** @hide */
   1024     @SystemApi
   1025     @Deprecated
   1026     @SuppressLint("Doclava125")
   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          */
   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          */
   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} */
   1052         public int describeContents() {
   1053             return 0;
   1054         }
   1055 
   1056         /** Implement the Parcelable interface {@hide} */
   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")
   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")
   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      */
   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 
   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.
   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 
   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 
   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 
   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 
   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 
   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 
   1285         public OperationResult(int reason, String description) {
   1286             this.reason = reason;
   1287             this.description = description;
   1288         }
   1289 
   1290         /** Implement the Parcelable interface {@hide} */
   1291         public int describeContents() {
   1292             return 0;
   1293         }
   1294 
   1295         /** Implement the Parcelable interface {@hide} */
   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 {
   1317         ServiceHandler(Looper looper) {
   1318             super(looper);
   1319         }
   1320         @Override
   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