Wi-Fi scanning overview

You can use the Wi-Fi scanning capabilities provided by the WifiManager API 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, 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(). 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(). 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 broadcast.

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

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

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

Android 8.0 and Android 8.1:

A successful call to WifiManager.getScanResults() requires any one of the following permissions:

If the calling app does not have any of these permissions, the call fails with a SecurityException.

Alternatively, on devices running Android 8.0 (API level 26) and higher, you can use the 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.

Android 9:

A successful call to WifiManager.startScan() requires all of the following conditions to be met:

Android 10 (API level 29) and higher:

A successful call to WifiManager.startScan() requires all of the following conditions to be met:

To successfully call 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 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.

Throttling

The following limitations apply to the frequency of scans using 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).