• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.app.AlarmManager;
20 import android.app.PendingIntent;
21 import android.content.BroadcastReceiver;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.content.IntentFilter;
25 import android.database.ContentObserver;
26 import android.net.ConnectivityManager;
27 import android.net.NetworkInfo;
28 import android.net.wifi.WifiConfiguration;
29 import android.net.wifi.WifiManager;
30 import static android.net.wifi.WifiManager.WIFI_MODE_FULL;
31 import static android.net.wifi.WifiManager.WIFI_MODE_FULL_HIGH_PERF;
32 import static android.net.wifi.WifiManager.WIFI_MODE_SCAN_ONLY;
33 import android.net.wifi.WifiStateMachine;
34 import android.os.Handler;
35 import android.os.Looper;
36 import android.os.Message;
37 import android.os.SystemClock;
38 import android.os.WorkSource;
39 import android.provider.Settings;
40 import android.util.Slog;
41 
42 import com.android.internal.util.Protocol;
43 import com.android.internal.util.State;
44 import com.android.internal.util.StateMachine;
45 import com.android.server.wifi.WifiService.LockList;
46 
47 import java.io.FileDescriptor;
48 import java.io.PrintWriter;
49 
50 class WifiController extends StateMachine {
51     private static final String TAG = "WifiController";
52     private static final boolean DBG = false;
53     private Context mContext;
54     private boolean mScreenOff;
55     private boolean mDeviceIdle;
56     private int mPluggedType;
57     private int mStayAwakeConditions;
58     private long mIdleMillis;
59     private int mSleepPolicy;
60     private boolean mFirstUserSignOnSeen = false;
61 
62     private AlarmManager mAlarmManager;
63     private PendingIntent mIdleIntent;
64     private static final int IDLE_REQUEST = 0;
65 
66     /**
67      * See {@link Settings.Global#WIFI_IDLE_MS}. This is the default value if a
68      * Settings.Global value is not present. This timeout value is chosen as
69      * the approximate point at which the battery drain caused by Wi-Fi
70      * being enabled but not active exceeds the battery drain caused by
71      * re-establishing a connection to the mobile data network.
72      */
73     private static final long DEFAULT_IDLE_MS = 15 * 60 * 1000; /* 15 minutes */
74 
75     /**
76      * See {@link Settings.Global#WIFI_REENABLE_DELAY_MS}.  This is the default value if a
77      * Settings.Global value is not present.  This is the minimum time after wifi is disabled
78      * we'll act on an enable.  Enable requests received before this delay will be deferred.
79      */
80     private static final long DEFAULT_REENABLE_DELAY_MS = 500;
81 
82     // finding that delayed messages can sometimes be delivered earlier than expected
83     // probably rounding errors..  add a margin to prevent problems
84     private static final long DEFER_MARGIN_MS = 5;
85 
86     NetworkInfo mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0, "WIFI", "");
87 
88     private static final String ACTION_DEVICE_IDLE =
89             "com.android.server.WifiManager.action.DEVICE_IDLE";
90 
91     /* References to values tracked in WifiService */
92     final WifiStateMachine mWifiStateMachine;
93     final WifiSettingsStore mSettingsStore;
94     final LockList mLocks;
95 
96     /**
97      * Temporary for computing UIDS that are responsible for starting WIFI.
98      * Protected by mWifiStateTracker lock.
99      */
100     private final WorkSource mTmpWorkSource = new WorkSource();
101 
102     private long mReEnableDelayMillis;
103 
104     private static final int BASE = Protocol.BASE_WIFI_CONTROLLER;
105 
106     static final int CMD_EMERGENCY_MODE_CHANGED     = BASE + 1;
107     static final int CMD_SCREEN_ON                  = BASE + 2;
108     static final int CMD_SCREEN_OFF                 = BASE + 3;
109     static final int CMD_BATTERY_CHANGED            = BASE + 4;
110     static final int CMD_DEVICE_IDLE                = BASE + 5;
111     static final int CMD_LOCKS_CHANGED              = BASE + 6;
112     static final int CMD_SCAN_ALWAYS_MODE_CHANGED   = BASE + 7;
113     static final int CMD_WIFI_TOGGLED               = BASE + 8;
114     static final int CMD_AIRPLANE_TOGGLED           = BASE + 9;
115     static final int CMD_SET_AP                     = BASE + 10;
116     static final int CMD_DEFERRED_TOGGLE            = BASE + 11;
117     static final int CMD_USER_PRESENT               = BASE + 12;
118 
119     private DefaultState mDefaultState = new DefaultState();
120     private StaEnabledState mStaEnabledState = new StaEnabledState();
121     private ApStaDisabledState mApStaDisabledState = new ApStaDisabledState();
122     private StaDisabledWithScanState mStaDisabledWithScanState = new StaDisabledWithScanState();
123     private ApEnabledState mApEnabledState = new ApEnabledState();
124     private DeviceActiveState mDeviceActiveState = new DeviceActiveState();
125     private DeviceInactiveState mDeviceInactiveState = new DeviceInactiveState();
126     private ScanOnlyLockHeldState mScanOnlyLockHeldState = new ScanOnlyLockHeldState();
127     private FullLockHeldState mFullLockHeldState = new FullLockHeldState();
128     private FullHighPerfLockHeldState mFullHighPerfLockHeldState = new FullHighPerfLockHeldState();
129     private NoLockHeldState mNoLockHeldState = new NoLockHeldState();
130     private EcmState mEcmState = new EcmState();
131 
WifiController(Context context, WifiService service, Looper looper)132     WifiController(Context context, WifiService service, Looper looper) {
133         super(TAG, looper);
134         mContext = context;
135         mWifiStateMachine = service.mWifiStateMachine;
136         mSettingsStore = service.mSettingsStore;
137         mLocks = service.mLocks;
138 
139         mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
140         Intent idleIntent = new Intent(ACTION_DEVICE_IDLE, null);
141         mIdleIntent = PendingIntent.getBroadcast(mContext, IDLE_REQUEST, idleIntent, 0);
142 
143         addState(mDefaultState);
144             addState(mApStaDisabledState, mDefaultState);
145             addState(mStaEnabledState, mDefaultState);
146                 addState(mDeviceActiveState, mStaEnabledState);
147                 addState(mDeviceInactiveState, mStaEnabledState);
148                     addState(mScanOnlyLockHeldState, mDeviceInactiveState);
149                     addState(mFullLockHeldState, mDeviceInactiveState);
150                     addState(mFullHighPerfLockHeldState, mDeviceInactiveState);
151                     addState(mNoLockHeldState, mDeviceInactiveState);
152             addState(mStaDisabledWithScanState, mDefaultState);
153             addState(mApEnabledState, mDefaultState);
154             addState(mEcmState, mDefaultState);
155 
156         boolean isAirplaneModeOn = mSettingsStore.isAirplaneModeOn();
157         boolean isWifiEnabled = mSettingsStore.isWifiToggleEnabled();
158         boolean isScanningAlwaysAvailable = mSettingsStore.isScanAlwaysAvailable();
159 
160         log("isAirplaneModeOn = " + isAirplaneModeOn +
161                 ", isWifiEnabled = " + isWifiEnabled +
162                 ", isScanningAvailable = " + isScanningAlwaysAvailable);
163 
164         if (isWifiEnabled && isScanningAlwaysAvailable) {
165             setInitialState(mStaDisabledWithScanState);
166         } else {
167             setInitialState(mApStaDisabledState);
168         }
169 
170         setLogRecSize(100);
171         setLogOnlyTransitions(false);
172 
173         IntentFilter filter = new IntentFilter();
174         filter.addAction(ACTION_DEVICE_IDLE);
175         filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
176         mContext.registerReceiver(
177                 new BroadcastReceiver() {
178                     @Override
179                     public void onReceive(Context context, Intent intent) {
180                         String action = intent.getAction();
181                         if (action.equals(ACTION_DEVICE_IDLE)) {
182                             sendMessage(CMD_DEVICE_IDLE);
183                         } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
184                             mNetworkInfo = (NetworkInfo) intent.getParcelableExtra(
185                                     WifiManager.EXTRA_NETWORK_INFO);
186                         }
187                     }
188                 },
189                 new IntentFilter(filter));
190 
191         initializeAndRegisterForSettingsChange(looper);
192     }
193 
initializeAndRegisterForSettingsChange(Looper looper)194     private void initializeAndRegisterForSettingsChange(Looper looper) {
195         Handler handler = new Handler(looper);
196         readStayAwakeConditions();
197         registerForStayAwakeModeChange(handler);
198         readWifiIdleTime();
199         registerForWifiIdleTimeChange(handler);
200         readWifiSleepPolicy();
201         registerForWifiSleepPolicyChange(handler);
202         readWifiReEnableDelay();
203     }
204 
readStayAwakeConditions()205     private void readStayAwakeConditions() {
206         mStayAwakeConditions = Settings.Global.getInt(mContext.getContentResolver(),
207                 Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0);
208     }
209 
readWifiIdleTime()210     private void readWifiIdleTime() {
211         mIdleMillis = Settings.Global.getLong(mContext.getContentResolver(),
212                 Settings.Global.WIFI_IDLE_MS, DEFAULT_IDLE_MS);
213     }
214 
readWifiSleepPolicy()215     private void readWifiSleepPolicy() {
216         mSleepPolicy = Settings.Global.getInt(mContext.getContentResolver(),
217                 Settings.Global.WIFI_SLEEP_POLICY,
218                 Settings.Global.WIFI_SLEEP_POLICY_NEVER);
219     }
220 
readWifiReEnableDelay()221     private void readWifiReEnableDelay() {
222         mReEnableDelayMillis = Settings.Global.getLong(mContext.getContentResolver(),
223                 Settings.Global.WIFI_REENABLE_DELAY_MS, DEFAULT_REENABLE_DELAY_MS);
224     }
225 
226     /**
227      * Observes settings changes to scan always mode.
228      */
registerForStayAwakeModeChange(Handler handler)229     private void registerForStayAwakeModeChange(Handler handler) {
230         ContentObserver contentObserver = new ContentObserver(handler) {
231             @Override
232             public void onChange(boolean selfChange) {
233                 readStayAwakeConditions();
234             }
235         };
236 
237         mContext.getContentResolver().registerContentObserver(
238                 Settings.Global.getUriFor(Settings.Global.STAY_ON_WHILE_PLUGGED_IN),
239                 false, contentObserver);
240     }
241 
242     /**
243      * Observes settings changes to scan always mode.
244      */
registerForWifiIdleTimeChange(Handler handler)245     private void registerForWifiIdleTimeChange(Handler handler) {
246         ContentObserver contentObserver = new ContentObserver(handler) {
247             @Override
248             public void onChange(boolean selfChange) {
249                 readWifiIdleTime();
250             }
251         };
252 
253         mContext.getContentResolver().registerContentObserver(
254                 Settings.Global.getUriFor(Settings.Global.WIFI_IDLE_MS),
255                 false, contentObserver);
256     }
257 
258     /**
259      * Observes changes to wifi sleep policy
260      */
registerForWifiSleepPolicyChange(Handler handler)261     private void registerForWifiSleepPolicyChange(Handler handler) {
262         ContentObserver contentObserver = new ContentObserver(handler) {
263             @Override
264             public void onChange(boolean selfChange) {
265                 readWifiSleepPolicy();
266             }
267         };
268         mContext.getContentResolver().registerContentObserver(
269                 Settings.Global.getUriFor(Settings.Global.WIFI_SLEEP_POLICY),
270                 false, contentObserver);
271     }
272 
273     /**
274      * Determines whether the Wi-Fi chipset should stay awake or be put to
275      * sleep. Looks at the setting for the sleep policy and the current
276      * conditions.
277      *
278      * @see #shouldDeviceStayAwake(int)
279      */
shouldWifiStayAwake(int pluggedType)280     private boolean shouldWifiStayAwake(int pluggedType) {
281         if (mSleepPolicy == Settings.Global.WIFI_SLEEP_POLICY_NEVER) {
282             // Never sleep
283             return true;
284         } else if ((mSleepPolicy == Settings.Global.WIFI_SLEEP_POLICY_NEVER_WHILE_PLUGGED) &&
285                 (pluggedType != 0)) {
286             // Never sleep while plugged, and we're plugged
287             return true;
288         } else {
289             // Default
290             return shouldDeviceStayAwake(pluggedType);
291         }
292     }
293 
294     /**
295      * Determine whether the bit value corresponding to {@code pluggedType} is set in
296      * the bit string mStayAwakeConditions. This determines whether the device should
297      * stay awake based on the current plugged type.
298      *
299      * @param pluggedType the type of plug (USB, AC, or none) for which the check is
300      * being made
301      * @return {@code true} if {@code pluggedType} indicates that the device is
302      * supposed to stay awake, {@code false} otherwise.
303      */
shouldDeviceStayAwake(int pluggedType)304     private boolean shouldDeviceStayAwake(int pluggedType) {
305         return (mStayAwakeConditions & pluggedType) != 0;
306     }
307 
updateBatteryWorkSource()308     private void updateBatteryWorkSource() {
309         mTmpWorkSource.clear();
310         if (mDeviceIdle) {
311             mLocks.updateWorkSource(mTmpWorkSource);
312         }
313         mWifiStateMachine.updateBatteryWorkSource(mTmpWorkSource);
314     }
315 
316     class DefaultState extends State {
317         @Override
processMessage(Message msg)318         public boolean processMessage(Message msg) {
319             switch (msg.what) {
320                 case CMD_SCREEN_ON:
321                     mAlarmManager.cancel(mIdleIntent);
322                     mScreenOff = false;
323                     mDeviceIdle = false;
324                     updateBatteryWorkSource();
325                     break;
326                 case CMD_SCREEN_OFF:
327                     mScreenOff = true;
328                     /*
329                     * Set a timer to put Wi-Fi to sleep, but only if the screen is off
330                     * AND the "stay on while plugged in" setting doesn't match the
331                     * current power conditions (i.e, not plugged in, plugged in to USB,
332                     * or plugged in to AC).
333                     */
334                     if (!shouldWifiStayAwake(mPluggedType)) {
335                         //Delayed shutdown if wifi is connected
336                         if (mNetworkInfo.getDetailedState() ==
337                                 NetworkInfo.DetailedState.CONNECTED) {
338                             if (DBG) Slog.d(TAG, "set idle timer: " + mIdleMillis + " ms");
339                             mAlarmManager.set(AlarmManager.RTC_WAKEUP,
340                                     System.currentTimeMillis() + mIdleMillis, mIdleIntent);
341                         } else {
342                             sendMessage(CMD_DEVICE_IDLE);
343                         }
344                     }
345                     break;
346                 case CMD_DEVICE_IDLE:
347                     mDeviceIdle = true;
348                     updateBatteryWorkSource();
349                     break;
350                 case CMD_BATTERY_CHANGED:
351                     /*
352                     * Set a timer to put Wi-Fi to sleep, but only if the screen is off
353                     * AND we are transitioning from a state in which the device was supposed
354                     * to stay awake to a state in which it is not supposed to stay awake.
355                     * If "stay awake" state is not changing, we do nothing, to avoid resetting
356                     * the already-set timer.
357                     */
358                     int pluggedType = msg.arg1;
359                     if (DBG) Slog.d(TAG, "battery changed pluggedType: " + pluggedType);
360                     if (mScreenOff && shouldWifiStayAwake(mPluggedType) &&
361                             !shouldWifiStayAwake(pluggedType)) {
362                         long triggerTime = System.currentTimeMillis() + mIdleMillis;
363                         if (DBG) Slog.d(TAG, "set idle timer for " + mIdleMillis + "ms");
364                         mAlarmManager.set(AlarmManager.RTC_WAKEUP, triggerTime, mIdleIntent);
365                     }
366 
367                     mPluggedType = pluggedType;
368                     break;
369                 case CMD_SET_AP:
370                 case CMD_SCAN_ALWAYS_MODE_CHANGED:
371                 case CMD_LOCKS_CHANGED:
372                 case CMD_WIFI_TOGGLED:
373                 case CMD_AIRPLANE_TOGGLED:
374                 case CMD_EMERGENCY_MODE_CHANGED:
375                     break;
376                 case CMD_USER_PRESENT:
377                     mFirstUserSignOnSeen = true;
378                     break;
379                 case CMD_DEFERRED_TOGGLE:
380                     log("DEFERRED_TOGGLE ignored due to state change");
381                     break;
382                 default:
383                     throw new RuntimeException("WifiController.handleMessage " + msg.what);
384             }
385             return HANDLED;
386         }
387 
388     }
389 
390     class ApStaDisabledState extends State {
391         private int mDeferredEnableSerialNumber = 0;
392         private boolean mHaveDeferredEnable = false;
393         private long mDisabledTimestamp;
394 
395         @Override
enter()396         public void enter() {
397             mWifiStateMachine.setSupplicantRunning(false);
398             // Supplicant can't restart right away, so not the time we switched off
399             mDisabledTimestamp = SystemClock.elapsedRealtime();
400             mDeferredEnableSerialNumber++;
401             mHaveDeferredEnable = false;
402         }
403         @Override
processMessage(Message msg)404         public boolean processMessage(Message msg) {
405             switch (msg.what) {
406                 case CMD_WIFI_TOGGLED:
407                 case CMD_AIRPLANE_TOGGLED:
408                     if (mSettingsStore.isWifiToggleEnabled()) {
409                         if (doDeferEnable(msg)) {
410                             if (mHaveDeferredEnable) {
411                                 //  have 2 toggles now, inc serial number an ignore both
412                                 mDeferredEnableSerialNumber++;
413                             }
414                             mHaveDeferredEnable = !mHaveDeferredEnable;
415                             break;
416                         }
417                         if (mDeviceIdle == false) {
418                             transitionTo(mDeviceActiveState);
419                         } else {
420                             checkLocksAndTransitionWhenDeviceIdle();
421                         }
422                     }
423                     break;
424                 case CMD_SCAN_ALWAYS_MODE_CHANGED:
425                     if (mSettingsStore.isScanAlwaysAvailable()) {
426                         transitionTo(mStaDisabledWithScanState);
427                     }
428                     break;
429                 case CMD_SET_AP:
430                     if (msg.arg1 == 1) {
431                         mWifiStateMachine.setHostApRunning((WifiConfiguration) msg.obj,
432                                 true);
433                         transitionTo(mApEnabledState);
434                     }
435                     break;
436                 case CMD_DEFERRED_TOGGLE:
437                     if (msg.arg1 != mDeferredEnableSerialNumber) {
438                         log("DEFERRED_TOGGLE ignored due to serial mismatch");
439                         break;
440                     }
441                     log("DEFERRED_TOGGLE handled");
442                     sendMessage((Message)(msg.obj));
443                     break;
444                 default:
445                     return NOT_HANDLED;
446             }
447             return HANDLED;
448         }
449 
doDeferEnable(Message msg)450         private boolean doDeferEnable(Message msg) {
451             long delaySoFar = SystemClock.elapsedRealtime() - mDisabledTimestamp;
452             if (delaySoFar >= mReEnableDelayMillis) {
453                 return false;
454             }
455 
456             log("WifiController msg " + msg + " deferred for " +
457                     (mReEnableDelayMillis - delaySoFar) + "ms");
458 
459             // need to defer this action.
460             Message deferredMsg = obtainMessage(CMD_DEFERRED_TOGGLE);
461             deferredMsg.obj = Message.obtain(msg);
462             deferredMsg.arg1 = ++mDeferredEnableSerialNumber;
463             sendMessageDelayed(deferredMsg, mReEnableDelayMillis - delaySoFar + DEFER_MARGIN_MS);
464             return true;
465         }
466 
467     }
468 
469     class StaEnabledState extends State {
470         @Override
enter()471         public void enter() {
472             mWifiStateMachine.setSupplicantRunning(true);
473         }
474         @Override
processMessage(Message msg)475         public boolean processMessage(Message msg) {
476             switch (msg.what) {
477                 case CMD_WIFI_TOGGLED:
478                     if (! mSettingsStore.isWifiToggleEnabled()) {
479                         if (mSettingsStore.isScanAlwaysAvailable()) {
480                             transitionTo(mStaDisabledWithScanState);
481                         } else {
482                             transitionTo(mApStaDisabledState);
483                         }
484                     }
485                     break;
486                 case CMD_AIRPLANE_TOGGLED:
487                     /* When wi-fi is turned off due to airplane,
488                     * disable entirely (including scan)
489                     */
490                     if (! mSettingsStore.isWifiToggleEnabled()) {
491                         transitionTo(mApStaDisabledState);
492                     }
493                     break;
494                 case CMD_EMERGENCY_MODE_CHANGED:
495                     if (msg.arg1 == 1) {
496                         transitionTo(mEcmState);
497                         break;
498                     }
499                 default:
500                     return NOT_HANDLED;
501 
502             }
503             return HANDLED;
504         }
505     }
506 
507     class StaDisabledWithScanState extends State {
508         private int mDeferredEnableSerialNumber = 0;
509         private boolean mHaveDeferredEnable = false;
510         private long mDisabledTimestamp;
511 
512         @Override
enter()513         public void enter() {
514             mWifiStateMachine.setSupplicantRunning(true);
515             mWifiStateMachine.setOperationalMode(WifiStateMachine.SCAN_ONLY_WITH_WIFI_OFF_MODE);
516             mWifiStateMachine.setDriverStart(true);
517             // Supplicant can't restart right away, so not the time we switched off
518             mDisabledTimestamp = SystemClock.elapsedRealtime();
519             mDeferredEnableSerialNumber++;
520             mHaveDeferredEnable = false;
521         }
522 
523         @Override
processMessage(Message msg)524         public boolean processMessage(Message msg) {
525             switch (msg.what) {
526                 case CMD_WIFI_TOGGLED:
527                     if (mSettingsStore.isWifiToggleEnabled()) {
528                         if (doDeferEnable(msg)) {
529                             if (mHaveDeferredEnable) {
530                                 // have 2 toggles now, inc serial number and ignore both
531                                 mDeferredEnableSerialNumber++;
532                             }
533                             mHaveDeferredEnable = !mHaveDeferredEnable;
534                             break;
535                         }
536                         if (mDeviceIdle == false) {
537                             transitionTo(mDeviceActiveState);
538                         } else {
539                             checkLocksAndTransitionWhenDeviceIdle();
540                         }
541                     }
542                     break;
543                 case CMD_AIRPLANE_TOGGLED:
544                     if (mSettingsStore.isAirplaneModeOn() &&
545                             ! mSettingsStore.isWifiToggleEnabled()) {
546                         transitionTo(mApStaDisabledState);
547                     }
548                 case CMD_SCAN_ALWAYS_MODE_CHANGED:
549                     if (! mSettingsStore.isScanAlwaysAvailable()) {
550                         transitionTo(mApStaDisabledState);
551                     }
552                     break;
553                 case CMD_SET_AP:
554                     // Before starting tethering, turn off supplicant for scan mode
555                     if (msg.arg1 == 1) {
556                         deferMessage(msg);
557                         transitionTo(mApStaDisabledState);
558                     }
559                     break;
560                 case CMD_DEFERRED_TOGGLE:
561                     if (msg.arg1 != mDeferredEnableSerialNumber) {
562                         log("DEFERRED_TOGGLE ignored due to serial mismatch");
563                         break;
564                     }
565                     logd("DEFERRED_TOGGLE handled");
566                     sendMessage((Message)(msg.obj));
567                     break;
568                 default:
569                     return NOT_HANDLED;
570             }
571             return HANDLED;
572         }
573 
doDeferEnable(Message msg)574         private boolean doDeferEnable(Message msg) {
575             long delaySoFar = SystemClock.elapsedRealtime() - mDisabledTimestamp;
576             if (delaySoFar >= mReEnableDelayMillis) {
577                 return false;
578             }
579 
580             log("WifiController msg " + msg + " deferred for " +
581                     (mReEnableDelayMillis - delaySoFar) + "ms");
582 
583             // need to defer this action.
584             Message deferredMsg = obtainMessage(CMD_DEFERRED_TOGGLE);
585             deferredMsg.obj = Message.obtain(msg);
586             deferredMsg.arg1 = ++mDeferredEnableSerialNumber;
587             sendMessageDelayed(deferredMsg, mReEnableDelayMillis - delaySoFar + DEFER_MARGIN_MS);
588             return true;
589         }
590 
591     }
592 
593     class ApEnabledState extends State {
594         @Override
processMessage(Message msg)595         public boolean processMessage(Message msg) {
596             switch (msg.what) {
597                 case CMD_AIRPLANE_TOGGLED:
598                     if (mSettingsStore.isAirplaneModeOn()) {
599                         mWifiStateMachine.setHostApRunning(null, false);
600                         transitionTo(mApStaDisabledState);
601                     }
602                     break;
603                 case CMD_SET_AP:
604                     if (msg.arg1 == 0) {
605                         mWifiStateMachine.setHostApRunning(null, false);
606                         transitionTo(mApStaDisabledState);
607                     }
608                     break;
609                 default:
610                     return NOT_HANDLED;
611             }
612             return HANDLED;
613         }
614     }
615 
616     class EcmState extends State {
617         @Override
enter()618         public void enter() {
619             mWifiStateMachine.setSupplicantRunning(false);
620         }
621 
622         @Override
processMessage(Message msg)623         public boolean processMessage(Message msg) {
624             if (msg.what == CMD_EMERGENCY_MODE_CHANGED && msg.arg1 == 0) {
625                 if (mSettingsStore.isWifiToggleEnabled()) {
626                     if (mDeviceIdle == false) {
627                         transitionTo(mDeviceActiveState);
628                     } else {
629                         checkLocksAndTransitionWhenDeviceIdle();
630                     }
631                 } else if (mSettingsStore.isScanAlwaysAvailable()) {
632                     transitionTo(mStaDisabledWithScanState);
633                 } else {
634                     transitionTo(mApStaDisabledState);
635                 }
636                 return HANDLED;
637             } else {
638                 return NOT_HANDLED;
639             }
640         }
641     }
642 
643     /* Parent: StaEnabledState */
644     class DeviceActiveState extends State {
645         @Override
enter()646         public void enter() {
647             mWifiStateMachine.setOperationalMode(WifiStateMachine.CONNECT_MODE);
648             mWifiStateMachine.setDriverStart(true);
649             mWifiStateMachine.setHighPerfModeEnabled(false);
650         }
651 
652         @Override
processMessage(Message msg)653         public boolean processMessage(Message msg) {
654             if (msg.what == CMD_DEVICE_IDLE) {
655                 checkLocksAndTransitionWhenDeviceIdle();
656                 // We let default state handle the rest of work
657             } else if (msg.what == CMD_USER_PRESENT) {
658                 // TLS networks can't connect until user unlocks keystore. KeyStore
659                 // unlocks when the user punches PIN after the reboot. So use this
660                 // trigger to get those networks connected.
661                 if (mFirstUserSignOnSeen == false) {
662                     mWifiStateMachine.reloadTlsNetworksAndReconnect();
663                 }
664                 mFirstUserSignOnSeen = true;
665                 return HANDLED;
666             }
667             return NOT_HANDLED;
668         }
669     }
670 
671     /* Parent: StaEnabledState */
672     class DeviceInactiveState extends State {
673         @Override
processMessage(Message msg)674         public boolean processMessage(Message msg) {
675             switch (msg.what) {
676                 case CMD_LOCKS_CHANGED:
677                     checkLocksAndTransitionWhenDeviceIdle();
678                     updateBatteryWorkSource();
679                     return HANDLED;
680                 case CMD_SCREEN_ON:
681                     transitionTo(mDeviceActiveState);
682                     // More work in default state
683                     return NOT_HANDLED;
684                 default:
685                     return NOT_HANDLED;
686             }
687         }
688     }
689 
690     /* Parent: DeviceInactiveState. Device is inactive, but an app is holding a scan only lock. */
691     class ScanOnlyLockHeldState extends State {
692         @Override
enter()693         public void enter() {
694             mWifiStateMachine.setOperationalMode(WifiStateMachine.SCAN_ONLY_MODE);
695             mWifiStateMachine.setDriverStart(true);
696         }
697     }
698 
699     /* Parent: DeviceInactiveState. Device is inactive, but an app is holding a full lock. */
700     class FullLockHeldState extends State {
701         @Override
enter()702         public void enter() {
703             mWifiStateMachine.setOperationalMode(WifiStateMachine.CONNECT_MODE);
704             mWifiStateMachine.setDriverStart(true);
705             mWifiStateMachine.setHighPerfModeEnabled(false);
706         }
707     }
708 
709     /* Parent: DeviceInactiveState. Device is inactive, but an app is holding a high perf lock. */
710     class FullHighPerfLockHeldState extends State {
711         @Override
enter()712         public void enter() {
713             mWifiStateMachine.setOperationalMode(WifiStateMachine.CONNECT_MODE);
714             mWifiStateMachine.setDriverStart(true);
715             mWifiStateMachine.setHighPerfModeEnabled(true);
716         }
717     }
718 
719     /* Parent: DeviceInactiveState. Device is inactive and no app is holding a wifi lock. */
720     class NoLockHeldState extends State {
721         @Override
enter()722         public void enter() {
723             mWifiStateMachine.setDriverStart(false);
724         }
725     }
726 
checkLocksAndTransitionWhenDeviceIdle()727     private void checkLocksAndTransitionWhenDeviceIdle() {
728         if (mLocks.hasLocks()) {
729             switch (mLocks.getStrongestLockMode()) {
730                 case WIFI_MODE_FULL:
731                     transitionTo(mFullLockHeldState);
732                     break;
733                 case WIFI_MODE_FULL_HIGH_PERF:
734                     transitionTo(mFullHighPerfLockHeldState);
735                     break;
736                 case WIFI_MODE_SCAN_ONLY:
737                     transitionTo(mScanOnlyLockHeldState);
738                     break;
739                 default:
740                     loge("Illegal lock " + mLocks.getStrongestLockMode());
741             }
742         } else {
743             if (mSettingsStore.isScanAlwaysAvailable()) {
744                 transitionTo(mScanOnlyLockHeldState);
745             } else {
746                 transitionTo(mNoLockHeldState);
747             }
748         }
749     }
750 
751     @Override
dump(FileDescriptor fd, PrintWriter pw, String[] args)752     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
753         super.dump(fd, pw, args);
754 
755         pw.println("mScreenOff " + mScreenOff);
756         pw.println("mDeviceIdle " + mDeviceIdle);
757         pw.println("mPluggedType " + mPluggedType);
758         pw.println("mIdleMillis " + mIdleMillis);
759         pw.println("mSleepPolicy " + mSleepPolicy);
760     }
761 }
762