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