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