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:
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.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.
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 successfulSCAN_RESULTS_AVAILABLE_ACTIONbroadcast.
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:
- Your app has the
ACCESS_FINE_LOCATIONorACCESS_COARSE_LOCATIONpermission. - Your app has the
CHANGE_WIFI_STATEpermission. - Location services are enabled on the device (under Settings > Location).
Android 10 (API level 29) and higher:
A successful call to
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_LOCATIONpermission. - If your app is targeting SDK lower than Android 10 (API level 29), your app
has the
ACCESS_COARSE_LOCATIONorACCESS_FINE_LOCATIONpermission. - Your app has the
CHANGE_WIFI_STATEpermission. - Location services are enabled on the device (under Settings > Location).
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_LOCATIONpermission. - If your app is targeting SDK lower than Android 10 (API level 29), your app
has the
ACCESS_COARSE_LOCATIONorACCESS_FINE_LOCATIONpermission. - Your app has the
ACCESS_WIFI_STATEpermission. - 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).

