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