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