您的位置:首页 > 移动开发 > Android开发

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

2017-05-10 13:59 465 查看

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

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

一、Wifi的断开

我们断开Wifi,调用WifiManager::disconnect()方法,嵌套调用WifiServiceImpl的同名方法:

[java]
view plain
copy





/** 
 * see {@link android.net.wifi.WifiManager#disconnect()} 
 */  
public void disconnect() {  
    enforceChangePermission();  
    mWifiStateMachine.disconnectCommand();  
}  

调用Wifi状态机的内部方法,发送断开Wifi的指令CMD_DISCONNECT。由于之前网络连接成功时,State Machine停在ConnectedState,所以由它的父状态L2ConnectedState处理该消息:

[java]
view plain
copy





case CMD_DISCONNECT:  
    mWifiNative.disconnect();  
    transitionTo(mDisconnectingState);  
    break;  

通过WifiNative::disconnect()方法下发断开指令,当驱动断开AP连接后,wpa_s反馈断开事件,WifiMonitor接收来自wpa_s的event,并向WifiStateMachine反馈NETWORK_DISCONNECTION_EVENT消息(该过程参考前面的博文)。切换到DisconnectingState状态,断开的消息尤其父状态ConnectModeState处理:

[java]
view plain
copy





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()方法:

[java]
view plain
copy





@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(),接着看该函数做了哪些处理:

[java]
view plain
copy





/** 
     * 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连接断开时,主要会做如下几处理:

stopDhcp():停止DHCP过程,即停止dhcpcd进程

mNwService.clearInterfaceAddresses(mInterfaceName):通过NetworkManagementService通知Netd清除wlan0的地址信息

mWifiInfo.reset():重置WifiInfo对象的状态

setNetworkDetailedState(DetailedState.DISCONNECTED):将mNetworkInfo的连接状态设为DISCONNECTED

mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.DISCONNECTED):将mLastNetworkId对应的 WifiConfiguration对象状态设为DISCONNECTED

clearLinkProperties():清除mLinkProperties对象的配置信息
sendNetworkStateChangeBroadcast():发广播通知上层网络状态变化

registerDisconnected():做一些清理、记录工作

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

二、Wifi的关闭

从前面的博文可知,Wifi的开启、关闭都是调用WifiManager::setWifiEnabled(),嵌套调用WifiServiceImpl中的同名方法,向WifiController发送CMD_WIFI_TOGGLED消息:

[java]
view plain
copy





/** 
 * 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处理:

[java]
view plain
copy





case CMD_WIFI_TOGGLED:  
                   if (! mSettingsStore.isWifiToggleEnabled()) {  
                       if (mSettingsStore.isScanAlwaysAvailable()) {  
                           transitionTo(mStaDisabledWithScanState);  
                       } else {  
                           transitionTo(mApStaDisabledState);  
                       }  
                   }  
                   break;  

结合前面的所讲的内容,可知状态切换到ApStaDisabledState,进行关闭流程,调用它的enter()方法:

[java]
view plain
copy





@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接收、处理该消息:

[java]
view plain
copy





case CMD_STOP_SUPPLICANT:   /* Supplicant stopped by user */  
    if (mP2pSupported) {  
        transitionTo(mWaitForP2pDisableState);  
    } else {  
        transitionTo(mSupplicantStoppingState);  
    }  
    break;  

切换到SupplicantStoppingState,进入enter()方法:

[java]
view plain
copy





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

调用handleNetworkDisconnect()处理AP连接的断开部分,第一部分已经讲过。
设置DhcpStateMachine的状态,停止DhcpStateMachine
调用WifiMonitor::stopSupplicant()停掉wpa_s
将wifi状态设置为disabling;setWifiState()函数会设置当前Wifi的状态,比如disabled、disabling、enabled等;我们通过监听函数发送的WifiManager.WIFI_STATE_CHANGED_ACTION广播,可以得知当前Wifi的具体状态。
WifiMonitor中下发终止指令后,会收到wpa_s断开的event:

[java]
view plain
copy





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处理该消息:

[java]
view plain
copy





case WifiMonitor.SUP_DISCONNECTION_EVENT:  
     if (DBG) log("Supplicant connection lost");  
     handleSupplicantConnectionLoss(false);  
     transitionTo(mInitialState);  
     break;  

[java]
view plain
copy





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:

[java]
view plain
copy





@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关闭的流程就结束了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息