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 android.app.AlarmManager;
     20 import android.app.PendingIntent;
     21 import android.content.BroadcastReceiver;
     22 import android.content.Context;
     23 import android.content.Intent;
     24 import android.content.IntentFilter;
     25 import android.database.ContentObserver;
     26 import android.net.ConnectivityManager;
     27 import android.net.NetworkInfo;
     28 import android.net.wifi.WifiConfiguration;
     29 import android.net.wifi.WifiManager;
     30 import static android.net.wifi.WifiManager.WIFI_MODE_FULL;
     31 import static android.net.wifi.WifiManager.WIFI_MODE_FULL_HIGH_PERF;
     32 import static android.net.wifi.WifiManager.WIFI_MODE_SCAN_ONLY;
     33 import android.net.wifi.WifiStateMachine;
     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 import com.android.server.wifi.WifiService.LockList;
     46 
     47 import java.io.FileDescriptor;
     48 import java.io.PrintWriter;
     49 
     50 class WifiController extends StateMachine {
     51     private static final String TAG = "WifiController";
     52     private static final boolean DBG = false;
     53     private Context mContext;
     54     private boolean mScreenOff;
     55     private boolean mDeviceIdle;
     56     private int mPluggedType;
     57     private int mStayAwakeConditions;
     58     private long mIdleMillis;
     59     private int mSleepPolicy;
     60     private boolean mFirstUserSignOnSeen = false;
     61 
     62     private AlarmManager mAlarmManager;
     63     private PendingIntent mIdleIntent;
     64     private static final int IDLE_REQUEST = 0;
     65 
     66     /**
     67      * See {@link Settings.Global#WIFI_IDLE_MS}. This is the default value if a
     68      * Settings.Global value is not present. This timeout value is chosen as
     69      * the approximate point at which the battery drain caused by Wi-Fi
     70      * being enabled but not active exceeds the battery drain caused by
     71      * re-establishing a connection to the mobile data network.
     72      */
     73     private static final long DEFAULT_IDLE_MS = 15 * 60 * 1000; /* 15 minutes */
     74 
     75     /**
     76      * See {@link Settings.Global#WIFI_REENABLE_DELAY_MS}.  This is the default value if a
     77      * Settings.Global value is not present.  This is the minimum time after wifi is disabled
     78      * we'll act on an enable.  Enable requests received before this delay will be deferred.
     79      */
     80     private static final long DEFAULT_REENABLE_DELAY_MS = 500;
     81 
     82     // finding that delayed messages can sometimes be delivered earlier than expected
     83     // probably rounding errors..  add a margin to prevent problems
     84     private static final long DEFER_MARGIN_MS = 5;
     85 
     86     NetworkInfo mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0, "WIFI", "");
     87 
     88     private static final String ACTION_DEVICE_IDLE =
     89             "com.android.server.WifiManager.action.DEVICE_IDLE";
     90 
     91     /* References to values tracked in WifiService */
     92     final WifiStateMachine mWifiStateMachine;
     93     final WifiSettingsStore mSettingsStore;
     94     final LockList mLocks;
     95 
     96     /**
     97      * Temporary for computing UIDS that are responsible for starting WIFI.
     98      * Protected by mWifiStateTracker lock.
     99      */
    100     private final WorkSource mTmpWorkSource = new WorkSource();
    101 
    102     private long mReEnableDelayMillis;
    103 
    104     private static final int BASE = Protocol.BASE_WIFI_CONTROLLER;
    105 
    106     static final int CMD_EMERGENCY_MODE_CHANGED     = BASE + 1;
    107     static final int CMD_SCREEN_ON                  = BASE + 2;
    108     static final int CMD_SCREEN_OFF                 = BASE + 3;
    109     static final int CMD_BATTERY_CHANGED            = BASE + 4;
    110     static final int CMD_DEVICE_IDLE                = BASE + 5;
    111     static final int CMD_LOCKS_CHANGED              = BASE + 6;
    112     static final int CMD_SCAN_ALWAYS_MODE_CHANGED   = BASE + 7;
    113     static final int CMD_WIFI_TOGGLED               = BASE + 8;
    114     static final int CMD_AIRPLANE_TOGGLED           = BASE + 9;
    115     static final int CMD_SET_AP                     = BASE + 10;
    116     static final int CMD_DEFERRED_TOGGLE            = BASE + 11;
    117     static final int CMD_USER_PRESENT               = BASE + 12;
    118 
    119     private DefaultState mDefaultState = new DefaultState();
    120     private StaEnabledState mStaEnabledState = new StaEnabledState();
    121     private ApStaDisabledState mApStaDisabledState = new ApStaDisabledState();
    122     private StaDisabledWithScanState mStaDisabledWithScanState = new StaDisabledWithScanState();
    123     private ApEnabledState mApEnabledState = new ApEnabledState();
    124     private DeviceActiveState mDeviceActiveState = new DeviceActiveState();
    125     private DeviceInactiveState mDeviceInactiveState = new DeviceInactiveState();
    126     private ScanOnlyLockHeldState mScanOnlyLockHeldState = new ScanOnlyLockHeldState();
    127     private FullLockHeldState mFullLockHeldState = new FullLockHeldState();
    128     private FullHighPerfLockHeldState mFullHighPerfLockHeldState = new FullHighPerfLockHeldState();
    129     private NoLockHeldState mNoLockHeldState = new NoLockHeldState();
    130     private EcmState mEcmState = new EcmState();
    131 
    132     WifiController(Context context, WifiService service, Looper looper) {
    133         super(TAG, looper);
    134         mContext = context;
    135         mWifiStateMachine = service.mWifiStateMachine;
    136         mSettingsStore = service.mSettingsStore;
    137         mLocks = service.mLocks;
    138 
    139         mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
    140         Intent idleIntent = new Intent(ACTION_DEVICE_IDLE, null);
    141         mIdleIntent = PendingIntent.getBroadcast(mContext, IDLE_REQUEST, idleIntent, 0);
    142 
    143         addState(mDefaultState);
    144             addState(mApStaDisabledState, mDefaultState);
    145             addState(mStaEnabledState, mDefaultState);
    146                 addState(mDeviceActiveState, mStaEnabledState);
    147                 addState(mDeviceInactiveState, mStaEnabledState);
    148                     addState(mScanOnlyLockHeldState, mDeviceInactiveState);
    149                     addState(mFullLockHeldState, mDeviceInactiveState);
    150                     addState(mFullHighPerfLockHeldState, mDeviceInactiveState);
    151                     addState(mNoLockHeldState, mDeviceInactiveState);
    152             addState(mStaDisabledWithScanState, mDefaultState);
    153             addState(mApEnabledState, mDefaultState);
    154             addState(mEcmState, mDefaultState);
    155         if (mSettingsStore.isScanAlwaysAvailable()) {
    156             setInitialState(mStaDisabledWithScanState);
    157         } else {
    158             setInitialState(mApStaDisabledState);
    159         }
    160         setLogRecSize(100);
    161         setLogOnlyTransitions(false);
    162 
    163         IntentFilter filter = new IntentFilter();
    164         filter.addAction(ACTION_DEVICE_IDLE);
    165         filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
    166         mContext.registerReceiver(
    167                 new BroadcastReceiver() {
    168                     @Override
    169                     public void onReceive(Context context, Intent intent) {
    170                         String action = intent.getAction();
    171                         if (action.equals(ACTION_DEVICE_IDLE)) {
    172                             sendMessage(CMD_DEVICE_IDLE);
    173                         } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
    174                             mNetworkInfo = (NetworkInfo) intent.getParcelableExtra(
    175                                     WifiManager.EXTRA_NETWORK_INFO);
    176                         }
    177                     }
    178                 },
    179                 new IntentFilter(filter));
    180 
    181         initializeAndRegisterForSettingsChange(looper);
    182     }
    183 
    184     private void initializeAndRegisterForSettingsChange(Looper looper) {
    185         Handler handler = new Handler(looper);
    186         readStayAwakeConditions();
    187         registerForStayAwakeModeChange(handler);
    188         readWifiIdleTime();
    189         registerForWifiIdleTimeChange(handler);
    190         readWifiSleepPolicy();
    191         registerForWifiSleepPolicyChange(handler);
    192         readWifiReEnableDelay();
    193     }
    194 
    195     private void readStayAwakeConditions() {
    196         mStayAwakeConditions = Settings.Global.getInt(mContext.getContentResolver(),
    197                 Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0);
    198     }
    199 
    200     private void readWifiIdleTime() {
    201         mIdleMillis = Settings.Global.getLong(mContext.getContentResolver(),
    202                 Settings.Global.WIFI_IDLE_MS, DEFAULT_IDLE_MS);
    203     }
    204 
    205     private void readWifiSleepPolicy() {
    206         mSleepPolicy = Settings.Global.getInt(mContext.getContentResolver(),
    207                 Settings.Global.WIFI_SLEEP_POLICY,
    208                 Settings.Global.WIFI_SLEEP_POLICY_NEVER);
    209     }
    210 
    211     private void readWifiReEnableDelay() {
    212         mReEnableDelayMillis = Settings.Global.getLong(mContext.getContentResolver(),
    213                 Settings.Global.WIFI_REENABLE_DELAY_MS, DEFAULT_REENABLE_DELAY_MS);
    214     }
    215 
    216     /**
    217      * Observes settings changes to scan always mode.
    218      */
    219     private void registerForStayAwakeModeChange(Handler handler) {
    220         ContentObserver contentObserver = new ContentObserver(handler) {
    221             @Override
    222             public void onChange(boolean selfChange) {
    223                 readStayAwakeConditions();
    224             }
    225         };
    226 
    227         mContext.getContentResolver().registerContentObserver(
    228                 Settings.Global.getUriFor(Settings.Global.STAY_ON_WHILE_PLUGGED_IN),
    229                 false, contentObserver);
    230     }
    231 
    232     /**
    233      * Observes settings changes to scan always mode.
    234      */
    235     private void registerForWifiIdleTimeChange(Handler handler) {
    236         ContentObserver contentObserver = new ContentObserver(handler) {
    237             @Override
    238             public void onChange(boolean selfChange) {
    239                 readWifiIdleTime();
    240             }
    241         };
    242 
    243         mContext.getContentResolver().registerContentObserver(
    244                 Settings.Global.getUriFor(Settings.Global.WIFI_IDLE_MS),
    245                 false, contentObserver);
    246     }
    247 
    248     /**
    249      * Observes changes to wifi sleep policy
    250      */
    251     private void registerForWifiSleepPolicyChange(Handler handler) {
    252         ContentObserver contentObserver = new ContentObserver(handler) {
    253             @Override
    254             public void onChange(boolean selfChange) {
    255                 readWifiSleepPolicy();
    256             }
    257         };
    258         mContext.getContentResolver().registerContentObserver(
    259                 Settings.Global.getUriFor(Settings.Global.WIFI_SLEEP_POLICY),
    260                 false, contentObserver);
    261     }
    262 
    263     /**
    264      * Determines whether the Wi-Fi chipset should stay awake or be put to
    265      * sleep. Looks at the setting for the sleep policy and the current
    266      * conditions.
    267      *
    268      * @see #shouldDeviceStayAwake(int)
    269      */
    270     private boolean shouldWifiStayAwake(int pluggedType) {
    271         if (mSleepPolicy == Settings.Global.WIFI_SLEEP_POLICY_NEVER) {
    272             // Never sleep
    273             return true;
    274         } else if ((mSleepPolicy == Settings.Global.WIFI_SLEEP_POLICY_NEVER_WHILE_PLUGGED) &&
    275                 (pluggedType != 0)) {
    276             // Never sleep while plugged, and we're plugged
    277             return true;
    278         } else {
    279             // Default
    280             return shouldDeviceStayAwake(pluggedType);
    281         }
    282     }
    283 
    284     /**
    285      * Determine whether the bit value corresponding to {@code pluggedType} is set in
    286      * the bit string mStayAwakeConditions. This determines whether the device should
    287      * stay awake based on the current plugged type.
    288      *
    289      * @param pluggedType the type of plug (USB, AC, or none) for which the check is
    290      * being made
    291      * @return {@code true} if {@code pluggedType} indicates that the device is
    292      * supposed to stay awake, {@code false} otherwise.
    293      */
    294     private boolean shouldDeviceStayAwake(int pluggedType) {
    295         return (mStayAwakeConditions & pluggedType) != 0;
    296     }
    297 
    298     private void updateBatteryWorkSource() {
    299         mTmpWorkSource.clear();
    300         if (mDeviceIdle) {
    301             mLocks.updateWorkSource(mTmpWorkSource);
    302         }
    303         mWifiStateMachine.updateBatteryWorkSource(mTmpWorkSource);
    304     }
    305 
    306     class DefaultState extends State {
    307         @Override
    308         public boolean processMessage(Message msg) {
    309             switch (msg.what) {
    310                 case CMD_SCREEN_ON:
    311                     mAlarmManager.cancel(mIdleIntent);
    312                     mScreenOff = false;
    313                     mDeviceIdle = false;
    314                     updateBatteryWorkSource();
    315                     break;
    316                 case CMD_SCREEN_OFF:
    317                     mScreenOff = true;
    318                     /*
    319                     * Set a timer to put Wi-Fi to sleep, but only if the screen is off
    320                     * AND the "stay on while plugged in" setting doesn't match the
    321                     * current power conditions (i.e, not plugged in, plugged in to USB,
    322                     * or plugged in to AC).
    323                     */
    324                     if (!shouldWifiStayAwake(mPluggedType)) {
    325                         //Delayed shutdown if wifi is connected
    326                         if (mNetworkInfo.getDetailedState() ==
    327                                 NetworkInfo.DetailedState.CONNECTED) {
    328                             if (DBG) Slog.d(TAG, "set idle timer: " + mIdleMillis + " ms");
    329                             mAlarmManager.set(AlarmManager.RTC_WAKEUP,
    330                                     System.currentTimeMillis() + mIdleMillis, mIdleIntent);
    331                         } else {
    332                             sendMessage(CMD_DEVICE_IDLE);
    333                         }
    334                     }
    335                     break;
    336                 case CMD_DEVICE_IDLE:
    337                     mDeviceIdle = true;
    338                     updateBatteryWorkSource();
    339                     break;
    340                 case CMD_BATTERY_CHANGED:
    341                     /*
    342                     * Set a timer to put Wi-Fi to sleep, but only if the screen is off
    343                     * AND we are transitioning from a state in which the device was supposed
    344                     * to stay awake to a state in which it is not supposed to stay awake.
    345                     * If "stay awake" state is not changing, we do nothing, to avoid resetting
    346                     * the already-set timer.
    347                     */
    348                     int pluggedType = msg.arg1;
    349                     if (DBG) Slog.d(TAG, "battery changed pluggedType: " + pluggedType);
    350                     if (mScreenOff && shouldWifiStayAwake(mPluggedType) &&
    351                             !shouldWifiStayAwake(pluggedType)) {
    352                         long triggerTime = System.currentTimeMillis() + mIdleMillis;
    353                         if (DBG) Slog.d(TAG, "set idle timer for " + mIdleMillis + "ms");
    354                         mAlarmManager.set(AlarmManager.RTC_WAKEUP, triggerTime, mIdleIntent);
    355                     }
    356 
    357                     mPluggedType = pluggedType;
    358                     break;
    359                 case CMD_SET_AP:
    360                 case CMD_SCAN_ALWAYS_MODE_CHANGED:
    361                 case CMD_LOCKS_CHANGED:
    362                 case CMD_WIFI_TOGGLED:
    363                 case CMD_AIRPLANE_TOGGLED:
    364                 case CMD_EMERGENCY_MODE_CHANGED:
    365                     break;
    366                 case CMD_USER_PRESENT:
    367                     mFirstUserSignOnSeen = true;
    368                     break;
    369                 case CMD_DEFERRED_TOGGLE:
    370                     log("DEFERRED_TOGGLE ignored due to state change");
    371                     break;
    372                 default:
    373                     throw new RuntimeException("WifiController.handleMessage " + msg.what);
    374             }
    375             return HANDLED;
    376         }
    377 
    378     }
    379 
    380     class ApStaDisabledState extends State {
    381         private int mDeferredEnableSerialNumber = 0;
    382         private boolean mHaveDeferredEnable = false;
    383         private long mDisabledTimestamp;
    384 
    385         @Override
    386         public void enter() {
    387             mWifiStateMachine.setSupplicantRunning(false);
    388             // Supplicant can't restart right away, so not the time we switched off
    389             mDisabledTimestamp = SystemClock.elapsedRealtime();
    390             mDeferredEnableSerialNumber++;
    391             mHaveDeferredEnable = false;
    392         }
    393         @Override
    394         public boolean processMessage(Message msg) {
    395             switch (msg.what) {
    396                 case CMD_WIFI_TOGGLED:
    397                 case CMD_AIRPLANE_TOGGLED:
    398                     if (mSettingsStore.isWifiToggleEnabled()) {
    399                         if (doDeferEnable(msg)) {
    400                             if (mHaveDeferredEnable) {
    401                                 //  have 2 toggles now, inc serial number an ignore both
    402                                 mDeferredEnableSerialNumber++;
    403                             }
    404                             mHaveDeferredEnable = !mHaveDeferredEnable;
    405                             break;
    406                         }
    407                         if (mDeviceIdle == false) {
    408                             transitionTo(mDeviceActiveState);
    409                         } else {
    410                             checkLocksAndTransitionWhenDeviceIdle();
    411                         }
    412                     }
    413                     break;
    414                 case CMD_SCAN_ALWAYS_MODE_CHANGED:
    415                     if (mSettingsStore.isScanAlwaysAvailable()) {
    416                         transitionTo(mStaDisabledWithScanState);
    417                     }
    418                     break;
    419                 case CMD_SET_AP:
    420                     if (msg.arg1 == 1) {
    421                         mWifiStateMachine.setHostApRunning((WifiConfiguration) msg.obj,
    422                                 true);
    423                         transitionTo(mApEnabledState);
    424                     }
    425                     break;
    426                 case CMD_DEFERRED_TOGGLE:
    427                     if (msg.arg1 != mDeferredEnableSerialNumber) {
    428                         log("DEFERRED_TOGGLE ignored due to serial mismatch");
    429                         break;
    430                     }
    431                     log("DEFERRED_TOGGLE handled");
    432                     sendMessage((Message)(msg.obj));
    433                     break;
    434                 default:
    435                     return NOT_HANDLED;
    436             }
    437             return HANDLED;
    438         }
    439 
    440         private boolean doDeferEnable(Message msg) {
    441             long delaySoFar = SystemClock.elapsedRealtime() - mDisabledTimestamp;
    442             if (delaySoFar >= mReEnableDelayMillis) {
    443                 return false;
    444             }
    445 
    446             log("WifiController msg " + msg + " deferred for " +
    447                     (mReEnableDelayMillis - delaySoFar) + "ms");
    448 
    449             // need to defer this action.
    450             Message deferredMsg = obtainMessage(CMD_DEFERRED_TOGGLE);
    451             deferredMsg.obj = Message.obtain(msg);
    452             deferredMsg.arg1 = ++mDeferredEnableSerialNumber;
    453             sendMessageDelayed(deferredMsg, mReEnableDelayMillis - delaySoFar + DEFER_MARGIN_MS);
    454             return true;
    455         }
    456 
    457     }
    458 
    459     class StaEnabledState extends State {
    460         @Override
    461         public void enter() {
    462             mWifiStateMachine.setSupplicantRunning(true);
    463         }
    464         @Override
    465         public boolean processMessage(Message msg) {
    466             switch (msg.what) {
    467                 case CMD_WIFI_TOGGLED:
    468                     if (! mSettingsStore.isWifiToggleEnabled()) {
    469                         if (mSettingsStore.isScanAlwaysAvailable()) {
    470                             transitionTo(mStaDisabledWithScanState);
    471                         } else {
    472                             transitionTo(mApStaDisabledState);
    473                         }
    474                     }
    475                     break;
    476                 case CMD_AIRPLANE_TOGGLED:
    477                     /* When wi-fi is turned off due to airplane,
    478                     * disable entirely (including scan)
    479                     */
    480                     if (! mSettingsStore.isWifiToggleEnabled()) {
    481                         transitionTo(mApStaDisabledState);
    482                     }
    483                     break;
    484                 case CMD_EMERGENCY_MODE_CHANGED:
    485                     if (msg.arg1 == 1) {
    486                         transitionTo(mEcmState);
    487                         break;
    488                     }
    489                 default:
    490                     return NOT_HANDLED;
    491 
    492             }
    493             return HANDLED;
    494         }
    495     }
    496 
    497     class StaDisabledWithScanState extends State {
    498         private int mDeferredEnableSerialNumber = 0;
    499         private boolean mHaveDeferredEnable = false;
    500         private long mDisabledTimestamp;
    501 
    502         @Override
    503         public void enter() {
    504             mWifiStateMachine.setSupplicantRunning(true);
    505             mWifiStateMachine.setOperationalMode(WifiStateMachine.SCAN_ONLY_WITH_WIFI_OFF_MODE);
    506             mWifiStateMachine.setDriverStart(true);
    507             // Supplicant can't restart right away, so not the time we switched off
    508             mDisabledTimestamp = SystemClock.elapsedRealtime();
    509             mDeferredEnableSerialNumber++;
    510             mHaveDeferredEnable = false;
    511         }
    512 
    513         @Override
    514         public boolean processMessage(Message msg) {
    515             switch (msg.what) {
    516                 case CMD_WIFI_TOGGLED:
    517                     if (mSettingsStore.isWifiToggleEnabled()) {
    518                         if (doDeferEnable(msg)) {
    519                             if (mHaveDeferredEnable) {
    520                                 // have 2 toggles now, inc serial number and ignore both
    521                                 mDeferredEnableSerialNumber++;
    522                             }
    523                             mHaveDeferredEnable = !mHaveDeferredEnable;
    524                             break;
    525                         }
    526                         if (mDeviceIdle == false) {
    527                             transitionTo(mDeviceActiveState);
    528                         } else {
    529                             checkLocksAndTransitionWhenDeviceIdle();
    530                         }
    531                     }
    532                     break;
    533                 case CMD_AIRPLANE_TOGGLED:
    534                     if (mSettingsStore.isAirplaneModeOn() &&
    535                             ! mSettingsStore.isWifiToggleEnabled()) {
    536                         transitionTo(mApStaDisabledState);
    537                     }
    538                 case CMD_SCAN_ALWAYS_MODE_CHANGED:
    539                     if (! mSettingsStore.isScanAlwaysAvailable()) {
    540                         transitionTo(mApStaDisabledState);
    541                     }
    542                     break;
    543                 case CMD_SET_AP:
    544                     // Before starting tethering, turn off supplicant for scan mode
    545                     if (msg.arg1 == 1) {
    546                         deferMessage(msg);
    547                         transitionTo(mApStaDisabledState);
    548                     }
    549                     break;
    550                 case CMD_DEFERRED_TOGGLE:
    551                     if (msg.arg1 != mDeferredEnableSerialNumber) {
    552                         log("DEFERRED_TOGGLE ignored due to serial mismatch");
    553                         break;
    554                     }
    555                     logd("DEFERRED_TOGGLE handled");
    556                     sendMessage((Message)(msg.obj));
    557                     break;
    558                 default:
    559                     return NOT_HANDLED;
    560             }
    561             return HANDLED;
    562         }
    563 
    564         private boolean doDeferEnable(Message msg) {
    565             long delaySoFar = SystemClock.elapsedRealtime() - mDisabledTimestamp;
    566             if (delaySoFar >= mReEnableDelayMillis) {
    567                 return false;
    568             }
    569 
    570             log("WifiController msg " + msg + " deferred for " +
    571                     (mReEnableDelayMillis - delaySoFar) + "ms");
    572 
    573             // need to defer this action.
    574             Message deferredMsg = obtainMessage(CMD_DEFERRED_TOGGLE);
    575             deferredMsg.obj = Message.obtain(msg);
    576             deferredMsg.arg1 = ++mDeferredEnableSerialNumber;
    577             sendMessageDelayed(deferredMsg, mReEnableDelayMillis - delaySoFar + DEFER_MARGIN_MS);
    578             return true;
    579         }
    580 
    581     }
    582 
    583     class ApEnabledState extends State {
    584         @Override
    585         public boolean processMessage(Message msg) {
    586             switch (msg.what) {
    587                 case CMD_AIRPLANE_TOGGLED:
    588                     if (mSettingsStore.isAirplaneModeOn()) {
    589                         mWifiStateMachine.setHostApRunning(null, false);
    590                         transitionTo(mApStaDisabledState);
    591                     }
    592                     break;
    593                 case CMD_SET_AP:
    594                     if (msg.arg1 == 0) {
    595                         mWifiStateMachine.setHostApRunning(null, false);
    596                         transitionTo(mApStaDisabledState);
    597                     }
    598                     break;
    599                 default:
    600                     return NOT_HANDLED;
    601             }
    602             return HANDLED;
    603         }
    604     }
    605 
    606     class EcmState extends State {
    607         @Override
    608         public void enter() {
    609             mWifiStateMachine.setSupplicantRunning(false);
    610         }
    611 
    612         @Override
    613         public boolean processMessage(Message msg) {
    614             if (msg.what == CMD_EMERGENCY_MODE_CHANGED && msg.arg1 == 0) {
    615                 if (mSettingsStore.isWifiToggleEnabled()) {
    616                     if (mDeviceIdle == false) {
    617                         transitionTo(mDeviceActiveState);
    618                     } else {
    619                         checkLocksAndTransitionWhenDeviceIdle();
    620                     }
    621                 } else if (mSettingsStore.isScanAlwaysAvailable()) {
    622                     transitionTo(mStaDisabledWithScanState);
    623                 } else {
    624                     transitionTo(mApStaDisabledState);
    625                 }
    626                 return HANDLED;
    627             } else {
    628                 return NOT_HANDLED;
    629             }
    630         }
    631     }
    632 
    633     /* Parent: StaEnabledState */
    634     class DeviceActiveState extends State {
    635         @Override
    636         public void enter() {
    637             mWifiStateMachine.setOperationalMode(WifiStateMachine.CONNECT_MODE);
    638             mWifiStateMachine.setDriverStart(true);
    639             mWifiStateMachine.setHighPerfModeEnabled(false);
    640         }
    641 
    642         @Override
    643         public boolean processMessage(Message msg) {
    644             if (msg.what == CMD_DEVICE_IDLE) {
    645                 checkLocksAndTransitionWhenDeviceIdle();
    646                 // We let default state handle the rest of work
    647             } else if (msg.what == CMD_USER_PRESENT) {
    648                 // TLS networks can't connect until user unlocks keystore. KeyStore
    649                 // unlocks when the user punches PIN after the reboot. So use this
    650                 // trigger to get those networks connected.
    651                 if (mFirstUserSignOnSeen == false) {
    652                     mWifiStateMachine.reloadTlsNetworksAndReconnect();
    653                 }
    654                 mFirstUserSignOnSeen = true;
    655                 return HANDLED;
    656             }
    657             return NOT_HANDLED;
    658         }
    659     }
    660 
    661     /* Parent: StaEnabledState */
    662     class DeviceInactiveState extends State {
    663         @Override
    664         public boolean processMessage(Message msg) {
    665             switch (msg.what) {
    666                 case CMD_LOCKS_CHANGED:
    667                     checkLocksAndTransitionWhenDeviceIdle();
    668                     updateBatteryWorkSource();
    669                     return HANDLED;
    670                 case CMD_SCREEN_ON:
    671                     transitionTo(mDeviceActiveState);
    672                     // More work in default state
    673                     return NOT_HANDLED;
    674                 default:
    675                     return NOT_HANDLED;
    676             }
    677         }
    678     }
    679 
    680     /* Parent: DeviceInactiveState. Device is inactive, but an app is holding a scan only lock. */
    681     class ScanOnlyLockHeldState extends State {
    682         @Override
    683         public void enter() {
    684             mWifiStateMachine.setOperationalMode(WifiStateMachine.SCAN_ONLY_MODE);
    685             mWifiStateMachine.setDriverStart(true);
    686         }
    687     }
    688 
    689     /* Parent: DeviceInactiveState. Device is inactive, but an app is holding a full lock. */
    690     class FullLockHeldState extends State {
    691         @Override
    692         public void enter() {
    693             mWifiStateMachine.setOperationalMode(WifiStateMachine.CONNECT_MODE);
    694             mWifiStateMachine.setDriverStart(true);
    695             mWifiStateMachine.setHighPerfModeEnabled(false);
    696         }
    697     }
    698 
    699     /* Parent: DeviceInactiveState. Device is inactive, but an app is holding a high perf lock. */
    700     class FullHighPerfLockHeldState extends State {
    701         @Override
    702         public void enter() {
    703             mWifiStateMachine.setOperationalMode(WifiStateMachine.CONNECT_MODE);
    704             mWifiStateMachine.setDriverStart(true);
    705             mWifiStateMachine.setHighPerfModeEnabled(true);
    706         }
    707     }
    708 
    709     /* Parent: DeviceInactiveState. Device is inactive and no app is holding a wifi lock. */
    710     class NoLockHeldState extends State {
    711         @Override
    712         public void enter() {
    713             mWifiStateMachine.setDriverStart(false);
    714         }
    715     }
    716 
    717     private void checkLocksAndTransitionWhenDeviceIdle() {
    718         if (mLocks.hasLocks()) {
    719             switch (mLocks.getStrongestLockMode()) {
    720                 case WIFI_MODE_FULL:
    721                     transitionTo(mFullLockHeldState);
    722                     break;
    723                 case WIFI_MODE_FULL_HIGH_PERF:
    724                     transitionTo(mFullHighPerfLockHeldState);
    725                     break;
    726                 case WIFI_MODE_SCAN_ONLY:
    727                     transitionTo(mScanOnlyLockHeldState);
    728                     break;
    729                 default:
    730                     loge("Illegal lock " + mLocks.getStrongestLockMode());
    731             }
    732         } else {
    733             if (mSettingsStore.isScanAlwaysAvailable()) {
    734                 transitionTo(mScanOnlyLockHeldState);
    735             } else {
    736                 transitionTo(mNoLockHeldState);
    737             }
    738         }
    739     }
    740 
    741     @Override
    742     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
    743         super.dump(fd, pw, args);
    744 
    745         pw.println("mScreenOff " + mScreenOff);
    746         pw.println("mDeviceIdle " + mDeviceIdle);
    747         pw.println("mPluggedType " + mPluggedType);
    748         pw.println("mIdleMillis " + mIdleMillis);
    749         pw.println("mSleepPolicy " + mSleepPolicy);
    750     }
    751 }
    752