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