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