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