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