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 { 93 public void onSuccess(); 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 */ 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 */ 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} */ 212 public int describeContents() { 213 return 0; 214 } 215 216 /** Implement the Parcelable interface {@hide} */ 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 292 ScanData() {} 293 294 public ScanData(int id, int flags, ScanResult[] results) { 295 mId = id; 296 mFlags = flags; 297 mResults = results; 298 } 299 300 /** {@hide} */ 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 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 320 public int getId() { 321 return mId; 322 } 323 324 public int getFlags() { 325 return mFlags; 326 } 327 328 /** {@hide} */ 329 public int getBucketsScanned() { 330 return mBucketsScanned; 331 } 332 333 public ScanResult[] getResults() { 334 return mResults; 335 } 336 337 /** Implement the Parcelable interface {@hide} */ 338 public int describeContents() { 339 return 0; 340 } 341 342 /** Implement the Parcelable interface {@hide} */ 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 383 public ParcelableScanData(ScanData[] results) { 384 mResults = results; 385 } 386 387 public ScanData[] getResults() { 388 return mResults; 389 } 390 391 /** Implement the Parcelable interface {@hide} */ 392 public int describeContents() { 393 return 0; 394 } 395 396 /** Implement the Parcelable interface {@hide} */ 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 431 public ParcelableScanResults(ScanResult[] results) { 432 mResults = results; 433 } 434 435 public ScanResult[] getResults() { 436 return mResults; 437 } 438 439 /** Implement the Parcelable interface {@hide} */ 440 public int describeContents() { 441 return 0; 442 } 443 444 /** Implement the Parcelable interface {@hide} */ 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 */ 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} */ 565 public int describeContents() { 566 return 0; 567 } 568 569 /** Implement the Parcelable interface {@hide} */ 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 */ 636 public void onPeriodChanged(int periodInMs); 637 /** 638 * reports results retrieved from background scan and single shot scans 639 */ 640 public void onResults(ScanData[] results); 641 /** 642 * reports full scan result for each access point found in scan 643 */ 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 */ 656 void onPnoNetworkFound(ScanResult[] results); 657 } 658 659 /** 660 * Register a listener that will receive results from all single scans 661 * Either the onSuccess/onFailure will be called once when the listener is registered. After 662 * (assuming onSuccess was called) all subsequent single scan results will be delivered to the 663 * listener. It is possible that onFullResult will not be called for all results of the first 664 * scan if the listener was registered during the scan. 665 * 666 * @param listener specifies the object to report events to. This object is also treated as a 667 * key for this request, and must also be specified to cancel the request. 668 * Multiple requests should also not share this object. 669 * {@hide} 670 */ 671 public void registerScanListener(ScanListener listener) { 672 Preconditions.checkNotNull(listener, "listener cannot be null"); 673 int key = addListener(listener); 674 if (key == INVALID_KEY) return; 675 validateChannel(); 676 mAsyncChannel.sendMessage(CMD_REGISTER_SCAN_LISTENER, 0, key); 677 } 678 679 /** 680 * Deregister a listener for ongoing single scans 681 * @param listener specifies which scan to cancel; must be same object as passed in {@link 682 * #registerScanListener} 683 * {@hide} 684 */ 685 public void deregisterScanListener(ScanListener listener) { 686 Preconditions.checkNotNull(listener, "listener cannot be null"); 687 int key = removeListener(listener); 688 if (key == INVALID_KEY) return; 689 validateChannel(); 690 mAsyncChannel.sendMessage(CMD_DEREGISTER_SCAN_LISTENER, 0, key); 691 } 692 693 /** start wifi scan in background 694 * @param settings specifies various parameters for the scan; for more information look at 695 * {@link ScanSettings} 696 * @param listener specifies the object to report events to. This object is also treated as a 697 * key for this scan, and must also be specified to cancel the scan. Multiple 698 * scans should also not share this object. 699 */ 700 public void startBackgroundScan(ScanSettings settings, ScanListener listener) { 701 startBackgroundScan(settings, listener, null); 702 } 703 704 /** start wifi scan in background 705 * @param settings specifies various parameters for the scan; for more information look at 706 * {@link ScanSettings} 707 * @param workSource WorkSource to blame for power usage 708 * @param listener specifies the object to report events to. This object is also treated as a 709 * key for this scan, and must also be specified to cancel the scan. Multiple 710 * scans should also not share this object. 711 */ 712 public void startBackgroundScan(ScanSettings settings, ScanListener listener, 713 WorkSource workSource) { 714 Preconditions.checkNotNull(listener, "listener cannot be null"); 715 int key = addListener(listener); 716 if (key == INVALID_KEY) return; 717 validateChannel(); 718 Bundle scanParams = new Bundle(); 719 scanParams.putParcelable(SCAN_PARAMS_SCAN_SETTINGS_KEY, settings); 720 scanParams.putParcelable(SCAN_PARAMS_WORK_SOURCE_KEY, workSource); 721 mAsyncChannel.sendMessage(CMD_START_BACKGROUND_SCAN, 0, key, scanParams); 722 } 723 724 /** 725 * stop an ongoing wifi scan 726 * @param listener specifies which scan to cancel; must be same object as passed in {@link 727 * #startBackgroundScan} 728 */ 729 public void stopBackgroundScan(ScanListener listener) { 730 Preconditions.checkNotNull(listener, "listener cannot be null"); 731 int key = removeListener(listener); 732 if (key == INVALID_KEY) return; 733 validateChannel(); 734 mAsyncChannel.sendMessage(CMD_STOP_BACKGROUND_SCAN, 0, key); 735 } 736 /** 737 * reports currently available scan results on appropriate listeners 738 * @return true if all scan results were reported correctly 739 */ 740 public boolean getScanResults() { 741 validateChannel(); 742 Message reply = mAsyncChannel.sendMessageSynchronously(CMD_GET_SCAN_RESULTS, 0); 743 return reply.what == CMD_OP_SUCCEEDED; 744 } 745 746 /** 747 * starts a single scan and reports results asynchronously 748 * @param settings specifies various parameters for the scan; for more information look at 749 * {@link ScanSettings} 750 * @param listener specifies the object to report events to. This object is also treated as a 751 * key for this scan, and must also be specified to cancel the scan. Multiple 752 * scans should also not share this object. 753 */ 754 public void startScan(ScanSettings settings, ScanListener listener) { 755 startScan(settings, listener, null); 756 } 757 758 /** 759 * starts a single scan and reports results asynchronously 760 * @param settings specifies various parameters for the scan; for more information look at 761 * {@link ScanSettings} 762 * @param workSource WorkSource to blame for power usage 763 * @param listener specifies the object to report events to. This object is also treated as a 764 * key for this scan, and must also be specified to cancel the scan. Multiple 765 * scans should also not share this object. 766 */ 767 public void startScan(ScanSettings settings, ScanListener listener, WorkSource workSource) { 768 Preconditions.checkNotNull(listener, "listener cannot be null"); 769 int key = addListener(listener); 770 if (key == INVALID_KEY) return; 771 validateChannel(); 772 Bundle scanParams = new Bundle(); 773 scanParams.putParcelable(SCAN_PARAMS_SCAN_SETTINGS_KEY, settings); 774 scanParams.putParcelable(SCAN_PARAMS_WORK_SOURCE_KEY, workSource); 775 mAsyncChannel.sendMessage(CMD_START_SINGLE_SCAN, 0, key, scanParams); 776 } 777 778 /** 779 * stops an ongoing single shot scan; only useful after {@link #startScan} if onResults() 780 * hasn't been called on the listener, ignored otherwise 781 * @param listener 782 */ 783 public void stopScan(ScanListener listener) { 784 Preconditions.checkNotNull(listener, "listener cannot be null"); 785 int key = removeListener(listener); 786 if (key == INVALID_KEY) return; 787 validateChannel(); 788 mAsyncChannel.sendMessage(CMD_STOP_SINGLE_SCAN, 0, key); 789 } 790 791 private void startPnoScan(ScanSettings scanSettings, PnoSettings pnoSettings, int key) { 792 // Bundle up both the settings and send it across. 793 Bundle pnoParams = new Bundle(); 794 // Set the PNO scan flag. 795 scanSettings.isPnoScan = true; 796 pnoParams.putParcelable(PNO_PARAMS_SCAN_SETTINGS_KEY, scanSettings); 797 pnoParams.putParcelable(PNO_PARAMS_PNO_SETTINGS_KEY, pnoSettings); 798 mAsyncChannel.sendMessage(CMD_START_PNO_SCAN, 0, key, pnoParams); 799 } 800 /** 801 * Start wifi connected PNO scan 802 * @param scanSettings specifies various parameters for the scan; for more information look at 803 * {@link ScanSettings} 804 * @param pnoSettings specifies various parameters for PNO; for more information look at 805 * {@link PnoSettings} 806 * @param listener specifies the object to report events to. This object is also treated as a 807 * key for this scan, and must also be specified to cancel the scan. Multiple 808 * scans should also not share this object. 809 * {@hide} 810 */ 811 public void startConnectedPnoScan(ScanSettings scanSettings, PnoSettings pnoSettings, 812 PnoScanListener listener) { 813 Preconditions.checkNotNull(listener, "listener cannot be null"); 814 Preconditions.checkNotNull(pnoSettings, "pnoSettings cannot be null"); 815 int key = addListener(listener); 816 if (key == INVALID_KEY) return; 817 validateChannel(); 818 pnoSettings.isConnected = true; 819 startPnoScan(scanSettings, pnoSettings, key); 820 } 821 /** 822 * Start wifi disconnected PNO scan 823 * @param scanSettings specifies various parameters for the scan; for more information look at 824 * {@link ScanSettings} 825 * @param pnoSettings specifies various parameters for PNO; for more information look at 826 * {@link PnoSettings} 827 * @param listener specifies the object to report events to. This object is also treated as a 828 * key for this scan, and must also be specified to cancel the scan. Multiple 829 * scans should also not share this object. 830 * {@hide} 831 */ 832 public void startDisconnectedPnoScan(ScanSettings scanSettings, PnoSettings pnoSettings, 833 PnoScanListener listener) { 834 Preconditions.checkNotNull(listener, "listener cannot be null"); 835 Preconditions.checkNotNull(pnoSettings, "pnoSettings cannot be null"); 836 int key = addListener(listener); 837 if (key == INVALID_KEY) return; 838 validateChannel(); 839 pnoSettings.isConnected = false; 840 startPnoScan(scanSettings, pnoSettings, key); 841 } 842 /** 843 * Stop an ongoing wifi PNO scan 844 * @param listener specifies which scan to cancel; must be same object as passed in {@link 845 * #startPnoScan} 846 * TODO(rpius): Check if we can remove pnoSettings param in stop. 847 * {@hide} 848 */ 849 public void stopPnoScan(ScanListener listener) { 850 Preconditions.checkNotNull(listener, "listener cannot be null"); 851 int key = removeListener(listener); 852 if (key == INVALID_KEY) return; 853 validateChannel(); 854 mAsyncChannel.sendMessage(CMD_STOP_PNO_SCAN, 0, key); 855 } 856 857 /** specifies information about an access point of interest */ 858 public static class BssidInfo { 859 /** bssid of the access point; in XX:XX:XX:XX:XX:XX format */ 860 public String bssid; 861 /** low signal strength threshold; more information at {@link ScanResult#level} */ 862 public int low; /* minimum RSSI */ 863 /** high signal threshold; more information at {@link ScanResult#level} */ 864 public int high; /* maximum RSSI */ 865 /** channel frequency (in KHz) where you may find this BSSID */ 866 public int frequencyHint; 867 } 868 869 /** @hide */ 870 @SystemApi 871 public static class WifiChangeSettings implements Parcelable { 872 public int rssiSampleSize; /* sample size for RSSI averaging */ 873 public int lostApSampleSize; /* samples to confirm AP's loss */ 874 public int unchangedSampleSize; /* samples to confirm no change */ 875 public int minApsBreachingThreshold; /* change threshold to trigger event */ 876 public int periodInMs; /* scan period in millisecond */ 877 public BssidInfo[] bssidInfos; 878 879 /** Implement the Parcelable interface {@hide} */ 880 public int describeContents() { 881 return 0; 882 } 883 884 /** Implement the Parcelable interface {@hide} */ 885 public void writeToParcel(Parcel dest, int flags) { 886 dest.writeInt(rssiSampleSize); 887 dest.writeInt(lostApSampleSize); 888 dest.writeInt(unchangedSampleSize); 889 dest.writeInt(minApsBreachingThreshold); 890 dest.writeInt(periodInMs); 891 if (bssidInfos != null) { 892 dest.writeInt(bssidInfos.length); 893 for (int i = 0; i < bssidInfos.length; i++) { 894 BssidInfo info = bssidInfos[i]; 895 dest.writeString(info.bssid); 896 dest.writeInt(info.low); 897 dest.writeInt(info.high); 898 dest.writeInt(info.frequencyHint); 899 } 900 } else { 901 dest.writeInt(0); 902 } 903 } 904 905 /** Implement the Parcelable interface {@hide} */ 906 public static final Creator<WifiChangeSettings> CREATOR = 907 new Creator<WifiChangeSettings>() { 908 public WifiChangeSettings createFromParcel(Parcel in) { 909 WifiChangeSettings settings = new WifiChangeSettings(); 910 settings.rssiSampleSize = in.readInt(); 911 settings.lostApSampleSize = in.readInt(); 912 settings.unchangedSampleSize = in.readInt(); 913 settings.minApsBreachingThreshold = in.readInt(); 914 settings.periodInMs = in.readInt(); 915 int len = in.readInt(); 916 settings.bssidInfos = new BssidInfo[len]; 917 for (int i = 0; i < len; i++) { 918 BssidInfo info = new BssidInfo(); 919 info.bssid = in.readString(); 920 info.low = in.readInt(); 921 info.high = in.readInt(); 922 info.frequencyHint = in.readInt(); 923 settings.bssidInfos[i] = info; 924 } 925 return settings; 926 } 927 928 public WifiChangeSettings[] newArray(int size) { 929 return new WifiChangeSettings[size]; 930 } 931 }; 932 933 } 934 935 /** configure WifiChange detection 936 * @param rssiSampleSize number of samples used for RSSI averaging 937 * @param lostApSampleSize number of samples to confirm an access point's loss 938 * @param unchangedSampleSize number of samples to confirm there are no changes 939 * @param minApsBreachingThreshold minimum number of access points that need to be 940 * out of range to detect WifiChange 941 * @param periodInMs indicates period of scan to find changes 942 * @param bssidInfos access points to watch 943 */ 944 public void configureWifiChange( 945 int rssiSampleSize, /* sample size for RSSI averaging */ 946 int lostApSampleSize, /* samples to confirm AP's loss */ 947 int unchangedSampleSize, /* samples to confirm no change */ 948 int minApsBreachingThreshold, /* change threshold to trigger event */ 949 int periodInMs, /* period of scan */ 950 BssidInfo[] bssidInfos /* signal thresholds to crosss */ 951 ) 952 { 953 validateChannel(); 954 955 WifiChangeSettings settings = new WifiChangeSettings(); 956 settings.rssiSampleSize = rssiSampleSize; 957 settings.lostApSampleSize = lostApSampleSize; 958 settings.unchangedSampleSize = unchangedSampleSize; 959 settings.minApsBreachingThreshold = minApsBreachingThreshold; 960 settings.periodInMs = periodInMs; 961 settings.bssidInfos = bssidInfos; 962 963 configureWifiChange(settings); 964 } 965 966 /** 967 * interface to get wifi change events on; use this on {@link #startTrackingWifiChange} 968 */ 969 public interface WifiChangeListener extends ActionListener { 970 /** indicates that changes were detected in wifi environment 971 * @param results indicate the access points that exhibited change 972 */ 973 public void onChanging(ScanResult[] results); /* changes are found */ 974 /** indicates that no wifi changes are being detected for a while 975 * @param results indicate the access points that are bing monitored for change 976 */ 977 public void onQuiescence(ScanResult[] results); /* changes settled down */ 978 } 979 980 /** 981 * track changes in wifi environment 982 * @param listener object to report events on; this object must be unique and must also be 983 * provided on {@link #stopTrackingWifiChange} 984 */ 985 public void startTrackingWifiChange(WifiChangeListener listener) { 986 Preconditions.checkNotNull(listener, "listener cannot be null"); 987 int key = addListener(listener); 988 if (key == INVALID_KEY) return; 989 validateChannel(); 990 mAsyncChannel.sendMessage(CMD_START_TRACKING_CHANGE, 0, key); 991 } 992 993 /** 994 * stop tracking changes in wifi environment 995 * @param listener object that was provided to report events on {@link 996 * #stopTrackingWifiChange} 997 */ 998 public void stopTrackingWifiChange(WifiChangeListener listener) { 999 int key = removeListener(listener); 1000 if (key == INVALID_KEY) return; 1001 validateChannel(); 1002 mAsyncChannel.sendMessage(CMD_STOP_TRACKING_CHANGE, 0, key); 1003 } 1004 1005 /** @hide */ 1006 @SystemApi 1007 public void configureWifiChange(WifiChangeSettings settings) { 1008 validateChannel(); 1009 mAsyncChannel.sendMessage(CMD_CONFIGURE_WIFI_CHANGE, 0, 0, settings); 1010 } 1011 1012 /** interface to receive hotlist events on; use this on {@link #setHotlist} */ 1013 public static interface BssidListener extends ActionListener { 1014 /** indicates that access points were found by on going scans 1015 * @param results list of scan results, one for each access point visible currently 1016 */ 1017 public void onFound(ScanResult[] results); 1018 /** indicates that access points were missed by on going scans 1019 * @param results list of scan results, for each access point that is not visible anymore 1020 */ 1021 public void onLost(ScanResult[] results); 1022 } 1023 1024 /** @hide */ 1025 @SystemApi 1026 public static class HotlistSettings implements Parcelable { 1027 public BssidInfo[] bssidInfos; 1028 public int apLostThreshold; 1029 1030 /** Implement the Parcelable interface {@hide} */ 1031 public int describeContents() { 1032 return 0; 1033 } 1034 1035 /** Implement the Parcelable interface {@hide} */ 1036 public void writeToParcel(Parcel dest, int flags) { 1037 dest.writeInt(apLostThreshold); 1038 1039 if (bssidInfos != null) { 1040 dest.writeInt(bssidInfos.length); 1041 for (int i = 0; i < bssidInfos.length; i++) { 1042 BssidInfo info = bssidInfos[i]; 1043 dest.writeString(info.bssid); 1044 dest.writeInt(info.low); 1045 dest.writeInt(info.high); 1046 dest.writeInt(info.frequencyHint); 1047 } 1048 } else { 1049 dest.writeInt(0); 1050 } 1051 } 1052 1053 /** Implement the Parcelable interface {@hide} */ 1054 public static final Creator<HotlistSettings> CREATOR = 1055 new Creator<HotlistSettings>() { 1056 public HotlistSettings createFromParcel(Parcel in) { 1057 HotlistSettings settings = new HotlistSettings(); 1058 settings.apLostThreshold = in.readInt(); 1059 int n = in.readInt(); 1060 settings.bssidInfos = new BssidInfo[n]; 1061 for (int i = 0; i < n; i++) { 1062 BssidInfo info = new BssidInfo(); 1063 info.bssid = in.readString(); 1064 info.low = in.readInt(); 1065 info.high = in.readInt(); 1066 info.frequencyHint = in.readInt(); 1067 settings.bssidInfos[i] = info; 1068 } 1069 return settings; 1070 } 1071 1072 public HotlistSettings[] newArray(int size) { 1073 return new HotlistSettings[size]; 1074 } 1075 }; 1076 } 1077 1078 /** 1079 * set interesting access points to find 1080 * @param bssidInfos access points of interest 1081 * @param apLostThreshold number of scans needed to indicate that AP is lost 1082 * @param listener object provided to report events on; this object must be unique and must 1083 * also be provided on {@link #stopTrackingBssids} 1084 */ 1085 public void startTrackingBssids(BssidInfo[] bssidInfos, 1086 int apLostThreshold, BssidListener listener) { 1087 Preconditions.checkNotNull(listener, "listener cannot be null"); 1088 int key = addListener(listener); 1089 if (key == INVALID_KEY) return; 1090 validateChannel(); 1091 HotlistSettings settings = new HotlistSettings(); 1092 settings.bssidInfos = bssidInfos; 1093 settings.apLostThreshold = apLostThreshold; 1094 mAsyncChannel.sendMessage(CMD_SET_HOTLIST, 0, key, settings); 1095 } 1096 1097 /** 1098 * remove tracking of interesting access points 1099 * @param listener same object provided in {@link #startTrackingBssids} 1100 */ 1101 public void stopTrackingBssids(BssidListener listener) { 1102 Preconditions.checkNotNull(listener, "listener cannot be null"); 1103 int key = removeListener(listener); 1104 if (key == INVALID_KEY) return; 1105 validateChannel(); 1106 mAsyncChannel.sendMessage(CMD_RESET_HOTLIST, 0, key); 1107 } 1108 1109 1110 /* private members and methods */ 1111 1112 private static final String TAG = "WifiScanner"; 1113 private static final boolean DBG = false; 1114 1115 /* commands for Wifi Service */ 1116 private static final int BASE = Protocol.BASE_WIFI_SCANNER; 1117 1118 /** @hide */ 1119 public static final int CMD_SCAN = BASE + 0; 1120 /** @hide */ 1121 public static final int CMD_START_BACKGROUND_SCAN = BASE + 2; 1122 /** @hide */ 1123 public static final int CMD_STOP_BACKGROUND_SCAN = BASE + 3; 1124 /** @hide */ 1125 public static final int CMD_GET_SCAN_RESULTS = BASE + 4; 1126 /** @hide */ 1127 public static final int CMD_SCAN_RESULT = BASE + 5; 1128 /** @hide */ 1129 public static final int CMD_SET_HOTLIST = BASE + 6; 1130 /** @hide */ 1131 public static final int CMD_RESET_HOTLIST = BASE + 7; 1132 /** @hide */ 1133 public static final int CMD_AP_FOUND = BASE + 9; 1134 /** @hide */ 1135 public static final int CMD_AP_LOST = BASE + 10; 1136 /** @hide */ 1137 public static final int CMD_START_TRACKING_CHANGE = BASE + 11; 1138 /** @hide */ 1139 public static final int CMD_STOP_TRACKING_CHANGE = BASE + 12; 1140 /** @hide */ 1141 public static final int CMD_CONFIGURE_WIFI_CHANGE = BASE + 13; 1142 /** @hide */ 1143 public static final int CMD_WIFI_CHANGE_DETECTED = BASE + 15; 1144 /** @hide */ 1145 public static final int CMD_WIFI_CHANGES_STABILIZED = BASE + 16; 1146 /** @hide */ 1147 public static final int CMD_OP_SUCCEEDED = BASE + 17; 1148 /** @hide */ 1149 public static final int CMD_OP_FAILED = BASE + 18; 1150 /** @hide */ 1151 public static final int CMD_PERIOD_CHANGED = BASE + 19; 1152 /** @hide */ 1153 public static final int CMD_FULL_SCAN_RESULT = BASE + 20; 1154 /** @hide */ 1155 public static final int CMD_START_SINGLE_SCAN = BASE + 21; 1156 /** @hide */ 1157 public static final int CMD_STOP_SINGLE_SCAN = BASE + 22; 1158 /** @hide */ 1159 public static final int CMD_SINGLE_SCAN_COMPLETED = BASE + 23; 1160 /** @hide */ 1161 public static final int CMD_START_PNO_SCAN = BASE + 24; 1162 /** @hide */ 1163 public static final int CMD_STOP_PNO_SCAN = BASE + 25; 1164 /** @hide */ 1165 public static final int CMD_PNO_NETWORK_FOUND = BASE + 26; 1166 /** @hide */ 1167 public static final int CMD_REGISTER_SCAN_LISTENER = BASE + 27; 1168 /** @hide */ 1169 public static final int CMD_DEREGISTER_SCAN_LISTENER = BASE + 28; 1170 1171 private Context mContext; 1172 private IWifiScanner mService; 1173 1174 private static final int INVALID_KEY = 0; 1175 private int mListenerKey = 1; 1176 1177 private final SparseArray mListenerMap = new SparseArray(); 1178 private final Object mListenerMapLock = new Object(); 1179 1180 private AsyncChannel mAsyncChannel; 1181 private final Handler mInternalHandler; 1182 1183 /** 1184 * Create a new WifiScanner instance. 1185 * Applications will almost always want to use 1186 * {@link android.content.Context#getSystemService Context.getSystemService()} to retrieve 1187 * the standard {@link android.content.Context#WIFI_SERVICE Context.WIFI_SERVICE}. 1188 * @param context the application context 1189 * @param service the Binder interface 1190 * @param looper the Looper used to deliver callbacks 1191 * @hide 1192 */ 1193 public WifiScanner(Context context, IWifiScanner service, Looper looper) { 1194 mContext = context; 1195 mService = service; 1196 1197 Messenger messenger = null; 1198 try { 1199 messenger = mService.getMessenger(); 1200 } catch (RemoteException e) { 1201 throw e.rethrowFromSystemServer(); 1202 } 1203 1204 if (messenger == null) { 1205 throw new IllegalStateException("getMessenger() returned null! This is invalid."); 1206 } 1207 1208 mAsyncChannel = new AsyncChannel(); 1209 1210 mInternalHandler = new ServiceHandler(looper); 1211 mAsyncChannel.connectSync(mContext, mInternalHandler, messenger); 1212 // We cannot use fullyConnectSync because it sends the FULL_CONNECTION message 1213 // synchronously, which causes WifiScanningService to receive the wrong replyTo value. 1214 mAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION); 1215 } 1216 1217 private void validateChannel() { 1218 if (mAsyncChannel == null) throw new IllegalStateException( 1219 "No permission to access and change wifi or a bad initialization"); 1220 } 1221 1222 // Add a listener into listener map. If the listener already exists, return INVALID_KEY and 1223 // send an error message to internal handler; Otherwise add the listener to the listener map and 1224 // return the key of the listener. 1225 private int addListener(ActionListener listener) { 1226 synchronized (mListenerMapLock) { 1227 boolean keyExists = (getListenerKey(listener) != INVALID_KEY); 1228 // Note we need to put the listener into listener map even if it's a duplicate as the 1229 // internal handler will need the key to find the listener. In case of duplicates, 1230 // removing duplicate key logic will be handled in internal handler. 1231 int key = putListener(listener); 1232 if (keyExists) { 1233 if (DBG) Log.d(TAG, "listener key already exists"); 1234 OperationResult operationResult = new OperationResult(REASON_DUPLICATE_REQEUST, 1235 "Outstanding request with same key not stopped yet"); 1236 Message message = Message.obtain(mInternalHandler, CMD_OP_FAILED, 0, key, 1237 operationResult); 1238 message.sendToTarget(); 1239 return INVALID_KEY; 1240 } else { 1241 return key; 1242 } 1243 } 1244 } 1245 1246 private int putListener(Object listener) { 1247 if (listener == null) return INVALID_KEY; 1248 int key; 1249 synchronized (mListenerMapLock) { 1250 do { 1251 key = mListenerKey++; 1252 } while (key == INVALID_KEY); 1253 mListenerMap.put(key, listener); 1254 } 1255 return key; 1256 } 1257 1258 private Object getListener(int key) { 1259 if (key == INVALID_KEY) return null; 1260 synchronized (mListenerMapLock) { 1261 Object listener = mListenerMap.get(key); 1262 return listener; 1263 } 1264 } 1265 1266 private int getListenerKey(Object listener) { 1267 if (listener == null) return INVALID_KEY; 1268 synchronized (mListenerMapLock) { 1269 int index = mListenerMap.indexOfValue(listener); 1270 if (index == -1) { 1271 return INVALID_KEY; 1272 } else { 1273 return mListenerMap.keyAt(index); 1274 } 1275 } 1276 } 1277 1278 private Object removeListener(int key) { 1279 if (key == INVALID_KEY) return null; 1280 synchronized (mListenerMapLock) { 1281 Object listener = mListenerMap.get(key); 1282 mListenerMap.remove(key); 1283 return listener; 1284 } 1285 } 1286 1287 private int removeListener(Object listener) { 1288 int key = getListenerKey(listener); 1289 if (key == INVALID_KEY) { 1290 Log.e(TAG, "listener cannot be found"); 1291 return key; 1292 } 1293 synchronized (mListenerMapLock) { 1294 mListenerMap.remove(key); 1295 return key; 1296 } 1297 } 1298 1299 /** @hide */ 1300 public static class OperationResult implements Parcelable { 1301 public int reason; 1302 public String description; 1303 1304 public OperationResult(int reason, String description) { 1305 this.reason = reason; 1306 this.description = description; 1307 } 1308 1309 /** Implement the Parcelable interface {@hide} */ 1310 public int describeContents() { 1311 return 0; 1312 } 1313 1314 /** Implement the Parcelable interface {@hide} */ 1315 public void writeToParcel(Parcel dest, int flags) { 1316 dest.writeInt(reason); 1317 dest.writeString(description); 1318 } 1319 1320 /** Implement the Parcelable interface {@hide} */ 1321 public static final Creator<OperationResult> CREATOR = 1322 new Creator<OperationResult>() { 1323 public OperationResult createFromParcel(Parcel in) { 1324 int reason = in.readInt(); 1325 String description = in.readString(); 1326 return new OperationResult(reason, description); 1327 } 1328 1329 public OperationResult[] newArray(int size) { 1330 return new OperationResult[size]; 1331 } 1332 }; 1333 } 1334 1335 private class ServiceHandler extends Handler { 1336 ServiceHandler(Looper looper) { 1337 super(looper); 1338 } 1339 @Override 1340 public void handleMessage(Message msg) { 1341 switch (msg.what) { 1342 case AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED: 1343 return; 1344 case AsyncChannel.CMD_CHANNEL_DISCONNECTED: 1345 Log.e(TAG, "Channel connection lost"); 1346 // This will cause all further async API calls on the WifiManager 1347 // to fail and throw an exception 1348 mAsyncChannel = null; 1349 getLooper().quit(); 1350 return; 1351 } 1352 1353 Object listener = getListener(msg.arg2); 1354 1355 if (listener == null) { 1356 if (DBG) Log.d(TAG, "invalid listener key = " + msg.arg2); 1357 return; 1358 } else { 1359 if (DBG) Log.d(TAG, "listener key = " + msg.arg2); 1360 } 1361 1362 switch (msg.what) { 1363 /* ActionListeners grouped together */ 1364 case CMD_OP_SUCCEEDED : 1365 ((ActionListener) listener).onSuccess(); 1366 break; 1367 case CMD_OP_FAILED : { 1368 OperationResult result = (OperationResult)msg.obj; 1369 ((ActionListener) listener).onFailure(result.reason, result.description); 1370 removeListener(msg.arg2); 1371 } 1372 break; 1373 case CMD_SCAN_RESULT : 1374 ((ScanListener) listener).onResults( 1375 ((ParcelableScanData) msg.obj).getResults()); 1376 return; 1377 case CMD_FULL_SCAN_RESULT : 1378 ScanResult result = (ScanResult) msg.obj; 1379 ((ScanListener) listener).onFullResult(result); 1380 return; 1381 case CMD_PERIOD_CHANGED: 1382 ((ScanListener) listener).onPeriodChanged(msg.arg1); 1383 return; 1384 case CMD_AP_FOUND: 1385 ((BssidListener) listener).onFound( 1386 ((ParcelableScanResults) msg.obj).getResults()); 1387 return; 1388 case CMD_AP_LOST: 1389 ((BssidListener) listener).onLost( 1390 ((ParcelableScanResults) msg.obj).getResults()); 1391 return; 1392 case CMD_WIFI_CHANGE_DETECTED: 1393 ((WifiChangeListener) listener).onChanging( 1394 ((ParcelableScanResults) msg.obj).getResults()); 1395 return; 1396 case CMD_WIFI_CHANGES_STABILIZED: 1397 ((WifiChangeListener) listener).onQuiescence( 1398 ((ParcelableScanResults) msg.obj).getResults()); 1399 return; 1400 case CMD_SINGLE_SCAN_COMPLETED: 1401 if (DBG) Log.d(TAG, "removing listener for single scan"); 1402 removeListener(msg.arg2); 1403 break; 1404 case CMD_PNO_NETWORK_FOUND: 1405 ((PnoScanListener) listener).onPnoNetworkFound( 1406 ((ParcelableScanResults) msg.obj).getResults()); 1407 return; 1408 default: 1409 if (DBG) Log.d(TAG, "Ignoring message " + msg.what); 1410 return; 1411 } 1412 } 1413 } 1414 } 1415