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; SelfRecovery(WifiController wifiController, Clock clock)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 */ trigger(int reason)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 */ trimPastRestartTimes()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