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.SAP_CLIENT_DISCONNECT_REASON_CODE_UNSPECIFIED; 20 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.app.compat.CompatChanges; 24 import android.content.BroadcastReceiver; 25 import android.content.Context; 26 import android.content.Intent; 27 import android.content.IntentFilter; 28 import android.net.MacAddress; 29 import android.net.wifi.OuiKeyedData; 30 import android.net.wifi.ScanResult; 31 import android.net.wifi.SoftApCapability; 32 import android.net.wifi.SoftApConfiguration; 33 import android.net.wifi.SoftApInfo; 34 import android.net.wifi.SoftApState; 35 import android.net.wifi.WifiAnnotations; 36 import android.net.wifi.WifiClient; 37 import android.net.wifi.WifiContext; 38 import android.net.wifi.WifiInfo; 39 import android.net.wifi.WifiManager; 40 import android.net.wifi.WifiScanner; 41 import android.net.wifi.WifiSsid; 42 import android.net.wifi.nl80211.DeviceWiphyCapabilities; 43 import android.net.wifi.util.WifiResourceCache; 44 import android.os.BatteryManager; 45 import android.os.Handler; 46 import android.os.Looper; 47 import android.os.Message; 48 import android.os.SystemClock; 49 import android.os.UserHandle; 50 import android.os.WorkSource; 51 import android.text.TextUtils; 52 import android.util.Log; 53 54 import androidx.annotation.IntDef; 55 56 import com.android.internal.annotations.VisibleForTesting; 57 import com.android.internal.util.IState; 58 import com.android.internal.util.Preconditions; 59 import com.android.internal.util.State; 60 import com.android.internal.util.StateMachine; 61 import com.android.internal.util.WakeupMessage; 62 import com.android.modules.utils.build.SdkLevel; 63 import com.android.server.wifi.WifiNative.InterfaceCallback; 64 import com.android.server.wifi.WifiNative.SoftApHalCallback; 65 import com.android.server.wifi.coex.CoexManager; 66 import com.android.server.wifi.coex.CoexManager.CoexListener; 67 import com.android.server.wifi.util.ApConfigUtil; 68 import com.android.server.wifi.util.WaitingState; 69 import com.android.wifi.flags.Flags; 70 import com.android.wifi.resources.R; 71 72 import com.google.common.collect.ImmutableList; 73 74 import java.io.FileDescriptor; 75 import java.io.PrintWriter; 76 import java.lang.annotation.Retention; 77 import java.lang.annotation.RetentionPolicy; 78 import java.text.SimpleDateFormat; 79 import java.util.ArrayList; 80 import java.util.Arrays; 81 import java.util.Date; 82 import java.util.HashMap; 83 import java.util.HashSet; 84 import java.util.Iterator; 85 import java.util.List; 86 import java.util.Locale; 87 import java.util.Map; 88 import java.util.Set; 89 import java.util.stream.Collectors; 90 91 /** 92 * Manage WiFi in AP mode. 93 * The internal state machine runs under the ClientModeImpl handler thread context. 94 */ 95 public class SoftApManager implements ActiveModeManager { 96 private static final String TAG = "SoftApManager"; 97 98 @VisibleForTesting 99 public static final String SOFT_AP_SEND_MESSAGE_TIMEOUT_TAG = TAG 100 + " Soft AP Send Message Timeout on "; 101 102 // Start result codes. These should reflect the SoftApStopped.StartResult metrics codes. 103 @Retention(RetentionPolicy.SOURCE) 104 @IntDef(value = { 105 START_RESULT_UNKNOWN, 106 START_RESULT_SUCCESS, 107 START_RESULT_FAILURE_GENERAL, 108 START_RESULT_FAILURE_NO_CHANNEL, 109 START_RESULT_FAILURE_UNSUPPORTED_CONFIG, 110 START_RESULT_FAILURE_START_HAL, 111 START_RESULT_FAILURE_START_HOSTAPD, 112 START_RESULT_FAILURE_INTERFACE_CONFLICT_USER_REJECTED, 113 START_RESULT_FAILURE_INTERFACE_CONFLICT, 114 START_RESULT_FAILURE_CREATE_INTERFACE, 115 START_RESULT_FAILURE_SET_COUNTRY_CODE, 116 START_RESULT_FAILURE_SET_MAC_ADDRESS, 117 START_RESULT_FAILURE_REGISTER_AP_CALLBACK_HOSTAPD, 118 START_RESULT_FAILURE_REGISTER_AP_CALLBACK_WIFICOND, 119 START_RESULT_FAILURE_ADD_AP_HOSTAPD, 120 }) 121 public @interface StartResult {} 122 123 // Unknown start result 124 public static final int START_RESULT_UNKNOWN = 0; 125 // Successful start 126 public static final int START_RESULT_SUCCESS = 1; 127 // General failure 128 public static final int START_RESULT_FAILURE_GENERAL = 2; 129 // Failed due to no channel available 130 public static final int START_RESULT_FAILURE_NO_CHANNEL = 3; 131 // Failed due to config being unsupported 132 public static final int START_RESULT_FAILURE_UNSUPPORTED_CONFIG = 4; 133 // Failed to start the HAL 134 public static final int START_RESULT_FAILURE_START_HAL = 5; 135 // Failed to start hostapd 136 public static final int START_RESULT_FAILURE_START_HOSTAPD = 6; 137 // Failed due to interface conflict with user rejection 138 public static final int START_RESULT_FAILURE_INTERFACE_CONFLICT_USER_REJECTED = 7; 139 // Failed due to interface conflict 140 public static final int START_RESULT_FAILURE_INTERFACE_CONFLICT = 8; 141 // Failed to create interface in vendor HAL 142 public static final int START_RESULT_FAILURE_CREATE_INTERFACE = 9; 143 // Failed to set country code 144 public static final int START_RESULT_FAILURE_SET_COUNTRY_CODE = 10; 145 // Failed to set mac address 146 public static final int START_RESULT_FAILURE_SET_MAC_ADDRESS = 11; 147 // Failed to register AP callback with hostapd 148 public static final int START_RESULT_FAILURE_REGISTER_AP_CALLBACK_HOSTAPD = 12; 149 // Failed to register AP callback with wificond 150 public static final int START_RESULT_FAILURE_REGISTER_AP_CALLBACK_WIFICOND = 13; 151 // Failed to add AP to hostapd 152 public static final int START_RESULT_FAILURE_ADD_AP_HOSTAPD = 14; 153 154 // Stop event codes. These should reflect the SoftApStopped.StopEvent metrics codes. 155 @Retention(RetentionPolicy.SOURCE) 156 @IntDef(value = { 157 STOP_EVENT_UNKNOWN, 158 STOP_EVENT_STOPPED, 159 STOP_EVENT_INTERFACE_DOWN, 160 STOP_EVENT_INTERFACE_DESTROYED, 161 STOP_EVENT_HOSTAPD_FAILURE, 162 STOP_EVENT_NO_USAGE_TIMEOUT, 163 }) 164 public @interface StopEvent {} 165 166 // Unknown stop event 167 public static final int STOP_EVENT_UNKNOWN = 0; 168 // Stopped by the user 169 public static final int STOP_EVENT_STOPPED = 1; 170 // Stopped due to interface down 171 public static final int STOP_EVENT_INTERFACE_DOWN = 2; 172 // Stopped due to interface destroyed 173 public static final int STOP_EVENT_INTERFACE_DESTROYED = 3; 174 // Stopped due to hostapd failure 175 public static final int STOP_EVENT_HOSTAPD_FAILURE = 4; 176 // Stopped due to no usage timeout 177 public static final int STOP_EVENT_NO_USAGE_TIMEOUT = 5; 178 179 private final WifiContext mContext; 180 private final FrameworkFacade mFrameworkFacade; 181 private final WifiNative mWifiNative; 182 // This will only be null if SdkLevel is not at least S 183 @Nullable private final CoexManager mCoexManager; 184 private final ClientModeImplMonitor mCmiMonitor; 185 private final ActiveModeWarden mActiveModeWarden; 186 private final SoftApNotifier mSoftApNotifier; 187 private final InterfaceConflictManager mInterfaceConflictManager; 188 private final WifiInjector mWifiInjector; 189 private final WifiResourceCache mResourceCache; 190 191 @VisibleForTesting 192 static final long SOFT_AP_PENDING_DISCONNECTION_CHECK_DELAY_MS = 1000; 193 194 private static final long SCHEDULE_IDLE_INSTANCE_SHUTDOWN_TIMEOUT_DELAY_MS = 10; 195 196 private String mCountryCode; 197 198 private final SoftApStateMachine mStateMachine; 199 200 private final Listener<SoftApManager> mModeListener; 201 private final WifiServiceImpl.SoftApCallbackInternal mSoftApCallback; 202 203 private String mApInterfaceName; 204 private boolean mIfaceIsUp; 205 private boolean mIfaceIsDestroyed; 206 207 private final WifiApConfigStore mWifiApConfigStore; 208 209 private final ClientModeImplListener mCmiListener = new ClientModeImplListener() { 210 @Override 211 public void onL2Connected(@NonNull ConcreteClientModeManager clientModeManager) { 212 SoftApManager.this.onL2Connected(clientModeManager); 213 } 214 }; 215 216 private final WifiMetrics mWifiMetrics; 217 private final long mId; 218 219 private boolean mIsUnsetBssid; 220 221 private boolean mVerboseLoggingEnabled = false; 222 223 // Whether this device supports multiple link operation in a single MLD. 224 private boolean mIsMLDApSupportMLO = false; 225 226 // Whether this SoftApManager (i.e. this AP interface) is using multiple link operation. 227 private boolean mIsUsingMlo = false; 228 229 private int mMaximumNumberOfMLDSupported; 230 private int mCurrentExistingMLD; 231 232 /** 233 * The specified configuration passed in during initialization or during a configuration update 234 * that doesn't require a restart. 235 * 236 * Use it when doing configuration update to know if the input configuration was changed. For 237 * others use case, it should use {@code mCurrentSoftApConfiguration}. 238 */ 239 @NonNull private SoftApModeConfiguration mSpecifiedModeConfiguration; 240 241 /** 242 * Current Soft AP configuration which is used to start Soft AP. 243 * The configuration may be changed because 244 * 1. bssid is changed because MAC randomization 245 * 2. bands are changed because fallback to single AP mode mechanism. 246 */ 247 @Nullable 248 private SoftApConfiguration mCurrentSoftApConfiguration; 249 /** 250 * Whether the configuration being used is the user's persistent SoftApConfiguration. 251 */ 252 private boolean mIsUsingPersistentSoftApConfiguration = false; 253 254 @NonNull 255 private Map<String, SoftApInfo> mCurrentSoftApInfoMap = new HashMap<>(); 256 257 @NonNull 258 private SoftApCapability mCurrentSoftApCapability; 259 260 private Map<String, List<WifiClient>> mConnectedClientWithApInfoMap = new HashMap<>(); 261 @VisibleForTesting 262 Map<WifiClient, Integer> mPendingDisconnectClients = new HashMap<>(); 263 264 private boolean mTimeoutEnabled = false; 265 private boolean mBridgedModeOpportunisticsShutdownTimeoutEnabled = false; 266 267 private final SarManager mSarManager; 268 269 private long mStartTimestampMs; 270 271 private long mDefaultShutdownTimeoutMillis; 272 273 private long mDefaultShutdownIdleInstanceInBridgedModeTimeoutMillis; 274 275 private final boolean mIsDisableShutDownBridgedModeIdleInstanceTimerWhenPlugged; 276 277 private static final SimpleDateFormat FORMATTER = new SimpleDateFormat("MM-dd HH:mm:ss.SSS"); 278 279 private WifiDiagnostics mWifiDiagnostics; 280 281 @Nullable 282 private SoftApRole mRole = null; 283 @Nullable 284 private WorkSource mRequestorWs = null; 285 286 private boolean mEverReportMetricsForMaxClient = false; 287 288 @NonNull 289 private Set<MacAddress> mBlockedClientList = new HashSet<>(); 290 291 @NonNull 292 private Set<MacAddress> mAllowedClientList = new HashSet<>(); 293 294 @NonNull 295 private Set<Integer> mSafeChannelFrequencyList = new HashSet<>(); 296 297 private boolean mIsPlugged = false; 298 299 private int mCurrentApState = WifiManager.WIFI_AP_STATE_DISABLED; 300 301 private boolean mIsSoftApStartedEventWritten = false; 302 303 private int mMaxConnectedClients = 0; 304 305 /** 306 * A map stores shutdown timeouts for each Soft Ap instance. 307 * There are three timeout messages now. 308 * 1. <mApInterfaceName, timeout> which uses to monitor whole Soft AP interface. 309 * It works on single AP mode and bridged AP mode. 310 * 311 * 2. <instance_lower_band, timeout> which is used to shutdown the AP when there are no 312 * connected devices. It is scheduled only in bridged mode to move dual mode AP to single 313 * mode AP in lower band. 314 * 315 * 3. <instance_higher_band, timeout> which is used to shutdown the AP when there are no 316 * connected devices. It is scheduled only in bridged mode to move dual mode AP to single 317 * mode AP in higher band. 318 */ 319 @VisibleForTesting 320 public Map<String, WakeupMessage> mSoftApTimeoutMessageMap = new HashMap<>(); 321 322 /** 323 * Listener for soft AP events. 324 */ 325 private final SoftApHalCallback mSoftApHalCallback = new SoftApHalCallback() { 326 @Override 327 public void onFailure() { 328 mStateMachine.sendMessage(SoftApStateMachine.CMD_FAILURE); 329 } 330 331 @Override 332 public void onInstanceFailure(String instanceName) { 333 mStateMachine.sendMessage(SoftApStateMachine.CMD_FAILURE, instanceName); 334 } 335 336 @Override 337 public void onInfoChanged(String apIfaceInstance, int frequency, 338 @WifiAnnotations.Bandwidth int bandwidth, 339 @WifiAnnotations.WifiStandard int generation, 340 @Nullable MacAddress apIfaceInstanceMacAddress, 341 @Nullable MacAddress mldAddress, 342 @NonNull List<OuiKeyedData> vendorData) { 343 SoftApInfo apInfo = new SoftApInfo(); 344 apInfo.setFrequency(frequency); 345 apInfo.setBandwidth(bandwidth); 346 apInfo.setWifiStandard(generation); 347 if (apIfaceInstanceMacAddress != null) { 348 apInfo.setBssid(apIfaceInstanceMacAddress); 349 } 350 if (mldAddress != null) { 351 apInfo.setMldAddress(mldAddress); 352 } 353 apInfo.setApInstanceIdentifier(apIfaceInstance != null 354 ? apIfaceInstance : mApInterfaceName); 355 if (SdkLevel.isAtLeastV() && vendorData != null && !vendorData.isEmpty()) { 356 apInfo.setVendorData(vendorData); 357 } 358 mStateMachine.sendMessage( 359 SoftApStateMachine.CMD_AP_INFO_CHANGED, 0, 0, apInfo); 360 } 361 362 @Override 363 public void onConnectedClientsChanged(String apIfaceInstance, MacAddress clientAddress, 364 boolean isConnected, @WifiAnnotations.SoftApDisconnectReason int disconnectReason) { 365 if (clientAddress != null) { 366 WifiClient client = new WifiClient(clientAddress, apIfaceInstance != null 367 ? apIfaceInstance : mApInterfaceName, disconnectReason); 368 mStateMachine.sendMessage(SoftApStateMachine.CMD_ASSOCIATED_STATIONS_CHANGED, 369 isConnected ? 1 : 0, 0, client); 370 } else { 371 Log.e(getTag(), "onConnectedClientsChanged: Invalid type returned"); 372 } 373 } 374 }; 375 376 // This will only be null if SdkLevel is not at least S 377 @Nullable private final CoexListener mCoexListener; 378 updateSafeChannelFrequencyList()379 private void updateSafeChannelFrequencyList() { 380 if (!SdkLevel.isAtLeastS() || mCurrentSoftApConfiguration == null) { 381 return; 382 } 383 mSafeChannelFrequencyList.clear(); 384 for (int configuredBand : mCurrentSoftApConfiguration.getBands()) { 385 for (int band : SoftApConfiguration.BAND_TYPES) { 386 if ((band & configuredBand) == 0) { 387 continue; 388 } 389 for (int channel : mCurrentSoftApCapability.getSupportedChannelList(band)) { 390 mSafeChannelFrequencyList.add( 391 ApConfigUtil.convertChannelToFrequency(channel, band)); 392 } 393 } 394 } 395 if ((mCoexManager.getCoexRestrictions() & WifiManager.COEX_RESTRICTION_SOFTAP) != 0) { 396 mSafeChannelFrequencyList.removeAll( 397 ApConfigUtil.getUnsafeChannelFreqsFromCoex(mCoexManager)); 398 } 399 if (isBridgedMode() && mCurrentSoftApInfoMap.size() == 2) { 400 // Logging only for bridged use case since it only used to fallback to single AP mode. 401 Log.d(getTag(), "SafeChannelFrequencyList = " + mSafeChannelFrequencyList); 402 } 403 } 404 configureInternalConfiguration()405 private void configureInternalConfiguration() { 406 if (mCurrentSoftApConfiguration == null) { 407 return; 408 } 409 mBlockedClientList = new HashSet<>(mCurrentSoftApConfiguration.getBlockedClientList()); 410 mAllowedClientList = new HashSet<>(mCurrentSoftApConfiguration.getAllowedClientList()); 411 mTimeoutEnabled = mCurrentSoftApConfiguration.isAutoShutdownEnabled(); 412 mBridgedModeOpportunisticsShutdownTimeoutEnabled = 413 mCurrentSoftApConfiguration.isBridgedModeOpportunisticShutdownEnabledInternal(); 414 } 415 updateChangeableConfiguration(SoftApConfiguration newConfig)416 private void updateChangeableConfiguration(SoftApConfiguration newConfig) { 417 if (mCurrentSoftApConfiguration == null || newConfig == null) { 418 return; 419 } 420 /** 421 * update configurations only which mentioned in WifiManager#setSoftApConfiguration 422 */ 423 long newShutdownTimeoutMillis = newConfig.getShutdownTimeoutMillis(); 424 // Compatibility check is used for unit test only since the SoftApManager is created by 425 // the unit test thread (not the system_server) when running unit test. In other cases, 426 // the SoftApManager would run in system server(i.e. always bypasses the app compat check). 427 if (CompatChanges.isChangeEnabled(SoftApConfiguration.REMOVE_ZERO_FOR_TIMEOUT_SETTING) 428 && newShutdownTimeoutMillis == 0) { 429 newShutdownTimeoutMillis = SoftApConfiguration.DEFAULT_TIMEOUT; 430 } 431 SoftApConfiguration.Builder newConfigurBuilder = 432 new SoftApConfiguration.Builder(mCurrentSoftApConfiguration) 433 .setAllowedClientList(newConfig.getAllowedClientList()) 434 .setBlockedClientList(newConfig.getBlockedClientList()) 435 .setClientControlByUserEnabled(newConfig.isClientControlByUserEnabled()) 436 .setMaxNumberOfClients(newConfig.getMaxNumberOfClients()) 437 .setShutdownTimeoutMillis(newShutdownTimeoutMillis) 438 .setAutoShutdownEnabled(newConfig.isAutoShutdownEnabled()); 439 if (SdkLevel.isAtLeastS()) { 440 newConfigurBuilder.setBridgedModeOpportunisticShutdownEnabled( 441 newConfig.isBridgedModeOpportunisticShutdownEnabledInternal()); 442 } 443 mCurrentSoftApConfiguration = newConfigurBuilder.build(); 444 configureInternalConfiguration(); 445 } 446 SoftApManager( @onNull WifiContext context, @NonNull Looper looper, @NonNull FrameworkFacade framework, @NonNull WifiNative wifiNative, @NonNull WifiInjector wifiInjector, @NonNull CoexManager coexManager, @NonNull InterfaceConflictManager interfaceConflictManager, @NonNull Listener<SoftApManager> listener, @NonNull WifiServiceImpl.SoftApCallbackInternal callback, @NonNull WifiApConfigStore wifiApConfigStore, @NonNull SoftApModeConfiguration apConfig, @NonNull WifiMetrics wifiMetrics, @NonNull SarManager sarManager, @NonNull WifiDiagnostics wifiDiagnostics, @NonNull SoftApNotifier softApNotifier, @NonNull ClientModeImplMonitor cmiMonitor, @NonNull ActiveModeWarden activeModeWarden, long id, @NonNull WorkSource requestorWs, @NonNull SoftApRole role, boolean verboseLoggingEnabled)447 public SoftApManager( 448 @NonNull WifiContext context, 449 @NonNull Looper looper, 450 @NonNull FrameworkFacade framework, 451 @NonNull WifiNative wifiNative, 452 @NonNull WifiInjector wifiInjector, 453 @NonNull CoexManager coexManager, 454 @NonNull InterfaceConflictManager interfaceConflictManager, 455 @NonNull Listener<SoftApManager> listener, 456 @NonNull WifiServiceImpl.SoftApCallbackInternal callback, 457 @NonNull WifiApConfigStore wifiApConfigStore, 458 @NonNull SoftApModeConfiguration apConfig, 459 @NonNull WifiMetrics wifiMetrics, 460 @NonNull SarManager sarManager, 461 @NonNull WifiDiagnostics wifiDiagnostics, 462 @NonNull SoftApNotifier softApNotifier, 463 @NonNull ClientModeImplMonitor cmiMonitor, 464 @NonNull ActiveModeWarden activeModeWarden, 465 long id, 466 @NonNull WorkSource requestorWs, 467 @NonNull SoftApRole role, 468 boolean verboseLoggingEnabled) { 469 mContext = context; 470 mFrameworkFacade = framework; 471 mSoftApNotifier = softApNotifier; 472 mWifiNative = wifiNative; 473 mWifiInjector = wifiInjector; 474 mCoexManager = coexManager; 475 mInterfaceConflictManager = interfaceConflictManager; 476 mResourceCache = mContext.getResourceCache(); 477 if (SdkLevel.isAtLeastS()) { 478 mCoexListener = new CoexListener() { 479 @Override 480 public void onCoexUnsafeChannelsChanged() { 481 if (mCurrentSoftApConfiguration == null) { 482 return; 483 } 484 mStateMachine.sendMessage( 485 SoftApStateMachine.CMD_SAFE_CHANNEL_FREQUENCY_CHANGED); 486 } 487 }; 488 } else { 489 mCoexListener = null; 490 } 491 mCountryCode = apConfig.getCountryCode(); 492 mModeListener = listener; 493 mSoftApCallback = callback; 494 mWifiApConfigStore = wifiApConfigStore; 495 mCurrentSoftApConfiguration = apConfig.getSoftApConfiguration(); 496 mCurrentSoftApCapability = apConfig.getCapability(); 497 498 // null is a valid input and means we use the user-configured tethering settings. 499 if (mCurrentSoftApConfiguration == null) { 500 mCurrentSoftApConfiguration = mWifiApConfigStore.getApConfiguration(); 501 mIsUsingPersistentSoftApConfiguration = true; 502 // may still be null if we fail to load the default config 503 } 504 // Store mode configuration before update the configuration. 505 mSpecifiedModeConfiguration = 506 new SoftApModeConfiguration( 507 apConfig.getTargetMode(), 508 mCurrentSoftApConfiguration, 509 mCurrentSoftApCapability, 510 mCountryCode, 511 apConfig.getTetheringRequest()); 512 if (mCurrentSoftApConfiguration != null) { 513 mIsUnsetBssid = mCurrentSoftApConfiguration.getBssid() == null; 514 if (mCurrentSoftApCapability.areFeaturesSupported( 515 SoftApCapability.SOFTAP_FEATURE_MAC_ADDRESS_CUSTOMIZATION)) { 516 mCurrentSoftApConfiguration = mWifiApConfigStore.randomizeBssidIfUnset( 517 mContext, mCurrentSoftApConfiguration); 518 } 519 } 520 mWifiMetrics = wifiMetrics; 521 mSarManager = sarManager; 522 mWifiDiagnostics = wifiDiagnostics; 523 mStateMachine = new SoftApStateMachine(looper); 524 configureInternalConfiguration(); 525 mDefaultShutdownTimeoutMillis = mResourceCache.getInteger( 526 R.integer.config_wifiFrameworkSoftApShutDownTimeoutMilliseconds); 527 mDefaultShutdownIdleInstanceInBridgedModeTimeoutMillis = mResourceCache 528 .getInteger(R.integer 529 .config_wifiFrameworkSoftApShutDownIdleInstanceInBridgedModeTimeoutMillisecond); 530 mIsDisableShutDownBridgedModeIdleInstanceTimerWhenPlugged = mResourceCache 531 .getBoolean(R.bool 532 .config_wifiFrameworkSoftApDisableBridgedModeShutdownIdleInstanceWhenCharging); 533 mCmiMonitor = cmiMonitor; 534 mActiveModeWarden = activeModeWarden; 535 mCmiMonitor.registerListener(mCmiListener); 536 updateSafeChannelFrequencyList(); 537 mId = id; 538 mRole = role; 539 // chip support it && overlay configuration is set. 540 mIsMLDApSupportMLO = mWifiNative.isMLDApSupportMLO(); 541 mMaximumNumberOfMLDSupported = ApConfigUtil.getMaximumSupportedMLD( 542 mContext, mWifiNative.isMultipleMLDSupportedOnSap()); 543 mCurrentExistingMLD = mActiveModeWarden.getCurrentMLDAp(); 544 mIsUsingMlo = useMultilinkMloSoftAp(); 545 enableVerboseLogging(verboseLoggingEnabled); 546 mStateMachine.sendMessage(SoftApStateMachine.CMD_START, requestorWs); 547 } 548 549 @Override getId()550 public long getId() { 551 return mId; 552 } 553 getTag()554 private String getTag() { 555 return TAG + "[" + (mApInterfaceName == null ? "unknown" : mApInterfaceName) + "]"; 556 } 557 558 /** 559 * Stop soft AP. 560 */ 561 @Override stop()562 public void stop() { 563 Log.d(getTag(), " currentstate: " + getCurrentStateName()); 564 mStateMachine.sendMessage(SoftApStateMachine.CMD_STOP); 565 } 566 isUsingMlo()567 public boolean isUsingMlo() { 568 return mIsUsingMlo; 569 } 570 useMultilinkMloSoftAp()571 private boolean useMultilinkMloSoftAp() { 572 if (!Flags.mloSap()) { 573 return false; 574 } 575 if (SdkLevel.isAtLeastT() && mCurrentSoftApConfiguration != null 576 && mCurrentSoftApConfiguration.isIeee80211beEnabled() 577 && isBridgedMode() && mIsMLDApSupportMLO) { 578 579 if (ApConfigUtil.is11beAllowedForThisConfiguration( 580 null /* Wiphy capability can be ignored for MLO case*/, 581 mContext, mCurrentSoftApConfiguration, true /* isBridgedMode */, 582 mMaximumNumberOfMLDSupported, mCurrentExistingMLD, 583 true /* isMLDApSupportMLO */)) { 584 return true; 585 } 586 } 587 return false; 588 } 589 isOweTransition()590 private boolean isOweTransition() { 591 return (SdkLevel.isAtLeastT() && mCurrentSoftApConfiguration != null 592 && mCurrentSoftApConfiguration.getSecurityType() 593 == SoftApConfiguration.SECURITY_TYPE_WPA3_OWE_TRANSITION); 594 } 595 isBridgedMode()596 public boolean isBridgedMode() { 597 return (SdkLevel.isAtLeastS() && mCurrentSoftApConfiguration != null 598 && (mCurrentSoftApConfiguration.getBands().length > 1)); 599 } 600 isBridgeRequired()601 private boolean isBridgeRequired() { 602 return isBridgedMode() || isOweTransition(); 603 } 604 getShutdownTimeoutMillis()605 private long getShutdownTimeoutMillis() { 606 long timeout = mCurrentSoftApConfiguration.getShutdownTimeoutMillis(); 607 return timeout > 0 ? timeout : mDefaultShutdownTimeoutMillis; 608 } 609 getShutdownIdleInstanceInBridgedModeTimeoutMillis()610 private long getShutdownIdleInstanceInBridgedModeTimeoutMillis() { 611 long timeout = mCurrentSoftApConfiguration 612 .getBridgedModeOpportunisticShutdownTimeoutMillisInternal(); 613 return timeout > 0 ? timeout : mDefaultShutdownIdleInstanceInBridgedModeTimeoutMillis; 614 } 615 getVendorData()616 private List<OuiKeyedData> getVendorData() { 617 return (SdkLevel.isAtLeastV() && mCurrentSoftApConfiguration != null) 618 ? mCurrentSoftApConfiguration.getVendorData() 619 : new ArrayList<>(); 620 } 621 getHighestFrequencyInstance(Set<String> candidateInstances)622 private String getHighestFrequencyInstance(Set<String> candidateInstances) { 623 int currentHighestFrequencyOnAP = 0; 624 String highestFrequencyInstance = null; 625 for (String instance : candidateInstances) { 626 SoftApInfo info = mCurrentSoftApInfoMap.get(instance); 627 if (info == null) { 628 Log.wtf(getTag(), "Invalid instance name, no way to get the frequency"); 629 return ""; 630 } 631 int frequencyOnInstance = info.getFrequency(); 632 if (frequencyOnInstance > currentHighestFrequencyOnAP) { 633 currentHighestFrequencyOnAP = frequencyOnInstance; 634 highestFrequencyInstance = instance; 635 } 636 } 637 return highestFrequencyInstance; 638 } 639 640 @Override getRole()641 @Nullable public SoftApRole getRole() { 642 return mRole; 643 } 644 645 @Override getPreviousRole()646 @Nullable public ClientRole getPreviousRole() { 647 return null; 648 } 649 650 @Override getLastRoleChangeSinceBootMs()651 public long getLastRoleChangeSinceBootMs() { 652 return 0; 653 } 654 655 /** Set the role of this SoftApManager */ setRole(SoftApRole role)656 public void setRole(SoftApRole role) { 657 // softap does not allow in-place switching of roles. 658 Preconditions.checkState(mRole == null); 659 mRole = role; 660 } 661 662 @Override getInterfaceName()663 public String getInterfaceName() { 664 return mApInterfaceName; 665 } 666 667 @Override getRequestorWs()668 public WorkSource getRequestorWs() { 669 return mRequestorWs; 670 } 671 672 /** 673 * Update AP capability. Called when carrier config or device resouce config changed. 674 * 675 * @param capability new AP capability. 676 */ updateCapability(@onNull SoftApCapability capability)677 public void updateCapability(@NonNull SoftApCapability capability) { 678 mStateMachine.sendMessage(SoftApStateMachine.CMD_UPDATE_CAPABILITY, capability); 679 } 680 681 /** 682 * Update AP configuration. Called when setting update config via 683 * {@link WifiManager#setSoftApConfiguration(SoftApConfiguration)} 684 * 685 * @param config new AP config. 686 */ updateConfiguration(@onNull SoftApConfiguration config)687 public void updateConfiguration(@NonNull SoftApConfiguration config) { 688 mStateMachine.sendMessage(SoftApStateMachine.CMD_UPDATE_CONFIG, config); 689 } 690 691 /** 692 * Retrieve the {@link SoftApModeConfiguration} instance associated with this mode manager. 693 */ getSoftApModeConfiguration()694 public SoftApModeConfiguration getSoftApModeConfiguration() { 695 return new SoftApModeConfiguration( 696 mSpecifiedModeConfiguration.getTargetMode(), 697 mSpecifiedModeConfiguration.getSoftApConfiguration(), 698 mCurrentSoftApCapability, 699 mCountryCode, 700 mSpecifiedModeConfiguration.getTetheringRequest()); 701 } 702 703 /** 704 * Retrieve the name of the Bridged AP iface instance to remove for a downgrade, or null if a 705 * downgrade is not possible. 706 */ getBridgedApDowngradeIfaceInstanceForRemoval()707 public String getBridgedApDowngradeIfaceInstanceForRemoval() { 708 if (!isBridgedMode() || mCurrentSoftApInfoMap.size() == 0) { 709 return null; 710 } 711 List<String> instances = mWifiNative.getBridgedApInstances(mApInterfaceName); 712 if (instances == null || instances.size() == 1) { 713 return null; 714 } 715 return getHighestFrequencyInstance(mCurrentSoftApInfoMap.keySet()); 716 } 717 718 /** 719 * Return true when current softap state is enabled. 720 */ isStarted()721 public boolean isStarted() { 722 return mCurrentApState == WifiManager.WIFI_AP_STATE_ENABLED; 723 } 724 725 /** 726 * Dump info about this softap manager. 727 */ 728 @Override dump(FileDescriptor fd, PrintWriter pw, String[] args)729 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 730 pw.println("Dump of SoftApManager id=" + mId); 731 732 pw.println("current StateMachine mode: " + getCurrentStateName()); 733 pw.println("mRole: " + mRole); 734 pw.println("mApInterfaceName: " + mApInterfaceName); 735 pw.println("mIfaceIsUp: " + mIfaceIsUp); 736 pw.println("mSoftApCountryCode: " + mCountryCode); 737 pw.println( 738 "mSpecifiedModeConfiguration.targetMode: " 739 + mSpecifiedModeConfiguration.getTargetMode()); 740 pw.println("mCurrentSoftApConfiguration: " + mCurrentSoftApConfiguration); 741 pw.println("mCurrentSoftApCapability: " + mCurrentSoftApCapability); 742 pw.println("getConnectedClientList().size(): " + getConnectedClientList().size()); 743 pw.println("mTimeoutEnabled: " + mTimeoutEnabled); 744 pw.println("mBridgedModeOpportunisticsShutdownTimeoutEnabled: " 745 + mBridgedModeOpportunisticsShutdownTimeoutEnabled); 746 pw.println("mCurrentSoftApInfoMap " + mCurrentSoftApInfoMap); 747 pw.println("mStartTimestamp: " + FORMATTER.format(new Date(mStartTimestampMs))); 748 pw.println("mSafeChannelFrequencyList: " + mSafeChannelFrequencyList.stream() 749 .map(Object::toString) 750 .collect(Collectors.joining(","))); 751 mStateMachine.dump(fd, pw, args); 752 } 753 754 @Override enableVerboseLogging(boolean verbose)755 public void enableVerboseLogging(boolean verbose) { 756 mVerboseLoggingEnabled = verbose; 757 } 758 759 @Override toString()760 public String toString() { 761 return "SoftApManager{id=" + getId() 762 + " iface=" + getInterfaceName() 763 + " role=" + getRole() 764 + "}"; 765 } 766 767 /** 768 * A ClientModeImpl instance has been L2 connected. 769 * 770 * @param newPrimary the corresponding ConcreteClientModeManager instance for the ClientModeImpl 771 * that has been L2 connected. 772 */ onL2Connected(@onNull ConcreteClientModeManager clientModeManager)773 private void onL2Connected(@NonNull ConcreteClientModeManager clientModeManager) { 774 Log.d(getTag(), "onL2Connected called"); 775 mStateMachine.sendMessage(SoftApStateMachine.CMD_HANDLE_WIFI_CONNECTED, 776 clientModeManager); 777 } 778 779 getCurrentStateName()780 private String getCurrentStateName() { 781 IState currentState = mStateMachine.getCurrentState(); 782 783 if (currentState != null) { 784 return currentState.getName(); 785 } 786 787 return "StateMachine not active"; 788 } 789 790 /** 791 * Update AP state. 792 * 793 * @param newState new AP state 794 * @param currentState current AP state 795 * @param reason Failure reason if the new AP state is in failure state 796 */ updateApState(int newState, int currentState, int reason)797 private void updateApState(int newState, int currentState, int reason) { 798 mCurrentApState = newState; 799 mSoftApCallback.onStateChanged(new SoftApState( 800 newState, 801 reason, 802 mSpecifiedModeConfiguration.getTetheringRequest(), 803 mApInterfaceName)); 804 805 //send the AP state change broadcast 806 final Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION); 807 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 808 intent.putExtra(WifiManager.EXTRA_WIFI_AP_STATE, newState); 809 intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_AP_STATE, currentState); 810 if (newState == WifiManager.WIFI_AP_STATE_FAILED) { 811 //only set reason number when softAP start failed 812 intent.putExtra(WifiManager.EXTRA_WIFI_AP_FAILURE_REASON, reason); 813 } 814 815 intent.putExtra(WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME, mApInterfaceName); 816 intent.putExtra( 817 WifiManager.EXTRA_WIFI_AP_MODE, mSpecifiedModeConfiguration.getTargetMode()); 818 819 if (SdkLevel.isAtLeastSv2()) { 820 mContext.sendBroadcastAsUser(intent, UserHandle.ALL, 821 android.Manifest.permission.ACCESS_WIFI_STATE); 822 } else { 823 mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); 824 } 825 } 826 setMacAddress()827 private int setMacAddress() { 828 MacAddress mac = mCurrentSoftApConfiguration.getBssid(); 829 830 if (mac == null) { 831 // If no BSSID is explicitly requested, (re-)configure the factory MAC address. Some 832 // drivers may not support setting the MAC at all, so fail soft in this case. 833 if (!mWifiNative.resetApMacToFactoryMacAddress(mApInterfaceName)) { 834 Log.w(getTag(), "failed to reset to factory MAC address; " 835 + "continuing with current MAC"); 836 } 837 } else { 838 if (mWifiNative.isApSetMacAddressSupported(mApInterfaceName)) { 839 if (!mWifiNative.setApMacAddress(mApInterfaceName, mac)) { 840 Log.e(getTag(), "failed to set explicitly requested MAC address"); 841 return START_RESULT_FAILURE_SET_MAC_ADDRESS; 842 } 843 } else if (!mIsUnsetBssid) { 844 // If hardware does not support MAC address setter, 845 // only report the error for non randomization. 846 return START_RESULT_FAILURE_UNSUPPORTED_CONFIG; 847 } 848 } 849 850 return START_RESULT_SUCCESS; 851 } 852 853 /** 854 * Dynamic update the country code when Soft AP enabled. 855 * 856 * @param countryCode 2 byte ASCII string. For ex: US, CA. 857 * @return true if request is sent successfully, false otherwise. 858 */ updateCountryCode(@onNull String countryCode)859 public boolean updateCountryCode(@NonNull String countryCode) { 860 if (ApConfigUtil.isSoftApDynamicCountryCodeSupported(mContext) 861 && mCurrentSoftApCapability.areFeaturesSupported( 862 SoftApCapability.SOFTAP_FEATURE_ACS_OFFLOAD)) { 863 mStateMachine.sendMessage(SoftApStateMachine.CMD_UPDATE_COUNTRY_CODE, countryCode); 864 return true; 865 } 866 return false; 867 } 868 setCountryCode()869 private boolean setCountryCode() { 870 int band = mCurrentSoftApConfiguration.getBand(); 871 if (TextUtils.isEmpty(mCountryCode)) { 872 if (band == SoftApConfiguration.BAND_5GHZ || band == SoftApConfiguration.BAND_6GHZ) { 873 // Country code is mandatory for 5GHz/6GHz band. 874 Log.e(getTag(), "Invalid country code, " 875 + "required for setting up soft ap in band:" + band); 876 return false; 877 } 878 // Absence of country code is not fatal for 2Ghz & Any band options. 879 return true; 880 } 881 if (!mWifiNative.setApCountryCode( 882 mApInterfaceName, mCountryCode.toUpperCase(Locale.ROOT))) { 883 if (band == SoftApConfiguration.BAND_5GHZ || band == SoftApConfiguration.BAND_6GHZ) { 884 // Return an error if failed to set country code when AP is configured for 885 // 5GHz/6GHz band. 886 Log.e(getTag(), "Failed to set country code, " 887 + "required for setting up soft ap in band: " + band); 888 return false; 889 } 890 // Failure to set country code is not fatal for other band options. 891 } 892 return true; 893 } 894 895 /** 896 * Start a soft AP instance as configured. 897 * 898 * @return One of {@link StartResult} 899 */ startSoftAp()900 private @StartResult int startSoftAp() { 901 if (SdkLevel.isAtLeastS()) { 902 Log.d(getTag(), "startSoftAp: channels " + mCurrentSoftApConfiguration.getChannels() 903 + " iface " + mApInterfaceName + " country " + mCountryCode); 904 } else { 905 Log.d(getTag(), "startSoftAp: band " + mCurrentSoftApConfiguration.getBand()); 906 } 907 updateApState(WifiManager.WIFI_AP_STATE_ENABLING, 908 WifiManager.WIFI_AP_STATE_DISABLED, 0); 909 910 int startResult = setMacAddress(); 911 if (startResult != START_RESULT_SUCCESS) { 912 return startResult; 913 } 914 915 // Make a copy of configuration for updating AP band and channel. 916 SoftApConfiguration.Builder localConfigBuilder = 917 new SoftApConfiguration.Builder(mCurrentSoftApConfiguration); 918 919 startResult = ApConfigUtil.updateApChannelConfig( 920 mWifiNative, mCoexManager, mResourceCache, mCountryCode, 921 localConfigBuilder, mCurrentSoftApConfiguration, mCurrentSoftApCapability); 922 if (startResult != START_RESULT_SUCCESS) { 923 Log.e(getTag(), "Failed to update AP band and channel"); 924 return startResult; 925 } 926 927 if (mCurrentSoftApConfiguration.isHiddenSsid()) { 928 Log.d(getTag(), "SoftAP is a hidden network"); 929 } 930 931 if (!ApConfigUtil.checkSupportAllConfiguration( 932 mCurrentSoftApConfiguration, mCurrentSoftApCapability)) { 933 Log.d(getTag(), "Unsupported Configuration detect! config = " 934 + mCurrentSoftApConfiguration); 935 return START_RESULT_FAILURE_UNSUPPORTED_CONFIG; 936 } 937 938 startResult = 939 mWifiNative.startSoftAp( 940 mApInterfaceName, 941 localConfigBuilder.build(), 942 mSpecifiedModeConfiguration.getTargetMode() 943 == WifiManager.IFACE_IP_MODE_TETHERED, 944 mSoftApHalCallback, mIsUsingMlo); 945 if (startResult != START_RESULT_SUCCESS) { 946 Log.e(getTag(), "Soft AP start failed"); 947 return startResult; 948 } 949 950 mWifiDiagnostics.startLogging(mApInterfaceName); 951 mStartTimestampMs = mWifiInjector.getClock().getWallClockMillis(); 952 Log.d(getTag(), "Soft AP is started "); 953 954 return START_RESULT_SUCCESS; 955 } 956 957 /** 958 * Handles a start failure and writes the start failure metrics. 959 * @param startResult One of {@link StartResult}. 960 */ handleStartSoftApFailure(@tartResult int startResult)961 private void handleStartSoftApFailure(@StartResult int startResult) { 962 if (startResult == START_RESULT_SUCCESS) { 963 Log.wtf(TAG, "handleStartSoftApFailure called with START_RESULT_SUCCESS"); 964 return; 965 } 966 967 int wifiManagerFailureReason = WifiManager.SAP_START_FAILURE_GENERAL; 968 if (startResult == START_RESULT_FAILURE_NO_CHANNEL) { 969 wifiManagerFailureReason = WifiManager.SAP_START_FAILURE_NO_CHANNEL; 970 } else if (startResult == START_RESULT_FAILURE_UNSUPPORTED_CONFIG) { 971 wifiManagerFailureReason = WifiManager.SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION; 972 } else if (startResult == START_RESULT_FAILURE_INTERFACE_CONFLICT_USER_REJECTED) { 973 wifiManagerFailureReason = WifiManager.SAP_START_FAILURE_USER_REJECTED; 974 } 975 updateApState(WifiManager.WIFI_AP_STATE_FAILED, 976 mCurrentApState, 977 wifiManagerFailureReason); 978 stopSoftAp(); 979 mWifiMetrics.incrementSoftApStartResult(false, wifiManagerFailureReason); 980 mModeListener.onStartFailure(SoftApManager.this); 981 writeSoftApStartedEvent(startResult); 982 } 983 984 /** 985 * Disconnect all connected clients on active softap interface(s). 986 * This is usually done just before stopSoftAp(). 987 */ disconnectAllClients()988 private void disconnectAllClients() { 989 for (WifiClient client : getConnectedClientList()) { 990 mWifiNative.forceClientDisconnect(mApInterfaceName, client.getMacAddress(), 991 SAP_CLIENT_DISCONNECT_REASON_CODE_UNSPECIFIED); 992 } 993 } 994 995 /** 996 * Teardown soft AP and teardown the interface. 997 */ stopSoftAp()998 private void stopSoftAp() { 999 disconnectAllClients(); 1000 mWifiDiagnostics.stopLogging(mApInterfaceName); 1001 mWifiNative.teardownInterface(mApInterfaceName); 1002 Log.d(getTag(), "Soft AP is stopped"); 1003 } 1004 addClientToPendingDisconnectionList(WifiClient client, int reason)1005 private void addClientToPendingDisconnectionList(WifiClient client, int reason) { 1006 Log.d(getTag(), "Fail to disconnect client: " + client.getMacAddress() 1007 + ", add it into pending list"); 1008 mPendingDisconnectClients.put(client, reason); 1009 mStateMachine.getHandler().removeMessages( 1010 SoftApStateMachine.CMD_FORCE_DISCONNECT_PENDING_CLIENTS); 1011 mStateMachine.sendMessageDelayed( 1012 SoftApStateMachine.CMD_FORCE_DISCONNECT_PENDING_CLIENTS, 1013 SOFT_AP_PENDING_DISCONNECTION_CHECK_DELAY_MS); 1014 } 1015 getConnectedClientList()1016 private List<WifiClient> getConnectedClientList() { 1017 List<WifiClient> connectedClientList = new ArrayList<>(); 1018 for (List<WifiClient> it : mConnectedClientWithApInfoMap.values()) { 1019 connectedClientList.addAll(it); 1020 } 1021 return connectedClientList; 1022 } 1023 checkSoftApClient(SoftApConfiguration config, WifiClient newClient)1024 private boolean checkSoftApClient(SoftApConfiguration config, WifiClient newClient) { 1025 if (!mCurrentSoftApCapability.areFeaturesSupported( 1026 SoftApCapability.SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT)) { 1027 return true; 1028 } 1029 1030 if (mBlockedClientList.contains(newClient.getMacAddress())) { 1031 Log.d(getTag(), "Force disconnect for client: " + newClient + "in blocked list"); 1032 if (!mWifiNative.forceClientDisconnect( 1033 mApInterfaceName, newClient.getMacAddress(), 1034 WifiManager.SAP_CLIENT_BLOCK_REASON_CODE_BLOCKED_BY_USER)) { 1035 addClientToPendingDisconnectionList(newClient, 1036 WifiManager.SAP_CLIENT_BLOCK_REASON_CODE_BLOCKED_BY_USER); 1037 } 1038 return false; 1039 } 1040 if (config.isClientControlByUserEnabled() 1041 && !mAllowedClientList.contains(newClient.getMacAddress())) { 1042 mSoftApCallback.onBlockedClientConnecting(newClient, 1043 WifiManager.SAP_CLIENT_BLOCK_REASON_CODE_BLOCKED_BY_USER); 1044 Log.d(getTag(), "Force disconnect for unauthorized client: " + newClient); 1045 if (!mWifiNative.forceClientDisconnect( 1046 mApInterfaceName, newClient.getMacAddress(), 1047 WifiManager.SAP_CLIENT_BLOCK_REASON_CODE_BLOCKED_BY_USER)) { 1048 addClientToPendingDisconnectionList(newClient, 1049 WifiManager.SAP_CLIENT_BLOCK_REASON_CODE_BLOCKED_BY_USER); 1050 } 1051 return false; 1052 } 1053 int maxConfig = mCurrentSoftApCapability.getMaxSupportedClients(); 1054 if (config.getMaxNumberOfClients() > 0) { 1055 maxConfig = Math.min(maxConfig, config.getMaxNumberOfClients()); 1056 } 1057 1058 if (getConnectedClientList().size() >= maxConfig) { 1059 Log.i(getTag(), "No more room for new client:" + newClient); 1060 if (!mWifiNative.forceClientDisconnect( 1061 mApInterfaceName, newClient.getMacAddress(), 1062 WifiManager.SAP_CLIENT_BLOCK_REASON_CODE_NO_MORE_STAS)) { 1063 addClientToPendingDisconnectionList(newClient, 1064 WifiManager.SAP_CLIENT_BLOCK_REASON_CODE_NO_MORE_STAS); 1065 } 1066 mSoftApCallback.onBlockedClientConnecting(newClient, 1067 WifiManager.SAP_CLIENT_BLOCK_REASON_CODE_NO_MORE_STAS); 1068 // Avoid report the max client blocked in the same settings. 1069 if (!mEverReportMetricsForMaxClient) { 1070 mWifiMetrics.noteSoftApClientBlocked(maxConfig); 1071 mEverReportMetricsForMaxClient = true; 1072 } 1073 return false; 1074 } 1075 return true; 1076 } 1077 1078 private class SoftApStateMachine extends StateMachine { 1079 // Commands for the state machine. 1080 public static final int CMD_START = 0; 1081 public static final int CMD_STOP = 1; 1082 public static final int CMD_FAILURE = 2; 1083 public static final int CMD_INTERFACE_STATUS_CHANGED = 3; 1084 public static final int CMD_ASSOCIATED_STATIONS_CHANGED = 4; 1085 public static final int CMD_NO_ASSOCIATED_STATIONS_TIMEOUT = 5; 1086 public static final int CMD_INTERFACE_DESTROYED = 7; 1087 public static final int CMD_INTERFACE_DOWN = 8; 1088 public static final int CMD_AP_INFO_CHANGED = 9; 1089 public static final int CMD_UPDATE_CAPABILITY = 10; 1090 public static final int CMD_UPDATE_CONFIG = 11; 1091 public static final int CMD_FORCE_DISCONNECT_PENDING_CLIENTS = 12; 1092 public static final int CMD_NO_ASSOCIATED_STATIONS_TIMEOUT_ON_ONE_INSTANCE = 13; 1093 public static final int CMD_SAFE_CHANNEL_FREQUENCY_CHANGED = 14; 1094 public static final int CMD_HANDLE_WIFI_CONNECTED = 15; 1095 public static final int CMD_UPDATE_COUNTRY_CODE = 16; 1096 public static final int CMD_DRIVER_COUNTRY_CODE_CHANGED = 17; 1097 public static final int CMD_DRIVER_COUNTRY_CODE_CHANGE_TIMED_OUT = 18; 1098 public static final int CMD_PLUGGED_STATE_CHANGED = 19; 1099 1100 private final State mActiveState = new ActiveState(); 1101 private final State mIdleState; 1102 private final State mWaitingForDriverCountryCodeChangedState; 1103 private final WaitingState mWaitingForIcmDialogState = new WaitingState(this); 1104 private final State mStartedState; 1105 1106 private final InterfaceCallback mWifiNativeInterfaceCallback = new InterfaceCallback() { 1107 @Override 1108 public void onDestroyed(String ifaceName) { 1109 sendMessage(CMD_INTERFACE_DESTROYED, ifaceName); 1110 } 1111 1112 @Override 1113 public void onUp(String ifaceName) { 1114 if (mApInterfaceName != null && mApInterfaceName.equals(ifaceName)) { 1115 sendMessage(CMD_INTERFACE_STATUS_CHANGED, 1); 1116 } 1117 } 1118 1119 @Override 1120 public void onDown(String ifaceName) { 1121 if (mApInterfaceName != null && mApInterfaceName.equals(ifaceName)) { 1122 sendMessage(CMD_INTERFACE_STATUS_CHANGED, 0); 1123 } 1124 } 1125 }; 1126 SoftApStateMachine(Looper looper)1127 SoftApStateMachine(Looper looper) { 1128 super(TAG, looper); 1129 1130 final int threshold = mResourceCache.getInteger( 1131 R.integer.config_wifiConfigurationWifiRunnerThresholdInMs); 1132 mIdleState = new IdleState(threshold); 1133 mWaitingForDriverCountryCodeChangedState = 1134 new WaitingForDriverCountryCodeChangedState(threshold); 1135 mStartedState = new StartedState(threshold); 1136 // CHECKSTYLE:OFF IndentationCheck 1137 addState(mActiveState); 1138 addState(mIdleState, mActiveState); 1139 addState(mWaitingForDriverCountryCodeChangedState, mActiveState); 1140 addState(mWaitingForIcmDialogState, mActiveState); 1141 addState(mStartedState, mActiveState); 1142 // CHECKSTYLE:ON IndentationCheck 1143 1144 setInitialState(mIdleState); 1145 start(); 1146 } 1147 1148 1149 private class ActiveState extends State { 1150 @Override exit()1151 public void exit() { 1152 mModeListener.onStopped(SoftApManager.this); 1153 mCmiMonitor.unregisterListener(mCmiListener); 1154 } 1155 } 1156 1157 @Override getWhatToString(int what)1158 protected String getWhatToString(int what) { 1159 switch (what) { 1160 case CMD_START: 1161 return "CMD_START"; 1162 case CMD_STOP: 1163 return "CMD_STOP"; 1164 case CMD_FAILURE: 1165 return "CMD_FAILURE"; 1166 case CMD_INTERFACE_STATUS_CHANGED: 1167 return "CMD_INTERFACE_STATUS_CHANGED"; 1168 case CMD_ASSOCIATED_STATIONS_CHANGED: 1169 return "CMD_ASSOCIATED_STATIONS_CHANGED"; 1170 case CMD_NO_ASSOCIATED_STATIONS_TIMEOUT: 1171 return "CMD_NO_ASSOCIATED_STATIONS_TIMEOUT"; 1172 case CMD_INTERFACE_DESTROYED: 1173 return "CMD_INTERFACE_DESTROYED"; 1174 case CMD_INTERFACE_DOWN: 1175 return "CMD_INTERFACE_DOWN"; 1176 case CMD_AP_INFO_CHANGED: 1177 return "CMD_AP_INFO_CHANGED"; 1178 case CMD_UPDATE_CAPABILITY: 1179 return "CMD_UPDATE_CAPABILITY"; 1180 case CMD_UPDATE_CONFIG: 1181 return "CMD_UPDATE_CONFIG"; 1182 case CMD_FORCE_DISCONNECT_PENDING_CLIENTS: 1183 return "CMD_FORCE_DISCONNECT_PENDING_CLIENTS"; 1184 case CMD_NO_ASSOCIATED_STATIONS_TIMEOUT_ON_ONE_INSTANCE: 1185 return "CMD_NO_ASSOCIATED_STATIONS_TIMEOUT_ON_ONE_INSTANCE"; 1186 case CMD_SAFE_CHANNEL_FREQUENCY_CHANGED: 1187 return "CMD_SAFE_CHANNEL_FREQUENCY_CHANGED"; 1188 case CMD_HANDLE_WIFI_CONNECTED: 1189 return "CMD_HANDLE_WIFI_CONNECTED"; 1190 case CMD_UPDATE_COUNTRY_CODE: 1191 return "CMD_UPDATE_COUNTRY_CODE"; 1192 case CMD_DRIVER_COUNTRY_CODE_CHANGED: 1193 return "CMD_DRIVER_COUNTRY_CODE_CHANGED"; 1194 case CMD_DRIVER_COUNTRY_CODE_CHANGE_TIMED_OUT: 1195 return "CMD_DRIVER_COUNTRY_CODE_CHANGE_TIMED_OUT"; 1196 case CMD_PLUGGED_STATE_CHANGED: 1197 return "CMD_PLUGGED_STATE_CHANGED"; 1198 case RunnerState.STATE_ENTER_CMD: 1199 return "Enter"; 1200 case RunnerState.STATE_EXIT_CMD: 1201 return "Exit"; 1202 default: 1203 return "what:" + what; 1204 } 1205 } 1206 1207 private class IdleState extends RunnerState { IdleState(int threshold)1208 IdleState(int threshold) { 1209 super(threshold, mWifiInjector.getWifiHandlerLocalLog()); 1210 } 1211 1212 @Override enterImpl()1213 public void enterImpl() { 1214 mApInterfaceName = null; 1215 mIfaceIsUp = false; 1216 mIfaceIsDestroyed = false; 1217 } 1218 1219 @Override exitImpl()1220 public void exitImpl() { 1221 } 1222 1223 @Override getMessageLogRec(int what)1224 public String getMessageLogRec(int what) { 1225 return SoftApManager.class.getSimpleName() + "." + IdleState.class.getSimpleName() 1226 + "." + getWhatToString(what); 1227 } 1228 1229 @Override processMessageImpl(Message message)1230 public boolean processMessageImpl(Message message) { 1231 switch (message.what) { 1232 case CMD_STOP: 1233 writeSoftApStoppedEvent(STOP_EVENT_STOPPED); 1234 quitNow(); 1235 break; 1236 case CMD_START: 1237 boolean isCountryCodeChanged = false; 1238 boolean shouldwaitForDriverCountryCodeIfNoCountryToSet = false; 1239 mRequestorWs = (WorkSource) message.obj; 1240 WifiSsid wifiSsid = mCurrentSoftApConfiguration != null 1241 ? mCurrentSoftApConfiguration.getWifiSsid() : null; 1242 if (wifiSsid == null || wifiSsid.getBytes().length == 0) { 1243 Log.e(getTag(), "Unable to start soft AP without valid configuration"); 1244 handleStartSoftApFailure(START_RESULT_FAILURE_GENERAL); 1245 break; 1246 } 1247 if (TextUtils.isEmpty(mCountryCode) && mResourceCache 1248 .getBoolean( 1249 R.bool.config_wifiDriverSupportedNl80211RegChangedEvent)) { 1250 Log.i(getTag(), "No country code set in the framework." 1251 + " Should Wait for driver country code update to start AP"); 1252 shouldwaitForDriverCountryCodeIfNoCountryToSet = true; 1253 } 1254 if (!TextUtils.isEmpty(mCountryCode) 1255 && !TextUtils.equals( 1256 mCountryCode, mCurrentSoftApCapability.getCountryCode())) { 1257 isCountryCodeChanged = true; 1258 Log.i(getTag(), "CountryCode changed - " 1259 + " mCountryCode = " + mCountryCode 1260 + ", base country in SoftApCapability = " 1261 + mCurrentSoftApCapability.getCountryCode()); 1262 } 1263 1264 if (isBridgedMode()) { 1265 if (!isCountryCodeChanged) { 1266 SoftApConfiguration tempConfig = 1267 ApConfigUtil.removeUnavailableBandsFromConfig( 1268 mCurrentSoftApConfiguration, 1269 mCurrentSoftApCapability, 1270 mCoexManager, 1271 mContext); 1272 if (tempConfig == null) { 1273 handleStartSoftApFailure( 1274 START_RESULT_FAILURE_UNSUPPORTED_CONFIG); 1275 break; 1276 } 1277 mCurrentSoftApConfiguration = tempConfig; 1278 } 1279 if (!isBridgedApAvailable() 1280 || mCurrentSoftApConfiguration.getBands().length == 1) { 1281 int newSingleApBand = 0; 1282 for (int configuredBand : mCurrentSoftApConfiguration.getBands()) { 1283 newSingleApBand |= configuredBand; 1284 } 1285 newSingleApBand = ApConfigUtil.append24GToBandIf24GSupported( 1286 newSingleApBand, mContext); 1287 Log.i(getTag(), "Fallback to single AP mode with band " 1288 + newSingleApBand); 1289 mCurrentSoftApConfiguration = 1290 new SoftApConfiguration.Builder(mCurrentSoftApConfiguration) 1291 .setBand(newSingleApBand) 1292 .build(); 1293 } 1294 } else if (!isCountryCodeChanged && isBridgedApAvailable() 1295 && mIsUsingPersistentSoftApConfiguration) { 1296 // Try upgrading config to 2 + 5 GHz Dual Band if the available config 1297 // bands only include 2 or 5 Ghz. This is to handle cases where the 1298 // config was previously set to single band in a CC that didn't support 1299 // DBS, but the current one does. 1300 mCurrentSoftApConfiguration = 1301 ApConfigUtil.upgradeTo2g5gBridgedIfAvailableBandsAreSubset( 1302 mCurrentSoftApConfiguration, 1303 mCurrentSoftApCapability, 1304 mContext); 1305 } 1306 1307 // Remove 6GHz from requested bands if security type is restricted 1308 // Note: 6GHz only band is already handled by initial validation 1309 SoftApConfiguration tempConfig = 1310 ApConfigUtil.remove6gBandForUnsupportedSecurity( 1311 mResourceCache, 1312 mCurrentSoftApConfiguration, isBridgedMode()); 1313 if (tempConfig == null) { 1314 handleStartSoftApFailure(START_RESULT_FAILURE_UNSUPPORTED_CONFIG); 1315 break; 1316 } 1317 mCurrentSoftApConfiguration = tempConfig; 1318 // Don't show the ICM dialog if this is for tethering. 1319 boolean bypassDialog = 1320 mSpecifiedModeConfiguration.getTargetMode() 1321 == WifiManager.IFACE_IP_MODE_TETHERED; 1322 int icmResult = mInterfaceConflictManager 1323 .manageInterfaceConflictForStateMachine( 1324 TAG, message, mStateMachine, mWaitingForIcmDialogState, 1325 mIdleState, isBridgeRequired() 1326 ? HalDeviceManager.HDM_CREATE_IFACE_AP_BRIDGE 1327 : HalDeviceManager.HDM_CREATE_IFACE_AP, 1328 mRequestorWs, bypassDialog); 1329 if (icmResult == InterfaceConflictManager.ICM_ABORT_COMMAND) { 1330 Log.e(getTag(), "User refused to set up interface"); 1331 handleStartSoftApFailure( 1332 START_RESULT_FAILURE_INTERFACE_CONFLICT_USER_REJECTED); 1333 break; 1334 } else if (icmResult 1335 == InterfaceConflictManager.ICM_SKIP_COMMAND_WAIT_FOR_USER) { 1336 break; 1337 } 1338 mApInterfaceName = mWifiNative.setupInterfaceForSoftApMode( 1339 mWifiNativeInterfaceCallback, mRequestorWs, 1340 mCurrentSoftApConfiguration.getBand(), isBridgeRequired(), 1341 SoftApManager.this, getVendorData(), mIsUsingMlo); 1342 if (TextUtils.isEmpty(mApInterfaceName)) { 1343 Log.e(getTag(), "setup failure when creating ap interface."); 1344 // Only check if it's possible to create single AP, since a DBS request 1345 // already falls back to single AP if we can't create DBS. 1346 if (!mWifiNative.isItPossibleToCreateApIface(mRequestorWs)) { 1347 handleStartSoftApFailure(START_RESULT_FAILURE_INTERFACE_CONFLICT); 1348 } else { 1349 handleStartSoftApFailure(START_RESULT_FAILURE_CREATE_INTERFACE); 1350 } 1351 break; 1352 } 1353 1354 if (!mIsUsingMlo && SdkLevel.isAtLeastT() 1355 && mCurrentSoftApConfiguration.isIeee80211beEnabled()) { 1356 DeviceWiphyCapabilities capabilities = 1357 mWifiNative.getDeviceWiphyCapabilities( 1358 mApInterfaceName, isBridgeRequired()); 1359 if (!ApConfigUtil.is11beAllowedForThisConfiguration(capabilities, 1360 mContext, mCurrentSoftApConfiguration, isBridgedMode(), 1361 mMaximumNumberOfMLDSupported, mCurrentExistingMLD, 1362 mIsMLDApSupportMLO)) { 1363 Log.d(getTag(), "11BE is not allowed," 1364 + " removing from configuration"); 1365 mCurrentSoftApConfiguration = new SoftApConfiguration.Builder( 1366 mCurrentSoftApConfiguration).setIeee80211beEnabled( 1367 false).build(); 1368 } 1369 } 1370 1371 mSoftApNotifier.dismissSoftApShutdownTimeoutExpiredNotification(); 1372 1373 if (!shouldwaitForDriverCountryCodeIfNoCountryToSet && !setCountryCode()) { 1374 handleStartSoftApFailure(START_RESULT_FAILURE_SET_COUNTRY_CODE); 1375 break; 1376 } 1377 // Wait for driver country code if driver supports regulatory change event. 1378 if (isCountryCodeChanged 1379 || shouldwaitForDriverCountryCodeIfNoCountryToSet) { 1380 Log.i(getTag(), "Need to wait for driver country code update before" 1381 + " starting"); 1382 transitionTo(mWaitingForDriverCountryCodeChangedState); 1383 break; 1384 } 1385 int startResult = startSoftAp(); 1386 if (startResult != START_RESULT_SUCCESS) { 1387 handleStartSoftApFailure(startResult); 1388 break; 1389 } 1390 1391 transitionTo(mStartedState); 1392 break; 1393 case CMD_UPDATE_CAPABILITY: 1394 SoftApCapability capability = (SoftApCapability) message.obj; 1395 mCurrentSoftApCapability = new SoftApCapability(capability); 1396 updateSafeChannelFrequencyList(); 1397 break; 1398 case CMD_UPDATE_CONFIG: { 1399 SoftApConfiguration newConfig = (SoftApConfiguration) message.obj; 1400 mSpecifiedModeConfiguration = 1401 new SoftApModeConfiguration( 1402 mSpecifiedModeConfiguration.getTargetMode(), 1403 newConfig, 1404 mCurrentSoftApCapability, 1405 mCountryCode, 1406 mSpecifiedModeConfiguration.getTetheringRequest()); 1407 Log.d(getTag(), "Configuration changed to " + newConfig); 1408 // Idle mode, update all configurations. 1409 mCurrentSoftApConfiguration = newConfig; 1410 configureInternalConfiguration(); 1411 break; 1412 } 1413 case CMD_UPDATE_COUNTRY_CODE: 1414 String countryCode = (String) message.obj; 1415 if (!TextUtils.isEmpty(countryCode)) { 1416 mCountryCode = countryCode; 1417 } 1418 break; 1419 default: 1420 // Ignore all other commands. 1421 break; 1422 } 1423 1424 return HANDLED; 1425 } 1426 } 1427 isBridgedApAvailable()1428 private boolean isBridgedApAvailable() { 1429 // Skip if bridged mode isn't supported. 1430 if (!ApConfigUtil.isBridgedModeSupported(mContext, mWifiNative)) { 1431 return false; 1432 } 1433 1434 // Checking STA status only when device supports STA + AP concurrency 1435 // since STA would be dropped when device doesn't support it. 1436 final List<ClientModeManager> cmms = 1437 mActiveModeWarden.getClientModeManagers(); 1438 if (cmms.size() != 0 && mWifiNative.isStaApConcurrencySupported()) { 1439 if (ApConfigUtil.isStaWithBridgedModeSupported(mContext, 1440 mWifiNative)) { 1441 for (ClientModeManager cmm 1442 : mActiveModeWarden.getClientModeManagers()) { 1443 WifiInfo wifiConnectedInfo = cmm.getConnectionInfo(); 1444 int wifiFrequency = wifiConnectedInfo.getFrequency(); 1445 if (wifiFrequency > 0 1446 && !mSafeChannelFrequencyList.contains( 1447 wifiFrequency)) { 1448 Log.d(getTag(), "Wifi connected to unavailable freq: " 1449 + wifiFrequency); 1450 return false; 1451 } 1452 } 1453 } else { 1454 // The client mode exist but DUT doesn't support 1455 // STA + bridged AP, we should fallback to single AP mode. 1456 Log.d(getTag(), " STA iface exist but device doesn't support STA + Bridged AP"); 1457 return false; 1458 } 1459 } 1460 1461 // Fallback if the target country code is world mode. 1462 if (mCountryCode != null && mCountryCode.equalsIgnoreCase( 1463 mResourceCache.getString( 1464 R.string.config_wifiDriverWorldModeCountryCode))) { 1465 Log.i(getTag(), "Country code changed to world mode - fallback to single AP"); 1466 return false; 1467 } 1468 1469 // Fall back to Single AP if it's not possible to create a Bridged AP. 1470 if (!mWifiNative.isItPossibleToCreateBridgedApIface(mRequestorWs)) { 1471 Log.i(getTag(), "Not possible to create bridged AP iface - fallback to single AP"); 1472 return false; 1473 } 1474 1475 // Fall back to single AP if creating a single AP does not require 1476 // destroying an exististng iface, but creating a bridged AP does. 1477 if (mWifiNative.shouldDowngradeToSingleApForConcurrency(mRequestorWs)) { 1478 Log.d(getTag(), "Creating bridged AP will destroy an existing" 1479 + " iface, but single AP will not."); 1480 return false; 1481 } 1482 1483 return true; 1484 } 1485 1486 private class WaitingForDriverCountryCodeChangedState extends RunnerState { 1487 private static final int TIMEOUT_MS = 5_000; 1488 1489 private final WifiCountryCode.ChangeListener mCountryCodeChangeListener = 1490 countryCode -> sendMessage(CMD_DRIVER_COUNTRY_CODE_CHANGED, countryCode); 1491 WaitingForDriverCountryCodeChangedState(int threshold)1492 WaitingForDriverCountryCodeChangedState(int threshold) { 1493 super(threshold, mWifiInjector.getWifiHandlerLocalLog()); 1494 } 1495 1496 @Override enterImpl()1497 public void enterImpl() { 1498 mWifiInjector.getWifiCountryCode().registerListener(mCountryCodeChangeListener); 1499 sendMessageDelayed(CMD_DRIVER_COUNTRY_CODE_CHANGE_TIMED_OUT, TIMEOUT_MS); 1500 } 1501 1502 @Override exitImpl()1503 public void exitImpl() { 1504 mWifiInjector.getWifiCountryCode().unregisterListener(mCountryCodeChangeListener); 1505 removeMessages(CMD_DRIVER_COUNTRY_CODE_CHANGE_TIMED_OUT); 1506 } 1507 1508 @Override processMessageImpl(Message message)1509 public boolean processMessageImpl(Message message) { 1510 if (message.what == CMD_DRIVER_COUNTRY_CODE_CHANGED) { 1511 if (!TextUtils.isEmpty(mCountryCode) 1512 && !TextUtils.equals(mCountryCode, (String) message.obj)) { 1513 Log.i(getTag(), "Ignore country code changed: " + message.obj); 1514 return HANDLED; 1515 } 1516 Log.i(getTag(), "Driver country code change to " + message.obj 1517 + ", continue starting."); 1518 mCountryCode = (String) message.obj; 1519 mCurrentSoftApCapability.setCountryCode(mCountryCode); 1520 mCurrentSoftApCapability = 1521 ApConfigUtil.updateSoftApCapabilityWithAvailableChannelList( 1522 mCurrentSoftApCapability, mContext, mWifiNative, null); 1523 updateSafeChannelFrequencyList(); 1524 int[] oldBands = mCurrentSoftApConfiguration.getBands(); 1525 if (isBridgedApAvailable() && mIsUsingPersistentSoftApConfiguration) { 1526 mCurrentSoftApConfiguration = 1527 ApConfigUtil.upgradeTo2g5gBridgedIfAvailableBandsAreSubset( 1528 mCurrentSoftApConfiguration, 1529 mCurrentSoftApCapability, 1530 mContext); 1531 } 1532 if (isBridgedMode()) { 1533 SoftApConfiguration tempConfig = 1534 ApConfigUtil.removeUnavailableBandsFromConfig( 1535 mCurrentSoftApConfiguration, 1536 mCurrentSoftApCapability, mCoexManager, mContext); 1537 if (tempConfig == null) { 1538 handleStartSoftApFailure(START_RESULT_FAILURE_UNSUPPORTED_CONFIG); 1539 transitionTo(mIdleState); 1540 return HANDLED; 1541 } 1542 mCurrentSoftApConfiguration = tempConfig; 1543 if (mCurrentSoftApConfiguration.getBands().length != oldBands.length) { 1544 Log.i(getTag(), "Restarting AP interface to accommodate band change" 1545 + " from " + Arrays.toString(oldBands) + " to " 1546 + Arrays.toString(mCurrentSoftApConfiguration.getBands())); 1547 mWifiNative.teardownInterface(mApInterfaceName); 1548 mApInterfaceName = mWifiNative.setupInterfaceForSoftApMode( 1549 mWifiNativeInterfaceCallback, mRequestorWs, 1550 mCurrentSoftApConfiguration.getBand(), isBridgeRequired(), 1551 SoftApManager.this, getVendorData(), mIsUsingMlo); 1552 if (TextUtils.isEmpty(mApInterfaceName)) { 1553 Log.e(getTag(), "setup failure when creating single AP iface"); 1554 handleStartSoftApFailure(START_RESULT_FAILURE_GENERAL); 1555 transitionTo(mIdleState); 1556 return HANDLED; 1557 } 1558 } 1559 } 1560 } else if (message.what == CMD_DRIVER_COUNTRY_CODE_CHANGE_TIMED_OUT) { 1561 Log.i(getTag(), "Timed out waiting for driver country code change, " 1562 + "continue starting anyway."); 1563 } else { 1564 Log.i(getTag(), "Defer " + getWhatToString(message.what) 1565 + " while waiting for driver country code change."); 1566 deferMessage(message); 1567 return HANDLED; 1568 } 1569 int startResult = startSoftAp(); 1570 if (startResult != START_RESULT_SUCCESS) { 1571 handleStartSoftApFailure(startResult); 1572 transitionTo(mIdleState); 1573 return HANDLED; 1574 } 1575 transitionTo(mStartedState); 1576 return HANDLED; 1577 } 1578 1579 @Override getMessageLogRec(int what)1580 public String getMessageLogRec(int what) { 1581 return SoftApManager.class.getSimpleName() + "." + RunnerState.class.getSimpleName() 1582 + "." + getWhatToString(what); 1583 } 1584 } 1585 1586 private class StartedState extends RunnerState { StartedState(int threshold)1587 StartedState(int threshold) { 1588 super(threshold, mWifiInjector.getWifiHandlerLocalLog()); 1589 } 1590 1591 BroadcastReceiver mBatteryPluggedReceiver = new BroadcastReceiver() { 1592 @Override 1593 public void onReceive(Context context, Intent intent) { 1594 sendMessage(CMD_PLUGGED_STATE_CHANGED, 1595 intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0)); 1596 } 1597 }; 1598 rescheduleBothBridgedInstancesTimeoutMessage()1599 private void rescheduleBothBridgedInstancesTimeoutMessage() { 1600 Set<String> instances = mCurrentSoftApInfoMap.keySet(); 1601 String HighestFrequencyInstance = getHighestFrequencyInstance(instances); 1602 // Schedule bridged mode timeout on all instances if needed 1603 for (String instance : instances) { 1604 long timeout = getShutdownIdleInstanceInBridgedModeTimeoutMillis(); 1605 if (!TextUtils.equals(instance, HighestFrequencyInstance)) { 1606 // Make sure that we shutdown the higher frequency instance first by adding 1607 // offset for lower frequency instance. 1608 timeout += SCHEDULE_IDLE_INSTANCE_SHUTDOWN_TIMEOUT_DELAY_MS; 1609 } 1610 rescheduleTimeoutMessageIfNeeded(instance, timeout); 1611 } 1612 } 1613 1614 /** 1615 * Schedule timeout message depends on Soft Ap instance 1616 * 1617 * @param changedInstance the schedule should change on specific instance only. 1618 * If changedInstance is mApInterfaceName, it means that 1619 * we need to reschedule all of timeout message. 1620 */ rescheduleTimeoutMessages(@onNull String changedInstance)1621 private void rescheduleTimeoutMessages(@NonNull String changedInstance) { 1622 // Don't trigger bridged mode shutdown timeout when only one active instance 1623 // In Dual AP, one instance may already be closed due to LTE coexistence or DFS 1624 // restrictions or due to inactivity. i.e. mCurrentSoftApInfoMap.size() is 1) 1625 if (isBridgedMode() && mCurrentSoftApInfoMap.size() == 2) { 1626 if (TextUtils.equals(mApInterfaceName, changedInstance)) { 1627 rescheduleBothBridgedInstancesTimeoutMessage(); 1628 } else { 1629 rescheduleTimeoutMessageIfNeeded(changedInstance, 1630 getShutdownIdleInstanceInBridgedModeTimeoutMillis()); 1631 } 1632 } 1633 1634 // Always evaluate timeout schedule on tetheringInterface 1635 rescheduleTimeoutMessageIfNeeded(mApInterfaceName, getShutdownTimeoutMillis()); 1636 } 1637 removeIfaceInstanceFromBridgedApIface(String instanceName)1638 private void removeIfaceInstanceFromBridgedApIface(String instanceName) { 1639 if (TextUtils.isEmpty(instanceName)) { 1640 return; 1641 } 1642 if (mCurrentSoftApInfoMap.containsKey(instanceName)) { 1643 Log.i(getTag(), "remove instance " + instanceName + "(" 1644 + mCurrentSoftApInfoMap.get(instanceName).getFrequency() 1645 + ") from bridged iface " + mApInterfaceName); 1646 mWifiNative.removeIfaceInstanceFromBridgedApIface(mApInterfaceName, 1647 instanceName, mIsUsingMlo); 1648 // Remove the info and update it. 1649 updateSoftApInfo(mCurrentSoftApInfoMap.get(instanceName), true); 1650 } 1651 } 1652 1653 /** 1654 * Schedule the timeout message when timeout control is enabled and there is no client 1655 * connect to the instance. 1656 * 1657 * @param instance The key of the {@code mSoftApTimeoutMessageMap}, 1658 * @see mSoftApTimeoutMessageMap for details. 1659 */ rescheduleTimeoutMessageIfNeeded(String instance, long timeoutValue)1660 private void rescheduleTimeoutMessageIfNeeded(String instance, long timeoutValue) { 1661 final boolean isTetheringInterface = 1662 TextUtils.equals(mApInterfaceName, instance); 1663 final boolean timeoutEnabled = isTetheringInterface ? mTimeoutEnabled 1664 : (mBridgedModeOpportunisticsShutdownTimeoutEnabled && !mIsPlugged); 1665 final int clientNumber = isTetheringInterface 1666 ? getConnectedClientList().size() 1667 : mConnectedClientWithApInfoMap.get(instance).size(); 1668 Log.d(getTag(), "rescheduleTimeoutMessageIfNeeded " + instance + ", timeoutEnabled=" 1669 + timeoutEnabled + ", isPlugged=" + mIsPlugged + ", clientNumber=" 1670 + clientNumber); 1671 if (!timeoutEnabled || clientNumber != 0) { 1672 cancelTimeoutMessage(instance); 1673 return; 1674 } 1675 scheduleTimeoutMessage(instance, timeoutValue); 1676 } 1677 scheduleTimeoutMessage(String instance, long timeout)1678 private void scheduleTimeoutMessage(String instance, long timeout) { 1679 if (mSoftApTimeoutMessageMap.containsKey(instance)) { 1680 mSoftApTimeoutMessageMap.get(instance).schedule( 1681 SystemClock.elapsedRealtime() + timeout); 1682 Log.d(getTag(), "Timeout message scheduled, on " + instance + ", delay = " 1683 + timeout); 1684 } 1685 } 1686 cancelTimeoutMessage(String instance)1687 private void cancelTimeoutMessage(String instance) { 1688 if (mSoftApTimeoutMessageMap.containsKey(instance)) { 1689 mSoftApTimeoutMessageMap.get(instance).cancel(); 1690 Log.d(getTag(), "Timeout message canceled on " + instance); 1691 } 1692 } 1693 1694 /** 1695 * When configuration changed, it need to force some clients disconnect to match the 1696 * configuration. 1697 */ updateClientConnection()1698 private void updateClientConnection() { 1699 if (!mCurrentSoftApCapability.areFeaturesSupported( 1700 SoftApCapability.SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT)) { 1701 return; 1702 } 1703 final int maxAllowedClientsByHardwareAndCarrier = 1704 mCurrentSoftApCapability.getMaxSupportedClients(); 1705 final int userApConfigMaxClientCount = 1706 mCurrentSoftApConfiguration.getMaxNumberOfClients(); 1707 int finalMaxClientCount = maxAllowedClientsByHardwareAndCarrier; 1708 if (userApConfigMaxClientCount > 0) { 1709 finalMaxClientCount = Math.min(userApConfigMaxClientCount, 1710 maxAllowedClientsByHardwareAndCarrier); 1711 } 1712 List<WifiClient> currentClients = getConnectedClientList(); 1713 int targetDisconnectClientNumber = currentClients.size() - finalMaxClientCount; 1714 List<WifiClient> allowedConnectedList = new ArrayList<>(); 1715 Iterator<WifiClient> iterator = currentClients.iterator(); 1716 while (iterator.hasNext()) { 1717 WifiClient client = iterator.next(); 1718 if (mBlockedClientList.contains(client.getMacAddress()) 1719 || (mCurrentSoftApConfiguration.isClientControlByUserEnabled() 1720 && !mAllowedClientList.contains(client.getMacAddress()))) { 1721 Log.d(getTag(), "Force disconnect for not allowed client: " + client); 1722 if (!mWifiNative.forceClientDisconnect( 1723 mApInterfaceName, client.getMacAddress(), 1724 WifiManager.SAP_CLIENT_BLOCK_REASON_CODE_BLOCKED_BY_USER)) { 1725 addClientToPendingDisconnectionList(client, 1726 WifiManager.SAP_CLIENT_BLOCK_REASON_CODE_BLOCKED_BY_USER); 1727 } 1728 targetDisconnectClientNumber--; 1729 } else { 1730 allowedConnectedList.add(client); 1731 } 1732 } 1733 1734 if (targetDisconnectClientNumber > 0) { 1735 Iterator<WifiClient> allowedClientIterator = allowedConnectedList.iterator(); 1736 while (allowedClientIterator.hasNext()) { 1737 if (targetDisconnectClientNumber == 0) break; 1738 WifiClient allowedClient = allowedClientIterator.next(); 1739 Log.d(getTag(), "Force disconnect for client due to no more room: " 1740 + allowedClient); 1741 if (!mWifiNative.forceClientDisconnect( 1742 mApInterfaceName, allowedClient.getMacAddress(), 1743 WifiManager.SAP_CLIENT_BLOCK_REASON_CODE_NO_MORE_STAS)) { 1744 addClientToPendingDisconnectionList(allowedClient, 1745 WifiManager.SAP_CLIENT_BLOCK_REASON_CODE_NO_MORE_STAS); 1746 } 1747 targetDisconnectClientNumber--; 1748 } 1749 } 1750 } 1751 1752 /** 1753 * Set stations associated with this soft AP 1754 * @param client The station for which connection state changed. 1755 * @param isConnected True for the connection changed to connect, otherwise false. 1756 */ updateConnectedClients(WifiClient client, boolean isConnected)1757 private void updateConnectedClients(WifiClient client, boolean isConnected) { 1758 if (client == null) { 1759 return; 1760 } 1761 1762 if (null != mPendingDisconnectClients.remove(client)) { 1763 Log.d(getTag(), "Remove client: " + client.getMacAddress() 1764 + "from pending disconnectionlist"); 1765 } 1766 1767 String apInstanceIdentifier = client.getApInstanceIdentifier(); 1768 List clientList = mConnectedClientWithApInfoMap.computeIfAbsent( 1769 apInstanceIdentifier, k -> new ArrayList<>()); 1770 int index = clientList.indexOf(client); 1771 1772 if ((index != -1) == isConnected) { 1773 Log.e(getTag(), "Drop client connection event, client " 1774 + client + "isConnected: " + isConnected 1775 + " , duplicate event or client is blocked"); 1776 return; 1777 } 1778 if (isConnected) { 1779 boolean isAllow = checkSoftApClient(mCurrentSoftApConfiguration, client); 1780 if (isAllow) { 1781 clientList.add(client); 1782 mMaxConnectedClients = clientList.size() > mMaxConnectedClients 1783 ? clientList.size() : mMaxConnectedClients; 1784 } else { 1785 return; 1786 } 1787 } else { 1788 if (null == clientList.remove(index)) { 1789 Log.e(getTag(), "client doesn't exist in list, it should NOT happen"); 1790 } 1791 } 1792 1793 // Update clients list. 1794 mConnectedClientWithApInfoMap.put(apInstanceIdentifier, clientList); 1795 SoftApInfo currentInfoWithClientsChanged = mCurrentSoftApInfoMap 1796 .get(apInstanceIdentifier); 1797 Log.d(getTag(), "The connected wifi stations have changed with count: " 1798 + clientList.size() + ": " + clientList + " on the AP which info is " 1799 + currentInfoWithClientsChanged); 1800 1801 if (mSoftApCallback != null) { 1802 if (Flags.softapDisconnectReason() && !isConnected) { 1803 // Client successfully disconnected, should also notify callback 1804 mWifiMetrics.reportOnClientsDisconnected(client.getDisconnectReason(), 1805 mRequestorWs); 1806 mSoftApCallback.onClientsDisconnected( 1807 currentInfoWithClientsChanged, 1808 ImmutableList.of(client)); 1809 } 1810 1811 mSoftApCallback.onConnectedClientsOrInfoChanged(mCurrentSoftApInfoMap, 1812 mConnectedClientWithApInfoMap, isBridgeRequired()); 1813 } else { 1814 Log.e(getTag(), 1815 "SoftApCallback is null. Dropping ConnectedClientsChanged event."); 1816 } 1817 1818 mWifiMetrics.addSoftApNumAssociatedStationsChangedEvent( 1819 getConnectedClientList().size(), 1820 mConnectedClientWithApInfoMap.get(apInstanceIdentifier).size(), 1821 mSpecifiedModeConfiguration.getTargetMode(), 1822 mCurrentSoftApInfoMap.get(apInstanceIdentifier)); 1823 1824 rescheduleTimeoutMessages(apInstanceIdentifier); 1825 } 1826 1827 /** 1828 * @param apInfo, the new SoftApInfo changed. Null used to clean up. 1829 */ updateSoftApInfo(@ullable SoftApInfo apInfo, boolean isRemoved)1830 private void updateSoftApInfo(@Nullable SoftApInfo apInfo, boolean isRemoved) { 1831 Log.d(getTag(), "SoftApInfo update " + apInfo + ", isRemoved: " + isRemoved); 1832 if (apInfo == null) { 1833 // Clean up 1834 mCurrentSoftApInfoMap.clear(); 1835 mConnectedClientWithApInfoMap.clear(); 1836 mSoftApCallback.onConnectedClientsOrInfoChanged(mCurrentSoftApInfoMap, 1837 mConnectedClientWithApInfoMap, isBridgeRequired()); 1838 return; 1839 } 1840 String changedInstance = apInfo.getApInstanceIdentifier(); 1841 if (apInfo.equals(mCurrentSoftApInfoMap.get(changedInstance))) { 1842 if (isRemoved) { 1843 boolean isClientConnected = 1844 mConnectedClientWithApInfoMap.get(changedInstance).size() > 0; 1845 mCurrentSoftApInfoMap.remove(changedInstance); 1846 mSoftApTimeoutMessageMap.remove(changedInstance); 1847 mConnectedClientWithApInfoMap.remove(changedInstance); 1848 mSoftApCallback.onConnectedClientsOrInfoChanged(mCurrentSoftApInfoMap, 1849 mConnectedClientWithApInfoMap, isBridgeRequired()); 1850 if (isClientConnected) { 1851 mWifiMetrics.addSoftApNumAssociatedStationsChangedEvent( 1852 getConnectedClientList().size(), 1853 0, 1854 mSpecifiedModeConfiguration.getTargetMode(), 1855 apInfo); 1856 } 1857 if (isBridgeRequired()) { 1858 mWifiMetrics.addSoftApInstanceDownEventInDualMode( 1859 mSpecifiedModeConfiguration.getTargetMode(), apInfo); 1860 } 1861 } 1862 return; 1863 } 1864 1865 // Make sure an empty client list is created when info updated 1866 List clientList = mConnectedClientWithApInfoMap.computeIfAbsent( 1867 changedInstance, k -> new ArrayList<>()); 1868 1869 if (clientList.size() != 0) { 1870 Log.e(getTag(), "The info: " + apInfo 1871 + " changed when client connected, it should NOT happen!!"); 1872 } 1873 1874 mCurrentSoftApInfoMap.put(changedInstance, new SoftApInfo(apInfo)); 1875 mSoftApCallback.onConnectedClientsOrInfoChanged(mCurrentSoftApInfoMap, 1876 mConnectedClientWithApInfoMap, isBridgeRequired()); 1877 1878 boolean isNeedToScheduleTimeoutMessage = false; 1879 if (!mSoftApTimeoutMessageMap.containsKey(mApInterfaceName)) { 1880 // First info update, create WakeupMessage for mApInterfaceName. 1881 mSoftApTimeoutMessageMap.put(mApInterfaceName, new WakeupMessage( 1882 mContext, mStateMachine.getHandler(), 1883 SOFT_AP_SEND_MESSAGE_TIMEOUT_TAG + mApInterfaceName, 1884 SoftApStateMachine.CMD_NO_ASSOCIATED_STATIONS_TIMEOUT)); 1885 isNeedToScheduleTimeoutMessage = true; 1886 } 1887 1888 if (isBridgedMode() 1889 && !mSoftApTimeoutMessageMap.containsKey(changedInstance)) { 1890 mSoftApTimeoutMessageMap.put(changedInstance, 1891 new WakeupMessage(mContext, mStateMachine.getHandler(), 1892 SOFT_AP_SEND_MESSAGE_TIMEOUT_TAG + changedInstance, 1893 SoftApStateMachine.CMD_NO_ASSOCIATED_STATIONS_TIMEOUT_ON_ONE_INSTANCE, 1894 0, 0, changedInstance)); 1895 isNeedToScheduleTimeoutMessage = true; 1896 } 1897 1898 // Trigger schedule after mCurrentSoftApInfoMap is updated. 1899 if (isNeedToScheduleTimeoutMessage) { 1900 rescheduleTimeoutMessages(mApInterfaceName); 1901 } 1902 1903 // ignore invalid freq and softap disable case for metrics 1904 if (apInfo.getFrequency() > 0 1905 && apInfo.getBandwidth() != SoftApInfo.CHANNEL_WIDTH_INVALID) { 1906 mWifiMetrics.addSoftApChannelSwitchedEvent( 1907 new ArrayList<>(mCurrentSoftApInfoMap.values()), 1908 mSpecifiedModeConfiguration.getTargetMode(), 1909 isBridgeRequired()); 1910 updateUserBandPreferenceViolationMetricsIfNeeded(apInfo); 1911 } 1912 } 1913 onUpChanged(boolean isUp)1914 private void onUpChanged(boolean isUp) { 1915 if (isUp == mIfaceIsUp) { 1916 return; // no change 1917 } 1918 1919 mIfaceIsUp = isUp; 1920 if (isUp) { 1921 Log.d(getTag(), "SoftAp is ready for use"); 1922 updateApState(WifiManager.WIFI_AP_STATE_ENABLED, 1923 WifiManager.WIFI_AP_STATE_ENABLING, 0); 1924 mModeListener.onStarted(SoftApManager.this); 1925 mWifiMetrics.incrementSoftApStartResult(true, 0); 1926 mCurrentSoftApInfoMap.clear(); 1927 mConnectedClientWithApInfoMap.clear(); 1928 if (mSoftApCallback != null) { 1929 mSoftApCallback.onConnectedClientsOrInfoChanged(mCurrentSoftApInfoMap, 1930 mConnectedClientWithApInfoMap, isBridgeRequired()); 1931 } 1932 } else { 1933 // the interface was up, but goes down 1934 sendMessage(CMD_INTERFACE_DOWN); 1935 } 1936 mWifiMetrics.addSoftApUpChangedEvent( 1937 isUp, 1938 mSpecifiedModeConfiguration.getTargetMode(), 1939 mDefaultShutdownTimeoutMillis, 1940 isBridgeRequired()); 1941 if (isUp) { 1942 mWifiMetrics.updateSoftApConfiguration( 1943 mCurrentSoftApConfiguration, 1944 mSpecifiedModeConfiguration.getTargetMode(), 1945 isBridgeRequired()); 1946 mWifiMetrics.updateSoftApCapability( 1947 mCurrentSoftApCapability, 1948 mSpecifiedModeConfiguration.getTargetMode(), 1949 isBridgeRequired()); 1950 } 1951 } 1952 1953 @Override enterImpl()1954 public void enterImpl() { 1955 mIfaceIsUp = false; 1956 mIfaceIsDestroyed = false; 1957 onUpChanged(mWifiNative.isInterfaceUp(mApInterfaceName)); 1958 1959 Handler handler = mStateMachine.getHandler(); 1960 if (SdkLevel.isAtLeastS()) { 1961 mCoexManager.registerCoexListener(mCoexListener); 1962 } 1963 if (mIsDisableShutDownBridgedModeIdleInstanceTimerWhenPlugged) { 1964 Intent stickyIntent = mContext.registerReceiver(mBatteryPluggedReceiver, 1965 new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); 1966 mIsPlugged = stickyIntent.getIntExtra(BatteryManager.EXTRA_STATUS, 0) != 0; 1967 } 1968 mSarManager.setSapWifiState(WifiManager.WIFI_AP_STATE_ENABLED); 1969 Log.d(getTag(), "Resetting connected clients on start"); 1970 mConnectedClientWithApInfoMap.clear(); 1971 mPendingDisconnectClients.clear(); 1972 mEverReportMetricsForMaxClient = false; 1973 writeSoftApStartedEvent(START_RESULT_SUCCESS); 1974 } 1975 1976 @Override exitImpl()1977 public void exitImpl() { 1978 if (!mIfaceIsDestroyed) { 1979 stopSoftAp(); 1980 } 1981 if (SdkLevel.isAtLeastS()) { 1982 mCoexManager.unregisterCoexListener(mCoexListener); 1983 } 1984 if (getConnectedClientList().size() != 0) { 1985 Log.d(getTag(), "Resetting num stations on stop"); 1986 for (List<WifiClient> it : mConnectedClientWithApInfoMap.values()) { 1987 if (it.size() != 0) { 1988 mWifiMetrics.addSoftApNumAssociatedStationsChangedEvent( 1989 0, 1990 0, 1991 mSpecifiedModeConfiguration.getTargetMode(), 1992 mCurrentSoftApInfoMap.get(it.get(0).getApInstanceIdentifier())); 1993 } 1994 } 1995 mConnectedClientWithApInfoMap.clear(); 1996 if (mSoftApCallback != null) { 1997 mSoftApCallback.onConnectedClientsOrInfoChanged(mCurrentSoftApInfoMap, 1998 mConnectedClientWithApInfoMap, isBridgeRequired()); 1999 } 2000 } 2001 mPendingDisconnectClients.clear(); 2002 for (String key : mSoftApTimeoutMessageMap.keySet()) { 2003 cancelTimeoutMessage(key); 2004 } 2005 mSoftApTimeoutMessageMap.clear(); 2006 if (mIsDisableShutDownBridgedModeIdleInstanceTimerWhenPlugged) { 2007 mContext.unregisterReceiver(mBatteryPluggedReceiver); 2008 } 2009 // Need this here since we are exiting |Started| state and won't handle any 2010 // future CMD_INTERFACE_STATUS_CHANGED events after this point 2011 mWifiMetrics.addSoftApUpChangedEvent( 2012 false, 2013 mSpecifiedModeConfiguration.getTargetMode(), 2014 mDefaultShutdownTimeoutMillis, 2015 isBridgeRequired()); 2016 updateApState(WifiManager.WIFI_AP_STATE_DISABLED, 2017 WifiManager.WIFI_AP_STATE_DISABLING, 0); 2018 2019 mSarManager.setSapWifiState(WifiManager.WIFI_AP_STATE_DISABLED); 2020 2021 mApInterfaceName = null; 2022 mIfaceIsUp = false; 2023 mIfaceIsDestroyed = false; 2024 mRole = null; 2025 updateSoftApInfo(null, false); 2026 } 2027 updateUserBandPreferenceViolationMetricsIfNeeded(SoftApInfo apInfo)2028 private void updateUserBandPreferenceViolationMetricsIfNeeded(SoftApInfo apInfo) { 2029 // The band preference violation only need to detect in single AP mode. 2030 if (isBridgeRequired()) return; 2031 int band = mCurrentSoftApConfiguration.getBand(); 2032 boolean bandPreferenceViolated = 2033 (ScanResult.is24GHz(apInfo.getFrequency()) 2034 && !ApConfigUtil.containsBand(band, 2035 SoftApConfiguration.BAND_2GHZ)) 2036 || (ScanResult.is5GHz(apInfo.getFrequency()) 2037 && !ApConfigUtil.containsBand(band, 2038 SoftApConfiguration.BAND_5GHZ)) 2039 || (ScanResult.is6GHz(apInfo.getFrequency()) 2040 && !ApConfigUtil.containsBand(band, 2041 SoftApConfiguration.BAND_6GHZ)); 2042 2043 if (bandPreferenceViolated) { 2044 Log.e(getTag(), "Channel does not satisfy user band preference: " 2045 + apInfo.getFrequency()); 2046 mWifiMetrics.incrementNumSoftApUserBandPreferenceUnsatisfied(); 2047 } 2048 } 2049 2050 @Override getMessageLogRec(int what)2051 public String getMessageLogRec(int what) { 2052 return SoftApManager.class.getSimpleName() + "." + RunnerState.class.getSimpleName() 2053 + "." + getWhatToString(what); 2054 } 2055 2056 @Override processMessageImpl(Message message)2057 public boolean processMessageImpl(Message message) { 2058 switch (message.what) { 2059 case CMD_ASSOCIATED_STATIONS_CHANGED: 2060 if (!(message.obj instanceof WifiClient)) { 2061 Log.e(getTag(), "Invalid type returned for" 2062 + " CMD_ASSOCIATED_STATIONS_CHANGED"); 2063 break; 2064 } 2065 boolean isConnected = (message.arg1 == 1); 2066 WifiClient client = (WifiClient) message.obj; 2067 Log.d(getTag(), "CMD_ASSOCIATED_STATIONS_CHANGED, Client: " 2068 + client.getMacAddress().toString() + " isConnected: " 2069 + isConnected + " disconnectReason: " 2070 + client.getDisconnectReason()); 2071 updateConnectedClients(client, isConnected); 2072 break; 2073 case CMD_AP_INFO_CHANGED: 2074 if (!(message.obj instanceof SoftApInfo)) { 2075 Log.e(getTag(), "Invalid type returned for" 2076 + " CMD_AP_INFO_CHANGED"); 2077 break; 2078 } 2079 SoftApInfo apInfo = (SoftApInfo) message.obj; 2080 if (apInfo.getFrequency() < 0) { 2081 Log.e(getTag(), "Invalid ap channel frequency: " 2082 + apInfo.getFrequency()); 2083 break; 2084 } 2085 // Update shutdown timeout 2086 apInfo.setAutoShutdownTimeoutMillis(mTimeoutEnabled 2087 ? getShutdownTimeoutMillis() : 0); 2088 updateSoftApInfo(apInfo, false); 2089 break; 2090 case CMD_INTERFACE_STATUS_CHANGED: 2091 boolean isUp = message.arg1 == 1; 2092 onUpChanged(isUp); 2093 break; 2094 case CMD_STOP: 2095 if (mIfaceIsUp) { 2096 updateApState(WifiManager.WIFI_AP_STATE_DISABLING, 2097 WifiManager.WIFI_AP_STATE_ENABLED, 0); 2098 } else { 2099 updateApState(WifiManager.WIFI_AP_STATE_DISABLING, 2100 WifiManager.WIFI_AP_STATE_ENABLING, 0); 2101 } 2102 writeSoftApStoppedEvent(STOP_EVENT_STOPPED); 2103 quitNow(); 2104 break; 2105 case CMD_START: 2106 // Already started, ignore this command. 2107 break; 2108 case CMD_NO_ASSOCIATED_STATIONS_TIMEOUT: 2109 if (!mTimeoutEnabled) { 2110 Log.i(getTag(), "Timeout message received while timeout is disabled." 2111 + " Dropping."); 2112 break; 2113 } 2114 if (getConnectedClientList().size() != 0) { 2115 Log.i(getTag(), "Timeout message received but has clients. " 2116 + "Dropping."); 2117 break; 2118 } 2119 mSoftApNotifier.showSoftApShutdownTimeoutExpiredNotification(); 2120 Log.i(getTag(), "Timeout message received. Stopping soft AP."); 2121 updateApState(WifiManager.WIFI_AP_STATE_DISABLING, 2122 WifiManager.WIFI_AP_STATE_ENABLED, 0); 2123 writeSoftApStoppedEvent(STOP_EVENT_NO_USAGE_TIMEOUT); 2124 quitNow(); 2125 break; 2126 case CMD_NO_ASSOCIATED_STATIONS_TIMEOUT_ON_ONE_INSTANCE: 2127 String idleInstance = (String) message.obj; 2128 if (!isBridgedMode() || mCurrentSoftApInfoMap.size() != 2) { 2129 Log.d(getTag(), "Ignore Bridged Mode Timeout message received" 2130 + " in single AP state. Dropping it from " + idleInstance); 2131 break; 2132 } 2133 if (!mBridgedModeOpportunisticsShutdownTimeoutEnabled) { 2134 Log.i(getTag(), "Bridged Mode Timeout message received" 2135 + " while timeout is disabled. Dropping."); 2136 break; 2137 } 2138 Log.d(getTag(), "Instance idle timout on " + idleInstance); 2139 removeIfaceInstanceFromBridgedApIface(idleInstance); 2140 break; 2141 case CMD_INTERFACE_DESTROYED: 2142 String ifaceName = (String) message.obj; 2143 Log.d(getTag(), "Interface: " + ifaceName + " was cleanly destroyed."); 2144 if (mApInterfaceName == null) { 2145 Log.e(getTag(), "softAp interface is null" 2146 + " - Drop interface destroyed message"); 2147 break; 2148 } 2149 if (!mApInterfaceName.equals(ifaceName)) { 2150 Log.d(getTag(), "Drop stale interface destroyed message"); 2151 break; 2152 } 2153 updateApState(WifiManager.WIFI_AP_STATE_DISABLING, 2154 WifiManager.WIFI_AP_STATE_ENABLED, 0); 2155 mIfaceIsDestroyed = true; 2156 writeSoftApStoppedEvent(STOP_EVENT_INTERFACE_DESTROYED); 2157 quitNow(); 2158 break; 2159 case CMD_FAILURE: 2160 String instance = (String) message.obj; 2161 if (isBridgedMode()) { 2162 List<String> instances = 2163 mWifiNative.getBridgedApInstances(mApInterfaceName); 2164 if (instance != null) { 2165 Log.i(getTag(), "receive instanceFailure on " + instance); 2166 removeIfaceInstanceFromBridgedApIface(instance); 2167 instances = 2168 mWifiNative.getBridgedApInstances(mApInterfaceName); 2169 // Check if there's any instance still active. 2170 if (instances != null && instances.size() > 0) { 2171 break; 2172 } 2173 } else if (mCurrentSoftApInfoMap.size() == 1 && instances != null 2174 && instances.size() == 1) { 2175 if (!mCurrentSoftApInfoMap.containsKey(instances.get(0))) { 2176 // there is an available instance but the info doesn't be 2177 // updated, keep AP on and remove unavailable instance info. 2178 for (String unavailableInstance 2179 : mCurrentSoftApInfoMap.keySet()) { 2180 removeIfaceInstanceFromBridgedApIface(unavailableInstance); 2181 } 2182 break; 2183 } 2184 } 2185 } 2186 Log.w(getTag(), "hostapd failure, stop and report failure"); 2187 writeSoftApStoppedEvent(STOP_EVENT_HOSTAPD_FAILURE); 2188 /* fall through */ 2189 case CMD_INTERFACE_DOWN: 2190 Log.w(getTag(), "interface error, stop and report failure"); 2191 updateApState(WifiManager.WIFI_AP_STATE_FAILED, 2192 WifiManager.WIFI_AP_STATE_ENABLED, 2193 WifiManager.SAP_START_FAILURE_GENERAL); 2194 updateApState(WifiManager.WIFI_AP_STATE_DISABLING, 2195 WifiManager.WIFI_AP_STATE_FAILED, 0); 2196 writeSoftApStoppedEvent(STOP_EVENT_INTERFACE_DOWN); 2197 quitNow(); 2198 break; 2199 case CMD_UPDATE_CAPABILITY: 2200 SoftApCapability capability = (SoftApCapability) message.obj; 2201 mCurrentSoftApCapability = new SoftApCapability(capability); 2202 mWifiMetrics.updateSoftApCapability( 2203 mCurrentSoftApCapability, 2204 mSpecifiedModeConfiguration.getTargetMode(), 2205 isBridgeRequired()); 2206 updateClientConnection(); 2207 updateSafeChannelFrequencyList(); 2208 break; 2209 case CMD_UPDATE_CONFIG: { 2210 SoftApConfiguration newConfig = (SoftApConfiguration) message.obj; 2211 SoftApConfiguration originalConfig = 2212 mSpecifiedModeConfiguration.getSoftApConfiguration(); 2213 if (!ApConfigUtil.checkConfigurationChangeNeedToRestart( 2214 originalConfig, newConfig)) { 2215 mSpecifiedModeConfiguration = 2216 new SoftApModeConfiguration( 2217 mSpecifiedModeConfiguration.getTargetMode(), 2218 newConfig, 2219 mCurrentSoftApCapability, 2220 mCountryCode, 2221 mSpecifiedModeConfiguration.getTetheringRequest()); 2222 Log.d(getTag(), "Configuration changed to " + newConfig); 2223 if (mCurrentSoftApConfiguration.getMaxNumberOfClients() 2224 != newConfig.getMaxNumberOfClients()) { 2225 Log.d(getTag(), "Max Client changed, reset to record the metrics"); 2226 mEverReportMetricsForMaxClient = false; 2227 } 2228 boolean needRescheduleTimeoutMessage = 2229 mCurrentSoftApConfiguration.getShutdownTimeoutMillis() 2230 != newConfig.getShutdownTimeoutMillis() 2231 || mTimeoutEnabled != newConfig.isAutoShutdownEnabled() 2232 || mBridgedModeOpportunisticsShutdownTimeoutEnabled 2233 != newConfig 2234 .isBridgedModeOpportunisticShutdownEnabledInternal(); 2235 updateChangeableConfiguration(newConfig); 2236 updateClientConnection(); 2237 if (needRescheduleTimeoutMessage) { 2238 for (String key : mSoftApTimeoutMessageMap.keySet()) { 2239 cancelTimeoutMessage(key); 2240 } 2241 rescheduleTimeoutMessages(mApInterfaceName); 2242 // Update SoftApInfo 2243 for (SoftApInfo info : mCurrentSoftApInfoMap.values()) { 2244 SoftApInfo newInfo = new SoftApInfo(info); 2245 newInfo.setAutoShutdownTimeoutMillis(mTimeoutEnabled 2246 ? getShutdownTimeoutMillis() : 0); 2247 updateSoftApInfo(newInfo, false); 2248 } 2249 } 2250 mWifiMetrics.updateSoftApConfiguration( 2251 mCurrentSoftApConfiguration, 2252 mSpecifiedModeConfiguration.getTargetMode(), 2253 isBridgeRequired()); 2254 } else { 2255 Log.d(getTag(), "Ignore the config: " + newConfig 2256 + " update since it requires restart"); 2257 } 2258 break; 2259 } 2260 case CMD_UPDATE_COUNTRY_CODE: 2261 String countryCode = (String) message.obj; 2262 if (!TextUtils.isEmpty(countryCode) 2263 && !TextUtils.equals(mCountryCode, countryCode) 2264 && mWifiNative.setApCountryCode( 2265 mApInterfaceName, countryCode.toUpperCase(Locale.ROOT))) { 2266 Log.i(getTag(), "Update country code when Soft AP enabled from " 2267 + mCountryCode + " to " + countryCode); 2268 mCountryCode = countryCode; 2269 } 2270 break; 2271 case CMD_FORCE_DISCONNECT_PENDING_CLIENTS: 2272 if (mPendingDisconnectClients.size() != 0) { 2273 Log.d(getTag(), "Disconnect pending list is NOT empty"); 2274 mPendingDisconnectClients.forEach((pendingClient, reason)-> 2275 mWifiNative.forceClientDisconnect(mApInterfaceName, 2276 pendingClient.getMacAddress(), reason)); 2277 sendMessageDelayed( 2278 SoftApStateMachine.CMD_FORCE_DISCONNECT_PENDING_CLIENTS, 2279 SOFT_AP_PENDING_DISCONNECTION_CHECK_DELAY_MS); 2280 } 2281 break; 2282 case CMD_SAFE_CHANNEL_FREQUENCY_CHANGED: 2283 updateSafeChannelFrequencyList(); 2284 if (!isBridgedMode() || mCurrentSoftApInfoMap.size() != 2) { 2285 Log.d(getTag(), "Ignore safe channel changed in single AP state"); 2286 break; 2287 } 2288 Set<String> unavailableInstances = new HashSet<>(); 2289 for (SoftApInfo currentInfo : mCurrentSoftApInfoMap.values()) { 2290 int sapFreq = currentInfo.getFrequency(); 2291 if (!mSafeChannelFrequencyList.contains(sapFreq)) { 2292 int sapBand = ApConfigUtil.convertFrequencyToBand(sapFreq); 2293 if (sapBand != ApConfigUtil.removeUnavailableBands( 2294 mCurrentSoftApCapability, 2295 sapBand, mCoexManager)) { 2296 unavailableInstances.add(currentInfo.getApInstanceIdentifier()); 2297 } 2298 } 2299 } 2300 removeIfaceInstanceFromBridgedApIface( 2301 getHighestFrequencyInstance(unavailableInstances)); 2302 break; 2303 case CMD_HANDLE_WIFI_CONNECTED: 2304 if (!isBridgeRequired() || mCurrentSoftApInfoMap.size() != 2) { 2305 Log.d(getTag(), "Ignore wifi connected in single AP state"); 2306 break; 2307 } 2308 ConcreteClientModeManager cmm = (ConcreteClientModeManager) message.obj; 2309 String wifiInterface = cmm.getInterfaceName(); 2310 WifiInfo wifiInfo = cmm.getConnectionInfo(); 2311 int wifiFreq = wifiInfo.getFrequency(); 2312 int wifiBand = ApConfigUtil.convertFrequencyToBand(wifiFreq); 2313 List<Integer> bands = new ArrayList<Integer>(); 2314 bands.add(wifiBand); 2315 String targetShutDownInstance = ""; 2316 if (wifiFreq > 0 && !mSafeChannelFrequencyList.contains(wifiFreq)) { 2317 Log.i(getTag(), "Wifi connected to freq:" + wifiFreq 2318 + " which is unavailable for SAP"); 2319 for (SoftApInfo sapInfo : mCurrentSoftApInfoMap.values()) { 2320 int sapBand = 2321 ApConfigUtil.convertFrequencyToBand(sapInfo.getFrequency()); 2322 if (sapBand == wifiBand) { 2323 targetShutDownInstance = sapInfo.getApInstanceIdentifier(); 2324 Log.d(getTag(), "Remove the " + targetShutDownInstance 2325 + " instance which is running on the same band as " 2326 + "the wifi connection on an unsafe channel"); 2327 } else { 2328 bands.add(sapBand); 2329 } 2330 } 2331 // Wifi may connect to different band as the SAP. For instances: 2332 // Wifi connect to 6Ghz but bridged AP is running on 2.4Ghz + 5Ghz. 2333 // In this case, targetShutDownInstance will be empty, check whether 2334 // the chip supports this combination. If not, shutdown the highest 2335 // frequency instance. 2336 if (TextUtils.isEmpty(targetShutDownInstance)) { 2337 // We have to use STA ifacename to query band combinations. 2338 if (!mWifiNative.isBandCombinationSupported(wifiInterface, bands)) { 2339 removeIfaceInstanceFromBridgedApIface( 2340 getHighestFrequencyInstance( 2341 mCurrentSoftApInfoMap.keySet())); 2342 } 2343 } else { 2344 removeIfaceInstanceFromBridgedApIface(targetShutDownInstance); 2345 } 2346 } 2347 break; 2348 case CMD_PLUGGED_STATE_CHANGED: 2349 boolean newIsPlugged = (message.arg1 != 0); 2350 if (mIsPlugged != newIsPlugged) { 2351 mIsPlugged = newIsPlugged; 2352 if (mCurrentSoftApInfoMap.size() == 2) { 2353 rescheduleBothBridgedInstancesTimeoutMessage(); 2354 } 2355 } 2356 break; 2357 default: 2358 return NOT_HANDLED; 2359 } 2360 return HANDLED; 2361 } 2362 } 2363 } 2364 2365 // Logging code 2366 getCurrentStaFreqMhz()2367 private int getCurrentStaFreqMhz() { 2368 int staFreqMhz = WifiInfo.UNKNOWN_FREQUENCY; 2369 for (ClientModeManager cmm : mActiveModeWarden.getClientModeManagers()) { 2370 WifiInfo wifiConnectedInfo = cmm.getConnectionInfo(); 2371 if (wifiConnectedInfo != null) { 2372 staFreqMhz = wifiConnectedInfo.getFrequency(); 2373 break; 2374 } 2375 } 2376 return staFreqMhz; 2377 } 2378 2379 /** 2380 * Writes the SoftApStarted event to metrics. Only the first call will write the metrics, any 2381 * subsequent calls will be ignored. 2382 */ writeSoftApStartedEvent(@tartResult int startResult)2383 public void writeSoftApStartedEvent(@StartResult int startResult) { 2384 if (mIsSoftApStartedEventWritten) { 2385 return; 2386 } 2387 mIsSoftApStartedEventWritten = true; 2388 int band1 = WifiScanner.WIFI_BAND_UNSPECIFIED; 2389 int band2 = WifiScanner.WIFI_BAND_UNSPECIFIED; 2390 @SoftApConfiguration.SecurityType int securityType = SoftApConfiguration.SECURITY_TYPE_OPEN; 2391 if (mCurrentSoftApConfiguration != null) { 2392 int[] bands = mCurrentSoftApConfiguration.getBands(); 2393 if (bands.length >= 1) { 2394 band1 = bands[0]; 2395 } 2396 if (bands.length >= 2) { 2397 band2 = bands[1]; 2398 } 2399 securityType = mCurrentSoftApConfiguration.getSecurityType(); 2400 } 2401 mWifiMetrics.writeSoftApStartedEvent(startResult, 2402 getRole(), 2403 band1, 2404 band2, 2405 ApConfigUtil.isBridgedModeSupported(mContext, mWifiNative), 2406 mWifiNative.isStaApConcurrencySupported(), 2407 ApConfigUtil.isStaWithBridgedModeSupported(mContext, mWifiNative), 2408 getCurrentStaFreqMhz(), 2409 securityType, 2410 mRequestorWs); 2411 } 2412 writeSoftApStoppedEvent(@topEvent int stopEvent)2413 private void writeSoftApStoppedEvent(@StopEvent int stopEvent) { 2414 @WifiScanner.WifiBand int band = WifiScanner.WIFI_BAND_UNSPECIFIED; 2415 @WifiAnnotations.WifiStandard int standard = ScanResult.WIFI_STANDARD_UNKNOWN; 2416 for (SoftApInfo info : mCurrentSoftApInfoMap.values()) { 2417 band |= ScanResult.toBand(info.getFrequency()); 2418 if (SdkLevel.isAtLeastS()) { 2419 standard = info.getWifiStandard(); 2420 } 2421 } 2422 @SoftApConfiguration.SecurityType int securityType = SoftApConfiguration.SECURITY_TYPE_OPEN; 2423 if (mCurrentSoftApConfiguration != null) { 2424 securityType = mCurrentSoftApConfiguration.getSecurityType(); 2425 } 2426 int durationSeconds = 2427 (int) ((mWifiInjector.getClock().getWallClockMillis() - mStartTimestampMs) / 1000); 2428 // TODO(b/245824786): Fill out the rest of the fields 2429 mWifiMetrics.writeSoftApStoppedEvent( 2430 stopEvent, 2431 getRole(), 2432 band, 2433 isBridgedMode(), 2434 mWifiNative.isStaApConcurrencySupported(), 2435 ApConfigUtil.isStaWithBridgedModeSupported(mContext, mWifiNative), 2436 getCurrentStaFreqMhz(), 2437 mDefaultShutdownTimeoutMillis > 0, 2438 durationSeconds, 2439 securityType, 2440 standard, 2441 mMaxConnectedClients, 2442 mDefaultShutdownIdleInstanceInBridgedModeTimeoutMillis > 0, 2443 -1, 2444 -1, 2445 null); 2446 } 2447 } 2448