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