WifiScanner.java revision ea676a0e51504aa53dff9cbe64085658987703b2
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.Handler; 22import android.os.HandlerThread; 23import android.os.Looper; 24import android.os.Message; 25import android.os.Messenger; 26import android.os.Parcel; 27import android.os.Parcelable; 28import android.os.RemoteException; 29import android.util.Log; 30import android.util.SparseArray; 31 32import com.android.internal.util.AsyncChannel; 33import com.android.internal.util.Protocol; 34 35import java.util.List; 36import java.util.concurrent.CountDownLatch; 37 38 39/** 40 * This class provides a way to scan the Wifi universe around the device 41 * Get an instance of this class by calling 42 * {@link android.content.Context#getSystemService(String) Context.getSystemService(Context 43 * .WIFI_SCANNING_SERVICE)}. 44 * @hide 45 */ 46@SystemApi 47public class WifiScanner { 48 49 /** no band specified; use channel list instead */ 50 public static final int WIFI_BAND_UNSPECIFIED = 0; /* not specified */ 51 52 /** 2.4 GHz band */ 53 public static final int WIFI_BAND_24_GHZ = 1; /* 2.4 GHz band */ 54 /** 5 GHz band excluding DFS channels */ 55 public static final int WIFI_BAND_5_GHZ = 2; /* 5 GHz band without DFS channels */ 56 /** DFS channels from 5 GHz band only */ 57 public static final int WIFI_BAND_5_GHZ_DFS_ONLY = 4; /* 5 GHz band with DFS channels */ 58 /** 5 GHz band including DFS channels */ 59 public static final int WIFI_BAND_5_GHZ_WITH_DFS = 6; /* 5 GHz band with DFS channels */ 60 /** Both 2.4 GHz band and 5 GHz band; no DFS channels */ 61 public static final int WIFI_BAND_BOTH = 3; /* both bands without DFS channels */ 62 /** Both 2.4 GHz band and 5 GHz band; with DFS channels */ 63 public static final int WIFI_BAND_BOTH_WITH_DFS = 7; /* both bands with DFS channels */ 64 65 /** Minimum supported scanning period */ 66 public static final int MIN_SCAN_PERIOD_MS = 1000; /* minimum supported period */ 67 /** Maximum supported scanning period */ 68 public static final int MAX_SCAN_PERIOD_MS = 1024000; /* maximum supported period */ 69 70 /** No Error */ 71 public static final int REASON_SUCCEEDED = 0; 72 /** Unknown error */ 73 public static final int REASON_UNSPECIFIED = -1; 74 /** Invalid listener */ 75 public static final int REASON_INVALID_LISTENER = -2; 76 /** Invalid request */ 77 public static final int REASON_INVALID_REQUEST = -3; 78 79 /** 80 * Generic action callback invocation interface 81 * @hide 82 */ 83 @SystemApi 84 public static interface ActionListener { 85 public void onSuccess(); 86 public void onFailure(int reason, String description); 87 } 88 89 /** 90 * gives you all the possible channels; channel is specified as an 91 * integer with frequency in MHz i.e. channel 1 is 2412 92 * @hide 93 */ 94 public List<Integer> getAvailableChannels(int band) { 95 return null; 96 } 97 98 /** 99 * provides channel specification for scanning 100 */ 101 public static class ChannelSpec { 102 /** 103 * channel frequency in KHz; for example channel 1 is specified as 2412 104 */ 105 public int frequency; 106 /** 107 * if true, scan this channel in passive fashion. 108 * This flag is ignored on DFS channel specification. 109 * @hide 110 */ 111 public boolean passive; /* ignored on DFS channels */ 112 /** 113 * how long to dwell on this channel 114 * @hide 115 */ 116 public int dwellTimeMS; /* not supported for now */ 117 118 /** 119 * default constructor for channel spec 120 */ 121 public ChannelSpec(int frequency) { 122 this.frequency = frequency; 123 passive = false; 124 dwellTimeMS = 0; 125 } 126 } 127 128 /** reports {@link ScanListener#onResults} when underlying buffers are full */ 129 public static final int REPORT_EVENT_AFTER_BUFFER_FULL = 0; 130 /** reports {@link ScanListener#onResults} after each scan */ 131 public static final int REPORT_EVENT_AFTER_EACH_SCAN = 1; 132 /** reports {@link ScanListener#onFullResult} whenever each beacon is discovered */ 133 public static final int REPORT_EVENT_FULL_SCAN_RESULT = 2; 134 135 /** 136 * scan configuration parameters to be sent to {@link #startBackgroundScan} 137 */ 138 public static class ScanSettings implements Parcelable { 139 140 /** one of the WIFI_BAND values */ 141 public int band; 142 /** list of channels; used when band is set to WIFI_BAND_UNSPECIFIED */ 143 public ChannelSpec[] channels; 144 /** period of background scan; in millisecond, 0 => single shot scan */ 145 public int periodInMs; 146 /** must have a valid REPORT_EVENT value */ 147 public int reportEvents; 148 /** defines number of bssids to cache from each scan */ 149 public int numBssidsPerScan; 150 151 /** Implement the Parcelable interface {@hide} */ 152 public int describeContents() { 153 return 0; 154 } 155 156 /** Implement the Parcelable interface {@hide} */ 157 public void writeToParcel(Parcel dest, int flags) { 158 dest.writeInt(band); 159 dest.writeInt(periodInMs); 160 dest.writeInt(reportEvents); 161 162 if (channels != null) { 163 dest.writeInt(channels.length); 164 165 for (int i = 0; i < channels.length; i++) { 166 dest.writeInt(channels[i].frequency); 167 dest.writeInt(channels[i].dwellTimeMS); 168 dest.writeInt(channels[i].passive ? 1 : 0); 169 } 170 } else { 171 dest.writeInt(0); 172 } 173 } 174 175 /** Implement the Parcelable interface {@hide} */ 176 public static final Creator<ScanSettings> CREATOR = 177 new Creator<ScanSettings>() { 178 public ScanSettings createFromParcel(Parcel in) { 179 180 ScanSettings settings = new ScanSettings(); 181 settings.band = in.readInt(); 182 settings.periodInMs = in.readInt(); 183 settings.reportEvents = in.readInt(); 184 int num_channels = in.readInt(); 185 settings.channels = new ChannelSpec[num_channels]; 186 for (int i = 0; i < num_channels; i++) { 187 int frequency = in.readInt(); 188 189 ChannelSpec spec = new ChannelSpec(frequency); 190 spec.dwellTimeMS = in.readInt(); 191 spec.passive = in.readInt() == 1; 192 settings.channels[i] = spec; 193 } 194 195 return settings; 196 } 197 198 public ScanSettings[] newArray(int size) { 199 return new ScanSettings[size]; 200 } 201 }; 202 203 } 204 205 /** @hide */ 206 public static class ParcelableScanResults implements Parcelable { 207 public ScanResult mResults[]; 208 209 public ParcelableScanResults(ScanResult[] results) { 210 mResults = results; 211 } 212 213 public ScanResult[] getResults() { 214 return mResults; 215 } 216 217 /** Implement the Parcelable interface {@hide} */ 218 public int describeContents() { 219 return 0; 220 } 221 222 /** Implement the Parcelable interface {@hide} */ 223 public void writeToParcel(Parcel dest, int flags) { 224 if (mResults != null) { 225 dest.writeInt(mResults.length); 226 for (int i = 0; i < mResults.length; i++) { 227 ScanResult result = mResults[i]; 228 result.writeToParcel(dest, flags); 229 } 230 } else { 231 dest.writeInt(0); 232 } 233 } 234 235 /** Implement the Parcelable interface {@hide} */ 236 public static final Creator<ParcelableScanResults> CREATOR = 237 new Creator<ParcelableScanResults>() { 238 public ParcelableScanResults createFromParcel(Parcel in) { 239 int n = in.readInt(); 240 ScanResult results[] = new ScanResult[n]; 241 for (int i = 0; i < n; i++) { 242 results[i] = ScanResult.CREATOR.createFromParcel(in); 243 } 244 return new ParcelableScanResults(results); 245 } 246 247 public ParcelableScanResults[] newArray(int size) { 248 return new ParcelableScanResults[size]; 249 } 250 }; 251 } 252 253 /** 254 * interface to get scan events on; specify this on {@link #startBackgroundScan} 255 */ 256 public interface ScanListener extends ActionListener { 257 /** 258 * Framework co-ordinates scans across multiple apps; so it may not give exactly the 259 * same period requested. If period of a scan is changed; it is reported by this event. 260 */ 261 public void onPeriodChanged(int periodInMs); 262 /** 263 * reports results retrieved from background scan 264 */ 265 public void onResults(ScanResult[] results); 266 /** 267 * reports full scan result for each access point found in scan 268 */ 269 public void onFullResult(ScanResult fullScanResult); 270 } 271 272 /** start wifi scan in background 273 * @param settings specifies various parameters for the scan; for more information look at 274 * {@link ScanSettings} 275 * @param listener specifies the object to report events to. This object is also treated as a 276 * key for this scan, and must also be specified to cancel the scan. Multiple 277 * scans should also not share this object. 278 */ 279 public void startBackgroundScan(ScanSettings settings, ScanListener listener) { 280 validateChannel(); 281 sAsyncChannel.sendMessage(CMD_START_BACKGROUND_SCAN, 0, putListener(listener), settings); 282 } 283 /** 284 * stop an ongoing wifi scan 285 * @param listener specifies which scan to cancel; must be same object as passed in {@link 286 * #startBackgroundScan} 287 */ 288 public void stopBackgroundScan(ScanListener listener) { 289 validateChannel(); 290 sAsyncChannel.sendMessage(CMD_STOP_BACKGROUND_SCAN, 0, removeListener(listener)); 291 } 292 /** 293 * retrieves currently available scan results 294 */ 295 public ScanResult[] getScanResults() { 296 validateChannel(); 297 Message reply = sAsyncChannel.sendMessageSynchronously(CMD_GET_SCAN_RESULTS, 0); 298 ScanResult[] results = (ScanResult[]) reply.obj; 299 return results; 300 } 301 302 /** specifies information about an access point of interest */ 303 public static class BssidInfo { 304 /** bssid of the access point; in XX:XX:XX:XX:XX:XX format */ 305 public String bssid; 306 /** low signal strength threshold; more information at {@link ScanResult#level} */ 307 public int low; /* minimum RSSI */ 308 /** high signal threshold; more information at {@link ScanResult#level} */ 309 public int high; /* maximum RSSI */ 310 /** channel frequency (in KHz) where you may find this BSSID */ 311 public int frequencyHint; 312 } 313 314 /** @hide */ 315 @SystemApi 316 public static class WifiChangeSettings implements Parcelable { 317 public int rssiSampleSize; /* sample size for RSSI averaging */ 318 public int lostApSampleSize; /* samples to confirm AP's loss */ 319 public int unchangedSampleSize; /* samples to confirm no change */ 320 public int minApsBreachingThreshold; /* change threshold to trigger event */ 321 public int periodInMs; /* scan period in millisecond */ 322 public BssidInfo[] bssidInfos; 323 324 /** Implement the Parcelable interface {@hide} */ 325 public int describeContents() { 326 return 0; 327 } 328 329 /** Implement the Parcelable interface {@hide} */ 330 public void writeToParcel(Parcel dest, int flags) { 331 dest.writeInt(rssiSampleSize); 332 dest.writeInt(lostApSampleSize); 333 dest.writeInt(unchangedSampleSize); 334 dest.writeInt(minApsBreachingThreshold); 335 dest.writeInt(periodInMs); 336 if (bssidInfos != null) { 337 dest.writeInt(bssidInfos.length); 338 for (int i = 0; i < bssidInfos.length; i++) { 339 BssidInfo info = bssidInfos[i]; 340 dest.writeString(info.bssid); 341 dest.writeInt(info.low); 342 dest.writeInt(info.high); 343 dest.writeInt(info.frequencyHint); 344 } 345 } else { 346 dest.writeInt(0); 347 } 348 } 349 350 /** Implement the Parcelable interface {@hide} */ 351 public static final Creator<WifiChangeSettings> CREATOR = 352 new Creator<WifiChangeSettings>() { 353 public WifiChangeSettings createFromParcel(Parcel in) { 354 WifiChangeSettings settings = new WifiChangeSettings(); 355 settings.rssiSampleSize = in.readInt(); 356 settings.lostApSampleSize = in.readInt(); 357 settings.unchangedSampleSize = in.readInt(); 358 settings.minApsBreachingThreshold = in.readInt(); 359 settings.periodInMs = in.readInt(); 360 int len = in.readInt(); 361 settings.bssidInfos = new BssidInfo[len]; 362 for (int i = 0; i < len; i++) { 363 BssidInfo info = new BssidInfo(); 364 info.bssid = in.readString(); 365 info.low = in.readInt(); 366 info.high = in.readInt(); 367 info.frequencyHint = in.readInt(); 368 settings.bssidInfos[i] = info; 369 } 370 return settings; 371 } 372 373 public WifiChangeSettings[] newArray(int size) { 374 return new WifiChangeSettings[size]; 375 } 376 }; 377 378 } 379 380 /** configure WifiChange detection 381 * @param rssiSampleSize number of samples used for RSSI averaging 382 * @param lostApSampleSize number of samples to confirm an access point's loss 383 * @param unchangedSampleSize number of samples to confirm there are no changes 384 * @param minApsBreachingThreshold minimum number of access points that need to be 385 * out of range to detect WifiChange 386 * @param periodInMs indicates period of scan to find changes 387 * @param bssidInfos access points to watch 388 */ 389 public void configureWifiChange( 390 int rssiSampleSize, /* sample size for RSSI averaging */ 391 int lostApSampleSize, /* samples to confirm AP's loss */ 392 int unchangedSampleSize, /* samples to confirm no change */ 393 int minApsBreachingThreshold, /* change threshold to trigger event */ 394 int periodInMs, /* period of scan */ 395 BssidInfo[] bssidInfos /* signal thresholds to crosss */ 396 ) 397 { 398 validateChannel(); 399 400 WifiChangeSettings settings = new WifiChangeSettings(); 401 settings.rssiSampleSize = rssiSampleSize; 402 settings.lostApSampleSize = lostApSampleSize; 403 settings.unchangedSampleSize = unchangedSampleSize; 404 settings.minApsBreachingThreshold = minApsBreachingThreshold; 405 settings.periodInMs = periodInMs; 406 settings.bssidInfos = bssidInfos; 407 408 configureWifiChange(settings); 409 } 410 411 /** 412 * interface to get wifi change events on; use this on {@link #startTrackingWifiChange} 413 */ 414 public interface WifiChangeListener extends ActionListener { 415 /** indicates that changes were detected in wifi environment 416 * @param results indicate the access points that exhibited change 417 */ 418 public void onChanging(ScanResult[] results); /* changes are found */ 419 /** indicates that no wifi changes are being detected for a while 420 * @param results indicate the access points that are bing monitored for change 421 */ 422 public void onQuiescence(ScanResult[] results); /* changes settled down */ 423 } 424 425 /** 426 * track changes in wifi environment 427 * @param listener object to report events on; this object must be unique and must also be 428 * provided on {@link #stopTrackingWifiChange} 429 */ 430 public void startTrackingWifiChange(WifiChangeListener listener) { 431 validateChannel(); 432 sAsyncChannel.sendMessage(CMD_START_TRACKING_CHANGE, 0, putListener(listener)); 433 } 434 435 /** 436 * stop tracking changes in wifi environment 437 * @param listener object that was provided to report events on {@link 438 * #stopTrackingWifiChange} 439 */ 440 public void stopTrackingWifiChange(WifiChangeListener listener) { 441 validateChannel(); 442 sAsyncChannel.sendMessage(CMD_STOP_TRACKING_CHANGE, 0, removeListener(listener)); 443 } 444 445 /** @hide */ 446 @SystemApi 447 public void configureWifiChange(WifiChangeSettings settings) { 448 validateChannel(); 449 sAsyncChannel.sendMessage(CMD_CONFIGURE_WIFI_CHANGE, 0, 0, settings); 450 } 451 452 /** interface to receive hotlist events on; use this on {@link #setHotlist} */ 453 public static interface BssidListener extends ActionListener { 454 /** indicates that access points were found by on going scans 455 * @param results list of scan results, one for each access point visible currently 456 */ 457 public void onFound(ScanResult[] results); 458 } 459 460 /** @hide */ 461 @SystemApi 462 public static class HotlistSettings implements Parcelable { 463 public BssidInfo[] bssidInfos; 464 public int apLostThreshold; 465 466 /** Implement the Parcelable interface {@hide} */ 467 public int describeContents() { 468 return 0; 469 } 470 471 /** Implement the Parcelable interface {@hide} */ 472 public void writeToParcel(Parcel dest, int flags) { 473 dest.writeInt(apLostThreshold); 474 475 if (bssidInfos != null) { 476 dest.writeInt(bssidInfos.length); 477 for (int i = 0; i < bssidInfos.length; i++) { 478 BssidInfo info = bssidInfos[i]; 479 dest.writeString(info.bssid); 480 dest.writeInt(info.low); 481 dest.writeInt(info.high); 482 dest.writeInt(info.frequencyHint); 483 } 484 } else { 485 dest.writeInt(0); 486 } 487 } 488 489 /** Implement the Parcelable interface {@hide} */ 490 public static final Creator<HotlistSettings> CREATOR = 491 new Creator<HotlistSettings>() { 492 public HotlistSettings createFromParcel(Parcel in) { 493 HotlistSettings settings = new HotlistSettings(); 494 settings.apLostThreshold = in.readInt(); 495 int n = in.readInt(); 496 settings.bssidInfos = new BssidInfo[n]; 497 for (int i = 0; i < n; i++) { 498 BssidInfo info = new BssidInfo(); 499 info.bssid = in.readString(); 500 info.low = in.readInt(); 501 info.high = in.readInt(); 502 info.frequencyHint = in.readInt(); 503 settings.bssidInfos[i] = info; 504 } 505 return settings; 506 } 507 508 public HotlistSettings[] newArray(int size) { 509 return new HotlistSettings[size]; 510 } 511 }; 512 } 513 514 /** 515 * set interesting access points to find 516 * @param bssidInfos access points of interest 517 * @param apLostThreshold number of scans needed to indicate that AP is lost 518 * @param listener object provided to report events on; this object must be unique and must 519 * also be provided on {@link #stopTrackingBssids} 520 */ 521 public void startTrackingBssids(BssidInfo[] bssidInfos, 522 int apLostThreshold, BssidListener listener) { 523 validateChannel(); 524 HotlistSettings settings = new HotlistSettings(); 525 settings.bssidInfos = bssidInfos; 526 sAsyncChannel.sendMessage(CMD_SET_HOTLIST, 0, putListener(listener), settings); 527 } 528 529 /** 530 * remove tracking of interesting access points 531 * @param listener same object provided in {@link #startTrackingBssids} 532 */ 533 public void stopTrackingBssids(BssidListener listener) { 534 validateChannel(); 535 sAsyncChannel.sendMessage(CMD_RESET_HOTLIST, 0, removeListener(listener)); 536 } 537 538 539 /* private members and methods */ 540 541 private static final String TAG = "WifiScanner"; 542 private static final boolean DBG = true; 543 544 /* commands for Wifi Service */ 545 private static final int BASE = Protocol.BASE_WIFI_SCANNER; 546 547 /** @hide */ 548 public static final int CMD_SCAN = BASE + 0; 549 /** @hide */ 550 public static final int CMD_START_BACKGROUND_SCAN = BASE + 2; 551 /** @hide */ 552 public static final int CMD_STOP_BACKGROUND_SCAN = BASE + 3; 553 /** @hide */ 554 public static final int CMD_GET_SCAN_RESULTS = BASE + 4; 555 /** @hide */ 556 public static final int CMD_SCAN_RESULT = BASE + 5; 557 /** @hide */ 558 public static final int CMD_SET_HOTLIST = BASE + 6; 559 /** @hide */ 560 public static final int CMD_RESET_HOTLIST = BASE + 7; 561 /** @hide */ 562 public static final int CMD_AP_FOUND = BASE + 9; 563 /** @hide */ 564 public static final int CMD_AP_LOST = BASE + 10; 565 /** @hide */ 566 public static final int CMD_START_TRACKING_CHANGE = BASE + 11; 567 /** @hide */ 568 public static final int CMD_STOP_TRACKING_CHANGE = BASE + 12; 569 /** @hide */ 570 public static final int CMD_CONFIGURE_WIFI_CHANGE = BASE + 13; 571 /** @hide */ 572 public static final int CMD_WIFI_CHANGE_DETECTED = BASE + 15; 573 /** @hide */ 574 public static final int CMD_WIFI_CHANGES_STABILIZED = BASE + 16; 575 /** @hide */ 576 public static final int CMD_OP_SUCCEEDED = BASE + 17; 577 /** @hide */ 578 public static final int CMD_OP_FAILED = BASE + 18; 579 /** @hide */ 580 public static final int CMD_PERIOD_CHANGED = BASE + 19; 581 /** @hide */ 582 public static final int CMD_FULL_SCAN_RESULT = BASE + 20; 583 584 private Context mContext; 585 private IWifiScanner mService; 586 587 private static final int INVALID_KEY = 0; 588 private static int sListenerKey = 1; 589 590 private static final SparseArray sListenerMap = new SparseArray(); 591 private static final Object sListenerMapLock = new Object(); 592 593 private static AsyncChannel sAsyncChannel; 594 private static CountDownLatch sConnected; 595 596 private static final Object sThreadRefLock = new Object(); 597 private static int sThreadRefCount; 598 private static HandlerThread sHandlerThread; 599 600 /** 601 * Create a new WifiScanner instance. 602 * Applications will almost always want to use 603 * {@link android.content.Context#getSystemService Context.getSystemService()} to retrieve 604 * the standard {@link android.content.Context#WIFI_SERVICE Context.WIFI_SERVICE}. 605 * @param context the application context 606 * @param service the Binder interface 607 * @hide 608 */ 609 public WifiScanner(Context context, IWifiScanner service) { 610 mContext = context; 611 mService = service; 612 init(); 613 } 614 615 private void init() { 616 synchronized (sThreadRefLock) { 617 if (++sThreadRefCount == 1) { 618 Messenger messenger = null; 619 try { 620 messenger = mService.getMessenger(); 621 } catch (RemoteException e) { 622 /* do nothing */ 623 } catch (SecurityException e) { 624 /* do nothing */ 625 } 626 627 if (messenger == null) { 628 sAsyncChannel = null; 629 return; 630 } 631 632 sHandlerThread = new HandlerThread("WifiScanner"); 633 sAsyncChannel = new AsyncChannel(); 634 sConnected = new CountDownLatch(1); 635 636 sHandlerThread.start(); 637 Handler handler = new ServiceHandler(sHandlerThread.getLooper()); 638 sAsyncChannel.connect(mContext, handler, messenger); 639 try { 640 sConnected.await(); 641 } catch (InterruptedException e) { 642 Log.e(TAG, "interrupted wait at init"); 643 } 644 } 645 } 646 } 647 648 private void validateChannel() { 649 if (sAsyncChannel == null) throw new IllegalStateException( 650 "No permission to access and change wifi or a bad initialization"); 651 } 652 653 private static int putListener(Object listener) { 654 if (listener == null) return INVALID_KEY; 655 int key; 656 synchronized (sListenerMapLock) { 657 do { 658 key = sListenerKey++; 659 } while (key == INVALID_KEY); 660 sListenerMap.put(key, listener); 661 } 662 return key; 663 } 664 665 private static Object getListener(int key) { 666 if (key == INVALID_KEY) return null; 667 synchronized (sListenerMapLock) { 668 Object listener = sListenerMap.get(key); 669 return listener; 670 } 671 } 672 673 private static int getListenerKey(Object listener) { 674 if (listener == null) return INVALID_KEY; 675 synchronized (sListenerMapLock) { 676 int index = sListenerMap.indexOfValue(listener); 677 if (index == -1) { 678 return INVALID_KEY; 679 } else { 680 return sListenerMap.keyAt(index); 681 } 682 } 683 } 684 685 private static Object removeListener(int key) { 686 if (key == INVALID_KEY) return null; 687 synchronized (sListenerMapLock) { 688 Object listener = sListenerMap.get(key); 689 sListenerMap.remove(key); 690 return listener; 691 } 692 } 693 694 private static int removeListener(Object listener) { 695 int key = getListenerKey(listener); 696 if (key == INVALID_KEY) return key; 697 synchronized (sListenerMapLock) { 698 sListenerMap.remove(key); 699 return key; 700 } 701 } 702 703 /** @hide */ 704 public static class OperationResult implements Parcelable { 705 public int reason; 706 public String description; 707 708 public OperationResult(int reason, String description) { 709 this.reason = reason; 710 this.description = description; 711 } 712 713 /** Implement the Parcelable interface {@hide} */ 714 public int describeContents() { 715 return 0; 716 } 717 718 /** Implement the Parcelable interface {@hide} */ 719 public void writeToParcel(Parcel dest, int flags) { 720 dest.writeInt(reason); 721 dest.writeString(description); 722 } 723 724 /** Implement the Parcelable interface {@hide} */ 725 public static final Creator<OperationResult> CREATOR = 726 new Creator<OperationResult>() { 727 public OperationResult createFromParcel(Parcel in) { 728 int reason = in.readInt(); 729 String description = in.readString(); 730 return new OperationResult(reason, description); 731 } 732 733 public OperationResult[] newArray(int size) { 734 return new OperationResult[size]; 735 } 736 }; 737 } 738 739 private static class ServiceHandler extends Handler { 740 ServiceHandler(Looper looper) { 741 super(looper); 742 } 743 @Override 744 public void handleMessage(Message msg) { 745 switch (msg.what) { 746 case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: 747 if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) { 748 sAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION); 749 } else { 750 Log.e(TAG, "Failed to set up channel connection"); 751 // This will cause all further async API calls on the WifiManager 752 // to fail and throw an exception 753 sAsyncChannel = null; 754 } 755 sConnected.countDown(); 756 return; 757 case AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED: 758 return; 759 case AsyncChannel.CMD_CHANNEL_DISCONNECTED: 760 Log.e(TAG, "Channel connection lost"); 761 // This will cause all further async API calls on the WifiManager 762 // to fail and throw an exception 763 sAsyncChannel = null; 764 getLooper().quit(); 765 return; 766 } 767 768 Object listener = getListener(msg.arg2); 769 770 if (listener == null) { 771 if (DBG) Log.d(TAG, "invalid listener key = " + msg.arg2); 772 return; 773 } else { 774 if (DBG) Log.d(TAG, "listener key = " + msg.arg2); 775 } 776 777 switch (msg.what) { 778 /* ActionListeners grouped together */ 779 case CMD_OP_SUCCEEDED : 780 ((ActionListener) listener).onSuccess(); 781 break; 782 case CMD_OP_FAILED : { 783 OperationResult result = (OperationResult)msg.obj; 784 ((ActionListener) listener).onFailure(result.reason, result.description); 785 removeListener(msg.arg2); 786 } 787 break; 788 case CMD_SCAN_RESULT : 789 ((ScanListener) listener).onResults( 790 ((ParcelableScanResults) msg.obj).getResults()); 791 return; 792 case CMD_FULL_SCAN_RESULT : 793 ScanResult result = (ScanResult) msg.obj; 794 ((ScanListener) listener).onFullResult(result); 795 return; 796 case CMD_PERIOD_CHANGED: 797 ((ScanListener) listener).onPeriodChanged(msg.arg1); 798 return; 799 case CMD_AP_FOUND: 800 ((BssidListener) listener).onFound( 801 ((ParcelableScanResults) msg.obj).getResults()); 802 return; 803 case CMD_WIFI_CHANGE_DETECTED: 804 ((WifiChangeListener) listener).onChanging( 805 ((ParcelableScanResults) msg.obj).getResults()); 806 return; 807 case CMD_WIFI_CHANGES_STABILIZED: 808 ((WifiChangeListener) listener).onQuiescence( 809 ((ParcelableScanResults) msg.obj).getResults()); 810 return; 811 default: 812 if (DBG) Log.d(TAG, "Ignoring message " + msg.what); 813 return; 814 } 815 } 816 } 817} 818