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