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