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