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