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