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.WIFI_STATE_ENABLED; 20 import static android.net.wifi.WifiManager.WIFI_STATE_ENABLING; 21 22 import android.annotation.NonNull; 23 import android.annotation.Nullable; 24 import android.content.Context; 25 import android.content.Intent; 26 import android.net.ConnectivityManager; 27 import android.net.ConnectivityManager.NetworkCallback; 28 import android.net.DhcpResultsParcelable; 29 import android.net.MacAddress; 30 import android.net.Network; 31 import android.net.NetworkCapabilities; 32 import android.net.NetworkRequest; 33 import android.net.wifi.IWifiConnectedNetworkScorer; 34 import android.net.wifi.WifiAnnotations; 35 import android.net.wifi.WifiConfiguration; 36 import android.net.wifi.WifiInfo; 37 import android.net.wifi.WifiManager; 38 import android.net.wifi.WifiManager.DeviceMobilityState; 39 import android.net.wifi.hotspot2.IProvisioningCallback; 40 import android.net.wifi.hotspot2.OsuProvider; 41 import android.net.wifi.nl80211.DeviceWiphyCapabilities; 42 import android.os.Handler; 43 import android.os.IBinder; 44 import android.os.Looper; 45 import android.os.Message; 46 import android.os.PersistableBundle; 47 import android.os.UserHandle; 48 import android.os.WorkSource; 49 import android.telephony.AccessNetworkConstants; 50 import android.telephony.CarrierConfigManager; 51 import android.telephony.SubscriptionInfo; 52 import android.telephony.SubscriptionManager; 53 import android.telephony.ims.ImsException; 54 import android.telephony.ims.ImsMmTelManager; 55 import android.telephony.ims.ImsReasonInfo; 56 import android.telephony.ims.RegistrationManager; 57 import android.telephony.ims.feature.MmTelFeature; 58 import android.telephony.ims.stub.ImsRegistrationImplBase; 59 import android.text.TextUtils; 60 import android.util.Log; 61 62 import com.android.internal.util.IState; 63 import com.android.internal.util.State; 64 import com.android.internal.util.StateMachine; 65 import com.android.modules.utils.HandlerExecutor; 66 import com.android.server.wifi.WifiNative.InterfaceCallback; 67 import com.android.server.wifi.WifiNative.InterfaceEventCallback; 68 import com.android.server.wifi.WifiNative.RxFateReport; 69 import com.android.server.wifi.WifiNative.TxFateReport; 70 import com.android.server.wifi.util.ActionListenerWrapper; 71 import com.android.server.wifi.util.StateMachineObituary; 72 import com.android.wifi.resources.R; 73 74 import java.io.FileDescriptor; 75 import java.io.PrintWriter; 76 import java.util.ArrayDeque; 77 import java.util.ArrayList; 78 import java.util.List; 79 import java.util.Set; 80 81 /** 82 * Manage WiFi in Client Mode where we connect to configured networks and in Scan Only Mode where 83 * we do not connect to configured networks but do perform scanning. 84 * 85 * An instance of this class is active to manage each client interface. This is in contrast to 86 * {@link DefaultClientModeManager} which handles calls when no client interfaces are active. 87 * 88 * This class will dynamically instantiate {@link ClientModeImpl} when it enters client mode, and 89 * tear it down when it exits client mode. No instance of ClientModeImpl will be active in 90 * scan-only mode, instead {@link ScanOnlyModeImpl} will be used to respond to calls. 91 * 92 * <pre> 93 * ActiveModeWarden 94 * / \ 95 * / \ 96 * ConcreteClientModeManager DefaultClientModeManager 97 * (Client Mode + Scan Only Mode) (Wifi off) 98 * / \ 99 * / \ 100 * ClientModeImpl ScanOnlyModeImpl 101 * </pre> 102 */ 103 public class ConcreteClientModeManager implements ClientModeManager { 104 private static final String TAG = "WifiClientModeManager"; 105 106 private final ClientModeStateMachine mStateMachine; 107 108 private final Context mContext; 109 private final Clock mClock; 110 private final WifiNative mWifiNative; 111 private final WifiMetrics mWifiMetrics; 112 private final WakeupController mWakeupController; 113 private final WifiInjector mWifiInjector; 114 private final SelfRecovery mSelfRecovery; 115 private final WifiGlobals mWifiGlobals; 116 private final DefaultClientModeManager mDefaultClientModeManager; 117 private final ClientModeManagerBroadcastQueue mBroadcastQueue; 118 private final long mId; 119 private final Graveyard mGraveyard = new Graveyard(); 120 121 private String mClientInterfaceName; 122 private boolean mIfaceIsUp = false; 123 private boolean mShouldReduceNetworkScore = false; 124 private final DeferStopHandler mDeferStopHandler; 125 @Nullable 126 private ClientRole mRole = null; 127 @Nullable 128 private ClientRole mPreviousRole = null; 129 private long mLastRoleChangeSinceBootMs = 0; 130 @Nullable 131 private WorkSource mRequestorWs = null; 132 @NonNull 133 private Listener<ConcreteClientModeManager> mModeListener; 134 /** Caches the latest role change request. This is needed for the IMS dereg delay */ 135 @Nullable 136 private RoleChangeInfo mTargetRoleChangeInfo; 137 private boolean mVerboseLoggingEnabled = false; 138 private int mActiveSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; 139 private boolean mWifiStateChangeBroadcastEnabled = true; 140 private boolean mSecondaryInternet = false; 141 private boolean mIsDbs = false; 142 /** 143 * mClientModeImpl is only non-null when in {@link ClientModeStateMachine.ConnectModeState} - 144 * it will be null in all other states 145 */ 146 @Nullable 147 private ClientModeImpl mClientModeImpl = null; 148 149 @Nullable 150 private ScanOnlyModeImpl mScanOnlyModeImpl = null; 151 152 private boolean mIsStopped = true; 153 ConcreteClientModeManager( Context context, @NonNull Looper looper, Clock clock, WifiNative wifiNative, @NonNull Listener<ConcreteClientModeManager> listener, WifiMetrics wifiMetrics, WakeupController wakeupController, WifiInjector wifiInjector, SelfRecovery selfRecovery, WifiGlobals wifiGlobals, DefaultClientModeManager defaultClientModeManager, long id, @NonNull WorkSource requestorWs, @NonNull ClientRole role, @NonNull ClientModeManagerBroadcastQueue broadcastQueue, boolean verboseLoggingEnabled)154 ConcreteClientModeManager( 155 Context context, @NonNull Looper looper, Clock clock, 156 WifiNative wifiNative, @NonNull Listener<ConcreteClientModeManager> listener, 157 WifiMetrics wifiMetrics, 158 WakeupController wakeupController, WifiInjector wifiInjector, 159 SelfRecovery selfRecovery, WifiGlobals wifiGlobals, 160 DefaultClientModeManager defaultClientModeManager, long id, 161 @NonNull WorkSource requestorWs, @NonNull ClientRole role, 162 @NonNull ClientModeManagerBroadcastQueue broadcastQueue, 163 boolean verboseLoggingEnabled) { 164 mContext = context; 165 mClock = clock; 166 mWifiNative = wifiNative; 167 mModeListener = listener; 168 mWifiMetrics = wifiMetrics; 169 mWakeupController = wakeupController; 170 mWifiInjector = wifiInjector; 171 mStateMachine = new ClientModeStateMachine(looper); 172 mDeferStopHandler = new DeferStopHandler(looper); 173 mSelfRecovery = selfRecovery; 174 mWifiGlobals = wifiGlobals; 175 mDefaultClientModeManager = defaultClientModeManager; 176 mId = id; 177 mTargetRoleChangeInfo = new RoleChangeInfo(role, requestorWs, listener); 178 mBroadcastQueue = broadcastQueue; 179 enableVerboseLogging(verboseLoggingEnabled); 180 mStateMachine.sendMessage(ClientModeStateMachine.CMD_START, mTargetRoleChangeInfo); 181 } 182 getTag()183 private String getTag() { 184 return TAG + "[" + mId + ":" + (mClientInterfaceName == null ? "unknown" 185 : mClientInterfaceName) + "]"; 186 } 187 188 /** 189 * Sets whether to send WIFI_STATE_CHANGED broadcast for this ClientModeManager. 190 * @param enabled 191 */ setWifiStateChangeBroadcastEnabled(boolean enabled)192 public void setWifiStateChangeBroadcastEnabled(boolean enabled) { 193 mWifiStateChangeBroadcastEnabled = enabled; 194 } 195 196 /** 197 * Sets whether this ClientModeManager is for secondary STA with internet. 198 * @param secondaryInternet whether the ClientModeManager is for secondary internet. 199 */ setSecondaryInternet(boolean secondaryInternet)200 public void setSecondaryInternet(boolean secondaryInternet) { 201 // TODO: b/197670907 : Add client role ROLE_CLIENT_SECONDARY_INTERNET 202 if (mRole == ROLE_CLIENT_SECONDARY_LONG_LIVED) { 203 mSecondaryInternet = secondaryInternet; 204 } 205 } 206 207 /** 208 * Sets whether this ClientModeManager is for DBS AP multi internet. 209 * @param isDbs whether the ClientModeManager is connecting to to the same SSID as primary. 210 */ setSecondaryInternetDbsAp(boolean isDbs)211 public void setSecondaryInternetDbsAp(boolean isDbs) { 212 // TODO: b/197670907 : Add client role ROLE_CLIENT_SECONDARY_INTERNET 213 if (mRole == ROLE_CLIENT_SECONDARY_LONG_LIVED) { 214 mIsDbs = isDbs; 215 } 216 } 217 218 /** 219 * Returns whether this ClientModeManager is for secondary STA with internet. 220 * @return true if it is for secondary STA with internet. 221 */ isSecondaryInternet()222 public boolean isSecondaryInternet() { 223 return mSecondaryInternet; 224 } 225 226 /** 227 * Returns whether this ClientModeManager is for DBS AP multi internet. 228 * @return true if the ClientModeManager is connecting to to the same SSID as primary. 229 */ isSecondaryInternetDbsAp()230 public boolean isSecondaryInternetDbsAp() { 231 if (!isSecondaryInternet()) { 232 Log.wtf(TAG, "isSecondaryInternetDbsAp called while not secondary internet!?"); 233 (new Throwable()).printStackTrace(); 234 } 235 return mIsDbs; 236 } 237 238 /** 239 * Disconnect from any currently connected networks and stop client mode. 240 */ 241 @Override stop()242 public void stop() { 243 Log.d(getTag(), " currentstate: " + getCurrentStateName()); 244 mTargetRoleChangeInfo = null; 245 if (mIfaceIsUp) { 246 updateConnectModeState(mRole, WifiManager.WIFI_STATE_DISABLING, 247 WifiManager.WIFI_STATE_ENABLED); 248 } else { 249 updateConnectModeState(mRole, WifiManager.WIFI_STATE_DISABLING, 250 WifiManager.WIFI_STATE_ENABLING); 251 } 252 mDeferStopHandler.start(getWifiOffDeferringTimeMs()); 253 } 254 255 private class DeferStopHandler extends Handler { 256 private boolean mIsDeferring = false; 257 private ImsMmTelManager mImsMmTelManager = null; 258 private Looper mLooper = null; 259 private final Runnable mRunnable = () -> continueToStopWifi(); 260 private int mMaximumDeferringTimeMillis = 0; 261 private long mDeferringStartTimeMillis = 0; 262 private ConnectivityManager mConnectivityManager = null; 263 private List<ImsNetworkCallback> mImsNetworks = new ArrayList<>(); 264 private boolean mIsImsNetworkUnregistered = false; 265 266 private final RegistrationManager.RegistrationCallback mImsRegistrationCallback = 267 new RegistrationManager.RegistrationCallback() { 268 @Override 269 public void onRegistered(int imsRadioTech) { 270 Log.d(getTag(), "on IMS registered on type " + imsRadioTech); 271 if (!mIsDeferring) return; 272 273 if (imsRadioTech != AccessNetworkConstants.TRANSPORT_TYPE_WLAN) { 274 continueToStopWifi(); 275 } 276 } 277 278 @Override 279 public void onUnregistered(ImsReasonInfo imsReasonInfo) { 280 Log.d(getTag(), "on IMS unregistered"); 281 mIsImsNetworkUnregistered = true; 282 checkAndContinueToStopWifi(); 283 } 284 }; 285 286 private final class ImsNetworkCallback extends NetworkCallback { 287 private final int mNetworkType; 288 private int mRegisteredImsNetworkCount = 0; 289 290 /** 291 * Constructor for ImsNetworkCallback. 292 * 293 * @param type One of android.net.NetworkCapabilities.NetCapability. 294 */ ImsNetworkCallback(int type)295 ImsNetworkCallback(int type) { 296 mNetworkType = type; 297 } 298 299 @Override onAvailable(Network network)300 public void onAvailable(Network network) { 301 synchronized (this) { 302 Log.d(getTag(), "IMS network available: " + network 303 + ", type: " + mNetworkType); 304 mRegisteredImsNetworkCount++; 305 } 306 } 307 308 @Override onLost(Network network)309 public void onLost(Network network) { 310 synchronized (this) { 311 Log.d(getTag(), "IMS network lost: " + network 312 + " ,isDeferring: " + mIsDeferring 313 + " ,registered IMS network count: " + mRegisteredImsNetworkCount 314 + ", type: " + mNetworkType); 315 mRegisteredImsNetworkCount--; 316 if (mIsDeferring && mRegisteredImsNetworkCount <= 0) { 317 mRegisteredImsNetworkCount = 0; 318 checkAndContinueToStopWifi(); 319 } 320 } 321 } 322 isNetworkLost()323 public boolean isNetworkLost() { 324 return 0 == mRegisteredImsNetworkCount; 325 } 326 } 327 DeferStopHandler(Looper looper)328 DeferStopHandler(Looper looper) { 329 super(looper); 330 mLooper = looper; 331 mConnectivityManager = mContext.getSystemService(ConnectivityManager.class); 332 } 333 start(int delayMs)334 public void start(int delayMs) { 335 if (mIsDeferring) return; 336 337 mMaximumDeferringTimeMillis = delayMs; 338 mDeferringStartTimeMillis = mClock.getElapsedSinceBootMillis(); 339 // Most cases don't need delay, check it first to avoid unnecessary work. 340 if (delayMs == 0) { 341 continueToStopWifi(); 342 return; 343 } 344 345 mImsMmTelManager = ImsMmTelManager.createForSubscriptionId(mActiveSubId); 346 if (mImsMmTelManager == null || !postDelayed(mRunnable, delayMs)) { 347 // if no delay or failed to add runnable, stop Wifi immediately. 348 continueToStopWifi(); 349 return; 350 } 351 352 mIsDeferring = true; 353 Log.d(getTag(), "Start DeferWifiOff handler with deferring time " 354 + delayMs + " ms for subId: " + mActiveSubId); 355 try { 356 mImsMmTelManager.registerImsRegistrationCallback( 357 new HandlerExecutor(new Handler(mLooper)), 358 mImsRegistrationCallback); 359 } catch (RuntimeException | ImsException e) { 360 Log.e(getTag(), "registerImsRegistrationCallback failed", e); 361 continueToStopWifi(); 362 return; 363 } 364 365 registerImsNetworkCallback(NetworkCapabilities.NET_CAPABILITY_IMS); 366 registerImsNetworkCallback(NetworkCapabilities.NET_CAPABILITY_EIMS); 367 } 368 registerImsNetworkCallback(int imsType)369 private void registerImsNetworkCallback(int imsType) { 370 NetworkRequest imsRequest = new NetworkRequest.Builder() 371 .addCapability(imsType) 372 .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) 373 .build(); 374 ImsNetworkCallback imsCallback = new ImsNetworkCallback(imsType); 375 mConnectivityManager.registerNetworkCallback(imsRequest, imsCallback, 376 new Handler(mLooper)); 377 mImsNetworks.add(imsCallback); 378 } 379 checkAndContinueToStopWifi()380 private void checkAndContinueToStopWifi() { 381 if (!mIsImsNetworkUnregistered) return; 382 383 for (ImsNetworkCallback c: mImsNetworks) { 384 if (!c.isNetworkLost()) return; 385 } 386 387 // Add delay for targets where IMS PDN down at modem takes additional delay. 388 int delay = mContext.getResources() 389 .getInteger(R.integer.config_wifiDelayDisconnectOnImsLostMs); 390 if (delay == 0 || !postDelayed(mRunnable, delay)) { 391 continueToStopWifi(); 392 } 393 } 394 continueToStopWifi()395 private void continueToStopWifi() { 396 Log.d(getTag(), "The target role change info " + mTargetRoleChangeInfo); 397 398 int deferringDurationMillis = 399 (int) (mClock.getElapsedSinceBootMillis() - mDeferringStartTimeMillis); 400 boolean isTimedOut = mMaximumDeferringTimeMillis > 0 401 && deferringDurationMillis >= mMaximumDeferringTimeMillis; 402 if (mTargetRoleChangeInfo == null) { 403 Log.d(getTag(), "Continue to stop wifi"); 404 mStateMachine.captureObituaryAndQuitNow(); 405 mWifiMetrics.noteWifiOff(mIsDeferring, isTimedOut, deferringDurationMillis); 406 } else if (mTargetRoleChangeInfo.role == ROLE_CLIENT_SCAN_ONLY) { 407 if (!mWifiNative.switchClientInterfaceToScanMode( 408 mClientInterfaceName, mTargetRoleChangeInfo.requestorWs)) { 409 mModeListener.onStartFailure(ConcreteClientModeManager.this); 410 updateConnectModeState(mRole, WifiManager.WIFI_STATE_UNKNOWN, 411 WifiManager.WIFI_STATE_DISABLING); 412 updateConnectModeState(mRole, WifiManager.WIFI_STATE_DISABLED, 413 WifiManager.WIFI_STATE_UNKNOWN); 414 takeBugReportInterfaceFailureIfNeeded( 415 "Wi-Fi BugReport (STA interface failure):", 416 "Fail to switch to scan-only mode in started state"); 417 } else { 418 mStateMachine.sendMessage( 419 ClientModeStateMachine.CMD_SWITCH_TO_SCAN_ONLY_MODE_CONTINUE, 420 mTargetRoleChangeInfo); 421 mWifiMetrics.noteWifiOff(mIsDeferring, isTimedOut, deferringDurationMillis); 422 } 423 } else { 424 updateConnectModeState(mRole, WifiManager.WIFI_STATE_ENABLED, 425 WifiManager.WIFI_STATE_DISABLING); 426 } 427 428 if (!mIsDeferring) return; 429 430 Log.d(getTag(), "Stop DeferWifiOff handler."); 431 removeCallbacks(mRunnable); 432 if (mImsMmTelManager != null) { 433 try { 434 mImsMmTelManager.unregisterImsRegistrationCallback(mImsRegistrationCallback); 435 } catch (RuntimeException e) { 436 Log.e(getTag(), "unregisterImsRegistrationCallback failed", e); 437 } 438 } 439 440 if (mConnectivityManager != null && mImsNetworks.size() > 0) { 441 for (ImsNetworkCallback c: mImsNetworks) { 442 mConnectivityManager.unregisterNetworkCallback(c); 443 } 444 mImsNetworks.clear(); 445 } 446 447 mIsDeferring = false; 448 mIsImsNetworkUnregistered = false; 449 } 450 } 451 isAnyImsServiceOverWlanAvailable(int subId)452 private boolean isAnyImsServiceOverWlanAvailable(int subId) { 453 ImsMmTelManager imsMmTelManager = ImsMmTelManager.createForSubscriptionId(subId); 454 try { 455 int[] possibleServiceOverWlan = new int[] { 456 MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE, 457 MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO, 458 MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT, 459 MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_SMS, 460 MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_CALL_COMPOSER, 461 }; 462 for (int i: possibleServiceOverWlan) { 463 if (imsMmTelManager.isAvailable(i, 464 ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN)) { 465 return true; 466 } 467 } 468 } catch (RuntimeException ex) { 469 Log.e(TAG, "IMS Manager is not available.", ex); 470 } 471 return false; 472 } 473 474 /** 475 * Get deferring time before turning off WiFi. 476 */ getWifiOffDeferringTimeMs()477 private int getWifiOffDeferringTimeMs() { 478 if (mRole != ROLE_CLIENT_PRIMARY && !isSecondaryInternet()) { 479 Log.d(getTag(), "Do not defer stop for non-internet providing CMMs"); 480 return 0; 481 } 482 SubscriptionManager subscriptionManager = 483 mContext.getSystemService(SubscriptionManager.class); 484 if (subscriptionManager == null) { 485 Log.d(getTag(), "SubscriptionManager not found"); 486 return 0; 487 } 488 489 List<SubscriptionInfo> subInfoList = subscriptionManager 490 .getCompleteActiveSubscriptionInfoList(); 491 if (subInfoList == null) { 492 Log.d(getTag(), "Active SubscriptionInfo list not found"); 493 return 0; 494 } 495 496 // Get the maximum delay for the active subscription latched on IWLAN. 497 int maxDelay = 0; 498 for (SubscriptionInfo subInfo : subInfoList) { 499 int curDelay = getWifiOffDeferringTimeMs(subInfo.getSubscriptionId()); 500 if (curDelay > maxDelay) { 501 maxDelay = curDelay; 502 mActiveSubId = subInfo.getSubscriptionId(); 503 } 504 } 505 return maxDelay; 506 } 507 getWifiOffDeferringTimeMs(int subId)508 private int getWifiOffDeferringTimeMs(int subId) { 509 if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { 510 Log.d(getTag(), "Invalid Subscription ID: " + subId); 511 return 0; 512 } 513 514 // If no IMS service over WLAN, no delay 515 if (!isAnyImsServiceOverWlanAvailable(subId)) { 516 Log.d(getTag(), "IMS not registered over IWLAN for subId: " + subId); 517 return 0; 518 } 519 520 CarrierConfigManager configManager = mContext.getSystemService(CarrierConfigManager.class); 521 PersistableBundle config = configManager.getConfigForSubId(subId); 522 return (config != null) 523 ? config.getInt(CarrierConfigManager.Ims.KEY_WIFI_OFF_DEFERRING_TIME_MILLIS_INT) 524 : 0; 525 } 526 527 @Override getRole()528 @Nullable public ClientRole getRole() { 529 return mRole; 530 } 531 532 /** 533 * Get the role this ClientModeManager is expected to become. 534 */ getTargetRole()535 @Nullable public ClientRole getTargetRole() { 536 return mTargetRoleChangeInfo == null ? null : mTargetRoleChangeInfo.role; 537 } 538 539 @Override getPreviousRole()540 @Nullable public ClientRole getPreviousRole() { 541 return mPreviousRole; 542 } 543 544 @Override getLastRoleChangeSinceBootMs()545 public long getLastRoleChangeSinceBootMs() { 546 return mLastRoleChangeSinceBootMs; 547 } 548 549 /** 550 * Class to hold info needed for role change. 551 */ 552 private static class RoleChangeInfo { 553 @Nullable public final ClientRole role; 554 @Nullable public final WorkSource requestorWs; 555 @Nullable public final Listener<ConcreteClientModeManager> modeListener; 556 RoleChangeInfo(@ullable ClientRole role)557 RoleChangeInfo(@Nullable ClientRole role) { 558 this(role, null, null); 559 } 560 RoleChangeInfo(@ullable ClientRole role, @Nullable WorkSource requestorWs, @Nullable Listener<ConcreteClientModeManager> modeListener)561 RoleChangeInfo(@Nullable ClientRole role, @Nullable WorkSource requestorWs, 562 @Nullable Listener<ConcreteClientModeManager> modeListener) { 563 this.role = role; 564 this.requestorWs = requestorWs; 565 this.modeListener = modeListener; 566 } 567 568 @Override toString()569 public String toString() { 570 return "Role: " + role + ", RequestorWs: " + requestorWs 571 + ", ModeListener: " + modeListener; 572 } 573 } 574 575 /** Set the role of this ClientModeManager */ setRole(@onNull ClientRole role, @NonNull WorkSource requestorWs)576 public void setRole(@NonNull ClientRole role, @NonNull WorkSource requestorWs) { 577 setRole(role, requestorWs, null); 578 } 579 580 /** Set the role of this ClientModeManager */ setRole(@onNull ClientRole role, @NonNull WorkSource requestorWs, @Nullable Listener<ConcreteClientModeManager> modeListener)581 public void setRole(@NonNull ClientRole role, @NonNull WorkSource requestorWs, 582 @Nullable Listener<ConcreteClientModeManager> modeListener) { 583 mTargetRoleChangeInfo = new RoleChangeInfo(role, requestorWs, modeListener); 584 if (role == ROLE_CLIENT_SCAN_ONLY) { 585 // Switch client mode manager to scan only mode. 586 mStateMachine.sendMessage( 587 ClientModeStateMachine.CMD_SWITCH_TO_SCAN_ONLY_MODE); 588 } else { 589 // Switch client mode manager to connect mode. 590 mStateMachine.sendMessage( 591 ClientModeStateMachine.CMD_SWITCH_TO_CONNECT_MODE, 592 mTargetRoleChangeInfo); 593 } 594 } 595 596 @Override getInterfaceName()597 public String getInterfaceName() { 598 return mClientInterfaceName; 599 } 600 601 @Override getRequestorWs()602 public WorkSource getRequestorWs() { 603 return mRequestorWs; 604 } 605 606 /** 607 * Keep stopped {@link ClientModeImpl} instances so that they can be dumped to aid debugging. 608 * 609 * TODO(b/160283853): Find a smarter way to evict old ClientModeImpls 610 */ 611 private static class Graveyard { 612 private static final int INSTANCES_TO_KEEP = 3; 613 614 private final ArrayDeque<ClientModeImpl> mClientModeImpls = new ArrayDeque<>(); 615 616 /** 617 * Add this stopped {@link ClientModeImpl} to the graveyard, and evict the oldest 618 * ClientModeImpl if the graveyard is full. 619 */ inter(ClientModeImpl clientModeImpl)620 void inter(ClientModeImpl clientModeImpl) { 621 if (mClientModeImpls.size() == INSTANCES_TO_KEEP) { 622 mClientModeImpls.removeFirst(); 623 } 624 mClientModeImpls.addLast(clientModeImpl); 625 } 626 627 /** Dump the contents of the graveyard. */ dump(FileDescriptor fd, PrintWriter pw, String[] args)628 void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 629 pw.println("Dump of ConcreteClientModeManager.Graveyard"); 630 pw.println("Stopped ClientModeImpls: " + mClientModeImpls.size() + " total"); 631 for (ClientModeImpl clientModeImpl : mClientModeImpls) { 632 clientModeImpl.dump(fd, pw, args); 633 } 634 pw.println(); 635 } 636 hasAllClientModeImplsQuit()637 boolean hasAllClientModeImplsQuit() { 638 for (ClientModeImpl cmi : mClientModeImpls) { 639 if (!cmi.hasQuit()) return false; 640 } 641 return true; 642 } 643 } 644 645 /** 646 * Dump info about this ClientMode manager. 647 */ 648 @Override dump(FileDescriptor fd, PrintWriter pw, String[] args)649 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 650 pw.println("Dump of ClientModeManager id=" + mId); 651 pw.println("current StateMachine mode: " + getCurrentStateName()); 652 pw.println("mRole: " + mRole); 653 pw.println("mPreviousRole: " + mPreviousRole); 654 pw.println("mTargetRoleChangeInfo: " + mTargetRoleChangeInfo); 655 pw.println("mClientInterfaceName: " + mClientInterfaceName); 656 pw.println("mIfaceIsUp: " + mIfaceIsUp); 657 pw.println("mSecondaryInternet: " + mSecondaryInternet); 658 pw.println("mIsDbs: " + mIsDbs); 659 mStateMachine.dump(fd, pw, args); 660 pw.println(); 661 if (mClientModeImpl == null) { 662 pw.println("No active ClientModeImpl instance"); 663 } else { 664 mClientModeImpl.dump(fd, pw, args); 665 } 666 mGraveyard.dump(fd, pw, args); 667 pw.println(); 668 } 669 getCurrentStateName()670 private String getCurrentStateName() { 671 IState currentState = mStateMachine.getCurrentState(); 672 673 if (currentState != null) { 674 return currentState.getName(); 675 } 676 677 return "StateMachine not active"; 678 } 679 680 /** 681 * Update Wifi state and send the broadcast. 682 * 683 * @param role Target/Set role for this client mode manager instance. 684 * @param newState new Wifi state 685 * @param currentState current wifi state 686 */ updateConnectModeState(ClientRole role, int newState, int currentState)687 private void updateConnectModeState(ClientRole role, int newState, int currentState) { 688 if (role != ROLE_CLIENT_PRIMARY || !mWifiStateChangeBroadcastEnabled) { 689 // do not raise public broadcast unless this is the primary client mode manager 690 return; 691 } 692 // TODO(b/186881160): May need to restore per STA state for Battery state reported. 693 mWifiInjector.getActiveModeWarden().setWifiStateForApiCalls(newState); 694 if (newState == WifiManager.WIFI_STATE_UNKNOWN) { 695 // do not need to broadcast failure to system 696 return; 697 } 698 699 // TODO(b/175839153): this broadcast should only be sent out when wifi is toggled on/off, 700 // not per CMM 701 final Intent intent = new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION); 702 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 703 intent.putExtra(WifiManager.EXTRA_WIFI_STATE, newState); 704 intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_STATE, currentState); 705 String summary = "broadcast=WIFI_STATE_CHANGED_ACTION" 706 + " EXTRA_WIFI_STATE=" + newState 707 + " EXTRA_PREVIOUS_WIFI_STATE=" + currentState; 708 if (mVerboseLoggingEnabled) Log.d(getTag(), "Queuing " + summary); 709 ClientModeManagerBroadcastQueue.QueuedBroadcast broadcast = 710 () -> { 711 if (mVerboseLoggingEnabled) Log.d(getTag(), "Sending " + summary); 712 mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); 713 }; 714 if (mRole == null && role == ROLE_CLIENT_PRIMARY) { 715 // This CMM is intended to be the primary, but has not completed the mode transition 716 // yet. Need to force broadcast to be sent. 717 broadcast.send(); 718 } else { 719 mBroadcastQueue.queueOrSendBroadcast(this, broadcast); 720 } 721 } 722 723 private class ClientModeStateMachine extends StateMachine { 724 // Commands for the state machine. 725 public static final int CMD_START = 0; 726 public static final int CMD_SWITCH_TO_SCAN_ONLY_MODE = 1; 727 public static final int CMD_SWITCH_TO_CONNECT_MODE = 2; 728 public static final int CMD_INTERFACE_STATUS_CHANGED = 3; 729 public static final int CMD_INTERFACE_DESTROYED = 4; 730 public static final int CMD_INTERFACE_DOWN = 5; 731 public static final int CMD_SWITCH_TO_SCAN_ONLY_MODE_CONTINUE = 6; 732 public static final int CMD_INTERFACE_ADDED = 7; 733 private final State mIdleState; 734 private final State mStartedState; 735 private final State mScanOnlyModeState; 736 private final State mConnectModeState; 737 // Workaround since we cannot use transitionTo(mScanOnlyModeState, RoleChangeInfo) 738 private RoleChangeInfo mScanRoleChangeInfoToSetOnTransition = null; 739 // Workaround since we cannot use transitionTo(mConnectModeState, RoleChangeInfo) 740 private RoleChangeInfo mConnectRoleChangeInfoToSetOnTransition = null; 741 742 @Nullable 743 private StateMachineObituary mObituary = null; 744 745 private final InterfaceEventCallback mWifiNativeInterfaceEventCallback = 746 new InterfaceEventCallback() { 747 748 boolean mEnabling = false; 749 750 @Override 751 public void onInterfaceLinkStateChanged(String ifaceName, boolean isLinkUp) { 752 Log.d("InterfaceEventCallback", 753 "onInterfaceLinkStateChanged, ifaceName=" + ifaceName + " up=" 754 + isLinkUp + " CurrentState=" + getCurrentStateName()); 755 if (isLinkUp) { 756 mEnabling = false; 757 } 758 } 759 760 @Override 761 public void onInterfaceAdded(String ifaceName) { 762 Log.d("InterfaceEventCallback", 763 "onInterfaceAdded, ifaceName=" + ifaceName 764 + " CurrentState=" + getCurrentStateName()); 765 if (mStateMachine.getCurrentState() == null) { 766 Log.d(TAG, "StateMachine not active, trigger ifaceAddedDetected"); 767 mSelfRecovery.trigger(SelfRecovery.REASON_IFACE_ADDED); 768 } else if (!mEnabling) { 769 Log.d("InterfaceEventCallback", "send CMD_INTERFACE_ADDED"); 770 mStateMachine.sendMessage(CMD_INTERFACE_ADDED); 771 mEnabling = true; 772 } else { 773 Log.d("InterfaceEventCallback", "wifi already in the start"); 774 } 775 } 776 }; 777 778 private final InterfaceCallback mWifiNativeInterfaceCallback = new InterfaceCallback() { 779 @Override 780 public void onDestroyed(String ifaceName) { 781 if (mClientInterfaceName != null && mClientInterfaceName.equals(ifaceName)) { 782 Log.d(getTag(), "STA iface " + ifaceName + " was destroyed, " 783 + "stopping client mode"); 784 785 // we must immediately clean up state in ClientModeImpl to unregister 786 // all client mode related objects 787 // Note: onDestroyed is only called from the main Wifi thread 788 if (mClientModeImpl == null) { 789 Log.w(getTag(), "Received mWifiNativeInterfaceCallback.onDestroyed " 790 + "callback when no ClientModeImpl instance is active."); 791 } else { 792 mClientModeImpl.handleIfaceDestroyed(); 793 } 794 795 // set it to null since the interface had been destroyed 796 mClientInterfaceName = null; 797 sendMessage(CMD_INTERFACE_DESTROYED); 798 } 799 } 800 801 @Override 802 public void onUp(String ifaceName) { 803 if (mClientInterfaceName != null && mClientInterfaceName.equals(ifaceName)) { 804 sendMessage(CMD_INTERFACE_STATUS_CHANGED, 1); 805 } 806 } 807 808 @Override 809 public void onDown(String ifaceName) { 810 if (mClientInterfaceName != null && mClientInterfaceName.equals(ifaceName)) { 811 sendMessage(CMD_INTERFACE_STATUS_CHANGED, 0); 812 } 813 } 814 }; 815 ClientModeStateMachine(Looper looper)816 ClientModeStateMachine(Looper looper) { 817 super(TAG, looper); 818 final int threshold = mContext.getResources().getInteger( 819 R.integer.config_wifiConfigurationWifiRunnerThresholdInMs); 820 mIdleState = new IdleState(threshold); 821 mStartedState = new StartedState(threshold); 822 mScanOnlyModeState = new ScanOnlyModeState(threshold); 823 mConnectModeState = new ConnectModeState(threshold); 824 // CHECKSTYLE:OFF IndentationCheck 825 addState(mIdleState); 826 addState(mStartedState, mIdleState); 827 addState(mScanOnlyModeState, mStartedState); 828 addState(mConnectModeState, mStartedState); 829 // CHECKSTYLE:ON IndentationCheck 830 831 setInitialState(mIdleState); 832 start(); 833 } 834 captureObituaryAndQuitNow()835 void captureObituaryAndQuitNow() { 836 // capture StateMachine LogRecs since we will lose them after we call quitNow() 837 // This is used for debugging. 838 mObituary = new StateMachineObituary(this); 839 840 quitNow(); 841 } 842 843 @Override dump(FileDescriptor fd, PrintWriter pw, String[] args)844 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 845 if (mObituary == null) { 846 // StateMachine hasn't quit yet, dump `this` via StateMachineObituary's dump() 847 // method for consistency with `else` branch. 848 new StateMachineObituary(this).dump(fd, pw, args); 849 } else { 850 // StateMachine has quit and cleared all LogRecs. 851 // Get them from the obituary instead. 852 mObituary.dump(fd, pw, args); 853 } 854 } 855 856 /** 857 * Return the additional string to be logged by LogRec. 858 * 859 * @param msg that was processed 860 * @return information to be logged as a String 861 */ 862 @Override getLogRecString(Message msg)863 protected String getLogRecString(Message msg) { 864 StringBuilder sb = new StringBuilder(); 865 sb.append(msg.arg1) 866 .append(" ").append(msg.arg2); 867 if (msg.obj != null) { 868 sb.append(" ").append(msg.obj); 869 } 870 return sb.toString(); 871 } 872 873 /** 874 * Convert the |what| field in logs from int to String. 875 */ 876 @Override getWhatToString(int what)877 protected String getWhatToString(int what) { 878 switch (what) { 879 case CMD_START: 880 return "CMD_START"; 881 case CMD_SWITCH_TO_SCAN_ONLY_MODE: 882 return "CMD_SWITCH_TO_SCAN_ONLY_MODE"; 883 case CMD_SWITCH_TO_CONNECT_MODE: 884 return "CMD_SWITCH_TO_CONNECT_MODE"; 885 case CMD_INTERFACE_STATUS_CHANGED: 886 return "CMD_INTERFACE_STATUS_CHANGED"; 887 case CMD_INTERFACE_DESTROYED: 888 return "CMD_INTERFACE_DESTROYED"; 889 case CMD_INTERFACE_DOWN: 890 return "CMD_INTERFACE_DOWN"; 891 case CMD_SWITCH_TO_SCAN_ONLY_MODE_CONTINUE: 892 return "CMD_SWITCH_TO_SCAN_ONLY_MODE_CONTINUE"; 893 case RunnerState.STATE_ENTER_CMD: 894 return "Enter"; 895 case RunnerState.STATE_EXIT_CMD: 896 return "Exit"; 897 default: 898 return "what:" + what; 899 } 900 } 901 902 /** 903 * Reset this ConcreteClientModeManager when its role changes, so that it can be reused for 904 * another purpose. 905 */ reset()906 private void reset() { 907 // Therefore, the caller must ensure that the role change has been completed and these 908 // settings have already reset before setting them, otherwise the new setting would be 909 // lost. 910 setShouldReduceNetworkScore(false); 911 } 912 setRoleInternal(@onNull RoleChangeInfo roleChangeInfo)913 private void setRoleInternal(@NonNull RoleChangeInfo roleChangeInfo) { 914 mPreviousRole = mRole; 915 mLastRoleChangeSinceBootMs = mClock.getElapsedSinceBootMillis(); 916 mRole = roleChangeInfo.role; 917 if (roleChangeInfo.requestorWs != null) { 918 mRequestorWs = roleChangeInfo.requestorWs; 919 } 920 if (roleChangeInfo.modeListener != null) { 921 mModeListener = roleChangeInfo.modeListener; 922 } 923 } 924 setRoleInternalAndInvokeCallback(@onNull RoleChangeInfo roleChangeInfo)925 private void setRoleInternalAndInvokeCallback(@NonNull RoleChangeInfo roleChangeInfo) { 926 if (roleChangeInfo.role == mRole) return; 927 if (mRole == null) { 928 if (mVerboseLoggingEnabled) { 929 Log.v(getTag(), "CurState:" + getCurrentStateName() 930 + ", clientModeManager started in role: " + roleChangeInfo); 931 } 932 setRoleInternal(roleChangeInfo); 933 mModeListener.onStarted(ConcreteClientModeManager.this); 934 } else { 935 if (mVerboseLoggingEnabled) { 936 Log.v(getTag(), "CurState:" + getCurrentStateName() 937 + ", clientModeManager role changed: " + roleChangeInfo); 938 } 939 setRoleInternal(roleChangeInfo); 940 reset(); 941 mModeListener.onRoleChanged(ConcreteClientModeManager.this); 942 } 943 if (mClientModeImpl != null) { 944 mClientModeImpl.onRoleChanged(); 945 } 946 } 947 948 private class IdleState extends RunnerState { IdleState(int threshold)949 IdleState(int threshold) { 950 super(threshold, mWifiInjector.getWifiHandlerLocalLog()); 951 } 952 953 @Override enterImpl()954 public void enterImpl() { 955 Log.d(getTag(), "entering IdleState"); 956 mClientInterfaceName = null; 957 mIfaceIsUp = false; 958 } 959 960 @Override exitImpl()961 public void exitImpl() { 962 // Sometimes the wifi handler thread may become blocked that the statemachine 963 // will exit in the IdleState without first entering StartedState. Trigger a 964 // cleanup here in case the above sequence happens. This the statemachine was 965 // started normally this will will not send a duplicate broadcast since mIsStopped 966 // will get set to false the first time the exit happens. 967 cleanupOnQuitIfApplicable(); 968 Log.d(getTag(), "IdleState.exit()"); 969 } 970 971 @Override getMessageLogRec(int what)972 public String getMessageLogRec(int what) { 973 return ConcreteClientModeManager.class.getSimpleName() + "." 974 + IdleState.class.getSimpleName() + "." 975 + getWhatToString(what); 976 } 977 978 @Override processMessageImpl(Message message)979 public boolean processMessageImpl(Message message) { 980 if (mVerboseLoggingEnabled) { 981 Log.d(getTag(), 982 getName() + " cmd = " + getWhatToString(message.what) + " " 983 + message.toString()); 984 } 985 switch (message.what) { 986 case CMD_START: 987 // Always start in scan mode first. 988 RoleChangeInfo roleChangeInfo = (RoleChangeInfo) message.obj; 989 mClientInterfaceName = mWifiNative.setupInterfaceForClientInScanMode( 990 mWifiNativeInterfaceCallback, roleChangeInfo.requestorWs, 991 ConcreteClientModeManager.this); 992 if (TextUtils.isEmpty(mClientInterfaceName)) { 993 Log.e(getTag(), "Failed to create ClientInterface. Sit in Idle"); 994 takeBugReportInterfaceFailureIfNeeded( 995 "Wi-Fi BugReport (scan STA interface failure):", 996 "Failed to create client interface in idle state"); 997 mModeListener.onStartFailure(ConcreteClientModeManager.this); 998 break; 999 } 1000 mWifiNative.setWifiNativeInterfaceEventCallback( 1001 mWifiNativeInterfaceEventCallback); 1002 if (roleChangeInfo.role instanceof ClientConnectivityRole) { 1003 sendMessage(CMD_SWITCH_TO_CONNECT_MODE, roleChangeInfo); 1004 transitionTo(mStartedState); 1005 } else { 1006 mScanRoleChangeInfoToSetOnTransition = roleChangeInfo; 1007 transitionTo(mScanOnlyModeState); 1008 } 1009 break; 1010 case CMD_INTERFACE_ADDED: 1011 Log.d(getTag(), "IdleState received CMD_INTERFACE_ADDED"); 1012 mSelfRecovery.trigger(SelfRecovery.REASON_IFACE_ADDED); 1013 break; 1014 default: 1015 Log.d(getTag(), getName() + ", received an invalid message: " + message); 1016 return NOT_HANDLED; 1017 } 1018 return HANDLED; 1019 } 1020 } 1021 1022 private class StartedState extends RunnerState { StartedState(int threshold)1023 StartedState(int threshold) { 1024 super(threshold, mWifiInjector.getWifiHandlerLocalLog()); 1025 } 1026 onUpChanged(boolean isUp)1027 private void onUpChanged(boolean isUp) { 1028 if (isUp == mIfaceIsUp) { 1029 return; // no change 1030 } 1031 mIfaceIsUp = isUp; 1032 if (!isUp) { 1033 // if the interface goes down we should exit and go back to idle state. 1034 Log.d(getTag(), getName() + ", interface down!"); 1035 mStateMachine.sendMessage(CMD_INTERFACE_DOWN); 1036 } 1037 if (mClientModeImpl != null) { 1038 mClientModeImpl.onUpChanged(isUp); 1039 } 1040 } 1041 1042 @Override enterImpl()1043 public void enterImpl() { 1044 Log.d(getTag(), "entering StartedState"); 1045 mIfaceIsUp = false; 1046 mIsStopped = false; 1047 onUpChanged(mWifiNative.isInterfaceUp(mClientInterfaceName)); 1048 } 1049 1050 @Override getMessageLogRec(int what)1051 public String getMessageLogRec(int what) { 1052 return ConcreteClientModeManager.class.getSimpleName() + "." 1053 + StartedState.class.getSimpleName() + "." 1054 + getWhatToString(what); 1055 } 1056 1057 @Override processMessageImpl(Message message)1058 public boolean processMessageImpl(Message message) { 1059 if (mVerboseLoggingEnabled) { 1060 Log.d(getTag(), 1061 getName() + " cmd = " + getWhatToString(message.what) + " " 1062 + message.toString()); 1063 } 1064 switch (message.what) { 1065 case CMD_START: 1066 // Already started, ignore this command. 1067 break; 1068 case CMD_SWITCH_TO_CONNECT_MODE: { 1069 RoleChangeInfo roleChangeInfo = (RoleChangeInfo) message.obj; 1070 updateConnectModeState(roleChangeInfo.role, 1071 WifiManager.WIFI_STATE_ENABLING, 1072 WifiManager.WIFI_STATE_DISABLED); 1073 if (!mWifiNative.switchClientInterfaceToConnectivityMode( 1074 mClientInterfaceName, roleChangeInfo.requestorWs)) { 1075 updateConnectModeState(roleChangeInfo.role, 1076 WifiManager.WIFI_STATE_UNKNOWN, 1077 WifiManager.WIFI_STATE_ENABLING); 1078 updateConnectModeState(roleChangeInfo.role, 1079 WifiManager.WIFI_STATE_DISABLED, 1080 WifiManager.WIFI_STATE_UNKNOWN); 1081 takeBugReportInterfaceFailureIfNeeded( 1082 "Wi-Fi BugReport (STA interface failure):", 1083 "Fail to switch to connection mode in started state"); 1084 mModeListener.onStartFailure(ConcreteClientModeManager.this); 1085 break; 1086 } 1087 // Role set in the enter of ConnectModeState. 1088 mConnectRoleChangeInfoToSetOnTransition = roleChangeInfo; 1089 transitionTo(mConnectModeState); 1090 break; 1091 } 1092 case CMD_SWITCH_TO_SCAN_ONLY_MODE: 1093 updateConnectModeState(mRole, WifiManager.WIFI_STATE_DISABLING, 1094 WifiManager.WIFI_STATE_ENABLED); 1095 mDeferStopHandler.start(getWifiOffDeferringTimeMs()); 1096 break; 1097 case CMD_SWITCH_TO_SCAN_ONLY_MODE_CONTINUE: { 1098 RoleChangeInfo roleChangeInfo = (RoleChangeInfo) message.obj; 1099 mScanRoleChangeInfoToSetOnTransition = roleChangeInfo; 1100 transitionTo(mScanOnlyModeState); 1101 break; 1102 } 1103 case CMD_INTERFACE_DOWN: 1104 Log.e(getTag(), 1105 getName() + ", detected an interface down, reporting failure to " 1106 + "SelfRecovery"); 1107 mSelfRecovery.trigger(SelfRecovery.REASON_STA_IFACE_DOWN); 1108 // once interface down, nothing else to do... stop the state machine 1109 captureObituaryAndQuitNow(); 1110 break; 1111 case CMD_INTERFACE_STATUS_CHANGED: 1112 boolean isUp = message.arg1 == 1; 1113 onUpChanged(isUp); 1114 break; 1115 case CMD_INTERFACE_DESTROYED: 1116 Log.e(getTag(), getName() + ", interface destroyed - client mode stopping"); 1117 mClientInterfaceName = null; 1118 // once interface destroyed, nothing else to do... stop the state machine 1119 captureObituaryAndQuitNow(); 1120 break; 1121 default: 1122 return NOT_HANDLED; 1123 } 1124 return HANDLED; 1125 } 1126 1127 /** 1128 * Clean up state, unregister listeners and update wifi state. 1129 */ 1130 @Override exitImpl()1131 public void exitImpl() { 1132 if (mClientInterfaceName != null) { 1133 mWifiNative.teardownInterface(mClientInterfaceName); 1134 mClientInterfaceName = null; 1135 mIfaceIsUp = false; 1136 } 1137 1138 Log.i(getTag(), "StartedState.exit(), setting mRole = null"); 1139 mIsStopped = true; 1140 cleanupOnQuitIfApplicable(); 1141 } 1142 } 1143 1144 private class ScanOnlyModeState extends RunnerState { ScanOnlyModeState(int threshold)1145 ScanOnlyModeState(int threshold) { 1146 super(threshold, mWifiInjector.getWifiHandlerLocalLog()); 1147 } 1148 1149 @Override enterImpl()1150 public void enterImpl() { 1151 Log.d(getTag(), "entering ScanOnlyModeState"); 1152 1153 if (mClientInterfaceName != null) { 1154 mScanOnlyModeImpl = mWifiInjector.makeScanOnlyModeImpl( 1155 mClientInterfaceName); 1156 } else { 1157 Log.e(getTag(), "Entered ScanOnlyModeState with a null interface name!"); 1158 } 1159 1160 if (mScanRoleChangeInfoToSetOnTransition == null 1161 || (mScanRoleChangeInfoToSetOnTransition.role != ROLE_CLIENT_SCAN_ONLY)) { 1162 Log.wtf(TAG, "Unexpected mScanRoleChangeInfoToSetOnTransition: " 1163 + mScanRoleChangeInfoToSetOnTransition); 1164 // Should never happen, but fallback to scan only to avoid a crash. 1165 mScanRoleChangeInfoToSetOnTransition = 1166 new RoleChangeInfo(ROLE_CLIENT_SCAN_ONLY); 1167 } 1168 1169 setRoleInternalAndInvokeCallback(mScanRoleChangeInfoToSetOnTransition); 1170 // If we're in ScanOnlyModeState, there is only 1 CMM. So it's ok to call 1171 // WakeupController directly, there won't be multiple CMMs trampling over each other 1172 mWakeupController.start(); 1173 mWifiNative.setScanMode(mClientInterfaceName, true); 1174 } 1175 1176 @Override getMessageLogRec(int what)1177 public String getMessageLogRec(int what) { 1178 return ConcreteClientModeManager.class.getSimpleName() + "." 1179 + ScanOnlyModeState.class.getSimpleName() + "." 1180 + getWhatToString(what); 1181 } 1182 1183 @Override processMessageImpl(Message message)1184 public boolean processMessageImpl(Message message) { 1185 if (mVerboseLoggingEnabled) { 1186 Log.d(getTag(), 1187 getName() + " cmd = " + getWhatToString(message.what) + " " 1188 + message.toString()); 1189 } 1190 switch (message.what) { 1191 case CMD_SWITCH_TO_SCAN_ONLY_MODE: 1192 // Already in scan only mode, ignore this command. 1193 break; 1194 default: 1195 return NOT_HANDLED; 1196 } 1197 return HANDLED; 1198 } 1199 1200 @Override exitImpl()1201 public void exitImpl() { 1202 mScanOnlyModeImpl = null; 1203 mScanRoleChangeInfoToSetOnTransition = null; 1204 1205 // If we're in ScanOnlyModeState, there is only 1 CMM. So it's ok to call 1206 // WakeupController directly, there won't be multiple CMMs trampling over each other 1207 mWakeupController.stop(); 1208 mWifiNative.setScanMode(mClientInterfaceName, false); 1209 } 1210 } 1211 1212 private class ConnectModeState extends RunnerState { ConnectModeState(int threshold)1213 ConnectModeState(int threshold) { 1214 super(threshold, mWifiInjector.getWifiHandlerLocalLog()); 1215 } 1216 1217 @Override enterImpl()1218 public void enterImpl() { 1219 Log.d(getTag(), "entering ConnectModeState, starting ClientModeImpl"); 1220 if (mClientInterfaceName == null) { 1221 Log.e(getTag(), "Supposed to start ClientModeImpl, but iface is null!"); 1222 } else { 1223 if (mClientModeImpl != null) { 1224 Log.e(getTag(), "ConnectModeState.enter(): mClientModeImpl is already " 1225 + "instantiated?!"); 1226 } 1227 mClientModeImpl = mWifiInjector.makeClientModeImpl( 1228 mClientInterfaceName, ConcreteClientModeManager.this, 1229 mVerboseLoggingEnabled); 1230 mClientModeImpl.setShouldReduceNetworkScore(mShouldReduceNetworkScore); 1231 } 1232 if (mConnectRoleChangeInfoToSetOnTransition == null 1233 || !(mConnectRoleChangeInfoToSetOnTransition.role 1234 instanceof ClientConnectivityRole)) { 1235 Log.wtf(TAG, "Unexpected mConnectRoleChangeInfoToSetOnTransition: " 1236 + mConnectRoleChangeInfoToSetOnTransition); 1237 // Should never happen, but fallback to primary to avoid a crash. 1238 mConnectRoleChangeInfoToSetOnTransition = 1239 new RoleChangeInfo(ROLE_CLIENT_PRIMARY); 1240 } 1241 1242 // Could be any one of possible connect mode roles. 1243 setRoleInternalAndInvokeCallback(mConnectRoleChangeInfoToSetOnTransition); 1244 updateConnectModeState(mConnectRoleChangeInfoToSetOnTransition.role, 1245 WIFI_STATE_ENABLED, WIFI_STATE_ENABLING); 1246 } 1247 1248 @Override getMessageLogRec(int what)1249 public String getMessageLogRec(int what) { 1250 return ConcreteClientModeManager.class.getSimpleName() + "." 1251 + ConnectModeState.class.getSimpleName() + "." 1252 + getWhatToString(what); 1253 } 1254 1255 @Override processMessageImpl(Message message)1256 public boolean processMessageImpl(Message message) { 1257 if (mVerboseLoggingEnabled) { 1258 Log.d(getTag(), 1259 getName() + " cmd = " + getWhatToString(message.what) + " " 1260 + message.toString()); 1261 } 1262 switch (message.what) { 1263 case CMD_SWITCH_TO_CONNECT_MODE: 1264 RoleChangeInfo roleChangeInfo = (RoleChangeInfo) message.obj; 1265 // switching to connect mode when already in connect mode, just update the 1266 // requestor WorkSource. 1267 boolean success = mWifiNative.replaceStaIfaceRequestorWs( 1268 mClientInterfaceName, roleChangeInfo.requestorWs); 1269 if (success) { 1270 setRoleInternalAndInvokeCallback(roleChangeInfo); 1271 } else { 1272 // If this call failed, the iface would be torn down. 1273 // Thus, simply abort and let the iface down handling take care of the 1274 // rest. 1275 Log.e(getTag(), getName() + ", Failed to switch ClientModeManager=" 1276 + ConcreteClientModeManager.this + "'s requestorWs"); 1277 } 1278 break; 1279 case CMD_SWITCH_TO_SCAN_ONLY_MODE: 1280 case CMD_INTERFACE_DESTROYED: 1281 updateConnectModeState(mRole, WifiManager.WIFI_STATE_DISABLING, 1282 WifiManager.WIFI_STATE_ENABLED); 1283 return NOT_HANDLED; // Handled in StartedState. 1284 case CMD_INTERFACE_DOWN: 1285 updateConnectModeState(mRole, WifiManager.WIFI_STATE_DISABLING, 1286 WifiManager.WIFI_STATE_UNKNOWN); 1287 return NOT_HANDLED; // Handled in StartedState. 1288 case CMD_INTERFACE_STATUS_CHANGED: 1289 boolean isUp = message.arg1 == 1; 1290 if (isUp == mIfaceIsUp) { 1291 break; // no change 1292 } 1293 if (!isUp) { 1294 // TODO(b/201584491) Figure out what to do with this block of code 1295 // handling iface down since most devices should have MAC randomization 1296 // enabled, which makes the "else" block essentially no-op. Also, the 1297 // "else" block would actually fully disable wifi which is not desirable 1298 // behavior because the firmware can recover the iface after it is down. 1299 if (mWifiGlobals.isConnectedMacRandomizationEnabled()) { 1300 return HANDLED; // For MAC randomization, ignore... 1301 } else { 1302 // Handle the error case where our underlying interface went down if 1303 // we do not have mac randomization enabled (b/72459123). 1304 // if the interface goes down we should exit and go back to idle 1305 // state. 1306 updateConnectModeState(mRole, WifiManager.WIFI_STATE_UNKNOWN, 1307 WifiManager.WIFI_STATE_ENABLED); 1308 } 1309 } 1310 return NOT_HANDLED; // Handled in StartedState. 1311 default: 1312 return NOT_HANDLED; 1313 } 1314 return HANDLED; 1315 } 1316 1317 @Override exitImpl()1318 public void exitImpl() { 1319 updateConnectModeState(mRole, WifiManager.WIFI_STATE_DISABLED, 1320 WifiManager.WIFI_STATE_DISABLING); 1321 1322 if (mClientModeImpl == null) { 1323 Log.w(getTag(), "ConnectModeState.exit(): mClientModeImpl is already null?!"); 1324 } else { 1325 Log.d(getTag(), "ConnectModeState.exit(): Stopping ClientModeImpl"); 1326 mClientModeImpl.stop(); 1327 mGraveyard.inter(mClientModeImpl); 1328 mClientModeImpl = null; 1329 } 1330 1331 mConnectRoleChangeInfoToSetOnTransition = null; 1332 } 1333 } 1334 } 1335 1336 /** Called by a ClientModeImpl owned by this CMM informing it has fully stopped. */ onClientModeImplQuit()1337 public void onClientModeImplQuit() { 1338 cleanupOnQuitIfApplicable(); 1339 } 1340 1341 /** 1342 * Only clean up this CMM once the CMM and all associated ClientModeImpls have been stopped. 1343 * This is necessary because ClientModeImpl sends broadcasts during stop, and the role must 1344 * remain primary for {@link ClientModeManagerBroadcastQueue} to send them out. 1345 */ cleanupOnQuitIfApplicable()1346 private void cleanupOnQuitIfApplicable() { 1347 if (mIsStopped && mGraveyard.hasAllClientModeImplsQuit()) { 1348 mPreviousRole = mRole; 1349 mLastRoleChangeSinceBootMs = mClock.getElapsedSinceBootMillis(); 1350 mRole = null; 1351 // only call onStopped() after role has been reset to null since ActiveModeWarden 1352 // expects the CMM to be fully stopped before onStopped(). 1353 mModeListener.onStopped(ConcreteClientModeManager.this); 1354 1355 // reset to false so that onStopped() won't be triggered again. 1356 mIsStopped = false; 1357 } 1358 } 1359 takeBugReportInterfaceFailureIfNeeded(String bugTitle, String bugDetail)1360 private void takeBugReportInterfaceFailureIfNeeded(String bugTitle, String bugDetail) { 1361 if (mWifiInjector.getDeviceConfigFacade().isInterfaceFailureBugreportEnabled()) { 1362 mWifiInjector.getWifiDiagnostics().takeBugReport(bugTitle, bugDetail); 1363 } 1364 } 1365 1366 @NonNull getClientMode()1367 private ClientMode getClientMode() { 1368 if (mClientModeImpl != null) { 1369 return mClientModeImpl; 1370 } 1371 if (mScanOnlyModeImpl != null) { 1372 return mScanOnlyModeImpl; 1373 } 1374 return mDefaultClientModeManager; 1375 } 1376 1377 /* 1378 * Note: These are simple wrappers over methods to {@link ClientModeImpl}. 1379 */ 1380 1381 @Override connectNetwork(NetworkUpdateResult result, ActionListenerWrapper wrapper, int callingUid, @NonNull String packageName, @Nullable String attributionTag)1382 public void connectNetwork(NetworkUpdateResult result, ActionListenerWrapper wrapper, 1383 int callingUid, @NonNull String packageName, @Nullable String attributionTag) { 1384 getClientMode().connectNetwork(result, wrapper, callingUid, packageName, attributionTag); 1385 } 1386 1387 @Override saveNetwork(NetworkUpdateResult result, ActionListenerWrapper wrapper, int callingUid, @NonNull String packageName)1388 public void saveNetwork(NetworkUpdateResult result, ActionListenerWrapper wrapper, 1389 int callingUid, @NonNull String packageName) { 1390 getClientMode().saveNetwork(result, wrapper, callingUid, packageName); 1391 } 1392 1393 @Override disconnect()1394 public void disconnect() { 1395 getClientMode().disconnect(); 1396 } 1397 1398 @Override reconnect(WorkSource ws)1399 public void reconnect(WorkSource ws) { 1400 getClientMode().reconnect(ws); 1401 } 1402 1403 @Override reassociate()1404 public void reassociate() { 1405 getClientMode().reassociate(); 1406 } 1407 1408 @Override startConnectToNetwork(int networkId, int uid, String bssid)1409 public void startConnectToNetwork(int networkId, int uid, String bssid) { 1410 getClientMode().startConnectToNetwork(networkId, uid, bssid); 1411 } 1412 1413 @Override startRoamToNetwork(int networkId, String bssid)1414 public void startRoamToNetwork(int networkId, String bssid) { 1415 getClientMode().startRoamToNetwork(networkId, bssid); 1416 } 1417 1418 @Override onDeviceMobilityStateUpdated(@eviceMobilityState int newState)1419 public void onDeviceMobilityStateUpdated(@DeviceMobilityState int newState) { 1420 getClientMode().onDeviceMobilityStateUpdated(newState); 1421 } 1422 1423 @Override setLinkLayerStatsPollingInterval(int newIntervalMs)1424 public void setLinkLayerStatsPollingInterval(int newIntervalMs) { 1425 getClientMode().setLinkLayerStatsPollingInterval(newIntervalMs); 1426 } 1427 1428 @Override setWifiConnectedNetworkScorer( IBinder binder, IWifiConnectedNetworkScorer scorer, int callerUid)1429 public boolean setWifiConnectedNetworkScorer( 1430 IBinder binder, IWifiConnectedNetworkScorer scorer, int callerUid) { 1431 return getClientMode().setWifiConnectedNetworkScorer(binder, scorer, callerUid); 1432 } 1433 1434 @Override clearWifiConnectedNetworkScorer()1435 public void clearWifiConnectedNetworkScorer() { 1436 getClientMode().clearWifiConnectedNetworkScorer(); 1437 } 1438 1439 @Override onNetworkSwitchAccepted(int targetNetworkId, String targetBssid)1440 public void onNetworkSwitchAccepted(int targetNetworkId, String targetBssid) { 1441 getClientMode().onNetworkSwitchAccepted(targetNetworkId, targetBssid); 1442 } 1443 1444 @Override onNetworkSwitchRejected(int targetNetworkId, String targetBssid)1445 public void onNetworkSwitchRejected(int targetNetworkId, String targetBssid) { 1446 getClientMode().onNetworkSwitchRejected(targetNetworkId, targetBssid); 1447 } 1448 1449 @Override resetSimAuthNetworks(@lientModeImpl.ResetSimReason int resetReason)1450 public void resetSimAuthNetworks(@ClientModeImpl.ResetSimReason int resetReason) { 1451 getClientMode().resetSimAuthNetworks(resetReason); 1452 } 1453 1454 @Override onBluetoothConnectionStateChanged()1455 public void onBluetoothConnectionStateChanged() { 1456 getClientMode().onBluetoothConnectionStateChanged(); 1457 } 1458 1459 @Override getConnectionInfo()1460 public WifiInfo getConnectionInfo() { 1461 return getClientMode().getConnectionInfo(); 1462 } 1463 1464 @Override syncQueryPasspointIcon(long bssid, String fileName)1465 public boolean syncQueryPasspointIcon(long bssid, String fileName) { 1466 return getClientMode().syncQueryPasspointIcon(bssid, fileName); 1467 } 1468 1469 @Override getCurrentNetwork()1470 public Network getCurrentNetwork() { 1471 return getClientMode().getCurrentNetwork(); 1472 } 1473 1474 @Override syncGetDhcpResultsParcelable()1475 public DhcpResultsParcelable syncGetDhcpResultsParcelable() { 1476 return getClientMode().syncGetDhcpResultsParcelable(); 1477 } 1478 1479 @Override getSupportedFeatures()1480 public long getSupportedFeatures() { 1481 return getClientMode().getSupportedFeatures(); 1482 } 1483 1484 @Override syncStartSubscriptionProvisioning(int callingUid, OsuProvider provider, IProvisioningCallback callback)1485 public boolean syncStartSubscriptionProvisioning(int callingUid, OsuProvider provider, 1486 IProvisioningCallback callback) { 1487 return getClientMode().syncStartSubscriptionProvisioning( 1488 callingUid, provider, callback); 1489 } 1490 1491 @Override isWifiStandardSupported(@ifiAnnotations.WifiStandard int standard)1492 public boolean isWifiStandardSupported(@WifiAnnotations.WifiStandard int standard) { 1493 return getClientMode().isWifiStandardSupported(standard); 1494 } 1495 1496 @Override enableTdls(String remoteMacAddress, boolean enable)1497 public boolean enableTdls(String remoteMacAddress, boolean enable) { 1498 return getClientMode().enableTdls(remoteMacAddress, enable); 1499 } 1500 1501 @Override enableTdlsWithRemoteIpAddress(String remoteIpAddress, boolean enable)1502 public boolean enableTdlsWithRemoteIpAddress(String remoteIpAddress, boolean enable) { 1503 return getClientMode().enableTdlsWithRemoteIpAddress(remoteIpAddress, enable); 1504 } 1505 1506 @Override isTdlsOperationCurrentlyAvailable()1507 public boolean isTdlsOperationCurrentlyAvailable() { 1508 return getClientMode().isTdlsOperationCurrentlyAvailable(); 1509 } 1510 1511 @Override getMaxSupportedConcurrentTdlsSessions()1512 public int getMaxSupportedConcurrentTdlsSessions() { 1513 return getClientMode().getMaxSupportedConcurrentTdlsSessions(); 1514 } 1515 1516 @Override getNumberOfEnabledTdlsSessions()1517 public int getNumberOfEnabledTdlsSessions() { 1518 return getClientMode().getNumberOfEnabledTdlsSessions(); 1519 } 1520 1521 @Override dumpIpClient(FileDescriptor fd, PrintWriter pw, String[] args)1522 public void dumpIpClient(FileDescriptor fd, PrintWriter pw, String[] args) { 1523 getClientMode().dumpIpClient(fd, pw, args); 1524 } 1525 1526 @Override dumpWifiScoreReport(FileDescriptor fd, PrintWriter pw, String[] args)1527 public void dumpWifiScoreReport(FileDescriptor fd, PrintWriter pw, String[] args) { 1528 getClientMode().dumpWifiScoreReport(fd, pw, args); 1529 } 1530 1531 @Override enableVerboseLogging(boolean verbose)1532 public void enableVerboseLogging(boolean verbose) { 1533 mVerboseLoggingEnabled = verbose; 1534 getClientMode().enableVerboseLogging(verbose); 1535 } 1536 1537 @Override getFactoryMacAddress()1538 public String getFactoryMacAddress() { 1539 return getClientMode().getFactoryMacAddress(); 1540 } 1541 1542 @Override getConnectedWifiConfiguration()1543 public WifiConfiguration getConnectedWifiConfiguration() { 1544 return getClientMode().getConnectedWifiConfiguration(); 1545 } 1546 1547 @Override getConnectingWifiConfiguration()1548 public WifiConfiguration getConnectingWifiConfiguration() { 1549 return getClientMode().getConnectingWifiConfiguration(); 1550 } 1551 1552 @Override getConnectedBssid()1553 public String getConnectedBssid() { 1554 return getClientMode().getConnectedBssid(); 1555 } 1556 1557 @Override getConnectingBssid()1558 public String getConnectingBssid() { 1559 return getClientMode().getConnectingBssid(); 1560 } 1561 1562 @Override getWifiLinkLayerStats()1563 public WifiLinkLayerStats getWifiLinkLayerStats() { 1564 return getClientMode().getWifiLinkLayerStats(); 1565 } 1566 1567 @Override setPowerSave(@owerSaveClientType int client, boolean ps)1568 public boolean setPowerSave(@PowerSaveClientType int client, boolean ps) { 1569 return getClientMode().setPowerSave(client, ps); 1570 } 1571 1572 @Override enablePowerSave()1573 public boolean enablePowerSave() { 1574 return getClientMode().enablePowerSave(); 1575 } 1576 1577 @Override setLowLatencyMode(boolean enabled)1578 public boolean setLowLatencyMode(boolean enabled) { 1579 return getClientMode().setLowLatencyMode(enabled); 1580 } 1581 1582 @Override getMcastLockManagerFilterController()1583 public WifiMulticastLockManager.FilterController getMcastLockManagerFilterController() { 1584 return getClientMode().getMcastLockManagerFilterController(); 1585 } 1586 1587 @Override isConnected()1588 public boolean isConnected() { 1589 return getClientMode().isConnected(); 1590 } 1591 1592 @Override isConnecting()1593 public boolean isConnecting() { 1594 return getClientMode().isConnecting(); 1595 } 1596 1597 @Override isRoaming()1598 public boolean isRoaming() { 1599 return getClientMode().isRoaming(); 1600 } 1601 1602 @Override isDisconnected()1603 public boolean isDisconnected() { 1604 return getClientMode().isDisconnected(); 1605 } 1606 1607 @Override isIpProvisioningTimedOut()1608 public boolean isIpProvisioningTimedOut() { 1609 return getClientMode().isIpProvisioningTimedOut(); 1610 } 1611 1612 @Override isSupplicantTransientState()1613 public boolean isSupplicantTransientState() { 1614 return getClientMode().isSupplicantTransientState(); 1615 } 1616 1617 @Override onCellularConnectivityChanged(@ifiDataStall.CellularDataStatusCode int status)1618 public void onCellularConnectivityChanged(@WifiDataStall.CellularDataStatusCode int status) { 1619 getClientMode().onCellularConnectivityChanged(status); 1620 } 1621 1622 @Override probeLink(LinkProbeCallback callback, int mcs)1623 public void probeLink(LinkProbeCallback callback, int mcs) { 1624 getClientMode().probeLink(callback, mcs); 1625 } 1626 1627 @Override sendMessageToClientModeImpl(Message msg)1628 public void sendMessageToClientModeImpl(Message msg) { 1629 getClientMode().sendMessageToClientModeImpl(msg); 1630 } 1631 1632 @Override getId()1633 public long getId() { 1634 return mId; 1635 } 1636 1637 @Override setMboCellularDataStatus(boolean available)1638 public void setMboCellularDataStatus(boolean available) { 1639 getClientMode().setMboCellularDataStatus(available); 1640 } 1641 1642 @Override getRoamingCapabilities()1643 public WifiNative.RoamingCapabilities getRoamingCapabilities() { 1644 return getClientMode().getRoamingCapabilities(); 1645 } 1646 1647 @Override configureRoaming(WifiNative.RoamingConfig config)1648 public boolean configureRoaming(WifiNative.RoamingConfig config) { 1649 return getClientMode().configureRoaming(config); 1650 } 1651 1652 @Override enableRoaming(boolean enabled)1653 public boolean enableRoaming(boolean enabled) { 1654 return getClientMode().enableRoaming(enabled); 1655 } 1656 1657 @Override setCountryCode(String countryCode)1658 public boolean setCountryCode(String countryCode) { 1659 return getClientMode().setCountryCode(countryCode); 1660 } 1661 1662 @Override getTxPktFates()1663 public List<TxFateReport> getTxPktFates() { 1664 return getClientMode().getTxPktFates(); 1665 } 1666 1667 @Override getRxPktFates()1668 public List<RxFateReport> getRxPktFates() { 1669 return getClientMode().getRxPktFates(); 1670 } 1671 1672 @Override getDeviceWiphyCapabilities()1673 public DeviceWiphyCapabilities getDeviceWiphyCapabilities() { 1674 return getClientMode().getDeviceWiphyCapabilities(); 1675 } 1676 1677 @Override requestAnqp(String bssid, Set<Integer> anqpIds, Set<Integer> hs20Subtypes)1678 public boolean requestAnqp(String bssid, Set<Integer> anqpIds, Set<Integer> hs20Subtypes) { 1679 return getClientMode().requestAnqp(bssid, anqpIds, hs20Subtypes); 1680 } 1681 1682 @Override requestVenueUrlAnqp(String bssid)1683 public boolean requestVenueUrlAnqp(String bssid) { 1684 return getClientMode().requestVenueUrlAnqp(bssid); 1685 } 1686 1687 @Override requestIcon(String bssid, String fileName)1688 public boolean requestIcon(String bssid, String fileName) { 1689 return getClientMode().requestIcon(bssid, fileName); 1690 } 1691 1692 @Override setShouldReduceNetworkScore(boolean shouldReduceNetworkScore)1693 public void setShouldReduceNetworkScore(boolean shouldReduceNetworkScore) { 1694 mShouldReduceNetworkScore = shouldReduceNetworkScore; 1695 getClientMode().setShouldReduceNetworkScore(shouldReduceNetworkScore); 1696 } 1697 1698 @Override toString()1699 public String toString() { 1700 return "ConcreteClientModeManager{id=" + getId() 1701 + " iface=" + getInterfaceName() 1702 + " role=" + getRole() 1703 + "}"; 1704 } 1705 1706 @Override updateCapabilities()1707 public void updateCapabilities() { 1708 getClientMode().updateCapabilities(); 1709 } 1710 1711 @Override isAffiliatedLinkBssid(MacAddress bssid)1712 public boolean isAffiliatedLinkBssid(MacAddress bssid) { 1713 return getClientMode().isAffiliatedLinkBssid(bssid); 1714 } 1715 1716 @Override isMlo()1717 public boolean isMlo() { 1718 return getClientMode().isMlo(); 1719 } 1720 1721 @Override onIdleModeChanged(boolean isIdle)1722 public void onIdleModeChanged(boolean isIdle) { 1723 getClientMode().onIdleModeChanged(isIdle); 1724 } 1725 } 1726