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