You can use the Wi-Fi scanning capabilities provided by the
[WifiManager API](https://developer.android.com/reference/android/net/wifi/WifiManager) to get a list of
Wi-Fi access points that are visible from the device.

## Wi-Fi scanning process

There are three steps to the scanning process:

1. **Register a broadcast listener** for
   [`SCAN_RESULTS_AVAILABLE_ACTION`](https://developer.android.com/reference/android/net/wifi/WifiManager#SCAN_RESULTS_AVAILABLE_ACTION),
   which is called when scan requests are completed, providing their
   success/failure status. For devices running Android 10 (API level 29) and higher, this
   broadcast will be sent for any full Wi-Fi scan performed on the device by
   the platform or other apps. Apps can passively listen to all scan
   completions on device by using the broadcast without issuing a scan of their
   own.

2. **Request a scan** using
   [`WifiManager.startScan()`](https://developer.android.com/reference/android/net/wifi/WifiManager#startScan()).
   Make sure to check the return status of the method, since the call may fail
   for any of the following reasons:

   - Scan requests may be throttled because of too many scans in a short time.
   - The device is idle and scanning is disabled.
   - Wi-Fi hardware reports a scan failure.
3. **Get scan results** using
   [`WifiManager.getScanResults()`](https://developer.android.com/reference/android/net/wifi/WifiManager#getScanResults()).
   The returned scan results are the most recently updated results, which may
   be from a previous scan if your current scan has not completed or succeeded.
   This means that you might get older scan results if you call this method
   before receiving a successful
   [`SCAN_RESULTS_AVAILABLE_ACTION`](https://developer.android.com/reference/android/net/wifi/WifiManager#SCAN_RESULTS_AVAILABLE_ACTION)
   broadcast.

The following code provides an example of how to implement these steps:  

### Kotlin

```kotlin
val wifiManager = context.getSystemService(Context.WIFI_SERVICE) as WifiManager

val wifiScanReceiver = object : BroadcastReceiver() {

  override fun onReceive(context: Context, intent: Intent) {
    val success = intent.getBooleanExtra(WifiManager.EXTRA_RESULTS_UPDATED, false)
    if (success) {
      scanSuccess()
    } else {
      scanFailure()
    }
  }
}

val intentFilter = IntentFilter()
intentFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)
context.registerReceiver(wifiScanReceiver, intentFilter)

val success = wifiManager.startScan()
if (!success) {
  // scan failure handling
  scanFailure()
}

....

private fun scanSuccess() {
  val results = wifiManager.scanResults
  ... use new scan results ...
}

private fun scanFailure() {
  // handle failure: new scan did NOT succeed
  // consider using old scan results: these are the OLD results!
  val results = wifiManager.scanResults
  ... potentially use older scan results ...
}
```

### Java

```java
WifiManager wifiManager = (WifiManager)
                   context.getSystemService(Context.WIFI_SERVICE);

BroadcastReceiver wifiScanReceiver = new BroadcastReceiver() {
  @Override
  public void onReceive(Context c, Intent intent) {
    boolean success = intent.getBooleanExtra(
                       WifiManager.EXTRA_RESULTS_UPDATED, false);
    if (success) {
      scanSuccess();
    } else {
      // scan failure handling
      scanFailure();
    }
  }
};

IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
context.registerReceiver(wifiScanReceiver, intentFilter);

boolean success = wifiManager.startScan();
if (!success) {
  // scan failure handling
  scanFailure();
}

....

private void scanSuccess() {
  List<ScanResult> results = wifiManager.getScanResults();
  ... use new scan results ...
}

private void scanFailure() {
  // handle failure: new scan did NOT succeed
  // consider using old scan results: these are the OLD results!
  List<ScanResult> results = wifiManager.getScanResults();
  ... potentially use older scan results ...
}
```

## Restrictions

Android 8.0 (API level 26) introduced restrictions regarding permissions and the
allowed frequency of Wi-Fi scans.

To improve network performance, security, and battery life, Android 9 (API
level 28) tightened permission requirements and further limited the frequency of
Wi-Fi scans.

### Permissions

| **Note:** In each of the following sections that mention location permissions or location-gathering logic, keep in mind that, when your app is running in the background, [access to location](https://developer.android.com/training/location/background) should be critical to the core functionality of the app and is accompanied with proper disclosure to users.

**Android 8.0 and Android 8.1:**

A successful call to
[`WifiManager.getScanResults()`](https://developer.android.com/reference/android/net/wifi/WifiManager#getScanResults())
requires *any one* of the following permissions:

- [`ACCESS_FINE_LOCATION`](https://developer.android.com/reference/android/Manifest.permission#ACCESS_FINE_LOCATION)
- [`ACCESS_COARSE_LOCATION`](https://developer.android.com/reference/android/Manifest.permission#ACCESS_COARSE_LOCATION)
- [`CHANGE_WIFI_STATE`](https://developer.android.com/reference/android/Manifest.permission#CHANGE_WIFI_STATE)

If the calling app does not have any of these permissions, the call fails with a
[`SecurityException`](https://developer.android.com/reference/java/lang/SecurityException).

Alternatively, on devices running Android 8.0 (API level 26) and higher, you can
use the
[`CompanionDeviceManager`](https://developer.android.com/reference/android/companion/CompanionDeviceManager)
to perform a scan of nearby companion devices on behalf of your app without
requiring the location permission. For more on this option, see
[Companion device
pairing](https://developer.android.com/develop/connectivity/bluetooth/companion-device-pairing).

**Android 9:**

A successful call to
[`WifiManager.startScan()`](https://developer.android.com/reference/android/net/wifi/WifiManager#startScan())
requires *all* of the following conditions to be met:

- Your app has the [`ACCESS_FINE_LOCATION`](https://developer.android.com/reference/android/Manifest.permission#ACCESS_FINE_LOCATION) **or** [`ACCESS_COARSE_LOCATION`](https://developer.android.com/reference/android/Manifest.permission#ACCESS_COARSE_LOCATION) permission.
- Your app has the [`CHANGE_WIFI_STATE`](https://developer.android.com/reference/android/Manifest.permission#CHANGE_WIFI_STATE) permission.
- Location services are enabled on the device (under **Settings \> Location**).

**Android 10 (API level 29) and higher:**

A successful call to
[`WifiManager.startScan()`](https://developer.android.com/reference/android/net/wifi/WifiManager#startScan())
requires *all* of the following conditions to be met:

- If your app is targeting Android 10 (API level 29) SDK or higher, your app has the [`ACCESS_FINE_LOCATION`](https://developer.android.com/reference/android/Manifest.permission#ACCESS_FINE_LOCATION) permission.
- If your app is targeting SDK lower than Android 10 (API level 29), your app has the [`ACCESS_COARSE_LOCATION`](https://developer.android.com/reference/android/Manifest.permission#ACCESS_COARSE_LOCATION) or [`ACCESS_FINE_LOCATION`](https://developer.android.com/reference/android/Manifest.permission#ACCESS_FINE_LOCATION) permission.
- Your app has the [`CHANGE_WIFI_STATE`](https://developer.android.com/reference/android/Manifest.permission#CHANGE_WIFI_STATE) permission.
- Location services are enabled on the device (under **Settings** \> **Location**).

To successfully call
[`WifiManager.getScanResults()`](https://developer.android.com/reference/android/net/wifi/WifiManager#getScanResults()),
ensure *all* of the following conditions are met:

- If your app is targeting Android 10 (API level 29) SDK or higher, your app has the `ACCESS_FINE_LOCATION` permission.
- If your app is targeting SDK lower than Android 10 (API level 29), your app has the `ACCESS_COARSE_LOCATION` or `ACCESS_FINE_LOCATION` permission.
- Your app has the [`ACCESS_WIFI_STATE`](https://developer.android.com/reference/android/Manifest.permission#ACCESS_WIFI_STATE) permission.
- Location services are enabled on the device (under **Settings** \> **Location**).

If the calling app doesn't meet all of these requirements, the call fails with
a [`SecurityException`](https://developer.android.com/reference/java/lang/SecurityException).

### Throttling

The following limitations apply to the frequency of scans using
[`WifiManager.startScan()`](https://developer.android.com/reference/android/net/wifi/WifiManager#startScan()).

**Android 8.0 and Android 8.1:**

Each background app can scan one time in a 30-minute period.

**Android 9:**

Each foreground app can scan four times in a 2-minute period. This allows for a
burst of scans in a short time.

All background apps combined can scan one time in a 30-minute period.

**Android 10 and higher:**

The same throttling limits from Android 9 apply. There is a new developer option
to toggle the throttling off for local testing (under **Developer Options \>**
**Networking \> Wi-Fi scan throttling**).