Android -- Wifi的断开及关闭流程简介

Android -- Wifi的断开及关闭流程简介


当我们连接上一个AP时,Wifi的断开及关闭都会导致设备与AP之间的连接中断;关闭Wifi同时会导致Wifi断开。下面就简单介绍Wifi断开及关闭的流程。

一、Wifi的断开


我们断开Wifi,调用WifiManager::disconnect()方法,嵌套调用WifiServiceImpl的同名方法:
    /**
     * see {@link android.net.wifi.WifiManager#disconnect()}
     */
    public void disconnect() {
        enforceChangePermission();
        mWifiStateMachine.disconnectCommand();
    }
调用Wifi状态机的内部方法,发送断开Wifi的指令CMD_DISCONNECT。由于之前网络连接成功时,State Machine停在ConnectedState,所以由它的父状态L2ConnectedState处理该消息:
                case CMD_DISCONNECT:
                    mWifiNative.disconnect();
                    transitionTo(mDisconnectingState);
                    break;
通过WifiNative::disconnect()方法下发断开指令,当驱动断开AP连接后,wpa_s反馈断开事件,WifiMonitor接收来自wpa_s的event,并向WifiStateMachine反馈NETWORK_DISCONNECTION_EVENT消息(该过程参考前面的博文)。

切换到DisconnectingState状态,断开的消息尤其父状态ConnectModeState处理:
case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
                    // Calling handleNetworkDisconnect here is redundant because we might already
                    // have called it when leaving L2ConnectedState to go to disconnecting state
                    // or thru other path
                    // We should normally check the mWifiInfo or mLastNetworkId so as to check
                    // if they are valid, and only in this case call handleNEtworkDisconnect,
                    // TODO: this should be fixed for a L MR release
                    // The side effect of calling handleNetworkDisconnect twice is that a bunch of
                    // idempotent commands are executed twice (stopping Dhcp, enabling the SPS mode
                    // at the chip etc...
                    if (DBG) log("ConnectModeState: Network connection lost ");
                    handleNetworkDisconnect();
                    transitionTo(mDisconnectedState);
                    break;
由代码中的注释可知,我们或许会在别的地方已经调用过handleNetworkDisconnect(),事实也确实如此;当我们离开L2ConnectedState状态时,会调用它的exit()方法:
       @Override
        public void exit() {
            if (mIpReachabilityMonitor != null) {
                mIpReachabilityMonitor.stop();
                mIpReachabilityMonitor = null;
            }

            // This is handled by receiving a NETWORK_DISCONNECTION_EVENT in ConnectModeState
            // Bug: 15347363
            // For paranoia's sake, call handleNetworkDisconnect
            // only if BSSID is null or last networkId
            // is not invalid.
            if (DBG) {
                StringBuilder sb = new StringBuilder();
                sb.append("leaving L2ConnectedState state nid=" + Integer.toString(mLastNetworkId));
                if (mLastBssid !=null) {
                    sb.append(" ").append(mLastBssid);
                }
            }
            if (mLastBssid != null || mLastNetworkId != WifiConfiguration.INVALID_NETWORK_ID) {
                handleNetworkDisconnect();//call the function,执行忘了断开时的处理
            }
        }
我们发现该方法中确实可能会调用handleNetworkDisconnect(),接着看该函数做了哪些处理:
/**
     * Resets the Wi-Fi Connections by clearing any state, resetting any sockets
     * using the interface, stopping DHCP & disabling interface
     */
    private void handleNetworkDisconnect() {

        clearCurrentConfigBSSID("handleNetworkDisconnect");

        stopDhcp();

        try {
            mNwService.clearInterfaceAddresses(mInterfaceName);
            mNwService.disableIpv6(mInterfaceName);
        } catch (Exception e) {
            loge("Failed to clear addresses or disable ipv6" + e);
        }

        /* Reset data structures */
        mBadLinkspeedcount = 0;
        mWifiInfo.reset();
        linkDebouncing = false;
        /* Reset roaming parameters */
        mAutoRoaming = WifiAutoJoinController.AUTO_JOIN_IDLE;

        /**
         *  fullBandConnectedTimeIntervalMilli:
         *  - start scans at mWifiConfigStore.wifiAssociatedShortScanIntervalMilli seconds interval
         *  - exponentially increase to mWifiConfigStore.associatedFullScanMaxIntervalMilli
         *  Initialize to sane value = 20 seconds
         */
        fullBandConnectedTimeIntervalMilli = 20 * 1000;

        setNetworkDetailedState(DetailedState.DISCONNECTED);
        if (mNetworkAgent != null) {
            mNetworkAgent.sendNetworkInfo(mNetworkInfo);
            mNetworkAgent = null;
        }
        mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.DISCONNECTED);

        /* Clear network properties */
        clearLinkProperties();

        /* Cend event to CM & network change broadcast */
        sendNetworkStateChangeBroadcast(mLastBssid);

        /* Cancel auto roam requests */
        autoRoamSetBSSID(mLastNetworkId, "any");

        mLastBssid = null;
        registerDisconnected();
        mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
    }
当一个AP连接断开时,主要会做如下几处理:

  1. stopDhcp():停止DHCP过程,即停止dhcpcd进程
  2. mNwService.clearInterfaceAddresses(mInterfaceName):通过NetworkManagementService通知Netd清除wlan0的地址信息
  3. mWifiInfo.reset():重置WifiInfo对象的状态
  4. setNetworkDetailedState(DetailedState.DISCONNECTED):将mNetworkInfo的连接状态设为DISCONNECTED
  5. mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.DISCONNECTED):将mLastNetworkId对应的 WifiConfiguration对象状态设为DISCONNECTED
  6. clearLinkProperties():清除mLinkProperties对象的配置信息
  7. sendNetworkStateChangeBroadcast():发广播通知上层网络状态变化
  8. registerDisconnected():做一些清理、记录工作

最后状态切换到DisconnectedState,等待下一次连接的发起,断开流程结束。

二、Wifi的关闭


从前面的博文可知,Wifi的开启、关闭都是调用WifiManager::setWifiEnabled(),嵌套调用WifiServiceImpl中的同名方法,向WifiController发送CMD_WIFI_TOGGLED消息:
    /**
     * see {@link android.net.wifi.WifiManager#setWifiEnabled(boolean)}
     * @param enable {@code true} to enable, {@code false} to disable.
     * @return {@code true} if the enable/disable operation was
     *         started or is already in the queue.
     */
    public synchronized boolean setWifiEnabled(boolean enable) {
        enforceChangePermission();
        Slog.d(TAG, "setWifiEnabled: " + enable + " pid=" + Binder.getCallingPid()
                    + ", uid=" + Binder.getCallingUid());
        if (DBG) {
            Slog.e(TAG, "Invoking mWifiStateMachine.setWifiEnabled\n");
        }

        /*
        * Caller might not have WRITE_SECURE_SETTINGS,
        * only CHANGE_WIFI_STATE is enforced
        */

        long ident = Binder.clearCallingIdentity();
        try {
            if (! mSettingsStore.handleWifiToggled(enable)) {
                // Nothing to do if wifi cannot be toggled
                return true;
            }
        } finally {
            Binder.restoreCallingIdentity(ident);
        }

        mWifiController.sendMessage(CMD_WIFI_TOGGLED);
        return true;
    }
前面的博文介绍过Wifi的启动流程,当WifiController向Wifi状态机发送启动wpa_s消息的时候,状态停留在DeviceActiveState。DeviceActiveState不处理CMD_WIFI_TOGGLED消息,交由其父状态StaEnabledState处理:
 case CMD_WIFI_TOGGLED:
                    if (! mSettingsStore.isWifiToggleEnabled()) {
                        if (mSettingsStore.isScanAlwaysAvailable()) {
                            transitionTo(mStaDisabledWithScanState);
                        } else {
                            transitionTo(mApStaDisabledState);
                        }
                    }
                    break;
结合前面的所讲的内容,可知状态切换到ApStaDisabledState,进行关闭流程,调用它的enter()方法:
        @Override
        public void enter() {
            mWifiStateMachine.setSupplicantRunning(false);
            // Supplicant can't restart right away, so not the time we switched off
            mDisabledTimestamp = SystemClock.elapsedRealtime();
            mDeferredEnableSerialNumber++;
            mHaveDeferredEnable = false;
            mWifiStateMachine.clearANQPCache();
        }
调用WifiStateMachine::setSupplicantRunning(false)向WifiStateMachine发送停止wpa_s的消息:CMD_STOP_SUPPLICANT。

SupplicantStartedState接收、处理该消息:
                case CMD_STOP_SUPPLICANT:   /* Supplicant stopped by user */
                    if (mP2pSupported) {
                        transitionTo(mWaitForP2pDisableState);
                    } else {
                        transitionTo(mSupplicantStoppingState);
                    }
                    break;
切换到SupplicantStoppingState,进入enter()方法:
public void enter() {
            /* Send any reset commands to supplicant before shutting it down */
            handleNetworkDisconnect();
            if (mDhcpStateMachine != null) {
                mDhcpStateMachine.doQuit();
            }

            String suppState = System.getProperty("init.svc.wpa_supplicant");
            if (suppState == null) suppState = "unknown";
            String p2pSuppState = System.getProperty("init.svc.p2p_supplicant");
            if (p2pSuppState == null) p2pSuppState = "unknown";

            logd("SupplicantStoppingState: stopSupplicant "
                    + " init.svc.wpa_supplicant=" + suppState
                    + " init.svc.p2p_supplicant=" + p2pSuppState);
            mWifiMonitor.stopSupplicant();

            /* Send ourselves a delayed message to indicate failure after a wait time */
            sendMessageDelayed(obtainMessage(CMD_STOP_SUPPLICANT_FAILED,
                    ++mSupplicantStopFailureToken, 0), SUPPLICANT_RESTART_INTERVAL_MSECS);
            setWifiState(WIFI_STATE_DISABLING);
            mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE);
        }
  1. 调用handleNetworkDisconnect()处理AP连接的断开部分,第一部分已经讲过。
  2. 设置DhcpStateMachine的状态,停止DhcpStateMachine
  3. 调用WifiMonitor::stopSupplicant()停掉wpa_s
  4. 将wifi状态设置为disabling;setWifiState()函数会设置当前Wifi的状态,比如disabled、disabling、enabled等;我们通过监听函数发送的WifiManager.WIFI_STATE_CHANGED_ACTION广播,可以得知当前Wifi的具体状态。

WifiMonitor中下发终止指令后,会收到wpa_s断开的event:
else if (event == TERMINATING) {
            /**
             * Close the supplicant connection if we see
             * too many recv errors
             */
            if (eventData.startsWith(WPA_RECV_ERROR_STR)) {
                if (++sRecvErrors > MAX_RECV_ERRORS) {
                    if (DBG) {
                        Log.d(TAG, "too many recv errors, closing connection");
                    }
                } else {
                    eventLogCounter++;
                    return false;
                }
            }

            // Notify and exit
            mStateMachine.sendMessage(SUP_DISCONNECTION_EVENT, eventLogCounter);
            return true;
WifiMonitor会向wifi状态机发送SUP_DISCONNECTION_EVENT消息并最终退出,等待下一次重新与wpa_s建立通信。SupplicantStoppingState处理该消息:
               case WifiMonitor.SUP_DISCONNECTION_EVENT:
                    if (DBG) log("Supplicant connection lost");
                    handleSupplicantConnectionLoss(false);
                    transitionTo(mInitialState);
                    break;
    private void handleSupplicantConnectionLoss(boolean killSupplicant) {
        /* Socket connection can be lost when we do a graceful shutdown
        * or when the driver is hung. Ensure supplicant is stopped here.
        */
        if (killSupplicant) {
            mWifiMonitor.killSupplicant(mP2pSupported);
        }
        mWifiNative.closeSupplicantConnection();
        sendSupplicantConnectionChangedBroadcast(false);
        setWifiState(WIFI_STATE_DISABLED);
    }
handleSupplicantConnectionLoss()函数中,因为之前已经执行过stopSupplicant()的操作,killSupplicant传入false,所以不会重复执行关闭wpa_s的killSupplicant()函数;随后关闭wpa_s与wifi之间传递指令、事件的socket连接,最后设置当前Wifi状态为disabled,代表Wifi已经关闭完成。

handleSupplicantConnectionLoss()执行完毕后,wpa_s的状态已经被彻底清除掉,随之将Wifi状态机切换到InitialState:
        @Override
        public void enter() {
            WifiNative.stopHal();
            mWifiNative.unloadDriver();
            if (mWifiP2pChannel == null) {
                mWifiP2pChannel = new AsyncChannel();
                mWifiP2pChannel.connect(mContext, getHandler(),
                    mWifiP2pServiceImpl.getP2pStateMachineMessenger());
            }

            if (mWifiApConfigChannel == null) {
                mWifiApConfigChannel = new AsyncChannel();
                mWifiApConfigStore = WifiApConfigStore.makeWifiApConfigStore(
                        mContext, getHandler());
                mWifiApConfigStore.loadApConfiguration();
                mWifiApConfigChannel.connectSync(mContext, getHandler(),
                        mWifiApConfigStore.getMessenger());
            }

            if (mWifiConfigStore.enableHalBasedPno.get()) {
                // make sure developer Settings are in sync with the config option
                mHalBasedPnoEnableInDevSettings = true;
            }
        }
在enter()函数中,比较重要的操作就是mWifiNative.unloadDriver(),当Wifi完全关闭后,将驱动也卸载掉。当下一次Wifi启动流程启动时,再重新加载驱动、启动wpa_s。
到此,Wifi关闭的流程就结束了。


            

你可能感兴趣的:(Android网络接入框架分析)