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