Android11 Wifi turns on, scans and connects

Turn on Wifi

Turn on the Wifi switch. The Wifi switch is WifiEnabler. WifiEnabler implements SwitchWidgetController.OnSwitchChangeListener for monitoring. Turning on/off the switch will call back to

//Handle the state change event of the Switch control
 public boolean onSwitchToggled(boolean isChecked) {
        //Do nothing if called as a result of a state machine event
// When setting the Switch control status through the Switch.setEnabled method, there is no need to disable/allow Wi-Fi again, otherwise it will cause the consequences of circular calls.
        if (mStateMachineEvent) {
            return true;
        }
        // Show toast message if Wi-Fi is not allowed in airplane mode
//If Wi-Fi alone is not allowed to be used in airplane mode, use the Toast message box to prompt
        if (isChecked & amp; & amp; !WirelessUtils.isRadioAllowed(mContext, Settings.Global.RADIO_WIFI)) {
            Toast.makeText(mContext, R.string.wifi_in_airplane_mode, Toast.LENGTH_SHORT).show();
            // Reset switch to off. No infinite check/listener loop.
            mSwitchWidget.setChecked(false);
            return false;
        }
\t\t
        if (isChecked) {
            mMetricsFeatureProvider.action(mContext, SettingsEnums.ACTION_WIFI_ON);
        } else {
            // Log if user was connected at the time of switching off.
            mMetricsFeatureProvider.action(
                mContext, SettingsEnums.ACTION_WIFI_OFF,
                mConnected.get()
            );
        }
//Turn on/off wifi through WifiManager.setWifiEnabled()
        if (!mWifiManager.setWifiEnabled(isChecked)) {
            // Error
            mSwitchWidget.setEnabled(true);
            Toast.makeText(mContext, R.string.wifi_error, Toast.LENGTH_SHORT).show();
        }
        return true;
    }

mWifiManager.setWifiEnabled(isChecked) is used to turn off or turn on Wi-Fi based on the current state of the Switch control.

A mStateMachineEvent variable is used at the beginning of the onCheckedChanged method. When the variable is true, the onCheckedChanged method jumps out directly. In fact, the reason for adding this jump out condition is because the status change of the Switch control can have the following two situations.

  • Click directly on the Switch control.

  • Call the Switch.setChecked method.

Unfortunately, both of the above situations will trigger a call to the onCheckedChanged method. No matter which method changes the status of the Switch control, calling the WifiManager.setWifiEnabled method in onCheckedChanged to set the Wi-Fi status will trigger onCheckedChanged again. code> method call. When calling the onCheckedChanged method again, you need to set the mStateMachineEvent variable value to true, so that the onCheckedChanged method will return immediately at the beginning of execution (if Continuing to execute the following code will cause an infinite loop), and then set the mStateMachineEvent variable value to false. In this way, you can continue to execute the onCheckedChanged method the next time you set the Wi-Fi status.

Set the state of the Switch control using broadcasting

 /packages/apps/Settings/src/com/android/settings/wifi/WifiEnabler.java
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent . getAction ();
        if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) {
            handleWifiStateChanged(mWifiManager.getWifiState());
        } else if (WifiManager.SUPPLICANT_STATE_CHANGED_ACTION.equals(action)) {
            if (!mConnected.get()) {
                handleStateChanged(
                    WifiInfo.getDetailedStateOf(
                        (SupplicantState)
                                intent .getParcelableExtra (WifiManager.EXTRA_NEW_STATE)
                    )
                );
            }
        } else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) {
            NetworkInfo info =(NetworkInfo) intent . getParcelableExtra (
                    WifiManager.EXTRA_NETWORK_INFO);
            mConnected.set(info.isConnected());
            handleStateChanged(info.getDetailedState());
        }
    }
};

For Wi-Fi, this broadcast receiver is mainly used to receive Wi-Fi status changes and perform further processing. The method for handling Wi-Fi state changes is handleWifiStateChanged

private void handleWifiStateChanged(int state) {
    // Clear any previous state
    mSwitchWidget.setDisabledByAdmin(null);

    switch(state) {
        case WifiManager . WIFI_STATE_ENABLING : // Handle the state of turning on Wi-Fi
        break;
        case WifiManager . WIFI_STATE_ENABLED : // Handle the status of Wi-Fi turned on
        setSwitchBarChecked(true);
        mSwitchWidget.setEnabled(true);
        break;
        case WifiManager . WIFI_STATE_DISABLING : // Handle the state of turning off Wi-Fi
        break;
        case WifiManager . WIFI_STATE_DISABLED : // Handle the state where Wi-Fi has been turned off
        setSwitchBarChecked(false);
        mSwitchWidget.setEnabled(true);
        break;
        default: // Handle other situations
        setSwitchBarChecked(false);
        mSwitchWidget.setEnabled(true);
    }
}



 /packages/apps/Settings/src/com/android/settings/wifi/WifiEnabler.java
// If the current Wi-Fi status is inconsistent with the status of the Switch control, change the status of the Switch control
private void setSwitchBarChecked(boolean checked) {
    mStateMachineEvent = true;
    mSwitchWidget.setChecked(checked);
    mStateMachineEvent = false;
}

As can be seen from the setSwitchChecked method code, before changing the status of the Switch control (calling the Switch.setChecked method), first set the mStateMachineEvent variable to true . This means that when setting the Switch control state, the Wi-Fi state will not be set again due to triggering the onCheckedChanged method, thereby making a recursive call.

It can be seen from this point that the purpose of the broadcast receiver in the WifiEnabler class is only to set the status of the Switch control, not to set the status of Wi-Fi. The purpose of this is that when the user sets the Wi-Fi status through the WifiManager.setWifiEnabled method, although the Wi-Fi status can be successfully set, the status of the Switch control cannot be changed, so the current The Wi-Fi status is inconsistent with the status of the Switch control. In order to solve this problem, the WifiManager.setWifiEnabled method will send a broadcast (WifiManager.WIFI_STATE_CHANGED_ACTION) after setting the Wi-Fi state, and then the broadcast receiver in the WifiEnabler class will This broadcast will be received and the state of the Switch control will be changed according to the current Wi-Fi state.

Scan WiFi

The logic to start scanning is triggered from WifiSettings.
After WifiTracker receives the broadcast of the wifi status change,

final BroadcastReceiver mReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
// Actions to handle network status changes
            if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) {
                updateWifiState(
                        intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
                                WifiManager.WIFI_STATE_UNKNOWN));
            } else if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(action)) { // Handle the action of hotspot search completion
                mStaleScanResults = false;
                mLastScanSucceeded=
                        intent.getBooleanExtra(WifiManager.EXTRA_RESULTS_UPDATED, true);
//Update the searched hotspots
                fetchScansAndConfigsAndUpdateAccessPoints();
            } else if (WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION.equals(action)
                    || WifiManager.ACTION_LINK_CONFIGURATION_CHANGED.equals(action)) {
                fetchScansAndConfigsAndUpdateAccessPoints();
            } else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) { // Network status change action
                // TODO(sghuman): Refactor these methods so they cannot result in duplicate
                // onAccessPointsChanged updates being called from this intent.
                NetworkInfo info = intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
                updateNetworkInfo(info);
                fetchScansAndConfigsAndUpdateAccessPoints();
            } else if (WifiManager.RSSI_CHANGED_ACTION.equals(action)) {
                updateNetworkInfo(/* networkInfo= */ null);
            }
        }
    };


If wifi is already on, start scanning wifi

 private void updateWifiState(int state) {
        if (isVerboseLoggingEnabled()) {
            Log.d(TAG, "updateWifiState: " + state);
        }
        if (state == WifiManager.WIFI\_STATE\_ENABLED) {
            synchronized (mLock) {
                if (mScanner != null) {
// We only need to resume if mScanner isn't null because
// that means we want to be scanning.
                    mScanner.resume();
                }
            }
        }
        mListener.onWifiStateChanged(state);
    }

Scanner is a class used to scan Wifi:

class Scanner extends Handler {
    static final int MSG_SCAN = 0;

    private int mRetry = 0;

    // The resume method can trigger loop scanning of hot spots
    void resume () {
        if (isVerboseLoggingEnabled()) {
            Log.d(TAG, "Scanner resume");
        }
        if (!hasMessages(MSG_SCAN)) {
            sendEmptyMessage(MSG_SCAN);
        }
    }

    public void handleMessage(Message message) {
        if (message.what != MSG_SCAN) return;
        //Start scanning hot spots
        if (mWifiManager.startScan()) {
            mRetry = 0;
        } else if ( + + mRetry >= 3) {
            mRetry = 0;
            if (mContext != null) {
                Toast.makeText(mContext, R.string.wifi_fail_to_scan, Toast.LENGTH_LONG).show();
            }
            return;
        }
        //Send a delayed message and search for hot spots every 10 seconds
        sendEmptyMessageDelayed(MSG_SCAN, WIFI_RESCAN_INTERVAL_MS);
    }
}

First, the WifiManager.NETWORK_STATE_CHANGED_ACTION broadcast action will be processed by the broadcast receiver created in the WifiSettings class, and then the Scanner.resume method is called during the processing to start scanning hot spots. When the hotspot scan is completed, the system sends a WifiManager.SCAN_RESULTS_AVAILABLE_ACTION broadcast.

Call the fetchScansAndConfigsAndUpdateAccessPoints method to update the hotspot list. The specific update is in updateAccessPoints(). The Scanner object here continuously sends delayed messages in the Scanner. handleMessage method, so that the system scans hot spots every certain time (10 seconds).

private void fetchScansAndConfigsAndUpdateAccessPoints() {
// Get all searched hotspot information
        List<ScanResult> newScanResults = mWifiManager.getScanResults();

        // Filter all unsupported networks from the scan result list
        final List<ScanResult> filteredScanResults =
                filterScanResultsByCapabilities(newScanResults);

// if (isVerboseLoggingEnabled()) {
            Log.i(TAG, "Fetched scan results: " + filteredScanResults);
// }
// Get currently connected hotspot information
        List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks();
        updateAccessPoints(filteredScanResults, configs);
    }

In updateAccessPoints(filteredScanResults, configs);, the AccessPointer object of the connected hotspot information will be created, call accessPoint.update to update the status information, add the hotspot information to the hotspot, and finally Callback onAccessPointsChanged() in WifiSettings to update the UI;

Connect to Wifi

When the user clicks on a hotspot, a dialog box will pop up asking for a password, and finally clicks the “Connect” button to connect. Specifically in the submit method in WifiSettings:

 void submit(WifiConfigController configController) {

    final WifiConfiguration config = configController.getConfig();

    if (config == null) {
        if (mSelectedAccessPoint != null
                 & amp; & amp; mSelectedAccessPoint.isSaved()) {
            connect(mSelectedAccessPoint.getConfig(),
                    true /* isSavedNetwork */,
                    CONNECT_SOURCE_UNSPECIFIED);
        }
    } else if (configController.getMode() == WifiConfigUiBase.MODE_MODIFY) {
        mWifiManager.save(config, mSaveListener);
    } else {
        mWifiManager.save(config, mSaveListener);
        if (mSelectedAccessPoint != null) { // Not an "Add network"
            connect(config, false /* isSavedNetwork */,
                    CONNECT_SOURCE_UNSPECIFIED);
        }
    }

    mWifiTracker.resumeScanning();
}



protected void connect(final WifiConfiguration config,
        boolean isSavedNetwork, @ConnectSource int connectSource) {
    // Log subtype if configuration is a saved network.
    mMetricsFeatureProvider.action(getContext(), SettingsEnums.ACTION_WIFI_CONNECT,
            isSavedNetwork);
    mConnectSource = connectSource;
// Connect to Wifi
    mWifiManager.connect(config, mConnectListener);
    mClickedConnect = true;
}

After clicking Connect, if config is not null, the network will be saved first and then connected, so even if the connection fails, the network will still be in the saved network list.