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