(八十六)探讨WiFi开关变化的原因
发布日期:2021-06-30 15:25:15 浏览次数:2 分类:技术文章

本文共 22013 字,大约阅读时间需要 73 分钟。

前言:之前只知道WiFi开关有所变化是由于接收到了WiFi state变化的广播,根据广播所携带的state信息更新开关,那变化的深层原因是什么呢?

先期工作:

1)

先抓取了小米Mix2 8.0的WiFi开启log:

09-01 00:07:19.994  1561  2456 D WifiStateMachine: setting operational mode to 109-01 00:07:19.995  1561  2457 D WifiStateMachine:  InitialState !CMD_SET_OPERATIONAL_MODE rt=386651444/1132848219 1 009-01 00:07:19.995  1561  2457 D WifiStateMachine:  InitialState !CMD_START_SUPPLICANT rt=386651445/1132848220 0 009-01 00:07:20.245  1561  2457 D WifiStateMachine: Supplicant start successful09-01 00:07:20.447  1561  2457 D WifiStateMachine:  SupplicantStartingState !CMD_START_SUPPLICANT rt=386651897/1132848672 0 009-01 00:07:20.448  1561  2457 D WifiStateMachine:  SupplicantStartingState !SUP_CONNECTION_EVENT rt=386651897/1132848672 0 009-01 00:07:20.448  1561  2457 D WifiStateMachine: Supplicant connection established09-01 00:07:20.454  1561  2457 D WifiStateMachine: SupplicantStartedState enter09-01 00:07:20.454  1561  2457 D WifiStateMachine: Setting OUI to DA-A1-1909-01 00:07:20.461  1561  2457 D WifiStateMachine: setDetailed state, old =DISCONNECTED and new state=DISCONNECTED hidden=false09-01 00:07:20.466  1561  2457 D WifiStateMachine: setWifiState: enabling09-01 00:07:20.492  1561  2457 D WifiStateMachine: setWifiState: enabled09-01 00:07:20.494  1561  2456 D WifiStateMachine: setting operational mode to 109-01 00:07:20.496  1561  2457 D WifiStateMachine: setDetailed state, old =DISCONNECTED and new state=DISCONNECTED hidden=false09-01 00:07:20.501  1561  2457 I WifiStateMachine: disconnectedstate enter09-01 00:07:20.501  1561  2457 D WifiStateMachine:  Enter DisconnectedState screenOn=true

2)wifiStateMachine状态机

3)WiFi启动时序图

 

1.设置WiFi开关刷新

我比较熟悉设置,就以设置的WiFi开关开始梳理

WifiEnabler

1.注册广播接收器

@VisibleForTesting    WifiEnabler(Context context, SwitchWidgetController switchWidget,            MetricsFeatureProvider metricsFeatureProvider,            ConnectivityManagerWrapper connectivityManagerWrapper) {        mContext = context;        mSwitchWidget = switchWidget;        mSwitchWidget.setListener(this);        mMetricsFeatureProvider = metricsFeatureProvider;        mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);        mConnectivityManager = connectivityManagerWrapper;        mIntentFilter = new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION);        // The order matters! We really should not depend on this. :(        mIntentFilter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);        mIntentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);        setupSwitchController();    }    public void resume(Context context) {        mContext = context;        // Wi-Fi state is sticky, so just let the receiver update UI        mContext.registerReceiver(mReceiver, mIntentFilter);        if (!mListeningToOnSwitchChange) {            mSwitchWidget.startListening();            mListeningToOnSwitchChange = true;        }    }    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());            }        }    };

2.当接收到WifiManager.WIFI_STATE_CHANGED_ACTION时调用handleWifiStateChanged刷新开关状态

private void handleWifiStateChanged(int state) {        // Clear any previous state        mSwitchWidget.setDisabledByAdmin(null);        switch (state) {            case WifiManager.WIFI_STATE_ENABLING:                break;            case WifiManager.WIFI_STATE_ENABLED:                setSwitchBarChecked(true);                mSwitchWidget.setEnabled(true);                break;            case WifiManager.WIFI_STATE_DISABLING:                break;            case WifiManager.WIFI_STATE_DISABLED:                setSwitchBarChecked(false);                mSwitchWidget.setEnabled(true);                break;            default:                setSwitchBarChecked(false);                mSwitchWidget.setEnabled(true);        }        if (mayDisableTethering(!mSwitchWidget.isChecked())) {            if (RestrictedLockUtils.hasBaseUserRestriction(mContext,                    UserManager.DISALLOW_CONFIG_TETHERING, UserHandle.myUserId())) {                mSwitchWidget.setEnabled(false);            } else {                final EnforcedAdmin admin = RestrictedLockUtils.checkIfRestrictionEnforced(mContext,                    UserManager.DISALLOW_CONFIG_TETHERING, UserHandle.myUserId());                mSwitchWidget.setDisabledByAdmin(admin);            }        }    }

 

2.广播的发起者

那WiFi开启完毕后WifiManager.WIFI_STATE_CHANGED_ACTION这个广播是由谁发出的呢?之前梳理WiFi启动流程的时候不够细致,这里补充一下。

基于 继续梳理

这里补充梳理setSupplicantRunning以后的wifiStateMachine的状态机变化。

2.1 InitialState

class InitialState extends State {        private void cleanup() {            // Tearing down the client interfaces below is going to stop our supplicant.            mWifiMonitor.stopAllMonitoring();            mDeathRecipient.unlinkToDeath();            mWifiNative.tearDown();        }        @Override        public void enter() {            mWifiStateTracker.updateState(WifiStateTracker.INVALID);            cleanup();        }        @Override        public boolean processMessage(Message message) {            logStateAndMessage(message, this);            switch (message.what) {                case CMD_START_SUPPLICANT:                    Pair
statusAndInterface = mWifiNative.setupForClientMode(); if (statusAndInterface.first == WifiNative.SETUP_SUCCESS) { mClientInterface = statusAndInterface.second; } else { incrementMetricsForSetupFailure(statusAndInterface.first); } if (mClientInterface == null || !mDeathRecipient.linkToDeath(mClientInterface.asBinder())) { setWifiState(WifiManager.WIFI_STATE_UNKNOWN); cleanup(); break; } try { // A runtime crash or shutting down AP mode can leave // IP addresses configured, and this affects // connectivity when supplicant starts up. // Ensure we have no IP addresses before a supplicant start. mNwService.clearInterfaceAddresses(mInterfaceName); // Set privacy extensions mNwService.setInterfaceIpv6PrivacyExtensions(mInterfaceName, true); // IPv6 is enabled only as long as access point is connected since: // - IPv6 addresses and routes stick around after disconnection // - kernel is unaware when connected and fails to start IPv6 negotiation // - kernel can start autoconfiguration when 802.1x is not complete mNwService.disableIpv6(mInterfaceName); } catch (RemoteException re) { loge("Unable to change interface settings: " + re); } catch (IllegalStateException ie) { loge("Unable to change interface settings: " + ie); } if (!mWifiNative.enableSupplicant()) { loge("Failed to start supplicant!"); setWifiState(WifiManager.WIFI_STATE_UNKNOWN); cleanup(); break; } if (mVerboseLoggingEnabled) log("Supplicant start successful"); mWifiMonitor.startMonitoring(mInterfaceName, true); mWifiInjector.getWifiLastResortWatchdog().clearAllFailureCounts(); setSupplicantLogLevel(); transitionTo(mSupplicantStartingState); break;

WifiStateMachine的初始状态是InitialState状态,接收到CMD_START_SUPPLICANT消息后开始做如下几步操作后变化到SupplicantStartingState状态。

1.mWifiNative.setupForClientMode()

2.mWifiNative.enableSupplicant()

3.mWifiMonitor.startMonitoring(mInterfaceName, true);

之前梳理忽略了第3步,这第3步里文章比较大。

WifiMonitor:

/**     * Start Monitoring for wpa_supplicant events.     *     * @param iface Name of iface.     * TODO: Add unit tests for these once we remove the legacy code.     */    public synchronized void startMonitoring(String iface, boolean isStaIface) {        if (ensureConnectedLocked()) {            setMonitoring(iface, true);            broadcastSupplicantConnectionEvent(iface);        } else {            boolean originalMonitoring = isMonitoring(iface);            setMonitoring(iface, true);            broadcastSupplicantDisconnectionEvent(iface);            setMonitoring(iface, originalMonitoring);            Log.e(TAG, "startMonitoring(" + iface + ") failed!");        }    }
/**     * Wait for wpa_supplicant's control interface to be ready.     *     * TODO: Add unit tests for these once we remove the legacy code.     */    private boolean ensureConnectedLocked() {        if (mConnected) {            return true;        }        if (mVerboseLoggingEnabled) Log.d(TAG, "connecting to supplicant");        int connectTries = 0;        while (true) {            mConnected = mWifiInjector.getWifiNative().connectToSupplicant();            if (mConnected) {                return true;            }            if (connectTries++ < 50) {                try {                    Thread.sleep(100);                } catch (InterruptedException ignore) {                }            } else {                return false;            }        }    }

这个ensureConnectedLocked是个轮询方法,会每隔100ms,上限50次的去查看与supplicant的连接有没有建立起来。

WifiNative

/**     * This method is called repeatedly until the connection to wpa_supplicant is established.     *     * @return true if connection is established, false otherwise.     * TODO: Add unit tests for these once we remove the legacy code.     */    public boolean connectToSupplicant() {        // Start initialization if not already started.        if (!mSupplicantStaIfaceHal.isInitializationStarted()                && !mSupplicantStaIfaceHal.initialize()) {            return false;        }        // Check if the initialization is complete.        return mSupplicantStaIfaceHal.isInitializationComplete();    }

如果建立好了,就做两个事,启动对某iface的监控并其向该注册该事件的所有handler发送supplicant的连接消息。

/**     * Enable/Disable monitoring for the provided iface.     *     * @param iface Name of the iface.     * @param enabled true to enable, false to disable.     */    @VisibleForTesting    public void setMonitoring(String iface, boolean enabled) {        mMonitoringMap.put(iface, enabled);    }
/**     * Broadcast the connection to wpa_supplicant event to all the handlers registered for     * this event.     *     * @param iface Name of iface on which this occurred.     */    public void broadcastSupplicantConnectionEvent(String iface) {        sendMessage(iface, SUP_CONNECTION_EVENT);    }

PS:

1)iface除非特意指定wifi.interface,一般是wlan0

mWifiNative = new WifiNative(SystemProperties.get("wifi.interface", "wlan0"),                mWifiVendorHal, mSupplicantStaIfaceHal, mWificondControl);

2)wifistateMachine是注册supplicant连接消息的。

mWifiMonitor.registerHandler(mInterfaceName, WifiMonitor.SUP_CONNECTION_EVENT, getHandler());

 

2.2 SupplicantStartingState

class SupplicantStartingState extends State {        private void initializeWpsDetails() {    ...        }        @Override        public boolean processMessage(Message message) {            logStateAndMessage(message, this);            switch(message.what) {                case WifiMonitor.SUP_CONNECTION_EVENT:                    if (mVerboseLoggingEnabled) log("Supplicant connection established");                    mSupplicantRestartCount = 0;                    /* Reset the supplicant state to indicate the supplicant                     * state is not known at this time */                    mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE);                    /* Initialize data structures */                    mLastBssid = null;                    mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID;                    mLastSignalLevel = -1;                    mWifiInfo.setMacAddress(mWifiNative.getMacAddress());                    initializeWpsDetails();                    sendSupplicantConnectionChangedBroadcast(true);                    transitionTo(mSupplicantStartedState);                    break;

SupplicantStartingState并没有特殊的enter方法,只是在收到supplicant连接好的消息后会有相应的处理并将状态切到SupplicantStartedState。

 

2.3 SupplicantStartedState

class SupplicantStartedState extends State {        @Override        public void enter() {            if (mVerboseLoggingEnabled) {                logd("SupplicantStartedState enter");            }            mWifiNative.setExternalSim(true);            setRandomMacOui();            mCountryCode.setReadyForChange(true);            // We can't do this in the constructor because WifiStateMachine is created before the            // wifi scanning service is initialized            if (mWifiScanner == null) {                mWifiScanner = mWifiInjector.getWifiScanner();                synchronized (mWifiReqCountLock) {                    mWifiConnectivityManager =                            mWifiInjector.makeWifiConnectivityManager(mWifiInfo,                                                                      hasConnectionRequests());                    mWifiConnectivityManager.setUntrustedConnectionAllowed(mUntrustedReqCount > 0);                    mWifiConnectivityManager.handleScreenStateChanged(mScreenOn);                }            }            mWifiDiagnostics.startLogging(mVerboseLoggingEnabled);            mIsRunning = true;            updateBatteryWorkSource(null);            /**             * Enable bluetooth coexistence scan mode when bluetooth connection is active.             * When this mode is on, some of the low-level scan parameters used by the             * driver are changed to reduce interference with bluetooth             */            mWifiNative.setBluetoothCoexistenceScanMode(mBluetoothConnectionActive);            // Check if there is a voice call on-going and set/reset the tx power limit            // appropriately.            if (mEnableVoiceCallSarTxPowerLimit) {                if (getTelephonyManager().isOffhook()) {                    sendMessage(CMD_SELECT_TX_POWER_SCENARIO,                            WifiNative.TX_POWER_SCENARIO_VOICE_CALL);                } else {                    sendMessage(CMD_SELECT_TX_POWER_SCENARIO,                            WifiNative.TX_POWER_SCENARIO_NORMAL);                }            }            // initialize network state            setNetworkDetailedState(DetailedState.DISCONNECTED);            // Disable legacy multicast filtering, which on some chipsets defaults to enabled.            // Legacy IPv6 multicast filtering blocks ICMPv6 router advertisements which breaks IPv6            // provisioning. Legacy IPv4 multicast filtering may be re-enabled later via            // IpClient.Callback.setFallbackMulticastFilter()            mWifiNative.stopFilteringMulticastV4Packets();            mWifiNative.stopFilteringMulticastV6Packets();            if (mOperationalMode == SCAN_ONLY_MODE ||                    mOperationalMode == SCAN_ONLY_WITH_WIFI_OFF_MODE) {                mWifiNative.disconnect();                setWifiState(WIFI_STATE_DISABLED);                transitionTo(mScanModeState);            } else if (mOperationalMode == CONNECT_MODE) {                setWifiState(WIFI_STATE_ENABLING);                // Transitioning to Disconnected state will trigger a scan and subsequently AutoJoin                transitionTo(mDisconnectedState);            } else if (mOperationalMode == DISABLED_MODE) {                transitionTo(mSupplicantStoppingState);            }            // Set the right suspend mode settings            mWifiNative.setSuspendOptimizations(mSuspendOptNeedsDisabled == 0                    && mUserWantsSuspendOpt.get());            mWifiNative.setPowerSave(true);            if (mP2pSupported) {                if (mOperationalMode == CONNECT_MODE) {                    p2pSendMessage(WifiStateMachine.CMD_ENABLE_P2P);                } else {                    // P2P state machine starts in disabled state, and is not enabled until                    // CMD_ENABLE_P2P is sent from here; so, nothing needs to be done to                    // keep it disabled.                }            }            final Intent intent = new Intent(WifiManager.WIFI_SCAN_AVAILABLE);            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);            intent.putExtra(WifiManager.EXTRA_SCAN_AVAILABLE, WIFI_STATE_ENABLED);            mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);            // Disable wpa_supplicant from auto reconnecting.            mWifiNative.enableStaAutoReconnect(false);            // STA has higher priority over P2P            mWifiNative.setConcurrencyPriority(true);        }

由于之前启动WiFi的时候设置操作模式为CONNECT_MODE,所以会走这边

} else if (mOperationalMode == CONNECT_MODE) {                setWifiState(WIFI_STATE_ENABLING);                // Transitioning to Disconnected state will trigger a scan and subsequently AutoJoin                transitionTo(mDisconnectedState);            }
private void setWifiState(int wifiState) {        final int previousWifiState = mWifiState.get();        try {            if (wifiState == WIFI_STATE_ENABLED) {                mBatteryStats.noteWifiOn();            } else if (wifiState == WIFI_STATE_DISABLED) {                mBatteryStats.noteWifiOff();            }        } catch (RemoteException e) {            loge("Failed to note battery stats in wifi");        }        mWifiState.set(wifiState);        if (mVerboseLoggingEnabled) log("setWifiState: " + syncGetWifiStateByName());        final Intent intent = new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION);        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);        intent.putExtra(WifiManager.EXTRA_WIFI_STATE, wifiState);        intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_STATE, previousWifiState);        mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);    }

这边的setWifiState会修改wifi state状态为WIFI_STATE_ENABLING并发出广播通知各个App更新状态,表示WiFi正在打开,之后会切换到DisconnectedState状态。

 

2.4 DisconnectedState(ConnectModeState)

这里先提下DisconnectedState的父状态是ConnectModeState

                        addState(mDisconnectedState, mConnectModeState);

所以会先调用ConnectModeState的enter方法

class ConnectModeState extends State {        @Override        public void enter() {            if (!mWifiNative.removeAllNetworks()) {                loge("Failed to remove networks on entering connect mode");            }            mWifiInfo.reset();            mWifiInfo.setSupplicantState(SupplicantState.DISCONNECTED);            // Let the system know that wifi is available in client mode.            setWifiState(WIFI_STATE_ENABLED);            mNetworkInfo.setIsAvailable(true);            if (mNetworkAgent != null) mNetworkAgent.sendNetworkInfo(mNetworkInfo);            // initialize network state            setNetworkDetailedState(DetailedState.DISCONNECTED);            // Inform WifiConnectivityManager that Wifi is enabled            mWifiConnectivityManager.setWifiEnabled(true);            // Inform metrics that Wifi is Enabled (but not yet connected)            mWifiMetrics.setWifiState(WifiMetricsProto.WifiLog.WIFI_DISCONNECTED);            // Inform p2p service that wifi is up and ready when applicable            p2pSendMessage(WifiStateMachine.CMD_ENABLE_P2P);        }

看到setWifiState(WIFI_STATE_ENABLED);表示WiFi状态被设为已打开状态并通知给各app,这时候设置收到这个广播自然就刷新开关状态为开啦,其实梳理到这边就结束了。

之后进入DisconnectedState的enter方法

class DisconnectedState extends State {        @Override        public void enter() {            Log.i(TAG, "disconnectedstate enter");            // We dont scan frequently if this is a temporary disconnect            // due to p2p            if (mTemporarilyDisconnectWifi) {                p2pSendMessage(WifiP2pServiceImpl.DISCONNECT_WIFI_RESPONSE);                return;            }            if (mVerboseLoggingEnabled) {                logd(" Enter DisconnectedState screenOn=" + mScreenOn);            }            /** clear the roaming state, if we were roaming, we failed */            mIsAutoRoaming = false;            mWifiConnectivityManager.handleConnectionStateChanged(                    WifiConnectivityManager.WIFI_STATE_DISCONNECTED);            /**             * If we have no networks saved, the supplicant stops doing the periodic scan.             * The scans are useful to notify the user of the presence of an open network.             * Note that these are not wake up scans.             */            if (mNoNetworksPeriodicScan != 0 && !mP2pConnected.get()                    && mWifiConfigManager.getSavedNetworks().size() == 0) {                sendMessageDelayed(obtainMessage(CMD_NO_NETWORKS_PERIODIC_SCAN,                        ++mPeriodicScanToken, 0), mNoNetworksPeriodicScan);            }            mDisconnectedTimeStamp = mClock.getWallClockMillis();            mWifiStateTracker.updateState(WifiStateTracker.DISCONNECTED);        }

 

3.总结

灵魂画师

转载地址:https://jiatai.blog.csdn.net/article/details/82263373 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:(八十七) WiFi & DHCP
下一篇:(八十五)探究activity生命周期和onMeasure/onLayout/onDraw的时序

发表评论

最新留言

初次前来,多多关照!
[***.217.46.12]2024年04月17日 05时15分09秒