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