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 static com.android.server.wifi.ActiveModeManager.ROLE_CLIENT_LOCAL_ONLY; 23 import static com.android.server.wifi.ActiveModeManager.ROLE_CLIENT_PRIMARY; 24 import static com.android.server.wifi.ActiveModeManager.ROLE_CLIENT_SCAN_ONLY; 25 import static com.android.server.wifi.ActiveModeManager.ROLE_CLIENT_SECONDARY_LONG_LIVED; 26 import static com.android.server.wifi.ActiveModeManager.ROLE_CLIENT_SECONDARY_TRANSIENT; 27 import static com.android.server.wifi.ActiveModeManager.ROLE_SOFTAP_TETHERED; 28 29 import android.annotation.NonNull; 30 import android.annotation.Nullable; 31 import android.content.BroadcastReceiver; 32 import android.content.Context; 33 import android.content.Intent; 34 import android.content.IntentFilter; 35 import android.location.LocationManager; 36 import android.net.wifi.ISubsystemRestartCallback; 37 import android.net.wifi.IWifiConnectedNetworkScorer; 38 import android.net.wifi.SoftApCapability; 39 import android.net.wifi.SoftApConfiguration; 40 import android.net.wifi.WifiConfiguration; 41 import android.net.wifi.WifiManager; 42 import android.os.BatteryStatsManager; 43 import android.os.Build; 44 import android.os.Handler; 45 import android.os.IBinder; 46 import android.os.Looper; 47 import android.os.Message; 48 import android.os.Process; 49 import android.os.RemoteCallbackList; 50 import android.os.RemoteException; 51 import android.os.WorkSource; 52 import android.telephony.TelephonyManager; 53 import android.text.TextUtils; 54 import android.util.ArraySet; 55 import android.util.Log; 56 import android.util.Pair; 57 58 import com.android.internal.annotations.VisibleForTesting; 59 import com.android.internal.util.IState; 60 import com.android.internal.util.Preconditions; 61 import com.android.internal.util.Protocol; 62 import com.android.internal.util.State; 63 import com.android.internal.util.StateMachine; 64 import com.android.server.wifi.ActiveModeManager.ClientConnectivityRole; 65 import com.android.server.wifi.ActiveModeManager.ClientInternetConnectivityRole; 66 import com.android.server.wifi.ActiveModeManager.ClientRole; 67 import com.android.server.wifi.ActiveModeManager.SoftApRole; 68 import com.android.server.wifi.util.WifiPermissionsUtil; 69 import com.android.wifi.resources.R; 70 71 import java.io.FileDescriptor; 72 import java.io.PrintWriter; 73 import java.util.ArrayDeque; 74 import java.util.ArrayList; 75 import java.util.Collection; 76 import java.util.Collections; 77 import java.util.List; 78 import java.util.Objects; 79 import java.util.Set; 80 import java.util.concurrent.Executor; 81 import java.util.stream.Collectors; 82 import java.util.stream.Stream; 83 84 /** 85 * This class provides the implementation for different WiFi operating modes. 86 */ 87 public class ActiveModeWarden { 88 private static final String TAG = "WifiActiveModeWarden"; 89 private static final String STATE_MACHINE_EXITED_STATE_NAME = "STATE_MACHINE_EXITED"; 90 public static final WorkSource INTERNAL_REQUESTOR_WS = new WorkSource(Process.WIFI_UID); 91 92 // Holder for active mode managers 93 private final Set<ConcreteClientModeManager> mClientModeManagers = new ArraySet<>(); 94 private final Set<SoftApManager> mSoftApManagers = new ArraySet<>(); 95 96 private final Set<ModeChangeCallback> mCallbacks = new ArraySet<>(); 97 private final Set<PrimaryClientModeManagerChangedCallback> mPrimaryChangedCallbacks = 98 new ArraySet<>(); 99 // DefaultModeManager used to service API calls when there are no active client mode managers. 100 private final DefaultClientModeManager mDefaultClientModeManager; 101 private final WifiInjector mWifiInjector; 102 private final Looper mLooper; 103 private final Handler mHandler; 104 private final Context mContext; 105 private final WifiDiagnostics mWifiDiagnostics; 106 private final WifiSettingsStore mSettingsStore; 107 private final FrameworkFacade mFacade; 108 private final WifiPermissionsUtil mWifiPermissionsUtil; 109 private final BatteryStatsManager mBatteryStatsManager; 110 private final ScanRequestProxy mScanRequestProxy; 111 private final WifiNative mWifiNative; 112 private final WifiController mWifiController; 113 private final Graveyard mGraveyard; 114 private final WifiMetrics mWifiMetrics; 115 private final ExternalScoreUpdateObserverProxy mExternalScoreUpdateObserverProxy; 116 private final DppManager mDppManager; 117 118 private WifiServiceImpl.SoftApCallbackInternal mSoftApCallback; 119 private WifiServiceImpl.SoftApCallbackInternal mLohsCallback; 120 121 private final RemoteCallbackList<ISubsystemRestartCallback> mRestartCallbacks = 122 new RemoteCallbackList<>(); 123 124 private boolean mIsShuttingdown = false; 125 private boolean mVerboseLoggingEnabled = false; 126 /** Cache to store the external scorer for primary and secondary (MBB) client mode manager. */ 127 @Nullable private Pair<IBinder, IWifiConnectedNetworkScorer> mClientModeManagerScorer; 128 129 @Nullable 130 private ConcreteClientModeManager mLastPrimaryClientModeManager = null; 131 132 @Nullable 133 private WorkSource mLastPrimaryClientModeManagerRequestorWs = null; 134 @Nullable 135 private WorkSource mLastScanOnlyClientModeManagerRequestorWs = null; 136 137 /** 138 * Called from WifiServiceImpl to register a callback for notifications from SoftApManager 139 */ registerSoftApCallback(@onNull WifiServiceImpl.SoftApCallbackInternal callback)140 public void registerSoftApCallback(@NonNull WifiServiceImpl.SoftApCallbackInternal callback) { 141 mSoftApCallback = callback; 142 } 143 144 /** 145 * Called from WifiServiceImpl to register a callback for notifications from SoftApManager 146 * for local-only hotspot. 147 */ registerLohsCallback(@onNull WifiServiceImpl.SoftApCallbackInternal callback)148 public void registerLohsCallback(@NonNull WifiServiceImpl.SoftApCallbackInternal callback) { 149 mLohsCallback = callback; 150 } 151 152 /** 153 * Callbacks for indicating any mode manager changes to the rest of the system. 154 */ 155 public interface ModeChangeCallback { 156 /** 157 * Invoked when new mode manager is added. 158 * 159 * @param activeModeManager Instance of {@link ActiveModeManager}. 160 */ onActiveModeManagerAdded(@onNull ActiveModeManager activeModeManager)161 void onActiveModeManagerAdded(@NonNull ActiveModeManager activeModeManager); 162 163 /** 164 * Invoked when a mode manager is removed. 165 * 166 * @param activeModeManager Instance of {@link ActiveModeManager}. 167 */ onActiveModeManagerRemoved(@onNull ActiveModeManager activeModeManager)168 void onActiveModeManagerRemoved(@NonNull ActiveModeManager activeModeManager); 169 170 /** 171 * Invoked when an existing mode manager's role is changed. 172 * 173 * @param activeModeManager Instance of {@link ActiveModeManager}. 174 */ onActiveModeManagerRoleChanged(@onNull ActiveModeManager activeModeManager)175 void onActiveModeManagerRoleChanged(@NonNull ActiveModeManager activeModeManager); 176 } 177 178 /** Called when the primary ClientModeManager changes. */ 179 public interface PrimaryClientModeManagerChangedCallback { 180 /** 181 * Note: The current implementation for changing primary CMM is not atomic (due to setRole() 182 * needing to go through StateMachine, which is async). Thus, when the primary CMM changes, 183 * the sequence of calls looks like this: 184 * 1. onChange(prevPrimaryCmm, null) 185 * 2. onChange(null, newPrimaryCmm) 186 * Nevertheless, at run time, these two calls should occur in rapid succession. 187 * 188 * @param prevPrimaryClientModeManager the previous primary ClientModeManager, or null if 189 * there was no previous primary (e.g. Wifi was off). 190 * @param newPrimaryClientModeManager the new primary ClientModeManager, or null if there is 191 * no longer a primary (e.g. Wifi was turned off). 192 */ onChange( @ullable ConcreteClientModeManager prevPrimaryClientModeManager, @Nullable ConcreteClientModeManager newPrimaryClientModeManager)193 void onChange( 194 @Nullable ConcreteClientModeManager prevPrimaryClientModeManager, 195 @Nullable ConcreteClientModeManager newPrimaryClientModeManager); 196 } 197 198 /** 199 * Keep stopped {@link ActiveModeManager} instances so that they can be dumped to aid debugging. 200 * 201 * TODO(b/160283853): Find a smarter way to evict old ActiveModeManagers 202 */ 203 private static class Graveyard { 204 private static final int INSTANCES_TO_KEEP = 3; 205 206 private final ArrayDeque<ConcreteClientModeManager> mClientModeManagers = 207 new ArrayDeque<>(); 208 private final ArrayDeque<SoftApManager> mSoftApManagers = new ArrayDeque<>(); 209 210 /** 211 * Add this stopped {@link ConcreteClientModeManager} to the graveyard, and evict the oldest 212 * ClientModeManager if the graveyard is full. 213 */ inter(ConcreteClientModeManager clientModeManager)214 void inter(ConcreteClientModeManager clientModeManager) { 215 if (mClientModeManagers.size() == INSTANCES_TO_KEEP) { 216 mClientModeManagers.removeFirst(); 217 } 218 mClientModeManagers.addLast(clientModeManager); 219 } 220 221 /** 222 * Add this stopped {@link SoftApManager} to the graveyard, and evict the oldest 223 * SoftApManager if the graveyard is full. 224 */ inter(SoftApManager softApManager)225 void inter(SoftApManager softApManager) { 226 if (mSoftApManagers.size() == INSTANCES_TO_KEEP) { 227 mSoftApManagers.removeFirst(); 228 } 229 mSoftApManagers.addLast(softApManager); 230 } 231 232 /** Dump the contents of the graveyard. */ dump(FileDescriptor fd, PrintWriter pw, String[] args)233 void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 234 pw.println("Dump of ActiveModeWarden.Graveyard"); 235 pw.println("Stopped ClientModeManagers: " + mClientModeManagers.size() + " total"); 236 for (ConcreteClientModeManager clientModeManager : mClientModeManagers) { 237 clientModeManager.dump(fd, pw, args); 238 } 239 pw.println("Stopped SoftApManagers: " + mSoftApManagers.size() + " total"); 240 for (SoftApManager softApManager : mSoftApManagers) { 241 softApManager.dump(fd, pw, args); 242 } 243 pw.println(); 244 } 245 } 246 ActiveModeWarden(WifiInjector wifiInjector, Looper looper, WifiNative wifiNative, DefaultClientModeManager defaultClientModeManager, BatteryStatsManager batteryStatsManager, WifiDiagnostics wifiDiagnostics, Context context, WifiSettingsStore settingsStore, FrameworkFacade facade, WifiPermissionsUtil wifiPermissionsUtil, WifiMetrics wifiMetrics, ExternalScoreUpdateObserverProxy externalScoreUpdateObserverProxy, DppManager dppManager)247 ActiveModeWarden(WifiInjector wifiInjector, 248 Looper looper, 249 WifiNative wifiNative, 250 DefaultClientModeManager defaultClientModeManager, 251 BatteryStatsManager batteryStatsManager, 252 WifiDiagnostics wifiDiagnostics, 253 Context context, 254 WifiSettingsStore settingsStore, 255 FrameworkFacade facade, 256 WifiPermissionsUtil wifiPermissionsUtil, 257 WifiMetrics wifiMetrics, 258 ExternalScoreUpdateObserverProxy externalScoreUpdateObserverProxy, 259 DppManager dppManager) { 260 mWifiInjector = wifiInjector; 261 mLooper = looper; 262 mHandler = new Handler(looper); 263 mContext = context; 264 mWifiDiagnostics = wifiDiagnostics; 265 mSettingsStore = settingsStore; 266 mFacade = facade; 267 mWifiPermissionsUtil = wifiPermissionsUtil; 268 mDefaultClientModeManager = defaultClientModeManager; 269 mBatteryStatsManager = batteryStatsManager; 270 mScanRequestProxy = wifiInjector.getScanRequestProxy(); 271 mWifiNative = wifiNative; 272 mWifiMetrics = wifiMetrics; 273 mWifiController = new WifiController(); 274 mExternalScoreUpdateObserverProxy = externalScoreUpdateObserverProxy; 275 mDppManager = dppManager; 276 mGraveyard = new Graveyard(); 277 278 wifiNative.registerStatusListener(isReady -> { 279 if (!isReady && !mIsShuttingdown) { 280 mHandler.post(() -> { 281 Log.e(TAG, "One of the native daemons died. Triggering recovery"); 282 wifiDiagnostics.triggerBugReportDataCapture( 283 WifiDiagnostics.REPORT_REASON_WIFINATIVE_FAILURE); 284 285 // immediately trigger SelfRecovery if we receive a notice about an 286 // underlying daemon failure 287 // Note: SelfRecovery has a circular dependency with ActiveModeWarden and is 288 // instantiated after ActiveModeWarden, so use WifiInjector to get the instance 289 // instead of directly passing in SelfRecovery in the constructor. 290 mWifiInjector.getSelfRecovery().trigger(SelfRecovery.REASON_WIFINATIVE_FAILURE); 291 }); 292 } 293 }); 294 295 registerPrimaryClientModeManagerChangedCallback( 296 (prevPrimaryClientModeManager, newPrimaryClientModeManager) -> { 297 // TODO (b/181363901): We can always propagate the external scorer to all 298 // ClientModeImpl instances. WifiScoreReport already handles skipping external 299 // scorer notification for local only & restricted STA + STA use-cases. For MBB 300 // use-case, we may want the external scorer to be notified. 301 if (prevPrimaryClientModeManager != null) { 302 prevPrimaryClientModeManager.clearWifiConnectedNetworkScorer(); 303 } 304 if (newPrimaryClientModeManager != null && mClientModeManagerScorer != null) { 305 newPrimaryClientModeManager.setWifiConnectedNetworkScorer( 306 mClientModeManagerScorer.first, 307 mClientModeManagerScorer.second); 308 } 309 }); 310 } 311 invokeOnAddedCallbacks(@onNull ActiveModeManager activeModeManager)312 private void invokeOnAddedCallbacks(@NonNull ActiveModeManager activeModeManager) { 313 if (mVerboseLoggingEnabled) { 314 Log.v(TAG, "ModeManager added " + activeModeManager); 315 } 316 for (ModeChangeCallback callback : mCallbacks) { 317 callback.onActiveModeManagerAdded(activeModeManager); 318 } 319 } 320 invokeOnRemovedCallbacks(@onNull ActiveModeManager activeModeManager)321 private void invokeOnRemovedCallbacks(@NonNull ActiveModeManager activeModeManager) { 322 if (mVerboseLoggingEnabled) { 323 Log.v(TAG, "ModeManager removed " + activeModeManager); 324 } 325 for (ModeChangeCallback callback : mCallbacks) { 326 callback.onActiveModeManagerRemoved(activeModeManager); 327 } 328 } 329 invokeOnRoleChangedCallbacks(@onNull ActiveModeManager activeModeManager)330 private void invokeOnRoleChangedCallbacks(@NonNull ActiveModeManager activeModeManager) { 331 if (mVerboseLoggingEnabled) { 332 Log.v(TAG, "ModeManager role changed " + activeModeManager); 333 } 334 for (ModeChangeCallback callback : mCallbacks) { 335 callback.onActiveModeManagerRoleChanged(activeModeManager); 336 } 337 } 338 invokeOnPrimaryClientModeManagerChangedCallbacks( @ullable ConcreteClientModeManager prevPrimaryClientModeManager, @Nullable ConcreteClientModeManager newPrimaryClientModeManager)339 private void invokeOnPrimaryClientModeManagerChangedCallbacks( 340 @Nullable ConcreteClientModeManager prevPrimaryClientModeManager, 341 @Nullable ConcreteClientModeManager newPrimaryClientModeManager) { 342 if (mVerboseLoggingEnabled) { 343 Log.v(TAG, "Primary ClientModeManager changed from " + prevPrimaryClientModeManager 344 + " to " + newPrimaryClientModeManager); 345 } 346 for (PrimaryClientModeManagerChangedCallback callback : mPrimaryChangedCallbacks) { 347 callback.onChange(prevPrimaryClientModeManager, newPrimaryClientModeManager); 348 } 349 } 350 351 /** 352 * Enable verbose logging. 353 */ enableVerboseLogging(boolean verbose)354 public void enableVerboseLogging(boolean verbose) { 355 mVerboseLoggingEnabled = verbose; 356 for (ActiveModeManager modeManager : getActiveModeManagers()) { 357 modeManager.enableVerboseLogging(verbose); 358 } 359 } 360 361 /** 362 * See {@link android.net.wifi.WifiManager#setWifiConnectedNetworkScorer(Executor, 363 * WifiManager.WifiConnectedNetworkScorer)} 364 */ setWifiConnectedNetworkScorer(IBinder binder, IWifiConnectedNetworkScorer scorer)365 public boolean setWifiConnectedNetworkScorer(IBinder binder, 366 IWifiConnectedNetworkScorer scorer) { 367 try { 368 scorer.onSetScoreUpdateObserver(mExternalScoreUpdateObserverProxy); 369 } catch (RemoteException e) { 370 Log.e(TAG, "Unable to set score update observer " + scorer, e); 371 return false; 372 } 373 mClientModeManagerScorer = Pair.create(binder, scorer); 374 return getPrimaryClientModeManager().setWifiConnectedNetworkScorer(binder, scorer); 375 } 376 377 /** 378 * See {@link WifiManager#clearWifiConnectedNetworkScorer()} 379 */ clearWifiConnectedNetworkScorer()380 public void clearWifiConnectedNetworkScorer() { 381 mClientModeManagerScorer = null; 382 getPrimaryClientModeManager().clearWifiConnectedNetworkScorer(); 383 } 384 385 /** 386 * Register for mode change callbacks. 387 */ registerModeChangeCallback(@onNull ModeChangeCallback callback)388 public void registerModeChangeCallback(@NonNull ModeChangeCallback callback) { 389 mCallbacks.add(Objects.requireNonNull(callback)); 390 } 391 392 /** 393 * Unregister mode change callback. 394 */ unregisterModeChangeCallback(@onNull ModeChangeCallback callback)395 public void unregisterModeChangeCallback(@NonNull ModeChangeCallback callback) { 396 mCallbacks.remove(Objects.requireNonNull(callback)); 397 } 398 399 /** Register for primary ClientModeManager changed callbacks. */ registerPrimaryClientModeManagerChangedCallback( @onNull PrimaryClientModeManagerChangedCallback callback)400 public void registerPrimaryClientModeManagerChangedCallback( 401 @NonNull PrimaryClientModeManagerChangedCallback callback) { 402 mPrimaryChangedCallbacks.add(Objects.requireNonNull(callback)); 403 // If there is already a primary CMM when registering, send a callback with the info. 404 ConcreteClientModeManager cm = getPrimaryClientModeManagerNullable(); 405 if (cm != null) callback.onChange(null, cm); 406 } 407 408 /** Unregister for primary ClientModeManager changed callbacks. */ unregisterPrimaryClientModeManagerChangedCallback( @onNull PrimaryClientModeManagerChangedCallback callback)409 public void unregisterPrimaryClientModeManagerChangedCallback( 410 @NonNull PrimaryClientModeManagerChangedCallback callback) { 411 mPrimaryChangedCallbacks.remove(Objects.requireNonNull(callback)); 412 } 413 414 /** 415 * Notify that device is shutting down 416 * Keep it simple and don't add collection access codes 417 * to avoid concurrentModificationException when it is directly called from a different thread 418 */ notifyShuttingDown()419 public void notifyShuttingDown() { 420 mIsShuttingdown = true; 421 } 422 423 /** 424 * @return Returns whether we can create more client mode managers or not. 425 */ canRequestMoreClientModeManagersInRole(@onNull WorkSource requestorWs, @NonNull ClientRole clientRole)426 public boolean canRequestMoreClientModeManagersInRole(@NonNull WorkSource requestorWs, 427 @NonNull ClientRole clientRole) { 428 if (!mWifiNative.isItPossibleToCreateStaIface(requestorWs)) { 429 return false; 430 } 431 if (clientRole == ROLE_CLIENT_LOCAL_ONLY) { 432 if (!mContext.getResources().getBoolean( 433 R.bool.config_wifiMultiStaLocalOnlyConcurrencyEnabled)) { 434 return false; 435 } 436 final int uid = requestorWs.getUid(0); 437 final String packageName = requestorWs.getPackageName(0); 438 // For peer to peer use-case, only allow secondary STA if the app is targeting S SDK 439 // or is a system app to provide backward compatibility. 440 return mWifiPermissionsUtil.isSystem(packageName, uid) 441 || !mWifiPermissionsUtil.isTargetSdkLessThan( 442 packageName, Build.VERSION_CODES.S, uid); 443 } 444 if (clientRole == ROLE_CLIENT_SECONDARY_TRANSIENT) { 445 return mContext.getResources().getBoolean( 446 R.bool.config_wifiMultiStaNetworkSwitchingMakeBeforeBreakEnabled); 447 } 448 if (clientRole == ROLE_CLIENT_SECONDARY_LONG_LIVED) { 449 return mContext.getResources().getBoolean( 450 R.bool.config_wifiMultiStaRestrictedConcurrencyEnabled); 451 } 452 Log.e(TAG, "Unrecognized role=" + clientRole); 453 return false; 454 } 455 456 /** 457 * @return Returns whether we can create more SoftAp managers or not. 458 */ canRequestMoreSoftApManagers(@onNull WorkSource requestorWs)459 public boolean canRequestMoreSoftApManagers(@NonNull WorkSource requestorWs) { 460 return mWifiNative.isItPossibleToCreateApIface(requestorWs); 461 } 462 463 /** 464 * @return Returns whether the device can support at least one concurrent client mode manager & 465 * softap manager. 466 */ isStaApConcurrencySupported()467 public boolean isStaApConcurrencySupported() { 468 return mWifiNative.isStaApConcurrencySupported(); 469 } 470 471 /** 472 * @return Returns whether the device can support at least two concurrent client mode managers 473 * and the local only use-case is enabled. 474 */ isStaStaConcurrencySupportedForLocalOnlyConnections()475 public boolean isStaStaConcurrencySupportedForLocalOnlyConnections() { 476 return mWifiNative.isStaStaConcurrencySupported() 477 && mContext.getResources().getBoolean( 478 R.bool.config_wifiMultiStaLocalOnlyConcurrencyEnabled); 479 } 480 481 /** 482 * @return Returns whether the device can support at least two concurrent client mode managers 483 * and the mbb wifi switching is enabled. 484 */ isStaStaConcurrencySupportedForMbb()485 public boolean isStaStaConcurrencySupportedForMbb() { 486 return mWifiNative.isStaStaConcurrencySupported() 487 && mContext.getResources().getBoolean( 488 R.bool.config_wifiMultiStaNetworkSwitchingMakeBeforeBreakEnabled); 489 } 490 491 /** 492 * @return Returns whether the device can support at least two concurrent client mode managers 493 * and the restricted use-case is enabled. 494 */ isStaStaConcurrencySupportedForRestrictedConnections()495 public boolean isStaStaConcurrencySupportedForRestrictedConnections() { 496 return mWifiNative.isStaStaConcurrencySupported() 497 && mContext.getResources().getBoolean( 498 R.bool.config_wifiMultiStaRestrictedConcurrencyEnabled); 499 } 500 501 /** Begin listening to broadcasts and start the internal state machine. */ start()502 public void start() { 503 mContext.registerReceiver(new BroadcastReceiver() { 504 @Override 505 public void onReceive(Context context, Intent intent) { 506 // Location mode has been toggled... trigger with the scan change 507 // update to make sure we are in the correct mode 508 scanAlwaysModeChanged(); 509 } 510 }, new IntentFilter(LocationManager.MODE_CHANGED_ACTION)); 511 mContext.registerReceiver(new BroadcastReceiver() { 512 @Override 513 public void onReceive(Context context, Intent intent) { 514 if (mSettingsStore.handleAirplaneModeToggled()) { 515 airplaneModeToggled(); 516 } 517 } 518 }, new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED)); 519 mContext.registerReceiver(new BroadcastReceiver() { 520 @Override 521 public void onReceive(Context context, Intent intent) { 522 boolean emergencyMode = 523 intent.getBooleanExtra(TelephonyManager.EXTRA_PHONE_IN_ECM_STATE, false); 524 emergencyCallbackModeChanged(emergencyMode); 525 } 526 }, new IntentFilter(TelephonyManager.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED)); 527 boolean trackEmergencyCallState = mContext.getResources().getBoolean( 528 R.bool.config_wifi_turn_off_during_emergency_call); 529 if (trackEmergencyCallState) { 530 mContext.registerReceiver(new BroadcastReceiver() { 531 @Override 532 public void onReceive(Context context, Intent intent) { 533 boolean inCall = intent.getBooleanExtra( 534 TelephonyManager.EXTRA_PHONE_IN_EMERGENCY_CALL, false); 535 emergencyCallStateChanged(inCall); 536 } 537 }, new IntentFilter(TelephonyManager.ACTION_EMERGENCY_CALL_STATE_CHANGED)); 538 } 539 540 mWifiController.start(); 541 } 542 543 /** Disable Wifi for recovery purposes. */ recoveryDisableWifi()544 public void recoveryDisableWifi() { 545 mWifiController.sendMessage(WifiController.CMD_RECOVERY_DISABLE_WIFI); 546 } 547 548 /** 549 * Restart Wifi for recovery purposes. 550 * @param reason One of {@link SelfRecovery.RecoveryReason} 551 */ recoveryRestartWifi(@elfRecovery.RecoveryReason int reason, @Nullable String reasonDetail, boolean requestBugReport)552 public void recoveryRestartWifi(@SelfRecovery.RecoveryReason int reason, 553 @Nullable String reasonDetail, boolean requestBugReport) { 554 mWifiController.sendMessage(WifiController.CMD_RECOVERY_RESTART_WIFI, reason, 555 requestBugReport ? 1 : 0, reasonDetail); 556 } 557 558 /** 559 * register a callback to monitor the progress of Wi-Fi subsystem operation (started/finished) 560 * - started via {@link #recoveryRestartWifi(int, String, boolean)}. 561 */ registerSubsystemRestartCallback(ISubsystemRestartCallback callback)562 public boolean registerSubsystemRestartCallback(ISubsystemRestartCallback callback) { 563 return mRestartCallbacks.register(callback); 564 } 565 566 /** 567 * unregister a callback to monitor the progress of Wi-Fi subsystem operation (started/finished) 568 * - started via {@link #recoveryRestartWifi(int, String, boolean)}. Callback is registered via 569 * {@link #registerSubsystemRestartCallback(ISubsystemRestartCallback)}. 570 */ unregisterSubsystemRestartCallback(ISubsystemRestartCallback callback)571 public boolean unregisterSubsystemRestartCallback(ISubsystemRestartCallback callback) { 572 return mRestartCallbacks.unregister(callback); 573 } 574 575 /** Wifi has been toggled. */ wifiToggled(WorkSource requestorWs)576 public void wifiToggled(WorkSource requestorWs) { 577 mWifiController.sendMessage(WifiController.CMD_WIFI_TOGGLED, requestorWs); 578 } 579 580 /** Airplane Mode has been toggled. */ airplaneModeToggled()581 public void airplaneModeToggled() { 582 mWifiController.sendMessage(WifiController.CMD_AIRPLANE_TOGGLED); 583 } 584 585 /** Starts SoftAp. */ startSoftAp(SoftApModeConfiguration softApConfig, WorkSource requestorWs)586 public void startSoftAp(SoftApModeConfiguration softApConfig, WorkSource requestorWs) { 587 mWifiController.sendMessage(WifiController.CMD_SET_AP, 1, 0, 588 Pair.create(softApConfig, requestorWs)); 589 } 590 591 /** Stop SoftAp. */ stopSoftAp(int mode)592 public void stopSoftAp(int mode) { 593 mWifiController.sendMessage(WifiController.CMD_SET_AP, 0, mode); 594 } 595 596 /** Update SoftAp Capability. */ updateSoftApCapability(SoftApCapability capability)597 public void updateSoftApCapability(SoftApCapability capability) { 598 mWifiController.sendMessage(WifiController.CMD_UPDATE_AP_CAPABILITY, capability); 599 } 600 601 /** Update SoftAp Configuration. */ updateSoftApConfiguration(SoftApConfiguration config)602 public void updateSoftApConfiguration(SoftApConfiguration config) { 603 mWifiController.sendMessage(WifiController.CMD_UPDATE_AP_CONFIG, config); 604 } 605 606 /** Emergency Callback Mode has changed. */ emergencyCallbackModeChanged(boolean isInEmergencyCallbackMode)607 public void emergencyCallbackModeChanged(boolean isInEmergencyCallbackMode) { 608 mWifiController.sendMessage( 609 WifiController.CMD_EMERGENCY_MODE_CHANGED, isInEmergencyCallbackMode ? 1 : 0); 610 } 611 612 /** Emergency Call state has changed. */ emergencyCallStateChanged(boolean isInEmergencyCall)613 public void emergencyCallStateChanged(boolean isInEmergencyCall) { 614 mWifiController.sendMessage( 615 WifiController.CMD_EMERGENCY_CALL_STATE_CHANGED, isInEmergencyCall ? 1 : 0); 616 } 617 618 /** Scan always mode has changed. */ scanAlwaysModeChanged()619 public void scanAlwaysModeChanged() { 620 mWifiController.sendMessage( 621 WifiController.CMD_SCAN_ALWAYS_MODE_CHANGED, 622 // Scan only mode change is not considered a direct user interaction since user 623 // is not explicitly turning on wifi scanning (side-effect of location toggle). 624 // So, use the lowest priority internal requestor worksource to ensure that this 625 // is treated with the lowest priority. 626 INTERNAL_REQUESTOR_WS); 627 } 628 629 /** emergency scan progress indication. */ setEmergencyScanRequestInProgress(boolean inProgress)630 public void setEmergencyScanRequestInProgress(boolean inProgress) { 631 mWifiController.sendMessage( 632 WifiController.CMD_EMERGENCY_SCAN_STATE_CHANGED, 633 inProgress ? 1 : 0, 0, 634 // Emergency scans should have the highest priority, so use settings worksource. 635 mFacade.getSettingsWorkSource(mContext)); 636 } 637 638 /** 639 * Listener to request a ModeManager instance for a particular operation. 640 */ 641 public interface ExternalClientModeManagerRequestListener { 642 /** 643 * Returns an instance of ClientModeManager or null if the request failed (when wifi is 644 * off). 645 */ onAnswer(@ullable ClientModeManager modeManager)646 void onAnswer(@Nullable ClientModeManager modeManager); 647 } 648 649 private static class AdditionalClientModeManagerRequestInfo { 650 @NonNull public final ExternalClientModeManagerRequestListener listener; 651 @NonNull public final WorkSource requestorWs; 652 @NonNull public final ClientConnectivityRole clientRole; 653 @NonNull public final String ssid; 654 @Nullable public final String bssid; 655 AdditionalClientModeManagerRequestInfo( @onNull ExternalClientModeManagerRequestListener listener, @NonNull WorkSource requestorWs, @NonNull ClientConnectivityRole clientRole, @NonNull String ssid, @Nullable String bssid)656 AdditionalClientModeManagerRequestInfo( 657 @NonNull ExternalClientModeManagerRequestListener listener, 658 @NonNull WorkSource requestorWs, 659 @NonNull ClientConnectivityRole clientRole, 660 @NonNull String ssid, 661 // For some use-cases, bssid is selected by firmware. 662 @Nullable String bssid) { 663 this.listener = listener; 664 this.requestorWs = requestorWs; 665 this.clientRole = clientRole; 666 this.ssid = ssid; 667 this.bssid = bssid; 668 } 669 } 670 671 /** 672 * Request a local only client manager. 673 * 674 * @param listener used to receive the requested ClientModeManager. Will receive: 675 * 1. null - if Wifi is toggled off 676 * 2. The primary ClientModeManager - if a new ClientModeManager cannot be 677 * created. 678 * 3. The new ClientModeManager - if it was created successfully. 679 * @param requestorWs the WorkSource for this request 680 */ requestLocalOnlyClientModeManager( @onNull ExternalClientModeManagerRequestListener listener, @NonNull WorkSource requestorWs, @NonNull String ssid, @NonNull String bssid)681 public void requestLocalOnlyClientModeManager( 682 @NonNull ExternalClientModeManagerRequestListener listener, 683 @NonNull WorkSource requestorWs, @NonNull String ssid, @NonNull String bssid) { 684 mWifiController.sendMessage( 685 WifiController.CMD_REQUEST_ADDITIONAL_CLIENT_MODE_MANAGER, 686 new AdditionalClientModeManagerRequestInfo( 687 Objects.requireNonNull(listener), Objects.requireNonNull(requestorWs), 688 ROLE_CLIENT_LOCAL_ONLY, ssid, bssid)); 689 } 690 691 /** 692 * Request a secondary long lived client manager. 693 * 694 * @param listener used to receive the requested ClientModeManager. Will receive: 695 * 1. null - if Wifi is toggled off 696 * 2. The primary ClientModeManager - if a new ClientModeManager cannot be 697 * created. 698 * 3. The new ClientModeManager - if it was created successfully. 699 * @param requestorWs the WorkSource for this request 700 */ requestSecondaryLongLivedClientModeManager( @onNull ExternalClientModeManagerRequestListener listener, @NonNull WorkSource requestorWs, @NonNull String ssid, @Nullable String bssid)701 public void requestSecondaryLongLivedClientModeManager( 702 @NonNull ExternalClientModeManagerRequestListener listener, 703 @NonNull WorkSource requestorWs, @NonNull String ssid, @Nullable String bssid) { 704 mWifiController.sendMessage( 705 WifiController.CMD_REQUEST_ADDITIONAL_CLIENT_MODE_MANAGER, 706 new AdditionalClientModeManagerRequestInfo( 707 Objects.requireNonNull(listener), Objects.requireNonNull(requestorWs), 708 ROLE_CLIENT_SECONDARY_LONG_LIVED, ssid, bssid)); 709 } 710 711 /** 712 * Request a secondary transient client manager. 713 * 714 * @param listener used to receive the requested ClientModeManager. Will receive: 715 * 1. null - if Wifi is toggled off. 716 * 2. An existing secondary transient ClientModeManager - if it already exists. 717 * 3. A new secondary transient ClientModeManager - if one doesn't exist and one 718 * was created successfully. 719 * 4. The primary ClientModeManager - if a new ClientModeManager cannot be 720 * created. 721 * @param requestorWs the WorkSource for this request 722 */ requestSecondaryTransientClientModeManager( @onNull ExternalClientModeManagerRequestListener listener, @NonNull WorkSource requestorWs, @NonNull String ssid, @Nullable String bssid)723 public void requestSecondaryTransientClientModeManager( 724 @NonNull ExternalClientModeManagerRequestListener listener, 725 @NonNull WorkSource requestorWs, @NonNull String ssid, @Nullable String bssid) { 726 mWifiController.sendMessage( 727 WifiController.CMD_REQUEST_ADDITIONAL_CLIENT_MODE_MANAGER, 728 new AdditionalClientModeManagerRequestInfo( 729 Objects.requireNonNull(listener), Objects.requireNonNull(requestorWs), 730 ROLE_CLIENT_SECONDARY_TRANSIENT, ssid, bssid)); 731 } 732 733 /** 734 * Remove the provided client manager. 735 */ removeClientModeManager(ClientModeManager clientModeManager)736 public void removeClientModeManager(ClientModeManager clientModeManager) { 737 mWifiController.sendMessage( 738 WifiController.CMD_REMOVE_ADDITIONAL_CLIENT_MODE_MANAGER, clientModeManager); 739 } 740 741 /** 742 * Check whether we have a primary client mode manager (indicates wifi toggle on). 743 */ hasPrimaryClientModeManager()744 public boolean hasPrimaryClientModeManager() { 745 return getClientModeManagerInRole(ROLE_CLIENT_PRIMARY) != null; 746 } 747 748 /** 749 * Returns primary client mode manager if any, else returns null 750 * This mode manager can be the default route on the device & will handle all external API 751 * calls. 752 * @return Instance of {@link ConcreteClientModeManager} or null. 753 */ 754 @Nullable getPrimaryClientModeManagerNullable()755 public ConcreteClientModeManager getPrimaryClientModeManagerNullable() { 756 return getClientModeManagerInRole(ROLE_CLIENT_PRIMARY); 757 } 758 759 /** 760 * Returns primary client mode manager if any, else returns an instance of 761 * {@link ClientModeManager}. 762 * This mode manager can be the default route on the device & will handle all external API 763 * calls. 764 * @return Instance of {@link ClientModeManager}. 765 */ 766 @NonNull getPrimaryClientModeManager()767 public ClientModeManager getPrimaryClientModeManager() { 768 ClientModeManager cm = getPrimaryClientModeManagerNullable(); 769 if (cm != null) return cm; 770 // If there is no primary client manager, return the default one. 771 return mDefaultClientModeManager; 772 } 773 774 /** 775 * Returns all instances of ClientModeManager in 776 * {@link ActiveModeManager.ClientInternetConnectivityRole} roles. 777 * @return List of {@link ClientModeManager}. 778 */ 779 @NonNull getInternetConnectivityClientModeManagers()780 public List<ClientModeManager> getInternetConnectivityClientModeManagers() { 781 List<ClientModeManager> modeManagers = new ArrayList<>(); 782 for (ConcreteClientModeManager manager : mClientModeManagers) { 783 if (manager.getRole() instanceof ClientInternetConnectivityRole) { 784 modeManagers.add(manager); 785 } 786 } 787 return modeManagers; 788 } 789 790 /** Stop all secondary transient ClientModeManagers. */ stopAllClientModeManagersInRole(ClientRole role)791 public void stopAllClientModeManagersInRole(ClientRole role) { 792 // there should only be at most one Make Before Break CMM, but check all of them to be safe. 793 for (ConcreteClientModeManager manager : mClientModeManagers) { 794 if (manager.getRole() == role) { 795 stopAdditionalClientModeManager(manager); 796 } 797 } 798 } 799 800 @NonNull getClientModeManagers()801 public List<ClientModeManager> getClientModeManagers() { 802 return new ArrayList<>(mClientModeManagers); 803 } 804 805 /** 806 * Returns scan only client mode manager, if any. 807 * This mode manager will only allow scanning. 808 * @return Instance of {@link ClientModeManager} or null if none present. 809 */ 810 @Nullable getScanOnlyClientModeManager()811 public ClientModeManager getScanOnlyClientModeManager() { 812 return getClientModeManagerInRole(ROLE_CLIENT_SCAN_ONLY); 813 } 814 815 /** 816 * Returns tethered softap manager, if any. 817 * @return Instance of {@link SoftApManager} or null if none present. 818 */ 819 @Nullable getTetheredSoftApManager()820 public SoftApManager getTetheredSoftApManager() { 821 return getSoftApManagerInRole(ROLE_SOFTAP_TETHERED); 822 } 823 824 /** 825 * Returns LOHS softap manager, if any. 826 * @return Instance of {@link SoftApManager} or null if none present. 827 */ 828 @Nullable getLocalOnlySoftApManager()829 public SoftApManager getLocalOnlySoftApManager() { 830 return getSoftApManagerInRole(ActiveModeManager.ROLE_SOFTAP_LOCAL_ONLY); 831 } 832 hasAnyModeManager()833 private boolean hasAnyModeManager() { 834 return !mClientModeManagers.isEmpty() || !mSoftApManagers.isEmpty(); 835 } 836 hasAnyClientModeManager()837 private boolean hasAnyClientModeManager() { 838 return !mClientModeManagers.isEmpty(); 839 } 840 hasAnyClientModeManagerInConnectivityRole()841 private boolean hasAnyClientModeManagerInConnectivityRole() { 842 for (ConcreteClientModeManager manager : mClientModeManagers) { 843 if (manager.getRole() instanceof ClientConnectivityRole) return true; 844 } 845 return false; 846 } 847 hasAnySoftApManager()848 private boolean hasAnySoftApManager() { 849 return !mSoftApManagers.isEmpty(); 850 } 851 852 /** 853 * @return true if all the client mode managers are in scan only role, 854 * false if there are no client mode managers present or if any of them are not in scan only 855 * role. 856 */ areAllClientModeManagersInScanOnlyRole()857 private boolean areAllClientModeManagersInScanOnlyRole() { 858 if (mClientModeManagers.isEmpty()) return false; 859 for (ConcreteClientModeManager manager : mClientModeManagers) { 860 if (manager.getRole() != ROLE_CLIENT_SCAN_ONLY) return false; 861 } 862 return true; 863 } 864 865 /** Get any client mode manager in the given role, or null if none was found. */ 866 @Nullable getClientModeManagerInRole(ClientRole role)867 public ConcreteClientModeManager getClientModeManagerInRole(ClientRole role) { 868 for (ConcreteClientModeManager manager : mClientModeManagers) { 869 if (manager.getRole() == role) return manager; 870 } 871 return null; 872 } 873 874 /** Get all client mode managers in the specified roles. */ 875 @NonNull getClientModeManagersInRoles(ClientRole... roles)876 public List<ConcreteClientModeManager> getClientModeManagersInRoles(ClientRole... roles) { 877 Set<ClientRole> rolesSet = Set.of(roles); 878 List<ConcreteClientModeManager> result = new ArrayList<>(); 879 for (ConcreteClientModeManager manager : mClientModeManagers) { 880 ClientRole role = manager.getRole(); 881 if (role != null && rolesSet.contains(role)) { 882 result.add(manager); 883 } 884 } 885 return result; 886 } 887 888 @Nullable getSoftApManagerInRole(SoftApRole role)889 private SoftApManager getSoftApManagerInRole(SoftApRole role) { 890 for (SoftApManager manager : mSoftApManagers) { 891 if (manager.getRole() == role) return manager; 892 } 893 return null; 894 } 895 getRoleForSoftApIpMode(int ipMode)896 private SoftApRole getRoleForSoftApIpMode(int ipMode) { 897 return ipMode == IFACE_IP_MODE_TETHERED 898 ? ROLE_SOFTAP_TETHERED 899 : ActiveModeManager.ROLE_SOFTAP_LOCAL_ONLY; 900 } 901 902 /** 903 * Method to enable soft ap for wifi hotspot. 904 * 905 * The supplied SoftApModeConfiguration includes the target softap WifiConfiguration (or null if 906 * the persisted config is to be used) and the target operating mode (ex, 907 * {@link WifiManager#IFACE_IP_MODE_TETHERED} {@link WifiManager#IFACE_IP_MODE_LOCAL_ONLY}). 908 * 909 * @param softApConfig SoftApModeConfiguration for the hostapd softap 910 */ startSoftApModeManager( @onNull SoftApModeConfiguration softApConfig, @NonNull WorkSource requestorWs)911 private void startSoftApModeManager( 912 @NonNull SoftApModeConfiguration softApConfig, @NonNull WorkSource requestorWs) { 913 Log.d(TAG, "Starting SoftApModeManager config = " + softApConfig.getSoftApConfiguration()); 914 Preconditions.checkState(softApConfig.getTargetMode() == IFACE_IP_MODE_LOCAL_ONLY 915 || softApConfig.getTargetMode() == IFACE_IP_MODE_TETHERED); 916 917 WifiServiceImpl.SoftApCallbackInternal callback = 918 softApConfig.getTargetMode() == IFACE_IP_MODE_LOCAL_ONLY 919 ? mLohsCallback : mSoftApCallback; 920 SoftApManager manager = mWifiInjector.makeSoftApManager( 921 new SoftApListener(), callback, softApConfig, requestorWs, 922 getRoleForSoftApIpMode(softApConfig.getTargetMode()), mVerboseLoggingEnabled); 923 mSoftApManagers.add(manager); 924 } 925 926 /** 927 * Method to stop all soft ap for the specified mode. 928 * 929 * This method will stop any active softAp mode managers. 930 * 931 * @param ipMode the operating mode of APs to bring down (ex, 932 * {@link WifiManager#IFACE_IP_MODE_TETHERED} or 933 * {@link WifiManager#IFACE_IP_MODE_LOCAL_ONLY}). 934 * Use {@link WifiManager#IFACE_IP_MODE_UNSPECIFIED} to stop all APs. 935 */ stopSoftApModeManagers(int ipMode)936 private void stopSoftApModeManagers(int ipMode) { 937 Log.d(TAG, "Shutting down all softap mode managers in mode " + ipMode); 938 for (SoftApManager softApManager : mSoftApManagers) { 939 if (ipMode == WifiManager.IFACE_IP_MODE_UNSPECIFIED 940 || getRoleForSoftApIpMode(ipMode) == softApManager.getRole()) { 941 softApManager.stop(); 942 } 943 } 944 } 945 updateCapabilityToSoftApModeManager(SoftApCapability capability)946 private void updateCapabilityToSoftApModeManager(SoftApCapability capability) { 947 for (SoftApManager softApManager : mSoftApManagers) { 948 softApManager.updateCapability(capability); 949 } 950 } 951 updateConfigurationToSoftApModeManager(SoftApConfiguration config)952 private void updateConfigurationToSoftApModeManager(SoftApConfiguration config) { 953 for (SoftApManager softApManager : mSoftApManagers) { 954 softApManager.updateConfiguration(config); 955 } 956 } 957 958 /** 959 * Method to enable a new primary client mode manager in scan only mode. 960 */ startScanOnlyClientModeManager(WorkSource requestorWs)961 private boolean startScanOnlyClientModeManager(WorkSource requestorWs) { 962 Log.d(TAG, "Starting primary ClientModeManager in scan only mode"); 963 ConcreteClientModeManager manager = mWifiInjector.makeClientModeManager( 964 new ClientListener(), requestorWs, ROLE_CLIENT_SCAN_ONLY, mVerboseLoggingEnabled); 965 mClientModeManagers.add(manager); 966 mLastScanOnlyClientModeManagerRequestorWs = requestorWs; 967 return true; 968 } 969 970 /** 971 * Method to enable a new primary client mode manager in connect mode. 972 */ startPrimaryClientModeManager(WorkSource requestorWs)973 private boolean startPrimaryClientModeManager(WorkSource requestorWs) { 974 Log.d(TAG, "Starting primary ClientModeManager in connect mode"); 975 ConcreteClientModeManager manager = mWifiInjector.makeClientModeManager( 976 new ClientListener(), requestorWs, ROLE_CLIENT_PRIMARY, mVerboseLoggingEnabled); 977 mClientModeManagers.add(manager); 978 mLastPrimaryClientModeManagerRequestorWs = requestorWs; 979 return true; 980 } 981 982 /** 983 * Method to enable a new primary client mode manager. 984 */ startPrimaryOrScanOnlyClientModeManager(WorkSource requestorWs)985 private boolean startPrimaryOrScanOnlyClientModeManager(WorkSource requestorWs) { 986 ActiveModeManager.ClientRole role = getRoleForPrimaryOrScanOnlyClientModeManager(); 987 if (role == ROLE_CLIENT_PRIMARY) { 988 return startPrimaryClientModeManager(requestorWs); 989 } else if (role == ROLE_CLIENT_SCAN_ONLY) { 990 return startScanOnlyClientModeManager(requestorWs); 991 } else { 992 return false; 993 } 994 } 995 996 /** 997 * Method to stop all client mode mangers. 998 */ stopAllClientModeManagers()999 private void stopAllClientModeManagers() { 1000 Log.d(TAG, "Shutting down all client mode managers"); 1001 for (ConcreteClientModeManager clientModeManager : mClientModeManagers) { 1002 clientModeManager.stop(); 1003 } 1004 } 1005 1006 /** 1007 * Method to switch all primary client mode manager mode of operation to ScanOnly mode. 1008 */ switchAllPrimaryClientModeManagersToScanOnlyMode(@onNull WorkSource requestorWs)1009 private void switchAllPrimaryClientModeManagersToScanOnlyMode(@NonNull WorkSource requestorWs) { 1010 Log.d(TAG, "Switching all primary client mode managers to scan only mode"); 1011 for (ConcreteClientModeManager clientModeManager : mClientModeManagers) { 1012 if (clientModeManager.getRole() != ROLE_CLIENT_PRIMARY) { 1013 continue; 1014 } 1015 clientModeManager.setRole(ROLE_CLIENT_SCAN_ONLY, requestorWs); 1016 } 1017 } 1018 1019 /** 1020 * Method to switch all client mode manager mode of operation (from ScanOnly To Connect & 1021 * vice-versa) based on the toggle state. 1022 */ switchAllPrimaryOrScanOnlyClientModeManagers()1023 private boolean switchAllPrimaryOrScanOnlyClientModeManagers() { 1024 Log.d(TAG, "Switching all client mode managers"); 1025 for (ConcreteClientModeManager clientModeManager : mClientModeManagers) { 1026 if (clientModeManager.getRole() != ROLE_CLIENT_PRIMARY 1027 && clientModeManager.getRole() != ROLE_CLIENT_SCAN_ONLY) { 1028 continue; 1029 } 1030 if (!switchPrimaryOrScanOnlyClientModeManagerRole(clientModeManager)) { 1031 return false; 1032 } 1033 } 1034 return true; 1035 } 1036 getRoleForPrimaryOrScanOnlyClientModeManager()1037 private ActiveModeManager.ClientRole getRoleForPrimaryOrScanOnlyClientModeManager() { 1038 if (mSettingsStore.isWifiToggleEnabled()) { 1039 return ROLE_CLIENT_PRIMARY; 1040 } else if (mWifiController.shouldEnableScanOnlyMode()) { 1041 return ROLE_CLIENT_SCAN_ONLY; 1042 } else { 1043 Log.e(TAG, "Something is wrong, no client mode toggles enabled"); 1044 return null; 1045 } 1046 } 1047 1048 /** 1049 * Method to switch a client mode manager mode of operation (from ScanOnly To Connect & 1050 * vice-versa) based on the toggle state. 1051 */ switchPrimaryOrScanOnlyClientModeManagerRole( @onNull ConcreteClientModeManager modeManager)1052 private boolean switchPrimaryOrScanOnlyClientModeManagerRole( 1053 @NonNull ConcreteClientModeManager modeManager) { 1054 ActiveModeManager.ClientRole role = getRoleForPrimaryOrScanOnlyClientModeManager(); 1055 final WorkSource lastRequestorWs; 1056 if (role == ROLE_CLIENT_PRIMARY) { 1057 lastRequestorWs = mLastPrimaryClientModeManagerRequestorWs; 1058 } else if (role == ROLE_CLIENT_SCAN_ONLY) { 1059 lastRequestorWs = mLastScanOnlyClientModeManagerRequestorWs; 1060 } else { 1061 return false; 1062 } 1063 modeManager.setRole(role, lastRequestorWs); 1064 return true; 1065 } 1066 1067 /** 1068 * Method to start a new client mode manager. 1069 */ startAdditionalClientModeManager( ClientConnectivityRole role, @NonNull ExternalClientModeManagerRequestListener externalRequestListener, @NonNull WorkSource requestorWs)1070 private boolean startAdditionalClientModeManager( 1071 ClientConnectivityRole role, 1072 @NonNull ExternalClientModeManagerRequestListener externalRequestListener, 1073 @NonNull WorkSource requestorWs) { 1074 Log.d(TAG, "Starting additional ClientModeManager in role: " + role); 1075 ClientListener listener = new ClientListener(externalRequestListener); 1076 ConcreteClientModeManager manager = mWifiInjector.makeClientModeManager( 1077 listener, requestorWs, role, mVerboseLoggingEnabled); 1078 mClientModeManagers.add(manager); 1079 return true; 1080 } 1081 1082 /** 1083 * Method to switch role for an existing non-primary client mode manager. 1084 */ switchRoleForAdditionalClientModeManager( @onNull ConcreteClientModeManager manager, @NonNull ClientConnectivityRole role, @NonNull ExternalClientModeManagerRequestListener externalRequestListener, @NonNull WorkSource requestorWs)1085 private boolean switchRoleForAdditionalClientModeManager( 1086 @NonNull ConcreteClientModeManager manager, 1087 @NonNull ClientConnectivityRole role, 1088 @NonNull ExternalClientModeManagerRequestListener externalRequestListener, 1089 @NonNull WorkSource requestorWs) { 1090 Log.d(TAG, "Switching role for additional ClientModeManager to role: " + role); 1091 ClientListener listener = new ClientListener(externalRequestListener); 1092 manager.setRole(role, requestorWs, listener); 1093 return true; 1094 } 1095 1096 /** 1097 * Method to stop client mode manger. 1098 */ stopAdditionalClientModeManager(ClientModeManager clientModeManager)1099 private void stopAdditionalClientModeManager(ClientModeManager clientModeManager) { 1100 if (clientModeManager.getRole() == ROLE_CLIENT_PRIMARY 1101 || clientModeManager.getRole() == ROLE_CLIENT_SCAN_ONLY) return; 1102 Log.d(TAG, "Shutting down additional client mode manager: " + clientModeManager); 1103 clientModeManager.stop(); 1104 } 1105 1106 /** 1107 * Method to stop all active modes, for example, when toggling airplane mode. 1108 */ shutdownWifi()1109 private void shutdownWifi() { 1110 Log.d(TAG, "Shutting down all mode managers"); 1111 for (ActiveModeManager manager : getActiveModeManagers()) { 1112 manager.stop(); 1113 } 1114 } 1115 1116 /** 1117 * Dump current state for active mode managers. 1118 * 1119 * Must be called from the main Wifi thread. 1120 */ dump(FileDescriptor fd, PrintWriter pw, String[] args)1121 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 1122 pw.println("Dump of " + TAG); 1123 pw.println("Current wifi mode: " + getCurrentMode()); 1124 pw.println("NumActiveModeManagers: " + getActiveModeManagerCount()); 1125 mWifiController.dump(fd, pw, args); 1126 for (ActiveModeManager manager : getActiveModeManagers()) { 1127 manager.dump(fd, pw, args); 1128 } 1129 mGraveyard.dump(fd, pw, args); 1130 boolean isStaStaConcurrencySupported = mWifiNative.isStaStaConcurrencySupported(); 1131 pw.println("STA + STA Concurrency Supported: " + isStaStaConcurrencySupported); 1132 if (isStaStaConcurrencySupported) { 1133 pw.println(" MBB use-case enabled: " 1134 + mContext.getResources().getBoolean( 1135 R.bool.config_wifiMultiStaNetworkSwitchingMakeBeforeBreakEnabled)); 1136 pw.println(" Local only use-case enabled: " 1137 + mContext.getResources().getBoolean( 1138 R.bool.config_wifiMultiStaLocalOnlyConcurrencyEnabled)); 1139 pw.println(" Restricted use-case enabled: " 1140 + mContext.getResources().getBoolean( 1141 R.bool.config_wifiMultiStaRestrictedConcurrencyEnabled)); 1142 } 1143 pw.println("STA + AP Concurrency Supported: " + isStaApConcurrencySupported()); 1144 mWifiInjector.getHalDeviceManager().dump(fd, pw, args); 1145 } 1146 1147 @VisibleForTesting getCurrentMode()1148 String getCurrentMode() { 1149 IState state = mWifiController.getCurrentState(); 1150 return state == null ? STATE_MACHINE_EXITED_STATE_NAME : state.getName(); 1151 } 1152 1153 @VisibleForTesting getActiveModeManagers()1154 Collection<ActiveModeManager> getActiveModeManagers() { 1155 ArrayList<ActiveModeManager> activeModeManagers = new ArrayList<>(); 1156 activeModeManagers.addAll(mClientModeManagers); 1157 activeModeManagers.addAll(mSoftApManagers); 1158 return activeModeManagers; 1159 } 1160 getActiveModeManagerCount()1161 private int getActiveModeManagerCount() { 1162 return mSoftApManagers.size() + mClientModeManagers.size(); 1163 } 1164 1165 @VisibleForTesting isInEmergencyMode()1166 boolean isInEmergencyMode() { 1167 IState state = mWifiController.getCurrentState(); 1168 return ((WifiController.BaseState) state).isInEmergencyMode(); 1169 } 1170 updateBatteryStats()1171 private void updateBatteryStats() { 1172 updateBatteryStatsWifiState(hasAnyModeManager()); 1173 if (areAllClientModeManagersInScanOnlyRole()) { 1174 updateBatteryStatsScanModeActive(); 1175 } 1176 } 1177 1178 private class SoftApListener implements ActiveModeManager.Listener<SoftApManager> { 1179 @Override onStarted(SoftApManager softApManager)1180 public void onStarted(SoftApManager softApManager) { 1181 updateBatteryStats(); 1182 invokeOnAddedCallbacks(softApManager); 1183 } 1184 1185 @Override onRoleChanged(SoftApManager softApManager)1186 public void onRoleChanged(SoftApManager softApManager) { 1187 Log.w(TAG, "Role switched received on SoftApManager unexpectedly"); 1188 } 1189 1190 @Override onStopped(SoftApManager softApManager)1191 public void onStopped(SoftApManager softApManager) { 1192 mSoftApManagers.remove(softApManager); 1193 mGraveyard.inter(softApManager); 1194 updateBatteryStats(); 1195 mWifiController.sendMessage(WifiController.CMD_AP_STOPPED); 1196 invokeOnRemovedCallbacks(softApManager); 1197 } 1198 1199 @Override onStartFailure(SoftApManager softApManager)1200 public void onStartFailure(SoftApManager softApManager) { 1201 mSoftApManagers.remove(softApManager); 1202 mGraveyard.inter(softApManager); 1203 updateBatteryStats(); 1204 mWifiController.sendMessage(WifiController.CMD_AP_START_FAILURE); 1205 // onStartFailure can be called when switching between roles. So, remove 1206 // update listeners. 1207 Log.e(TAG, "SoftApManager start failed!" + softApManager); 1208 invokeOnRemovedCallbacks(softApManager); 1209 } 1210 } 1211 1212 private class ClientListener implements ActiveModeManager.Listener<ConcreteClientModeManager> { 1213 @Nullable 1214 private ExternalClientModeManagerRequestListener mExternalRequestListener; // one shot 1215 ClientListener()1216 ClientListener() { 1217 this(null); 1218 } 1219 ClientListener( @ullable ExternalClientModeManagerRequestListener externalRequestListener)1220 ClientListener( 1221 @Nullable ExternalClientModeManagerRequestListener externalRequestListener) { 1222 mExternalRequestListener = externalRequestListener; 1223 } 1224 1225 @WifiNative.MultiStaUseCase getMultiStatUseCase()1226 private int getMultiStatUseCase() { 1227 // Note: The use-case setting finds the first non-primary client mode manager to set 1228 // the use-case to HAL. This does not extend to 3 STA concurrency when there are 1229 // 2 secondary STA client mode managers. 1230 for (ClientModeManager cmm : getClientModeManagers()) { 1231 ClientRole clientRole = cmm.getRole(); 1232 if (clientRole == ROLE_CLIENT_LOCAL_ONLY 1233 || clientRole == ROLE_CLIENT_SECONDARY_LONG_LIVED) { 1234 return WifiNative.DUAL_STA_NON_TRANSIENT_UNBIASED; 1235 } else if (clientRole == ROLE_CLIENT_SECONDARY_TRANSIENT) { 1236 return WifiNative.DUAL_STA_TRANSIENT_PREFER_PRIMARY; 1237 } 1238 } 1239 // if single STA, a safe default is PREFER_PRIMARY 1240 return WifiNative.DUAL_STA_TRANSIENT_PREFER_PRIMARY; 1241 } 1242 1243 /** 1244 * Hardware needs to be configured for STA + STA before sending the callbacks to clients 1245 * letting them know that CM is ready for use. 1246 */ configureHwForMultiStaIfNecessary()1247 private void configureHwForMultiStaIfNecessary() { 1248 mWifiNative.setMultiStaUseCase(getMultiStatUseCase()); 1249 String primaryIfaceName = getPrimaryClientModeManager().getInterfaceName(); 1250 // if no primary exists (occurs briefly during Make Before Break), don't update the 1251 // primary and keep the previous primary. Only update WifiNative when the new primary is 1252 // activated. 1253 if (primaryIfaceName != null) { 1254 mWifiNative.setMultiStaPrimaryConnection(primaryIfaceName); 1255 } 1256 } 1257 onStartedOrRoleChanged(ConcreteClientModeManager clientModeManager)1258 private void onStartedOrRoleChanged(ConcreteClientModeManager clientModeManager) { 1259 updateClientScanMode(); 1260 updateBatteryStats(); 1261 configureHwForMultiStaIfNecessary(); 1262 if (mExternalRequestListener != null) { 1263 mExternalRequestListener.onAnswer(clientModeManager); 1264 mExternalRequestListener = null; // reset after one shot. 1265 } 1266 1267 // Report to SarManager 1268 reportWifiStateToSarManager(); 1269 } 1270 reportWifiStateToSarManager()1271 private void reportWifiStateToSarManager() { 1272 if (areAllClientModeManagersInScanOnlyRole()) { 1273 // Inform sar manager that scan only is being enabled 1274 mWifiInjector.getSarManager().setScanOnlyWifiState(WifiManager.WIFI_STATE_ENABLED); 1275 } else { 1276 // Inform sar manager that scan only is being disabled 1277 mWifiInjector.getSarManager().setScanOnlyWifiState(WifiManager.WIFI_STATE_DISABLED); 1278 } 1279 if (hasAnyClientModeManagerInConnectivityRole()) { 1280 // Inform sar manager that wifi is Enabled 1281 mWifiInjector.getSarManager().setClientWifiState(WifiManager.WIFI_STATE_ENABLED); 1282 } else { 1283 // Inform sar manager that wifi is being disabled 1284 mWifiInjector.getSarManager().setClientWifiState(WifiManager.WIFI_STATE_DISABLED); 1285 } 1286 } 1287 onPrimaryChangedDueToStartedOrRoleChanged( ConcreteClientModeManager clientModeManager)1288 private void onPrimaryChangedDueToStartedOrRoleChanged( 1289 ConcreteClientModeManager clientModeManager) { 1290 if (clientModeManager.getRole() != ROLE_CLIENT_PRIMARY 1291 && clientModeManager == mLastPrimaryClientModeManager) { 1292 // CMM was primary, but is no longer primary 1293 invokeOnPrimaryClientModeManagerChangedCallbacks(clientModeManager, null); 1294 mLastPrimaryClientModeManager = null; 1295 } else if (clientModeManager.getRole() == ROLE_CLIENT_PRIMARY 1296 && clientModeManager != mLastPrimaryClientModeManager) { 1297 // CMM is primary, but wasn't primary before 1298 invokeOnPrimaryClientModeManagerChangedCallbacks( 1299 mLastPrimaryClientModeManager, clientModeManager); 1300 mLastPrimaryClientModeManager = clientModeManager; 1301 } 1302 } 1303 1304 @Override onStarted(@onNull ConcreteClientModeManager clientModeManager)1305 public void onStarted(@NonNull ConcreteClientModeManager clientModeManager) { 1306 onStartedOrRoleChanged(clientModeManager); 1307 invokeOnAddedCallbacks(clientModeManager); 1308 // invoke "added" callbacks before primary changed 1309 onPrimaryChangedDueToStartedOrRoleChanged(clientModeManager); 1310 } 1311 1312 @Override onRoleChanged(@onNull ConcreteClientModeManager clientModeManager)1313 public void onRoleChanged(@NonNull ConcreteClientModeManager clientModeManager) { 1314 onStartedOrRoleChanged(clientModeManager); 1315 invokeOnRoleChangedCallbacks(clientModeManager); 1316 onPrimaryChangedDueToStartedOrRoleChanged(clientModeManager); 1317 } 1318 onStoppedOrStartFailure(ConcreteClientModeManager clientModeManager)1319 private void onStoppedOrStartFailure(ConcreteClientModeManager clientModeManager) { 1320 mClientModeManagers.remove(clientModeManager); 1321 mGraveyard.inter(clientModeManager); 1322 updateClientScanMode(); 1323 updateBatteryStats(); 1324 if (clientModeManager == mLastPrimaryClientModeManager) { 1325 // CMM was primary, but was stopped 1326 invokeOnPrimaryClientModeManagerChangedCallbacks( 1327 mLastPrimaryClientModeManager, null); 1328 mLastPrimaryClientModeManager = null; 1329 } 1330 // invoke "removed" callbacks after primary changed 1331 invokeOnRemovedCallbacks(clientModeManager); 1332 1333 // Report to SarManager 1334 reportWifiStateToSarManager(); 1335 } 1336 1337 @Override onStopped(@onNull ConcreteClientModeManager clientModeManager)1338 public void onStopped(@NonNull ConcreteClientModeManager clientModeManager) { 1339 onStoppedOrStartFailure(clientModeManager); 1340 mWifiController.sendMessage(WifiController.CMD_STA_STOPPED); 1341 } 1342 1343 @Override onStartFailure(@onNull ConcreteClientModeManager clientModeManager)1344 public void onStartFailure(@NonNull ConcreteClientModeManager clientModeManager) { 1345 Log.e(TAG, "ClientModeManager start failed!" + clientModeManager); 1346 // onStartFailure can be called when switching between roles. So, remove 1347 // update listeners. 1348 onStoppedOrStartFailure(clientModeManager); 1349 mWifiController.sendMessage(WifiController.CMD_STA_START_FAILURE); 1350 } 1351 } 1352 1353 // Update the scan state based on all active mode managers. updateClientScanMode()1354 private void updateClientScanMode() { 1355 boolean scanEnabled = hasAnyClientModeManager(); 1356 boolean scanningForHiddenNetworksEnabled; 1357 1358 if (mContext.getResources().getBoolean(R.bool.config_wifiScanHiddenNetworksScanOnlyMode)) { 1359 scanningForHiddenNetworksEnabled = hasAnyClientModeManager(); 1360 } else { 1361 scanningForHiddenNetworksEnabled = hasAnyClientModeManagerInConnectivityRole(); 1362 } 1363 mScanRequestProxy.enableScanning(scanEnabled, scanningForHiddenNetworksEnabled); 1364 } 1365 1366 /** 1367 * Helper method to report wifi state as on/off (doesn't matter which mode). 1368 * 1369 * @param enabled boolean indicating that some mode has been turned on or off 1370 */ updateBatteryStatsWifiState(boolean enabled)1371 private void updateBatteryStatsWifiState(boolean enabled) { 1372 if (enabled) { 1373 if (getActiveModeManagerCount() == 1) { 1374 // only report wifi on if we haven't already 1375 mBatteryStatsManager.reportWifiOn(); 1376 } 1377 } else { 1378 if (getActiveModeManagerCount() == 0) { 1379 // only report if we don't have any active modes 1380 mBatteryStatsManager.reportWifiOff(); 1381 } 1382 } 1383 } 1384 updateBatteryStatsScanModeActive()1385 private void updateBatteryStatsScanModeActive() { 1386 mBatteryStatsManager.reportWifiState(BatteryStatsManager.WIFI_STATE_OFF_SCANNING, null); 1387 } 1388 1389 /** 1390 * Called to pull metrics from ActiveModeWarden to WifiMetrics when a dump is triggered, as 1391 * opposed to the more common push metrics which are reported to WifiMetrics as soon as they 1392 * occur. 1393 */ updateMetrics()1394 public void updateMetrics() { 1395 mWifiMetrics.setIsMakeBeforeBreakSupported(isStaStaConcurrencySupportedForMbb()); 1396 } 1397 1398 /** 1399 * WifiController is the class used to manage wifi state for various operating 1400 * modes (normal, airplane, wifi hotspot, etc.). 1401 */ 1402 private class WifiController extends StateMachine { 1403 private static final String TAG = "WifiController"; 1404 1405 // Maximum limit to use for timeout delay if the value from overlay setting is too large. 1406 private static final int MAX_RECOVERY_TIMEOUT_DELAY_MS = 4000; 1407 1408 private static final int BASE = Protocol.BASE_WIFI_CONTROLLER; 1409 1410 static final int CMD_EMERGENCY_MODE_CHANGED = BASE + 1; 1411 static final int CMD_EMERGENCY_SCAN_STATE_CHANGED = BASE + 2; 1412 static final int CMD_SCAN_ALWAYS_MODE_CHANGED = BASE + 7; 1413 static final int CMD_WIFI_TOGGLED = BASE + 8; 1414 static final int CMD_AIRPLANE_TOGGLED = BASE + 9; 1415 static final int CMD_SET_AP = BASE + 10; 1416 static final int CMD_EMERGENCY_CALL_STATE_CHANGED = BASE + 14; 1417 static final int CMD_AP_STOPPED = BASE + 15; 1418 static final int CMD_STA_START_FAILURE = BASE + 16; 1419 // Command used to trigger a wifi stack restart when in active mode 1420 static final int CMD_RECOVERY_RESTART_WIFI = BASE + 17; 1421 // Internal command used to complete wifi stack restart 1422 private static final int CMD_RECOVERY_RESTART_WIFI_CONTINUE = BASE + 18; 1423 // Command to disable wifi when SelfRecovery is throttled or otherwise not doing full 1424 // recovery 1425 static final int CMD_RECOVERY_DISABLE_WIFI = BASE + 19; 1426 static final int CMD_STA_STOPPED = BASE + 20; 1427 static final int CMD_DEFERRED_RECOVERY_RESTART_WIFI = BASE + 22; 1428 static final int CMD_AP_START_FAILURE = BASE + 23; 1429 static final int CMD_UPDATE_AP_CAPABILITY = BASE + 24; 1430 static final int CMD_UPDATE_AP_CONFIG = BASE + 25; 1431 static final int CMD_REQUEST_ADDITIONAL_CLIENT_MODE_MANAGER = BASE + 26; 1432 static final int CMD_REMOVE_ADDITIONAL_CLIENT_MODE_MANAGER = BASE + 27; 1433 1434 private final EnabledState mEnabledState = new EnabledState(); 1435 private final DisabledState mDisabledState = new DisabledState(); 1436 1437 private boolean mIsInEmergencyCall = false; 1438 private boolean mIsInEmergencyCallbackMode = false; 1439 private boolean mIsEmergencyScanInProgress = false; 1440 WifiController()1441 WifiController() { 1442 super(TAG, mLooper); 1443 1444 DefaultState defaultState = new DefaultState(); 1445 addState(defaultState); { 1446 addState(mDisabledState, defaultState); 1447 addState(mEnabledState, defaultState); 1448 } 1449 1450 setLogRecSize(100); 1451 setLogOnlyTransitions(false); 1452 1453 } 1454 1455 @Override getWhatToString(int what)1456 protected String getWhatToString(int what) { 1457 switch (what) { 1458 case CMD_AIRPLANE_TOGGLED: 1459 return "CMD_AIRPLANE_TOGGLED"; 1460 case CMD_AP_START_FAILURE: 1461 return "CMD_AP_START_FAILURE"; 1462 case CMD_AP_STOPPED: 1463 return "CMD_AP_STOPPED"; 1464 case CMD_DEFERRED_RECOVERY_RESTART_WIFI: 1465 return "CMD_DEFERRED_RECOVERY_RESTART_WIFI"; 1466 case CMD_EMERGENCY_CALL_STATE_CHANGED: 1467 return "CMD_EMERGENCY_CALL_STATE_CHANGED"; 1468 case CMD_EMERGENCY_MODE_CHANGED: 1469 return "CMD_EMERGENCY_MODE_CHANGED"; 1470 case CMD_RECOVERY_DISABLE_WIFI: 1471 return "CMD_RECOVERY_DISABLE_WIFI"; 1472 case CMD_RECOVERY_RESTART_WIFI: 1473 return "CMD_RECOVERY_RESTART_WIFI"; 1474 case CMD_RECOVERY_RESTART_WIFI_CONTINUE: 1475 return "CMD_RECOVERY_RESTART_WIFI_CONTINUE"; 1476 case CMD_REMOVE_ADDITIONAL_CLIENT_MODE_MANAGER: 1477 return "CMD_REMOVE_ADDITIONAL_CLIENT_MODE_MANAGER"; 1478 case CMD_REQUEST_ADDITIONAL_CLIENT_MODE_MANAGER: 1479 return "CMD_REQUEST_ADDITIONAL_CLIENT_MODE_MANAGER"; 1480 case CMD_EMERGENCY_SCAN_STATE_CHANGED: 1481 return "CMD_EMERGENCY_SCAN_STATE_CHANGED"; 1482 case CMD_SCAN_ALWAYS_MODE_CHANGED: 1483 return "CMD_SCAN_ALWAYS_MODE_CHANGED"; 1484 case CMD_SET_AP: 1485 return "CMD_SET_AP"; 1486 case CMD_STA_START_FAILURE: 1487 return "CMD_STA_START_FAILURE"; 1488 case CMD_STA_STOPPED: 1489 return "CMD_STA_STOPPED"; 1490 case CMD_UPDATE_AP_CAPABILITY: 1491 return "CMD_UPDATE_AP_CAPABILITY"; 1492 case CMD_UPDATE_AP_CONFIG: 1493 return "CMD_UPDATE_AP_CONFIG"; 1494 case CMD_WIFI_TOGGLED: 1495 return "CMD_WIFI_TOGGLED"; 1496 default: 1497 return "what:" + what; 1498 } 1499 } 1500 1501 @Override start()1502 public void start() { 1503 boolean isAirplaneModeOn = mSettingsStore.isAirplaneModeOn(); 1504 boolean isWifiEnabled = mSettingsStore.isWifiToggleEnabled(); 1505 boolean isScanningAlwaysAvailable = mSettingsStore.isScanAlwaysAvailable(); 1506 boolean isLocationModeActive = mWifiPermissionsUtil.isLocationModeEnabled(); 1507 1508 log("isAirplaneModeOn = " + isAirplaneModeOn 1509 + ", isWifiEnabled = " + isWifiEnabled 1510 + ", isScanningAvailable = " + isScanningAlwaysAvailable 1511 + ", isLocationModeActive = " + isLocationModeActive); 1512 1513 // Initialize these values at bootup to defaults, will be overridden by API calls 1514 // for further toggles. 1515 mLastPrimaryClientModeManagerRequestorWs = mFacade.getSettingsWorkSource(mContext); 1516 mLastScanOnlyClientModeManagerRequestorWs = INTERNAL_REQUESTOR_WS; 1517 ActiveModeManager.ClientRole role = getRoleForPrimaryOrScanOnlyClientModeManager(); 1518 if (role == ROLE_CLIENT_PRIMARY) { 1519 startPrimaryClientModeManager(mLastPrimaryClientModeManagerRequestorWs); 1520 setInitialState(mEnabledState); 1521 } else if (role == ROLE_CLIENT_SCAN_ONLY) { 1522 startScanOnlyClientModeManager(mLastScanOnlyClientModeManagerRequestorWs); 1523 setInitialState(mEnabledState); 1524 } else { 1525 setInitialState(mDisabledState); 1526 } 1527 mWifiMetrics.noteWifiEnabledDuringBoot(mSettingsStore.isWifiToggleEnabled()); 1528 1529 // Initialize the lower layers before we start. 1530 mWifiNative.initialize(); 1531 super.start(); 1532 } 1533 readWifiRecoveryDelay()1534 private int readWifiRecoveryDelay() { 1535 int recoveryDelayMillis = mContext.getResources().getInteger( 1536 R.integer.config_wifi_framework_recovery_timeout_delay); 1537 if (recoveryDelayMillis > MAX_RECOVERY_TIMEOUT_DELAY_MS) { 1538 recoveryDelayMillis = MAX_RECOVERY_TIMEOUT_DELAY_MS; 1539 Log.w(TAG, "Overriding timeout delay with maximum limit value"); 1540 } 1541 return recoveryDelayMillis; 1542 } 1543 1544 abstract class BaseState extends State { 1545 @VisibleForTesting isInEmergencyMode()1546 boolean isInEmergencyMode() { 1547 return mIsInEmergencyCall || mIsInEmergencyCallbackMode; 1548 } 1549 1550 /** Device is in emergency mode & carrier config requires wifi off in emergency mode */ isInEmergencyModeWhichRequiresWifiDisable()1551 private boolean isInEmergencyModeWhichRequiresWifiDisable() { 1552 return isInEmergencyMode() && mFacade.getConfigWiFiDisableInECBM(mContext); 1553 } 1554 updateEmergencyMode(Message msg)1555 private void updateEmergencyMode(Message msg) { 1556 if (msg.what == CMD_EMERGENCY_CALL_STATE_CHANGED) { 1557 mIsInEmergencyCall = msg.arg1 == 1; 1558 } else if (msg.what == CMD_EMERGENCY_MODE_CHANGED) { 1559 mIsInEmergencyCallbackMode = msg.arg1 == 1; 1560 } 1561 } 1562 enterEmergencyMode()1563 private void enterEmergencyMode() { 1564 stopSoftApModeManagers(WifiManager.IFACE_IP_MODE_UNSPECIFIED); 1565 boolean configWiFiDisableInECBM = mFacade.getConfigWiFiDisableInECBM(mContext); 1566 log("Entering emergency callback mode, " 1567 + "CarrierConfigManager.KEY_CONFIG_WIFI_DISABLE_IN_ECBM: " 1568 + configWiFiDisableInECBM); 1569 if (!mIsEmergencyScanInProgress) { 1570 if (configWiFiDisableInECBM) { 1571 shutdownWifi(); 1572 } 1573 } else { 1574 if (configWiFiDisableInECBM) { 1575 switchAllPrimaryClientModeManagersToScanOnlyMode( 1576 mFacade.getSettingsWorkSource(mContext)); 1577 } 1578 } 1579 } 1580 exitEmergencyMode()1581 private void exitEmergencyMode() { 1582 log("Exiting emergency callback mode"); 1583 // may be in DisabledState or EnabledState (depending on whether Wifi was shut down 1584 // in enterEmergencyMode() or not based on getConfigWiFiDisableInECBM). 1585 // Let CMD_WIFI_TOGGLED handling decide what the next state should be, or if we're 1586 // already in the correct state. 1587 1588 // Assumes user toggled it on from settings before. 1589 wifiToggled(mFacade.getSettingsWorkSource(mContext)); 1590 } 1591 processMessageInEmergencyMode(Message msg)1592 private boolean processMessageInEmergencyMode(Message msg) { 1593 // In emergency mode: Some messages need special handling in this mode, 1594 // all others are dropped. 1595 switch (msg.what) { 1596 case CMD_STA_STOPPED: 1597 case CMD_AP_STOPPED: 1598 log("Processing message in Emergency Callback Mode: " + msg); 1599 if (!hasAnyModeManager()) { 1600 log("No active mode managers, return to DisabledState."); 1601 transitionTo(mDisabledState); 1602 } 1603 break; 1604 case CMD_SET_AP: 1605 // arg1 == 1 => enable AP 1606 if (msg.arg1 == 1) { 1607 log("AP cannot be started in Emergency Callback Mode: " + msg); 1608 // SoftAP was disabled upon entering emergency mode. It also cannot 1609 // be re-enabled during emergency mode. Drop the message and invoke 1610 // the failure callback. 1611 Pair<SoftApModeConfiguration, WorkSource> softApConfigAndWs = 1612 (Pair<SoftApModeConfiguration, WorkSource>) msg.obj; 1613 SoftApModeConfiguration softApConfig = softApConfigAndWs.first; 1614 WifiServiceImpl.SoftApCallbackInternal callback = 1615 softApConfig.getTargetMode() == IFACE_IP_MODE_LOCAL_ONLY 1616 ? mLohsCallback : mSoftApCallback; 1617 // need to notify SoftApCallback that start/stop AP failed 1618 callback.onStateChanged(WifiManager.WIFI_AP_STATE_FAILED, 1619 WifiManager.SAP_START_FAILURE_GENERAL); 1620 } 1621 break; 1622 default: 1623 log("Dropping message in emergency callback mode: " + msg); 1624 break; 1625 1626 } 1627 return HANDLED; 1628 } 1629 handleEmergencyModeStateChange(Message msg)1630 private void handleEmergencyModeStateChange(Message msg) { 1631 boolean wasInEmergencyMode = isInEmergencyMode(); 1632 updateEmergencyMode(msg); 1633 boolean isInEmergencyMode = isInEmergencyMode(); 1634 if (!wasInEmergencyMode && isInEmergencyMode) { 1635 enterEmergencyMode(); 1636 } else if (wasInEmergencyMode && !isInEmergencyMode) { 1637 exitEmergencyMode(); 1638 } 1639 } 1640 handleEmergencyScanStateChange(Message msg)1641 private void handleEmergencyScanStateChange(Message msg) { 1642 final boolean scanInProgress = msg.arg1 == 1; 1643 final WorkSource requestorWs = (WorkSource) msg.obj; 1644 log("Processing scan state change: " + scanInProgress); 1645 mIsEmergencyScanInProgress = scanInProgress; 1646 if (isInEmergencyModeWhichRequiresWifiDisable()) { 1647 // If wifi was disabled because of emergency mode 1648 // (getConfigWiFiDisableInECBM == true), don't use the 1649 // generic method to handle toggle change since that may put wifi in 1650 // connectivity mode (since wifi toggle may actually be on underneath) 1651 if (getCurrentState() == mDisabledState && scanInProgress) { 1652 // go to scan only mode. 1653 startScanOnlyClientModeManager(requestorWs); 1654 transitionTo(mEnabledState); 1655 } else if (getCurrentState() == mEnabledState && !scanInProgress) { 1656 // shut down to go back to previous state. 1657 stopAllClientModeManagers(); 1658 } 1659 } else { 1660 if (getCurrentState() == mDisabledState) { 1661 handleStaToggleChangeInDisabledState(requestorWs); 1662 } else if (getCurrentState() == mEnabledState) { 1663 handleStaToggleChangeInEnabledState(requestorWs); 1664 } 1665 } 1666 } 1667 1668 @Override processMessage(Message msg)1669 public final boolean processMessage(Message msg) { 1670 // potentially enter emergency mode 1671 if (msg.what == CMD_EMERGENCY_CALL_STATE_CHANGED 1672 || msg.what == CMD_EMERGENCY_MODE_CHANGED) { 1673 handleEmergencyModeStateChange(msg); 1674 return HANDLED; 1675 } else if (msg.what == CMD_EMERGENCY_SCAN_STATE_CHANGED) { 1676 // emergency scans need to be allowed even in emergency mode. 1677 handleEmergencyScanStateChange(msg); 1678 return HANDLED; 1679 } else if (isInEmergencyMode()) { 1680 return processMessageInEmergencyMode(msg); 1681 } else { 1682 // not in emergency mode, process messages normally 1683 return processMessageFiltered(msg); 1684 } 1685 } 1686 processMessageFiltered(Message msg)1687 protected abstract boolean processMessageFiltered(Message msg); 1688 } 1689 1690 class DefaultState extends State { 1691 @Override processMessage(Message msg)1692 public boolean processMessage(Message msg) { 1693 switch (msg.what) { 1694 case CMD_SCAN_ALWAYS_MODE_CHANGED: 1695 case CMD_EMERGENCY_SCAN_STATE_CHANGED: 1696 case CMD_WIFI_TOGGLED: 1697 case CMD_STA_STOPPED: 1698 case CMD_STA_START_FAILURE: 1699 case CMD_AP_STOPPED: 1700 case CMD_AP_START_FAILURE: 1701 case CMD_RECOVERY_RESTART_WIFI: 1702 case CMD_RECOVERY_RESTART_WIFI_CONTINUE: 1703 case CMD_DEFERRED_RECOVERY_RESTART_WIFI: 1704 case CMD_REMOVE_ADDITIONAL_CLIENT_MODE_MANAGER: 1705 break; 1706 case CMD_REQUEST_ADDITIONAL_CLIENT_MODE_MANAGER: 1707 AdditionalClientModeManagerRequestInfo requestInfo = 1708 (AdditionalClientModeManagerRequestInfo) msg.obj; 1709 requestInfo.listener.onAnswer(null); 1710 break; 1711 case CMD_RECOVERY_DISABLE_WIFI: 1712 log("Recovery has been throttled, disable wifi"); 1713 shutdownWifi(); 1714 // onStopped will move the state machine to "DisabledState". 1715 break; 1716 case CMD_AIRPLANE_TOGGLED: 1717 if (mSettingsStore.isAirplaneModeOn()) { 1718 log("Airplane mode toggled, shutdown all modes"); 1719 shutdownWifi(); 1720 // onStopped will move the state machine to "DisabledState". 1721 } else { 1722 log("Airplane mode disabled, determine next state"); 1723 if (shouldEnableSta()) { 1724 startPrimaryOrScanOnlyClientModeManager( 1725 // Assumes user toggled it on from settings before. 1726 mFacade.getSettingsWorkSource(mContext)); 1727 transitionTo(mEnabledState); 1728 } 1729 // wifi should remain disabled, do not need to transition 1730 } 1731 break; 1732 case CMD_UPDATE_AP_CAPABILITY: 1733 updateCapabilityToSoftApModeManager((SoftApCapability) msg.obj); 1734 break; 1735 case CMD_UPDATE_AP_CONFIG: 1736 updateConfigurationToSoftApModeManager((SoftApConfiguration) msg.obj); 1737 break; 1738 default: 1739 throw new RuntimeException("WifiController.handleMessage " + msg.what); 1740 } 1741 return HANDLED; 1742 } 1743 } 1744 shouldEnableScanOnlyMode()1745 private boolean shouldEnableScanOnlyMode() { 1746 return (mWifiPermissionsUtil.isLocationModeEnabled() 1747 && mSettingsStore.isScanAlwaysAvailable()) 1748 || mIsEmergencyScanInProgress; 1749 } 1750 shouldEnableSta()1751 private boolean shouldEnableSta() { 1752 return mSettingsStore.isWifiToggleEnabled() || shouldEnableScanOnlyMode(); 1753 } 1754 handleStaToggleChangeInDisabledState(WorkSource requestorWs)1755 private void handleStaToggleChangeInDisabledState(WorkSource requestorWs) { 1756 if (shouldEnableSta()) { 1757 startPrimaryOrScanOnlyClientModeManager(requestorWs); 1758 transitionTo(mEnabledState); 1759 } 1760 } 1761 handleStaToggleChangeInEnabledState(WorkSource requestorWs)1762 private void handleStaToggleChangeInEnabledState(WorkSource requestorWs) { 1763 if (shouldEnableSta()) { 1764 if (hasAnyClientModeManager()) { 1765 switchAllPrimaryOrScanOnlyClientModeManagers(); 1766 } else { 1767 startPrimaryOrScanOnlyClientModeManager(requestorWs); 1768 } 1769 } else { 1770 stopAllClientModeManagers(); 1771 } 1772 } 1773 1774 class DisabledState extends BaseState { 1775 @Override enter()1776 public void enter() { 1777 log("DisabledState.enter()"); 1778 super.enter(); 1779 if (hasAnyModeManager()) { 1780 Log.e(TAG, "Entered DisabledState, but has active mode managers"); 1781 } 1782 } 1783 1784 @Override exit()1785 public void exit() { 1786 log("DisabledState.exit()"); 1787 super.exit(); 1788 } 1789 1790 @Override processMessageFiltered(Message msg)1791 public boolean processMessageFiltered(Message msg) { 1792 switch (msg.what) { 1793 case CMD_WIFI_TOGGLED: 1794 case CMD_SCAN_ALWAYS_MODE_CHANGED: 1795 handleStaToggleChangeInDisabledState((WorkSource) msg.obj); 1796 break; 1797 case CMD_SET_AP: 1798 // note: CMD_SET_AP is handled/dropped in ECM mode - will not start here 1799 if (msg.arg1 == 1) { 1800 Pair<SoftApModeConfiguration, WorkSource> softApConfigAndWs = 1801 (Pair) msg.obj; 1802 startSoftApModeManager( 1803 softApConfigAndWs.first, softApConfigAndWs.second); 1804 transitionTo(mEnabledState); 1805 } 1806 break; 1807 case CMD_RECOVERY_RESTART_WIFI: 1808 log("Recovery triggered, already in disabled state"); 1809 sendMessageDelayed(CMD_RECOVERY_RESTART_WIFI_CONTINUE, 1810 Collections.emptyList(), readWifiRecoveryDelay()); 1811 break; 1812 case CMD_DEFERRED_RECOVERY_RESTART_WIFI: 1813 // wait mRecoveryDelayMillis for letting driver clean reset. 1814 sendMessageDelayed(CMD_RECOVERY_RESTART_WIFI_CONTINUE, 1815 msg.obj, readWifiRecoveryDelay()); 1816 break; 1817 case CMD_RECOVERY_RESTART_WIFI_CONTINUE: 1818 log("Recovery in progress, start wifi"); 1819 List<ActiveModeManager> modeManagersBeforeRecovery = (List) msg.obj; 1820 // No user controlled mode managers before recovery, so check if wifi 1821 // was toggled on. 1822 if (modeManagersBeforeRecovery.isEmpty()) { 1823 if (shouldEnableSta()) { 1824 startPrimaryOrScanOnlyClientModeManager( 1825 // Assumes user toggled it on from settings before. 1826 mFacade.getSettingsWorkSource(mContext)); 1827 transitionTo(mEnabledState); 1828 } 1829 break; 1830 } 1831 for (ActiveModeManager activeModeManager : modeManagersBeforeRecovery) { 1832 if (activeModeManager instanceof ConcreteClientModeManager) { 1833 startPrimaryOrScanOnlyClientModeManager( 1834 activeModeManager.getRequestorWs()); 1835 } else if (activeModeManager instanceof SoftApManager) { 1836 SoftApManager softApManager = (SoftApManager) activeModeManager; 1837 startSoftApModeManager( 1838 softApManager.getSoftApModeConfiguration(), 1839 softApManager.getRequestorWs()); 1840 } 1841 } 1842 transitionTo(mEnabledState); 1843 int numCallbacks = mRestartCallbacks.beginBroadcast(); 1844 for (int i = 0; i < numCallbacks; i++) { 1845 try { 1846 mRestartCallbacks.getBroadcastItem(i).onSubsystemRestarted(); 1847 } catch (RemoteException e) { 1848 Log.e(TAG, "Failure calling onSubsystemRestarted" + e); 1849 } 1850 } 1851 mRestartCallbacks.finishBroadcast(); 1852 break; 1853 default: 1854 return NOT_HANDLED; 1855 } 1856 return HANDLED; 1857 } 1858 } 1859 1860 class EnabledState extends BaseState { 1861 1862 private boolean mIsDisablingDueToAirplaneMode; 1863 1864 @Override enter()1865 public void enter() { 1866 log("EnabledState.enter()"); 1867 super.enter(); 1868 if (!hasAnyModeManager()) { 1869 Log.e(TAG, "Entered EnabledState, but no active mode managers"); 1870 } 1871 mIsDisablingDueToAirplaneMode = false; 1872 } 1873 1874 @Override exit()1875 public void exit() { 1876 log("EnabledState.exit()"); 1877 if (hasAnyModeManager()) { 1878 Log.e(TAG, "Exiting EnabledState, but has active mode managers"); 1879 } 1880 super.exit(); 1881 } 1882 isClientModeManagerConnectedOrConnectingToBssid( @onNull ClientModeManager clientModeManager, @NonNull String ssid, @NonNull String bssid)1883 private boolean isClientModeManagerConnectedOrConnectingToBssid( 1884 @NonNull ClientModeManager clientModeManager, 1885 @NonNull String ssid, @NonNull String bssid) { 1886 WifiConfiguration connectedOrConnectingWifiConfiguration = coalesce( 1887 clientModeManager.getConnectingWifiConfiguration(), 1888 clientModeManager.getConnectedWifiConfiguration()); 1889 String connectedOrConnectingBssid = coalesce( 1890 clientModeManager.getConnectingBssid(), 1891 clientModeManager.getConnectedBssid()); 1892 String connectedOrConnectingSsid = 1893 connectedOrConnectingWifiConfiguration == null 1894 ? null : connectedOrConnectingWifiConfiguration.SSID; 1895 return Objects.equals(ssid, connectedOrConnectingSsid) 1896 && Objects.equals(bssid, connectedOrConnectingBssid); 1897 } 1898 1899 @Nullable findAnyClientModeManagerConnectingOrConnectedToBssid( @onNull String ssid, @Nullable String bssid)1900 private ConcreteClientModeManager findAnyClientModeManagerConnectingOrConnectedToBssid( 1901 @NonNull String ssid, @Nullable String bssid) { 1902 if (bssid == null) { 1903 return null; 1904 } 1905 for (ConcreteClientModeManager cmm : mClientModeManagers) { 1906 if (isClientModeManagerConnectedOrConnectingToBssid(cmm, ssid, bssid)) { 1907 return cmm; 1908 } 1909 } 1910 return null; 1911 } 1912 handleAdditionalClientModeManagerRequest( @onNull AdditionalClientModeManagerRequestInfo requestInfo)1913 private void handleAdditionalClientModeManagerRequest( 1914 @NonNull AdditionalClientModeManagerRequestInfo requestInfo) { 1915 ClientModeManager primaryManager = getPrimaryClientModeManager(); 1916 if (requestInfo.clientRole == ROLE_CLIENT_SECONDARY_TRANSIENT 1917 && mDppManager.isSessionInProgress()) { 1918 // When MBB is triggered, we could end up switching the primary interface 1919 // after completion. So if we have any DPP session in progress, they will fail 1920 // when the previous primary iface is removed after MBB completion. 1921 Log.v(TAG, "DPP session in progress, fallback to single STA behavior " 1922 + "using primary ClientModeManager=" + primaryManager); 1923 requestInfo.listener.onAnswer(primaryManager); 1924 return; 1925 } 1926 ConcreteClientModeManager cmmForSameBssid = 1927 findAnyClientModeManagerConnectingOrConnectedToBssid( 1928 requestInfo.ssid, requestInfo.bssid); 1929 if (cmmForSameBssid != null) { 1930 // Can't allow 2 client mode managers triggering connection to same bssid. 1931 Log.v(TAG, "Already connected to bssid=" + requestInfo.bssid 1932 + " on ClientModeManager=" + cmmForSameBssid); 1933 if (cmmForSameBssid.getRole() == ROLE_CLIENT_PRIMARY) { 1934 // fallback to single STA behavior. 1935 requestInfo.listener.onAnswer(cmmForSameBssid); 1936 return; 1937 } 1938 // Existing secondary CMM connected to the same ssid/bssid. 1939 if (!canRequestMoreClientModeManagersInRole( 1940 requestInfo.requestorWs, requestInfo.clientRole)) { 1941 Log.e(TAG, "New request cannot override existing request on " 1942 + "ClientModeManager=" + cmmForSameBssid); 1943 // If the new request does not have priority over the existing request, 1944 // reject it since we cannot have 2 CMM's connected to same ssid/bssid. 1945 requestInfo.listener.onAnswer(null); 1946 return; 1947 } 1948 // If the new request has a higher priority over the existing one, change it's 1949 // role and send it to the new client. 1950 // Switch role for non primary CMM & wait for it to complete before 1951 // handing it to the requestor. 1952 switchRoleForAdditionalClientModeManager( 1953 cmmForSameBssid, requestInfo.clientRole, requestInfo.listener, 1954 requestInfo.requestorWs); 1955 return; 1956 } 1957 1958 ClientModeManager cmmForSameRole = 1959 getClientModeManagerInRole(requestInfo.clientRole); 1960 if (cmmForSameRole != null) { 1961 // Already have a client mode manager in the requested role. 1962 // Note: This logic results in the framework not supporting more than 1 CMM in 1963 // the same role concurrently. There is no use-case for that currently & 1964 // none of the clients (i.e WifiNetworkFactory, WifiConnectivityManager, etc) 1965 // are ready to support that either. If this assumption changes in the future 1966 // when the device supports 3 STA's for example, change this logic! 1967 Log.v(TAG, "Already exists ClientModeManager for role: " + cmmForSameRole); 1968 requestInfo.listener.onAnswer(cmmForSameRole); 1969 return; 1970 } 1971 if (canRequestMoreClientModeManagersInRole( 1972 requestInfo.requestorWs, requestInfo.clientRole)) { 1973 // Can create an additional client mode manager. 1974 Log.v(TAG, "Starting a new ClientModeManager"); 1975 startAdditionalClientModeManager( 1976 requestInfo.clientRole, 1977 requestInfo.listener, requestInfo.requestorWs); 1978 return; 1979 } 1980 // Fall back to single STA behavior. 1981 Log.v(TAG, "Falling back to single STA behavior using primary ClientModeManager=" 1982 + primaryManager); 1983 requestInfo.listener.onAnswer(primaryManager); 1984 } 1985 1986 @Override processMessageFiltered(Message msg)1987 public boolean processMessageFiltered(Message msg) { 1988 switch (msg.what) { 1989 case CMD_WIFI_TOGGLED: 1990 case CMD_SCAN_ALWAYS_MODE_CHANGED: 1991 handleStaToggleChangeInEnabledState((WorkSource) msg.obj); 1992 break; 1993 case CMD_REQUEST_ADDITIONAL_CLIENT_MODE_MANAGER: 1994 handleAdditionalClientModeManagerRequest( 1995 (AdditionalClientModeManagerRequestInfo) msg.obj); 1996 break; 1997 case CMD_REMOVE_ADDITIONAL_CLIENT_MODE_MANAGER: 1998 stopAdditionalClientModeManager((ClientModeManager) msg.obj); 1999 break; 2000 case CMD_SET_AP: 2001 // note: CMD_SET_AP is handled/dropped in ECM mode - will not start here 2002 if (msg.arg1 == 1) { 2003 Pair<SoftApModeConfiguration, WorkSource> softApConfigAndWs = 2004 (Pair) msg.obj; 2005 startSoftApModeManager( 2006 softApConfigAndWs.first, softApConfigAndWs.second); 2007 } else { 2008 stopSoftApModeManagers(msg.arg2); 2009 } 2010 break; 2011 case CMD_AIRPLANE_TOGGLED: 2012 // airplane mode toggled on is handled in the default state 2013 if (mSettingsStore.isAirplaneModeOn()) { 2014 mIsDisablingDueToAirplaneMode = true; 2015 return NOT_HANDLED; 2016 } else { 2017 if (mIsDisablingDueToAirplaneMode) { 2018 // Previous airplane mode toggle on is being processed, defer the 2019 // message toggle off until previous processing is completed. 2020 // Once previous airplane mode toggle is complete, we should 2021 // transition to DisabledState. There, we will process the deferred 2022 // airplane mode toggle message to disable airplane mode. 2023 deferMessage(msg); 2024 } else { 2025 // when airplane mode is toggled off, but wifi is on, we can keep it 2026 // on 2027 log("airplane mode toggled - and airplane mode is off. return " 2028 + "handled"); 2029 } 2030 return HANDLED; 2031 } 2032 case CMD_AP_STOPPED: 2033 case CMD_AP_START_FAILURE: 2034 if (!hasAnyModeManager()) { 2035 if (shouldEnableSta()) { 2036 log("SoftAp disabled, start client mode"); 2037 startPrimaryOrScanOnlyClientModeManager( 2038 // Assumes user toggled it on from settings before. 2039 mFacade.getSettingsWorkSource(mContext)); 2040 } else { 2041 log("SoftAp mode disabled, return to DisabledState"); 2042 transitionTo(mDisabledState); 2043 } 2044 } else { 2045 log("AP disabled, remain in EnabledState."); 2046 } 2047 break; 2048 case CMD_STA_START_FAILURE: 2049 case CMD_STA_STOPPED: 2050 // Client mode stopped. Head to Disabled to wait for next command if there 2051 // no active mode managers. 2052 if (!hasAnyModeManager()) { 2053 log("STA disabled, return to DisabledState."); 2054 transitionTo(mDisabledState); 2055 } else { 2056 log("STA disabled, remain in EnabledState."); 2057 } 2058 break; 2059 case CMD_RECOVERY_RESTART_WIFI: { 2060 final String bugTitle; 2061 final String bugDetail = (String) msg.obj; 2062 if (TextUtils.isEmpty(bugDetail)) { 2063 bugTitle = "Wi-Fi BugReport"; 2064 } else { 2065 bugTitle = "Wi-Fi BugReport: " + bugDetail; 2066 } 2067 log("Recovery triggered, disable wifi"); 2068 boolean bugReportRequested = msg.arg2 != 0; 2069 if (bugReportRequested) { 2070 mHandler.post(() -> 2071 mWifiDiagnostics.takeBugReport(bugTitle, bugDetail)); 2072 } 2073 // Store all instances of tethered SAP + scan only/primary STA mode managers 2074 List<ActiveModeManager> modeManagersBeforeRecovery = Stream.concat( 2075 mClientModeManagers.stream() 2076 .filter(m -> ROLE_CLIENT_SCAN_ONLY.equals(m.getRole()) 2077 || ROLE_CLIENT_PRIMARY.equals(m.getRole())), 2078 mSoftApManagers.stream() 2079 .filter(m -> ROLE_SOFTAP_TETHERED.equals(m.getRole()))) 2080 .collect(Collectors.toList()); 2081 deferMessage(obtainMessage(CMD_DEFERRED_RECOVERY_RESTART_WIFI, 2082 modeManagersBeforeRecovery)); 2083 int numCallbacks = mRestartCallbacks.beginBroadcast(); 2084 for (int i = 0; i < numCallbacks; i++) { 2085 try { 2086 mRestartCallbacks.getBroadcastItem(i).onSubsystemRestarting(); 2087 } catch (RemoteException e) { 2088 Log.e(TAG, "Failure calling onSubsystemRestarting" + e); 2089 } 2090 } 2091 mRestartCallbacks.finishBroadcast(); 2092 shutdownWifi(); 2093 // onStopped will move the state machine to "DisabledState". 2094 break; 2095 } 2096 default: 2097 return NOT_HANDLED; 2098 } 2099 return HANDLED; 2100 } 2101 } 2102 } 2103 coalesce(T a, T b)2104 private static <T> T coalesce(T a, T b) { 2105 return a != null ? a : b; 2106 } 2107 } 2108