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