1 /*
2  * Copyright (C) 2013 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.content.BroadcastReceiver;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.content.IntentFilter;
23 import android.location.LocationManager;
24 import android.net.wifi.WifiManager;
25 import android.os.Handler;
26 import android.os.Looper;
27 import android.os.Message;
28 import android.os.SystemClock;
29 import android.provider.Settings;
30 import android.util.Log;
31 
32 import com.android.internal.R;
33 import com.android.internal.util.Protocol;
34 import com.android.internal.util.State;
35 import com.android.internal.util.StateMachine;
36 import com.android.server.wifi.util.WifiPermissionsUtil;
37 
38 /**
39  * WifiController is the class used to manage wifi state for various operating
40  * modes (normal, airplane, wifi hotspot, etc.).
41  */
42 public class WifiController extends StateMachine {
43     private static final String TAG = "WifiController";
44     private static final boolean DBG = false;
45     private Context mContext;
46     private boolean mFirstUserSignOnSeen = false;
47 
48     /**
49      * See {@link Settings.Global#WIFI_REENABLE_DELAY_MS}.  This is the default value if a
50      * Settings.Global value is not present.  This is the minimum time after wifi is disabled
51      * we'll act on an enable.  Enable requests received before this delay will be deferred.
52      */
53     private static final long DEFAULT_REENABLE_DELAY_MS = 500;
54 
55     // Maximum limit to use for timeout delay if the value from overlay setting is too large.
56     private static final int MAX_RECOVERY_TIMEOUT_DELAY_MS = 4000;
57 
58     // finding that delayed messages can sometimes be delivered earlier than expected
59     // probably rounding errors.  add a margin to prevent problems
60     private static final long DEFER_MARGIN_MS = 5;
61 
62     /* References to values tracked in WifiService */
63     private final ClientModeImpl mClientModeImpl;
64     private final Looper mClientModeImplLooper;
65     private final ActiveModeWarden mActiveModeWarden;
66     private final WifiSettingsStore mSettingsStore;
67     private final FrameworkFacade mFacade;
68     private final WifiPermissionsUtil mWifiPermissionsUtil;
69 
70     private long mReEnableDelayMillis;
71 
72     private int mRecoveryDelayMillis;
73 
74     private static final int BASE = Protocol.BASE_WIFI_CONTROLLER;
75 
76     static final int CMD_EMERGENCY_MODE_CHANGED                 = BASE + 1;
77     static final int CMD_SCAN_ALWAYS_MODE_CHANGED               = BASE + 7;
78     static final int CMD_WIFI_TOGGLED                           = BASE + 8;
79     static final int CMD_AIRPLANE_TOGGLED                       = BASE + 9;
80     static final int CMD_SET_AP                                 = BASE + 10;
81     static final int CMD_DEFERRED_TOGGLE                        = BASE + 11;
82     static final int CMD_AP_START_FAILURE                       = BASE + 13;
83     static final int CMD_EMERGENCY_CALL_STATE_CHANGED           = BASE + 14;
84     static final int CMD_AP_STOPPED                             = BASE + 15;
85     static final int CMD_STA_START_FAILURE                      = BASE + 16;
86     // Command used to trigger a wifi stack restart when in active mode
87     static final int CMD_RECOVERY_RESTART_WIFI                  = BASE + 17;
88     // Internal command used to complete wifi stack restart
89     private static final int CMD_RECOVERY_RESTART_WIFI_CONTINUE = BASE + 18;
90     // Command to disable wifi when SelfRecovery is throttled or otherwise not doing full recovery
91     static final int CMD_RECOVERY_DISABLE_WIFI                  = BASE + 19;
92     static final int CMD_STA_STOPPED                            = BASE + 20;
93     static final int CMD_SCANNING_STOPPED                       = BASE + 21;
94     static final int CMD_DEFERRED_RECOVERY_RESTART_WIFI         = BASE + 22;
95 
96     private DefaultState mDefaultState = new DefaultState();
97     private StaEnabledState mStaEnabledState = new StaEnabledState();
98     private StaDisabledState mStaDisabledState = new StaDisabledState();
99     private StaDisabledWithScanState mStaDisabledWithScanState = new StaDisabledWithScanState();
100     private EcmState mEcmState = new EcmState();
101 
102     private ScanOnlyModeManager.Listener mScanOnlyModeCallback = new ScanOnlyCallback();
103     private ClientModeManager.Listener mClientModeCallback = new ClientModeCallback();
104 
WifiController(Context context, ClientModeImpl clientModeImpl, Looper clientModeImplLooper, WifiSettingsStore wss, Looper wifiServiceLooper, FrameworkFacade f, ActiveModeWarden amw, WifiPermissionsUtil wifiPermissionsUtil)105     WifiController(Context context, ClientModeImpl clientModeImpl, Looper clientModeImplLooper,
106                    WifiSettingsStore wss, Looper wifiServiceLooper, FrameworkFacade f,
107                    ActiveModeWarden amw, WifiPermissionsUtil wifiPermissionsUtil) {
108         super(TAG, wifiServiceLooper);
109         mFacade = f;
110         mContext = context;
111         mClientModeImpl = clientModeImpl;
112         mClientModeImplLooper = clientModeImplLooper;
113         mActiveModeWarden = amw;
114         mSettingsStore = wss;
115         mWifiPermissionsUtil = wifiPermissionsUtil;
116 
117         // CHECKSTYLE:OFF IndentationCheck
118         addState(mDefaultState);
119             addState(mStaDisabledState, mDefaultState);
120             addState(mStaEnabledState, mDefaultState);
121             addState(mStaDisabledWithScanState, mDefaultState);
122             addState(mEcmState, mDefaultState);
123         // CHECKSTYLE:ON IndentationCheck
124 
125         setLogRecSize(100);
126         setLogOnlyTransitions(false);
127 
128         // register for state updates via callbacks (vs the intents registered below)
129         mActiveModeWarden.registerScanOnlyCallback(mScanOnlyModeCallback);
130         mActiveModeWarden.registerClientModeCallback(mClientModeCallback);
131 
132         readWifiReEnableDelay();
133         readWifiRecoveryDelay();
134     }
135 
136     @Override
start()137     public void start() {
138         boolean isAirplaneModeOn = mSettingsStore.isAirplaneModeOn();
139         boolean isWifiEnabled = mSettingsStore.isWifiToggleEnabled();
140         boolean isScanningAlwaysAvailable = mSettingsStore.isScanAlwaysAvailable();
141         boolean isLocationModeActive = mWifiPermissionsUtil.isLocationModeEnabled();
142 
143         log("isAirplaneModeOn = " + isAirplaneModeOn
144                 + ", isWifiEnabled = " + isWifiEnabled
145                 + ", isScanningAvailable = " + isScanningAlwaysAvailable
146                 + ", isLocationModeActive = " + isLocationModeActive);
147 
148         if (checkScanOnlyModeAvailable()) {
149             setInitialState(mStaDisabledWithScanState);
150         } else {
151             setInitialState(mStaDisabledState);
152         }
153         IntentFilter filter = new IntentFilter();
154         filter.addAction(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
155         filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
156         filter.addAction(LocationManager.MODE_CHANGED_ACTION);
157         mContext.registerReceiver(
158                 new BroadcastReceiver() {
159                     @Override
160                     public void onReceive(Context context, Intent intent) {
161                         String action = intent.getAction();
162                         if (action.equals(WifiManager.WIFI_AP_STATE_CHANGED_ACTION)) {
163                             int state = intent.getIntExtra(
164                                     WifiManager.EXTRA_WIFI_AP_STATE,
165                                     WifiManager.WIFI_AP_STATE_FAILED);
166                             if (state == WifiManager.WIFI_AP_STATE_FAILED) {
167                                 Log.e(TAG, "SoftAP start failed");
168                                 sendMessage(CMD_AP_START_FAILURE);
169                             } else if (state == WifiManager.WIFI_AP_STATE_DISABLED) {
170                                 sendMessage(CMD_AP_STOPPED);
171                             }
172                         } else if (action.equals(LocationManager.MODE_CHANGED_ACTION)) {
173                             // Location mode has been toggled...  trigger with the scan change
174                             // update to make sure we are in the correct mode
175                             sendMessage(CMD_SCAN_ALWAYS_MODE_CHANGED);
176                         }
177                     }
178                 },
179                 new IntentFilter(filter));
180         super.start();
181     }
182 
checkScanOnlyModeAvailable()183     private boolean checkScanOnlyModeAvailable() {
184         // first check if Location service is disabled, if so return false
185         if (!mWifiPermissionsUtil.isLocationModeEnabled()) {
186             return false;
187         }
188         return mSettingsStore.isScanAlwaysAvailable();
189     }
190 
191     /**
192      * Listener used to receive scan mode updates - really needed for disabled updates to trigger
193      * mode changes.
194      */
195     private class ScanOnlyCallback implements ScanOnlyModeManager.Listener {
196         @Override
onStateChanged(int state)197         public void onStateChanged(int state) {
198             if (state == WifiManager.WIFI_STATE_UNKNOWN) {
199                 Log.d(TAG, "ScanOnlyMode unexpected failure: state unknown");
200             } else if (state == WifiManager.WIFI_STATE_DISABLED) {
201                 Log.d(TAG, "ScanOnlyMode stopped");
202                 sendMessage(CMD_SCANNING_STOPPED);
203             } else if (state == WifiManager.WIFI_STATE_ENABLED) {
204                 // scan mode is ready to go
205                 Log.d(TAG, "scan mode active");
206             } else {
207                 Log.d(TAG, "unexpected state update: " + state);
208             }
209         }
210     }
211 
212     /**
213      * Listener used to receive client mode updates
214      */
215     private class ClientModeCallback implements ClientModeManager.Listener {
216         @Override
onStateChanged(int state)217         public void onStateChanged(int state) {
218             if (state == WifiManager.WIFI_STATE_UNKNOWN) {
219                 logd("ClientMode unexpected failure: state unknown");
220                 sendMessage(CMD_STA_START_FAILURE);
221             } else if (state == WifiManager.WIFI_STATE_DISABLED) {
222                 logd("ClientMode stopped");
223                 sendMessage(CMD_STA_STOPPED);
224             } else if (state == WifiManager.WIFI_STATE_ENABLED) {
225                 // scan mode is ready to go
226                 logd("client mode active");
227             } else {
228                 logd("unexpected state update: " + state);
229             }
230         }
231     }
232 
readWifiReEnableDelay()233     private void readWifiReEnableDelay() {
234         mReEnableDelayMillis = mFacade.getLongSetting(mContext,
235                 Settings.Global.WIFI_REENABLE_DELAY_MS, DEFAULT_REENABLE_DELAY_MS);
236     }
237 
readWifiRecoveryDelay()238     private void readWifiRecoveryDelay() {
239         mRecoveryDelayMillis = mContext.getResources().getInteger(
240                 R.integer.config_wifi_framework_recovery_timeout_delay);
241         if (mRecoveryDelayMillis > MAX_RECOVERY_TIMEOUT_DELAY_MS) {
242             mRecoveryDelayMillis = MAX_RECOVERY_TIMEOUT_DELAY_MS;
243             Log.w(TAG, "Overriding timeout delay with maximum limit value");
244         }
245     }
246 
247     class DefaultState extends State {
248         @Override
processMessage(Message msg)249         public boolean processMessage(Message msg) {
250             switch (msg.what) {
251                 case CMD_SCAN_ALWAYS_MODE_CHANGED:
252                 case CMD_WIFI_TOGGLED:
253                 case CMD_AP_START_FAILURE:
254                 case CMD_SCANNING_STOPPED:
255                 case CMD_STA_STOPPED:
256                 case CMD_STA_START_FAILURE:
257                 case CMD_RECOVERY_RESTART_WIFI_CONTINUE:
258                 case CMD_DEFERRED_RECOVERY_RESTART_WIFI:
259                     break;
260                 case CMD_RECOVERY_DISABLE_WIFI:
261                     log("Recovery has been throttled, disable wifi");
262                     mActiveModeWarden.shutdownWifi();
263                     transitionTo(mStaDisabledState);
264                     break;
265                 case CMD_RECOVERY_RESTART_WIFI:
266                     deferMessage(obtainMessage(CMD_DEFERRED_RECOVERY_RESTART_WIFI));
267                     mActiveModeWarden.shutdownWifi();
268                     transitionTo(mStaDisabledState);
269                     break;
270                 case CMD_DEFERRED_TOGGLE:
271                     log("DEFERRED_TOGGLE ignored due to state change");
272                     break;
273                 case CMD_SET_AP:
274                     // note: CMD_SET_AP is handled/dropped in ECM mode - will not start here
275                     if (msg.arg1 == 1) {
276                         SoftApModeConfiguration config = (SoftApModeConfiguration) msg.obj;
277                         mActiveModeWarden.enterSoftAPMode((SoftApModeConfiguration) msg.obj);
278                     } else {
279                         mActiveModeWarden.stopSoftAPMode(msg.arg2);
280                     }
281                     break;
282                 case CMD_AIRPLANE_TOGGLED:
283                     if (mSettingsStore.isAirplaneModeOn()) {
284                         log("Airplane mode toggled, shutdown all modes");
285                         mActiveModeWarden.shutdownWifi();
286                         transitionTo(mStaDisabledState);
287                     } else {
288                         log("Airplane mode disabled, determine next state");
289                         if (mSettingsStore.isWifiToggleEnabled()) {
290                             transitionTo(mStaEnabledState);
291                         } else if (checkScanOnlyModeAvailable()) {
292                             transitionTo(mStaDisabledWithScanState);
293                         }
294                         // wifi should remain disabled, do not need to transition
295                     }
296                     break;
297                 case CMD_EMERGENCY_CALL_STATE_CHANGED:
298                 case CMD_EMERGENCY_MODE_CHANGED:
299                     if (msg.arg1 == 1) {
300                         transitionTo(mEcmState);
301                     }
302                     break;
303                 case CMD_AP_STOPPED:
304                     log("SoftAp mode disabled, determine next state");
305                     if (mSettingsStore.isWifiToggleEnabled()) {
306                         transitionTo(mStaEnabledState);
307                     } else if (checkScanOnlyModeAvailable()) {
308                         transitionTo(mStaDisabledWithScanState);
309                     }
310                     // wifi should remain disabled, do not need to transition
311                     break;
312                 default:
313                     throw new RuntimeException("WifiController.handleMessage " + msg.what);
314             }
315             return HANDLED;
316         }
317 
318     }
319 
320     class StaDisabledState extends State {
321         private int mDeferredEnableSerialNumber = 0;
322         private boolean mHaveDeferredEnable = false;
323         private long mDisabledTimestamp;
324 
325         @Override
enter()326         public void enter() {
327             mActiveModeWarden.disableWifi();
328             // Supplicant can't restart right away, so note the time we switched off
329             mDisabledTimestamp = SystemClock.elapsedRealtime();
330             mDeferredEnableSerialNumber++;
331             mHaveDeferredEnable = false;
332         }
333         @Override
processMessage(Message msg)334         public boolean processMessage(Message msg) {
335             switch (msg.what) {
336                 case CMD_WIFI_TOGGLED:
337                     if (mSettingsStore.isWifiToggleEnabled()) {
338                         if (doDeferEnable(msg)) {
339                             if (mHaveDeferredEnable) {
340                                 //  have 2 toggles now, inc serial number and ignore both
341                                 mDeferredEnableSerialNumber++;
342                             }
343                             mHaveDeferredEnable = !mHaveDeferredEnable;
344                             break;
345                         }
346                         transitionTo(mStaEnabledState);
347                     } else if (checkScanOnlyModeAvailable()) {
348                         // only go to scan mode if we aren't in airplane mode
349                         if (mSettingsStore.isAirplaneModeOn()) {
350                             transitionTo(mStaDisabledWithScanState);
351                         }
352                     }
353                     break;
354                 case CMD_SCAN_ALWAYS_MODE_CHANGED:
355                     if (checkScanOnlyModeAvailable()) {
356                         transitionTo(mStaDisabledWithScanState);
357                         break;
358                     }
359                     break;
360                 case CMD_SET_AP:
361                     if (msg.arg1 == 1) {
362                         // remember that we were disabled, but pass the command up to start softap
363                         mSettingsStore.setWifiSavedState(WifiSettingsStore.WIFI_DISABLED);
364                     }
365                     return NOT_HANDLED;
366                 case CMD_DEFERRED_TOGGLE:
367                     if (msg.arg1 != mDeferredEnableSerialNumber) {
368                         log("DEFERRED_TOGGLE ignored due to serial mismatch");
369                         break;
370                     }
371                     log("DEFERRED_TOGGLE handled");
372                     sendMessage((Message)(msg.obj));
373                     break;
374                 case CMD_DEFERRED_RECOVERY_RESTART_WIFI:
375                     // wait mRecoveryDelayMillis for letting driver clean reset.
376                     sendMessageDelayed(CMD_RECOVERY_RESTART_WIFI_CONTINUE, mRecoveryDelayMillis);
377                     break;
378                 case CMD_RECOVERY_RESTART_WIFI_CONTINUE:
379                     if (mSettingsStore.isWifiToggleEnabled()) {
380                         // wifi is currently disabled but the toggle is on, must have had an
381                         // interface down before the recovery triggered
382                         transitionTo(mStaEnabledState);
383                         break;
384                     } else if (checkScanOnlyModeAvailable()) {
385                         transitionTo(mStaDisabledWithScanState);
386                         break;
387                     }
388                     break;
389                 default:
390                     return NOT_HANDLED;
391             }
392             return HANDLED;
393         }
394 
doDeferEnable(Message msg)395         private boolean doDeferEnable(Message msg) {
396             long delaySoFar = SystemClock.elapsedRealtime() - mDisabledTimestamp;
397             if (delaySoFar >= mReEnableDelayMillis) {
398                 return false;
399             }
400 
401             log("WifiController msg " + msg + " deferred for " +
402                     (mReEnableDelayMillis - delaySoFar) + "ms");
403 
404             // need to defer this action.
405             Message deferredMsg = obtainMessage(CMD_DEFERRED_TOGGLE);
406             deferredMsg.obj = Message.obtain(msg);
407             deferredMsg.arg1 = ++mDeferredEnableSerialNumber;
408             sendMessageDelayed(deferredMsg, mReEnableDelayMillis - delaySoFar + DEFER_MARGIN_MS);
409             return true;
410         }
411 
412     }
413 
414     class StaEnabledState extends State {
415         @Override
enter()416         public void enter() {
417             log("StaEnabledState.enter()");
418             mActiveModeWarden.enterClientMode();
419         }
420 
421         @Override
processMessage(Message msg)422         public boolean processMessage(Message msg) {
423             switch (msg.what) {
424                 case CMD_WIFI_TOGGLED:
425                     if (! mSettingsStore.isWifiToggleEnabled()) {
426                         if (checkScanOnlyModeAvailable()) {
427                             transitionTo(mStaDisabledWithScanState);
428                         } else {
429                             transitionTo(mStaDisabledState);
430                         }
431                     }
432                     break;
433                 case CMD_AIRPLANE_TOGGLED:
434                     // airplane mode toggled on is handled in the default state
435                     if (mSettingsStore.isAirplaneModeOn()) {
436                         return NOT_HANDLED;
437                     } else {
438                         // when airplane mode is toggled off, but wifi is on, we can keep it on
439                         log("airplane mode toggled - and airplane mode is off.  return handled");
440                         return HANDLED;
441                     }
442                 case CMD_STA_START_FAILURE:
443                     if (!checkScanOnlyModeAvailable()) {
444                         transitionTo(mStaDisabledState);
445                     } else {
446                         transitionTo(mStaDisabledWithScanState);
447                     }
448                     break;
449                 case CMD_SET_AP:
450                     if (msg.arg1 == 1) {
451                         // remember that we were enabled, but pass the command up to start softap
452                         mSettingsStore.setWifiSavedState(WifiSettingsStore.WIFI_ENABLED);
453                     }
454                     return NOT_HANDLED;
455                 case CMD_AP_START_FAILURE:
456                 case CMD_AP_STOPPED:
457                     // already in a wifi mode, no need to check where we should go with softap
458                     // stopped
459                     break;
460                 case CMD_STA_STOPPED:
461                     // Client mode stopped.  head to Disabled to wait for next command
462                     transitionTo(mStaDisabledState);
463                     break;
464                 case CMD_RECOVERY_RESTART_WIFI:
465                     final String bugTitle;
466                     final String bugDetail;
467                     if (msg.arg1 < SelfRecovery.REASON_STRINGS.length && msg.arg1 >= 0) {
468                         bugDetail = SelfRecovery.REASON_STRINGS[msg.arg1];
469                         bugTitle = "Wi-Fi BugReport: " + bugDetail;
470                     } else {
471                         bugDetail = "";
472                         bugTitle = "Wi-Fi BugReport";
473                     }
474                     if (msg.arg1 != SelfRecovery.REASON_LAST_RESORT_WATCHDOG) {
475                         (new Handler(mClientModeImplLooper)).post(() -> {
476                             mClientModeImpl.takeBugReport(bugTitle, bugDetail);
477                         });
478                     }
479                     // after the bug report trigger, more handling needs to be done
480                     return NOT_HANDLED;
481                 default:
482                     return NOT_HANDLED;
483             }
484             return HANDLED;
485         }
486     }
487 
488     class StaDisabledWithScanState extends State {
489         private int mDeferredEnableSerialNumber = 0;
490         private boolean mHaveDeferredEnable = false;
491         private long mDisabledTimestamp;
492 
493         @Override
enter()494         public void enter() {
495             // now trigger the actual mode switch in ActiveModeWarden
496             mActiveModeWarden.enterScanOnlyMode();
497 
498             // TODO b/71559473: remove the defered enable after mode management changes are complete
499             // Supplicant can't restart right away, so not the time we switched off
500             mDisabledTimestamp = SystemClock.elapsedRealtime();
501             mDeferredEnableSerialNumber++;
502             mHaveDeferredEnable = false;
503         }
504 
505         @Override
processMessage(Message msg)506         public boolean processMessage(Message msg) {
507             switch (msg.what) {
508                 case CMD_WIFI_TOGGLED:
509                     if (mSettingsStore.isWifiToggleEnabled()) {
510                         if (doDeferEnable(msg)) {
511                             if (mHaveDeferredEnable) {
512                                 // have 2 toggles now, inc serial number and ignore both
513                                 mDeferredEnableSerialNumber++;
514                             }
515                             mHaveDeferredEnable = !mHaveDeferredEnable;
516                             break;
517                         }
518                         transitionTo(mStaEnabledState);
519                     }
520                     break;
521                 case CMD_SCAN_ALWAYS_MODE_CHANGED:
522                     if (!checkScanOnlyModeAvailable()) {
523                         log("StaDisabledWithScanState: scan no longer available");
524                         transitionTo(mStaDisabledState);
525                     }
526                     break;
527                 case CMD_SET_AP:
528                     if (msg.arg1 == 1) {
529                         // remember that we were disabled, but pass the command up to start softap
530                         mSettingsStore.setWifiSavedState(WifiSettingsStore.WIFI_DISABLED);
531                     }
532                     return NOT_HANDLED;
533                 case CMD_DEFERRED_TOGGLE:
534                     if (msg.arg1 != mDeferredEnableSerialNumber) {
535                         log("DEFERRED_TOGGLE ignored due to serial mismatch");
536                         break;
537                     }
538                     logd("DEFERRED_TOGGLE handled");
539                     sendMessage((Message)(msg.obj));
540                     break;
541                 case CMD_AP_START_FAILURE:
542                 case CMD_AP_STOPPED:
543                     // already in a wifi mode, no need to check where we should go with softap
544                     // stopped
545                     break;
546                 case CMD_SCANNING_STOPPED:
547                     // stopped due to interface destruction - return to disabled and wait
548                     log("WifiController: SCANNING_STOPPED when in scan mode -> StaDisabled");
549                     transitionTo(mStaDisabledState);
550                     break;
551                 default:
552                     return NOT_HANDLED;
553             }
554             return HANDLED;
555         }
556 
doDeferEnable(Message msg)557         private boolean doDeferEnable(Message msg) {
558             long delaySoFar = SystemClock.elapsedRealtime() - mDisabledTimestamp;
559             if (delaySoFar >= mReEnableDelayMillis) {
560                 return false;
561             }
562 
563             log("WifiController msg " + msg + " deferred for " +
564                     (mReEnableDelayMillis - delaySoFar) + "ms");
565 
566             // need to defer this action.
567             Message deferredMsg = obtainMessage(CMD_DEFERRED_TOGGLE);
568             deferredMsg.obj = Message.obtain(msg);
569             deferredMsg.arg1 = ++mDeferredEnableSerialNumber;
570             sendMessageDelayed(deferredMsg, mReEnableDelayMillis - delaySoFar + DEFER_MARGIN_MS);
571             return true;
572         }
573 
574     }
575 
576     /**
577      * Determine the next state based on the current settings (e.g. saved
578      * wifi state).
579      */
getNextWifiState()580     private State getNextWifiState() {
581         if (mSettingsStore.getWifiSavedState() == WifiSettingsStore.WIFI_ENABLED) {
582             return mStaEnabledState;
583         }
584 
585         if (checkScanOnlyModeAvailable()) {
586             return mStaDisabledWithScanState;
587         }
588 
589         return mStaDisabledState;
590     }
591 
592     class EcmState extends State {
593         // we can enter EcmState either because an emergency call started or because
594         // emergency callback mode started. This count keeps track of how many such
595         // events happened; so we can exit after all are undone
596 
597         private int mEcmEntryCount;
598         @Override
enter()599         public void enter() {
600             mActiveModeWarden.stopSoftAPMode(WifiManager.IFACE_IP_MODE_UNSPECIFIED);
601             boolean configWiFiDisableInECBM =
602                     mFacade.getConfigWiFiDisableInECBM(mContext);
603             log("WifiController msg getConfigWiFiDisableInECBM "
604                     + configWiFiDisableInECBM);
605             if (configWiFiDisableInECBM) {
606                 mActiveModeWarden.shutdownWifi();
607             }
608             mEcmEntryCount = 1;
609         }
610 
611         /**
612          * Handles messages received while in EcmMode.
613          */
614         @Override
processMessage(Message msg)615         public boolean processMessage(Message msg) {
616             switch (msg.what) {
617                 case CMD_EMERGENCY_CALL_STATE_CHANGED:
618                     if (msg.arg1 == 1) {
619                         // nothing to do - just says emergency call started
620                         mEcmEntryCount++;
621                     } else if (msg.arg1 == 0) {
622                         // emergency call ended
623                         decrementCountAndReturnToAppropriateState();
624                     }
625                     return HANDLED;
626                 case CMD_EMERGENCY_MODE_CHANGED:
627                     if (msg.arg1 == 1) {
628                         // Transitioned into emergency callback mode
629                         mEcmEntryCount++;
630                     } else if (msg.arg1 == 0) {
631                         // out of emergency callback mode
632                         decrementCountAndReturnToAppropriateState();
633                     }
634                     return HANDLED;
635                 case CMD_RECOVERY_RESTART_WIFI:
636                 case CMD_RECOVERY_DISABLE_WIFI:
637                     // do not want to restart wifi if we are in emergency mode
638                     return HANDLED;
639                 case CMD_AP_STOPPED:
640                 case CMD_SCANNING_STOPPED:
641                 case CMD_STA_STOPPED:
642                     // do not want to trigger a mode switch if we are in emergency mode
643                     return HANDLED;
644                 case CMD_SET_AP:
645                     // do not want to start softap if we are in emergency mode
646                     return HANDLED;
647                 default:
648                     return NOT_HANDLED;
649             }
650         }
651 
decrementCountAndReturnToAppropriateState()652         private void decrementCountAndReturnToAppropriateState() {
653             boolean exitEcm = false;
654 
655             if (mEcmEntryCount == 0) {
656                 loge("mEcmEntryCount is 0; exiting Ecm");
657                 exitEcm = true;
658             } else if (--mEcmEntryCount == 0) {
659                 exitEcm = true;
660             }
661 
662             if (exitEcm) {
663                 if (mSettingsStore.isWifiToggleEnabled()) {
664                     transitionTo(mStaEnabledState);
665                 } else if (checkScanOnlyModeAvailable()) {
666                     transitionTo(mStaDisabledWithScanState);
667                 } else {
668                     transitionTo(mStaDisabledState);
669                 }
670             }
671         }
672     }
673 }
674