Android11 Wifi开启、扫描和连接

开启Wifi

开启Wifi开关,Wifi开关是WifiEnablerWifiEnabler实现了 SwitchWidgetController.OnSwitchChangeListener监听,打开/关闭开关会回调至

// 处理Switch 控件的状态变化事件   
 public boolean onSwitchToggled(boolean isChecked) {
        //Do nothing if called as a result of a state machine event
		// 通过Switch.setEnabled 方法设置Switch 控件状态时,不需要再次禁止/允许Wi-Fi,否则将造成循环调用的后果
        if (mStateMachineEvent) {
            return true;
        }
        // Show toast message if Wi-Fi is not allowed in airplane mode
		//如果在飞行模式时不允许单独使用Wi-Fi,使用Toast 信息框进行提示
        if (isChecked && !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;
        }
		
        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()
            );
        }
		 //通过WifiManager.setWifiEnabled()开启/关闭wifi
        if (!mWifiManager.setWifiEnabled(isChecked)) {
            // Error
            mSwitchWidget.setEnabled(true);
            Toast.makeText(mContext, R.string.wifi_error, Toast.LENGTH_SHORT).show();
        }
        return true;
    }

mWifiManager.setWifiEnabled(isChecked) 用于根据Switch控件的当前状态关闭或打开Wi-Fi。

onCheckedChanged方法的开始部分使用了一个mStateMachineEvent变量,当该变量为true时直接跳出了onCheckedChanged方法。实际上,增加这个跳出条件的原因是因为Switch控件的状态变化可以有如下两种情况。

  • 直接单击Switch 控件。

  • 调用Switch.setChecked 方法。

遗憾的是,上述两种情况都会触发onCheckedChanged方法的调用。不管是哪种方法使Switch控件的状态发生了变化,在onCheckedChanged中调用WifiManager.setWifiEnabled方法设置Wi-Fi状态都会再次触发 onCheckedChanged方法的调用。当再次调用onCheckedChanged 方法时就需要将mStateMachineEvent变量值设为true,这样onCheckedChanged方法在执行之初就会立刻返回(如果继续执行后面的代码,将会造成死循环),然后再将mStateMachineEvent变量值设为false。这样在下一次设置Wi-Fi状态时仍然可以继续执行onCheckedChanged方法了。

用广播方式设置 Switch 控件的状态

 /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());
        }
    }
};

对于Wi-Fi来说,这个广播接收器主要用于接收Wi-Fi的状态变化,并做进一步地处理。其中处理Wi-Fi状态变化的方法是handleWifiStateChanged

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

    switch(state) {
        case WifiManager . WIFI_STATE_ENABLING : // 处理正在开启Wi-Fi的状态
        break;
        case WifiManager . WIFI_STATE_ENABLED : // 处理已经开启Wi-Fi的状态
        setSwitchBarChecked(true);
        mSwitchWidget.setEnabled(true);
        break;
        case WifiManager . WIFI_STATE_DISABLING : // 处理正在关闭Wi-Fi的状态
        break;
        case WifiManager . WIFI_STATE_DISABLED :  // 处理已经关闭Wi-Fi的状态
        setSwitchBarChecked(false);
        mSwitchWidget.setEnabled(true);
        break;
        default:                                   // 处理其他情况
        setSwitchBarChecked(false);
        mSwitchWidget.setEnabled(true);
    }
}



 /packages/apps/Settings/src/com/android/settings/wifi/WifiEnabler.java
// 如果当前Wi-Fi 的状态与Switch 控件的状态不一致,改变Switch 控件的状态
private void setSwitchBarChecked(boolean checked) {
    mStateMachineEvent = true;
    mSwitchWidget.setChecked(checked);
    mStateMachineEvent = false;
}

setSwitchChecked方法代码可以看出,在改变Switch控件状态之前(调用Switch.setChecked方法),先将mStateMachineEvent变量设为true。这就意味着在设置Switch控件状态时不会由于触发了onCheckedChanged方法而再次设置Wi-Fi状态,从而进行递归调用。

从这一点可以看出,WifiEnabler类中的广播接收器的目的只是为了设置Switch控件的状态,并不是为了设置Wi-Fi的状态。这么做的目的是当用户通过WifiManager.setWifiEnabled方法设置Wi-Fi状态时,尽管可以成功设置Wi-Fi的状态,但却无法改变Switch控件的状态,所以就会造成当前Wi-Fi状态与Switch控件的状态不一致的情况。为了解决这个问题,WifiManager.setWifiEnabled方法在设置Wi-Fi状态后,会发送一个广播(WifiManager.WIFI_STATE_CHANGED_ACTION),然后WifiEnabler类中的广播接收器就会接收到这个广播,并根据当前Wi-Fi的状态改变Switch控件的状态。

扫描WiFi

开始扫描的逻辑是从WifiSettings触发的。
WifiTracker接收到wifi状态改变的广播以后,

扫描二维码关注公众号,回复: 17059882 查看本文章
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)) {
                updateWifiState(
                        intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
                                WifiManager.WIFI_STATE_UNKNOWN));
            } else if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(action)) { // 处理热点搜索完成的动作
                mStaleScanResults = false;
                mLastScanSucceeded =
                        intent.getBooleanExtra(WifiManager.EXTRA_RESULTS_UPDATED, true);
				// 更新搜索到的热点
                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)) { // 网络状态变化动作
                // 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);
            }
        }
    };


如果wifi已经打开了,则开始扫描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是用于扫描Wifi的类:

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

    private int mRetry = 0;

    // 通过resume 方法可以触发循环扫描热点
    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;
        // 开始扫描热点
        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;
        }
        // 发送延迟消息,会每10 秒搜索一次热点
        sendEmptyMessageDelayed(MSG_SCAN, WIFI_RESCAN_INTERVAL_MS);
    }
}

首先会由WifiSettings类中创建的广播接收器处理WifiManager.NETWORK_STATE_CHANGED_ACTION 广播动作,然后在处理的过程中调用了Scanner.resume 方法开始扫描热点。当热点扫描完成后,系统发送 WifiManager.SCAN_RESULTS_AVAILABLE_ACTION广播。

调用fetchScansAndConfigsAndUpdateAccessPoints方法更新热点列表,具体更新是在updateAccessPoints()。这里的Scanner对象通过不断在Scanner. handleMessage方法中发送延迟消息,从而使系统每隔一定时间(10秒)就会扫描一次热点。

private void fetchScansAndConfigsAndUpdateAccessPoints() {
		// 获取所有搜索到的热点信息
        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);
//        }
		// 获取当前已经连接的热点信息
        List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks();
        updateAccessPoints(filteredScanResults, configs);
    }

updateAccessPoints(filteredScanResults, configs);中会创建已经连接的热点信息的AccessPointer对象,调用accessPoint.update更新状态信息,将热点信息添加到热点,最后回调WifiSettings中的onAccessPointsChanged()更新UI;

连接Wifi

当用户单击一个热点后,就会弹出对话框要求输入密码,最后单击“连接”按钮进行连接。具体是在WifiSettings中的submit方法中:

 void submit(WifiConfigController configController) {

    final WifiConfiguration config = configController.getConfig();

    if (config == null) {
        if (mSelectedAccessPoint != null
                && 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;
	// 连接Wifi
    mWifiManager.connect(config, mConnectListener);
    mClickedConnect = true;
}

点击连接以后,如果config不为null,则先保存网络,再进行连接,所以即使连接失败,此网络依然在已保存网络列表里。

猜你喜欢

转载自blog.csdn.net/jxq1994/article/details/132909718
今日推荐