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