1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.wifi; 18 19 import static android.net.wifi.WifiManager.SAP_CLIENT_DISCONNECT_REASON_CODE_UNSPECIFIED; 20 21 import static com.android.server.wifi.util.ApConfigUtil.ERROR_GENERIC; 22 import static com.android.server.wifi.util.ApConfigUtil.ERROR_NO_CHANNEL; 23 import static com.android.server.wifi.util.ApConfigUtil.ERROR_UNSUPPORTED_CONFIGURATION; 24 import static com.android.server.wifi.util.ApConfigUtil.SUCCESS; 25 26 import android.annotation.NonNull; 27 import android.content.Intent; 28 import android.net.MacAddress; 29 import android.net.wifi.ScanResult; 30 import android.net.wifi.SoftApCapability; 31 import android.net.wifi.SoftApConfiguration; 32 import android.net.wifi.SoftApInfo; 33 import android.net.wifi.WifiAnnotations; 34 import android.net.wifi.WifiClient; 35 import android.net.wifi.WifiManager; 36 import android.net.wifi.nl80211.NativeWifiClient; 37 import android.os.Handler; 38 import android.os.Looper; 39 import android.os.Message; 40 import android.os.SystemClock; 41 import android.os.UserHandle; 42 import android.text.TextUtils; 43 import android.util.Log; 44 45 import com.android.internal.annotations.VisibleForTesting; 46 import com.android.internal.util.IState; 47 import com.android.internal.util.Preconditions; 48 import com.android.internal.util.State; 49 import com.android.internal.util.StateMachine; 50 import com.android.internal.util.WakeupMessage; 51 import com.android.server.wifi.WifiNative.InterfaceCallback; 52 import com.android.server.wifi.WifiNative.SoftApListener; 53 import com.android.server.wifi.util.ApConfigUtil; 54 import com.android.wifi.resources.R; 55 56 import java.io.FileDescriptor; 57 import java.io.PrintWriter; 58 import java.text.SimpleDateFormat; 59 import java.util.ArrayList; 60 import java.util.Date; 61 import java.util.HashSet; 62 import java.util.Iterator; 63 import java.util.List; 64 import java.util.Locale; 65 import java.util.Set; 66 67 /** 68 * Manage WiFi in AP mode. 69 * The internal state machine runs under the ClientModeImpl handler thread context. 70 */ 71 public class SoftApManager implements ActiveModeManager { 72 private static final String TAG = "SoftApManager"; 73 74 @VisibleForTesting 75 public static final String SOFT_AP_SEND_MESSAGE_TIMEOUT_TAG = TAG 76 + " Soft AP Send Message Timeout"; 77 78 private final WifiContext mContext; 79 private final FrameworkFacade mFrameworkFacade; 80 private final WifiNative mWifiNative; 81 82 @VisibleForTesting 83 SoftApNotifier mSoftApNotifier; 84 85 private final String mCountryCode; 86 87 private final SoftApStateMachine mStateMachine; 88 89 private final Listener mModeListener; 90 private final WifiManager.SoftApCallback mSoftApCallback; 91 92 private String mApInterfaceName; 93 private boolean mIfaceIsUp; 94 private boolean mIfaceIsDestroyed; 95 96 private final WifiApConfigStore mWifiApConfigStore; 97 98 private final WifiMetrics mWifiMetrics; 99 100 private boolean mIsRandomizeBssid; 101 102 @NonNull 103 private SoftApModeConfiguration mApConfig; 104 105 @NonNull 106 private SoftApInfo mCurrentSoftApInfo = new SoftApInfo(); 107 108 @NonNull 109 private SoftApCapability mCurrentSoftApCapability; 110 111 private List<WifiClient> mConnectedClients = new ArrayList<>(); 112 private boolean mTimeoutEnabled = false; 113 114 private final SarManager mSarManager; 115 116 private String mStartTimestamp; 117 118 private long mDefaultShutDownTimeoutMills; 119 120 private static final SimpleDateFormat FORMATTER = new SimpleDateFormat("MM-dd HH:mm:ss.SSS"); 121 122 private BaseWifiDiagnostics mWifiDiagnostics; 123 124 private @Role int mRole = ROLE_UNSPECIFIED; 125 private @Role int mTargetRole = ROLE_UNSPECIFIED; 126 127 private boolean mEverReportMetricsForMaxClient = false; 128 129 @NonNull 130 private Set<MacAddress> mBlockedClientList = new HashSet<>(); 131 132 @NonNull 133 private Set<MacAddress> mAllowedClientList = new HashSet<>(); 134 135 /** 136 * Listener for soft AP events. 137 */ 138 private final SoftApListener mSoftApListener = new SoftApListener() { 139 140 @Override 141 public void onFailure() { 142 mStateMachine.sendMessage(SoftApStateMachine.CMD_FAILURE); 143 } 144 145 @Override 146 public void onConnectedClientsChanged(NativeWifiClient client, boolean isConnected) { 147 if (client != null) { 148 mStateMachine.sendMessage(SoftApStateMachine.CMD_ASSOCIATED_STATIONS_CHANGED, 149 isConnected ? 1 : 0, 0, client); 150 } else { 151 Log.e(TAG, "onConnectedClientsChanged: Invalid type returned"); 152 } 153 } 154 155 @Override 156 public void onSoftApChannelSwitched(int frequency, 157 @WifiAnnotations.Bandwidth int bandwidth) { 158 mStateMachine.sendMessage( 159 SoftApStateMachine.CMD_SOFT_AP_CHANNEL_SWITCHED, frequency, bandwidth); 160 } 161 }; 162 SoftApManager(@onNull WifiContext context, @NonNull Looper looper, @NonNull FrameworkFacade framework, @NonNull WifiNative wifiNative, String countryCode, @NonNull Listener listener, @NonNull WifiManager.SoftApCallback callback, @NonNull WifiApConfigStore wifiApConfigStore, @NonNull SoftApModeConfiguration apConfig, @NonNull WifiMetrics wifiMetrics, @NonNull SarManager sarManager, @NonNull BaseWifiDiagnostics wifiDiagnostics)163 public SoftApManager(@NonNull WifiContext context, 164 @NonNull Looper looper, 165 @NonNull FrameworkFacade framework, 166 @NonNull WifiNative wifiNative, 167 String countryCode, 168 @NonNull Listener listener, 169 @NonNull WifiManager.SoftApCallback callback, 170 @NonNull WifiApConfigStore wifiApConfigStore, 171 @NonNull SoftApModeConfiguration apConfig, 172 @NonNull WifiMetrics wifiMetrics, 173 @NonNull SarManager sarManager, 174 @NonNull BaseWifiDiagnostics wifiDiagnostics) { 175 mContext = context; 176 mFrameworkFacade = framework; 177 mSoftApNotifier = new SoftApNotifier(mContext, mFrameworkFacade); 178 mWifiNative = wifiNative; 179 mCountryCode = countryCode; 180 mModeListener = listener; 181 mSoftApCallback = callback; 182 mWifiApConfigStore = wifiApConfigStore; 183 SoftApConfiguration softApConfig = apConfig.getSoftApConfiguration(); 184 mCurrentSoftApCapability = apConfig.getCapability(); 185 // null is a valid input and means we use the user-configured tethering settings. 186 if (softApConfig == null) { 187 softApConfig = mWifiApConfigStore.getApConfiguration(); 188 // may still be null if we fail to load the default config 189 } 190 if (softApConfig != null) { 191 mIsRandomizeBssid = softApConfig.getBssid() == null; 192 softApConfig = mWifiApConfigStore.randomizeBssidIfUnset(mContext, softApConfig); 193 } 194 mApConfig = new SoftApModeConfiguration(apConfig.getTargetMode(), 195 softApConfig, mCurrentSoftApCapability); 196 mWifiMetrics = wifiMetrics; 197 mSarManager = sarManager; 198 mWifiDiagnostics = wifiDiagnostics; 199 mStateMachine = new SoftApStateMachine(looper); 200 if (softApConfig != null) { 201 mBlockedClientList = new HashSet<>(softApConfig.getBlockedClientList()); 202 mAllowedClientList = new HashSet<>(softApConfig.getAllowedClientList()); 203 mTimeoutEnabled = softApConfig.isAutoShutdownEnabled(); 204 } 205 mDefaultShutDownTimeoutMills = mContext.getResources().getInteger( 206 R.integer.config_wifiFrameworkSoftApShutDownTimeoutMilliseconds); 207 } 208 209 /** 210 * Start soft AP, as configured in the constructor. 211 */ 212 @Override start()213 public void start() { 214 mStateMachine.sendMessage(SoftApStateMachine.CMD_START); 215 } 216 217 /** 218 * Stop soft AP. 219 */ 220 @Override stop()221 public void stop() { 222 Log.d(TAG, " currentstate: " + getCurrentStateName()); 223 mTargetRole = ROLE_UNSPECIFIED; 224 if (mApInterfaceName != null) { 225 if (mIfaceIsUp) { 226 updateApState(WifiManager.WIFI_AP_STATE_DISABLING, 227 WifiManager.WIFI_AP_STATE_ENABLED, 0); 228 } else { 229 updateApState(WifiManager.WIFI_AP_STATE_DISABLING, 230 WifiManager.WIFI_AP_STATE_ENABLING, 0); 231 } 232 } 233 mStateMachine.quitNow(); 234 } 235 236 @Override isStopping()237 public boolean isStopping() { 238 return mTargetRole == ROLE_UNSPECIFIED && mRole != ROLE_UNSPECIFIED; 239 } 240 241 @Override getRole()242 public @Role int getRole() { 243 return mRole; 244 } 245 246 @Override setRole(@ole int role)247 public void setRole(@Role int role) { 248 // softap does not allow in-place switching of roles. 249 Preconditions.checkState(mRole == ROLE_UNSPECIFIED); 250 Preconditions.checkState(SOFTAP_ROLES.contains(role)); 251 mTargetRole = role; 252 mRole = role; 253 } 254 255 /** 256 * Update AP capability. Called when carrier config or device resouce config changed. 257 * 258 * @param capability new AP capability. 259 */ updateCapability(@onNull SoftApCapability capability)260 public void updateCapability(@NonNull SoftApCapability capability) { 261 mStateMachine.sendMessage(SoftApStateMachine.CMD_UPDATE_CAPABILITY, capability); 262 } 263 264 /** 265 * Update AP configuration. Called when setting update config via 266 * {@link WifiManager#setSoftApConfiguration(SoftApConfiguration)} 267 * 268 * @param config new AP config. 269 */ updateConfiguration(@onNull SoftApConfiguration config)270 public void updateConfiguration(@NonNull SoftApConfiguration config) { 271 mStateMachine.sendMessage(SoftApStateMachine.CMD_UPDATE_CONFIG, config); 272 } 273 274 /** 275 * Dump info about this softap manager. 276 */ 277 @Override dump(FileDescriptor fd, PrintWriter pw, String[] args)278 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 279 pw.println("--Dump of SoftApManager--"); 280 281 pw.println("current StateMachine mode: " + getCurrentStateName()); 282 pw.println("mRole: " + mRole); 283 pw.println("mApInterfaceName: " + mApInterfaceName); 284 pw.println("mIfaceIsUp: " + mIfaceIsUp); 285 pw.println("mSoftApCountryCode: " + mCountryCode); 286 pw.println("mApConfig.targetMode: " + mApConfig.getTargetMode()); 287 SoftApConfiguration softApConfig = mApConfig.getSoftApConfiguration(); 288 pw.println("mApConfig.SoftApConfiguration.SSID: " + softApConfig.getSsid()); 289 pw.println("mApConfig.SoftApConfiguration.mBand: " + softApConfig.getBand()); 290 pw.println("mApConfig.SoftApConfiguration.hiddenSSID: " + softApConfig.isHiddenSsid()); 291 pw.println("mConnectedClients.size(): " + mConnectedClients.size()); 292 pw.println("mTimeoutEnabled: " + mTimeoutEnabled); 293 pw.println("mCurrentSoftApInfo " + mCurrentSoftApInfo); 294 pw.println("mStartTimestamp: " + mStartTimestamp); 295 mStateMachine.dump(fd, pw, args); 296 } 297 getCurrentStateName()298 private String getCurrentStateName() { 299 IState currentState = mStateMachine.getCurrentState(); 300 301 if (currentState != null) { 302 return currentState.getName(); 303 } 304 305 return "StateMachine not active"; 306 } 307 308 /** 309 * Update AP state. 310 * 311 * @param newState new AP state 312 * @param currentState current AP state 313 * @param reason Failure reason if the new AP state is in failure state 314 */ updateApState(int newState, int currentState, int reason)315 private void updateApState(int newState, int currentState, int reason) { 316 mSoftApCallback.onStateChanged(newState, reason); 317 318 //send the AP state change broadcast 319 final Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION); 320 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 321 intent.putExtra(WifiManager.EXTRA_WIFI_AP_STATE, newState); 322 intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_AP_STATE, currentState); 323 if (newState == WifiManager.WIFI_AP_STATE_FAILED) { 324 //only set reason number when softAP start failed 325 intent.putExtra(WifiManager.EXTRA_WIFI_AP_FAILURE_REASON, reason); 326 } 327 328 intent.putExtra(WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME, mApInterfaceName); 329 intent.putExtra(WifiManager.EXTRA_WIFI_AP_MODE, mApConfig.getTargetMode()); 330 mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); 331 } 332 setMacAddress()333 private int setMacAddress() { 334 MacAddress mac = mApConfig.getSoftApConfiguration().getBssid(); 335 336 if (mac == null) { 337 // If no BSSID is explicitly requested, (re-)configure the factory MAC address. Some 338 // drivers may not support setting the MAC at all, so fail soft in this case. 339 mac = mWifiNative.getFactoryMacAddress(mApInterfaceName); 340 if (mac == null) { 341 Log.e(TAG, "failed to get factory MAC address"); 342 return ERROR_GENERIC; 343 } 344 345 if (!mWifiNative.setMacAddress(mApInterfaceName, mac)) { 346 Log.w(TAG, "failed to reset to factory MAC address; continuing with current MAC"); 347 } 348 return SUCCESS; 349 } 350 351 352 if (mWifiNative.isSetMacAddressSupported(mApInterfaceName)) { 353 if (!mWifiNative.setMacAddress(mApInterfaceName, mac)) { 354 Log.e(TAG, "failed to set explicitly requested MAC address"); 355 return ERROR_GENERIC; 356 } 357 } else if (!mIsRandomizeBssid) { 358 // If hardware does not support MAC address setter, 359 // only report the error for non randomization. 360 return ERROR_UNSUPPORTED_CONFIGURATION; 361 } 362 return SUCCESS; 363 } 364 setCountryCode()365 private int setCountryCode() { 366 int band = mApConfig.getSoftApConfiguration().getBand(); 367 if (TextUtils.isEmpty(mCountryCode)) { 368 if (band == SoftApConfiguration.BAND_5GHZ) { 369 // Country code is mandatory for 5GHz band. 370 Log.e(TAG, "Invalid country code, required for setting up soft ap in 5GHz"); 371 return ERROR_GENERIC; 372 } 373 // Absence of country code is not fatal for 2Ghz & Any band options. 374 return SUCCESS; 375 } 376 377 if (!mWifiNative.setCountryCodeHal( 378 mApInterfaceName, mCountryCode.toUpperCase(Locale.ROOT))) { 379 if (band == SoftApConfiguration.BAND_5GHZ) { 380 // Return an error if failed to set country code when AP is configured for 381 // 5GHz band. 382 Log.e(TAG, "Failed to set country code, required for setting up soft ap in 5GHz"); 383 return ERROR_GENERIC; 384 } 385 // Failure to set country code is not fatal for other band options. 386 } 387 return SUCCESS; 388 } 389 390 /** 391 * Start a soft AP instance as configured. 392 * 393 * @return integer result code 394 */ startSoftAp()395 private int startSoftAp() { 396 SoftApConfiguration config = mApConfig.getSoftApConfiguration(); 397 if (config == null || config.getSsid() == null) { 398 Log.e(TAG, "Unable to start soft AP without valid configuration"); 399 return ERROR_GENERIC; 400 } 401 402 Log.d(TAG, "band " + config.getBand() + " iface " 403 + mApInterfaceName + " country " + mCountryCode); 404 405 int result = setMacAddress(); 406 if (result != SUCCESS) { 407 return result; 408 } 409 410 result = setCountryCode(); 411 if (result != SUCCESS) { 412 return result; 413 } 414 415 // Make a copy of configuration for updating AP band and channel. 416 SoftApConfiguration.Builder localConfigBuilder = new SoftApConfiguration.Builder(config); 417 418 boolean acsEnabled = mCurrentSoftApCapability.areFeaturesSupported( 419 SoftApCapability.SOFTAP_FEATURE_ACS_OFFLOAD); 420 421 result = ApConfigUtil.updateApChannelConfig( 422 mWifiNative, mContext.getResources(), mCountryCode, localConfigBuilder, config, 423 acsEnabled); 424 if (result != SUCCESS) { 425 Log.e(TAG, "Failed to update AP band and channel"); 426 return result; 427 } 428 429 if (config.isHiddenSsid()) { 430 Log.d(TAG, "SoftAP is a hidden network"); 431 } 432 433 if (!ApConfigUtil.checkSupportAllConfiguration(config, mCurrentSoftApCapability)) { 434 Log.d(TAG, "Unsupported Configuration detect! config = " + config); 435 return ERROR_UNSUPPORTED_CONFIGURATION; 436 } 437 438 if (!mWifiNative.startSoftAp(mApInterfaceName, 439 localConfigBuilder.build(), mSoftApListener)) { 440 Log.e(TAG, "Soft AP start failed"); 441 return ERROR_GENERIC; 442 } 443 444 mWifiDiagnostics.startLogging(mApInterfaceName); 445 mStartTimestamp = FORMATTER.format(new Date(System.currentTimeMillis())); 446 Log.d(TAG, "Soft AP is started "); 447 448 return SUCCESS; 449 } 450 451 /** 452 * Disconnect all connected clients on active softap interface(s). 453 * This is usually done just before stopSoftAp(). 454 */ disconnectAllClients()455 private void disconnectAllClients() { 456 for (WifiClient client : mConnectedClients) { 457 mWifiNative.forceClientDisconnect(mApInterfaceName, client.getMacAddress(), 458 SAP_CLIENT_DISCONNECT_REASON_CODE_UNSPECIFIED); 459 } 460 } 461 462 /** 463 * Teardown soft AP and teardown the interface. 464 */ stopSoftAp()465 private void stopSoftAp() { 466 disconnectAllClients(); 467 mWifiDiagnostics.stopLogging(mApInterfaceName); 468 mWifiNative.teardownInterface(mApInterfaceName); 469 Log.d(TAG, "Soft AP is stopped"); 470 } 471 checkSoftApClient(SoftApConfiguration config, WifiClient newClient)472 private boolean checkSoftApClient(SoftApConfiguration config, WifiClient newClient) { 473 if (!mCurrentSoftApCapability.areFeaturesSupported( 474 SoftApCapability.SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT)) { 475 return true; 476 } 477 478 if (mBlockedClientList.contains(newClient.getMacAddress())) { 479 Log.d(TAG, "Force disconnect for client: " + newClient + "in blocked list"); 480 mWifiNative.forceClientDisconnect( 481 mApInterfaceName, newClient.getMacAddress(), 482 WifiManager.SAP_CLIENT_BLOCK_REASON_CODE_BLOCKED_BY_USER); 483 return false; 484 } 485 if (config.isClientControlByUserEnabled() 486 && !mAllowedClientList.contains(newClient.getMacAddress())) { 487 mSoftApCallback.onBlockedClientConnecting(newClient, 488 WifiManager.SAP_CLIENT_BLOCK_REASON_CODE_BLOCKED_BY_USER); 489 Log.d(TAG, "Force disconnect for unauthorized client: " + newClient); 490 mWifiNative.forceClientDisconnect( 491 mApInterfaceName, newClient.getMacAddress(), 492 WifiManager.SAP_CLIENT_BLOCK_REASON_CODE_BLOCKED_BY_USER); 493 return false; 494 } 495 int maxConfig = mCurrentSoftApCapability.getMaxSupportedClients(); 496 if (config.getMaxNumberOfClients() > 0) { 497 maxConfig = Math.min(maxConfig, config.getMaxNumberOfClients()); 498 } 499 500 if (mConnectedClients.size() >= maxConfig) { 501 Log.i(TAG, "No more room for new client:" + newClient); 502 mWifiNative.forceClientDisconnect( 503 mApInterfaceName, newClient.getMacAddress(), 504 WifiManager.SAP_CLIENT_BLOCK_REASON_CODE_NO_MORE_STAS); 505 mSoftApCallback.onBlockedClientConnecting(newClient, 506 WifiManager.SAP_CLIENT_BLOCK_REASON_CODE_NO_MORE_STAS); 507 // Avoid report the max client blocked in the same settings. 508 if (!mEverReportMetricsForMaxClient) { 509 mWifiMetrics.noteSoftApClientBlocked(maxConfig); 510 mEverReportMetricsForMaxClient = true; 511 } 512 return false; 513 } 514 return true; 515 } 516 517 private class SoftApStateMachine extends StateMachine { 518 // Commands for the state machine. 519 public static final int CMD_START = 0; 520 public static final int CMD_FAILURE = 2; 521 public static final int CMD_INTERFACE_STATUS_CHANGED = 3; 522 public static final int CMD_ASSOCIATED_STATIONS_CHANGED = 4; 523 public static final int CMD_NO_ASSOCIATED_STATIONS_TIMEOUT = 5; 524 public static final int CMD_INTERFACE_DESTROYED = 7; 525 public static final int CMD_INTERFACE_DOWN = 8; 526 public static final int CMD_SOFT_AP_CHANNEL_SWITCHED = 9; 527 public static final int CMD_UPDATE_CAPABILITY = 10; 528 public static final int CMD_UPDATE_CONFIG = 11; 529 530 private final State mIdleState = new IdleState(); 531 private final State mStartedState = new StartedState(); 532 533 private final InterfaceCallback mWifiNativeInterfaceCallback = new InterfaceCallback() { 534 @Override 535 public void onDestroyed(String ifaceName) { 536 if (mApInterfaceName != null && mApInterfaceName.equals(ifaceName)) { 537 sendMessage(CMD_INTERFACE_DESTROYED); 538 } 539 } 540 541 @Override 542 public void onUp(String ifaceName) { 543 if (mApInterfaceName != null && mApInterfaceName.equals(ifaceName)) { 544 sendMessage(CMD_INTERFACE_STATUS_CHANGED, 1); 545 } 546 } 547 548 @Override 549 public void onDown(String ifaceName) { 550 if (mApInterfaceName != null && mApInterfaceName.equals(ifaceName)) { 551 sendMessage(CMD_INTERFACE_STATUS_CHANGED, 0); 552 } 553 } 554 }; 555 SoftApStateMachine(Looper looper)556 SoftApStateMachine(Looper looper) { 557 super(TAG, looper); 558 559 addState(mIdleState); 560 addState(mStartedState); 561 562 setInitialState(mIdleState); 563 start(); 564 } 565 566 private class IdleState extends State { 567 @Override enter()568 public void enter() { 569 mApInterfaceName = null; 570 mIfaceIsUp = false; 571 mIfaceIsDestroyed = false; 572 } 573 574 @Override processMessage(Message message)575 public boolean processMessage(Message message) { 576 switch (message.what) { 577 case CMD_START: 578 mApInterfaceName = mWifiNative.setupInterfaceForSoftApMode( 579 mWifiNativeInterfaceCallback); 580 if (TextUtils.isEmpty(mApInterfaceName)) { 581 Log.e(TAG, "setup failure when creating ap interface."); 582 updateApState(WifiManager.WIFI_AP_STATE_FAILED, 583 WifiManager.WIFI_AP_STATE_DISABLED, 584 WifiManager.SAP_START_FAILURE_GENERAL); 585 mWifiMetrics.incrementSoftApStartResult( 586 false, WifiManager.SAP_START_FAILURE_GENERAL); 587 mModeListener.onStartFailure(); 588 break; 589 } 590 mSoftApNotifier.dismissSoftApShutDownTimeoutExpiredNotification(); 591 updateApState(WifiManager.WIFI_AP_STATE_ENABLING, 592 WifiManager.WIFI_AP_STATE_DISABLED, 0); 593 int result = startSoftAp(); 594 if (result != SUCCESS) { 595 int failureReason = WifiManager.SAP_START_FAILURE_GENERAL; 596 if (result == ERROR_NO_CHANNEL) { 597 failureReason = WifiManager.SAP_START_FAILURE_NO_CHANNEL; 598 } else if (result == ERROR_UNSUPPORTED_CONFIGURATION) { 599 failureReason = WifiManager 600 .SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION; 601 } 602 updateApState(WifiManager.WIFI_AP_STATE_FAILED, 603 WifiManager.WIFI_AP_STATE_ENABLING, 604 failureReason); 605 stopSoftAp(); 606 mWifiMetrics.incrementSoftApStartResult(false, failureReason); 607 mModeListener.onStartFailure(); 608 break; 609 } 610 transitionTo(mStartedState); 611 break; 612 case CMD_UPDATE_CAPABILITY: 613 // Capability should only changed by carrier requirement. Only apply to 614 // Tether Mode 615 if (mApConfig.getTargetMode() == WifiManager.IFACE_IP_MODE_TETHERED) { 616 SoftApCapability capability = (SoftApCapability) message.obj; 617 mCurrentSoftApCapability = new SoftApCapability(capability); 618 } 619 break; 620 case CMD_UPDATE_CONFIG: 621 SoftApConfiguration newConfig = (SoftApConfiguration) message.obj; 622 Log.d(TAG, "Configuration changed to " + newConfig); 623 mApConfig = new SoftApModeConfiguration(mApConfig.getTargetMode(), 624 newConfig, mCurrentSoftApCapability); 625 mBlockedClientList = new HashSet<>(newConfig.getBlockedClientList()); 626 mAllowedClientList = new HashSet<>(newConfig.getAllowedClientList()); 627 mTimeoutEnabled = newConfig.isAutoShutdownEnabled(); 628 break; 629 default: 630 // Ignore all other commands. 631 break; 632 } 633 634 return HANDLED; 635 } 636 } 637 638 private class StartedState extends State { 639 private WakeupMessage mSoftApTimeoutMessage; 640 scheduleTimeoutMessage()641 private void scheduleTimeoutMessage() { 642 if (!mTimeoutEnabled || mConnectedClients.size() != 0) { 643 cancelTimeoutMessage(); 644 return; 645 } 646 long timeout = mApConfig.getSoftApConfiguration().getShutdownTimeoutMillis(); 647 if (timeout == 0) { 648 timeout = mDefaultShutDownTimeoutMills; 649 } 650 mSoftApTimeoutMessage.schedule(SystemClock.elapsedRealtime() 651 + timeout); 652 Log.d(TAG, "Timeout message scheduled, delay = " 653 + timeout); 654 } 655 cancelTimeoutMessage()656 private void cancelTimeoutMessage() { 657 mSoftApTimeoutMessage.cancel(); 658 Log.d(TAG, "Timeout message canceled"); 659 } 660 661 /** 662 * When configuration changed, it need to force some clients disconnect to match the 663 * configuration. 664 */ updateClientConnection()665 private void updateClientConnection() { 666 if (!mCurrentSoftApCapability.areFeaturesSupported( 667 SoftApCapability.SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT)) { 668 return; 669 } 670 final int maxAllowedClientsByHardwareAndCarrier = 671 mCurrentSoftApCapability.getMaxSupportedClients(); 672 final int userApConfigMaxClientCount = 673 mApConfig.getSoftApConfiguration().getMaxNumberOfClients(); 674 int finalMaxClientCount = maxAllowedClientsByHardwareAndCarrier; 675 if (userApConfigMaxClientCount > 0) { 676 finalMaxClientCount = Math.min(userApConfigMaxClientCount, 677 maxAllowedClientsByHardwareAndCarrier); 678 } 679 int targetDisconnectClientNumber = mConnectedClients.size() - finalMaxClientCount; 680 List<WifiClient> allowedConnectedList = new ArrayList<>(); 681 Iterator<WifiClient> iterator = mConnectedClients.iterator(); 682 while (iterator.hasNext()) { 683 WifiClient client = iterator.next(); 684 if (mBlockedClientList.contains(client.getMacAddress()) 685 || (mApConfig.getSoftApConfiguration().isClientControlByUserEnabled() 686 && !mAllowedClientList.contains(client.getMacAddress()))) { 687 Log.d(TAG, "Force disconnect for not allowed client: " + client); 688 mWifiNative.forceClientDisconnect( 689 mApInterfaceName, client.getMacAddress(), 690 WifiManager.SAP_CLIENT_BLOCK_REASON_CODE_BLOCKED_BY_USER); 691 targetDisconnectClientNumber--; 692 } else { 693 allowedConnectedList.add(client); 694 } 695 } 696 697 if (targetDisconnectClientNumber > 0) { 698 Iterator<WifiClient> allowedClientIterator = allowedConnectedList.iterator(); 699 while (allowedClientIterator.hasNext()) { 700 if (targetDisconnectClientNumber == 0) break; 701 WifiClient allowedClient = allowedClientIterator.next(); 702 Log.d(TAG, "Force disconnect for client due to no more room: " 703 + allowedClient); 704 mWifiNative.forceClientDisconnect( 705 mApInterfaceName, allowedClient.getMacAddress(), 706 WifiManager.SAP_CLIENT_BLOCK_REASON_CODE_NO_MORE_STAS); 707 targetDisconnectClientNumber--; 708 } 709 } 710 } 711 712 /** 713 * Set stations associated with this soft AP 714 * @param client The station for which connection state changed. 715 * @param isConnected True for the connection changed to connect, otherwise false. 716 */ updateConnectedClients(WifiClient client, boolean isConnected)717 private void updateConnectedClients(WifiClient client, boolean isConnected) { 718 if (client == null) { 719 return; 720 } 721 722 int index = mConnectedClients.indexOf(client); 723 if ((index != -1) == isConnected) { 724 Log.e(TAG, "Drop client connection event, client " 725 + client + "isConnected: " + isConnected 726 + " , duplicate event or client is blocked"); 727 return; 728 } 729 if (isConnected) { 730 boolean isAllow = checkSoftApClient( 731 mApConfig.getSoftApConfiguration(), client); 732 if (isAllow) { 733 mConnectedClients.add(client); 734 } else { 735 return; 736 } 737 } else { 738 mConnectedClients.remove(index); 739 } 740 741 Log.d(TAG, "The connected wifi stations have changed with count: " 742 + mConnectedClients.size() + ": " + mConnectedClients); 743 744 if (mSoftApCallback != null) { 745 mSoftApCallback.onConnectedClientsChanged(mConnectedClients); 746 } else { 747 Log.e(TAG, 748 "SoftApCallback is null. Dropping ConnectedClientsChanged event." 749 ); 750 } 751 752 mWifiMetrics.addSoftApNumAssociatedStationsChangedEvent( 753 mConnectedClients.size(), mApConfig.getTargetMode()); 754 755 scheduleTimeoutMessage(); 756 } 757 setSoftApChannel(int freq, @WifiAnnotations.Bandwidth int apBandwidth)758 private void setSoftApChannel(int freq, @WifiAnnotations.Bandwidth int apBandwidth) { 759 Log.d(TAG, "Channel switched. Frequency: " + freq 760 + " Bandwidth: " + apBandwidth); 761 762 if (freq == mCurrentSoftApInfo.getFrequency() 763 && apBandwidth == mCurrentSoftApInfo.getBandwidth()) { 764 return; // no change 765 } 766 767 mCurrentSoftApInfo.setFrequency(freq); 768 mCurrentSoftApInfo.setBandwidth(apBandwidth); 769 mSoftApCallback.onInfoChanged(mCurrentSoftApInfo); 770 771 // ignore invalid freq and softap disable case for metrics 772 if (freq > 0 && apBandwidth != SoftApInfo.CHANNEL_WIDTH_INVALID) { 773 mWifiMetrics.addSoftApChannelSwitchedEvent(mCurrentSoftApInfo.getFrequency(), 774 mCurrentSoftApInfo.getBandwidth(), mApConfig.getTargetMode()); 775 updateUserBandPreferenceViolationMetricsIfNeeded(); 776 } 777 } 778 onUpChanged(boolean isUp)779 private void onUpChanged(boolean isUp) { 780 if (isUp == mIfaceIsUp) { 781 return; // no change 782 } 783 784 mIfaceIsUp = isUp; 785 if (isUp) { 786 Log.d(TAG, "SoftAp is ready for use"); 787 updateApState(WifiManager.WIFI_AP_STATE_ENABLED, 788 WifiManager.WIFI_AP_STATE_ENABLING, 0); 789 mModeListener.onStarted(); 790 mWifiMetrics.incrementSoftApStartResult(true, 0); 791 if (mSoftApCallback != null) { 792 mSoftApCallback.onConnectedClientsChanged(mConnectedClients); 793 } 794 } else { 795 // the interface was up, but goes down 796 sendMessage(CMD_INTERFACE_DOWN); 797 } 798 mWifiMetrics.addSoftApUpChangedEvent(isUp, mApConfig.getTargetMode(), 799 mDefaultShutDownTimeoutMills); 800 if (isUp) { 801 mWifiMetrics.updateSoftApConfiguration(mApConfig.getSoftApConfiguration(), 802 mApConfig.getTargetMode()); 803 mWifiMetrics.updateSoftApCapability(mCurrentSoftApCapability, 804 mApConfig.getTargetMode()); 805 } 806 } 807 808 @Override enter()809 public void enter() { 810 mIfaceIsUp = false; 811 mIfaceIsDestroyed = false; 812 onUpChanged(mWifiNative.isInterfaceUp(mApInterfaceName)); 813 814 Handler handler = mStateMachine.getHandler(); 815 mSoftApTimeoutMessage = new WakeupMessage(mContext, handler, 816 SOFT_AP_SEND_MESSAGE_TIMEOUT_TAG, 817 SoftApStateMachine.CMD_NO_ASSOCIATED_STATIONS_TIMEOUT); 818 819 mSarManager.setSapWifiState(WifiManager.WIFI_AP_STATE_ENABLED); 820 821 Log.d(TAG, "Resetting connected clients on start"); 822 mConnectedClients.clear(); 823 mEverReportMetricsForMaxClient = false; 824 scheduleTimeoutMessage(); 825 } 826 827 @Override exit()828 public void exit() { 829 if (!mIfaceIsDestroyed) { 830 stopSoftAp(); 831 } 832 833 Log.d(TAG, "Resetting num stations on stop"); 834 if (mConnectedClients.size() != 0) { 835 mConnectedClients.clear(); 836 if (mSoftApCallback != null) { 837 mSoftApCallback.onConnectedClientsChanged(mConnectedClients); 838 } 839 mWifiMetrics.addSoftApNumAssociatedStationsChangedEvent( 840 0, mApConfig.getTargetMode()); 841 } 842 cancelTimeoutMessage(); 843 844 // Need this here since we are exiting |Started| state and won't handle any 845 // future CMD_INTERFACE_STATUS_CHANGED events after this point 846 mWifiMetrics.addSoftApUpChangedEvent(false, mApConfig.getTargetMode(), 847 mDefaultShutDownTimeoutMills); 848 updateApState(WifiManager.WIFI_AP_STATE_DISABLED, 849 WifiManager.WIFI_AP_STATE_DISABLING, 0); 850 851 mSarManager.setSapWifiState(WifiManager.WIFI_AP_STATE_DISABLED); 852 mApInterfaceName = null; 853 mIfaceIsUp = false; 854 mIfaceIsDestroyed = false; 855 mRole = ROLE_UNSPECIFIED; 856 mStateMachine.quitNow(); 857 mModeListener.onStopped(); 858 setSoftApChannel(0, SoftApInfo.CHANNEL_WIDTH_INVALID); 859 } 860 updateUserBandPreferenceViolationMetricsIfNeeded()861 private void updateUserBandPreferenceViolationMetricsIfNeeded() { 862 int band = mApConfig.getSoftApConfiguration().getBand(); 863 boolean bandPreferenceViolated = 864 (ScanResult.is24GHz(mCurrentSoftApInfo.getFrequency()) 865 && !ApConfigUtil.containsBand(band, 866 SoftApConfiguration.BAND_2GHZ)) 867 || (ScanResult.is5GHz(mCurrentSoftApInfo.getFrequency()) 868 && !ApConfigUtil.containsBand(band, 869 SoftApConfiguration.BAND_5GHZ)) 870 || (ScanResult.is6GHz(mCurrentSoftApInfo.getFrequency()) 871 && !ApConfigUtil.containsBand(band, 872 SoftApConfiguration.BAND_6GHZ)); 873 874 if (bandPreferenceViolated) { 875 Log.e(TAG, "Channel does not satisfy user band preference: " 876 + mCurrentSoftApInfo.getFrequency()); 877 mWifiMetrics.incrementNumSoftApUserBandPreferenceUnsatisfied(); 878 } 879 } 880 881 @Override processMessage(Message message)882 public boolean processMessage(Message message) { 883 switch (message.what) { 884 case CMD_ASSOCIATED_STATIONS_CHANGED: 885 if (!(message.obj instanceof NativeWifiClient)) { 886 Log.e(TAG, "Invalid type returned for" 887 + " CMD_ASSOCIATED_STATIONS_CHANGED"); 888 break; 889 } 890 NativeWifiClient nativeClient = (NativeWifiClient) message.obj; 891 boolean isConnected = (message.arg1 == 1); 892 if (nativeClient != null && nativeClient.getMacAddress() != null) { 893 WifiClient client = new WifiClient(nativeClient.getMacAddress()); 894 Log.d(TAG, "CMD_ASSOCIATED_STATIONS_CHANGED, Client: " 895 + nativeClient.getMacAddress().toString() + " isConnected: " 896 + isConnected); 897 updateConnectedClients(client, isConnected); 898 } 899 break; 900 case CMD_SOFT_AP_CHANNEL_SWITCHED: 901 if (message.arg1 < 0) { 902 Log.e(TAG, "Invalid ap channel frequency: " + message.arg1); 903 break; 904 } 905 setSoftApChannel(message.arg1, message.arg2); 906 break; 907 case CMD_INTERFACE_STATUS_CHANGED: 908 boolean isUp = message.arg1 == 1; 909 onUpChanged(isUp); 910 break; 911 case CMD_START: 912 // Already started, ignore this command. 913 break; 914 case CMD_NO_ASSOCIATED_STATIONS_TIMEOUT: 915 if (!mTimeoutEnabled) { 916 Log.wtf(TAG, "Timeout message received while timeout is disabled." 917 + " Dropping."); 918 break; 919 } 920 if (mConnectedClients.size() != 0) { 921 Log.wtf(TAG, "Timeout message received but has clients. Dropping."); 922 break; 923 } 924 mSoftApNotifier.showSoftApShutDownTimeoutExpiredNotification(); 925 Log.i(TAG, "Timeout message received. Stopping soft AP."); 926 updateApState(WifiManager.WIFI_AP_STATE_DISABLING, 927 WifiManager.WIFI_AP_STATE_ENABLED, 0); 928 transitionTo(mIdleState); 929 break; 930 case CMD_INTERFACE_DESTROYED: 931 Log.d(TAG, "Interface was cleanly destroyed."); 932 updateApState(WifiManager.WIFI_AP_STATE_DISABLING, 933 WifiManager.WIFI_AP_STATE_ENABLED, 0); 934 mIfaceIsDestroyed = true; 935 transitionTo(mIdleState); 936 break; 937 case CMD_FAILURE: 938 Log.w(TAG, "hostapd failure, stop and report failure"); 939 /* fall through */ 940 case CMD_INTERFACE_DOWN: 941 Log.w(TAG, "interface error, stop and report failure"); 942 updateApState(WifiManager.WIFI_AP_STATE_FAILED, 943 WifiManager.WIFI_AP_STATE_ENABLED, 944 WifiManager.SAP_START_FAILURE_GENERAL); 945 updateApState(WifiManager.WIFI_AP_STATE_DISABLING, 946 WifiManager.WIFI_AP_STATE_FAILED, 0); 947 transitionTo(mIdleState); 948 break; 949 case CMD_UPDATE_CAPABILITY: 950 // Capability should only changed by carrier requirement. Only apply to 951 // Tether Mode 952 if (mApConfig.getTargetMode() == WifiManager.IFACE_IP_MODE_TETHERED) { 953 SoftApCapability capability = (SoftApCapability) message.obj; 954 mCurrentSoftApCapability = new SoftApCapability(capability); 955 mWifiMetrics.updateSoftApCapability(mCurrentSoftApCapability, 956 mApConfig.getTargetMode()); 957 updateClientConnection(); 958 } 959 break; 960 case CMD_UPDATE_CONFIG: 961 SoftApConfiguration newConfig = (SoftApConfiguration) message.obj; 962 SoftApConfiguration currentConfig = mApConfig.getSoftApConfiguration(); 963 if (mIsRandomizeBssid) { 964 // Current bssid is ramdon because unset. Set back to null.. 965 currentConfig = new SoftApConfiguration.Builder(currentConfig) 966 .setBssid(null) 967 .build(); 968 } 969 if (!ApConfigUtil.checkConfigurationChangeNeedToRestart( 970 currentConfig, newConfig)) { 971 Log.d(TAG, "Configuration changed to " + newConfig); 972 if (mApConfig.getSoftApConfiguration().getMaxNumberOfClients() 973 != newConfig.getMaxNumberOfClients()) { 974 Log.d(TAG, "Max Client changed, reset to record the metrics"); 975 mEverReportMetricsForMaxClient = false; 976 } 977 boolean needRescheduleTimer = 978 mApConfig.getSoftApConfiguration().getShutdownTimeoutMillis() 979 != newConfig.getShutdownTimeoutMillis() 980 || mTimeoutEnabled != newConfig.isAutoShutdownEnabled(); 981 mBlockedClientList = new HashSet<>(newConfig.getBlockedClientList()); 982 mAllowedClientList = new HashSet<>(newConfig.getAllowedClientList()); 983 mTimeoutEnabled = newConfig.isAutoShutdownEnabled(); 984 mApConfig = new SoftApModeConfiguration(mApConfig.getTargetMode(), 985 newConfig, mCurrentSoftApCapability); 986 updateClientConnection(); 987 if (needRescheduleTimer) { 988 cancelTimeoutMessage(); 989 scheduleTimeoutMessage(); 990 } 991 mWifiMetrics.updateSoftApConfiguration( 992 mApConfig.getSoftApConfiguration(), 993 mApConfig.getTargetMode()); 994 } else { 995 Log.d(TAG, "Ignore the config: " + newConfig 996 + " update since it requires restart"); 997 } 998 break; 999 default: 1000 return NOT_HANDLED; 1001 } 1002 return HANDLED; 1003 } 1004 } 1005 } 1006 } 1007