| /* |
| * Copyright (C) 2008 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package android.net.wifi; |
| |
| import static android.Manifest.permission.ACCESS_FINE_LOCATION; |
| import static android.Manifest.permission.LOCATION_HARDWARE; |
| import static android.Manifest.permission.NEARBY_WIFI_DEVICES; |
| |
| import android.Manifest; |
| import android.annotation.CallbackExecutor; |
| import android.annotation.FlaggedApi; |
| import android.annotation.IntDef; |
| import android.annotation.NonNull; |
| import android.annotation.Nullable; |
| import android.annotation.RequiresPermission; |
| import android.annotation.SuppressLint; |
| import android.annotation.SystemApi; |
| import android.annotation.SystemService; |
| import android.content.Context; |
| import android.os.Binder; |
| import android.os.Build; |
| import android.os.Bundle; |
| import android.os.Looper; |
| import android.os.Parcel; |
| import android.os.Parcelable; |
| import android.os.Process; |
| import android.os.RemoteException; |
| import android.os.WorkSource; |
| import android.text.TextUtils; |
| import android.util.Log; |
| |
| import androidx.annotation.RequiresApi; |
| |
| import com.android.internal.util.Protocol; |
| import com.android.modules.utils.build.SdkLevel; |
| import com.android.wifi.flags.Flags; |
| |
| import java.lang.annotation.Retention; |
| import java.lang.annotation.RetentionPolicy; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Objects; |
| import java.util.concurrent.Executor; |
| import java.util.function.Consumer; |
| |
| /** |
| * This class provides a way to scan the Wifi universe around the device |
| * @hide |
| */ |
| @SystemApi |
| @SystemService(Context.WIFI_SCANNING_SERVICE) |
| public class WifiScanner { |
| |
| /** @hide */ |
| public static final int WIFI_BAND_INDEX_24_GHZ = 0; |
| /** @hide */ |
| public static final int WIFI_BAND_INDEX_5_GHZ = 1; |
| /** @hide */ |
| public static final int WIFI_BAND_INDEX_5_GHZ_DFS_ONLY = 2; |
| /** @hide */ |
| public static final int WIFI_BAND_INDEX_6_GHZ = 3; |
| /** @hide */ |
| public static final int WIFI_BAND_INDEX_60_GHZ = 4; |
| /** @hide */ |
| public static final int WIFI_BAND_COUNT = 5; |
| |
| /** |
| * Reserved bit for Multi-internet connection only, not for scanning. |
| * @hide |
| */ |
| public static final int WIFI_BAND_INDEX_5_GHZ_LOW = 29; |
| |
| /** |
| * Reserved bit for Multi-internet connection only, not for scanning. |
| * @hide |
| */ |
| public static final int WIFI_BAND_INDEX_5_GHZ_HIGH = 30; |
| |
| /** @hide */ |
| @Retention(RetentionPolicy.SOURCE) |
| @IntDef(prefix = {"WIFI_BAND_INDEX_"}, value = { |
| WIFI_BAND_INDEX_24_GHZ, |
| WIFI_BAND_INDEX_5_GHZ, |
| WIFI_BAND_INDEX_5_GHZ_DFS_ONLY, |
| WIFI_BAND_INDEX_6_GHZ, |
| WIFI_BAND_INDEX_60_GHZ}) |
| public @interface WifiBandIndex {} |
| |
| /** no band specified; use channel list instead */ |
| public static final int WIFI_BAND_UNSPECIFIED = 0; |
| /** 2.4 GHz band */ |
| public static final int WIFI_BAND_24_GHZ = 1 << WIFI_BAND_INDEX_24_GHZ; |
| /** 5 GHz band excluding DFS channels */ |
| public static final int WIFI_BAND_5_GHZ = 1 << WIFI_BAND_INDEX_5_GHZ; |
| /** DFS channels from 5 GHz band only */ |
| public static final int WIFI_BAND_5_GHZ_DFS_ONLY = 1 << WIFI_BAND_INDEX_5_GHZ_DFS_ONLY; |
| /** 6 GHz band */ |
| public static final int WIFI_BAND_6_GHZ = 1 << WIFI_BAND_INDEX_6_GHZ; |
| /** 60 GHz band */ |
| public static final int WIFI_BAND_60_GHZ = 1 << WIFI_BAND_INDEX_60_GHZ; |
| |
| /** |
| * Reserved for Multi-internet connection only, not for scanning. |
| * @hide |
| */ |
| public static final int WIFI_BAND_5_GHZ_LOW = 1 << WIFI_BAND_INDEX_5_GHZ_LOW; |
| /** |
| * Reserved for Multi-internet connection only, not for scanning. |
| * @hide |
| */ |
| public static final int WIFI_BAND_5_GHZ_HIGH = 1 << WIFI_BAND_INDEX_5_GHZ_HIGH; |
| |
| /** |
| * Combination of bands |
| * Note that those are only the common band combinations, |
| * other combinations can be created by combining any of the basic bands above |
| */ |
| /** Both 2.4 GHz band and 5 GHz band; no DFS channels */ |
| public static final int WIFI_BAND_BOTH = WIFI_BAND_24_GHZ | WIFI_BAND_5_GHZ; |
| /** |
| * 2.4Ghz band + DFS channels from 5 GHz band only |
| * @hide |
| */ |
| public static final int WIFI_BAND_24_GHZ_WITH_5GHZ_DFS = |
| WIFI_BAND_24_GHZ | WIFI_BAND_5_GHZ_DFS_ONLY; |
| /** 5 GHz band including DFS channels */ |
| public static final int WIFI_BAND_5_GHZ_WITH_DFS = WIFI_BAND_5_GHZ | WIFI_BAND_5_GHZ_DFS_ONLY; |
| /** Both 2.4 GHz band and 5 GHz band; with DFS channels */ |
| public static final int WIFI_BAND_BOTH_WITH_DFS = |
| WIFI_BAND_24_GHZ | WIFI_BAND_5_GHZ | WIFI_BAND_5_GHZ_DFS_ONLY; |
| /** 2.4 GHz band and 5 GHz band (no DFS channels) and 6 GHz */ |
| public static final int WIFI_BAND_24_5_6_GHZ = WIFI_BAND_BOTH | WIFI_BAND_6_GHZ; |
| /** 2.4 GHz band and 5 GHz band; with DFS channels and 6 GHz */ |
| public static final int WIFI_BAND_24_5_WITH_DFS_6_GHZ = |
| WIFI_BAND_BOTH_WITH_DFS | WIFI_BAND_6_GHZ; |
| /** @hide */ |
| public static final int WIFI_BAND_24_5_6_60_GHZ = |
| WIFI_BAND_24_5_6_GHZ | WIFI_BAND_60_GHZ; |
| /** @hide */ |
| public static final int WIFI_BAND_24_5_WITH_DFS_6_60_GHZ = |
| WIFI_BAND_24_5_6_60_GHZ | WIFI_BAND_5_GHZ_DFS_ONLY; |
| |
| /** @hide */ |
| @Retention(RetentionPolicy.SOURCE) |
| @IntDef(prefix = {"WIFI_BAND_"}, value = { |
| WIFI_BAND_UNSPECIFIED, |
| WIFI_BAND_24_GHZ, |
| WIFI_BAND_5_GHZ, |
| WIFI_BAND_BOTH, |
| WIFI_BAND_5_GHZ_DFS_ONLY, |
| WIFI_BAND_24_GHZ_WITH_5GHZ_DFS, |
| WIFI_BAND_5_GHZ_WITH_DFS, |
| WIFI_BAND_BOTH_WITH_DFS, |
| WIFI_BAND_6_GHZ, |
| WIFI_BAND_24_5_6_GHZ, |
| WIFI_BAND_24_5_WITH_DFS_6_GHZ, |
| WIFI_BAND_60_GHZ, |
| WIFI_BAND_24_5_6_60_GHZ, |
| WIFI_BAND_24_5_WITH_DFS_6_60_GHZ}) |
| public @interface WifiBand {} |
| |
| /** |
| * All bands |
| * @hide |
| */ |
| public static final int WIFI_BAND_ALL = (1 << WIFI_BAND_COUNT) - 1; |
| |
| /** Minimum supported scanning period */ |
| public static final int MIN_SCAN_PERIOD_MS = 1000; |
| /** Maximum supported scanning period */ |
| public static final int MAX_SCAN_PERIOD_MS = 1024000; |
| |
| /** No Error */ |
| public static final int REASON_SUCCEEDED = 0; |
| /** Unknown error */ |
| public static final int REASON_UNSPECIFIED = -1; |
| /** Invalid listener */ |
| public static final int REASON_INVALID_LISTENER = -2; |
| /** Invalid request */ |
| public static final int REASON_INVALID_REQUEST = -3; |
| /** Invalid request */ |
| public static final int REASON_NOT_AUTHORIZED = -4; |
| /** An outstanding request with the same listener hasn't finished yet. */ |
| public static final int REASON_DUPLICATE_REQEUST = -5; |
| /** Busy - Due to Connection in progress, processing another scan request etc. */ |
| public static final int REASON_BUSY = -6; |
| /** Abort - Due to another high priority operation like roaming, offload scan etc. */ |
| public static final int REASON_ABORT = -7; |
| /** No such device - Wrong interface or interface doesn't exist. */ |
| public static final int REASON_NO_DEVICE = -8; |
| /** Invalid argument - Wrong/unsupported argument passed in scan params. */ |
| public static final int REASON_INVALID_ARGS = -9; |
| /** Timeout - Device didn't respond back with scan results */ |
| public static final int REASON_TIMEOUT = -10; |
| |
| /** @hide */ |
| @Retention(RetentionPolicy.SOURCE) |
| @IntDef(prefix = { "REASON_" }, value = { |
| REASON_SUCCEEDED, |
| REASON_UNSPECIFIED, |
| REASON_INVALID_LISTENER, |
| REASON_INVALID_REQUEST, |
| REASON_NOT_AUTHORIZED, |
| REASON_DUPLICATE_REQEUST, |
| REASON_BUSY, |
| REASON_ABORT, |
| REASON_NO_DEVICE, |
| REASON_INVALID_ARGS, |
| REASON_TIMEOUT, |
| }) |
| public @interface ScanStatusCode {} |
| |
| /** @hide */ |
| public static final String GET_AVAILABLE_CHANNELS_EXTRA = "Channels"; |
| |
| /** |
| * This constant is used for {@link ScanSettings#setRnrSetting(int)}. |
| * <p> |
| * Scan 6Ghz APs co-located with 2.4/5Ghz APs using Reduced Neighbor Report (RNR) if the 6Ghz |
| * band is explicitly requested to be scanned and the current country code supports scanning |
| * of at least one 6Ghz channel. The 6Ghz band is explicitly requested if the |
| * ScanSetting.band parameter is set to one of: |
| * <li> {@link #WIFI_BAND_6_GHZ} </li> |
| * <li> {@link #WIFI_BAND_24_5_6_GHZ} </li> |
| * <li> {@link #WIFI_BAND_24_5_WITH_DFS_6_GHZ} </li> |
| * <li> {@link #WIFI_BAND_24_5_6_60_GHZ} </li> |
| * <li> {@link #WIFI_BAND_24_5_WITH_DFS_6_60_GHZ} </li> |
| * <li> {@link #WIFI_BAND_ALL} </li> |
| **/ |
| public static final int WIFI_RNR_ENABLED_IF_WIFI_BAND_6_GHZ_SCANNED = 0; |
| /** |
| * This constant is used for {@link ScanSettings#setRnrSetting(int)}. |
| * <p> |
| * Request to scan 6Ghz APs co-located with 2.4/5Ghz APs using Reduced Neighbor Report (RNR) |
| * when the current country code supports scanning of at least one 6Ghz channel. |
| **/ |
| public static final int WIFI_RNR_ENABLED = 1; |
| /** |
| * This constant is used for {@link ScanSettings#setRnrSetting(int)}. |
| * <p> |
| * Do not request to scan 6Ghz APs co-located with 2.4/5Ghz APs using |
| * Reduced Neighbor Report (RNR) |
| **/ |
| public static final int WIFI_RNR_NOT_NEEDED = 2; |
| |
| /** @hide */ |
| @Retention(RetentionPolicy.SOURCE) |
| @IntDef(prefix = {"RNR_"}, value = { |
| WIFI_RNR_ENABLED_IF_WIFI_BAND_6_GHZ_SCANNED, |
| WIFI_RNR_ENABLED, |
| WIFI_RNR_NOT_NEEDED}) |
| public @interface RnrSetting {} |
| |
| /** |
| * Maximum length in bytes of all vendor specific information elements (IEs) allowed to set. |
| * @hide |
| */ |
| public static final int WIFI_SCANNER_SETTINGS_VENDOR_ELEMENTS_MAX_LEN = 512; |
| |
| /** |
| * Information Element head: id (1 byte) + length (1 byte) |
| * @hide |
| */ |
| public static final int WIFI_IE_HEAD_LEN = 2; |
| |
| /** |
| * Generic action callback invocation interface |
| * @hide |
| */ |
| @SystemApi |
| public static interface ActionListener { |
| public void onSuccess(); |
| public void onFailure(int reason, String description); |
| } |
| |
| /** |
| * Test if scan is a full scan. i.e. scanning all available bands. |
| * For backward compatibility, since some apps don't include 6GHz or 60Ghz in their requests |
| * yet, lacking 6GHz or 60Ghz band does not cause the result to be false. |
| * |
| * @param bandsScanned bands that are fully scanned |
| * @param excludeDfs when true, DFS band is excluded from the check |
| * @return true if all bands are scanned, false otherwise |
| * |
| * @hide |
| */ |
| public static boolean isFullBandScan(@WifiBand int bandsScanned, boolean excludeDfs) { |
| return (bandsScanned | WIFI_BAND_6_GHZ | WIFI_BAND_60_GHZ |
| | (excludeDfs ? WIFI_BAND_5_GHZ_DFS_ONLY : 0)) |
| == WIFI_BAND_ALL; |
| } |
| |
| /** |
| * Returns a list of all the possible channels for the given band(s). |
| * |
| * @param band one of the WifiScanner#WIFI_BAND_* constants, e.g. {@link #WIFI_BAND_24_GHZ} |
| * @return a list of all the frequencies, in MHz, for the given band(s) e.g. channel 1 is |
| * 2412, or null if an error occurred. |
| */ |
| @NonNull |
| @RequiresPermission(NEARBY_WIFI_DEVICES) |
| public List<Integer> getAvailableChannels(int band) { |
| try { |
| Bundle extras = new Bundle(); |
| if (SdkLevel.isAtLeastS()) { |
| extras.putParcelable(WifiManager.EXTRA_PARAM_KEY_ATTRIBUTION_SOURCE, |
| mContext.getAttributionSource()); |
| } |
| Bundle bundle = mService.getAvailableChannels(band, mContext.getOpPackageName(), |
| mContext.getAttributionTag(), extras); |
| List<Integer> channels = bundle.getIntegerArrayList(GET_AVAILABLE_CHANNELS_EXTRA); |
| return channels == null ? new ArrayList<>() : channels; |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } |
| } |
| |
| private class ServiceListener extends IWifiScannerListener.Stub { |
| private ActionListener mActionListener; |
| private Executor mExecutor; |
| |
| ServiceListener(ActionListener listener, Executor executor) { |
| mActionListener = listener; |
| mExecutor = executor; |
| } |
| |
| @Override |
| public void onSuccess() { |
| if (mActionListener == null) return; |
| Binder.clearCallingIdentity(); |
| mExecutor.execute(mActionListener::onSuccess); |
| } |
| |
| @Override |
| public void onFailure(int reason, String description) { |
| if (mActionListener == null) return; |
| Binder.clearCallingIdentity(); |
| mExecutor.execute(() -> |
| mActionListener.onFailure(reason, description)); |
| removeListener(mActionListener); |
| } |
| |
| /** |
| * reports results retrieved from background scan and single shot scans |
| */ |
| @Override |
| public void onResults(WifiScanner.ScanData[] results) { |
| if (mActionListener == null) return; |
| if (!(mActionListener instanceof ScanListener)) return; |
| ScanListener scanListener = (ScanListener) mActionListener; |
| Binder.clearCallingIdentity(); |
| mExecutor.execute( |
| () -> scanListener.onResults(results)); |
| } |
| |
| /** |
| * reports full scan result for each access point found in scan |
| */ |
| @Override |
| public void onFullResult(ScanResult fullScanResult) { |
| if (mActionListener == null) return; |
| if (!(mActionListener instanceof ScanListener)) return; |
| ScanListener scanListener = (ScanListener) mActionListener; |
| Binder.clearCallingIdentity(); |
| mExecutor.execute( |
| () -> scanListener.onFullResult(fullScanResult)); |
| } |
| |
| @Override |
| public void onSingleScanCompleted() { |
| if (DBG) Log.d(TAG, "single scan completed"); |
| removeListener(mActionListener); |
| } |
| |
| /** |
| * Invoked when one of the PNO networks are found in scan results. |
| */ |
| @Override |
| public void onPnoNetworkFound(ScanResult[] results) { |
| if (mActionListener == null) return; |
| if (!(mActionListener instanceof PnoScanListener)) return; |
| PnoScanListener pnoScanListener = (PnoScanListener) mActionListener; |
| Binder.clearCallingIdentity(); |
| mExecutor.execute( |
| () -> pnoScanListener.onPnoNetworkFound(results)); |
| } |
| } |
| |
| /** |
| * provides channel specification for scanning |
| */ |
| public static class ChannelSpec { |
| /** |
| * channel frequency in MHz; for example channel 1 is specified as 2412 |
| */ |
| public int frequency; |
| /** |
| * if true, scan this channel in passive fashion. |
| * This flag is ignored on DFS channel specification. |
| * @hide |
| */ |
| public boolean passive; /* ignored on DFS channels */ |
| /** |
| * how long to dwell on this channel |
| * @hide |
| */ |
| public int dwellTimeMS; /* not supported for now */ |
| |
| /** |
| * default constructor for channel spec |
| */ |
| public ChannelSpec(int frequency) { |
| this.frequency = frequency; |
| passive = false; |
| dwellTimeMS = 0; |
| } |
| } |
| |
| /** |
| * reports {@link ScanListener#onResults} when underlying buffers are full |
| * this is simply the lack of the {@link #REPORT_EVENT_AFTER_EACH_SCAN} flag |
| * @deprecated It is not supported anymore. |
| */ |
| @Deprecated |
| public static final int REPORT_EVENT_AFTER_BUFFER_FULL = 0; |
| /** |
| * reports {@link ScanListener#onResults} after each scan |
| */ |
| public static final int REPORT_EVENT_AFTER_EACH_SCAN = (1 << 0); |
| /** |
| * reports {@link ScanListener#onFullResult} whenever each beacon is discovered |
| */ |
| public static final int REPORT_EVENT_FULL_SCAN_RESULT = (1 << 1); |
| /** |
| * Do not place scans in the chip's scan history buffer |
| */ |
| public static final int REPORT_EVENT_NO_BATCH = (1 << 2); |
| |
| /** |
| * Optimize the scan for lower latency. |
| * @see ScanSettings#type |
| */ |
| public static final int SCAN_TYPE_LOW_LATENCY = 0; |
| /** |
| * Optimize the scan for lower power usage. |
| * @see ScanSettings#type |
| */ |
| public static final int SCAN_TYPE_LOW_POWER = 1; |
| /** |
| * Optimize the scan for higher accuracy. |
| * @see ScanSettings#type |
| */ |
| public static final int SCAN_TYPE_HIGH_ACCURACY = 2; |
| /** |
| * Max valid value of SCAN_TYPE_ |
| * @hide |
| */ |
| public static final int SCAN_TYPE_MAX = 2; |
| |
| /** {@hide} */ |
| public static final String SCAN_PARAMS_SCAN_SETTINGS_KEY = "ScanSettings"; |
| /** {@hide} */ |
| public static final String SCAN_PARAMS_WORK_SOURCE_KEY = "WorkSource"; |
| /** {@hide} */ |
| public static final String REQUEST_PACKAGE_NAME_KEY = "PackageName"; |
| /** {@hide} */ |
| public static final String REQUEST_FEATURE_ID_KEY = "FeatureId"; |
| |
| /** |
| * scan configuration parameters to be sent to {@link #startBackgroundScan} |
| */ |
| public static class ScanSettings implements Parcelable { |
| /** Hidden network to be scanned for. */ |
| public static class HiddenNetwork { |
| /** SSID of the network */ |
| @NonNull |
| public final String ssid; |
| |
| /** Default constructor for HiddenNetwork. */ |
| public HiddenNetwork(@NonNull String ssid) { |
| this.ssid = ssid; |
| } |
| } |
| |
| /** one of the WIFI_BAND values */ |
| public int band; |
| /** |
| * one of the {@code WIFI_RNR_*} values. |
| */ |
| private int mRnrSetting = WIFI_RNR_ENABLED_IF_WIFI_BAND_6_GHZ_SCANNED; |
| |
| /** |
| * See {@link #set6GhzPscOnlyEnabled} |
| */ |
| private boolean mEnable6GhzPsc = false; |
| |
| /** list of channels; used when band is set to WIFI_BAND_UNSPECIFIED */ |
| public ChannelSpec[] channels; |
| /** |
| * List of hidden networks to scan for. Explicit probe requests are sent out for such |
| * networks during scan. Only valid for single scan requests. |
| */ |
| @NonNull |
| @RequiresPermission(android.Manifest.permission.NETWORK_STACK) |
| public final List<HiddenNetwork> hiddenNetworks = new ArrayList<>(); |
| |
| /** |
| * vendor IEs -- list of ScanResult.InformationElement, configured by App |
| * see {@link #setVendorIes(List)} |
| */ |
| @NonNull |
| private List<ScanResult.InformationElement> mVendorIes = new ArrayList<>(); |
| |
| /** |
| * period of background scan; in millisecond, 0 => single shot scan |
| * @deprecated Background scan support has always been hardware vendor dependent. This |
| * support may not be present on newer devices. Use {@link #startScan(ScanSettings, |
| * ScanListener)} instead for single scans. |
| */ |
| @Deprecated |
| public int periodInMs; |
| /** |
| * must have a valid REPORT_EVENT value |
| * @deprecated Background scan support has always been hardware vendor dependent. This |
| * support may not be present on newer devices. Use {@link #startScan(ScanSettings, |
| * ScanListener)} instead for single scans. |
| */ |
| @Deprecated |
| public int reportEvents; |
| /** |
| * defines number of bssids to cache from each scan |
| * @deprecated Background scan support has always been hardware vendor dependent. This |
| * support may not be present on newer devices. Use {@link #startScan(ScanSettings, |
| * ScanListener)} instead for single scans. |
| */ |
| @Deprecated |
| public int numBssidsPerScan; |
| /** |
| * defines number of scans to cache; use it with REPORT_EVENT_AFTER_BUFFER_FULL |
| * to wake up at fixed interval |
| * @deprecated Background scan support has always been hardware vendor dependent. This |
| * support may not be present on newer devices. Use {@link #startScan(ScanSettings, |
| * ScanListener)} instead for single scans. |
| */ |
| @Deprecated |
| public int maxScansToCache; |
| /** |
| * if maxPeriodInMs is non zero or different than period, then this bucket is |
| * a truncated binary exponential backoff bucket and the scan period will grow |
| * exponentially as per formula: actual_period(N) = period * (2 ^ (N/stepCount)) |
| * to maxPeriodInMs |
| * @deprecated Background scan support has always been hardware vendor dependent. This |
| * support may not be present on newer devices. Use {@link #startScan(ScanSettings, |
| * ScanListener)} instead for single scans. |
| */ |
| @Deprecated |
| public int maxPeriodInMs; |
| /** |
| * for truncated binary exponential back off bucket, number of scans to perform |
| * for a given period |
| * @deprecated Background scan support has always been hardware vendor dependent. This |
| * support may not be present on newer devices. Use {@link #startScan(ScanSettings, |
| * ScanListener)} instead for single scans. |
| */ |
| @Deprecated |
| public int stepCount; |
| /** |
| * Flag to indicate if the scan settings are targeted for PNO scan. |
| * {@hide} |
| */ |
| public boolean isPnoScan; |
| /** |
| * Indicate the type of scan to be performed by the wifi chip. |
| * |
| * On devices with multiple hardware radio chains (and hence different modes of scan), |
| * this type serves as an indication to the hardware on what mode of scan to perform. |
| * Only apps holding {@link android.Manifest.permission.NETWORK_STACK} permission can set |
| * this value. |
| * |
| * Note: This serves as an intent and not as a stipulation, the wifi chip |
| * might honor or ignore the indication based on the current radio conditions. Always |
| * use the {@link ScanResult#radioChainInfos} to figure out the radio chain configuration |
| * used to receive the corresponding scan result. |
| * |
| * One of {@link #SCAN_TYPE_LOW_LATENCY}, {@link #SCAN_TYPE_LOW_POWER}, |
| * {@link #SCAN_TYPE_HIGH_ACCURACY}. |
| * Default value: {@link #SCAN_TYPE_LOW_LATENCY}. |
| */ |
| @WifiAnnotations.ScanType |
| @RequiresPermission(android.Manifest.permission.NETWORK_STACK) |
| public int type = SCAN_TYPE_LOW_LATENCY; |
| /** |
| * This scan request may ignore location settings while receiving scans. This should only |
| * be used in emergency situations. |
| * {@hide} |
| */ |
| @SystemApi |
| public boolean ignoreLocationSettings; |
| /** |
| * This scan request will be hidden from app-ops noting for location information. This |
| * should only be used by FLP/NLP module on the device which is using the scan results to |
| * compute results for behalf on their clients. FLP/NLP module using this flag should ensure |
| * that they note in app-ops the eventual delivery of location information computed using |
| * these results to their client . |
| * {@hide} |
| */ |
| @SystemApi |
| public boolean hideFromAppOps; |
| |
| /** |
| * Configure whether it is needed to scan 6Ghz non Preferred Scanning Channels when scanning |
| * {@link #WIFI_BAND_6_GHZ}. If set to true and a band that contains |
| * {@link #WIFI_BAND_6_GHZ} is configured for scanning, then only scan 6Ghz PSC channels in |
| * addition to any other bands configured for scanning. Note, 6Ghz non-PSC channels that |
| * are co-located with 2.4/5Ghz APs could still be scanned via the |
| * {@link #setRnrSetting(int)} API. |
| * |
| * <p> |
| * For example, given a ScanSettings with band set to {@link #WIFI_BAND_24_5_WITH_DFS_6_GHZ} |
| * If this API is set to "true" then the ScanSettings is configured to scan all of 2.4Ghz |
| * + all of 5Ghz(DFS and non-DFS) + 6Ghz PSC channels. If this API is set to "false", then |
| * the ScanSetting is configured to scan all of 2.4Ghz + all of 5Ghz(DFS and non_DFS) |
| * + all of 6Ghz channels. |
| * @param enable true to only scan 6Ghz PSC channels, false to scan all 6Ghz channels. |
| */ |
| @RequiresApi(Build.VERSION_CODES.S) |
| public void set6GhzPscOnlyEnabled(boolean enable) { |
| if (!SdkLevel.isAtLeastS()) { |
| throw new UnsupportedOperationException(); |
| } |
| mEnable6GhzPsc = enable; |
| } |
| |
| /** |
| * See {@link #set6GhzPscOnlyEnabled} |
| */ |
| @RequiresApi(Build.VERSION_CODES.S) |
| public boolean is6GhzPscOnlyEnabled() { |
| if (!SdkLevel.isAtLeastS()) { |
| throw new UnsupportedOperationException(); |
| } |
| return mEnable6GhzPsc; |
| } |
| |
| /** |
| * Configure when to scan 6Ghz APs co-located with 2.4/5Ghz APs using Reduced |
| * Neighbor Report (RNR). |
| * @param rnrSetting one of the {@code WIFI_RNR_*} values |
| */ |
| @RequiresApi(Build.VERSION_CODES.S) |
| public void setRnrSetting(@RnrSetting int rnrSetting) { |
| if (!SdkLevel.isAtLeastS()) { |
| throw new UnsupportedOperationException(); |
| } |
| if (rnrSetting < WIFI_RNR_ENABLED_IF_WIFI_BAND_6_GHZ_SCANNED |
| || rnrSetting > WIFI_RNR_NOT_NEEDED) { |
| throw new IllegalArgumentException("Invalid rnrSetting"); |
| } |
| mRnrSetting = rnrSetting; |
| } |
| |
| /** |
| * See {@link #setRnrSetting} |
| */ |
| @RequiresApi(Build.VERSION_CODES.S) |
| public @RnrSetting int getRnrSetting() { |
| if (!SdkLevel.isAtLeastS()) { |
| throw new UnsupportedOperationException(); |
| } |
| return mRnrSetting; |
| } |
| |
| /** |
| * Set vendor IEs in scan probe req. |
| * |
| * @param vendorIes List of ScanResult.InformationElement configured by App. |
| */ |
| @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) |
| public void setVendorIes(@NonNull List<ScanResult.InformationElement> vendorIes) { |
| if (!SdkLevel.isAtLeastU()) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| mVendorIes.clear(); |
| int totalBytes = 0; |
| for (ScanResult.InformationElement e : vendorIes) { |
| if (e.id != ScanResult.InformationElement.EID_VSA) { |
| throw new IllegalArgumentException("received InformationElement which is not " |
| + "a Vendor Specific IE (VSIE). VSIEs have an ID = ScanResult" |
| + ".InformationElement.EID_VSA."); |
| } |
| if (e.bytes == null || e.bytes.length > 0xff) { |
| throw new IllegalArgumentException("received InformationElement whose payload " |
| + "is null or size is greater than 255."); |
| } |
| // The total bytes of an IE is EID (1 byte) + length (1 byte) + payload length. |
| totalBytes += WIFI_IE_HEAD_LEN + e.bytes.length; |
| if (totalBytes > WIFI_SCANNER_SETTINGS_VENDOR_ELEMENTS_MAX_LEN) { |
| throw new IllegalArgumentException( |
| "received InformationElement whose total size is greater than " |
| + WIFI_SCANNER_SETTINGS_VENDOR_ELEMENTS_MAX_LEN + "."); |
| } |
| } |
| mVendorIes.addAll(vendorIes); |
| } |
| |
| /** |
| * See {@link #setVendorIes(List)} |
| */ |
| @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) |
| public @NonNull List<ScanResult.InformationElement> getVendorIes() { |
| if (!SdkLevel.isAtLeastU()) { |
| throw new UnsupportedOperationException(); |
| } |
| return mVendorIes; |
| } |
| |
| /** Implement the Parcelable interface {@hide} */ |
| public int describeContents() { |
| return 0; |
| } |
| |
| /** Implement the Parcelable interface {@hide} */ |
| public void writeToParcel(Parcel dest, int flags) { |
| dest.writeInt(band); |
| dest.writeInt(periodInMs); |
| dest.writeInt(reportEvents); |
| dest.writeInt(numBssidsPerScan); |
| dest.writeInt(maxScansToCache); |
| dest.writeInt(maxPeriodInMs); |
| dest.writeInt(stepCount); |
| dest.writeInt(isPnoScan ? 1 : 0); |
| dest.writeInt(type); |
| dest.writeInt(ignoreLocationSettings ? 1 : 0); |
| dest.writeInt(hideFromAppOps ? 1 : 0); |
| dest.writeInt(mRnrSetting); |
| dest.writeBoolean(mEnable6GhzPsc); |
| if (channels != null) { |
| dest.writeInt(channels.length); |
| for (int i = 0; i < channels.length; i++) { |
| dest.writeInt(channels[i].frequency); |
| dest.writeInt(channels[i].dwellTimeMS); |
| dest.writeInt(channels[i].passive ? 1 : 0); |
| } |
| } else { |
| dest.writeInt(0); |
| } |
| dest.writeInt(hiddenNetworks.size()); |
| for (HiddenNetwork hiddenNetwork : hiddenNetworks) { |
| dest.writeString(hiddenNetwork.ssid); |
| } |
| dest.writeTypedList(mVendorIes); |
| } |
| |
| /** Implement the Parcelable interface */ |
| public static final @NonNull Creator<ScanSettings> CREATOR = |
| new Creator<ScanSettings>() { |
| public ScanSettings createFromParcel(Parcel in) { |
| ScanSettings settings = new ScanSettings(); |
| settings.band = in.readInt(); |
| settings.periodInMs = in.readInt(); |
| settings.reportEvents = in.readInt(); |
| settings.numBssidsPerScan = in.readInt(); |
| settings.maxScansToCache = in.readInt(); |
| settings.maxPeriodInMs = in.readInt(); |
| settings.stepCount = in.readInt(); |
| settings.isPnoScan = in.readInt() == 1; |
| settings.type = in.readInt(); |
| settings.ignoreLocationSettings = in.readInt() == 1; |
| settings.hideFromAppOps = in.readInt() == 1; |
| settings.mRnrSetting = in.readInt(); |
| settings.mEnable6GhzPsc = in.readBoolean(); |
| int num_channels = in.readInt(); |
| settings.channels = new ChannelSpec[num_channels]; |
| for (int i = 0; i < num_channels; i++) { |
| int frequency = in.readInt(); |
| ChannelSpec spec = new ChannelSpec(frequency); |
| spec.dwellTimeMS = in.readInt(); |
| spec.passive = in.readInt() == 1; |
| settings.channels[i] = spec; |
| } |
| int numNetworks = in.readInt(); |
| settings.hiddenNetworks.clear(); |
| for (int i = 0; i < numNetworks; i++) { |
| String ssid = in.readString(); |
| settings.hiddenNetworks.add(new HiddenNetwork(ssid)); |
| } |
| in.readTypedList(settings.mVendorIes, |
| ScanResult.InformationElement.CREATOR); |
| return settings; |
| } |
| |
| public ScanSettings[] newArray(int size) { |
| return new ScanSettings[size]; |
| } |
| }; |
| } |
| |
| /** |
| * All the information garnered from a single scan |
| */ |
| public static class ScanData implements Parcelable { |
| /** scan identifier */ |
| private int mId; |
| /** additional information about scan |
| * 0 => no special issues encountered in the scan |
| * non-zero => scan was truncated, so results may not be complete |
| */ |
| private int mFlags; |
| /** |
| * Indicates the buckets that were scanned to generate these results. |
| * This is not relevant to WifiScanner API users and is used internally. |
| * {@hide} |
| */ |
| private int mBucketsScanned; |
| /** |
| * Bands scanned. One of the WIFI_BAND values. |
| * Will be {@link #WIFI_BAND_UNSPECIFIED} if the list of channels do not fully cover |
| * any of the bands. |
| * {@hide} |
| */ |
| private int mScannedBands; |
| /** all scan results discovered in this scan, sorted by timestamp in ascending order */ |
| private final List<ScanResult> mResults; |
| |
| /** {@hide} */ |
| public ScanData() { |
| mResults = new ArrayList<>(); |
| } |
| |
| public ScanData(int id, int flags, ScanResult[] results) { |
| mId = id; |
| mFlags = flags; |
| mResults = new ArrayList<>(Arrays.asList(results)); |
| } |
| |
| /** {@hide} */ |
| public ScanData(int id, int flags, int bucketsScanned, int bandsScanned, |
| ScanResult[] results) { |
| this(id, flags, bucketsScanned, bandsScanned, new ArrayList<>(Arrays.asList(results))); |
| } |
| |
| /** {@hide} */ |
| public ScanData(int id, int flags, int bucketsScanned, int bandsScanned, |
| List<ScanResult> results) { |
| mId = id; |
| mFlags = flags; |
| mBucketsScanned = bucketsScanned; |
| mScannedBands = bandsScanned; |
| mResults = results; |
| } |
| |
| public ScanData(ScanData s) { |
| mId = s.mId; |
| mFlags = s.mFlags; |
| mBucketsScanned = s.mBucketsScanned; |
| mScannedBands = s.mScannedBands; |
| mResults = new ArrayList<>(); |
| for (ScanResult scanResult : s.mResults) { |
| mResults.add(new ScanResult(scanResult)); |
| } |
| } |
| |
| public int getId() { |
| return mId; |
| } |
| |
| public int getFlags() { |
| return mFlags; |
| } |
| |
| /** {@hide} */ |
| public int getBucketsScanned() { |
| return mBucketsScanned; |
| } |
| |
| /** |
| * Retrieve the bands that were fully scanned for this ScanData instance. "fully" here |
| * refers to all the channels available in the band based on the current regulatory |
| * domain. |
| * |
| * @return Bitmask of {@link #WIFI_BAND_24_GHZ}, {@link #WIFI_BAND_5_GHZ}, |
| * {@link #WIFI_BAND_5_GHZ_DFS_ONLY}, {@link #WIFI_BAND_6_GHZ} & {@link #WIFI_BAND_60_GHZ} |
| * values. Each bit is set only if all the channels in the corresponding band is scanned. |
| * Will be {@link #WIFI_BAND_UNSPECIFIED} if the list of channels do not fully cover |
| * any of the bands. |
| * <p> |
| * For ex: |
| * <li> Scenario 1: Fully scanned 2.4Ghz band, partially scanned 5Ghz band |
| * - Returns {@link #WIFI_BAND_24_GHZ} |
| * </li> |
| * <li> Scenario 2: Partially scanned 2.4Ghz band and 5Ghz band |
| * - Returns {@link #WIFI_BAND_UNSPECIFIED} |
| * </li> |
| * </p> |
| */ |
| public @WifiBand int getScannedBands() { |
| return getScannedBandsInternal(); |
| } |
| |
| /** |
| * Same as {@link #getScannedBands()}. For use in the wifi stack without version check. |
| * |
| * {@hide} |
| */ |
| public @WifiBand int getScannedBandsInternal() { |
| return mScannedBands; |
| } |
| |
| public ScanResult[] getResults() { |
| return mResults.toArray(new ScanResult[0]); |
| } |
| |
| /** {@hide} */ |
| public void addResults(@NonNull ScanResult[] newResults) { |
| for (ScanResult result : newResults) { |
| mResults.add(new ScanResult(result)); |
| } |
| } |
| |
| /** {@hide} */ |
| public void addResults(@NonNull ScanData s) { |
| mScannedBands |= s.mScannedBands; |
| mFlags |= s.mFlags; |
| addResults(s.getResults()); |
| } |
| |
| /** {@hide} */ |
| public boolean isFullBandScanResults() { |
| return (mScannedBands & WifiScanner.WIFI_BAND_24_GHZ) != 0 |
| && (mScannedBands & WifiScanner.WIFI_BAND_5_GHZ) != 0; |
| } |
| |
| /** Implement the Parcelable interface {@hide} */ |
| public int describeContents() { |
| return 0; |
| } |
| |
| /** Implement the Parcelable interface {@hide} */ |
| public void writeToParcel(Parcel dest, int flags) { |
| dest.writeInt(mId); |
| dest.writeInt(mFlags); |
| dest.writeInt(mBucketsScanned); |
| dest.writeInt(mScannedBands); |
| dest.writeParcelableList(mResults, 0); |
| } |
| |
| /** Implement the Parcelable interface {@hide} */ |
| public static final @NonNull Creator<ScanData> CREATOR = |
| new Creator<ScanData>() { |
| public ScanData createFromParcel(Parcel in) { |
| int id = in.readInt(); |
| int flags = in.readInt(); |
| int bucketsScanned = in.readInt(); |
| int bandsScanned = in.readInt(); |
| List<ScanResult> results = new ArrayList<>(); |
| in.readParcelableList(results, ScanResult.class.getClassLoader()); |
| return new ScanData(id, flags, bucketsScanned, bandsScanned, results); |
| } |
| |
| public ScanData[] newArray(int size) { |
| return new ScanData[size]; |
| } |
| }; |
| } |
| |
| public static class ParcelableScanData implements Parcelable { |
| |
| public ScanData mResults[]; |
| |
| public ParcelableScanData(ScanData[] results) { |
| mResults = results; |
| } |
| |
| public ScanData[] getResults() { |
| return mResults; |
| } |
| |
| /** Implement the Parcelable interface {@hide} */ |
| public int describeContents() { |
| return 0; |
| } |
| |
| /** Implement the Parcelable interface {@hide} */ |
| public void writeToParcel(Parcel dest, int flags) { |
| if (mResults != null) { |
| dest.writeInt(mResults.length); |
| for (int i = 0; i < mResults.length; i++) { |
| ScanData result = mResults[i]; |
| result.writeToParcel(dest, flags); |
| } |
| } else { |
| dest.writeInt(0); |
| } |
| } |
| |
| /** Implement the Parcelable interface {@hide} */ |
| public static final @NonNull Creator<ParcelableScanData> CREATOR = |
| new Creator<ParcelableScanData>() { |
| public ParcelableScanData createFromParcel(Parcel in) { |
| int n = in.readInt(); |
| ScanData results[] = new ScanData[n]; |
| for (int i = 0; i < n; i++) { |
| results[i] = ScanData.CREATOR.createFromParcel(in); |
| } |
| return new ParcelableScanData(results); |
| } |
| |
| public ParcelableScanData[] newArray(int size) { |
| return new ParcelableScanData[size]; |
| } |
| }; |
| } |
| |
| public static class ParcelableScanResults implements Parcelable { |
| |
| public ScanResult mResults[]; |
| |
| public ParcelableScanResults(ScanResult[] results) { |
| mResults = results; |
| } |
| |
| public ScanResult[] getResults() { |
| return mResults; |
| } |
| |
| /** Implement the Parcelable interface {@hide} */ |
| public int describeContents() { |
| return 0; |
| } |
| |
| /** Implement the Parcelable interface {@hide} */ |
| public void writeToParcel(Parcel dest, int flags) { |
| if (mResults != null) { |
| dest.writeInt(mResults.length); |
| for (int i = 0; i < mResults.length; i++) { |
| ScanResult result = mResults[i]; |
| result.writeToParcel(dest, flags); |
| } |
| } else { |
| dest.writeInt(0); |
| } |
| } |
| |
| /** Implement the Parcelable interface {@hide} */ |
| public static final @NonNull Creator<ParcelableScanResults> CREATOR = |
| new Creator<ParcelableScanResults>() { |
| public ParcelableScanResults createFromParcel(Parcel in) { |
| int n = in.readInt(); |
| ScanResult results[] = new ScanResult[n]; |
| for (int i = 0; i < n; i++) { |
| results[i] = ScanResult.CREATOR.createFromParcel(in); |
| } |
| return new ParcelableScanResults(results); |
| } |
| |
| public ParcelableScanResults[] newArray(int size) { |
| return new ParcelableScanResults[size]; |
| } |
| }; |
| } |
| |
| /** {@hide} */ |
| public static final String PNO_PARAMS_PNO_SETTINGS_KEY = "PnoSettings"; |
| /** {@hide} */ |
| public static final String PNO_PARAMS_SCAN_SETTINGS_KEY = "ScanSettings"; |
| /** |
| * PNO scan configuration parameters to be sent to {@link #startPnoScan}. |
| * Note: This structure needs to be in sync with |wifi_epno_params| struct in gscan HAL API. |
| * {@hide} |
| */ |
| public static class PnoSettings implements Parcelable { |
| /** |
| * Pno network to be added to the PNO scan filtering. |
| * {@hide} |
| */ |
| public static class PnoNetwork { |
| /* |
| * Pno flags bitmask to be set in {@link #PnoNetwork.flags} |
| */ |
| /** Whether directed scan needs to be performed (for hidden SSIDs) */ |
| public static final byte FLAG_DIRECTED_SCAN = (1 << 0); |
| /** Whether PNO event shall be triggered if the network is found on A band */ |
| public static final byte FLAG_A_BAND = (1 << 1); |
| /** Whether PNO event shall be triggered if the network is found on G band */ |
| public static final byte FLAG_G_BAND = (1 << 2); |
| /** |
| * Whether strict matching is required |
| * If required then the firmware must store the network's SSID and not just a hash |
| */ |
| public static final byte FLAG_STRICT_MATCH = (1 << 3); |
| /** |
| * If this SSID should be considered the same network as the currently connected |
| * one for scoring. |
| */ |
| public static final byte FLAG_SAME_NETWORK = (1 << 4); |
| |
| /* |
| * Code for matching the beacon AUTH IE - additional codes. Bitmask to be set in |
| * {@link #PnoNetwork.authBitField} |
| */ |
| /** Open Network */ |
| public static final byte AUTH_CODE_OPEN = (1 << 0); |
| /** WPA_PSK or WPA2PSK */ |
| public static final byte AUTH_CODE_PSK = (1 << 1); |
| /** any EAPOL */ |
| public static final byte AUTH_CODE_EAPOL = (1 << 2); |
| |
| /** SSID of the network */ |
| public String ssid; |
| /** Bitmask of the FLAG_XXX */ |
| public byte flags = 0; |
| /** Bitmask of the ATUH_XXX */ |
| public byte authBitField = 0; |
| /** frequencies on which the particular network needs to be scanned for */ |
| public int[] frequencies = {}; |
| |
| /** |
| * default constructor for PnoNetwork |
| */ |
| public PnoNetwork(String ssid) { |
| this.ssid = ssid; |
| } |
| |
| @Override |
| public int hashCode() { |
| return Objects.hash(ssid, flags, authBitField); |
| } |
| |
| @Override |
| public boolean equals(Object obj) { |
| if (this == obj) { |
| return true; |
| } |
| if (!(obj instanceof PnoNetwork)) { |
| return false; |
| } |
| PnoNetwork lhs = (PnoNetwork) obj; |
| return TextUtils.equals(this.ssid, lhs.ssid) |
| && this.flags == lhs.flags |
| && this.authBitField == lhs.authBitField; |
| } |
| } |
| |
| /** Connected vs Disconnected PNO flag {@hide} */ |
| public boolean isConnected; |
| /** Minimum 5GHz RSSI for a BSSID to be considered */ |
| public int min5GHzRssi; |
| /** Minimum 2.4GHz RSSI for a BSSID to be considered */ |
| public int min24GHzRssi; |
| /** Minimum 6GHz RSSI for a BSSID to be considered */ |
| public int min6GHzRssi; |
| /** Iterations of Pno scan */ |
| public int scanIterations; |
| /** Multiplier of Pno scan interval */ |
| public int scanIntervalMultiplier; |
| /** Pno Network filter list */ |
| public PnoNetwork[] networkList; |
| |
| /** Implement the Parcelable interface {@hide} */ |
| public int describeContents() { |
| return 0; |
| } |
| |
| /** Implement the Parcelable interface {@hide} */ |
| public void writeToParcel(Parcel dest, int flags) { |
| dest.writeInt(isConnected ? 1 : 0); |
| dest.writeInt(min5GHzRssi); |
| dest.writeInt(min24GHzRssi); |
| dest.writeInt(min6GHzRssi); |
| dest.writeInt(scanIterations); |
| dest.writeInt(scanIntervalMultiplier); |
| if (networkList != null) { |
| dest.writeInt(networkList.length); |
| for (int i = 0; i < networkList.length; i++) { |
| dest.writeString(networkList[i].ssid); |
| dest.writeByte(networkList[i].flags); |
| dest.writeByte(networkList[i].authBitField); |
| dest.writeIntArray(networkList[i].frequencies); |
| } |
| } else { |
| dest.writeInt(0); |
| } |
| } |
| |
| /** Implement the Parcelable interface {@hide} */ |
| public static final @NonNull Creator<PnoSettings> CREATOR = |
| new Creator<PnoSettings>() { |
| public PnoSettings createFromParcel(Parcel in) { |
| PnoSettings settings = new PnoSettings(); |
| settings.isConnected = in.readInt() == 1; |
| settings.min5GHzRssi = in.readInt(); |
| settings.min24GHzRssi = in.readInt(); |
| settings.min6GHzRssi = in.readInt(); |
| settings.scanIterations = in.readInt(); |
| settings.scanIntervalMultiplier = in.readInt(); |
| int numNetworks = in.readInt(); |
| settings.networkList = new PnoNetwork[numNetworks]; |
| for (int i = 0; i < numNetworks; i++) { |
| String ssid = in.readString(); |
| PnoNetwork network = new PnoNetwork(ssid); |
| network.flags = in.readByte(); |
| network.authBitField = in.readByte(); |
| network.frequencies = in.createIntArray(); |
| settings.networkList[i] = network; |
| } |
| return settings; |
| } |
| |
| public PnoSettings[] newArray(int size) { |
| return new PnoSettings[size]; |
| } |
| }; |
| |
| } |
| |
| /** |
| * interface to get scan events on; specify this on {@link #startBackgroundScan} or |
| * {@link #startScan} |
| */ |
| public interface ScanListener extends ActionListener { |
| /** |
| * Framework co-ordinates scans across multiple apps; so it may not give exactly the |
| * same period requested. If period of a scan is changed; it is reported by this event. |
| * @deprecated Background scan support has always been hardware vendor dependent. This |
| * support may not be present on newer devices. Use {@link #startScan(ScanSettings, |
| * ScanListener)} instead for single scans. |
| */ |
| @Deprecated |
| public void onPeriodChanged(int periodInMs); |
| /** |
| * reports results retrieved from background scan and single shot scans |
| */ |
| public void onResults(ScanData[] results); |
| /** |
| * reports full scan result for each access point found in scan |
| */ |
| public void onFullResult(ScanResult fullScanResult); |
| } |
| |
| /** |
| * interface to get PNO scan events on; specify this on {@link #startDisconnectedPnoScan} and |
| * {@link #startConnectedPnoScan}. |
| * {@hide} |
| */ |
| public interface PnoScanListener extends ScanListener { |
| /** |
| * Invoked when one of the PNO networks are found in scan results. |
| */ |
| void onPnoNetworkFound(ScanResult[] results); |
| } |
| |
| /** |
| * Enable/Disable wifi scanning. |
| * |
| * @param enable set to true to enable scanning, set to false to disable all types of scanning. |
| * |
| * @see WifiManager#ACTION_WIFI_SCAN_AVAILABILITY_CHANGED |
| * {@hide} |
| */ |
| @SystemApi |
| @RequiresPermission(Manifest.permission.NETWORK_STACK) |
| public void setScanningEnabled(boolean enable) { |
| try { |
| mService.setScanningEnabled(enable, Process.myTid(), mContext.getOpPackageName()); |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** |
| * Register a listener that will receive results from all single scans. |
| * Either the {@link ScanListener#onSuccess()} or {@link ScanListener#onFailure(int, String)} |
| * method will be called once when the listener is registered. |
| * Afterwards (assuming onSuccess was called), all subsequent single scan results will be |
| * delivered to the listener. It is possible that onFullResult will not be called for all |
| * results of the first scan if the listener was registered during the scan. |
| * <p> |
| * On {@link android.os.Build.VERSION_CODES#TIRAMISU} or above this API can be called by |
| * an app with either {@link android.Manifest.permission#LOCATION_HARDWARE} or |
| * {@link android.Manifest.permission#NETWORK_STACK}. On platform versions prior to |
| * {@link android.os.Build.VERSION_CODES#TIRAMISU}, the caller must have |
| * {@link android.Manifest.permission#NETWORK_STACK}. |
| * |
| * @param executor the Executor on which to run the callback. |
| * @param listener specifies the object to report events to. This object is also treated as a |
| * key for this request, and must also be specified to cancel the request. |
| * Multiple requests should also not share this object. |
| * @throws SecurityException if the caller does not have permission. |
| */ |
| @RequiresPermission(anyOf = { |
| Manifest.permission.LOCATION_HARDWARE, |
| Manifest.permission.NETWORK_STACK}) |
| public void registerScanListener(@NonNull @CallbackExecutor Executor executor, |
| @NonNull ScanListener listener) { |
| Objects.requireNonNull(executor, "executor cannot be null"); |
| Objects.requireNonNull(listener, "listener cannot be null"); |
| ServiceListener serviceListener = new ServiceListener(listener, executor); |
| if (!addListener(listener, serviceListener)) { |
| Binder.clearCallingIdentity(); |
| executor.execute(() -> |
| // TODO: fix the typo in WifiScanner system API. |
| listener.onFailure(REASON_DUPLICATE_REQEUST, // NOTYPO |
| "Outstanding request with same key not stopped yet")); |
| return; |
| } |
| try { |
| mService.registerScanListener(serviceListener, |
| mContext.getOpPackageName(), |
| mContext.getAttributionTag()); |
| } catch (RemoteException e) { |
| Log.e(TAG, "Failed to register listener " + listener); |
| removeListener(listener); |
| throw e.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** |
| * Overload of {@link #registerScanListener(Executor, ScanListener)} that executes the callback |
| * synchronously. |
| * @hide |
| */ |
| @RequiresPermission(Manifest.permission.NETWORK_STACK) |
| public void registerScanListener(@NonNull ScanListener listener) { |
| registerScanListener(new SynchronousExecutor(), listener); |
| } |
| |
| /** |
| * Deregister a listener for ongoing single scans |
| * @param listener specifies which scan to cancel; must be same object as passed in {@link |
| * #registerScanListener} |
| */ |
| public void unregisterScanListener(@NonNull ScanListener listener) { |
| Objects.requireNonNull(listener, "listener cannot be null"); |
| ServiceListener serviceListener = getServiceListener(listener); |
| if (serviceListener == null) { |
| Log.e(TAG, "listener does not exist"); |
| return; |
| } |
| try { |
| mService.unregisterScanListener(serviceListener, mContext.getOpPackageName(), |
| mContext.getAttributionTag()); |
| } catch (RemoteException e) { |
| Log.e(TAG, "failed to unregister listener"); |
| throw e.rethrowFromSystemServer(); |
| } finally { |
| removeListener(listener); |
| } |
| } |
| |
| /** |
| * Check whether the Wi-Fi subsystem has started a scan and is waiting for scan results. |
| * @return true if a scan initiated via |
| * {@link WifiScanner#startScan(ScanSettings, ScanListener)} or |
| * {@link WifiManager#startScan()} is in progress. |
| * false if there is currently no scanning initiated by {@link WifiScanner} or |
| * {@link WifiManager}, but it's still possible the wifi radio is scanning for |
| * another reason. |
| * @hide |
| */ |
| @SystemApi |
| @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) |
| public boolean isScanning() { |
| try { |
| return mService.isScanning(); |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** start wifi scan in background |
| * @param settings specifies various parameters for the scan; for more information look at |
| * {@link ScanSettings} |
| * @param listener specifies the object to report events to. This object is also treated as a |
| * key for this scan, and must also be specified to cancel the scan. Multiple |
| * scans should also not share this object. |
| */ |
| @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) |
| public void startBackgroundScan(ScanSettings settings, ScanListener listener) { |
| startBackgroundScan(settings, listener, null); |
| } |
| |
| /** start wifi scan in background |
| * @param settings specifies various parameters for the scan; for more information look at |
| * {@link ScanSettings} |
| * @param workSource WorkSource to blame for power usage |
| * @param listener specifies the object to report events to. This object is also treated as a |
| * key for this scan, and must also be specified to cancel the scan. Multiple |
| * scans should also not share this object. |
| * @deprecated Background scan support has always been hardware vendor dependent. This support |
| * may not be present on newer devices. Use {@link #startScan(ScanSettings, ScanListener)} |
| * instead for single scans. |
| */ |
| @Deprecated |
| @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) |
| public void startBackgroundScan(ScanSettings settings, ScanListener listener, |
| WorkSource workSource) { |
| Objects.requireNonNull(listener, "listener cannot be null"); |
| if (getServiceListener(listener) != null) return; |
| ServiceListener serviceListener = new ServiceListener(listener, new SynchronousExecutor()); |
| if (!addListener(listener, serviceListener)) { |
| Log.e(TAG, "listener already exist!"); |
| return; |
| } |
| try { |
| mService.startBackgroundScan(serviceListener, settings, workSource, |
| mContext.getOpPackageName(), mContext.getAttributionTag()); |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** |
| * stop an ongoing wifi scan |
| * @param listener specifies which scan to cancel; must be same object as passed in {@link |
| * #startBackgroundScan} |
| * @deprecated Background scan support has always been hardware vendor dependent. This support |
| * may not be present on newer devices. Use {@link #startScan(ScanSettings, ScanListener)} |
| * instead for single scans. |
| */ |
| @Deprecated |
| @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) |
| public void stopBackgroundScan(ScanListener listener) { |
| Objects.requireNonNull(listener, "listener cannot be null"); |
| ServiceListener serviceListener = getServiceListener(listener); |
| if (serviceListener == null) { |
| Log.e(TAG, "listener does not exist"); |
| return; |
| } |
| try { |
| mService.stopBackgroundScan(serviceListener, mContext.getOpPackageName(), |
| mContext.getAttributionTag()); |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } finally { |
| removeListener(listener); |
| } |
| } |
| |
| /** |
| * reports currently available scan results on appropriate listeners |
| * @return true if all scan results were reported correctly |
| * @deprecated Background scan support has always been hardware vendor dependent. This support |
| * may not be present on newer devices. Use {@link #startScan(ScanSettings, ScanListener)} |
| * instead for single scans. |
| */ |
| @Deprecated |
| @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) |
| public boolean getScanResults() { |
| try { |
| return mService.getScanResults(mContext.getOpPackageName(), |
| mContext.getAttributionTag()); |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** |
| * starts a single scan and reports results asynchronously |
| * @param settings specifies various parameters for the scan; for more information look at |
| * {@link ScanSettings} |
| * @param listener specifies the object to report events to. This object is also treated as a |
| * key for this scan, and must also be specified to cancel the scan. Multiple |
| * scans should also not share this object. |
| */ |
| @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) |
| public void startScan(ScanSettings settings, ScanListener listener) { |
| startScan(settings, listener, null); |
| } |
| |
| /** |
| * starts a single scan and reports results asynchronously |
| * @param settings specifies various parameters for the scan; for more information look at |
| * {@link ScanSettings} |
| * @param listener specifies the object to report events to. This object is also treated as a |
| * key for this scan, and must also be specified to cancel the scan. Multiple |
| * scans should also not share this object. |
| * @param workSource WorkSource to blame for power usage |
| */ |
| @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) |
| public void startScan(ScanSettings settings, ScanListener listener, WorkSource workSource) { |
| startScan(settings, new SynchronousExecutor(), listener, workSource); |
| } |
| |
| /** |
| * starts a single scan and reports results asynchronously |
| * @param settings specifies various parameters for the scan; for more information look at |
| * {@link ScanSettings} |
| * @param executor the Executor on which to run the callback. |
| * @param listener specifies the object to report events to. This object is also treated as a |
| * key for this scan, and must also be specified to cancel the scan. Multiple |
| * scans should also not share this object. |
| * @param workSource WorkSource to blame for power usage |
| * @hide |
| */ |
| @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) |
| public void startScan(ScanSettings settings, @Nullable @CallbackExecutor Executor executor, |
| ScanListener listener, WorkSource workSource) { |
| Objects.requireNonNull(listener, "listener cannot be null"); |
| if (getServiceListener(listener) != null) return; |
| ServiceListener serviceListener = new ServiceListener(listener, executor); |
| if (!addListener(listener, serviceListener)) { |
| Log.e(TAG, "listener already exist!"); |
| return; |
| } |
| try { |
| mService.startScan(serviceListener, settings, workSource, |
| mContext.getOpPackageName(), |
| mContext.getAttributionTag()); |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** |
| * stops an ongoing single shot scan; only useful after {@link #startScan} if onResults() |
| * hasn't been called on the listener, ignored otherwise |
| * @param listener |
| */ |
| @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) |
| public void stopScan(ScanListener listener) { |
| Objects.requireNonNull(listener, "listener cannot be null"); |
| ServiceListener serviceListener = getServiceListener(listener); |
| if (serviceListener == null) { |
| Log.e(TAG, "listener does not exist"); |
| return; |
| } |
| try { |
| mService.stopScan(serviceListener, mContext.getOpPackageName(), |
| mContext.getAttributionTag()); |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } finally { |
| removeListener(listener); |
| } |
| } |
| |
| /** |
| * Retrieve the most recent scan results from a single scan request. |
| * |
| * <p> |
| * When an Access Point’s beacon or probe response includes a Multi-BSSID Element, the |
| * returned scan results should include separate scan result for each BSSID within the |
| * Multi-BSSID Information Element. This includes both transmitted and non-transmitted BSSIDs. |
| * Original Multi-BSSID Element will be included in the Information Elements attached to |
| * each of the scan results. |
| * Note: This is the expected behavior for devices supporting 11ax (WiFi-6) and above, and an |
| * optional requirement for devices running with older WiFi generations. |
| * </p> |
| */ |
| @NonNull |
| @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) |
| public List<ScanResult> getSingleScanResults() { |
| try { |
| return mService.getSingleScanResults(mContext.getPackageName(), |
| mContext.getAttributionTag()); |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** |
| * Retrieve the scan data cached by the hardware. |
| * |
| * <p> |
| * When an Access Point’s beacon or probe response includes a Multi-BSSID Element, the |
| * returned scan results should include separate scan result for each BSSID within the |
| * Multi-BSSID Information Element. This includes both transmitted and non-transmitted BSSIDs. |
| * Original Multi-BSSID Element will be included in the Information Elements attached to |
| * each of the scan results. |
| * Note: This is the expected behavior for devices supporting 11ax (WiFi-6) and above, and an |
| * optional requirement for devices running with older WiFi generations. |
| * </p> |
| * |
| * @param executor The executor on which callback will be invoked. |
| * @param resultsCallback An asynchronous callback that will return the cached scan data. |
| * |
| * @throws UnsupportedOperationException if the API is not supported on this SDK version. |
| * @throws SecurityException if the caller does not have permission. |
| * @throws NullPointerException if the caller provided invalid inputs. |
| */ |
| @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) |
| @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM) |
| @RequiresPermission(allOf = {ACCESS_FINE_LOCATION, LOCATION_HARDWARE}) |
| public void getCachedScanData(@NonNull @CallbackExecutor Executor executor, |
| @NonNull Consumer<ScanData> resultsCallback) { |
| Objects.requireNonNull(executor, "executor cannot be null"); |
| Objects.requireNonNull(resultsCallback, "resultsCallback cannot be null"); |
| try { |
| mService.getCachedScanData(mContext.getPackageName(), |
| mContext.getAttributionTag(), |
| new IScanDataListener.Stub() { |
| @Override |
| public void onResult(@NonNull ScanData scanData) { |
| Binder.clearCallingIdentity(); |
| executor.execute(() -> { |
| resultsCallback.accept(scanData); |
| }); |
| } |
| }); |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } |
| } |
| |
| |
| private void startPnoScan(PnoScanListener listener, Executor executor, |
| ScanSettings scanSettings, PnoSettings pnoSettings) { |
| // Set the PNO scan flag. |
| scanSettings.isPnoScan = true; |
| if (getServiceListener(listener) != null) return; |
| ServiceListener serviceListener = new ServiceListener(listener, executor); |
| if (!addListener(listener, serviceListener)) { |
| Log.w(TAG, "listener already exist!"); |
| } |
| try { |
| mService.startPnoScan(serviceListener, scanSettings, pnoSettings, |
| mContext.getOpPackageName(), |
| mContext.getAttributionTag()); |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** |
| * Start wifi connected PNO scan |
| * @param scanSettings specifies various parameters for the scan; for more information look at |
| * {@link ScanSettings} |
| * @param pnoSettings specifies various parameters for PNO; for more information look at |
| * {@link PnoSettings} |
| * @param executor the Executor on which to run the callback. |
| * @param listener specifies the object to report events to. This object is also treated as a |
| * key for this scan, and must also be specified to cancel the scan. Multiple |
| * scans should also not share this object. |
| * {@hide} |
| */ |
| public void startConnectedPnoScan(ScanSettings scanSettings, PnoSettings pnoSettings, |
| @NonNull @CallbackExecutor Executor executor, PnoScanListener listener) { |
| Objects.requireNonNull(listener, "listener cannot be null"); |
| Objects.requireNonNull(pnoSettings, "pnoSettings cannot be null"); |
| pnoSettings.isConnected = true; |
| startPnoScan(listener, executor, scanSettings, pnoSettings); |
| } |
| /** |
| * Start wifi disconnected PNO scan |
| * @param scanSettings specifies various parameters for the scan; for more information look at |
| * {@link ScanSettings} |
| * @param pnoSettings specifies various parameters for PNO; for more information look at |
| * {@link PnoSettings} |
| * @param listener specifies the object to report events to. This object is also treated as a |
| * key for this scan, and must also be specified to cancel the scan. Multiple |
| * scans should also not share this object. |
| * {@hide} |
| */ |
| @RequiresPermission(android.Manifest.permission.NETWORK_STACK) |
| public void startDisconnectedPnoScan(ScanSettings scanSettings, PnoSettings pnoSettings, |
| @NonNull @CallbackExecutor Executor executor, PnoScanListener listener) { |
| Objects.requireNonNull(listener, "listener cannot be null"); |
| Objects.requireNonNull(pnoSettings, "pnoSettings cannot be null"); |
| pnoSettings.isConnected = false; |
| startPnoScan(listener, executor, scanSettings, pnoSettings); |
| } |
| /** |
| * Stop an ongoing wifi PNO scan |
| * @param listener specifies which scan to cancel; must be same object as passed in {@link |
| * #startPnoScan} |
| * {@hide} |
| */ |
| @RequiresPermission(android.Manifest.permission.NETWORK_STACK) |
| public void stopPnoScan(ScanListener listener) { |
| Objects.requireNonNull(listener, "listener cannot be null"); |
| ServiceListener serviceListener = getServiceListener(listener); |
| if (serviceListener == null) { |
| Log.e(TAG, "listener does not exist"); |
| return; |
| } |
| try { |
| mService.stopPnoScan(serviceListener, mContext.getOpPackageName(), |
| mContext.getAttributionTag()); |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } finally { |
| removeListener(listener); |
| } |
| } |
| |
| /** |
| * Enable verbose logging. For internal use by wifi framework only. |
| * @param enabled whether verbose logging is enabled |
| * @hide |
| */ |
| @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) |
| public void enableVerboseLogging(boolean enabled) { |
| try { |
| mService.enableVerboseLogging(enabled); |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** specifies information about an access point of interest */ |
| @Deprecated |
| public static class BssidInfo { |
| /** bssid of the access point; in XX:XX:XX:XX:XX:XX format */ |
| public String bssid; |
| /** low signal strength threshold; more information at {@link ScanResult#level} */ |
| public int low; /* minimum RSSI */ |
| /** high signal threshold; more information at {@link ScanResult#level} */ |
| public int high; /* maximum RSSI */ |
| /** channel frequency (in KHz) where you may find this BSSID */ |
| public int frequencyHint; |
| } |
| |
| /** @hide */ |
| @SystemApi |
| @Deprecated |
| public static class WifiChangeSettings implements Parcelable { |
| public int rssiSampleSize; /* sample size for RSSI averaging */ |
| public int lostApSampleSize; /* samples to confirm AP's loss */ |
| public int unchangedSampleSize; /* samples to confirm no change */ |
| public int minApsBreachingThreshold; /* change threshold to trigger event */ |
| public int periodInMs; /* scan period in millisecond */ |
| public BssidInfo[] bssidInfos; |
| |
| /** Implement the Parcelable interface {@hide} */ |
| public int describeContents() { |
| return 0; |
| } |
| |
| /** Implement the Parcelable interface {@hide} */ |
| public void writeToParcel(Parcel dest, int flags) { |
| } |
| |
| /** Implement the Parcelable interface {@hide} */ |
| public static final @NonNull Creator<WifiChangeSettings> CREATOR = |
| new Creator<WifiChangeSettings>() { |
| public WifiChangeSettings createFromParcel(Parcel in) { |
| return new WifiChangeSettings(); |
| } |
| |
| public WifiChangeSettings[] newArray(int size) { |
| return new WifiChangeSettings[size]; |
| } |
| }; |
| |
| } |
| |
| /** configure WifiChange detection |
| * @param rssiSampleSize number of samples used for RSSI averaging |
| * @param lostApSampleSize number of samples to confirm an access point's loss |
| * @param unchangedSampleSize number of samples to confirm there are no changes |
| * @param minApsBreachingThreshold minimum number of access points that need to be |
| * out of range to detect WifiChange |
| * @param periodInMs indicates period of scan to find changes |
| * @param bssidInfos access points to watch |
| */ |
| @Deprecated |
| @SuppressLint("RequiresPermission") |
| public void configureWifiChange( |
| int rssiSampleSize, /* sample size for RSSI averaging */ |
| int lostApSampleSize, /* samples to confirm AP's loss */ |
| int unchangedSampleSize, /* samples to confirm no change */ |
| int minApsBreachingThreshold, /* change threshold to trigger event */ |
| int periodInMs, /* period of scan */ |
| BssidInfo[] bssidInfos /* signal thresholds to cross */ |
| ) |
| { |
| throw new UnsupportedOperationException(); |
| } |
| |
| /** |
| * interface to get wifi change events on; use this on {@link #startTrackingWifiChange} |
| */ |
| @Deprecated |
| public interface WifiChangeListener extends ActionListener { |
| /** indicates that changes were detected in wifi environment |
| * @param results indicate the access points that exhibited change |
| */ |
| public void onChanging(ScanResult[] results); /* changes are found */ |
| /** indicates that no wifi changes are being detected for a while |
| * @param results indicate the access points that are bing monitored for change |
| */ |
| public void onQuiescence(ScanResult[] results); /* changes settled down */ |
| } |
| |
| /** |
| * track changes in wifi environment |
| * @param listener object to report events on; this object must be unique and must also be |
| * provided on {@link #stopTrackingWifiChange} |
| */ |
| @Deprecated |
| @SuppressLint("RequiresPermission") |
| public void startTrackingWifiChange(WifiChangeListener listener) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| /** |
| * stop tracking changes in wifi environment |
| * @param listener object that was provided to report events on {@link |
| * #stopTrackingWifiChange} |
| */ |
| @Deprecated |
| @SuppressLint("RequiresPermission") |
| public void stopTrackingWifiChange(WifiChangeListener listener) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| /** @hide */ |
| @SystemApi |
| @Deprecated |
| @SuppressLint("RequiresPermission") |
| public void configureWifiChange(WifiChangeSettings settings) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| /** interface to receive hotlist events on; use this on {@link #setHotlist} */ |
| @Deprecated |
| public static interface BssidListener extends ActionListener { |
| /** indicates that access points were found by on going scans |
| * @param results list of scan results, one for each access point visible currently |
| */ |
| public void onFound(ScanResult[] results); |
| /** indicates that access points were missed by on going scans |
| * @param results list of scan results, for each access point that is not visible anymore |
| */ |
| public void onLost(ScanResult[] results); |
| } |
| |
| /** @hide */ |
| @SystemApi |
| @Deprecated |
| public static class HotlistSettings implements Parcelable { |
| public BssidInfo[] bssidInfos; |
| public int apLostThreshold; |
| |
| /** Implement the Parcelable interface {@hide} */ |
| public int describeContents() { |
| return 0; |
| } |
| |
| /** Implement the Parcelable interface {@hide} */ |
| public void writeToParcel(Parcel dest, int flags) { |
| } |
| |
| /** Implement the Parcelable interface {@hide} */ |
| public static final @NonNull Creator<HotlistSettings> CREATOR = |
| new Creator<HotlistSettings>() { |
| public HotlistSettings createFromParcel(Parcel in) { |
| HotlistSettings settings = new HotlistSettings(); |
| return settings; |
| } |
| |
| public HotlistSettings[] newArray(int size) { |
| return new HotlistSettings[size]; |
| } |
| }; |
| } |
| |
| /** |
| * set interesting access points to find |
| * @param bssidInfos access points of interest |
| * @param apLostThreshold number of scans needed to indicate that AP is lost |
| * @param listener object provided to report events on; this object must be unique and must |
| * also be provided on {@link #stopTrackingBssids} |
| */ |
| @Deprecated |
| @SuppressLint("RequiresPermission") |
| public void startTrackingBssids(BssidInfo[] bssidInfos, |
| int apLostThreshold, BssidListener listener) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| /** |
| * remove tracking of interesting access points |
| * @param listener same object provided in {@link #startTrackingBssids} |
| */ |
| @Deprecated |
| @SuppressLint("RequiresPermission") |
| public void stopTrackingBssids(BssidListener listener) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| |
| /* private members and methods */ |
| |
| private static final String TAG = "WifiScanner"; |
| private static final boolean DBG = false; |
| |
| /* commands for Wifi Service */ |
| private static final int BASE = Protocol.BASE_WIFI_SCANNER; |
| |
| /** @hide */ |
| public static final int CMD_START_BACKGROUND_SCAN = BASE + 2; |
| /** @hide */ |
| public static final int CMD_STOP_BACKGROUND_SCAN = BASE + 3; |
| /** @hide */ |
| public static final int CMD_GET_SCAN_RESULTS = BASE + 4; |
| /** @hide */ |
| public static final int CMD_SCAN_RESULT = BASE + 5; |
| /** @hide */ |
| public static final int CMD_CACHED_SCAN_DATA = BASE + 6; |
| /** @hide */ |
| public static final int CMD_OP_SUCCEEDED = BASE + 17; |
| /** @hide */ |
| public static final int CMD_OP_FAILED = BASE + 18; |
| /** @hide */ |
| public static final int CMD_FULL_SCAN_RESULT = BASE + 20; |
| /** @hide */ |
| public static final int CMD_START_SINGLE_SCAN = BASE + 21; |
| /** @hide */ |
| public static final int CMD_STOP_SINGLE_SCAN = BASE + 22; |
| /** @hide */ |
| public static final int CMD_SINGLE_SCAN_COMPLETED = BASE + 23; |
| /** @hide */ |
| public static final int CMD_START_PNO_SCAN = BASE + 24; |
| /** @hide */ |
| public static final int CMD_STOP_PNO_SCAN = BASE + 25; |
| /** @hide */ |
| public static final int CMD_PNO_NETWORK_FOUND = BASE + 26; |
| /** @hide */ |
| public static final int CMD_REGISTER_SCAN_LISTENER = BASE + 27; |
| /** @hide */ |
| public static final int CMD_DEREGISTER_SCAN_LISTENER = BASE + 28; |
| /** @hide */ |
| public static final int CMD_GET_SINGLE_SCAN_RESULTS = BASE + 29; |
| /** @hide */ |
| public static final int CMD_ENABLE = BASE + 30; |
| /** @hide */ |
| public static final int CMD_DISABLE = BASE + 31; |
| |
| private Context mContext; |
| private IWifiScanner mService; |
| |
| private final Object mListenerMapLock = new Object(); |
| private final Map<ActionListener, ServiceListener> mListenerMap = new HashMap<>(); |
| |
| /** |
| * Create a new WifiScanner instance. |
| * Applications will almost always want to use |
| * {@link android.content.Context#getSystemService Context.getSystemService()} to retrieve |
| * the standard {@link android.content.Context#WIFI_SERVICE Context.WIFI_SERVICE}. |
| * |
| * @param context the application context |
| * @param service the Binder interface for {@link Context#WIFI_SCANNING_SERVICE} |
| * @param looper the Looper used to deliver callbacks |
| * |
| * @hide |
| */ |
| public WifiScanner(@NonNull Context context, @NonNull IWifiScanner service, |
| @NonNull Looper looper) { |
| mContext = context; |
| mService = service; |
| } |
| |
| // Add a listener into listener map. If the listener already exists, return INVALID_KEY and |
| // send an error message to internal handler; Otherwise add the listener to the listener map and |
| // return the key of the listener. |
| private boolean addListener(ActionListener listener, ServiceListener serviceListener) { |
| synchronized (mListenerMapLock) { |
| boolean keyExists = mListenerMap.containsKey(listener); |
| // Note we need to put the listener into listener map even if it's a duplicate as the |
| // internal handler will need the key to find the listener. In case of duplicates, |
| // removing duplicate key logic will be handled in internal handler. |
| if (keyExists) { |
| if (DBG) Log.d(TAG, "listener key already exists"); |
| return false; |
| } |
| mListenerMap.put(listener, serviceListener); |
| return true; |
| } |
| } |
| |
| private ServiceListener getServiceListener(ActionListener listener) { |
| if (listener == null) return null; |
| synchronized (mListenerMapLock) { |
| return mListenerMap.get(listener); |
| } |
| } |
| |
| private void removeListener(ActionListener listener) { |
| if (listener == null) return; |
| synchronized (mListenerMapLock) { |
| mListenerMap.remove(listener); |
| } |
| } |
| |
| } |