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 com.android.server.wifi.util.ApConfigUtil.ERROR_GENERIC; 20 import static com.android.server.wifi.util.ApConfigUtil.ERROR_NO_CHANNEL; 21 import static com.android.server.wifi.util.ApConfigUtil.SUCCESS; 22 23 import android.annotation.NonNull; 24 import android.content.Context; 25 import android.content.Intent; 26 import android.database.ContentObserver; 27 import android.net.MacAddress; 28 import android.net.wifi.ScanResult; 29 import android.net.wifi.WifiClient; 30 import android.net.wifi.WifiConfiguration; 31 import android.net.wifi.WifiManager; 32 import android.os.Handler; 33 import android.os.Looper; 34 import android.os.Message; 35 import android.os.SystemClock; 36 import android.os.UserHandle; 37 import android.provider.Settings; 38 import android.text.TextUtils; 39 import android.util.Log; 40 41 import com.android.internal.R; 42 import com.android.internal.annotations.VisibleForTesting; 43 import com.android.internal.util.IState; 44 import com.android.internal.util.State; 45 import com.android.internal.util.StateMachine; 46 import com.android.internal.util.WakeupMessage; 47 import com.android.server.wifi.WifiNative.InterfaceCallback; 48 import com.android.server.wifi.WifiNative.SoftApListener; 49 import com.android.server.wifi.util.ApConfigUtil; 50 import com.android.server.wifi.wificond.NativeWifiClient; 51 52 import java.io.FileDescriptor; 53 import java.io.PrintWriter; 54 import java.util.ArrayList; 55 import java.util.List; 56 import java.util.Locale; 57 58 /** 59 * Manage WiFi in AP mode. 60 * The internal state machine runs under the ClientModeImpl handler thread context. 61 */ 62 public class SoftApManager implements ActiveModeManager { 63 private static final String TAG = "SoftApManager"; 64 65 // Minimum limit to use for timeout delay if the value from overlay setting is too small. 66 private static final int MIN_SOFT_AP_TIMEOUT_DELAY_MS = 600_000; // 10 minutes 67 68 @VisibleForTesting 69 public static final String SOFT_AP_SEND_MESSAGE_TIMEOUT_TAG = TAG 70 + " Soft AP Send Message Timeout"; 71 72 private final Context mContext; 73 private final FrameworkFacade mFrameworkFacade; 74 private final WifiNative mWifiNative; 75 76 private final String mCountryCode; 77 78 private final SoftApStateMachine mStateMachine; 79 80 private final WifiManager.SoftApCallback mCallback; 81 82 private String mApInterfaceName; 83 private boolean mIfaceIsUp; 84 private boolean mIfaceIsDestroyed; 85 86 private final WifiApConfigStore mWifiApConfigStore; 87 88 private final WifiMetrics mWifiMetrics; 89 90 private final int mMode; 91 private WifiConfiguration mApConfig; 92 93 private int mReportedFrequency = -1; 94 private int mReportedBandwidth = -1; 95 96 private List<WifiClient> mConnectedClients = new ArrayList<>(); 97 private boolean mTimeoutEnabled = false; 98 99 private final SarManager mSarManager; 100 101 private long mStartTimestamp = -1; 102 103 private BaseWifiDiagnostics mWifiDiagnostics; 104 105 /** 106 * Listener for soft AP events. 107 */ 108 private final SoftApListener mSoftApListener = new SoftApListener() { 109 110 @Override 111 public void onFailure() { 112 mStateMachine.sendMessage(SoftApStateMachine.CMD_FAILURE); 113 } 114 115 @Override 116 public void onConnectedClientsChanged(List<NativeWifiClient> clients) { 117 mStateMachine.sendMessage(SoftApStateMachine.CMD_ASSOCIATED_STATIONS_CHANGED, 118 clients); 119 } 120 121 @Override 122 public void onSoftApChannelSwitched(int frequency, int bandwidth) { 123 mStateMachine.sendMessage( 124 SoftApStateMachine.CMD_SOFT_AP_CHANNEL_SWITCHED, frequency, bandwidth); 125 } 126 }; 127 SoftApManager(@onNull Context context, @NonNull Looper looper, @NonNull FrameworkFacade framework, @NonNull WifiNative wifiNative, String countryCode, @NonNull WifiManager.SoftApCallback callback, @NonNull WifiApConfigStore wifiApConfigStore, @NonNull SoftApModeConfiguration apConfig, @NonNull WifiMetrics wifiMetrics, @NonNull SarManager sarManager, @NonNull BaseWifiDiagnostics wifiDiagnostics)128 public SoftApManager(@NonNull Context context, 129 @NonNull Looper looper, 130 @NonNull FrameworkFacade framework, 131 @NonNull WifiNative wifiNative, 132 String countryCode, 133 @NonNull WifiManager.SoftApCallback callback, 134 @NonNull WifiApConfigStore wifiApConfigStore, 135 @NonNull SoftApModeConfiguration apConfig, 136 @NonNull WifiMetrics wifiMetrics, 137 @NonNull SarManager sarManager, 138 @NonNull BaseWifiDiagnostics wifiDiagnostics) { 139 mContext = context; 140 mFrameworkFacade = framework; 141 mWifiNative = wifiNative; 142 mCountryCode = countryCode; 143 mCallback = callback; 144 mWifiApConfigStore = wifiApConfigStore; 145 mMode = apConfig.getTargetMode(); 146 WifiConfiguration config = apConfig.getWifiConfiguration(); 147 if (config == null) { 148 mApConfig = mWifiApConfigStore.getApConfiguration(); 149 } else { 150 mApConfig = config; 151 } 152 mWifiMetrics = wifiMetrics; 153 mSarManager = sarManager; 154 mWifiDiagnostics = wifiDiagnostics; 155 mStateMachine = new SoftApStateMachine(looper); 156 } 157 158 /** 159 * Start soft AP with the supplied config. 160 */ start()161 public void start() { 162 mStateMachine.sendMessage(SoftApStateMachine.CMD_START, mApConfig); 163 } 164 165 /** 166 * Stop soft AP. 167 */ stop()168 public void stop() { 169 Log.d(TAG, " currentstate: " + getCurrentStateName()); 170 if (mApInterfaceName != null) { 171 if (mIfaceIsUp) { 172 updateApState(WifiManager.WIFI_AP_STATE_DISABLING, 173 WifiManager.WIFI_AP_STATE_ENABLED, 0); 174 } else { 175 updateApState(WifiManager.WIFI_AP_STATE_DISABLING, 176 WifiManager.WIFI_AP_STATE_ENABLING, 0); 177 } 178 } 179 mStateMachine.quitNow(); 180 } 181 getScanMode()182 public @ScanMode int getScanMode() { 183 return SCAN_NONE; 184 } 185 getIpMode()186 public int getIpMode() { 187 return mMode; 188 } 189 190 /** 191 * Dump info about this softap manager. 192 */ dump(FileDescriptor fd, PrintWriter pw, String[] args)193 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 194 pw.println("--Dump of SoftApManager--"); 195 196 pw.println("current StateMachine mode: " + getCurrentStateName()); 197 pw.println("mApInterfaceName: " + mApInterfaceName); 198 pw.println("mIfaceIsUp: " + mIfaceIsUp); 199 pw.println("mMode: " + mMode); 200 pw.println("mCountryCode: " + mCountryCode); 201 if (mApConfig != null) { 202 pw.println("mApConfig.SSID: " + mApConfig.SSID); 203 pw.println("mApConfig.apBand: " + mApConfig.apBand); 204 pw.println("mApConfig.hiddenSSID: " + mApConfig.hiddenSSID); 205 } else { 206 pw.println("mApConfig: null"); 207 } 208 pw.println("mConnectedClients.size(): " + mConnectedClients.size()); 209 pw.println("mTimeoutEnabled: " + mTimeoutEnabled); 210 pw.println("mReportedFrequency: " + mReportedFrequency); 211 pw.println("mReportedBandwidth: " + mReportedBandwidth); 212 pw.println("mStartTimestamp: " + mStartTimestamp); 213 } 214 getCurrentStateName()215 private String getCurrentStateName() { 216 IState currentState = mStateMachine.getCurrentState(); 217 218 if (currentState != null) { 219 return currentState.getName(); 220 } 221 222 return "StateMachine not active"; 223 } 224 225 /** 226 * Update AP state. 227 * @param newState new AP state 228 * @param currentState current AP state 229 * @param reason Failure reason if the new AP state is in failure state 230 */ updateApState(int newState, int currentState, int reason)231 private void updateApState(int newState, int currentState, int reason) { 232 mCallback.onStateChanged(newState, reason); 233 234 //send the AP state change broadcast 235 final Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION); 236 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 237 intent.putExtra(WifiManager.EXTRA_WIFI_AP_STATE, newState); 238 intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_AP_STATE, currentState); 239 if (newState == WifiManager.WIFI_AP_STATE_FAILED) { 240 //only set reason number when softAP start failed 241 intent.putExtra(WifiManager.EXTRA_WIFI_AP_FAILURE_REASON, reason); 242 } 243 244 intent.putExtra(WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME, mApInterfaceName); 245 intent.putExtra(WifiManager.EXTRA_WIFI_AP_MODE, mMode); 246 mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); 247 } 248 249 /** 250 * Start a soft AP instance with the given configuration. 251 * @param config AP configuration 252 * @return integer result code 253 */ startSoftAp(WifiConfiguration config)254 private int startSoftAp(WifiConfiguration config) { 255 if (config == null || config.SSID == null) { 256 Log.e(TAG, "Unable to start soft AP without valid configuration"); 257 return ERROR_GENERIC; 258 } 259 // Setup country code 260 if (TextUtils.isEmpty(mCountryCode)) { 261 if (config.apBand == WifiConfiguration.AP_BAND_5GHZ) { 262 // Country code is mandatory for 5GHz band. 263 Log.e(TAG, "Invalid country code, required for setting up " 264 + "soft ap in 5GHz"); 265 return ERROR_GENERIC; 266 } 267 // Absence of country code is not fatal for 2Ghz & Any band options. 268 } else if (!mWifiNative.setCountryCodeHal( 269 mApInterfaceName, mCountryCode.toUpperCase(Locale.ROOT))) { 270 if (config.apBand == WifiConfiguration.AP_BAND_5GHZ) { 271 // Return an error if failed to set country code when AP is configured for 272 // 5GHz band. 273 Log.e(TAG, "Failed to set country code, required for setting up " 274 + "soft ap in 5GHz"); 275 return ERROR_GENERIC; 276 } 277 // Failure to set country code is not fatal for 2Ghz & Any band options. 278 } 279 280 // Make a copy of configuration for updating AP band and channel. 281 WifiConfiguration localConfig = new WifiConfiguration(config); 282 283 int result = ApConfigUtil.updateApChannelConfig( 284 mWifiNative, mCountryCode, 285 mWifiApConfigStore.getAllowed2GChannel(), localConfig); 286 287 if (result != SUCCESS) { 288 Log.e(TAG, "Failed to update AP band and channel"); 289 return result; 290 } 291 292 if (localConfig.hiddenSSID) { 293 Log.d(TAG, "SoftAP is a hidden network"); 294 } 295 if (!mWifiNative.startSoftAp(mApInterfaceName, localConfig, mSoftApListener)) { 296 Log.e(TAG, "Soft AP start failed"); 297 return ERROR_GENERIC; 298 } 299 mWifiDiagnostics.startLogging(mApInterfaceName); 300 mStartTimestamp = SystemClock.elapsedRealtime(); 301 Log.d(TAG, "Soft AP is started"); 302 303 return SUCCESS; 304 } 305 306 /** 307 * Teardown soft AP and teardown the interface. 308 */ stopSoftAp()309 private void stopSoftAp() { 310 mWifiDiagnostics.stopLogging(mApInterfaceName); 311 mWifiNative.teardownInterface(mApInterfaceName); 312 Log.d(TAG, "Soft AP is stopped"); 313 } 314 315 private class SoftApStateMachine extends StateMachine { 316 // Commands for the state machine. 317 public static final int CMD_START = 0; 318 public static final int CMD_FAILURE = 2; 319 public static final int CMD_INTERFACE_STATUS_CHANGED = 3; 320 public static final int CMD_ASSOCIATED_STATIONS_CHANGED = 4; 321 public static final int CMD_NO_ASSOCIATED_STATIONS_TIMEOUT = 5; 322 public static final int CMD_TIMEOUT_TOGGLE_CHANGED = 6; 323 public static final int CMD_INTERFACE_DESTROYED = 7; 324 public static final int CMD_INTERFACE_DOWN = 8; 325 public static final int CMD_SOFT_AP_CHANNEL_SWITCHED = 9; 326 327 private final State mIdleState = new IdleState(); 328 private final State mStartedState = new StartedState(); 329 330 private final InterfaceCallback mWifiNativeInterfaceCallback = new InterfaceCallback() { 331 @Override 332 public void onDestroyed(String ifaceName) { 333 if (mApInterfaceName != null && mApInterfaceName.equals(ifaceName)) { 334 sendMessage(CMD_INTERFACE_DESTROYED); 335 } 336 } 337 338 @Override 339 public void onUp(String ifaceName) { 340 if (mApInterfaceName != null && mApInterfaceName.equals(ifaceName)) { 341 sendMessage(CMD_INTERFACE_STATUS_CHANGED, 1); 342 } 343 } 344 345 @Override 346 public void onDown(String ifaceName) { 347 if (mApInterfaceName != null && mApInterfaceName.equals(ifaceName)) { 348 sendMessage(CMD_INTERFACE_STATUS_CHANGED, 0); 349 } 350 } 351 }; 352 SoftApStateMachine(Looper looper)353 SoftApStateMachine(Looper looper) { 354 super(TAG, looper); 355 356 addState(mIdleState); 357 addState(mStartedState); 358 359 setInitialState(mIdleState); 360 start(); 361 } 362 363 private class IdleState extends State { 364 @Override enter()365 public void enter() { 366 mApInterfaceName = null; 367 mIfaceIsUp = false; 368 mIfaceIsDestroyed = false; 369 } 370 371 @Override processMessage(Message message)372 public boolean processMessage(Message message) { 373 switch (message.what) { 374 case CMD_START: 375 mApInterfaceName = mWifiNative.setupInterfaceForSoftApMode( 376 mWifiNativeInterfaceCallback); 377 if (TextUtils.isEmpty(mApInterfaceName)) { 378 Log.e(TAG, "setup failure when creating ap interface."); 379 updateApState(WifiManager.WIFI_AP_STATE_FAILED, 380 WifiManager.WIFI_AP_STATE_DISABLED, 381 WifiManager.SAP_START_FAILURE_GENERAL); 382 mWifiMetrics.incrementSoftApStartResult( 383 false, WifiManager.SAP_START_FAILURE_GENERAL); 384 break; 385 } 386 updateApState(WifiManager.WIFI_AP_STATE_ENABLING, 387 WifiManager.WIFI_AP_STATE_DISABLED, 0); 388 int result = startSoftAp((WifiConfiguration) message.obj); 389 if (result != SUCCESS) { 390 int failureReason = WifiManager.SAP_START_FAILURE_GENERAL; 391 if (result == ERROR_NO_CHANNEL) { 392 failureReason = WifiManager.SAP_START_FAILURE_NO_CHANNEL; 393 } 394 updateApState(WifiManager.WIFI_AP_STATE_FAILED, 395 WifiManager.WIFI_AP_STATE_ENABLING, 396 failureReason); 397 stopSoftAp(); 398 mWifiMetrics.incrementSoftApStartResult(false, failureReason); 399 break; 400 } 401 transitionTo(mStartedState); 402 break; 403 default: 404 // Ignore all other commands. 405 break; 406 } 407 408 return HANDLED; 409 } 410 } 411 412 private class StartedState extends State { 413 private int mTimeoutDelay; 414 private WakeupMessage mSoftApTimeoutMessage; 415 private SoftApTimeoutEnabledSettingObserver mSettingObserver; 416 417 /** 418 * Observer for timeout settings changes. 419 */ 420 private class SoftApTimeoutEnabledSettingObserver extends ContentObserver { SoftApTimeoutEnabledSettingObserver(Handler handler)421 SoftApTimeoutEnabledSettingObserver(Handler handler) { 422 super(handler); 423 } 424 register()425 public void register() { 426 mFrameworkFacade.registerContentObserver(mContext, 427 Settings.Global.getUriFor(Settings.Global.SOFT_AP_TIMEOUT_ENABLED), 428 true, this); 429 mTimeoutEnabled = getValue(); 430 } 431 unregister()432 public void unregister() { 433 mFrameworkFacade.unregisterContentObserver(mContext, this); 434 } 435 436 @Override onChange(boolean selfChange)437 public void onChange(boolean selfChange) { 438 super.onChange(selfChange); 439 mStateMachine.sendMessage(SoftApStateMachine.CMD_TIMEOUT_TOGGLE_CHANGED, 440 getValue() ? 1 : 0); 441 } 442 getValue()443 private boolean getValue() { 444 boolean enabled = mFrameworkFacade.getIntegerSetting(mContext, 445 Settings.Global.SOFT_AP_TIMEOUT_ENABLED, 1) == 1; 446 return enabled; 447 } 448 } 449 getConfigSoftApTimeoutDelay()450 private int getConfigSoftApTimeoutDelay() { 451 int delay = mContext.getResources().getInteger( 452 R.integer.config_wifi_framework_soft_ap_timeout_delay); 453 if (delay < MIN_SOFT_AP_TIMEOUT_DELAY_MS) { 454 delay = MIN_SOFT_AP_TIMEOUT_DELAY_MS; 455 Log.w(TAG, "Overriding timeout delay with minimum limit value"); 456 } 457 Log.d(TAG, "Timeout delay: " + delay); 458 return delay; 459 } 460 scheduleTimeoutMessage()461 private void scheduleTimeoutMessage() { 462 if (!mTimeoutEnabled) { 463 return; 464 } 465 mSoftApTimeoutMessage.schedule(SystemClock.elapsedRealtime() + mTimeoutDelay); 466 Log.d(TAG, "Timeout message scheduled"); 467 } 468 cancelTimeoutMessage()469 private void cancelTimeoutMessage() { 470 mSoftApTimeoutMessage.cancel(); 471 Log.d(TAG, "Timeout message canceled"); 472 } 473 474 /** 475 * Set stations associated with this soft AP 476 * @param clients The connected stations 477 */ setConnectedClients(List<NativeWifiClient> clients)478 private void setConnectedClients(List<NativeWifiClient> clients) { 479 if (clients == null) { 480 return; 481 } 482 483 List<WifiClient> convertedClients = createWifiClients(clients); 484 if (mConnectedClients.equals(convertedClients)) { 485 return; 486 } 487 488 mConnectedClients = new ArrayList<>(convertedClients); 489 Log.d(TAG, "The connected wifi stations have changed with count: " 490 + clients.size()); 491 492 if (mCallback != null) { 493 mCallback.onConnectedClientsChanged(mConnectedClients); 494 } else { 495 Log.e(TAG, 496 "SoftApCallback is null. Dropping ConnectedClientsChanged event." 497 ); 498 } 499 mWifiMetrics.addSoftApNumAssociatedStationsChangedEvent(mConnectedClients.size(), 500 mMode); 501 502 if (mConnectedClients.size() == 0) { 503 scheduleTimeoutMessage(); 504 } else { 505 cancelTimeoutMessage(); 506 } 507 } 508 createWifiClients(List<NativeWifiClient> nativeClients)509 private List<WifiClient> createWifiClients(List<NativeWifiClient> nativeClients) { 510 List<WifiClient> clients = new ArrayList<>(); 511 if (nativeClients == null || nativeClients.size() == 0) { 512 return clients; 513 } 514 515 for (int i = 0; i < nativeClients.size(); i++) { 516 MacAddress macAddress = MacAddress.fromBytes(nativeClients.get(i).macAddress); 517 WifiClient client = new WifiClient(macAddress); 518 clients.add(client); 519 } 520 521 return clients; 522 } 523 onUpChanged(boolean isUp)524 private void onUpChanged(boolean isUp) { 525 if (isUp == mIfaceIsUp) { 526 return; // no change 527 } 528 529 mIfaceIsUp = isUp; 530 if (isUp) { 531 Log.d(TAG, "SoftAp is ready for use"); 532 updateApState(WifiManager.WIFI_AP_STATE_ENABLED, 533 WifiManager.WIFI_AP_STATE_ENABLING, 0); 534 mWifiMetrics.incrementSoftApStartResult(true, 0); 535 if (mCallback != null) { 536 mCallback.onConnectedClientsChanged(mConnectedClients); 537 } 538 } else { 539 // the interface was up, but goes down 540 sendMessage(CMD_INTERFACE_DOWN); 541 } 542 mWifiMetrics.addSoftApUpChangedEvent(isUp, mMode); 543 } 544 545 @Override enter()546 public void enter() { 547 mIfaceIsUp = false; 548 mIfaceIsDestroyed = false; 549 onUpChanged(mWifiNative.isInterfaceUp(mApInterfaceName)); 550 551 mTimeoutDelay = getConfigSoftApTimeoutDelay(); 552 Handler handler = mStateMachine.getHandler(); 553 mSoftApTimeoutMessage = new WakeupMessage(mContext, handler, 554 SOFT_AP_SEND_MESSAGE_TIMEOUT_TAG, 555 SoftApStateMachine.CMD_NO_ASSOCIATED_STATIONS_TIMEOUT); 556 mSettingObserver = new SoftApTimeoutEnabledSettingObserver(handler); 557 558 if (mSettingObserver != null) { 559 mSettingObserver.register(); 560 } 561 562 mSarManager.setSapWifiState(WifiManager.WIFI_AP_STATE_ENABLED); 563 564 Log.d(TAG, "Resetting connected clients on start"); 565 mConnectedClients = new ArrayList<>(); 566 scheduleTimeoutMessage(); 567 } 568 569 @Override exit()570 public void exit() { 571 if (!mIfaceIsDestroyed) { 572 stopSoftAp(); 573 } 574 575 if (mSettingObserver != null) { 576 mSettingObserver.unregister(); 577 } 578 Log.d(TAG, "Resetting num stations on stop"); 579 setConnectedClients(new ArrayList<>()); 580 cancelTimeoutMessage(); 581 // Need this here since we are exiting |Started| state and won't handle any 582 // future CMD_INTERFACE_STATUS_CHANGED events after this point 583 mWifiMetrics.addSoftApUpChangedEvent(false, mMode); 584 updateApState(WifiManager.WIFI_AP_STATE_DISABLED, 585 WifiManager.WIFI_AP_STATE_DISABLING, 0); 586 587 mSarManager.setSapWifiState(WifiManager.WIFI_AP_STATE_DISABLED); 588 mApInterfaceName = null; 589 mIfaceIsUp = false; 590 mIfaceIsDestroyed = false; 591 mStateMachine.quitNow(); 592 } 593 updateUserBandPreferenceViolationMetricsIfNeeded()594 private void updateUserBandPreferenceViolationMetricsIfNeeded() { 595 boolean bandPreferenceViolated = false; 596 if (mApConfig.apBand == WifiConfiguration.AP_BAND_2GHZ 597 && ScanResult.is5GHz(mReportedFrequency)) { 598 bandPreferenceViolated = true; 599 } else if (mApConfig.apBand == WifiConfiguration.AP_BAND_5GHZ 600 && ScanResult.is24GHz(mReportedFrequency)) { 601 bandPreferenceViolated = true; 602 } 603 if (bandPreferenceViolated) { 604 Log.e(TAG, "Channel does not satisfy user band preference: " 605 + mReportedFrequency); 606 mWifiMetrics.incrementNumSoftApUserBandPreferenceUnsatisfied(); 607 } 608 } 609 610 @Override processMessage(Message message)611 public boolean processMessage(Message message) { 612 switch (message.what) { 613 case CMD_ASSOCIATED_STATIONS_CHANGED: 614 if (!(message.obj instanceof List)) { 615 Log.e(TAG, "Invalid type returned for" 616 + " CMD_ASSOCIATED_STATIONS_CHANGED"); 617 break; 618 } 619 620 Log.d(TAG, "Setting connected stations on" 621 + " CMD_ASSOCIATED_STATIONS_CHANGED"); 622 setConnectedClients((List<NativeWifiClient>) message.obj); 623 break; 624 case CMD_SOFT_AP_CHANNEL_SWITCHED: 625 mReportedFrequency = message.arg1; 626 mReportedBandwidth = message.arg2; 627 Log.d(TAG, "Channel switched. Frequency: " + mReportedFrequency 628 + " Bandwidth: " + mReportedBandwidth); 629 mWifiMetrics.addSoftApChannelSwitchedEvent(mReportedFrequency, 630 mReportedBandwidth, mMode); 631 updateUserBandPreferenceViolationMetricsIfNeeded(); 632 break; 633 case CMD_TIMEOUT_TOGGLE_CHANGED: 634 boolean isEnabled = (message.arg1 == 1); 635 if (mTimeoutEnabled == isEnabled) { 636 break; 637 } 638 mTimeoutEnabled = isEnabled; 639 if (!mTimeoutEnabled) { 640 cancelTimeoutMessage(); 641 } 642 if (mTimeoutEnabled && mConnectedClients.size() == 0) { 643 scheduleTimeoutMessage(); 644 } 645 break; 646 case CMD_INTERFACE_STATUS_CHANGED: 647 boolean isUp = message.arg1 == 1; 648 onUpChanged(isUp); 649 break; 650 case CMD_START: 651 // Already started, ignore this command. 652 break; 653 case CMD_NO_ASSOCIATED_STATIONS_TIMEOUT: 654 if (!mTimeoutEnabled) { 655 Log.wtf(TAG, "Timeout message received while timeout is disabled." 656 + " Dropping."); 657 break; 658 } 659 if (mConnectedClients.size() != 0) { 660 Log.wtf(TAG, "Timeout message received but has clients. Dropping."); 661 break; 662 } 663 Log.i(TAG, "Timeout message received. Stopping soft AP."); 664 updateApState(WifiManager.WIFI_AP_STATE_DISABLING, 665 WifiManager.WIFI_AP_STATE_ENABLED, 0); 666 transitionTo(mIdleState); 667 break; 668 case CMD_INTERFACE_DESTROYED: 669 Log.d(TAG, "Interface was cleanly destroyed."); 670 updateApState(WifiManager.WIFI_AP_STATE_DISABLING, 671 WifiManager.WIFI_AP_STATE_ENABLED, 0); 672 mIfaceIsDestroyed = true; 673 transitionTo(mIdleState); 674 break; 675 case CMD_FAILURE: 676 Log.w(TAG, "hostapd failure, stop and report failure"); 677 /* fall through */ 678 case CMD_INTERFACE_DOWN: 679 Log.w(TAG, "interface error, stop and report failure"); 680 updateApState(WifiManager.WIFI_AP_STATE_FAILED, 681 WifiManager.WIFI_AP_STATE_ENABLED, 682 WifiManager.SAP_START_FAILURE_GENERAL); 683 updateApState(WifiManager.WIFI_AP_STATE_DISABLING, 684 WifiManager.WIFI_AP_STATE_FAILED, 0); 685 transitionTo(mIdleState); 686 break; 687 default: 688 return NOT_HANDLED; 689 } 690 return HANDLED; 691 } 692 } 693 } 694 } 695