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
17package com.android.server.wifi;
18
19import android.app.AlarmManager;
20import android.app.PendingIntent;
21import android.content.BroadcastReceiver;
22import android.content.Context;
23import android.content.Intent;
24import android.content.IntentFilter;
25import android.database.ContentObserver;
26import android.net.ConnectivityManager;
27import android.net.NetworkInfo;
28import android.net.wifi.WifiConfiguration;
29import android.net.wifi.WifiManager;
30import static android.net.wifi.WifiManager.WIFI_MODE_FULL;
31import static android.net.wifi.WifiManager.WIFI_MODE_FULL_HIGH_PERF;
32import static android.net.wifi.WifiManager.WIFI_MODE_SCAN_ONLY;
33import android.net.wifi.WifiStateMachine;
34import android.os.Handler;
35import android.os.Looper;
36import android.os.Message;
37import android.os.SystemClock;
38import android.os.WorkSource;
39import android.provider.Settings;
40import android.util.Slog;
41
42import com.android.internal.util.Protocol;
43import com.android.internal.util.State;
44import com.android.internal.util.StateMachine;
45import com.android.server.wifi.WifiService.LockList;
46
47import java.io.FileDescriptor;
48import java.io.PrintWriter;
49
50class 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
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
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
205    private void readStayAwakeConditions() {
206        mStayAwakeConditions = Settings.Global.getInt(mContext.getContentResolver(),
207                Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0);
208    }
209
210    private void readWifiIdleTime() {
211        mIdleMillis = Settings.Global.getLong(mContext.getContentResolver(),
212                Settings.Global.WIFI_IDLE_MS, DEFAULT_IDLE_MS);
213    }
214
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
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     */
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     */
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     */
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     */
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     */
304    private boolean shouldDeviceStayAwake(int pluggedType) {
305        return (mStayAwakeConditions & pluggedType) != 0;
306    }
307
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
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
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
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
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
471        public void enter() {
472            mWifiStateMachine.setSupplicantRunning(true);
473        }
474        @Override
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
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
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
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
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
618        public void enter() {
619            mWifiStateMachine.setSupplicantRunning(false);
620        }
621
622        @Override
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
646        public void enter() {
647            mWifiStateMachine.setOperationalMode(WifiStateMachine.CONNECT_MODE);
648            mWifiStateMachine.setDriverStart(true);
649            mWifiStateMachine.setHighPerfModeEnabled(false);
650        }
651
652        @Override
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
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
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
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
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
722        public void enter() {
723            mWifiStateMachine.setDriverStart(false);
724        }
725    }
726
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
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