Search code examples
androidbroadcastreceiverandroid-wifiandroid-broadcastwifimanager

Android Wifi Scan - BroadcastReceiver for SCAN_RESULTS_AVAILABLE_ACTION not getting called


Here is my code:

public class FloatWifiManager implements IWifiManager {

    private WifiManager wifiManager;

    private BroadcastReceiver wifiScanReceiver;

    public FloatWifiManager(Context context) {
        ...
        wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);

        // Registering Wifi Receiver
        wifiScanReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context c, Intent intent) {
                if (intent.getAction().equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
                    // not getting called, only after running app and manually going to the wifi settings in android
                }
            }
        };

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

I registered the BroadcastReceiver exactly like I saw in all the examples, and did startScan.

What happens is, the wifi list is changing (for sure, I tested), but onReceive is not called if I just stay in the app.

What makes onReceive finally to be called - is to launch the app, leave it running, and going in the android phone to Settings -> Wifi settings. when going there, all of the sudden the List is updating and onReceive is called.

What's the problem here?

  1. Does wifiManager.startScan(); runs the scan only once? or it is a function that keeps listening to incoming "Scan Results"?

  2. And obviously, why does the receiver doesn't get called?


Solution

  • Yes, startScan() requests only one single scan.

    You can get rid of the if (intent.getAction().equals(..)) condition. Anything else seems to be ok.

    just to make it clear - my goal to have a receiver that will get called every time the Wifi networks list are changing, without having to click a "start scan" button.

    AFAIK it is not possible to get notified whenever any of the wifi networks change. You can only request a scan with startScan - and of course you can call startScan repeatedly using a Thread or Handler.

    The docs say that SCAN_RESULTS_AVAILABLE_ACTION is called when "an access point scan has completed, and results are available from the supplicant". How and when a scan is proceeded depends on the implemention of the supplicant. Elenkov writes, that "Android devices rarely include the original wpa_supplicant code; the included implementation is often modified for better compatibility with the underlying SoC".


    Scan for access points

    This example scans for available access points and ad hoc networks. btnScan activates a scan initiated by the WifiManager.startScan() method. After the scan, WifiManager calls the SCAN_RESULTS_AVAILABLE_ACTION intent and the WifiScanReceiver class processes the scan result. The results are displayed in a TextView.

    public class MainActivity extends AppCompatActivity {
    
        private final static String TAG = "MainActivity";
    
        TextView txtWifiInfo;
        WifiManager wifi;
        WifiScanReceiver wifiReceiver;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            wifi=(WifiManager)getSystemService(Context.WIFI_SERVICE);
            wifiReceiver = new WifiScanReceiver();
    
            txtWifiInfo = (TextView)findViewById(R.id.txtWifiInfo);
            Button btnScan = (Button)findViewById(R.id.btnScan);
            btnScan.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Log.i(TAG, "Start scan...");
                    wifi.startScan();
                }
            });
        }
    
        protected void onPause() {
            unregisterReceiver(wifiReceiver);
            super.onPause();
        }
    
        protected void onResume() {
            registerReceiver(
                wifiReceiver, 
                new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)
            );
            super.onResume();
        }
    
        private class WifiScanReceiver extends BroadcastReceiver {
            public void onReceive(Context c, Intent intent) {
                List<ScanResult> wifiScanList = wifi.getScanResults();
                txtWifiInfo.setText("");
                for(int i = 0; i < wifiScanList.size(); i++){
                    String info = ((wifiScanList.get(i)).toString());
                    txtWifiInfo.append(info+"\n\n");
                }
            }
        }
    }
    

    Permissions

    The following permissions need to be defined in AndroidManifest.xml:

    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
    

    android.permission.ACCESS_WIFI_STATE is necessary for calling WifiManager.getScanResults(). Without android.permission.CHANGE_WIFI_STATE you cannot initiate a scan with WifiManager.startScan().

    When compiling the project for api level 23 or greater (Android 6.0 and up), either android.permission.ACCESS_FINE_LOCATION or android.permission.ACCESS_COARSE_LOCATION must be inserted. Furthermore that permission needs to be requested, e.g. in the onCreate method of your main activity:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...
        String[] PERMS_INITIAL={
                Manifest.permission.ACCESS_FINE_LOCATION,
        };
        ActivityCompat.requestPermissions(this, PERMS_INITIAL, 127);
    }