(一百一十三)Android O WifiLastResortWatchdog 学习

版权声明:本文为博主原创文章,未经授权禁止转载,O(∩_∩)O谢谢 https://blog.csdn.net/sinat_20059415/article/details/85196871

1.WifiLastResortWatchdog的初始化

    public WifiInjector(Context context) {
        if (context == null) {
            throw new IllegalStateException(
                    "WifiInjector should not be initialized with a null Context.");
        }

        if (sWifiInjector != null) {
            throw new IllegalStateException(
                    "WifiInjector was already created, use getInstance instead.");
        }

        sWifiInjector = this;
...
        WifiAwareMetrics awareMetrics = new WifiAwareMetrics(mClock);
        mWifiMetrics = new WifiMetrics(mClock, wifiStateMachineLooper, awareMetrics);

...
       mWifiController = new WifiController(mContext, mWifiStateMachine, mSettingsStore,
                mLockManager, mWifiServiceHandlerThread.getLooper(), mFrameworkFacade);
        mSelfRecovery = new SelfRecovery(mWifiController, mClock);
        mWifiLastResortWatchdog = new WifiLastResortWatchdog(mSelfRecovery, mWifiMetrics);
    }

主要类的注释:

1) SelfRecovery

/**
 * This class is used to recover the wifi stack from a fatal failure. The recovery mechanism
 * involves triggering a stack restart (essentially simulating an airplane mode toggle) using
 * {@link WifiController}.
 * The current triggers for:
 * 1. Last resort watchdog bite.
 * 2. HAL/wificond crashes during normal operation.
 * 3. TBD: supplicant crashes during normal operation.
 */
public class SelfRecovery 

2)WifiMetrics

/**
 * Provides storage for wireless connectivity metrics, as they are generated.
 * Metrics logged by this class include:
 *   Aggregated connection stats (num of connections, num of failures, ...)
 *   Discrete connection event stats (time, duration, failure codes, ...)
 *   Router details (technology type, authentication type, ...)
 *   Scan stats
 */
public class WifiMetrics

3)WifiLastResortWatchdog

/**
 * This Class is a Work-In-Progress, intended behavior is as follows:
 * Essentially this class automates a user toggling 'Airplane Mode' when WiFi "won't work".
 * IF each available saved network has failed connecting more times than the FAILURE_THRESHOLD
 * THEN Watchdog will restart Supplicant, wifi driver and return WifiStateMachine to InitialState.
 */
public class WifiLastResortWatchdog

2.WifiStateMachine中的调用

2.1 调用罗列

2.1.1 dhcp失败

    class IpClientCallback extends IpClient.Callback {
        @Override
        public void onPreDhcpAction() {
            sendMessage(DhcpClient.CMD_PRE_DHCP_ACTION);
        }

        @Override
        public void onPostDhcpAction() {
            sendMessage(DhcpClient.CMD_POST_DHCP_ACTION);
        }

        @Override
        public void onNewDhcpResults(DhcpResults dhcpResults) {
            if (dhcpResults != null) {
                sendMessage(CMD_IPV4_PROVISIONING_SUCCESS, dhcpResults);
            } else {
                sendMessage(CMD_IPV4_PROVISIONING_FAILURE);
                mWifiInjector.getWifiLastResortWatchdog().noteConnectionFailureAndTriggerIfNeeded(
                        getTargetSsid(), mTargetRoamBSSID,
                        WifiLastResortWatchdog.FAILURE_CODE_DHCP);
            }
        }

2.1.2 启动supplicant

    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:
...
mWifiInjector.getWifiLastResortWatchdog().clearAllFailureCounts();
                    setSupplicantLogLevel();
                    transitionTo(mSupplicantStartingState);
                    break;

2.1.3 ASSOCIATION_REJECTION_EVENT

    class ConnectModeState extends State {
...

        @Override
        public boolean processMessage(Message message) {
            WifiConfiguration config;
            int netId;
            boolean ok;
            boolean didDisconnect;
            String bssid;
            String ssid;
            NetworkUpdateResult result;
            Set<Integer> removedNetworkIds;
            int reasonCode;
            boolean timedOut;
            logStateAndMessage(message, this);

            switch (message.what) {
                case WifiMonitor.ASSOCIATION_REJECTION_EVENT:
...
                    mWifiInjector.getWifiLastResortWatchdog()
                            .noteConnectionFailureAndTriggerIfNeeded(
                                    getTargetSsid(), bssid,
                                    WifiLastResortWatchdog.FAILURE_CODE_ASSOCIATION);
                    break;

2.1.4 AUTHENTICATION_FAILURE_EVENT

    class ConnectModeState extends State {
...
                case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
...
                    mWifiInjector.getWifiLastResortWatchdog()
                            .noteConnectionFailureAndTriggerIfNeeded(
                                    getTargetSsid(), mTargetRoamBSSID,
                                    WifiLastResortWatchdog.FAILURE_CODE_AUTHENTICATION);

2.1.5 ConnectedState$enter&exit

    class ConnectedState extends State {
        @Override
        public void enter() {
 ...
mWifiInjector.getWifiLastResortWatchdog().connectedStateTransition(true);
            mWifiStateTracker.updateState(WifiStateTracker.CONNECTED);
        }

...
        @Override
        public void exit() {
            logd("WifiStateMachine: Leaving Connected state");
            mWifiConnectivityManager.handleConnectionStateChanged(
                     WifiConnectivityManager.WIFI_STATE_TRANSITIONING);

            mLastDriverRoamAttempt = 0;
            mWifiInjector.getWifiLastResortWatchdog().connectedStateTransition(false);
        }

一共调用了6次。

2.2 WifiStateMachine状态机

扫描二维码关注公众号,回复: 4603851 查看本文章

2.3 流程梳理

2.3.1 清空失败次数

首先肯定是启动supplicant的时候最先调用

    /**
     * Clear failure counts for each network in recentAvailableNetworks
     */
    public void clearAllFailureCounts() {
        if (mVerboseLoggingEnabled) Log.v(TAG, "clearAllFailureCounts.");
        for (Map.Entry<String, AvailableNetworkFailureCount> entry
                : mRecentAvailableNetworks.entrySet()) {
            final AvailableNetworkFailureCount failureCount = entry.getValue();
            entry.getValue().resetCounts();
        }
        for (Map.Entry<String, Pair<AvailableNetworkFailureCount, Integer>> entry
                : mSsidFailureCount.entrySet()) {
            final AvailableNetworkFailureCount failureCount = entry.getValue().first;
            failureCount.resetCounts();
        }
    }

2.3.2 连接过程失败次数统计

    class ConnectModeState extends State {
...

        @Override
        public boolean processMessage(Message message) {
            WifiConfiguration config;
            int netId;
            boolean ok;
            boolean didDisconnect;
            String bssid;
            String ssid;
            NetworkUpdateResult result;
            Set<Integer> removedNetworkIds;
            int reasonCode;
            boolean timedOut;
            logStateAndMessage(message, this);

            switch (message.what) {
                case WifiMonitor.ASSOCIATION_REJECTION_EVENT:
...
                    mWifiInjector.getWifiLastResortWatchdog()
                            .noteConnectionFailureAndTriggerIfNeeded(
                                    getTargetSsid(), bssid,
                                    WifiLastResortWatchdog.FAILURE_CODE_ASSOCIATION);
                    break;
    class ConnectModeState extends State {
...
                case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
...
                    mWifiInjector.getWifiLastResortWatchdog()
                            .noteConnectionFailureAndTriggerIfNeeded(
                                    getTargetSsid(), mTargetRoamBSSID,
                                    WifiLastResortWatchdog.FAILURE_CODE_AUTHENTICATION);
    class IpClientCallback extends IpClient.Callback {
        @Override
        public void onPreDhcpAction() {
            sendMessage(DhcpClient.CMD_PRE_DHCP_ACTION);
        }

        @Override
        public void onPostDhcpAction() {
            sendMessage(DhcpClient.CMD_POST_DHCP_ACTION);
        }

        @Override
        public void onNewDhcpResults(DhcpResults dhcpResults) {
            if (dhcpResults != null) {
                sendMessage(CMD_IPV4_PROVISIONING_SUCCESS, dhcpResults);
            } else {
                sendMessage(CMD_IPV4_PROVISIONING_FAILURE);
                mWifiInjector.getWifiLastResortWatchdog().noteConnectionFailureAndTriggerIfNeeded(
                        getTargetSsid(), mTargetRoamBSSID,
                        WifiLastResortWatchdog.FAILURE_CODE_DHCP);
            }
        }

api注释


    /**
     * Increments the failure reason count for the given bssid. Performs a check to see if we have
     * exceeded a failure threshold for all available networks, and executes the last resort restart
     * @param bssid of the network that has failed connection, can be "any"
     * @param reason Message id from WifiStateMachine for this failure
     * @return true if watchdog triggers, returned for test visibility
     */
    public boolean noteConnectionFailureAndTriggerIfNeeded(String ssid, String bssid, int reason) {

大致意思是说对失败原因进行计数,当达到阈值时进行重启。

    /**
     * Increments the failure reason count for the given bssid. Performs a check to see if we have
     * exceeded a failure threshold for all available networks, and executes the last resort restart
     * @param bssid of the network that has failed connection, can be "any"
     * @param reason Message id from WifiStateMachine for this failure
     * @return true if watchdog triggers, returned for test visibility
     */
    public boolean noteConnectionFailureAndTriggerIfNeeded(String ssid, String bssid, int reason) {
        if (mVerboseLoggingEnabled) {
            Log.v(TAG, "noteConnectionFailureAndTriggerIfNeeded: [" + ssid + ", " + bssid + ", "
                    + reason + "]");
        }
        // Update failure count for the failing network
        updateFailureCountForNetwork(ssid, bssid, reason);

        // Have we met conditions to trigger the Watchdog Wifi restart?
        boolean isRestartNeeded = checkTriggerCondition();
        if (mVerboseLoggingEnabled) {
            Log.v(TAG, "isRestartNeeded = " + isRestartNeeded);
        }
        if (isRestartNeeded) {
            // Stop the watchdog from triggering until re-enabled
            setWatchdogTriggerEnabled(false);
            Log.e(TAG, "Watchdog triggering recovery");
            mSelfRecovery.trigger(SelfRecovery.REASON_LAST_RESORT_WATCHDOG);
            // increment various watchdog trigger count stats
            incrementWifiMetricsTriggerCounts();
            clearAllFailureCounts();
        }
        return isRestartNeeded;
    }
  1. 更新失败次数 updateFailureCountForNetwork
  2. 检验是否需要重启 checkTriggerCondition
  3. 触发重启 mSelfRecovery.trigger(SelfRecovery.REASON_LAST_RESORT_WATCHDOG);

先看下checkTriggerCondition

   /**
     * Check trigger condition: For all available networks, have we met a failure threshold for each
     * of them, and have previously connected to at-least one of the available networks
     * @return is the trigger condition true
     */
    private boolean checkTriggerCondition() {
        if (mVerboseLoggingEnabled) Log.v(TAG, "checkTriggerCondition.");
        // Don't check Watchdog trigger if wifi is in a connected state
        // (This should not occur, but we want to protect against any race conditions)
        if (mWifiIsConnected) return false;
        // Don't check Watchdog trigger if trigger is not enabled
        if (!mWatchdogAllowedToTrigger) return false;

        boolean atleastOneNetworkHasEverConnected = false;
        for (Map.Entry<String, AvailableNetworkFailureCount> entry
                : mRecentAvailableNetworks.entrySet()) {
            if (entry.getValue().config != null
                    && entry.getValue().config.getNetworkSelectionStatus().getHasEverConnected()) {
                atleastOneNetworkHasEverConnected = true;
            }
            if (!isOverFailureThreshold(entry.getKey())) {
                // This available network is not over failure threshold, meaning we still have a
                // network to try connecting to
                return false;
            }
        }
        // We have met the failure count for every available network & there is at-least one network
        // we have previously connected to present.
        if (mVerboseLoggingEnabled) {
            Log.v(TAG, "checkTriggerCondition: return = " + atleastOneNetworkHasEverConnected);
        }
        return atleastOneNetworkHasEverConnected;
    }


    /**
     * @param bssid bssid to check the failures for
     * @return true if any failure count is over FAILURE_THRESHOLD
     */
    public boolean isOverFailureThreshold(String bssid) {
        if ((getFailureCount(bssid, FAILURE_CODE_ASSOCIATION) >= FAILURE_THRESHOLD)
                || (getFailureCount(bssid, FAILURE_CODE_AUTHENTICATION) >= FAILURE_THRESHOLD)
                || (getFailureCount(bssid, FAILURE_CODE_DHCP) >= FAILURE_THRESHOLD)) {
            return true;
        }
        return false;
    }

    /**
     * Failure count that each available networks must meet to possibly trigger the Watchdog
     */
    public static final int FAILURE_THRESHOLD = 7;

阈值为7,当失败次数超过7次并且至少曾经已连接过1次,那么触发重启。

至于重启实现就是通过WifiController发送重启命令

    /**
     * Trigger recovery.
     *
     * This method does the following:
     * 1. Raises a wtf.
     * 2. Sends {@link WifiController#CMD_RESTART_WIFI} to {@link WifiController} to initiate the
     * stack restart.
     * @param reason One of the above |REASON_*| codes.
     */
    public void trigger(int reason) {
        if (!(reason == REASON_LAST_RESORT_WATCHDOG || reason == REASON_HAL_CRASH
                || reason == REASON_WIFICOND_CRASH)) {
            Log.e(TAG, "Invalid trigger reason. Ignoring...");
            return;
        }
        Log.e(TAG, "Triggering recovery for reason: " + REASON_STRINGS[reason]);
        if (reason == REASON_WIFICOND_CRASH || reason == REASON_HAL_CRASH) {
            trimPastRestartTimes();
            // Ensure there haven't been too many restarts within MAX_RESTARTS_TIME_WINDOW
            if (mPastRestartTimes.size() >= MAX_RESTARTS_IN_TIME_WINDOW) {
                Log.e(TAG, "Already restarted wifi (" + MAX_RESTARTS_IN_TIME_WINDOW + ") times in"
                        + " last (" + MAX_RESTARTS_TIME_WINDOW_MILLIS + "ms ). Ignoring...");
                return;
            }
            mPastRestartTimes.add(mClock.getElapsedSinceBootMillis());
        }
        mWifiController.sendMessage(WifiController.CMD_RESTART_WIFI);
    }

2.3.3 已连接状态

    class ConnectedState extends State {
        @Override
        public void enter() {
 ...
mWifiInjector.getWifiLastResortWatchdog().connectedStateTransition(true);
            mWifiStateTracker.updateState(WifiStateTracker.CONNECTED);
        }

...
        @Override
        public void exit() {
            logd("WifiStateMachine: Leaving Connected state");
            mWifiConnectivityManager.handleConnectionStateChanged(
                     WifiConnectivityManager.WIFI_STATE_TRANSITIONING);

            mLastDriverRoamAttempt = 0;
            mWifiInjector.getWifiLastResortWatchdog().connectedStateTransition(false);
        }
    /**
     * Handles transitions entering and exiting WifiStateMachine ConnectedState
     * Used to track wifistate, and perform watchdog count reseting
     * @param isEntering true if called from ConnectedState.enter(), false for exit()
     */
    public void connectedStateTransition(boolean isEntering) {
        if (mVerboseLoggingEnabled) {
            Log.v(TAG, "connectedStateTransition: isEntering = " + isEntering);
        }
        mWifiIsConnected = isEntering;

        if (!mWatchdogAllowedToTrigger) {
            // WiFi has connected after a Watchdog trigger, without any new networks becoming
            // available, log a Watchdog success in wifi metrics
            mWifiMetrics.incrementNumLastResortWatchdogSuccesses();
        }
        if (isEntering) {
            // We connected to something! Reset failure counts for everything
            clearAllFailureCounts();
            // If the watchdog trigger was disabled (it triggered), connecting means we did
            // something right, re-enable it so it can fire again.
            setWatchdogTriggerEnabled(true);
        }
    }

当成功进入已连接状态的话自然是需要清空所有失败计数的,并且重新启用watchdog。进入和退出已连接状态时若watchdog没有被打开,那就增加一下成功计数。

3.总结

WifiLastResortWatchdog总体来看就是对WiFi连接过程的监控,当某一环境失败达到阈值那就触发重启。

猜你喜欢

转载自blog.csdn.net/sinat_20059415/article/details/85196871