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 static com.android.server.wifi.util.ApConfigUtil.ERROR_GENERIC; 22 import static com.android.server.wifi.util.ApConfigUtil.ERROR_NO_CHANNEL; 23 import static com.android.server.wifi.util.ApConfigUtil.ERROR_UNSUPPORTED_CONFIGURATION; 24 import static com.android.server.wifi.util.ApConfigUtil.SUCCESS; 25 26 import android.annotation.NonNull; 27 import android.annotation.Nullable; 28 import android.content.Intent; 29 import android.net.MacAddress; 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.WifiAnnotations; 35 import android.net.wifi.WifiClient; 36 import android.net.wifi.WifiInfo; 37 import android.net.wifi.WifiManager; 38 import android.os.Handler; 39 import android.os.Looper; 40 import android.os.Message; 41 import android.os.SystemClock; 42 import android.os.UserHandle; 43 import android.os.WorkSource; 44 import android.text.TextUtils; 45 import android.util.Log; 46 47 import com.android.internal.annotations.VisibleForTesting; 48 import com.android.internal.util.IState; 49 import com.android.internal.util.Preconditions; 50 import com.android.internal.util.State; 51 import com.android.internal.util.StateMachine; 52 import com.android.internal.util.WakeupMessage; 53 import com.android.modules.utils.build.SdkLevel; 54 import com.android.server.wifi.WifiNative.InterfaceCallback; 55 import com.android.server.wifi.WifiNative.SoftApListener; 56 import com.android.server.wifi.coex.CoexManager; 57 import com.android.server.wifi.coex.CoexManager.CoexListener; 58 import com.android.server.wifi.util.ApConfigUtil; 59 import com.android.wifi.resources.R; 60 61 import java.io.FileDescriptor; 62 import java.io.PrintWriter; 63 import java.text.SimpleDateFormat; 64 import java.util.ArrayList; 65 import java.util.Collections; 66 import java.util.Date; 67 import java.util.HashMap; 68 import java.util.HashSet; 69 import java.util.Iterator; 70 import java.util.List; 71 import java.util.Locale; 72 import java.util.Map; 73 import java.util.Set; 74 75 /** 76 * Manage WiFi in AP mode. 77 * The internal state machine runs under the ClientModeImpl handler thread context. 78 */ 79 public class SoftApManager implements ActiveModeManager { 80 private static final String TAG = "SoftApManager"; 81 82 @VisibleForTesting 83 public static final String SOFT_AP_SEND_MESSAGE_TIMEOUT_TAG = TAG 84 + " Soft AP Send Message Timeout"; 85 @VisibleForTesting 86 public static final String SOFT_AP_SEND_MESSAGE_IDLE_IN_BRIDGED_MODE_TIMEOUT_TAG = TAG 87 + " Soft AP Send Message Bridged Mode Idle Timeout"; 88 89 90 private final WifiContext mContext; 91 private final FrameworkFacade mFrameworkFacade; 92 private final WifiNative mWifiNative; 93 // This will only be null if SdkLevel is not at least S 94 @Nullable private final CoexManager mCoexManager; 95 private final ClientModeImplMonitor mCmiMonitor; 96 private final ActiveModeWarden mActiveModeWarden; 97 private final SoftApNotifier mSoftApNotifier; 98 99 @VisibleForTesting 100 static final long SOFT_AP_PENDING_DISCONNECTION_CHECK_DELAY_MS = 1000; 101 102 private String mCountryCode; 103 104 private final SoftApStateMachine mStateMachine; 105 106 private final Listener<SoftApManager> mModeListener; 107 private final WifiServiceImpl.SoftApCallbackInternal mSoftApCallback; 108 109 private String mApInterfaceName; 110 private boolean mIfaceIsUp; 111 private boolean mIfaceIsDestroyed; 112 113 private final WifiApConfigStore mWifiApConfigStore; 114 115 private final WifiMetrics mWifiMetrics; 116 private final long mId; 117 118 private boolean mIsUnsetBssid; 119 120 private boolean mVerboseLoggingEnabled = false; 121 122 /** 123 * Original configuration, which is the passed configuration when init or 124 * the user-configured {@code WifiApConfigStore#getApConfiguration}tethering} 125 * settings when input is null. 126 * 127 * Use it when doing configuration update to know if the input configuration was changed. 128 * For others use case, it should use {@code mCurrentSoftApConfiguration}. 129 */ 130 @NonNull 131 private final SoftApModeConfiguration mOriginalModeConfiguration; 132 133 134 /** 135 * Current Soft AP configuration which is used to start Soft AP. 136 * The configuration may be changed because 137 * 1. bssid is changed because MAC randomization 138 * 2. bands are changed because fallback to single AP mode mechanism. 139 */ 140 @Nullable 141 private SoftApConfiguration mCurrentSoftApConfiguration; 142 143 @NonNull 144 private Map<String, SoftApInfo> mCurrentSoftApInfoMap = new HashMap<>(); 145 146 @NonNull 147 private SoftApCapability mCurrentSoftApCapability; 148 149 private Map<String, List<WifiClient>> mConnectedClientWithApInfoMap = new HashMap<>(); 150 @VisibleForTesting 151 Map<WifiClient, Integer> mPendingDisconnectClients = new HashMap<>(); 152 153 private boolean mTimeoutEnabled = false; 154 private boolean mBridgedModeOpportunisticsShutdownTimeoutEnabled = false; 155 156 private final SarManager mSarManager; 157 158 private String mStartTimestamp; 159 160 private long mDefaultShutdownTimeoutMillis; 161 162 private long mDefaultShutdownIdleInstanceInBridgedModeTimeoutMillis; 163 164 private static final SimpleDateFormat FORMATTER = new SimpleDateFormat("MM-dd HH:mm:ss.SSS"); 165 166 private WifiDiagnostics mWifiDiagnostics; 167 168 @Nullable 169 private SoftApRole mRole = null; 170 @Nullable 171 private WorkSource mRequestorWs = null; 172 173 private boolean mEverReportMetricsForMaxClient = false; 174 175 @NonNull 176 private Set<MacAddress> mBlockedClientList = new HashSet<>(); 177 178 @NonNull 179 private Set<MacAddress> mAllowedClientList = new HashSet<>(); 180 181 @NonNull 182 private Set<Integer> mSafeChannelFrequencyList = new HashSet<>(); 183 184 @VisibleForTesting 185 public WakeupMessage mSoftApTimeoutMessage; 186 @VisibleForTesting 187 public WakeupMessage mSoftApBridgedModeIdleInstanceTimeoutMessage; 188 189 // Internal flag which is used to avoid the timer re-schedule. 190 private boolean mIsBridgedModeIdleInstanceTimerActive = false; 191 192 /** 193 * Listener for soft AP events. 194 */ 195 private final SoftApListener mSoftApListener = new SoftApListener() { 196 @Override 197 public void onFailure() { 198 mStateMachine.sendMessage(SoftApStateMachine.CMD_FAILURE); 199 } 200 201 @Override 202 public void onInfoChanged(String apIfaceInstance, int frequency, 203 @WifiAnnotations.Bandwidth int bandwidth, 204 @WifiAnnotations.WifiStandard int generation, 205 MacAddress apIfaceInstanceMacAddress) { 206 SoftApInfo apInfo = new SoftApInfo(); 207 apInfo.setFrequency(frequency); 208 apInfo.setBandwidth(bandwidth); 209 apInfo.setWifiStandard(generation); 210 if (apIfaceInstanceMacAddress != null) { 211 apInfo.setBssid(apIfaceInstanceMacAddress); 212 } 213 apInfo.setApInstanceIdentifier(apIfaceInstance != null 214 ? apIfaceInstance : mApInterfaceName); 215 mStateMachine.sendMessage( 216 SoftApStateMachine.CMD_AP_INFO_CHANGED, 0, 0, apInfo); 217 } 218 219 @Override 220 public void onConnectedClientsChanged(String apIfaceInstance, MacAddress clientAddress, 221 boolean isConnected) { 222 if (clientAddress != null) { 223 WifiClient client = new WifiClient(clientAddress, apIfaceInstance != null 224 ? apIfaceInstance : mApInterfaceName); 225 mStateMachine.sendMessage(SoftApStateMachine.CMD_ASSOCIATED_STATIONS_CHANGED, 226 isConnected ? 1 : 0, 0, client); 227 } else { 228 Log.e(getTag(), "onConnectedClientsChanged: Invalid type returned"); 229 } 230 } 231 }; 232 233 private final CoexListener mCoexListener = new CoexListener() { 234 @Override 235 public void onCoexUnsafeChannelsChanged() { 236 if (mCurrentSoftApConfiguration == null) { 237 return; 238 } 239 mStateMachine.sendMessage(SoftApStateMachine.CMD_SAFE_CHANNEL_FREQUENCY_CHANGED); 240 } 241 }; 242 updateSafeChannelFrequencyList()243 private void updateSafeChannelFrequencyList() { 244 if (!SdkLevel.isAtLeastS() || mCurrentSoftApConfiguration == null) { 245 return; 246 } 247 mSafeChannelFrequencyList.clear(); 248 for (int configuredBand : mCurrentSoftApConfiguration.getBands()) { 249 for (int band : SoftApConfiguration.BAND_TYPES) { 250 if ((band & configuredBand) == 0) { 251 continue; 252 } 253 for (int channel : mCurrentSoftApCapability.getSupportedChannelList(band)) { 254 mSafeChannelFrequencyList.add( 255 ApConfigUtil.convertChannelToFrequency(channel, band)); 256 } 257 } 258 } 259 if ((mCoexManager.getCoexRestrictions() & WifiManager.COEX_RESTRICTION_SOFTAP) != 0) { 260 mSafeChannelFrequencyList.removeAll( 261 ApConfigUtil.getUnsafeChannelFreqsFromCoex(mCoexManager)); 262 } 263 Log.d(getTag(), "SafeChannelFrequencyList = " + mSafeChannelFrequencyList); 264 } 265 configureInternalConfiguration()266 private void configureInternalConfiguration() { 267 if (mCurrentSoftApConfiguration == null) { 268 return; 269 } 270 mBlockedClientList = new HashSet<>(mCurrentSoftApConfiguration.getBlockedClientList()); 271 mAllowedClientList = new HashSet<>(mCurrentSoftApConfiguration.getAllowedClientList()); 272 mTimeoutEnabled = mCurrentSoftApConfiguration.isAutoShutdownEnabled(); 273 mBridgedModeOpportunisticsShutdownTimeoutEnabled = 274 mCurrentSoftApConfiguration.isBridgedModeOpportunisticShutdownEnabledInternal(); 275 } 276 updateChangeableConfiguration(SoftApConfiguration newConfig)277 private void updateChangeableConfiguration(SoftApConfiguration newConfig) { 278 if (mCurrentSoftApConfiguration == null || newConfig == null) { 279 return; 280 } 281 /** 282 * update configurations only which mentioned in WifiManager#setSoftApConfiguration 283 */ 284 SoftApConfiguration.Builder newConfigurBuilder = 285 new SoftApConfiguration.Builder(mCurrentSoftApConfiguration) 286 .setAllowedClientList(newConfig.getAllowedClientList()) 287 .setBlockedClientList(newConfig.getBlockedClientList()) 288 .setClientControlByUserEnabled(newConfig.isClientControlByUserEnabled()) 289 .setMaxNumberOfClients(newConfig.getMaxNumberOfClients()) 290 .setShutdownTimeoutMillis(newConfig.getShutdownTimeoutMillis()) 291 .setAutoShutdownEnabled(newConfig.isAutoShutdownEnabled()); 292 if (SdkLevel.isAtLeastS()) { 293 newConfigurBuilder.setBridgedModeOpportunisticShutdownEnabled( 294 newConfig.isBridgedModeOpportunisticShutdownEnabledInternal()); 295 } 296 mCurrentSoftApConfiguration = newConfigurBuilder.build(); 297 configureInternalConfiguration(); 298 } 299 SoftApManager( @onNull WifiContext context, @NonNull Looper looper, @NonNull FrameworkFacade framework, @NonNull WifiNative wifiNative, @NonNull CoexManager coexManager, String countryCode, @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)300 public SoftApManager( 301 @NonNull WifiContext context, 302 @NonNull Looper looper, 303 @NonNull FrameworkFacade framework, 304 @NonNull WifiNative wifiNative, 305 @NonNull CoexManager coexManager, 306 String countryCode, 307 @NonNull Listener<SoftApManager> listener, 308 @NonNull WifiServiceImpl.SoftApCallbackInternal callback, 309 @NonNull WifiApConfigStore wifiApConfigStore, 310 @NonNull SoftApModeConfiguration apConfig, 311 @NonNull WifiMetrics wifiMetrics, 312 @NonNull SarManager sarManager, 313 @NonNull WifiDiagnostics wifiDiagnostics, 314 @NonNull SoftApNotifier softApNotifier, 315 @NonNull ClientModeImplMonitor cmiMonitor, 316 @NonNull ActiveModeWarden activeModeWarden, 317 long id, 318 @NonNull WorkSource requestorWs, 319 @NonNull SoftApRole role, 320 boolean verboseLoggingEnabled) { 321 mContext = context; 322 mFrameworkFacade = framework; 323 mSoftApNotifier = softApNotifier; 324 mWifiNative = wifiNative; 325 mCoexManager = coexManager; 326 mCountryCode = countryCode; 327 mModeListener = listener; 328 mSoftApCallback = callback; 329 mWifiApConfigStore = wifiApConfigStore; 330 mCurrentSoftApConfiguration = apConfig.getSoftApConfiguration(); 331 mCurrentSoftApCapability = apConfig.getCapability(); 332 // null is a valid input and means we use the user-configured tethering settings. 333 if (mCurrentSoftApConfiguration == null) { 334 mCurrentSoftApConfiguration = mWifiApConfigStore.getApConfiguration(); 335 // may still be null if we fail to load the default config 336 } 337 // Store mode configuration before update the configuration. 338 mOriginalModeConfiguration = new SoftApModeConfiguration(apConfig.getTargetMode(), 339 mCurrentSoftApConfiguration, mCurrentSoftApCapability); 340 if (mCurrentSoftApConfiguration != null) { 341 mIsUnsetBssid = mCurrentSoftApConfiguration.getBssid() == null; 342 if (mCurrentSoftApCapability.areFeaturesSupported( 343 SoftApCapability.SOFTAP_FEATURE_MAC_ADDRESS_CUSTOMIZATION)) { 344 mCurrentSoftApConfiguration = mWifiApConfigStore.randomizeBssidIfUnset( 345 mContext, mCurrentSoftApConfiguration); 346 } 347 } 348 mWifiMetrics = wifiMetrics; 349 mSarManager = sarManager; 350 mWifiDiagnostics = wifiDiagnostics; 351 mStateMachine = new SoftApStateMachine(looper); 352 configureInternalConfiguration(); 353 mDefaultShutdownTimeoutMillis = mContext.getResources().getInteger( 354 R.integer.config_wifiFrameworkSoftApShutDownTimeoutMilliseconds); 355 mDefaultShutdownIdleInstanceInBridgedModeTimeoutMillis = mContext.getResources().getInteger( 356 R.integer 357 .config_wifiFrameworkSoftApShutDownIdleInstanceInBridgedModeTimeoutMillisecond); 358 mCmiMonitor = cmiMonitor; 359 mActiveModeWarden = activeModeWarden; 360 mCmiMonitor.registerListener(new ClientModeImplListener() { 361 @Override 362 public void onL2Connected(@NonNull ConcreteClientModeManager clientModeManager) { 363 SoftApManager.this.onL2Connected(clientModeManager); 364 } 365 }); 366 updateSafeChannelFrequencyList(); 367 mId = id; 368 mRole = role; 369 enableVerboseLogging(verboseLoggingEnabled); 370 mStateMachine.sendMessage(SoftApStateMachine.CMD_START, requestorWs); 371 } 372 373 @Override getId()374 public long getId() { 375 return mId; 376 } 377 getTag()378 private String getTag() { 379 return TAG + "[" + (mApInterfaceName == null ? "unknown" : mApInterfaceName) + "]"; 380 } 381 382 /** 383 * Stop soft AP. 384 */ 385 @Override stop()386 public void stop() { 387 Log.d(getTag(), " currentstate: " + getCurrentStateName()); 388 mStateMachine.sendMessage(SoftApStateMachine.CMD_STOP); 389 } 390 isBridgedMode()391 private boolean isBridgedMode() { 392 return (SdkLevel.isAtLeastS() && mCurrentSoftApConfiguration != null 393 && mCurrentSoftApConfiguration.getBands().length > 1); 394 } 395 getShutdownTimeoutMillis()396 private long getShutdownTimeoutMillis() { 397 long timeout = mCurrentSoftApConfiguration.getShutdownTimeoutMillis(); 398 return timeout > 0 ? timeout : mDefaultShutdownTimeoutMillis; 399 } 400 401 @Override getRole()402 @Nullable public SoftApRole getRole() { 403 return mRole; 404 } 405 406 @Override getPreviousRole()407 @Nullable public ClientRole getPreviousRole() { 408 return null; 409 } 410 411 @Override getLastRoleChangeSinceBootMs()412 public long getLastRoleChangeSinceBootMs() { 413 return 0; 414 } 415 416 /** Set the role of this SoftApManager */ setRole(SoftApRole role)417 public void setRole(SoftApRole role) { 418 // softap does not allow in-place switching of roles. 419 Preconditions.checkState(mRole == null); 420 mRole = role; 421 } 422 423 @Override getInterfaceName()424 public String getInterfaceName() { 425 return mApInterfaceName; 426 } 427 428 @Override getRequestorWs()429 public WorkSource getRequestorWs() { 430 return mRequestorWs; 431 } 432 433 /** 434 * Update AP capability. Called when carrier config or device resouce config changed. 435 * 436 * @param capability new AP capability. 437 */ updateCapability(@onNull SoftApCapability capability)438 public void updateCapability(@NonNull SoftApCapability capability) { 439 mStateMachine.sendMessage(SoftApStateMachine.CMD_UPDATE_CAPABILITY, capability); 440 } 441 442 /** 443 * Update AP configuration. Called when setting update config via 444 * {@link WifiManager#setSoftApConfiguration(SoftApConfiguration)} 445 * 446 * @param config new AP config. 447 */ updateConfiguration(@onNull SoftApConfiguration config)448 public void updateConfiguration(@NonNull SoftApConfiguration config) { 449 mStateMachine.sendMessage(SoftApStateMachine.CMD_UPDATE_CONFIG, config); 450 } 451 452 /** 453 * Retrieve the {@link SoftApModeConfiguration} instance associated with this mode manager. 454 */ getSoftApModeConfiguration()455 public SoftApModeConfiguration getSoftApModeConfiguration() { 456 return new SoftApModeConfiguration(mOriginalModeConfiguration.getTargetMode(), 457 mCurrentSoftApConfiguration, mCurrentSoftApCapability); 458 } 459 460 /** 461 * Dump info about this softap manager. 462 */ 463 @Override dump(FileDescriptor fd, PrintWriter pw, String[] args)464 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 465 pw.println("Dump of SoftApManager id=" + mId); 466 467 pw.println("current StateMachine mode: " + getCurrentStateName()); 468 pw.println("mRole: " + mRole); 469 pw.println("mApInterfaceName: " + mApInterfaceName); 470 pw.println("mIfaceIsUp: " + mIfaceIsUp); 471 pw.println("mSoftApCountryCode: " + mCountryCode); 472 pw.println("mOriginalModeConfiguration.targetMode: " 473 + mOriginalModeConfiguration.getTargetMode()); 474 pw.println("mCurrentSoftApConfiguration.SSID: " + mCurrentSoftApConfiguration.getSsid()); 475 pw.println("mCurrentSoftApConfiguration.mBand: " + mCurrentSoftApConfiguration.getBand()); 476 pw.println("mCurrentSoftApConfiguration.hiddenSSID: " 477 + mCurrentSoftApConfiguration.isHiddenSsid()); 478 pw.println("getConnectedClientList().size(): " + getConnectedClientList().size()); 479 pw.println("mTimeoutEnabled: " + mTimeoutEnabled); 480 pw.println("mBridgedModeOpportunisticsShutdownTimeoutEnabled: " 481 + mBridgedModeOpportunisticsShutdownTimeoutEnabled); 482 pw.println("mCurrentSoftApInfoMap " + mCurrentSoftApInfoMap); 483 pw.println("mStartTimestamp: " + mStartTimestamp); 484 mStateMachine.dump(fd, pw, args); 485 } 486 487 @Override enableVerboseLogging(boolean verbose)488 public void enableVerboseLogging(boolean verbose) { 489 mVerboseLoggingEnabled = verbose; 490 } 491 492 @Override toString()493 public String toString() { 494 return "SoftApManager{id=" + getId() 495 + " iface=" + getInterfaceName() 496 + " role=" + getRole() 497 + "}"; 498 } 499 500 /** 501 * A ClientModeImpl instance has been L2 connected. 502 * 503 * @param newPrimary the corresponding ConcreteClientModeManager instance for the ClientModeImpl 504 * that has been L2 connected. 505 */ onL2Connected(@onNull ConcreteClientModeManager clientModeManager)506 private void onL2Connected(@NonNull ConcreteClientModeManager clientModeManager) { 507 Log.d(getTag(), "onL2Connected called"); 508 mStateMachine.sendMessage(SoftApStateMachine.CMD_HANDLE_WIFI_CONNECTED, 509 clientModeManager.syncRequestConnectionInfo()); 510 } 511 512 getCurrentStateName()513 private String getCurrentStateName() { 514 IState currentState = mStateMachine.getCurrentState(); 515 516 if (currentState != null) { 517 return currentState.getName(); 518 } 519 520 return "StateMachine not active"; 521 } 522 523 /** 524 * Update AP state. 525 * 526 * @param newState new AP state 527 * @param currentState current AP state 528 * @param reason Failure reason if the new AP state is in failure state 529 */ updateApState(int newState, int currentState, int reason)530 private void updateApState(int newState, int currentState, int reason) { 531 mSoftApCallback.onStateChanged(newState, reason); 532 533 //send the AP state change broadcast 534 final Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION); 535 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 536 intent.putExtra(WifiManager.EXTRA_WIFI_AP_STATE, newState); 537 intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_AP_STATE, currentState); 538 if (newState == WifiManager.WIFI_AP_STATE_FAILED) { 539 //only set reason number when softAP start failed 540 intent.putExtra(WifiManager.EXTRA_WIFI_AP_FAILURE_REASON, reason); 541 } 542 543 intent.putExtra(WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME, mApInterfaceName); 544 intent.putExtra(WifiManager.EXTRA_WIFI_AP_MODE, mOriginalModeConfiguration.getTargetMode()); 545 mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); 546 } 547 setMacAddress()548 private int setMacAddress() { 549 MacAddress mac = mCurrentSoftApConfiguration.getBssid(); 550 551 if (mac == null) { 552 // If no BSSID is explicitly requested, (re-)configure the factory MAC address. Some 553 // drivers may not support setting the MAC at all, so fail soft in this case. 554 if (!mWifiNative.resetApMacToFactoryMacAddress(mApInterfaceName)) { 555 Log.w(getTag(), "failed to reset to factory MAC address; " 556 + "continuing with current MAC"); 557 } 558 } else { 559 if (mWifiNative.isApSetMacAddressSupported(mApInterfaceName)) { 560 if (!mWifiNative.setApMacAddress(mApInterfaceName, mac)) { 561 Log.e(getTag(), "failed to set explicitly requested MAC address"); 562 return ERROR_GENERIC; 563 } 564 } else if (!mIsUnsetBssid) { 565 // If hardware does not support MAC address setter, 566 // only report the error for non randomization. 567 return ERROR_UNSUPPORTED_CONFIGURATION; 568 } 569 } 570 571 return SUCCESS; 572 } 573 574 /** 575 * Dynamic update the country code when Soft AP enabled. 576 * 577 * @param countryCode 2 byte ASCII string. For ex: US, CA. 578 * @return true if request is sent successfully, false otherwise. 579 */ updateCountryCode(@onNull String countryCode)580 public boolean updateCountryCode(@NonNull String countryCode) { 581 if (ApConfigUtil.isSoftApDynamicCountryCodeSupported(mContext) 582 && mCurrentSoftApCapability.areFeaturesSupported( 583 SoftApCapability.SOFTAP_FEATURE_ACS_OFFLOAD)) { 584 mStateMachine.sendMessage(SoftApStateMachine.CMD_UPDATE_COUNTRY_CODE, countryCode); 585 return true; 586 } 587 return false; 588 } 589 setCountryCode()590 private int setCountryCode() { 591 int band = mCurrentSoftApConfiguration.getBand(); 592 if (TextUtils.isEmpty(mCountryCode)) { 593 if (band == SoftApConfiguration.BAND_5GHZ) { 594 // Country code is mandatory for 5GHz band. 595 Log.e(getTag(), "Invalid country code, required for setting up soft ap in 5GHz"); 596 return ERROR_GENERIC; 597 } 598 // Absence of country code is not fatal for 2Ghz & Any band options. 599 return SUCCESS; 600 } 601 if (!mWifiNative.setApCountryCode( 602 mApInterfaceName, mCountryCode.toUpperCase(Locale.ROOT))) { 603 if (band == SoftApConfiguration.BAND_5GHZ) { 604 // Return an error if failed to set country code when AP is configured for 605 // 5GHz band. 606 Log.e(getTag(), "Failed to set country code, " 607 + "required for setting up soft ap in 5GHz"); 608 return ERROR_GENERIC; 609 } 610 // Failure to set country code is not fatal for other band options. 611 } 612 return SUCCESS; 613 } 614 615 /** 616 * Start a soft AP instance as configured. 617 * 618 * @return integer result code 619 */ startSoftAp()620 private int startSoftAp() { 621 Log.d(getTag(), "band " + mCurrentSoftApConfiguration.getBand() + " iface " 622 + mApInterfaceName + " country " + mCountryCode); 623 624 int result = setMacAddress(); 625 if (result != SUCCESS) { 626 return result; 627 } 628 629 result = setCountryCode(); 630 if (result != SUCCESS) { 631 return result; 632 } 633 634 // Make a copy of configuration for updating AP band and channel. 635 SoftApConfiguration.Builder localConfigBuilder = 636 new SoftApConfiguration.Builder(mCurrentSoftApConfiguration); 637 638 boolean acsEnabled = mCurrentSoftApCapability.areFeaturesSupported( 639 SoftApCapability.SOFTAP_FEATURE_ACS_OFFLOAD); 640 641 result = ApConfigUtil.updateApChannelConfig( 642 mWifiNative, mCoexManager, mContext.getResources(), mCountryCode, 643 localConfigBuilder, mCurrentSoftApConfiguration, acsEnabled); 644 if (result != SUCCESS) { 645 Log.e(getTag(), "Failed to update AP band and channel"); 646 return result; 647 } 648 649 if (mCurrentSoftApConfiguration.isHiddenSsid()) { 650 Log.d(getTag(), "SoftAP is a hidden network"); 651 } 652 653 if (!ApConfigUtil.checkSupportAllConfiguration( 654 mCurrentSoftApConfiguration, mCurrentSoftApCapability)) { 655 Log.d(getTag(), "Unsupported Configuration detect! config = " 656 + mCurrentSoftApConfiguration); 657 return ERROR_UNSUPPORTED_CONFIGURATION; 658 } 659 660 if (!mWifiNative.startSoftAp(mApInterfaceName, 661 localConfigBuilder.build(), 662 mOriginalModeConfiguration.getTargetMode() == WifiManager.IFACE_IP_MODE_TETHERED, 663 mSoftApListener)) { 664 Log.e(getTag(), "Soft AP start failed"); 665 return ERROR_GENERIC; 666 } 667 668 mWifiDiagnostics.startLogging(mApInterfaceName); 669 mStartTimestamp = FORMATTER.format(new Date(System.currentTimeMillis())); 670 Log.d(getTag(), "Soft AP is started "); 671 672 return SUCCESS; 673 } 674 675 /** 676 * Disconnect all connected clients on active softap interface(s). 677 * This is usually done just before stopSoftAp(). 678 */ disconnectAllClients()679 private void disconnectAllClients() { 680 for (WifiClient client : getConnectedClientList()) { 681 mWifiNative.forceClientDisconnect(mApInterfaceName, client.getMacAddress(), 682 SAP_CLIENT_DISCONNECT_REASON_CODE_UNSPECIFIED); 683 } 684 } 685 686 /** 687 * Teardown soft AP and teardown the interface. 688 */ stopSoftAp()689 private void stopSoftAp() { 690 disconnectAllClients(); 691 mWifiDiagnostics.stopLogging(mApInterfaceName); 692 mWifiNative.teardownInterface(mApInterfaceName); 693 Log.d(getTag(), "Soft AP is stopped"); 694 } 695 addClientToPendingDisconnectionList(WifiClient client, int reason)696 private void addClientToPendingDisconnectionList(WifiClient client, int reason) { 697 Log.d(getTag(), "Fail to disconnect client: " + client.getMacAddress() 698 + ", add it into pending list"); 699 mPendingDisconnectClients.put(client, reason); 700 mStateMachine.getHandler().removeMessages( 701 SoftApStateMachine.CMD_FORCE_DISCONNECT_PENDING_CLIENTS); 702 mStateMachine.sendMessageDelayed( 703 SoftApStateMachine.CMD_FORCE_DISCONNECT_PENDING_CLIENTS, 704 SOFT_AP_PENDING_DISCONNECTION_CHECK_DELAY_MS); 705 } 706 getConnectedClientList()707 private List<WifiClient> getConnectedClientList() { 708 List<WifiClient> connectedClientList = new ArrayList<>(); 709 for (List<WifiClient> it : mConnectedClientWithApInfoMap.values()) { 710 connectedClientList.addAll(it); 711 } 712 return connectedClientList; 713 } 714 checkSoftApClient(SoftApConfiguration config, WifiClient newClient)715 private boolean checkSoftApClient(SoftApConfiguration config, WifiClient newClient) { 716 if (!mCurrentSoftApCapability.areFeaturesSupported( 717 SoftApCapability.SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT)) { 718 return true; 719 } 720 721 if (mBlockedClientList.contains(newClient.getMacAddress())) { 722 Log.d(getTag(), "Force disconnect for client: " + newClient + "in blocked list"); 723 if (!mWifiNative.forceClientDisconnect( 724 mApInterfaceName, newClient.getMacAddress(), 725 WifiManager.SAP_CLIENT_BLOCK_REASON_CODE_BLOCKED_BY_USER)) { 726 addClientToPendingDisconnectionList(newClient, 727 WifiManager.SAP_CLIENT_BLOCK_REASON_CODE_BLOCKED_BY_USER); 728 } 729 return false; 730 } 731 if (config.isClientControlByUserEnabled() 732 && !mAllowedClientList.contains(newClient.getMacAddress())) { 733 mSoftApCallback.onBlockedClientConnecting(newClient, 734 WifiManager.SAP_CLIENT_BLOCK_REASON_CODE_BLOCKED_BY_USER); 735 Log.d(getTag(), "Force disconnect for unauthorized client: " + newClient); 736 if (!mWifiNative.forceClientDisconnect( 737 mApInterfaceName, newClient.getMacAddress(), 738 WifiManager.SAP_CLIENT_BLOCK_REASON_CODE_BLOCKED_BY_USER)) { 739 addClientToPendingDisconnectionList(newClient, 740 WifiManager.SAP_CLIENT_BLOCK_REASON_CODE_BLOCKED_BY_USER); 741 } 742 return false; 743 } 744 int maxConfig = mCurrentSoftApCapability.getMaxSupportedClients(); 745 if (config.getMaxNumberOfClients() > 0) { 746 maxConfig = Math.min(maxConfig, config.getMaxNumberOfClients()); 747 } 748 749 if (getConnectedClientList().size() >= maxConfig) { 750 Log.i(getTag(), "No more room for new client:" + newClient); 751 if (!mWifiNative.forceClientDisconnect( 752 mApInterfaceName, newClient.getMacAddress(), 753 WifiManager.SAP_CLIENT_BLOCK_REASON_CODE_NO_MORE_STAS)) { 754 addClientToPendingDisconnectionList(newClient, 755 WifiManager.SAP_CLIENT_BLOCK_REASON_CODE_NO_MORE_STAS); 756 } 757 mSoftApCallback.onBlockedClientConnecting(newClient, 758 WifiManager.SAP_CLIENT_BLOCK_REASON_CODE_NO_MORE_STAS); 759 // Avoid report the max client blocked in the same settings. 760 if (!mEverReportMetricsForMaxClient) { 761 mWifiMetrics.noteSoftApClientBlocked(maxConfig); 762 mEverReportMetricsForMaxClient = true; 763 } 764 return false; 765 } 766 return true; 767 } 768 769 private class SoftApStateMachine extends StateMachine { 770 // Commands for the state machine. 771 public static final int CMD_START = 0; 772 public static final int CMD_STOP = 1; 773 public static final int CMD_FAILURE = 2; 774 public static final int CMD_INTERFACE_STATUS_CHANGED = 3; 775 public static final int CMD_ASSOCIATED_STATIONS_CHANGED = 4; 776 public static final int CMD_NO_ASSOCIATED_STATIONS_TIMEOUT = 5; 777 public static final int CMD_INTERFACE_DESTROYED = 7; 778 public static final int CMD_INTERFACE_DOWN = 8; 779 public static final int CMD_AP_INFO_CHANGED = 9; 780 public static final int CMD_UPDATE_CAPABILITY = 10; 781 public static final int CMD_UPDATE_CONFIG = 11; 782 public static final int CMD_FORCE_DISCONNECT_PENDING_CLIENTS = 12; 783 public static final int CMD_NO_ASSOCIATED_STATIONS_TIMEOUT_ON_ONE_INSTANCE = 13; 784 public static final int CMD_SAFE_CHANNEL_FREQUENCY_CHANGED = 14; 785 public static final int CMD_HANDLE_WIFI_CONNECTED = 15; 786 public static final int CMD_UPDATE_COUNTRY_CODE = 16; 787 788 private final State mIdleState = new IdleState(); 789 private final State mStartedState = new StartedState(); 790 791 private final InterfaceCallback mWifiNativeInterfaceCallback = new InterfaceCallback() { 792 @Override 793 public void onDestroyed(String ifaceName) { 794 if (mApInterfaceName != null && mApInterfaceName.equals(ifaceName)) { 795 sendMessage(CMD_INTERFACE_DESTROYED); 796 } 797 } 798 799 @Override 800 public void onUp(String ifaceName) { 801 if (mApInterfaceName != null && mApInterfaceName.equals(ifaceName)) { 802 sendMessage(CMD_INTERFACE_STATUS_CHANGED, 1); 803 } 804 } 805 806 @Override 807 public void onDown(String ifaceName) { 808 if (mApInterfaceName != null && mApInterfaceName.equals(ifaceName)) { 809 sendMessage(CMD_INTERFACE_STATUS_CHANGED, 0); 810 } 811 } 812 }; 813 SoftApStateMachine(Looper looper)814 SoftApStateMachine(Looper looper) { 815 super(TAG, looper); 816 817 // CHECKSTYLE:OFF IndentationCheck 818 addState(mIdleState); 819 addState(mStartedState, mIdleState); 820 // CHECKSTYLE:ON IndentationCheck 821 822 setInitialState(mIdleState); 823 start(); 824 } 825 826 private class IdleState extends State { 827 @Override enter()828 public void enter() { 829 mApInterfaceName = null; 830 mIfaceIsUp = false; 831 mIfaceIsDestroyed = false; 832 } 833 834 @Override exit()835 public void exit() { 836 mModeListener.onStopped(SoftApManager.this); 837 } 838 839 @Override processMessage(Message message)840 public boolean processMessage(Message message) { 841 switch (message.what) { 842 case CMD_STOP: 843 mStateMachine.quitNow(); 844 break; 845 case CMD_START: 846 mRequestorWs = (WorkSource) message.obj; 847 if (mCurrentSoftApConfiguration == null 848 || mCurrentSoftApConfiguration.getSsid() == null) { 849 Log.e(getTag(), "Unable to start soft AP without valid configuration"); 850 updateApState(WifiManager.WIFI_AP_STATE_FAILED, 851 WifiManager.WIFI_AP_STATE_DISABLED, 852 WifiManager.SAP_START_FAILURE_GENERAL); 853 mWifiMetrics.incrementSoftApStartResult( 854 false, WifiManager.SAP_START_FAILURE_GENERAL); 855 mModeListener.onStartFailure(SoftApManager.this); 856 break; 857 } 858 if (isBridgedMode()) { 859 boolean isFallbackToSingleAp = false; 860 int newSingleApBand = 0; 861 for (ClientModeManager cmm 862 : mActiveModeWarden.getClientModeManagers()) { 863 WifiInfo wifiConnectedInfo = cmm.syncRequestConnectionInfo(); 864 int wifiFrequency = wifiConnectedInfo.getFrequency(); 865 if (wifiFrequency > 0 866 && !mSafeChannelFrequencyList.contains( 867 wifiFrequency)) { 868 Log.d(getTag(), "Wifi connected to unavailable freq: " 869 + wifiFrequency); 870 isFallbackToSingleAp = true; 871 break; 872 } 873 } 874 for (int configuredBand : mCurrentSoftApConfiguration.getBands()) { 875 int availableBand = ApConfigUtil.removeUnavailableBands( 876 mCurrentSoftApCapability, 877 configuredBand, mCoexManager); 878 if (configuredBand != availableBand) { 879 isFallbackToSingleAp = true; 880 } 881 newSingleApBand |= availableBand; 882 } 883 if (isFallbackToSingleAp) { 884 newSingleApBand = ApConfigUtil.append24GToBandIf24GSupported( 885 newSingleApBand, mContext); 886 Log.i(getTag(), "Fallback to single AP mode with band " 887 + newSingleApBand); 888 mCurrentSoftApConfiguration = 889 new SoftApConfiguration.Builder(mCurrentSoftApConfiguration) 890 .setBand(newSingleApBand) 891 .build(); 892 } 893 } 894 mApInterfaceName = mWifiNative.setupInterfaceForSoftApMode( 895 mWifiNativeInterfaceCallback, mRequestorWs, 896 mCurrentSoftApConfiguration.getBand(), isBridgedMode()); 897 if (TextUtils.isEmpty(mApInterfaceName)) { 898 Log.e(getTag(), "setup failure when creating ap interface."); 899 updateApState(WifiManager.WIFI_AP_STATE_FAILED, 900 WifiManager.WIFI_AP_STATE_DISABLED, 901 WifiManager.SAP_START_FAILURE_GENERAL); 902 mWifiMetrics.incrementSoftApStartResult( 903 false, WifiManager.SAP_START_FAILURE_GENERAL); 904 mModeListener.onStartFailure(SoftApManager.this); 905 break; 906 } 907 mSoftApNotifier.dismissSoftApShutdownTimeoutExpiredNotification(); 908 updateApState(WifiManager.WIFI_AP_STATE_ENABLING, 909 WifiManager.WIFI_AP_STATE_DISABLED, 0); 910 int result = startSoftAp(); 911 if (result != SUCCESS) { 912 int failureReason = WifiManager.SAP_START_FAILURE_GENERAL; 913 if (result == ERROR_NO_CHANNEL) { 914 failureReason = WifiManager.SAP_START_FAILURE_NO_CHANNEL; 915 } else if (result == ERROR_UNSUPPORTED_CONFIGURATION) { 916 failureReason = WifiManager 917 .SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION; 918 } 919 updateApState(WifiManager.WIFI_AP_STATE_FAILED, 920 WifiManager.WIFI_AP_STATE_ENABLING, 921 failureReason); 922 stopSoftAp(); 923 mWifiMetrics.incrementSoftApStartResult(false, failureReason); 924 mModeListener.onStartFailure(SoftApManager.this); 925 break; 926 } 927 transitionTo(mStartedState); 928 break; 929 case CMD_UPDATE_CAPABILITY: 930 // Capability should only changed by carrier requirement. Only apply to 931 // Tether Mode 932 if (mOriginalModeConfiguration.getTargetMode() 933 == WifiManager.IFACE_IP_MODE_TETHERED) { 934 SoftApCapability capability = (SoftApCapability) message.obj; 935 mCurrentSoftApCapability = new SoftApCapability(capability); 936 } 937 break; 938 case CMD_UPDATE_CONFIG: 939 SoftApConfiguration newConfig = (SoftApConfiguration) message.obj; 940 Log.d(getTag(), "Configuration changed to " + newConfig); 941 // Idle mode, update all configurations. 942 mCurrentSoftApConfiguration = newConfig; 943 configureInternalConfiguration(); 944 break; 945 case CMD_UPDATE_COUNTRY_CODE: 946 String countryCode = (String) message.obj; 947 if (!TextUtils.isEmpty(countryCode)) { 948 mCountryCode = countryCode; 949 } 950 break; 951 default: 952 // Ignore all other commands. 953 break; 954 } 955 956 return HANDLED; 957 } 958 } 959 960 private class StartedState extends State { scheduleTimeoutMessages()961 private void scheduleTimeoutMessages() { 962 // When SAP started, the mCurrentSoftApInfoMap is 0 because info does not update. 963 // Don't trigger bridged mode shutdown timeout when only one active instance 964 // In Dual AP, one instance may already be closed due to LTE coexistence or DFS 965 // restrictions or due to inactivity. i.e. mCurrentSoftApInfoMap.size() is 1) 966 final int connectedClients = getConnectedClientList().size(); 967 if (isBridgedMode() && mCurrentSoftApInfoMap.size() != 1) { 968 if (mBridgedModeOpportunisticsShutdownTimeoutEnabled 969 && (connectedClients == 0 || getIdleInstances().size() != 0)) { 970 if (!mIsBridgedModeIdleInstanceTimerActive) { 971 mSoftApBridgedModeIdleInstanceTimeoutMessage.schedule(SystemClock 972 .elapsedRealtime() 973 + mDefaultShutdownIdleInstanceInBridgedModeTimeoutMillis); 974 mIsBridgedModeIdleInstanceTimerActive = true; 975 Log.d(getTag(), "Bridged mode instance opportunistic timeout message" 976 + " scheduled, delay = " 977 + mDefaultShutdownIdleInstanceInBridgedModeTimeoutMillis); 978 } 979 } else { 980 cancelBridgedModeIdleInstanceTimeoutMessage(); 981 } 982 } 983 if (!mTimeoutEnabled || connectedClients != 0) { 984 cancelTimeoutMessage(); 985 return; 986 } 987 long timeout = getShutdownTimeoutMillis(); 988 mSoftApTimeoutMessage.schedule(SystemClock.elapsedRealtime() 989 + timeout); 990 Log.d(getTag(), "Timeout message scheduled, delay = " 991 + timeout); 992 } 993 getHighestFrequencyInstance(Set<String> candidateInstances)994 private String getHighestFrequencyInstance(Set<String> candidateInstances) { 995 int currentHighestFrequencyOnAP = 0; 996 String highestFrequencyInstance = null; 997 for (String instance : candidateInstances) { 998 SoftApInfo info = mCurrentSoftApInfoMap.get(instance); 999 if (info == null) { 1000 Log.wtf(getTag(), "Invalid instance name, no way to get the frequency"); 1001 return ""; 1002 } 1003 int frequencyOnInstance = info.getFrequency(); 1004 if (frequencyOnInstance > currentHighestFrequencyOnAP) { 1005 currentHighestFrequencyOnAP = frequencyOnInstance; 1006 highestFrequencyInstance = instance; 1007 } 1008 } 1009 return highestFrequencyInstance; 1010 } 1011 removeIfaceInstanceFromBridgedApIface(String instanceName)1012 private void removeIfaceInstanceFromBridgedApIface(String instanceName) { 1013 if (TextUtils.isEmpty(instanceName)) { 1014 return; 1015 } 1016 if (mCurrentSoftApInfoMap.containsKey(instanceName)) { 1017 Log.i(getTag(), "remove instance " + instanceName + "(" 1018 + mCurrentSoftApInfoMap.get(instanceName).getFrequency() 1019 + ") from bridged iface " + mApInterfaceName); 1020 mWifiNative.removeIfaceInstanceFromBridgedApIface(mApInterfaceName, 1021 instanceName); 1022 // Remove the info and update it. 1023 updateSoftApInfo(mCurrentSoftApInfoMap.get(instanceName), true); 1024 } 1025 } 1026 getIdleInstances()1027 private Set<String> getIdleInstances() { 1028 Set<String> idleInstances = new HashSet<String>(); 1029 for (String instance : mConnectedClientWithApInfoMap.keySet()) { 1030 if (mConnectedClientWithApInfoMap.getOrDefault( 1031 instance, Collections.emptyList()).size() == 0) { 1032 idleInstances.add(instance); 1033 } 1034 } 1035 return idleInstances; 1036 } 1037 cancelTimeoutMessage()1038 private void cancelTimeoutMessage() { 1039 mSoftApTimeoutMessage.cancel(); 1040 Log.d(getTag(), "Timeout message canceled"); 1041 } 1042 cancelBridgedModeIdleInstanceTimeoutMessage()1043 private void cancelBridgedModeIdleInstanceTimeoutMessage() { 1044 mSoftApBridgedModeIdleInstanceTimeoutMessage.cancel(); 1045 mIsBridgedModeIdleInstanceTimerActive = false; 1046 Log.d(getTag(), "Bridged mode idle instance timeout message canceled"); 1047 } 1048 1049 /** 1050 * When configuration changed, it need to force some clients disconnect to match the 1051 * configuration. 1052 */ updateClientConnection()1053 private void updateClientConnection() { 1054 if (!mCurrentSoftApCapability.areFeaturesSupported( 1055 SoftApCapability.SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT)) { 1056 return; 1057 } 1058 final int maxAllowedClientsByHardwareAndCarrier = 1059 mCurrentSoftApCapability.getMaxSupportedClients(); 1060 final int userApConfigMaxClientCount = 1061 mCurrentSoftApConfiguration.getMaxNumberOfClients(); 1062 int finalMaxClientCount = maxAllowedClientsByHardwareAndCarrier; 1063 if (userApConfigMaxClientCount > 0) { 1064 finalMaxClientCount = Math.min(userApConfigMaxClientCount, 1065 maxAllowedClientsByHardwareAndCarrier); 1066 } 1067 List<WifiClient> currentClients = getConnectedClientList(); 1068 int targetDisconnectClientNumber = currentClients.size() - finalMaxClientCount; 1069 List<WifiClient> allowedConnectedList = new ArrayList<>(); 1070 Iterator<WifiClient> iterator = currentClients.iterator(); 1071 while (iterator.hasNext()) { 1072 WifiClient client = iterator.next(); 1073 if (mBlockedClientList.contains(client.getMacAddress()) 1074 || (mCurrentSoftApConfiguration.isClientControlByUserEnabled() 1075 && !mAllowedClientList.contains(client.getMacAddress()))) { 1076 Log.d(getTag(), "Force disconnect for not allowed client: " + client); 1077 if (!mWifiNative.forceClientDisconnect( 1078 mApInterfaceName, client.getMacAddress(), 1079 WifiManager.SAP_CLIENT_BLOCK_REASON_CODE_BLOCKED_BY_USER)) { 1080 addClientToPendingDisconnectionList(client, 1081 WifiManager.SAP_CLIENT_BLOCK_REASON_CODE_BLOCKED_BY_USER); 1082 } 1083 targetDisconnectClientNumber--; 1084 } else { 1085 allowedConnectedList.add(client); 1086 } 1087 } 1088 1089 if (targetDisconnectClientNumber > 0) { 1090 Iterator<WifiClient> allowedClientIterator = allowedConnectedList.iterator(); 1091 while (allowedClientIterator.hasNext()) { 1092 if (targetDisconnectClientNumber == 0) break; 1093 WifiClient allowedClient = allowedClientIterator.next(); 1094 Log.d(getTag(), "Force disconnect for client due to no more room: " 1095 + allowedClient); 1096 if (!mWifiNative.forceClientDisconnect( 1097 mApInterfaceName, allowedClient.getMacAddress(), 1098 WifiManager.SAP_CLIENT_BLOCK_REASON_CODE_NO_MORE_STAS)) { 1099 addClientToPendingDisconnectionList(allowedClient, 1100 WifiManager.SAP_CLIENT_BLOCK_REASON_CODE_NO_MORE_STAS); 1101 } 1102 targetDisconnectClientNumber--; 1103 } 1104 } 1105 } 1106 1107 /** 1108 * Set stations associated with this soft AP 1109 * @param client The station for which connection state changed. 1110 * @param isConnected True for the connection changed to connect, otherwise false. 1111 */ updateConnectedClients(WifiClient client, boolean isConnected)1112 private void updateConnectedClients(WifiClient client, boolean isConnected) { 1113 if (client == null) { 1114 return; 1115 } 1116 1117 if (null != mPendingDisconnectClients.remove(client)) { 1118 Log.d(getTag(), "Remove client: " + client.getMacAddress() 1119 + "from pending disconnectionlist"); 1120 } 1121 1122 String apInstanceIdentifier = client.getApInstanceIdentifier(); 1123 List clientList = mConnectedClientWithApInfoMap.computeIfAbsent( 1124 apInstanceIdentifier, k -> new ArrayList<>()); 1125 int index = clientList.indexOf(client); 1126 1127 if ((index != -1) == isConnected) { 1128 Log.e(getTag(), "Drop client connection event, client " 1129 + client + "isConnected: " + isConnected 1130 + " , duplicate event or client is blocked"); 1131 return; 1132 } 1133 if (isConnected) { 1134 boolean isAllow = checkSoftApClient(mCurrentSoftApConfiguration, client); 1135 if (isAllow) { 1136 clientList.add(client); 1137 } else { 1138 return; 1139 } 1140 } else { 1141 if (null == clientList.remove(index)) { 1142 Log.e(getTag(), "client doesn't exist in list, it should NOT happen"); 1143 } 1144 } 1145 1146 // Update clients list. 1147 mConnectedClientWithApInfoMap.put(apInstanceIdentifier, clientList); 1148 SoftApInfo currentInfoWithClientsChanged = mCurrentSoftApInfoMap 1149 .get(apInstanceIdentifier); 1150 Log.d(getTag(), "The connected wifi stations have changed with count: " 1151 + clientList.size() + ": " + clientList + " on the AP which info is " 1152 + currentInfoWithClientsChanged); 1153 1154 if (mSoftApCallback != null) { 1155 mSoftApCallback.onConnectedClientsOrInfoChanged(mCurrentSoftApInfoMap, 1156 mConnectedClientWithApInfoMap, isBridgedMode()); 1157 } else { 1158 Log.e(getTag(), 1159 "SoftApCallback is null. Dropping ConnectedClientsChanged event."); 1160 } 1161 1162 mWifiMetrics.addSoftApNumAssociatedStationsChangedEvent( 1163 getConnectedClientList().size(), 1164 mConnectedClientWithApInfoMap.get(apInstanceIdentifier).size(), 1165 mOriginalModeConfiguration.getTargetMode(), 1166 mCurrentSoftApInfoMap.get(apInstanceIdentifier)); 1167 1168 scheduleTimeoutMessages(); 1169 } 1170 1171 /** 1172 * @param apInfo, the new SoftApInfo changed. Null used to clean up. 1173 */ updateSoftApInfo(@ullable SoftApInfo apInfo, boolean isRemoved)1174 private void updateSoftApInfo(@Nullable SoftApInfo apInfo, boolean isRemoved) { 1175 Log.d(getTag(), "SoftApInfo update " + apInfo + ", isRemoved: " + isRemoved); 1176 if (apInfo == null) { 1177 // Clean up 1178 mCurrentSoftApInfoMap.clear(); 1179 mConnectedClientWithApInfoMap.clear(); 1180 mSoftApCallback.onConnectedClientsOrInfoChanged(mCurrentSoftApInfoMap, 1181 mConnectedClientWithApInfoMap, isBridgedMode()); 1182 return; 1183 } 1184 String changedInstance = apInfo.getApInstanceIdentifier(); 1185 if (apInfo.equals(mCurrentSoftApInfoMap.get(changedInstance))) { 1186 if (isRemoved) { 1187 boolean isClientConnected = 1188 mConnectedClientWithApInfoMap.get(changedInstance).size() > 0; 1189 mCurrentSoftApInfoMap.remove(changedInstance); 1190 mConnectedClientWithApInfoMap.remove(changedInstance); 1191 mSoftApCallback.onConnectedClientsOrInfoChanged(mCurrentSoftApInfoMap, 1192 mConnectedClientWithApInfoMap, isBridgedMode()); 1193 if (isClientConnected) { 1194 mWifiMetrics.addSoftApNumAssociatedStationsChangedEvent( 1195 getConnectedClientList().size(), 0, 1196 mOriginalModeConfiguration.getTargetMode(), apInfo); 1197 } 1198 if (isBridgedMode()) { 1199 mWifiMetrics.addSoftApInstanceDownEventInDualMode( 1200 mOriginalModeConfiguration.getTargetMode(), apInfo); 1201 } 1202 } 1203 return; 1204 } 1205 1206 // Make sure an empty client list is created when info updated 1207 List clientList = mConnectedClientWithApInfoMap.computeIfAbsent( 1208 changedInstance, k -> new ArrayList<>()); 1209 1210 if (clientList.size() != 0) { 1211 Log.e(getTag(), "The info: " + apInfo 1212 + " changed when client connected, it should NOT happen!!"); 1213 } 1214 1215 // Update the info when getting two infos in bridged mode. 1216 // TODO: b/173999527. It may only one instance come up when starting bridged AP. 1217 // Consider the handling with co-ex mechanism in bridged mode. 1218 boolean waitForAnotherSoftApInfoInBridgedMode = 1219 isBridgedMode() && mCurrentSoftApInfoMap.size() == 0; 1220 1221 mCurrentSoftApInfoMap.put(changedInstance, new SoftApInfo(apInfo)); 1222 if (!waitForAnotherSoftApInfoInBridgedMode) { 1223 mSoftApCallback.onConnectedClientsOrInfoChanged(mCurrentSoftApInfoMap, 1224 mConnectedClientWithApInfoMap, isBridgedMode()); 1225 } 1226 1227 // ignore invalid freq and softap disable case for metrics 1228 if (apInfo.getFrequency() > 0 1229 && apInfo.getBandwidth() != SoftApInfo.CHANNEL_WIDTH_INVALID) { 1230 mWifiMetrics.addSoftApChannelSwitchedEvent( 1231 new ArrayList<>(mCurrentSoftApInfoMap.values()), 1232 mOriginalModeConfiguration.getTargetMode(), isBridgedMode()); 1233 updateUserBandPreferenceViolationMetricsIfNeeded(apInfo); 1234 } 1235 } 1236 onUpChanged(boolean isUp)1237 private void onUpChanged(boolean isUp) { 1238 if (isUp == mIfaceIsUp) { 1239 return; // no change 1240 } 1241 1242 mIfaceIsUp = isUp; 1243 if (isUp) { 1244 Log.d(getTag(), "SoftAp is ready for use"); 1245 updateApState(WifiManager.WIFI_AP_STATE_ENABLED, 1246 WifiManager.WIFI_AP_STATE_ENABLING, 0); 1247 mModeListener.onStarted(SoftApManager.this); 1248 mWifiMetrics.incrementSoftApStartResult(true, 0); 1249 mCurrentSoftApInfoMap.clear(); 1250 mConnectedClientWithApInfoMap.clear(); 1251 if (mSoftApCallback != null) { 1252 mSoftApCallback.onConnectedClientsOrInfoChanged(mCurrentSoftApInfoMap, 1253 mConnectedClientWithApInfoMap, isBridgedMode()); 1254 } 1255 } else { 1256 // the interface was up, but goes down 1257 sendMessage(CMD_INTERFACE_DOWN); 1258 } 1259 mWifiMetrics.addSoftApUpChangedEvent(isUp, 1260 mOriginalModeConfiguration.getTargetMode(), 1261 mDefaultShutdownTimeoutMillis, isBridgedMode()); 1262 if (isUp) { 1263 mWifiMetrics.updateSoftApConfiguration(mCurrentSoftApConfiguration, 1264 mOriginalModeConfiguration.getTargetMode(), isBridgedMode()); 1265 mWifiMetrics.updateSoftApCapability(mCurrentSoftApCapability, 1266 mOriginalModeConfiguration.getTargetMode(), isBridgedMode()); 1267 } 1268 } 1269 1270 @Override enter()1271 public void enter() { 1272 mIfaceIsUp = false; 1273 mIfaceIsDestroyed = false; 1274 onUpChanged(mWifiNative.isInterfaceUp(mApInterfaceName)); 1275 1276 Handler handler = mStateMachine.getHandler(); 1277 mSoftApTimeoutMessage = new WakeupMessage(mContext, handler, 1278 SOFT_AP_SEND_MESSAGE_TIMEOUT_TAG, 1279 SoftApStateMachine.CMD_NO_ASSOCIATED_STATIONS_TIMEOUT); 1280 1281 mSoftApBridgedModeIdleInstanceTimeoutMessage = new WakeupMessage(mContext, handler, 1282 SOFT_AP_SEND_MESSAGE_IDLE_IN_BRIDGED_MODE_TIMEOUT_TAG, 1283 SoftApStateMachine.CMD_NO_ASSOCIATED_STATIONS_TIMEOUT_ON_ONE_INSTANCE); 1284 if (SdkLevel.isAtLeastS()) { 1285 mCoexManager.registerCoexListener(mCoexListener); 1286 } 1287 mSarManager.setSapWifiState(WifiManager.WIFI_AP_STATE_ENABLED); 1288 Log.d(getTag(), "Resetting connected clients on start"); 1289 mConnectedClientWithApInfoMap.clear(); 1290 mPendingDisconnectClients.clear(); 1291 mEverReportMetricsForMaxClient = false; 1292 scheduleTimeoutMessages(); 1293 } 1294 1295 @Override exit()1296 public void exit() { 1297 if (!mIfaceIsDestroyed) { 1298 stopSoftAp(); 1299 } 1300 if (SdkLevel.isAtLeastS()) { 1301 mCoexManager.unregisterCoexListener(mCoexListener); 1302 } 1303 if (getConnectedClientList().size() != 0) { 1304 Log.d(getTag(), "Resetting num stations on stop"); 1305 for (List<WifiClient> it : mConnectedClientWithApInfoMap.values()) { 1306 if (it.size() != 0) { 1307 mWifiMetrics.addSoftApNumAssociatedStationsChangedEvent( 1308 0, 0, mOriginalModeConfiguration.getTargetMode(), 1309 mCurrentSoftApInfoMap 1310 .get(it.get(0).getApInstanceIdentifier())); 1311 } 1312 } 1313 mConnectedClientWithApInfoMap.clear(); 1314 if (mSoftApCallback != null) { 1315 mSoftApCallback.onConnectedClientsOrInfoChanged(mCurrentSoftApInfoMap, 1316 mConnectedClientWithApInfoMap, isBridgedMode()); 1317 } 1318 } 1319 mPendingDisconnectClients.clear(); 1320 cancelTimeoutMessage(); 1321 cancelBridgedModeIdleInstanceTimeoutMessage(); 1322 1323 // Need this here since we are exiting |Started| state and won't handle any 1324 // future CMD_INTERFACE_STATUS_CHANGED events after this point 1325 mWifiMetrics.addSoftApUpChangedEvent(false, 1326 mOriginalModeConfiguration.getTargetMode(), 1327 mDefaultShutdownTimeoutMillis, isBridgedMode()); 1328 updateApState(WifiManager.WIFI_AP_STATE_DISABLED, 1329 WifiManager.WIFI_AP_STATE_DISABLING, 0); 1330 1331 mSarManager.setSapWifiState(WifiManager.WIFI_AP_STATE_DISABLED); 1332 1333 mApInterfaceName = null; 1334 mIfaceIsUp = false; 1335 mIfaceIsDestroyed = false; 1336 mRole = null; 1337 updateSoftApInfo(null, false); 1338 } 1339 updateUserBandPreferenceViolationMetricsIfNeeded(SoftApInfo apInfo)1340 private void updateUserBandPreferenceViolationMetricsIfNeeded(SoftApInfo apInfo) { 1341 // The band preference violation only need to detect in single AP mode. 1342 if (isBridgedMode()) return; 1343 int band = mCurrentSoftApConfiguration.getBand(); 1344 boolean bandPreferenceViolated = 1345 (ScanResult.is24GHz(apInfo.getFrequency()) 1346 && !ApConfigUtil.containsBand(band, 1347 SoftApConfiguration.BAND_2GHZ)) 1348 || (ScanResult.is5GHz(apInfo.getFrequency()) 1349 && !ApConfigUtil.containsBand(band, 1350 SoftApConfiguration.BAND_5GHZ)) 1351 || (ScanResult.is6GHz(apInfo.getFrequency()) 1352 && !ApConfigUtil.containsBand(band, 1353 SoftApConfiguration.BAND_6GHZ)); 1354 1355 if (bandPreferenceViolated) { 1356 Log.e(getTag(), "Channel does not satisfy user band preference: " 1357 + apInfo.getFrequency()); 1358 mWifiMetrics.incrementNumSoftApUserBandPreferenceUnsatisfied(); 1359 } 1360 } 1361 1362 @Override processMessage(Message message)1363 public boolean processMessage(Message message) { 1364 switch (message.what) { 1365 case CMD_ASSOCIATED_STATIONS_CHANGED: 1366 if (!(message.obj instanceof WifiClient)) { 1367 Log.e(getTag(), "Invalid type returned for" 1368 + " CMD_ASSOCIATED_STATIONS_CHANGED"); 1369 break; 1370 } 1371 boolean isConnected = (message.arg1 == 1); 1372 WifiClient client = (WifiClient) message.obj; 1373 Log.d(getTag(), "CMD_ASSOCIATED_STATIONS_CHANGED, Client: " 1374 + client.getMacAddress().toString() + " isConnected: " 1375 + isConnected); 1376 updateConnectedClients(client, isConnected); 1377 break; 1378 case CMD_AP_INFO_CHANGED: 1379 if (!(message.obj instanceof SoftApInfo)) { 1380 Log.e(getTag(), "Invalid type returned for" 1381 + " CMD_AP_INFO_CHANGED"); 1382 break; 1383 } 1384 SoftApInfo apInfo = (SoftApInfo) message.obj; 1385 if (apInfo.getFrequency() < 0) { 1386 Log.e(getTag(), "Invalid ap channel frequency: " 1387 + apInfo.getFrequency()); 1388 break; 1389 } 1390 // Update shutdown timeout 1391 apInfo.setAutoShutdownTimeoutMillis(mTimeoutEnabled 1392 ? getShutdownTimeoutMillis() : 0); 1393 updateSoftApInfo(apInfo, false); 1394 break; 1395 case CMD_INTERFACE_STATUS_CHANGED: 1396 boolean isUp = message.arg1 == 1; 1397 onUpChanged(isUp); 1398 break; 1399 case CMD_STOP: 1400 if (mIfaceIsUp) { 1401 updateApState(WifiManager.WIFI_AP_STATE_DISABLING, 1402 WifiManager.WIFI_AP_STATE_ENABLED, 0); 1403 } else { 1404 updateApState(WifiManager.WIFI_AP_STATE_DISABLING, 1405 WifiManager.WIFI_AP_STATE_ENABLING, 0); 1406 } 1407 quitNow(); 1408 break; 1409 case CMD_START: 1410 // Already started, ignore this command. 1411 break; 1412 case CMD_NO_ASSOCIATED_STATIONS_TIMEOUT: 1413 if (!mTimeoutEnabled) { 1414 Log.wtf(getTag(), "Timeout message received while timeout is disabled." 1415 + " Dropping."); 1416 break; 1417 } 1418 if (getConnectedClientList().size() != 0) { 1419 Log.wtf(getTag(), "Timeout message received but has clients. " 1420 + "Dropping."); 1421 break; 1422 } 1423 mSoftApNotifier.showSoftApShutdownTimeoutExpiredNotification(); 1424 Log.i(getTag(), "Timeout message received. Stopping soft AP."); 1425 updateApState(WifiManager.WIFI_AP_STATE_DISABLING, 1426 WifiManager.WIFI_AP_STATE_ENABLED, 0); 1427 quitNow(); 1428 break; 1429 case CMD_NO_ASSOCIATED_STATIONS_TIMEOUT_ON_ONE_INSTANCE: 1430 if (!isBridgedMode() || mCurrentSoftApInfoMap.size() != 2) { 1431 Log.wtf(getTag(), "Ignore Bridged Mode Timeout message received" 1432 + " in single AP state. Dropping"); 1433 break; 1434 } 1435 if (!mBridgedModeOpportunisticsShutdownTimeoutEnabled) { 1436 Log.wtf(getTag(), "Bridged Mode Timeout message received" 1437 + " while timeout is disabled. Dropping."); 1438 break; 1439 } 1440 Set<String> idleInstances = getIdleInstances(); 1441 if (idleInstances.size() == 0) { 1442 break; 1443 } 1444 Log.d(getTag(), "Instance idle timout, the number of the idle instances is " 1445 + idleInstances.size()); 1446 removeIfaceInstanceFromBridgedApIface( 1447 getHighestFrequencyInstance(idleInstances)); 1448 break; 1449 case CMD_INTERFACE_DESTROYED: 1450 Log.d(getTag(), "Interface was cleanly destroyed."); 1451 updateApState(WifiManager.WIFI_AP_STATE_DISABLING, 1452 WifiManager.WIFI_AP_STATE_ENABLED, 0); 1453 mIfaceIsDestroyed = true; 1454 quitNow(); 1455 break; 1456 case CMD_FAILURE: 1457 Log.w(getTag(), "hostapd failure, stop and report failure"); 1458 /* fall through */ 1459 case CMD_INTERFACE_DOWN: 1460 Log.w(getTag(), "interface error, stop and report failure"); 1461 updateApState(WifiManager.WIFI_AP_STATE_FAILED, 1462 WifiManager.WIFI_AP_STATE_ENABLED, 1463 WifiManager.SAP_START_FAILURE_GENERAL); 1464 updateApState(WifiManager.WIFI_AP_STATE_DISABLING, 1465 WifiManager.WIFI_AP_STATE_FAILED, 0); 1466 quitNow(); 1467 break; 1468 case CMD_UPDATE_CAPABILITY: 1469 // Capability should only changed by carrier requirement. Only apply to 1470 // Tether Mode 1471 if (mOriginalModeConfiguration.getTargetMode() 1472 == WifiManager.IFACE_IP_MODE_TETHERED) { 1473 SoftApCapability capability = (SoftApCapability) message.obj; 1474 mCurrentSoftApCapability = new SoftApCapability(capability); 1475 mWifiMetrics.updateSoftApCapability(mCurrentSoftApCapability, 1476 mOriginalModeConfiguration.getTargetMode(), isBridgedMode()); 1477 updateClientConnection(); 1478 } 1479 break; 1480 case CMD_UPDATE_CONFIG: 1481 SoftApConfiguration newConfig = (SoftApConfiguration) message.obj; 1482 SoftApConfiguration originalConfig = 1483 mOriginalModeConfiguration.getSoftApConfiguration(); 1484 if (!ApConfigUtil.checkConfigurationChangeNeedToRestart( 1485 originalConfig, newConfig)) { 1486 Log.d(getTag(), "Configuration changed to " + newConfig); 1487 if (mCurrentSoftApConfiguration.getMaxNumberOfClients() 1488 != newConfig.getMaxNumberOfClients()) { 1489 Log.d(getTag(), "Max Client changed, reset to record the metrics"); 1490 mEverReportMetricsForMaxClient = false; 1491 } 1492 boolean needRescheduleTimer = 1493 mCurrentSoftApConfiguration.getShutdownTimeoutMillis() 1494 != newConfig.getShutdownTimeoutMillis() 1495 || mTimeoutEnabled != newConfig.isAutoShutdownEnabled() 1496 || mBridgedModeOpportunisticsShutdownTimeoutEnabled 1497 != newConfig 1498 .isBridgedModeOpportunisticShutdownEnabledInternal(); 1499 updateChangeableConfiguration(newConfig); 1500 updateClientConnection(); 1501 if (needRescheduleTimer) { 1502 cancelTimeoutMessage(); 1503 cancelBridgedModeIdleInstanceTimeoutMessage(); 1504 scheduleTimeoutMessages(); 1505 // Update SoftApInfo 1506 for (SoftApInfo info : mCurrentSoftApInfoMap.values()) { 1507 SoftApInfo newInfo = new SoftApInfo(info); 1508 newInfo.setAutoShutdownTimeoutMillis(mTimeoutEnabled 1509 ? getShutdownTimeoutMillis() : 0); 1510 updateSoftApInfo(newInfo, false); 1511 } 1512 } 1513 mWifiMetrics.updateSoftApConfiguration( 1514 mCurrentSoftApConfiguration, 1515 mOriginalModeConfiguration.getTargetMode(), isBridgedMode()); 1516 } else { 1517 Log.d(getTag(), "Ignore the config: " + newConfig 1518 + " update since it requires restart"); 1519 } 1520 break; 1521 case CMD_UPDATE_COUNTRY_CODE: 1522 String countryCode = (String) message.obj; 1523 if (!TextUtils.isEmpty(countryCode) 1524 && !TextUtils.equals(mCountryCode, countryCode) 1525 && mWifiNative.setApCountryCode( 1526 mApInterfaceName, countryCode.toUpperCase(Locale.ROOT))) { 1527 Log.i(getTag(), "Update country code when Soft AP enabled from " 1528 + mCountryCode + " to " + countryCode); 1529 mCountryCode = countryCode; 1530 } 1531 break; 1532 case CMD_FORCE_DISCONNECT_PENDING_CLIENTS: 1533 if (mPendingDisconnectClients.size() != 0) { 1534 Log.d(getTag(), "Disconnect pending list is NOT empty"); 1535 mPendingDisconnectClients.forEach((pendingClient, reason)-> 1536 mWifiNative.forceClientDisconnect(mApInterfaceName, 1537 pendingClient.getMacAddress(), reason)); 1538 sendMessageDelayed( 1539 SoftApStateMachine.CMD_FORCE_DISCONNECT_PENDING_CLIENTS, 1540 SOFT_AP_PENDING_DISCONNECTION_CHECK_DELAY_MS); 1541 } 1542 break; 1543 case CMD_SAFE_CHANNEL_FREQUENCY_CHANGED: 1544 updateSafeChannelFrequencyList(); 1545 if (!isBridgedMode() || mCurrentSoftApInfoMap.size() != 2) { 1546 Log.d(getTag(), "Ignore safe channel changed in single AP state"); 1547 break; 1548 } 1549 Set<String> unavailableInstances = new HashSet<>(); 1550 for (SoftApInfo currentInfo : mCurrentSoftApInfoMap.values()) { 1551 int sapFreq = currentInfo.getFrequency(); 1552 if (!mSafeChannelFrequencyList.contains(sapFreq)) { 1553 int sapBand = ApConfigUtil.convertFrequencyToBand(sapFreq); 1554 if (sapBand != ApConfigUtil.removeUnavailableBands( 1555 mCurrentSoftApCapability, 1556 sapBand, mCoexManager)) { 1557 unavailableInstances.add(currentInfo.getApInstanceIdentifier()); 1558 } 1559 } 1560 } 1561 removeIfaceInstanceFromBridgedApIface( 1562 getHighestFrequencyInstance(unavailableInstances)); 1563 break; 1564 case CMD_HANDLE_WIFI_CONNECTED: 1565 if (!isBridgedMode() || mCurrentSoftApInfoMap.size() != 2) { 1566 Log.d(getTag(), "Ignore wifi connected in single AP state"); 1567 break; 1568 } 1569 WifiInfo wifiInfo = (WifiInfo) message.obj; 1570 int wifiFreq = wifiInfo.getFrequency(); 1571 String targetShutDownInstance = ""; 1572 if (!mSafeChannelFrequencyList.contains(wifiFreq)) { 1573 Log.i(getTag(), "Wifi connected to freq:" + wifiFreq 1574 + " which is unavailable for SAP"); 1575 for (SoftApInfo sapInfo : mCurrentSoftApInfoMap.values()) { 1576 if (ApConfigUtil.convertFrequencyToBand(sapInfo.getFrequency()) 1577 == ApConfigUtil.convertFrequencyToBand(wifiFreq)) { 1578 targetShutDownInstance = sapInfo.getApInstanceIdentifier(); 1579 Log.d(getTag(), "Remove the " + targetShutDownInstance 1580 + " instance which is running on the same band as " 1581 + "the wifi connection on an unsafe channel"); 1582 break; 1583 } 1584 } 1585 // Wifi may connect to different band as the SAP. For instances: 1586 // Wifi connect to 6Ghz but bridged AP is running on 2.4Ghz + 5Ghz. 1587 // In this case, targetShutDownInstance will be empty, shutdown the 1588 // highest frequency instance. 1589 removeIfaceInstanceFromBridgedApIface( 1590 TextUtils.isEmpty(targetShutDownInstance) 1591 ? getHighestFrequencyInstance(mCurrentSoftApInfoMap.keySet()) 1592 : targetShutDownInstance); 1593 } 1594 break; 1595 default: 1596 return NOT_HANDLED; 1597 } 1598 return HANDLED; 1599 } 1600 } 1601 } 1602 } 1603