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