Home | History | Annotate | Download | only in wifi
      1 /*
      2  * Copyright (C) 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.server.wifi;
     18 
     19 import android.util.Log;
     20 
     21 import java.util.Iterator;
     22 import java.util.LinkedList;
     23 
     24 /**
     25  * This class is used to recover the wifi stack from a fatal failure. The recovery mechanism
     26  * involves triggering a stack restart (essentially simulating an airplane mode toggle) using
     27  * {@link WifiController}.
     28  * The current triggers for:
     29  * 1. Last resort watchdog bite.
     30  * 2. HAL/wificond crashes during normal operation.
     31  * 3. TBD: supplicant crashes during normal operation.
     32  */
     33 public class SelfRecovery {
     34     private static final String TAG = "WifiSelfRecovery";
     35 
     36     /**
     37      * Reason codes for the various recovery triggers.
     38      */
     39     public static final int REASON_LAST_RESORT_WATCHDOG = 0;
     40     public static final int REASON_WIFINATIVE_FAILURE = 1;
     41     public static final int REASON_STA_IFACE_DOWN = 2;
     42     public static final long MAX_RESTARTS_IN_TIME_WINDOW = 2; // 2 restarts per hour
     43     public static final long MAX_RESTARTS_TIME_WINDOW_MILLIS = 60 * 60 * 1000; // 1 hour
     44     protected static final String[] REASON_STRINGS = {
     45             "Last Resort Watchdog",  // REASON_LAST_RESORT_WATCHDOG
     46             "WifiNative Failure",    // REASON_WIFINATIVE_FAILURE
     47             "Sta Interface Down"     // REASON_STA_IFACE_DOWN
     48     };
     49 
     50     private final WifiController mWifiController;
     51     private final Clock mClock;
     52     // Time since boot (in millis) that restart occurred
     53     private final LinkedList<Long> mPastRestartTimes;
     54     public SelfRecovery(WifiController wifiController, Clock clock) {
     55         mWifiController = wifiController;
     56         mClock = clock;
     57         mPastRestartTimes = new LinkedList<Long>();
     58     }
     59 
     60     /**
     61      * Trigger recovery.
     62      *
     63      * This method does the following:
     64      * 1. Checks reason code used to trigger recovery
     65      * 2. Checks for sta iface down triggers and disables wifi by sending {@link
     66      * WifiController#CMD_RECOVERY_DISABLE_WIFI} to {@link WifiController} to disable wifi.
     67      * 3. Throttles restart calls for underlying native failures
     68      * 4. Sends {@link WifiController#CMD_RECOVERY_RESTART_WIFI} to {@link WifiController} to
     69      * initiate the stack restart.
     70      * @param reason One of the above |REASON_*| codes.
     71      */
     72     public void trigger(int reason) {
     73         if (!(reason == REASON_LAST_RESORT_WATCHDOG || reason == REASON_WIFINATIVE_FAILURE
     74                   || reason == REASON_STA_IFACE_DOWN)) {
     75             Log.e(TAG, "Invalid trigger reason. Ignoring...");
     76             return;
     77         }
     78         if (reason == REASON_STA_IFACE_DOWN) {
     79             Log.e(TAG, "STA interface down, disable wifi");
     80             mWifiController.sendMessage(WifiController.CMD_RECOVERY_DISABLE_WIFI);
     81             return;
     82         }
     83 
     84         Log.e(TAG, "Triggering recovery for reason: " + REASON_STRINGS[reason]);
     85         if (reason == REASON_WIFINATIVE_FAILURE) {
     86             trimPastRestartTimes();
     87             // Ensure there haven't been too many restarts within MAX_RESTARTS_TIME_WINDOW
     88             if (mPastRestartTimes.size() >= MAX_RESTARTS_IN_TIME_WINDOW) {
     89                 Log.e(TAG, "Already restarted wifi (" + MAX_RESTARTS_IN_TIME_WINDOW + ") times in"
     90                         + " last (" + MAX_RESTARTS_TIME_WINDOW_MILLIS + "ms ). Disabling wifi");
     91                 mWifiController.sendMessage(WifiController.CMD_RECOVERY_DISABLE_WIFI);
     92                 return;
     93             }
     94             mPastRestartTimes.add(mClock.getElapsedSinceBootMillis());
     95         }
     96         mWifiController.sendMessage(WifiController.CMD_RECOVERY_RESTART_WIFI, reason);
     97     }
     98 
     99     /**
    100      * Process the mPastRestartTimes list, removing elements outside the max restarts time window
    101      */
    102     private void trimPastRestartTimes() {
    103         Iterator<Long> iter = mPastRestartTimes.iterator();
    104         long now = mClock.getElapsedSinceBootMillis();
    105         while (iter.hasNext()) {
    106             Long restartTimeMillis = iter.next();
    107             if (now - restartTimeMillis > MAX_RESTARTS_TIME_WINDOW_MILLIS) {
    108                 iter.remove();
    109             } else {
    110                 break;
    111             }
    112         }
    113     }
    114 }
    115