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