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