1 /* 2 * Copyright (C) 2016 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.IFACE_IP_MODE_LOCAL_ONLY; 20 import static android.net.wifi.WifiManager.IFACE_IP_MODE_TETHERED; 21 22 import android.annotation.NonNull; 23 import android.content.BroadcastReceiver; 24 import android.content.Context; 25 import android.content.Intent; 26 import android.content.IntentFilter; 27 import android.location.LocationManager; 28 import android.net.wifi.SoftApCapability; 29 import android.net.wifi.SoftApConfiguration; 30 import android.net.wifi.WifiManager; 31 import android.os.BatteryStatsManager; 32 import android.os.Handler; 33 import android.os.Looper; 34 import android.os.Message; 35 import android.util.ArraySet; 36 import android.util.Log; 37 38 import com.android.internal.annotations.VisibleForTesting; 39 import com.android.internal.util.IState; 40 import com.android.internal.util.Preconditions; 41 import com.android.internal.util.Protocol; 42 import com.android.internal.util.State; 43 import com.android.internal.util.StateMachine; 44 import com.android.server.wifi.util.WifiPermissionsUtil; 45 import com.android.wifi.resources.R; 46 47 import java.io.FileDescriptor; 48 import java.io.PrintWriter; 49 import java.util.Collection; 50 import java.util.List; 51 52 /** 53 * This class provides the implementation for different WiFi operating modes. 54 */ 55 public class ActiveModeWarden { 56 private static final String TAG = "WifiActiveModeWarden"; 57 private static final String STATE_MACHINE_EXITED_STATE_NAME = "STATE_MACHINE_EXITED"; 58 59 // Holder for active mode managers 60 private final ArraySet<ActiveModeManager> mActiveModeManagers; 61 // DefaultModeManager used to service API calls when there are not active mode managers. 62 private final DefaultModeManager mDefaultModeManager; 63 64 private final WifiInjector mWifiInjector; 65 private final Looper mLooper; 66 private final Handler mHandler; 67 private final Context mContext; 68 private final ClientModeImpl mClientModeImpl; 69 private final WifiSettingsStore mSettingsStore; 70 private final FrameworkFacade mFacade; 71 private final WifiPermissionsUtil mWifiPermissionsUtil; 72 private final BatteryStatsManager mBatteryStatsManager; 73 private final ScanRequestProxy mScanRequestProxy; 74 private final WifiNative mWifiNative; 75 private final WifiController mWifiController; 76 77 private WifiManager.SoftApCallback mSoftApCallback; 78 private WifiManager.SoftApCallback mLohsCallback; 79 80 private boolean mCanRequestMoreClientModeManagers = false; 81 private boolean mCanRequestMoreSoftApManagers = false; 82 private boolean mIsShuttingdown = false; 83 84 /** 85 * Called from WifiServiceImpl to register a callback for notifications from SoftApManager 86 */ registerSoftApCallback(@onNull WifiManager.SoftApCallback callback)87 public void registerSoftApCallback(@NonNull WifiManager.SoftApCallback callback) { 88 mSoftApCallback = callback; 89 } 90 91 /** 92 * Called from WifiServiceImpl to register a callback for notifications from SoftApManager 93 * for local-only hotspot. 94 */ registerLohsCallback(@onNull WifiManager.SoftApCallback callback)95 public void registerLohsCallback(@NonNull WifiManager.SoftApCallback callback) { 96 mLohsCallback = callback; 97 } 98 ActiveModeWarden(WifiInjector wifiInjector, Looper looper, WifiNative wifiNative, DefaultModeManager defaultModeManager, BatteryStatsManager batteryStatsManager, BaseWifiDiagnostics wifiDiagnostics, Context context, ClientModeImpl clientModeImpl, WifiSettingsStore settingsStore, FrameworkFacade facade, WifiPermissionsUtil wifiPermissionsUtil)99 ActiveModeWarden(WifiInjector wifiInjector, 100 Looper looper, 101 WifiNative wifiNative, 102 DefaultModeManager defaultModeManager, 103 BatteryStatsManager batteryStatsManager, 104 BaseWifiDiagnostics wifiDiagnostics, 105 Context context, 106 ClientModeImpl clientModeImpl, 107 WifiSettingsStore settingsStore, 108 FrameworkFacade facade, 109 WifiPermissionsUtil wifiPermissionsUtil) { 110 mWifiInjector = wifiInjector; 111 mLooper = looper; 112 mHandler = new Handler(looper); 113 mContext = context; 114 mClientModeImpl = clientModeImpl; 115 mSettingsStore = settingsStore; 116 mFacade = facade; 117 mWifiPermissionsUtil = wifiPermissionsUtil; 118 mActiveModeManagers = new ArraySet<>(); 119 mDefaultModeManager = defaultModeManager; 120 mBatteryStatsManager = batteryStatsManager; 121 mScanRequestProxy = wifiInjector.getScanRequestProxy(); 122 mWifiNative = wifiNative; 123 mWifiController = new WifiController(); 124 125 wifiNative.registerStatusListener(isReady -> { 126 if (!isReady && !mIsShuttingdown) { 127 mHandler.post(() -> { 128 Log.e(TAG, "One of the native daemons died. Triggering recovery"); 129 wifiDiagnostics.captureBugReportData( 130 WifiDiagnostics.REPORT_REASON_WIFINATIVE_FAILURE); 131 132 // immediately trigger SelfRecovery if we receive a notice about an 133 // underlying daemon failure 134 // Note: SelfRecovery has a circular dependency with ActiveModeWarden and is 135 // instantiated after ActiveModeWarden, so use WifiInjector to get the instance 136 // instead of directly passing in SelfRecovery in the constructor. 137 mWifiInjector.getSelfRecovery().trigger(SelfRecovery.REASON_WIFINATIVE_FAILURE); 138 }); 139 } 140 }); 141 142 wifiNative.registerClientInterfaceAvailabilityListener( 143 (isAvailable) -> mHandler.post(() -> { 144 mCanRequestMoreClientModeManagers = isAvailable; 145 })); 146 wifiNative.registerSoftApInterfaceAvailabilityListener( 147 (isAvailable) -> mHandler.post(() -> { 148 mCanRequestMoreSoftApManagers = isAvailable; 149 })); 150 } 151 152 /** 153 * Notify that device is shutting down 154 * Keep it simple and don't add collection access codes 155 * to avoid concurrentModificationException when it is directly called from a different thread 156 */ notifyShuttingDown()157 public void notifyShuttingDown() { 158 mIsShuttingdown = true; 159 } 160 161 /** 162 * @return Returns whether we can create more client mode managers or not. 163 */ canRequestMoreClientModeManagers()164 public boolean canRequestMoreClientModeManagers() { 165 return mCanRequestMoreClientModeManagers; 166 } 167 168 /** 169 * @return Returns whether we can create more SoftAp managers or not. 170 */ canRequestMoreSoftApManagers()171 public boolean canRequestMoreSoftApManagers() { 172 return mCanRequestMoreSoftApManagers; 173 } 174 175 /** 176 * @return Returns whether the device can support at least one concurrent client mode manager & 177 * softap manager. 178 */ isStaApConcurrencySupported()179 public boolean isStaApConcurrencySupported() { 180 return mWifiNative.isStaApConcurrencySupported(); 181 } 182 183 /** Begin listening to broadcasts and start the internal state machine. */ start()184 public void start() { 185 mWifiController.start(); 186 } 187 188 /** Disable Wifi for recovery purposes. */ recoveryDisableWifi()189 public void recoveryDisableWifi() { 190 mWifiController.sendMessage(WifiController.CMD_RECOVERY_DISABLE_WIFI); 191 } 192 193 /** 194 * Restart Wifi for recovery purposes. 195 * @param reason One of {@link SelfRecovery.RecoveryReason} 196 */ recoveryRestartWifi(@elfRecovery.RecoveryReason int reason)197 public void recoveryRestartWifi(@SelfRecovery.RecoveryReason int reason) { 198 mWifiController.sendMessage(WifiController.CMD_RECOVERY_RESTART_WIFI, reason); 199 } 200 201 /** Wifi has been toggled. */ wifiToggled()202 public void wifiToggled() { 203 mWifiController.sendMessage(WifiController.CMD_WIFI_TOGGLED); 204 } 205 206 /** Airplane Mode has been toggled. */ airplaneModeToggled()207 public void airplaneModeToggled() { 208 mWifiController.sendMessage(WifiController.CMD_AIRPLANE_TOGGLED); 209 } 210 211 /** Starts SoftAp. */ startSoftAp(SoftApModeConfiguration softApConfig)212 public void startSoftAp(SoftApModeConfiguration softApConfig) { 213 mWifiController.sendMessage(WifiController.CMD_SET_AP, 1, 0, softApConfig); 214 } 215 216 /** Stop SoftAp. */ stopSoftAp(int mode)217 public void stopSoftAp(int mode) { 218 mWifiController.sendMessage(WifiController.CMD_SET_AP, 0, mode); 219 } 220 221 /** Update SoftAp Capability. */ updateSoftApCapability(SoftApCapability capability)222 public void updateSoftApCapability(SoftApCapability capability) { 223 mWifiController.sendMessage(WifiController.CMD_UPDATE_AP_CAPABILITY, capability); 224 } 225 226 /** Update SoftAp Configuration. */ updateSoftApConfiguration(SoftApConfiguration config)227 public void updateSoftApConfiguration(SoftApConfiguration config) { 228 mWifiController.sendMessage(WifiController.CMD_UPDATE_AP_CONFIG, config); 229 } 230 231 /** Emergency Callback Mode has changed. */ emergencyCallbackModeChanged(boolean isInEmergencyCallbackMode)232 public void emergencyCallbackModeChanged(boolean isInEmergencyCallbackMode) { 233 mWifiController.sendMessage( 234 WifiController.CMD_EMERGENCY_MODE_CHANGED, isInEmergencyCallbackMode ? 1 : 0); 235 } 236 237 /** Emergency Call state has changed. */ emergencyCallStateChanged(boolean isInEmergencyCall)238 public void emergencyCallStateChanged(boolean isInEmergencyCall) { 239 mWifiController.sendMessage( 240 WifiController.CMD_EMERGENCY_CALL_STATE_CHANGED, isInEmergencyCall ? 1 : 0); 241 } 242 243 /** Scan always mode has changed. */ scanAlwaysModeChanged()244 public void scanAlwaysModeChanged() { 245 mWifiController.sendMessage(WifiController.CMD_SCAN_ALWAYS_MODE_CHANGED); 246 } 247 hasAnyModeManager()248 private boolean hasAnyModeManager() { 249 return !mActiveModeManagers.isEmpty(); 250 } 251 252 /** 253 * @return true if any mode managers in specified role. 254 */ hasAnyModeManagerInRole(@ctiveModeManager.Role int role)255 private boolean hasAnyModeManagerInRole(@ActiveModeManager.Role int role) { 256 for (ActiveModeManager manager : mActiveModeManagers) { 257 if (manager.getRole() == role) return true; 258 } 259 return false; 260 } 261 262 /** 263 * @return true if any mode managers in one of the specified roles. 264 */ hasAnyModeManagerInOneOfRoles(List<Integer> rolesList)265 private boolean hasAnyModeManagerInOneOfRoles(List<Integer> rolesList) { 266 for (ActiveModeManager manager : mActiveModeManagers) { 267 if (rolesList.contains(manager.getRole())) return true; 268 } 269 return false; 270 } 271 hasAnyClientModeManager()272 private boolean hasAnyClientModeManager() { 273 return hasAnyModeManagerInOneOfRoles(ActiveModeManager.CLIENT_ROLES); 274 } 275 hasAnyClientModeManagerInConnectivityRole()276 private boolean hasAnyClientModeManagerInConnectivityRole() { 277 return hasAnyModeManagerInOneOfRoles(ActiveModeManager.CLIENT_CONNECTIVITY_ROLES); 278 } 279 hasAnySoftApManager()280 private boolean hasAnySoftApManager() { 281 return hasAnyModeManagerInOneOfRoles(ActiveModeManager.SOFTAP_ROLES); 282 } 283 284 /** 285 * @return true if any mode manager is stopping 286 */ hasAnyModeManagerStopping()287 private boolean hasAnyModeManagerStopping() { 288 for (ActiveModeManager manager : mActiveModeManagers) { 289 if (manager.isStopping()) return true; 290 } 291 return false; 292 } 293 294 /** 295 * @return true if all the client mode managers are in scan only role, 296 * false if there are no client mode managers present or if any of them are not in scan only 297 * role. 298 */ areAllClientModeManagersInScanOnlyRole()299 private boolean areAllClientModeManagersInScanOnlyRole() { 300 boolean hasAnyClientModeManager = false; 301 for (ActiveModeManager manager : mActiveModeManagers) { 302 if (ActiveModeManager.CLIENT_ROLES.contains(manager.getRole())) { 303 hasAnyClientModeManager = true; 304 if (manager.getRole() != ActiveModeManager.ROLE_CLIENT_SCAN_ONLY) return false; 305 } 306 } 307 return hasAnyClientModeManager; 308 } 309 getRoleForSoftApIpMode(int ipMode)310 private @ActiveModeManager.Role int getRoleForSoftApIpMode(int ipMode) { 311 return ipMode == IFACE_IP_MODE_TETHERED 312 ? ActiveModeManager.ROLE_SOFTAP_TETHERED : ActiveModeManager.ROLE_SOFTAP_LOCAL_ONLY; 313 } 314 315 /** 316 * Method to enable soft ap for wifi hotspot. 317 * 318 * The supplied SoftApModeConfiguration includes the target softap WifiConfiguration (or null if 319 * the persisted config is to be used) and the target operating mode (ex, 320 * {@link WifiManager#IFACE_IP_MODE_TETHERED} {@link WifiManager#IFACE_IP_MODE_LOCAL_ONLY}). 321 * 322 * @param softApConfig SoftApModeConfiguration for the hostapd softap 323 */ startSoftApModeManager(@onNull SoftApModeConfiguration softApConfig)324 private void startSoftApModeManager(@NonNull SoftApModeConfiguration softApConfig) { 325 Log.d(TAG, "Starting SoftApModeManager config = " 326 + softApConfig.getSoftApConfiguration()); 327 Preconditions.checkState(softApConfig.getTargetMode() == IFACE_IP_MODE_LOCAL_ONLY 328 || softApConfig.getTargetMode() == IFACE_IP_MODE_TETHERED); 329 330 WifiManager.SoftApCallback callback = 331 softApConfig.getTargetMode() == IFACE_IP_MODE_LOCAL_ONLY 332 ? mLohsCallback : mSoftApCallback; 333 SoftApListener listener = new SoftApListener(); 334 ActiveModeManager manager = 335 mWifiInjector.makeSoftApManager(listener, callback, softApConfig); 336 listener.setActiveModeManager(manager); 337 manager.start(); 338 manager.setRole(getRoleForSoftApIpMode(softApConfig.getTargetMode())); 339 mActiveModeManagers.add(manager); 340 } 341 342 /** 343 * Method to stop all soft ap for the specified mode. 344 * 345 * This method will stop any active softAp mode managers. 346 * 347 * @param ipMode the operating mode of APs to bring down (ex, 348 * {@link WifiManager#IFACE_IP_MODE_TETHERED} or 349 * {@link WifiManager#IFACE_IP_MODE_LOCAL_ONLY}). 350 * Use {@link WifiManager#IFACE_IP_MODE_UNSPECIFIED} to stop all APs. 351 */ stopSoftApModeManagers(int ipMode)352 private void stopSoftApModeManagers(int ipMode) { 353 Log.d(TAG, "Shutting down all softap mode managers in mode " + ipMode); 354 for (ActiveModeManager manager : mActiveModeManagers) { 355 if (!(manager instanceof SoftApManager)) continue; 356 SoftApManager softApManager = (SoftApManager) manager; 357 358 if (ipMode == WifiManager.IFACE_IP_MODE_UNSPECIFIED 359 || getRoleForSoftApIpMode(ipMode) == softApManager.getRole()) { 360 softApManager.stop(); 361 } 362 } 363 } 364 updateCapabilityToSoftApModeManager(SoftApCapability capability)365 private void updateCapabilityToSoftApModeManager(SoftApCapability capability) { 366 for (ActiveModeManager manager : mActiveModeManagers) { 367 if (!(manager instanceof SoftApManager)) continue; 368 SoftApManager softApManager = (SoftApManager) manager; 369 softApManager.updateCapability(capability); 370 } 371 } 372 updateConfigurationToSoftApModeManager(SoftApConfiguration config)373 private void updateConfigurationToSoftApModeManager(SoftApConfiguration config) { 374 for (ActiveModeManager manager : mActiveModeManagers) { 375 if (!(manager instanceof SoftApManager)) continue; 376 SoftApManager softApManager = (SoftApManager) manager; 377 softApManager.updateConfiguration(config); 378 } 379 } 380 381 /** 382 * Method to enable a new client mode manager. 383 */ startClientModeManager()384 private boolean startClientModeManager() { 385 Log.d(TAG, "Starting ClientModeManager"); 386 ClientListener listener = new ClientListener(); 387 ClientModeManager manager = mWifiInjector.makeClientModeManager(listener); 388 listener.setActiveModeManager(manager); 389 manager.start(); 390 if (!switchClientModeManagerRole(manager)) { 391 return false; 392 } 393 mActiveModeManagers.add(manager); 394 return true; 395 } 396 397 /** 398 * Method to stop all client mode mangers. 399 */ stopAllClientModeManagers()400 private void stopAllClientModeManagers() { 401 Log.d(TAG, "Shutting down all client mode managers"); 402 for (ActiveModeManager manager : mActiveModeManagers) { 403 if (!(manager instanceof ClientModeManager)) continue; 404 ClientModeManager clientModeManager = (ClientModeManager) manager; 405 clientModeManager.stop(); 406 } 407 } 408 409 /** 410 * Method to switch all client mode manager mode of operation (from ScanOnly To Connect & 411 * vice-versa) based on the toggle state. 412 */ switchAllClientModeManagers()413 private boolean switchAllClientModeManagers() { 414 Log.d(TAG, "Switching all client mode managers"); 415 for (ActiveModeManager manager : mActiveModeManagers) { 416 if (!(manager instanceof ClientModeManager)) continue; 417 ClientModeManager clientModeManager = (ClientModeManager) manager; 418 if (!switchClientModeManagerRole(clientModeManager)) { 419 return false; 420 } 421 } 422 updateBatteryStats(); 423 return true; 424 } 425 426 /** 427 * Method to switch a client mode manager mode of operation (from ScanOnly To Connect & 428 * vice-versa) based on the toggle state. 429 */ switchClientModeManagerRole(@onNull ClientModeManager modeManager)430 private boolean switchClientModeManagerRole(@NonNull ClientModeManager modeManager) { 431 if (mSettingsStore.isWifiToggleEnabled()) { 432 modeManager.setRole(ActiveModeManager.ROLE_CLIENT_PRIMARY); 433 } else if (checkScanOnlyModeAvailable()) { 434 modeManager.setRole(ActiveModeManager.ROLE_CLIENT_SCAN_ONLY); 435 } else { 436 Log.e(TAG, "Something is wrong, no client mode toggles enabled"); 437 return false; 438 } 439 return true; 440 } 441 442 /** 443 * Method to stop all active modes, for example, when toggling airplane mode. 444 */ shutdownWifi()445 private void shutdownWifi() { 446 Log.d(TAG, "Shutting down all mode managers"); 447 for (ActiveModeManager manager : mActiveModeManagers) { 448 manager.stop(); 449 } 450 } 451 452 /** 453 * Dump current state for active mode managers. 454 * 455 * Must be called from the main Wifi thread. 456 */ dump(FileDescriptor fd, PrintWriter pw, String[] args)457 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 458 pw.println("Dump of " + TAG); 459 pw.println("Current wifi mode: " + getCurrentMode()); 460 pw.println("NumActiveModeManagers: " + mActiveModeManagers.size()); 461 mWifiController.dump(fd, pw, args); 462 for (ActiveModeManager manager : mActiveModeManagers) { 463 manager.dump(fd, pw, args); 464 } 465 } 466 467 @VisibleForTesting getCurrentMode()468 String getCurrentMode() { 469 IState state = mWifiController.getCurrentState(); 470 return state == null ? STATE_MACHINE_EXITED_STATE_NAME : state.getName(); 471 } 472 473 @VisibleForTesting getActiveModeManagers()474 Collection<ActiveModeManager> getActiveModeManagers() { 475 return new ArraySet<>(mActiveModeManagers); 476 } 477 478 @VisibleForTesting isInEmergencyMode()479 boolean isInEmergencyMode() { 480 IState state = mWifiController.getCurrentState(); 481 return ((WifiController.BaseState) state).isInEmergencyMode(); 482 } 483 484 /** 485 * Helper class to wrap the ActiveModeManager callback objects. 486 */ 487 private static class ModeCallback { 488 private ActiveModeManager mActiveManager; 489 setActiveModeManager(ActiveModeManager manager)490 void setActiveModeManager(ActiveModeManager manager) { 491 mActiveManager = manager; 492 } 493 getActiveModeManager()494 ActiveModeManager getActiveModeManager() { 495 return mActiveManager; 496 } 497 } 498 updateBatteryStats()499 private void updateBatteryStats() { 500 updateBatteryStatsWifiState(hasAnyModeManager()); 501 if (areAllClientModeManagersInScanOnlyRole()) { 502 updateBatteryStatsScanModeActive(); 503 } 504 } 505 506 private class SoftApListener extends ModeCallback implements ActiveModeManager.Listener { 507 @Override onStarted()508 public void onStarted() { 509 updateBatteryStats(); 510 } 511 512 @Override onStopped()513 public void onStopped() { 514 mActiveModeManagers.remove(getActiveModeManager()); 515 updateBatteryStats(); 516 mWifiController.sendMessage(WifiController.CMD_AP_STOPPED); 517 } 518 519 @Override onStartFailure()520 public void onStartFailure() { 521 mActiveModeManagers.remove(getActiveModeManager()); 522 updateBatteryStats(); 523 mWifiController.sendMessage(WifiController.CMD_AP_START_FAILURE); 524 } 525 } 526 527 private class ClientListener extends ModeCallback implements ActiveModeManager.Listener { 528 @Override onStarted()529 public void onStarted() { 530 updateClientScanMode(); 531 updateBatteryStats(); 532 } 533 534 @Override onStopped()535 public void onStopped() { 536 mActiveModeManagers.remove(getActiveModeManager()); 537 updateClientScanMode(); 538 updateBatteryStats(); 539 mWifiController.sendMessage(WifiController.CMD_STA_STOPPED); 540 } 541 542 @Override onStartFailure()543 public void onStartFailure() { 544 mActiveModeManagers.remove(getActiveModeManager()); 545 updateClientScanMode(); 546 updateBatteryStats(); 547 mWifiController.sendMessage(WifiController.CMD_STA_START_FAILURE); 548 } 549 } 550 551 // Update the scan state based on all active mode managers. updateClientScanMode()552 private void updateClientScanMode() { 553 boolean scanEnabled = hasAnyClientModeManager(); 554 boolean scanningForHiddenNetworksEnabled; 555 556 if (mContext.getResources().getBoolean(R.bool.config_wifiScanHiddenNetworksScanOnlyMode)) { 557 scanningForHiddenNetworksEnabled = hasAnyClientModeManager(); 558 } else { 559 scanningForHiddenNetworksEnabled = hasAnyClientModeManagerInConnectivityRole(); 560 } 561 mScanRequestProxy.enableScanning(scanEnabled, scanningForHiddenNetworksEnabled); 562 } 563 564 /** 565 * Helper method to report wifi state as on/off (doesn't matter which mode). 566 * 567 * @param enabled boolean indicating that some mode has been turned on or off 568 */ updateBatteryStatsWifiState(boolean enabled)569 private void updateBatteryStatsWifiState(boolean enabled) { 570 if (enabled) { 571 if (mActiveModeManagers.size() == 1) { 572 // only report wifi on if we haven't already 573 mBatteryStatsManager.reportWifiOn(); 574 } 575 } else { 576 if (mActiveModeManagers.size() == 0) { 577 // only report if we don't have any active modes 578 mBatteryStatsManager.reportWifiOff(); 579 } 580 } 581 } 582 updateBatteryStatsScanModeActive()583 private void updateBatteryStatsScanModeActive() { 584 mBatteryStatsManager.reportWifiState(BatteryStatsManager.WIFI_STATE_OFF_SCANNING, null); 585 } 586 checkScanOnlyModeAvailable()587 private boolean checkScanOnlyModeAvailable() { 588 return mWifiPermissionsUtil.isLocationModeEnabled() 589 && mSettingsStore.isScanAlwaysAvailable(); 590 } 591 592 /** 593 * WifiController is the class used to manage wifi state for various operating 594 * modes (normal, airplane, wifi hotspot, etc.). 595 */ 596 private class WifiController extends StateMachine { 597 private static final String TAG = "WifiController"; 598 599 // Maximum limit to use for timeout delay if the value from overlay setting is too large. 600 private static final int MAX_RECOVERY_TIMEOUT_DELAY_MS = 4000; 601 602 private static final int BASE = Protocol.BASE_WIFI_CONTROLLER; 603 604 static final int CMD_EMERGENCY_MODE_CHANGED = BASE + 1; 605 static final int CMD_SCAN_ALWAYS_MODE_CHANGED = BASE + 7; 606 static final int CMD_WIFI_TOGGLED = BASE + 8; 607 static final int CMD_AIRPLANE_TOGGLED = BASE + 9; 608 static final int CMD_SET_AP = BASE + 10; 609 static final int CMD_EMERGENCY_CALL_STATE_CHANGED = BASE + 14; 610 static final int CMD_AP_STOPPED = BASE + 15; 611 static final int CMD_STA_START_FAILURE = BASE + 16; 612 // Command used to trigger a wifi stack restart when in active mode 613 static final int CMD_RECOVERY_RESTART_WIFI = BASE + 17; 614 // Internal command used to complete wifi stack restart 615 private static final int CMD_RECOVERY_RESTART_WIFI_CONTINUE = BASE + 18; 616 // Command to disable wifi when SelfRecovery is throttled or otherwise not doing full 617 // recovery 618 static final int CMD_RECOVERY_DISABLE_WIFI = BASE + 19; 619 static final int CMD_STA_STOPPED = BASE + 20; 620 static final int CMD_DEFERRED_RECOVERY_RESTART_WIFI = BASE + 22; 621 static final int CMD_AP_START_FAILURE = BASE + 23; 622 static final int CMD_UPDATE_AP_CAPABILITY = BASE + 24; 623 static final int CMD_UPDATE_AP_CONFIG = BASE + 25; 624 625 private final EnabledState mEnabledState = new EnabledState(); 626 private final DisabledState mDisabledState = new DisabledState(); 627 628 private boolean mIsInEmergencyCall = false; 629 private boolean mIsInEmergencyCallbackMode = false; 630 WifiController()631 WifiController() { 632 super(TAG, mLooper); 633 634 DefaultState defaultState = new DefaultState(); 635 addState(defaultState); { 636 addState(mDisabledState, defaultState); 637 addState(mEnabledState, defaultState); 638 } 639 640 setLogRecSize(100); 641 setLogOnlyTransitions(false); 642 643 } 644 645 @Override start()646 public void start() { 647 boolean isAirplaneModeOn = mSettingsStore.isAirplaneModeOn(); 648 boolean isWifiEnabled = mSettingsStore.isWifiToggleEnabled(); 649 boolean isScanningAlwaysAvailable = mSettingsStore.isScanAlwaysAvailable(); 650 boolean isLocationModeActive = mWifiPermissionsUtil.isLocationModeEnabled(); 651 652 log("isAirplaneModeOn = " + isAirplaneModeOn 653 + ", isWifiEnabled = " + isWifiEnabled 654 + ", isScanningAvailable = " + isScanningAlwaysAvailable 655 + ", isLocationModeActive = " + isLocationModeActive); 656 657 if (shouldEnableSta()) { 658 startClientModeManager(); 659 setInitialState(mEnabledState); 660 } else { 661 setInitialState(mDisabledState); 662 } 663 mContext.registerReceiver(new BroadcastReceiver() { 664 @Override 665 public void onReceive(Context context, Intent intent) { 666 // Location mode has been toggled... trigger with the scan change 667 // update to make sure we are in the correct mode 668 scanAlwaysModeChanged(); 669 } 670 }, new IntentFilter(LocationManager.MODE_CHANGED_ACTION)); 671 super.start(); 672 } 673 readWifiRecoveryDelay()674 private int readWifiRecoveryDelay() { 675 int recoveryDelayMillis = mContext.getResources().getInteger( 676 R.integer.config_wifi_framework_recovery_timeout_delay); 677 if (recoveryDelayMillis > MAX_RECOVERY_TIMEOUT_DELAY_MS) { 678 recoveryDelayMillis = MAX_RECOVERY_TIMEOUT_DELAY_MS; 679 Log.w(TAG, "Overriding timeout delay with maximum limit value"); 680 } 681 return recoveryDelayMillis; 682 } 683 684 abstract class BaseState extends State { 685 @VisibleForTesting isInEmergencyMode()686 boolean isInEmergencyMode() { 687 return mIsInEmergencyCall || mIsInEmergencyCallbackMode; 688 } 689 updateEmergencyMode(Message msg)690 private void updateEmergencyMode(Message msg) { 691 if (msg.what == CMD_EMERGENCY_CALL_STATE_CHANGED) { 692 mIsInEmergencyCall = msg.arg1 == 1; 693 } else if (msg.what == CMD_EMERGENCY_MODE_CHANGED) { 694 mIsInEmergencyCallbackMode = msg.arg1 == 1; 695 } 696 } 697 enterEmergencyMode()698 private void enterEmergencyMode() { 699 stopSoftApModeManagers(WifiManager.IFACE_IP_MODE_UNSPECIFIED); 700 boolean configWiFiDisableInECBM = mFacade.getConfigWiFiDisableInECBM(mContext); 701 log("WifiController msg getConfigWiFiDisableInECBM " + configWiFiDisableInECBM); 702 if (configWiFiDisableInECBM) { 703 shutdownWifi(); 704 } 705 } 706 exitEmergencyMode()707 private void exitEmergencyMode() { 708 if (shouldEnableSta()) { 709 startClientModeManager(); 710 transitionTo(mEnabledState); 711 } else { 712 transitionTo(mDisabledState); 713 } 714 } 715 716 @Override processMessage(Message msg)717 public final boolean processMessage(Message msg) { 718 // potentially enter emergency mode 719 if (msg.what == CMD_EMERGENCY_CALL_STATE_CHANGED 720 || msg.what == CMD_EMERGENCY_MODE_CHANGED) { 721 boolean wasInEmergencyMode = isInEmergencyMode(); 722 updateEmergencyMode(msg); 723 boolean isInEmergencyMode = isInEmergencyMode(); 724 if (!wasInEmergencyMode && isInEmergencyMode) { 725 enterEmergencyMode(); 726 } else if (wasInEmergencyMode && !isInEmergencyMode) { 727 exitEmergencyMode(); 728 } 729 return HANDLED; 730 } else if (isInEmergencyMode()) { 731 // already in emergency mode, drop all messages other than mode stop messages 732 // triggered by emergency mode start. 733 if (msg.what == CMD_STA_STOPPED || msg.what == CMD_AP_STOPPED) { 734 if (!hasAnyModeManager()) { 735 log("No active mode managers, return to DisabledState."); 736 transitionTo(mDisabledState); 737 } 738 } 739 return HANDLED; 740 } 741 // not in emergency mode, process messages normally 742 return processMessageFiltered(msg); 743 } 744 processMessageFiltered(Message msg)745 protected abstract boolean processMessageFiltered(Message msg); 746 } 747 748 class DefaultState extends State { 749 @Override processMessage(Message msg)750 public boolean processMessage(Message msg) { 751 switch (msg.what) { 752 case CMD_SCAN_ALWAYS_MODE_CHANGED: 753 case CMD_WIFI_TOGGLED: 754 case CMD_STA_STOPPED: 755 case CMD_STA_START_FAILURE: 756 case CMD_AP_STOPPED: 757 case CMD_AP_START_FAILURE: 758 case CMD_RECOVERY_RESTART_WIFI: 759 case CMD_RECOVERY_RESTART_WIFI_CONTINUE: 760 case CMD_DEFERRED_RECOVERY_RESTART_WIFI: 761 break; 762 case CMD_RECOVERY_DISABLE_WIFI: 763 log("Recovery has been throttled, disable wifi"); 764 shutdownWifi(); 765 // onStopped will move the state machine to "DisabledState". 766 break; 767 case CMD_AIRPLANE_TOGGLED: 768 if (mSettingsStore.isAirplaneModeOn()) { 769 log("Airplane mode toggled, shutdown all modes"); 770 shutdownWifi(); 771 // onStopped will move the state machine to "DisabledState". 772 } else { 773 log("Airplane mode disabled, determine next state"); 774 if (shouldEnableSta()) { 775 startClientModeManager(); 776 transitionTo(mEnabledState); 777 } 778 // wifi should remain disabled, do not need to transition 779 } 780 break; 781 case CMD_UPDATE_AP_CAPABILITY: 782 updateCapabilityToSoftApModeManager((SoftApCapability) msg.obj); 783 break; 784 case CMD_UPDATE_AP_CONFIG: 785 updateConfigurationToSoftApModeManager((SoftApConfiguration) msg.obj); 786 break; 787 default: 788 throw new RuntimeException("WifiController.handleMessage " + msg.what); 789 } 790 return HANDLED; 791 } 792 } 793 shouldEnableSta()794 private boolean shouldEnableSta() { 795 return mSettingsStore.isWifiToggleEnabled() || checkScanOnlyModeAvailable(); 796 } 797 798 class DisabledState extends BaseState { 799 @Override enter()800 public void enter() { 801 log("DisabledState.enter()"); 802 super.enter(); 803 if (hasAnyModeManager()) { 804 Log.e(TAG, "Entered DisabledState, but has active mode managers"); 805 } 806 } 807 808 @Override exit()809 public void exit() { 810 log("DisabledState.exit()"); 811 super.exit(); 812 } 813 814 @Override processMessageFiltered(Message msg)815 public boolean processMessageFiltered(Message msg) { 816 switch (msg.what) { 817 case CMD_WIFI_TOGGLED: 818 case CMD_SCAN_ALWAYS_MODE_CHANGED: 819 if (shouldEnableSta()) { 820 startClientModeManager(); 821 transitionTo(mEnabledState); 822 } 823 break; 824 case CMD_SET_AP: 825 // note: CMD_SET_AP is handled/dropped in ECM mode - will not start here 826 if (msg.arg1 == 1) { 827 startSoftApModeManager((SoftApModeConfiguration) msg.obj); 828 transitionTo(mEnabledState); 829 } 830 break; 831 case CMD_RECOVERY_RESTART_WIFI: 832 log("Recovery triggered, already in disabled state"); 833 // intentional fallthrough 834 case CMD_DEFERRED_RECOVERY_RESTART_WIFI: 835 // wait mRecoveryDelayMillis for letting driver clean reset. 836 sendMessageDelayed(CMD_RECOVERY_RESTART_WIFI_CONTINUE, 837 readWifiRecoveryDelay()); 838 break; 839 case CMD_RECOVERY_RESTART_WIFI_CONTINUE: 840 if (shouldEnableSta()) { 841 startClientModeManager(); 842 transitionTo(mEnabledState); 843 } 844 break; 845 default: 846 return NOT_HANDLED; 847 } 848 return HANDLED; 849 } 850 } 851 852 class EnabledState extends BaseState { 853 854 private boolean mIsDisablingDueToAirplaneMode; 855 856 @Override enter()857 public void enter() { 858 log("EnabledState.enter()"); 859 super.enter(); 860 if (!hasAnyModeManager()) { 861 Log.e(TAG, "Entered EnabledState, but no active mode managers"); 862 } 863 mIsDisablingDueToAirplaneMode = false; 864 } 865 866 @Override exit()867 public void exit() { 868 log("EnabledState.exit()"); 869 if (hasAnyModeManager()) { 870 Log.e(TAG, "Existing EnabledState, but has active mode managers"); 871 } 872 super.exit(); 873 } 874 875 @Override processMessageFiltered(Message msg)876 public boolean processMessageFiltered(Message msg) { 877 switch (msg.what) { 878 case CMD_WIFI_TOGGLED: 879 case CMD_SCAN_ALWAYS_MODE_CHANGED: 880 if (shouldEnableSta()) { 881 if (hasAnyClientModeManager()) { 882 switchAllClientModeManagers(); 883 } else { 884 startClientModeManager(); 885 } 886 } else { 887 stopAllClientModeManagers(); 888 } 889 break; 890 case CMD_SET_AP: 891 // note: CMD_SET_AP is handled/dropped in ECM mode - will not start here 892 if (msg.arg1 == 1) { 893 startSoftApModeManager((SoftApModeConfiguration) msg.obj); 894 } else { 895 stopSoftApModeManagers(msg.arg2); 896 } 897 break; 898 case CMD_AIRPLANE_TOGGLED: 899 // airplane mode toggled on is handled in the default state 900 if (mSettingsStore.isAirplaneModeOn()) { 901 mIsDisablingDueToAirplaneMode = true; 902 return NOT_HANDLED; 903 } else { 904 if (mIsDisablingDueToAirplaneMode) { 905 // Previous airplane mode toggle on is being processed, defer the 906 // message toggle off until previous processing is completed. 907 // Once previous airplane mode toggle is complete, we should 908 // transition to DisabledState. There, we will process the deferred 909 // airplane mode toggle message to disable airplane mode. 910 deferMessage(msg); 911 } else { 912 // when airplane mode is toggled off, but wifi is on, we can keep it 913 // on 914 log("airplane mode toggled - and airplane mode is off. return " 915 + "handled"); 916 } 917 return HANDLED; 918 } 919 case CMD_AP_STOPPED: 920 case CMD_AP_START_FAILURE: 921 if (!hasAnyModeManager()) { 922 if (shouldEnableSta()) { 923 log("SoftAp disabled, start client mode"); 924 startClientModeManager(); 925 } else { 926 log("SoftAp mode disabled, return to DisabledState"); 927 transitionTo(mDisabledState); 928 } 929 } else { 930 log("AP disabled, remain in EnabledState."); 931 } 932 break; 933 case CMD_STA_START_FAILURE: 934 case CMD_STA_STOPPED: 935 // Client mode stopped. Head to Disabled to wait for next command if there 936 // no active mode managers. 937 if (!hasAnyModeManager()) { 938 log("STA disabled, return to DisabledState."); 939 transitionTo(mDisabledState); 940 } else { 941 log("STA disabled, remain in EnabledState."); 942 } 943 break; 944 case CMD_RECOVERY_RESTART_WIFI: 945 final String bugTitle; 946 final String bugDetail; 947 if (msg.arg1 < SelfRecovery.REASON_STRINGS.length && msg.arg1 >= 0) { 948 bugDetail = SelfRecovery.REASON_STRINGS[msg.arg1]; 949 bugTitle = "Wi-Fi BugReport: " + bugDetail; 950 } else { 951 bugDetail = ""; 952 bugTitle = "Wi-Fi BugReport"; 953 } 954 if (msg.arg1 != SelfRecovery.REASON_LAST_RESORT_WATCHDOG) { 955 mHandler.post(() -> mClientModeImpl.takeBugReport(bugTitle, bugDetail)); 956 } 957 log("Recovery triggered, disable wifi"); 958 deferMessage(obtainMessage(CMD_DEFERRED_RECOVERY_RESTART_WIFI)); 959 shutdownWifi(); 960 // onStopped will move the state machine to "DisabledState". 961 break; 962 default: 963 return NOT_HANDLED; 964 } 965 return HANDLED; 966 } 967 } 968 } 969 } 970