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