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.content.BroadcastReceiver; 20 import android.content.Context; 21 import android.content.Intent; 22 import android.content.IntentFilter; 23 import android.location.LocationManager; 24 import android.net.wifi.WifiManager; 25 import android.os.Handler; 26 import android.os.Looper; 27 import android.os.Message; 28 import android.os.SystemClock; 29 import android.provider.Settings; 30 import android.util.Log; 31 32 import com.android.internal.R; 33 import com.android.internal.util.Protocol; 34 import com.android.internal.util.State; 35 import com.android.internal.util.StateMachine; 36 import com.android.server.wifi.util.WifiPermissionsUtil; 37 38 /** 39 * WifiController is the class used to manage wifi state for various operating 40 * modes (normal, airplane, wifi hotspot, etc.). 41 */ 42 public class WifiController extends StateMachine { 43 private static final String TAG = "WifiController"; 44 private static final boolean DBG = false; 45 private Context mContext; 46 private boolean mFirstUserSignOnSeen = false; 47 48 /** 49 * See {@link Settings.Global#WIFI_REENABLE_DELAY_MS}. This is the default value if a 50 * Settings.Global value is not present. This is the minimum time after wifi is disabled 51 * we'll act on an enable. Enable requests received before this delay will be deferred. 52 */ 53 private static final long DEFAULT_REENABLE_DELAY_MS = 500; 54 55 // Maximum limit to use for timeout delay if the value from overlay setting is too large. 56 private static final int MAX_RECOVERY_TIMEOUT_DELAY_MS = 4000; 57 58 // finding that delayed messages can sometimes be delivered earlier than expected 59 // probably rounding errors. add a margin to prevent problems 60 private static final long DEFER_MARGIN_MS = 5; 61 62 /* References to values tracked in WifiService */ 63 private final ClientModeImpl mClientModeImpl; 64 private final Looper mClientModeImplLooper; 65 private final ActiveModeWarden mActiveModeWarden; 66 private final WifiSettingsStore mSettingsStore; 67 private final FrameworkFacade mFacade; 68 private final WifiPermissionsUtil mWifiPermissionsUtil; 69 70 private long mReEnableDelayMillis; 71 72 private int mRecoveryDelayMillis; 73 74 private static final int BASE = Protocol.BASE_WIFI_CONTROLLER; 75 76 static final int CMD_EMERGENCY_MODE_CHANGED = BASE + 1; 77 static final int CMD_SCAN_ALWAYS_MODE_CHANGED = BASE + 7; 78 static final int CMD_WIFI_TOGGLED = BASE + 8; 79 static final int CMD_AIRPLANE_TOGGLED = BASE + 9; 80 static final int CMD_SET_AP = BASE + 10; 81 static final int CMD_DEFERRED_TOGGLE = BASE + 11; 82 static final int CMD_AP_START_FAILURE = BASE + 13; 83 static final int CMD_EMERGENCY_CALL_STATE_CHANGED = BASE + 14; 84 static final int CMD_AP_STOPPED = BASE + 15; 85 static final int CMD_STA_START_FAILURE = BASE + 16; 86 // Command used to trigger a wifi stack restart when in active mode 87 static final int CMD_RECOVERY_RESTART_WIFI = BASE + 17; 88 // Internal command used to complete wifi stack restart 89 private static final int CMD_RECOVERY_RESTART_WIFI_CONTINUE = BASE + 18; 90 // Command to disable wifi when SelfRecovery is throttled or otherwise not doing full recovery 91 static final int CMD_RECOVERY_DISABLE_WIFI = BASE + 19; 92 static final int CMD_STA_STOPPED = BASE + 20; 93 static final int CMD_SCANNING_STOPPED = BASE + 21; 94 static final int CMD_DEFERRED_RECOVERY_RESTART_WIFI = BASE + 22; 95 96 private DefaultState mDefaultState = new DefaultState(); 97 private StaEnabledState mStaEnabledState = new StaEnabledState(); 98 private StaDisabledState mStaDisabledState = new StaDisabledState(); 99 private StaDisabledWithScanState mStaDisabledWithScanState = new StaDisabledWithScanState(); 100 private EcmState mEcmState = new EcmState(); 101 102 private ScanOnlyModeManager.Listener mScanOnlyModeCallback = new ScanOnlyCallback(); 103 private ClientModeManager.Listener mClientModeCallback = new ClientModeCallback(); 104 WifiController(Context context, ClientModeImpl clientModeImpl, Looper clientModeImplLooper, WifiSettingsStore wss, Looper wifiServiceLooper, FrameworkFacade f, ActiveModeWarden amw, WifiPermissionsUtil wifiPermissionsUtil)105 WifiController(Context context, ClientModeImpl clientModeImpl, Looper clientModeImplLooper, 106 WifiSettingsStore wss, Looper wifiServiceLooper, FrameworkFacade f, 107 ActiveModeWarden amw, WifiPermissionsUtil wifiPermissionsUtil) { 108 super(TAG, wifiServiceLooper); 109 mFacade = f; 110 mContext = context; 111 mClientModeImpl = clientModeImpl; 112 mClientModeImplLooper = clientModeImplLooper; 113 mActiveModeWarden = amw; 114 mSettingsStore = wss; 115 mWifiPermissionsUtil = wifiPermissionsUtil; 116 117 // CHECKSTYLE:OFF IndentationCheck 118 addState(mDefaultState); 119 addState(mStaDisabledState, mDefaultState); 120 addState(mStaEnabledState, mDefaultState); 121 addState(mStaDisabledWithScanState, mDefaultState); 122 addState(mEcmState, mDefaultState); 123 // CHECKSTYLE:ON IndentationCheck 124 125 setLogRecSize(100); 126 setLogOnlyTransitions(false); 127 128 // register for state updates via callbacks (vs the intents registered below) 129 mActiveModeWarden.registerScanOnlyCallback(mScanOnlyModeCallback); 130 mActiveModeWarden.registerClientModeCallback(mClientModeCallback); 131 132 readWifiReEnableDelay(); 133 readWifiRecoveryDelay(); 134 } 135 136 @Override start()137 public void start() { 138 boolean isAirplaneModeOn = mSettingsStore.isAirplaneModeOn(); 139 boolean isWifiEnabled = mSettingsStore.isWifiToggleEnabled(); 140 boolean isScanningAlwaysAvailable = mSettingsStore.isScanAlwaysAvailable(); 141 boolean isLocationModeActive = mWifiPermissionsUtil.isLocationModeEnabled(); 142 143 log("isAirplaneModeOn = " + isAirplaneModeOn 144 + ", isWifiEnabled = " + isWifiEnabled 145 + ", isScanningAvailable = " + isScanningAlwaysAvailable 146 + ", isLocationModeActive = " + isLocationModeActive); 147 148 if (checkScanOnlyModeAvailable()) { 149 setInitialState(mStaDisabledWithScanState); 150 } else { 151 setInitialState(mStaDisabledState); 152 } 153 IntentFilter filter = new IntentFilter(); 154 filter.addAction(WifiManager.WIFI_AP_STATE_CHANGED_ACTION); 155 filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); 156 filter.addAction(LocationManager.MODE_CHANGED_ACTION); 157 mContext.registerReceiver( 158 new BroadcastReceiver() { 159 @Override 160 public void onReceive(Context context, Intent intent) { 161 String action = intent.getAction(); 162 if (action.equals(WifiManager.WIFI_AP_STATE_CHANGED_ACTION)) { 163 int state = intent.getIntExtra( 164 WifiManager.EXTRA_WIFI_AP_STATE, 165 WifiManager.WIFI_AP_STATE_FAILED); 166 if (state == WifiManager.WIFI_AP_STATE_FAILED) { 167 Log.e(TAG, "SoftAP start failed"); 168 sendMessage(CMD_AP_START_FAILURE); 169 } else if (state == WifiManager.WIFI_AP_STATE_DISABLED) { 170 sendMessage(CMD_AP_STOPPED); 171 } 172 } else if (action.equals(LocationManager.MODE_CHANGED_ACTION)) { 173 // Location mode has been toggled... trigger with the scan change 174 // update to make sure we are in the correct mode 175 sendMessage(CMD_SCAN_ALWAYS_MODE_CHANGED); 176 } 177 } 178 }, 179 new IntentFilter(filter)); 180 super.start(); 181 } 182 checkScanOnlyModeAvailable()183 private boolean checkScanOnlyModeAvailable() { 184 // first check if Location service is disabled, if so return false 185 if (!mWifiPermissionsUtil.isLocationModeEnabled()) { 186 return false; 187 } 188 return mSettingsStore.isScanAlwaysAvailable(); 189 } 190 191 /** 192 * Listener used to receive scan mode updates - really needed for disabled updates to trigger 193 * mode changes. 194 */ 195 private class ScanOnlyCallback implements ScanOnlyModeManager.Listener { 196 @Override onStateChanged(int state)197 public void onStateChanged(int state) { 198 if (state == WifiManager.WIFI_STATE_UNKNOWN) { 199 Log.d(TAG, "ScanOnlyMode unexpected failure: state unknown"); 200 } else if (state == WifiManager.WIFI_STATE_DISABLED) { 201 Log.d(TAG, "ScanOnlyMode stopped"); 202 sendMessage(CMD_SCANNING_STOPPED); 203 } else if (state == WifiManager.WIFI_STATE_ENABLED) { 204 // scan mode is ready to go 205 Log.d(TAG, "scan mode active"); 206 } else { 207 Log.d(TAG, "unexpected state update: " + state); 208 } 209 } 210 } 211 212 /** 213 * Listener used to receive client mode updates 214 */ 215 private class ClientModeCallback implements ClientModeManager.Listener { 216 @Override onStateChanged(int state)217 public void onStateChanged(int state) { 218 if (state == WifiManager.WIFI_STATE_UNKNOWN) { 219 logd("ClientMode unexpected failure: state unknown"); 220 sendMessage(CMD_STA_START_FAILURE); 221 } else if (state == WifiManager.WIFI_STATE_DISABLED) { 222 logd("ClientMode stopped"); 223 sendMessage(CMD_STA_STOPPED); 224 } else if (state == WifiManager.WIFI_STATE_ENABLED) { 225 // scan mode is ready to go 226 logd("client mode active"); 227 } else { 228 logd("unexpected state update: " + state); 229 } 230 } 231 } 232 readWifiReEnableDelay()233 private void readWifiReEnableDelay() { 234 mReEnableDelayMillis = mFacade.getLongSetting(mContext, 235 Settings.Global.WIFI_REENABLE_DELAY_MS, DEFAULT_REENABLE_DELAY_MS); 236 } 237 readWifiRecoveryDelay()238 private void readWifiRecoveryDelay() { 239 mRecoveryDelayMillis = mContext.getResources().getInteger( 240 R.integer.config_wifi_framework_recovery_timeout_delay); 241 if (mRecoveryDelayMillis > MAX_RECOVERY_TIMEOUT_DELAY_MS) { 242 mRecoveryDelayMillis = MAX_RECOVERY_TIMEOUT_DELAY_MS; 243 Log.w(TAG, "Overriding timeout delay with maximum limit value"); 244 } 245 } 246 247 class DefaultState extends State { 248 @Override processMessage(Message msg)249 public boolean processMessage(Message msg) { 250 switch (msg.what) { 251 case CMD_SCAN_ALWAYS_MODE_CHANGED: 252 case CMD_WIFI_TOGGLED: 253 case CMD_AP_START_FAILURE: 254 case CMD_SCANNING_STOPPED: 255 case CMD_STA_STOPPED: 256 case CMD_STA_START_FAILURE: 257 case CMD_RECOVERY_RESTART_WIFI_CONTINUE: 258 case CMD_DEFERRED_RECOVERY_RESTART_WIFI: 259 break; 260 case CMD_RECOVERY_DISABLE_WIFI: 261 log("Recovery has been throttled, disable wifi"); 262 mActiveModeWarden.shutdownWifi(); 263 transitionTo(mStaDisabledState); 264 break; 265 case CMD_RECOVERY_RESTART_WIFI: 266 deferMessage(obtainMessage(CMD_DEFERRED_RECOVERY_RESTART_WIFI)); 267 mActiveModeWarden.shutdownWifi(); 268 transitionTo(mStaDisabledState); 269 break; 270 case CMD_DEFERRED_TOGGLE: 271 log("DEFERRED_TOGGLE ignored due to state change"); 272 break; 273 case CMD_SET_AP: 274 // note: CMD_SET_AP is handled/dropped in ECM mode - will not start here 275 if (msg.arg1 == 1) { 276 SoftApModeConfiguration config = (SoftApModeConfiguration) msg.obj; 277 mActiveModeWarden.enterSoftAPMode((SoftApModeConfiguration) msg.obj); 278 } else { 279 mActiveModeWarden.stopSoftAPMode(msg.arg2); 280 } 281 break; 282 case CMD_AIRPLANE_TOGGLED: 283 if (mSettingsStore.isAirplaneModeOn()) { 284 log("Airplane mode toggled, shutdown all modes"); 285 mActiveModeWarden.shutdownWifi(); 286 transitionTo(mStaDisabledState); 287 } else { 288 log("Airplane mode disabled, determine next state"); 289 if (mSettingsStore.isWifiToggleEnabled()) { 290 transitionTo(mStaEnabledState); 291 } else if (checkScanOnlyModeAvailable()) { 292 transitionTo(mStaDisabledWithScanState); 293 } 294 // wifi should remain disabled, do not need to transition 295 } 296 break; 297 case CMD_EMERGENCY_CALL_STATE_CHANGED: 298 case CMD_EMERGENCY_MODE_CHANGED: 299 if (msg.arg1 == 1) { 300 transitionTo(mEcmState); 301 } 302 break; 303 case CMD_AP_STOPPED: 304 log("SoftAp mode disabled, determine next state"); 305 if (mSettingsStore.isWifiToggleEnabled()) { 306 transitionTo(mStaEnabledState); 307 } else if (checkScanOnlyModeAvailable()) { 308 transitionTo(mStaDisabledWithScanState); 309 } 310 // wifi should remain disabled, do not need to transition 311 break; 312 default: 313 throw new RuntimeException("WifiController.handleMessage " + msg.what); 314 } 315 return HANDLED; 316 } 317 318 } 319 320 class StaDisabledState extends State { 321 private int mDeferredEnableSerialNumber = 0; 322 private boolean mHaveDeferredEnable = false; 323 private long mDisabledTimestamp; 324 325 @Override enter()326 public void enter() { 327 mActiveModeWarden.disableWifi(); 328 // Supplicant can't restart right away, so note the time we switched off 329 mDisabledTimestamp = SystemClock.elapsedRealtime(); 330 mDeferredEnableSerialNumber++; 331 mHaveDeferredEnable = false; 332 } 333 @Override processMessage(Message msg)334 public boolean processMessage(Message msg) { 335 switch (msg.what) { 336 case CMD_WIFI_TOGGLED: 337 if (mSettingsStore.isWifiToggleEnabled()) { 338 if (doDeferEnable(msg)) { 339 if (mHaveDeferredEnable) { 340 // have 2 toggles now, inc serial number and ignore both 341 mDeferredEnableSerialNumber++; 342 } 343 mHaveDeferredEnable = !mHaveDeferredEnable; 344 break; 345 } 346 transitionTo(mStaEnabledState); 347 } else if (checkScanOnlyModeAvailable()) { 348 // only go to scan mode if we aren't in airplane mode 349 if (mSettingsStore.isAirplaneModeOn()) { 350 transitionTo(mStaDisabledWithScanState); 351 } 352 } 353 break; 354 case CMD_SCAN_ALWAYS_MODE_CHANGED: 355 if (checkScanOnlyModeAvailable()) { 356 transitionTo(mStaDisabledWithScanState); 357 break; 358 } 359 break; 360 case CMD_SET_AP: 361 if (msg.arg1 == 1) { 362 // remember that we were disabled, but pass the command up to start softap 363 mSettingsStore.setWifiSavedState(WifiSettingsStore.WIFI_DISABLED); 364 } 365 return NOT_HANDLED; 366 case CMD_DEFERRED_TOGGLE: 367 if (msg.arg1 != mDeferredEnableSerialNumber) { 368 log("DEFERRED_TOGGLE ignored due to serial mismatch"); 369 break; 370 } 371 log("DEFERRED_TOGGLE handled"); 372 sendMessage((Message)(msg.obj)); 373 break; 374 case CMD_DEFERRED_RECOVERY_RESTART_WIFI: 375 // wait mRecoveryDelayMillis for letting driver clean reset. 376 sendMessageDelayed(CMD_RECOVERY_RESTART_WIFI_CONTINUE, mRecoveryDelayMillis); 377 break; 378 case CMD_RECOVERY_RESTART_WIFI_CONTINUE: 379 if (mSettingsStore.isWifiToggleEnabled()) { 380 // wifi is currently disabled but the toggle is on, must have had an 381 // interface down before the recovery triggered 382 transitionTo(mStaEnabledState); 383 break; 384 } else if (checkScanOnlyModeAvailable()) { 385 transitionTo(mStaDisabledWithScanState); 386 break; 387 } 388 break; 389 default: 390 return NOT_HANDLED; 391 } 392 return HANDLED; 393 } 394 doDeferEnable(Message msg)395 private boolean doDeferEnable(Message msg) { 396 long delaySoFar = SystemClock.elapsedRealtime() - mDisabledTimestamp; 397 if (delaySoFar >= mReEnableDelayMillis) { 398 return false; 399 } 400 401 log("WifiController msg " + msg + " deferred for " + 402 (mReEnableDelayMillis - delaySoFar) + "ms"); 403 404 // need to defer this action. 405 Message deferredMsg = obtainMessage(CMD_DEFERRED_TOGGLE); 406 deferredMsg.obj = Message.obtain(msg); 407 deferredMsg.arg1 = ++mDeferredEnableSerialNumber; 408 sendMessageDelayed(deferredMsg, mReEnableDelayMillis - delaySoFar + DEFER_MARGIN_MS); 409 return true; 410 } 411 412 } 413 414 class StaEnabledState extends State { 415 @Override enter()416 public void enter() { 417 log("StaEnabledState.enter()"); 418 mActiveModeWarden.enterClientMode(); 419 } 420 421 @Override processMessage(Message msg)422 public boolean processMessage(Message msg) { 423 switch (msg.what) { 424 case CMD_WIFI_TOGGLED: 425 if (! mSettingsStore.isWifiToggleEnabled()) { 426 if (checkScanOnlyModeAvailable()) { 427 transitionTo(mStaDisabledWithScanState); 428 } else { 429 transitionTo(mStaDisabledState); 430 } 431 } 432 break; 433 case CMD_AIRPLANE_TOGGLED: 434 // airplane mode toggled on is handled in the default state 435 if (mSettingsStore.isAirplaneModeOn()) { 436 return NOT_HANDLED; 437 } else { 438 // when airplane mode is toggled off, but wifi is on, we can keep it on 439 log("airplane mode toggled - and airplane mode is off. return handled"); 440 return HANDLED; 441 } 442 case CMD_STA_START_FAILURE: 443 if (!checkScanOnlyModeAvailable()) { 444 transitionTo(mStaDisabledState); 445 } else { 446 transitionTo(mStaDisabledWithScanState); 447 } 448 break; 449 case CMD_SET_AP: 450 if (msg.arg1 == 1) { 451 // remember that we were enabled, but pass the command up to start softap 452 mSettingsStore.setWifiSavedState(WifiSettingsStore.WIFI_ENABLED); 453 } 454 return NOT_HANDLED; 455 case CMD_AP_START_FAILURE: 456 case CMD_AP_STOPPED: 457 // already in a wifi mode, no need to check where we should go with softap 458 // stopped 459 break; 460 case CMD_STA_STOPPED: 461 // Client mode stopped. head to Disabled to wait for next command 462 transitionTo(mStaDisabledState); 463 break; 464 case CMD_RECOVERY_RESTART_WIFI: 465 final String bugTitle; 466 final String bugDetail; 467 if (msg.arg1 < SelfRecovery.REASON_STRINGS.length && msg.arg1 >= 0) { 468 bugDetail = SelfRecovery.REASON_STRINGS[msg.arg1]; 469 bugTitle = "Wi-Fi BugReport: " + bugDetail; 470 } else { 471 bugDetail = ""; 472 bugTitle = "Wi-Fi BugReport"; 473 } 474 if (msg.arg1 != SelfRecovery.REASON_LAST_RESORT_WATCHDOG) { 475 (new Handler(mClientModeImplLooper)).post(() -> { 476 mClientModeImpl.takeBugReport(bugTitle, bugDetail); 477 }); 478 } 479 // after the bug report trigger, more handling needs to be done 480 return NOT_HANDLED; 481 default: 482 return NOT_HANDLED; 483 } 484 return HANDLED; 485 } 486 } 487 488 class StaDisabledWithScanState extends State { 489 private int mDeferredEnableSerialNumber = 0; 490 private boolean mHaveDeferredEnable = false; 491 private long mDisabledTimestamp; 492 493 @Override enter()494 public void enter() { 495 // now trigger the actual mode switch in ActiveModeWarden 496 mActiveModeWarden.enterScanOnlyMode(); 497 498 // TODO b/71559473: remove the defered enable after mode management changes are complete 499 // Supplicant can't restart right away, so not the time we switched off 500 mDisabledTimestamp = SystemClock.elapsedRealtime(); 501 mDeferredEnableSerialNumber++; 502 mHaveDeferredEnable = false; 503 } 504 505 @Override processMessage(Message msg)506 public boolean processMessage(Message msg) { 507 switch (msg.what) { 508 case CMD_WIFI_TOGGLED: 509 if (mSettingsStore.isWifiToggleEnabled()) { 510 if (doDeferEnable(msg)) { 511 if (mHaveDeferredEnable) { 512 // have 2 toggles now, inc serial number and ignore both 513 mDeferredEnableSerialNumber++; 514 } 515 mHaveDeferredEnable = !mHaveDeferredEnable; 516 break; 517 } 518 transitionTo(mStaEnabledState); 519 } 520 break; 521 case CMD_SCAN_ALWAYS_MODE_CHANGED: 522 if (!checkScanOnlyModeAvailable()) { 523 log("StaDisabledWithScanState: scan no longer available"); 524 transitionTo(mStaDisabledState); 525 } 526 break; 527 case CMD_SET_AP: 528 if (msg.arg1 == 1) { 529 // remember that we were disabled, but pass the command up to start softap 530 mSettingsStore.setWifiSavedState(WifiSettingsStore.WIFI_DISABLED); 531 } 532 return NOT_HANDLED; 533 case CMD_DEFERRED_TOGGLE: 534 if (msg.arg1 != mDeferredEnableSerialNumber) { 535 log("DEFERRED_TOGGLE ignored due to serial mismatch"); 536 break; 537 } 538 logd("DEFERRED_TOGGLE handled"); 539 sendMessage((Message)(msg.obj)); 540 break; 541 case CMD_AP_START_FAILURE: 542 case CMD_AP_STOPPED: 543 // already in a wifi mode, no need to check where we should go with softap 544 // stopped 545 break; 546 case CMD_SCANNING_STOPPED: 547 // stopped due to interface destruction - return to disabled and wait 548 log("WifiController: SCANNING_STOPPED when in scan mode -> StaDisabled"); 549 transitionTo(mStaDisabledState); 550 break; 551 default: 552 return NOT_HANDLED; 553 } 554 return HANDLED; 555 } 556 doDeferEnable(Message msg)557 private boolean doDeferEnable(Message msg) { 558 long delaySoFar = SystemClock.elapsedRealtime() - mDisabledTimestamp; 559 if (delaySoFar >= mReEnableDelayMillis) { 560 return false; 561 } 562 563 log("WifiController msg " + msg + " deferred for " + 564 (mReEnableDelayMillis - delaySoFar) + "ms"); 565 566 // need to defer this action. 567 Message deferredMsg = obtainMessage(CMD_DEFERRED_TOGGLE); 568 deferredMsg.obj = Message.obtain(msg); 569 deferredMsg.arg1 = ++mDeferredEnableSerialNumber; 570 sendMessageDelayed(deferredMsg, mReEnableDelayMillis - delaySoFar + DEFER_MARGIN_MS); 571 return true; 572 } 573 574 } 575 576 /** 577 * Determine the next state based on the current settings (e.g. saved 578 * wifi state). 579 */ getNextWifiState()580 private State getNextWifiState() { 581 if (mSettingsStore.getWifiSavedState() == WifiSettingsStore.WIFI_ENABLED) { 582 return mStaEnabledState; 583 } 584 585 if (checkScanOnlyModeAvailable()) { 586 return mStaDisabledWithScanState; 587 } 588 589 return mStaDisabledState; 590 } 591 592 class EcmState extends State { 593 // we can enter EcmState either because an emergency call started or because 594 // emergency callback mode started. This count keeps track of how many such 595 // events happened; so we can exit after all are undone 596 597 private int mEcmEntryCount; 598 @Override enter()599 public void enter() { 600 mActiveModeWarden.stopSoftAPMode(WifiManager.IFACE_IP_MODE_UNSPECIFIED); 601 boolean configWiFiDisableInECBM = 602 mFacade.getConfigWiFiDisableInECBM(mContext); 603 log("WifiController msg getConfigWiFiDisableInECBM " 604 + configWiFiDisableInECBM); 605 if (configWiFiDisableInECBM) { 606 mActiveModeWarden.shutdownWifi(); 607 } 608 mEcmEntryCount = 1; 609 } 610 611 /** 612 * Handles messages received while in EcmMode. 613 */ 614 @Override processMessage(Message msg)615 public boolean processMessage(Message msg) { 616 switch (msg.what) { 617 case CMD_EMERGENCY_CALL_STATE_CHANGED: 618 if (msg.arg1 == 1) { 619 // nothing to do - just says emergency call started 620 mEcmEntryCount++; 621 } else if (msg.arg1 == 0) { 622 // emergency call ended 623 decrementCountAndReturnToAppropriateState(); 624 } 625 return HANDLED; 626 case CMD_EMERGENCY_MODE_CHANGED: 627 if (msg.arg1 == 1) { 628 // Transitioned into emergency callback mode 629 mEcmEntryCount++; 630 } else if (msg.arg1 == 0) { 631 // out of emergency callback mode 632 decrementCountAndReturnToAppropriateState(); 633 } 634 return HANDLED; 635 case CMD_RECOVERY_RESTART_WIFI: 636 case CMD_RECOVERY_DISABLE_WIFI: 637 // do not want to restart wifi if we are in emergency mode 638 return HANDLED; 639 case CMD_AP_STOPPED: 640 case CMD_SCANNING_STOPPED: 641 case CMD_STA_STOPPED: 642 // do not want to trigger a mode switch if we are in emergency mode 643 return HANDLED; 644 case CMD_SET_AP: 645 // do not want to start softap if we are in emergency mode 646 return HANDLED; 647 default: 648 return NOT_HANDLED; 649 } 650 } 651 decrementCountAndReturnToAppropriateState()652 private void decrementCountAndReturnToAppropriateState() { 653 boolean exitEcm = false; 654 655 if (mEcmEntryCount == 0) { 656 loge("mEcmEntryCount is 0; exiting Ecm"); 657 exitEcm = true; 658 } else if (--mEcmEntryCount == 0) { 659 exitEcm = true; 660 } 661 662 if (exitEcm) { 663 if (mSettingsStore.isWifiToggleEnabled()) { 664 transitionTo(mStaEnabledState); 665 } else if (checkScanOnlyModeAvailable()) { 666 transitionTo(mStaDisabledWithScanState); 667 } else { 668 transitionTo(mStaDisabledState); 669 } 670 } 671 } 672 } 673 } 674