• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.annotation.IntDef;
20 import android.content.Context;
21 import android.util.Log;
22 
23 import com.android.wifi.resources.R;
24 
25 import java.lang.annotation.Retention;
26 import java.lang.annotation.RetentionPolicy;
27 import java.util.Iterator;
28 import java.util.LinkedList;
29 import java.util.concurrent.TimeUnit;
30 
31 /**
32  * This class is used to recover the wifi stack from a fatal failure. The recovery mechanism
33  * involves triggering a stack restart (essentially simulating an airplane mode toggle) using
34  * {@link ActiveModeWarden}.
35  * The current triggers for:
36  * 1. Last resort watchdog bite.
37  * 2. HAL/wificond crashes during normal operation.
38  * 3. TBD: supplicant crashes during normal operation.
39  */
40 public class SelfRecovery {
41     private static final String TAG = "WifiSelfRecovery";
42 
43     /**
44      * Reason codes for the various recovery triggers.
45      */
46     public static final int REASON_LAST_RESORT_WATCHDOG = 0;
47     public static final int REASON_WIFINATIVE_FAILURE = 1;
48     public static final int REASON_STA_IFACE_DOWN = 2;
49     public static final int REASON_API_CALL = 3;
50     public static final int REASON_SUBSYSTEM_RESTART = 4;
51 
52     @Retention(RetentionPolicy.SOURCE)
53     @IntDef(prefix = {"REASON_"}, value = {
54             REASON_LAST_RESORT_WATCHDOG,
55             REASON_WIFINATIVE_FAILURE,
56             REASON_STA_IFACE_DOWN,
57             REASON_API_CALL,
58             REASON_SUBSYSTEM_RESTART})
59     public @interface RecoveryReason {}
60 
61     protected static final String[] REASON_STRINGS = {
62             "Last Resort Watchdog",  // REASON_LAST_RESORT_WATCHDOG
63             "WifiNative Failure",    // REASON_WIFINATIVE_FAILURE
64             "Sta Interface Down",    // REASON_STA_IFACE_DOWN
65             "API call (e.g. user)",  // REASON_API_CALL
66             "Subsystem Restart"      // REASON_SUBSYSTEM_RESTART
67     };
68 
69     private final Context mContext;
70     private final ActiveModeWarden mActiveModeWarden;
71     private final Clock mClock;
72     // Time since boot (in millis) that restart occurred
73     private final LinkedList<Long> mPastRestartTimes;
74     private final WifiNative mWifiNative;
75     private int mSelfRecoveryReason;
76     private boolean mDidWeTriggerSelfRecovery;
77     private SubsystemRestartListenerInternal mSubsystemRestartListener;
78 
79     private class SubsystemRestartListenerInternal
80             implements HalDeviceManager.SubsystemRestartListener{
81         @Override
onSubsystemRestart()82         public void onSubsystemRestart() {
83             String reasonString =  "";
84             if (!mDidWeTriggerSelfRecovery) {
85                 // We did not trigger recovery, but looks like the firmware crashed?
86                 mSelfRecoveryReason = REASON_SUBSYSTEM_RESTART;
87             }
88 
89             if (mSelfRecoveryReason < REASON_STRINGS.length && mSelfRecoveryReason >= 0) {
90                 reasonString = REASON_STRINGS[mSelfRecoveryReason];
91             }
92 
93             Log.e(TAG, "Restarting wifi for reason: " + reasonString);
94             mActiveModeWarden.recoveryRestartWifi(mSelfRecoveryReason, reasonString,
95                     mSelfRecoveryReason != REASON_LAST_RESORT_WATCHDOG
96                      && mSelfRecoveryReason != REASON_API_CALL);
97 
98             mDidWeTriggerSelfRecovery = false;
99         }
100     }
101 
SelfRecovery(Context context, ActiveModeWarden activeModeWarden, Clock clock, WifiNative wifiNative)102     public SelfRecovery(Context context, ActiveModeWarden activeModeWarden,
103             Clock clock, WifiNative wifiNative) {
104         mContext = context;
105         mActiveModeWarden = activeModeWarden;
106         mClock = clock;
107         mPastRestartTimes = new LinkedList<>();
108         mWifiNative = wifiNative;
109         mSubsystemRestartListener = new SubsystemRestartListenerInternal();
110         mWifiNative.registerSubsystemRestartListener(mSubsystemRestartListener);
111         mDidWeTriggerSelfRecovery = false;
112     }
113 
114     /**
115      * Trigger recovery.
116      *
117      * This method does the following:
118      * 1. Checks reason code used to trigger recovery
119      * 2. Checks for sta iface down triggers and disables wifi by sending {@link
120      * ActiveModeWarden#recoveryDisableWifi()} to {@link ActiveModeWarden} to disable wifi.
121      * 3. Throttles restart calls for underlying native failures
122      * 4. Sends {@link ActiveModeWarden#recoveryRestartWifi(int)} to {@link ActiveModeWarden} to
123      * initiate the stack restart.
124      * @param reason One of the above |REASON_*| codes.
125      */
trigger(@ecoveryReason int reason)126     public void trigger(@RecoveryReason int reason) {
127         if (!(reason == REASON_LAST_RESORT_WATCHDOG || reason == REASON_WIFINATIVE_FAILURE
128                   || reason == REASON_STA_IFACE_DOWN || reason == REASON_API_CALL)) {
129             Log.e(TAG, "Invalid trigger reason. Ignoring...");
130             return;
131         }
132         if (reason == REASON_STA_IFACE_DOWN) {
133             Log.e(TAG, "STA interface down, disable wifi");
134             mActiveModeWarden.recoveryDisableWifi();
135             return;
136         }
137 
138         Log.e(TAG, "Triggering recovery for reason: " + REASON_STRINGS[reason]);
139         if (reason == REASON_WIFINATIVE_FAILURE) {
140             int maxRecoveriesPerHour = mContext.getResources().getInteger(
141                     R.integer.config_wifiMaxNativeFailureSelfRecoveryPerHour);
142             if (maxRecoveriesPerHour == 0) {
143                 Log.e(TAG, "Recovery disabled. Disabling wifi");
144                 mActiveModeWarden.recoveryDisableWifi();
145                 return;
146             }
147             trimPastRestartTimes();
148             if (mPastRestartTimes.size() >= maxRecoveriesPerHour) {
149                 Log.e(TAG, "Already restarted wifi " + maxRecoveriesPerHour + " times in"
150                         + " last 1 hour. Disabling wifi");
151                 mActiveModeWarden.recoveryDisableWifi();
152                 return;
153             }
154             mPastRestartTimes.add(mClock.getElapsedSinceBootMillis());
155         }
156 
157         mSelfRecoveryReason = reason;
158         mDidWeTriggerSelfRecovery = true;
159         if (!mWifiNative.startSubsystemRestart()) {
160             // HAL call failed, fallback to internal flow.
161             mSubsystemRestartListener.onSubsystemRestart();
162         }
163     }
164 
165     /**
166      * Process the mPastRestartTimes list, removing elements outside the max restarts time window
167      */
trimPastRestartTimes()168     private void trimPastRestartTimes() {
169         Iterator<Long> iter = mPastRestartTimes.iterator();
170         long now = mClock.getElapsedSinceBootMillis();
171         while (iter.hasNext()) {
172             Long restartTimeMillis = iter.next();
173             if (now - restartTimeMillis > TimeUnit.HOURS.toMillis(1)) {
174                 iter.remove();
175             } else {
176                 break;
177             }
178         }
179     }
180 }
181