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