WifiController.java revision 392506310ec5bf00d9908bebef4ed0a90c0150e5
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            // need to set the mode before starting supplicant because WSM will assume we are going
585            // in to client mode
586            mWifiStateMachine.setOperationalMode(WifiStateMachine.SCAN_ONLY_WITH_WIFI_OFF_MODE);
587            mWifiStateMachine.setSupplicantRunning(true);
588            mWifiStateMachine.setDriverStart(true);
589            // Supplicant can't restart right away, so not the time we switched off
590            mDisabledTimestamp = SystemClock.elapsedRealtime();
591            mDeferredEnableSerialNumber++;
592            mHaveDeferredEnable = false;
593            mWifiStateMachine.clearANQPCache();
594        }
595
596        @Override
597        public boolean processMessage(Message msg) {
598            switch (msg.what) {
599                case CMD_WIFI_TOGGLED:
600                    if (mSettingsStore.isWifiToggleEnabled()) {
601                        if (doDeferEnable(msg)) {
602                            if (mHaveDeferredEnable) {
603                                // have 2 toggles now, inc serial number and ignore both
604                                mDeferredEnableSerialNumber++;
605                            }
606                            mHaveDeferredEnable = !mHaveDeferredEnable;
607                            break;
608                        }
609                        if (mDeviceIdle == false) {
610                            transitionTo(mDeviceActiveState);
611                        } else {
612                            checkLocksAndTransitionWhenDeviceIdle();
613                        }
614                    }
615                    break;
616                case CMD_AIRPLANE_TOGGLED:
617                    if (mSettingsStore.isAirplaneModeOn() &&
618                            ! mSettingsStore.isWifiToggleEnabled()) {
619                        transitionTo(mApStaDisabledState);
620                    }
621                    break;
622                case CMD_SCAN_ALWAYS_MODE_CHANGED:
623                    if (! mSettingsStore.isScanAlwaysAvailable()) {
624                        transitionTo(mApStaDisabledState);
625                    }
626                    break;
627                case CMD_SET_AP:
628                    // Before starting tethering, turn off supplicant for scan mode
629                    if (msg.arg1 == 1) {
630                        mSettingsStore.setWifiSavedState(WifiSettingsStore.WIFI_DISABLED);
631                        deferMessage(obtainMessage(msg.what, msg.arg1, 1, msg.obj));
632                        transitionTo(mApStaDisabledState);
633                    }
634                    break;
635                case CMD_DEFERRED_TOGGLE:
636                    if (msg.arg1 != mDeferredEnableSerialNumber) {
637                        log("DEFERRED_TOGGLE ignored due to serial mismatch");
638                        break;
639                    }
640                    logd("DEFERRED_TOGGLE handled");
641                    sendMessage((Message)(msg.obj));
642                    break;
643                default:
644                    return NOT_HANDLED;
645            }
646            return HANDLED;
647        }
648
649        private boolean doDeferEnable(Message msg) {
650            long delaySoFar = SystemClock.elapsedRealtime() - mDisabledTimestamp;
651            if (delaySoFar >= mReEnableDelayMillis) {
652                return false;
653            }
654
655            log("WifiController msg " + msg + " deferred for " +
656                    (mReEnableDelayMillis - delaySoFar) + "ms");
657
658            // need to defer this action.
659            Message deferredMsg = obtainMessage(CMD_DEFERRED_TOGGLE);
660            deferredMsg.obj = Message.obtain(msg);
661            deferredMsg.arg1 = ++mDeferredEnableSerialNumber;
662            sendMessageDelayed(deferredMsg, mReEnableDelayMillis - delaySoFar + DEFER_MARGIN_MS);
663            return true;
664        }
665
666    }
667
668    /**
669     * Only transition out of this state when AP failed to start or AP is stopped.
670     */
671    class ApEnabledState extends State {
672        /**
673         * Save the pending state when stopping the AP, so that it will transition
674         * to the correct state when AP is stopped.  This is to avoid a possible
675         * race condition where the new state might try to update the driver/interface
676         * state before AP is completely torn down.
677         */
678        private State mPendingState = null;
679
680        /**
681         * Determine the next state based on the current settings (e.g. saved
682         * wifi state).
683         */
684        private State getNextWifiState() {
685            if (mSettingsStore.getWifiSavedState() == WifiSettingsStore.WIFI_ENABLED) {
686                return mDeviceActiveState;
687            }
688
689            if (mSettingsStore.isScanAlwaysAvailable()) {
690                return mStaDisabledWithScanState;
691            }
692
693            return mApStaDisabledState;
694        }
695
696        @Override
697        public boolean processMessage(Message msg) {
698            switch (msg.what) {
699                case CMD_AIRPLANE_TOGGLED:
700                    if (mSettingsStore.isAirplaneModeOn()) {
701                        mWifiStateMachine.setHostApRunning(null, false);
702                        mPendingState = mApStaDisabledState;
703                    }
704                    break;
705                case CMD_WIFI_TOGGLED:
706                    if (mSettingsStore.isWifiToggleEnabled()) {
707                        mWifiStateMachine.setHostApRunning(null, false);
708                        mPendingState = mDeviceActiveState;
709                    }
710                    break;
711                case CMD_SET_AP:
712                    if (msg.arg1 == 0) {
713                        mWifiStateMachine.setHostApRunning(null, false);
714                        mPendingState = getNextWifiState();
715                    }
716                    break;
717                case CMD_AP_STOPPED:
718                    if (mPendingState == null) {
719                        /**
720                         * Stop triggered internally, either tether notification
721                         * timed out or wifi is untethered for some reason.
722                         */
723                        mPendingState = getNextWifiState();
724                    }
725                    if (mPendingState == mDeviceActiveState && mDeviceIdle) {
726                        checkLocksAndTransitionWhenDeviceIdle();
727                    } else {
728                        // go ahead and transition because we are not idle or we are not going
729                        // to the active state.
730                        transitionTo(mPendingState);
731                    }
732                    break;
733                case CMD_EMERGENCY_CALL_STATE_CHANGED:
734                case CMD_EMERGENCY_MODE_CHANGED:
735                    if (msg.arg1 == 1) {
736                        mWifiStateMachine.setHostApRunning(null, false);
737                        mPendingState = mEcmState;
738                    }
739                    break;
740                case CMD_AP_START_FAILURE:
741                    transitionTo(getNextWifiState());
742                    break;
743                default:
744                    return NOT_HANDLED;
745            }
746            return HANDLED;
747        }
748    }
749
750    class EcmState extends State {
751        // we can enter EcmState either because an emergency call started or because
752        // emergency callback mode started. This count keeps track of how many such
753        // events happened; so we can exit after all are undone
754
755        private int mEcmEntryCount;
756        @Override
757        public void enter() {
758            mWifiStateMachine.setSupplicantRunning(false);
759            mWifiStateMachine.clearANQPCache();
760            mEcmEntryCount = 1;
761        }
762
763        @Override
764        public boolean processMessage(Message msg) {
765            if (msg.what == CMD_EMERGENCY_CALL_STATE_CHANGED) {
766                if (msg.arg1 == 1) {
767                    // nothing to do - just says emergency call started
768                    mEcmEntryCount++;
769                } else if (msg.arg1 == 0) {
770                    // emergency call ended
771                    decrementCountAndReturnToAppropriateState();
772                }
773                return HANDLED;
774            } else if (msg.what == CMD_EMERGENCY_MODE_CHANGED) {
775
776                if (msg.arg1 == 1) {
777                    // Transitioned into emergency callback mode
778                    mEcmEntryCount++;
779                } else if (msg.arg1 == 0) {
780                    // out of emergency callback mode
781                    decrementCountAndReturnToAppropriateState();
782                }
783                return HANDLED;
784            } else {
785                return NOT_HANDLED;
786            }
787        }
788
789        private void decrementCountAndReturnToAppropriateState() {
790            boolean exitEcm = false;
791
792            if (mEcmEntryCount == 0) {
793                loge("mEcmEntryCount is 0; exiting Ecm");
794                exitEcm = true;
795            } else if (--mEcmEntryCount == 0) {
796                exitEcm = true;
797            }
798
799            if (exitEcm) {
800                if (mSettingsStore.isWifiToggleEnabled()) {
801                    if (mDeviceIdle == false) {
802                        transitionTo(mDeviceActiveState);
803                    } else {
804                        checkLocksAndTransitionWhenDeviceIdle();
805                    }
806                } else if (mSettingsStore.isScanAlwaysAvailable()) {
807                    transitionTo(mStaDisabledWithScanState);
808                } else {
809                    transitionTo(mApStaDisabledState);
810                }
811            }
812        }
813    }
814
815    /* Parent: StaEnabledState */
816    class DeviceActiveState extends State {
817        @Override
818        public void enter() {
819            mWifiStateMachine.setOperationalMode(WifiStateMachine.CONNECT_MODE);
820            mWifiStateMachine.setDriverStart(true);
821            mWifiStateMachine.setHighPerfModeEnabled(false);
822        }
823
824        @Override
825        public boolean processMessage(Message msg) {
826            if (msg.what == CMD_DEVICE_IDLE) {
827                checkLocksAndTransitionWhenDeviceIdle();
828                // We let default state handle the rest of work
829            } else if (msg.what == CMD_USER_PRESENT) {
830                // TLS networks can't connect until user unlocks keystore. KeyStore
831                // unlocks when the user punches PIN after the reboot. So use this
832                // trigger to get those networks connected.
833                if (mFirstUserSignOnSeen == false) {
834                    mWifiStateMachine.reloadTlsNetworksAndReconnect();
835                }
836                mFirstUserSignOnSeen = true;
837                return HANDLED;
838            } else if (msg.what == CMD_RESTART_WIFI) {
839                deferMessage(obtainMessage(CMD_RESTART_WIFI_CONTINUE));
840                transitionTo(mApStaDisabledState);
841                return HANDLED;
842            }
843            return NOT_HANDLED;
844        }
845    }
846
847    /* Parent: StaEnabledState */
848    class DeviceInactiveState extends State {
849        @Override
850        public boolean processMessage(Message msg) {
851            switch (msg.what) {
852                case CMD_LOCKS_CHANGED:
853                    checkLocksAndTransitionWhenDeviceIdle();
854                    updateBatteryWorkSource();
855                    return HANDLED;
856                case CMD_SCREEN_ON:
857                    transitionTo(mDeviceActiveState);
858                    // More work in default state
859                    return NOT_HANDLED;
860                default:
861                    return NOT_HANDLED;
862            }
863        }
864    }
865
866    /* Parent: DeviceInactiveState. Device is inactive, but an app is holding a scan only lock. */
867    class ScanOnlyLockHeldState extends State {
868        @Override
869        public void enter() {
870            mWifiStateMachine.setOperationalMode(WifiStateMachine.SCAN_ONLY_MODE);
871            mWifiStateMachine.setDriverStart(true);
872        }
873    }
874
875    /* Parent: DeviceInactiveState. Device is inactive, but an app is holding a full lock. */
876    class FullLockHeldState extends State {
877        @Override
878        public void enter() {
879            mWifiStateMachine.setOperationalMode(WifiStateMachine.CONNECT_MODE);
880            mWifiStateMachine.setDriverStart(true);
881            mWifiStateMachine.setHighPerfModeEnabled(false);
882        }
883    }
884
885    /* Parent: DeviceInactiveState. Device is inactive, but an app is holding a high perf lock. */
886    class FullHighPerfLockHeldState extends State {
887        @Override
888        public void enter() {
889            mWifiStateMachine.setOperationalMode(WifiStateMachine.CONNECT_MODE);
890            mWifiStateMachine.setDriverStart(true);
891            mWifiStateMachine.setHighPerfModeEnabled(true);
892        }
893    }
894
895    /* Parent: DeviceInactiveState. Device is inactive and no app is holding a wifi lock. */
896    class NoLockHeldState extends State {
897        @Override
898        public void enter() {
899            mWifiStateMachine.setDriverStart(false);
900        }
901    }
902
903    private void checkLocksAndTransitionWhenDeviceIdle() {
904        switch (mWifiLockManager.getStrongestLockMode()) {
905            case WIFI_MODE_NO_LOCKS_HELD:
906                if (mSettingsStore.isScanAlwaysAvailable()) {
907                    transitionTo(mScanOnlyLockHeldState);
908                } else {
909                    transitionTo(mNoLockHeldState);
910                }
911                break;
912            case WIFI_MODE_FULL:
913                transitionTo(mFullLockHeldState);
914                break;
915            case WIFI_MODE_FULL_HIGH_PERF:
916                transitionTo(mFullHighPerfLockHeldState);
917                break;
918            case WIFI_MODE_SCAN_ONLY:
919                transitionTo(mScanOnlyLockHeldState);
920                break;
921        }
922    }
923
924    @Override
925    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
926        super.dump(fd, pw, args);
927
928        pw.println("mScreenOff " + mScreenOff);
929        pw.println("mDeviceIdle " + mDeviceIdle);
930        pw.println("mPluggedType " + mPluggedType);
931        pw.println("mIdleMillis " + mIdleMillis);
932        pw.println("mSleepPolicy " + mSleepPolicy);
933    }
934}
935