/*
|
* Copyright (C) 2016 The Android Open Source Project
|
*
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
* you may not use this file except in compliance with the License.
|
* You may obtain a copy of the License at
|
*
|
* http://www.apache.org/licenses/LICENSE-2.0
|
*
|
* Unless required by applicable law or agreed to in writing, software
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* See the License for the specific language governing permissions and
|
* limitations under the License.
|
*/
|
|
package com.android.server.wifi;
|
|
import static com.android.server.wifi.util.ApConfigUtil.ERROR_GENERIC;
|
import static com.android.server.wifi.util.ApConfigUtil.ERROR_NO_CHANNEL;
|
import static com.android.server.wifi.util.ApConfigUtil.SUCCESS;
|
|
import android.annotation.NonNull;
|
import android.content.Context;
|
import android.content.Intent;
|
import android.database.ContentObserver;
|
import android.net.wifi.ScanResult;
|
import android.net.wifi.WifiConfiguration;
|
import android.net.wifi.WifiManager;
|
import android.os.Handler;
|
import android.os.Looper;
|
import android.os.Message;
|
import android.os.SystemClock;
|
import android.os.UserHandle;
|
import android.provider.Settings;
|
import android.text.TextUtils;
|
import android.util.Log;
|
|
import com.android.internal.R;
|
import com.android.internal.annotations.VisibleForTesting;
|
import com.android.internal.util.IState;
|
import com.android.internal.util.State;
|
import com.android.internal.util.StateMachine;
|
import com.android.internal.util.WakeupMessage;
|
import com.android.server.wifi.WifiNative.InterfaceCallback;
|
import com.android.server.wifi.WifiNative.SoftApListener;
|
import com.android.server.wifi.util.ApConfigUtil;
|
|
import java.io.FileDescriptor;
|
import java.io.PrintWriter;
|
import java.util.Locale;
|
|
/**
|
* Manage WiFi in AP mode.
|
* The internal state machine runs under the ClientModeImpl handler thread context.
|
*/
|
public class SoftApManager implements ActiveModeManager {
|
private static final String TAG = "SoftApManager";
|
|
// Minimum limit to use for timeout delay if the value from overlay setting is too small.
|
private static final int MIN_SOFT_AP_TIMEOUT_DELAY_MS = 600_000; // 10 minutes
|
|
@VisibleForTesting
|
public static final String SOFT_AP_SEND_MESSAGE_TIMEOUT_TAG = TAG
|
+ " Soft AP Send Message Timeout";
|
|
private final Context mContext;
|
private final FrameworkFacade mFrameworkFacade;
|
private final WifiNative mWifiNative;
|
|
private final String mCountryCode;
|
|
private final SoftApStateMachine mStateMachine;
|
|
private final WifiManager.SoftApCallback mCallback;
|
|
private String mApInterfaceName;
|
private boolean mIfaceIsUp;
|
private boolean mIfaceIsDestroyed;
|
|
private final WifiApConfigStore mWifiApConfigStore;
|
|
private final WifiMetrics mWifiMetrics;
|
|
private final int mMode;
|
private WifiConfiguration mApConfig;
|
|
private int mReportedFrequency = -1;
|
private int mReportedBandwidth = -1;
|
|
private int mNumAssociatedStations = 0;
|
private boolean mTimeoutEnabled = false;
|
|
private final SarManager mSarManager;
|
|
private long mStartTimestamp = -1;
|
|
/**
|
* Listener for soft AP events.
|
*/
|
private final SoftApListener mSoftApListener = new SoftApListener() {
|
|
@Override
|
public void onFailure() {
|
mStateMachine.sendMessage(SoftApStateMachine.CMD_FAILURE);
|
}
|
|
@Override
|
public void onNumAssociatedStationsChanged(int numStations) {
|
mStateMachine.sendMessage(
|
SoftApStateMachine.CMD_NUM_ASSOCIATED_STATIONS_CHANGED, numStations);
|
}
|
|
@Override
|
public void onSoftApChannelSwitched(int frequency, int bandwidth) {
|
mStateMachine.sendMessage(
|
SoftApStateMachine.CMD_SOFT_AP_CHANNEL_SWITCHED, frequency, bandwidth);
|
}
|
};
|
|
public SoftApManager(@NonNull 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) {
|
mContext = context;
|
mFrameworkFacade = framework;
|
mWifiNative = wifiNative;
|
mCountryCode = countryCode;
|
mCallback = callback;
|
mWifiApConfigStore = wifiApConfigStore;
|
mMode = apConfig.getTargetMode();
|
WifiConfiguration config = apConfig.getWifiConfiguration();
|
if (config == null) {
|
mApConfig = mWifiApConfigStore.getApConfiguration();
|
} else {
|
mApConfig = config;
|
}
|
mWifiMetrics = wifiMetrics;
|
mSarManager = sarManager;
|
mStateMachine = new SoftApStateMachine(looper);
|
}
|
|
/**
|
* Start soft AP with the supplied config.
|
*/
|
public void start() {
|
mStateMachine.sendMessage(SoftApStateMachine.CMD_START, mApConfig);
|
}
|
|
/**
|
* Stop soft AP.
|
*/
|
public void stop() {
|
Log.d(TAG, " currentstate: " + getCurrentStateName());
|
if (mApInterfaceName != null) {
|
if (mIfaceIsUp) {
|
updateApState(WifiManager.WIFI_AP_STATE_DISABLING,
|
WifiManager.WIFI_AP_STATE_ENABLED, 0);
|
} else {
|
updateApState(WifiManager.WIFI_AP_STATE_DISABLING,
|
WifiManager.WIFI_AP_STATE_ENABLING, 0);
|
}
|
}
|
mStateMachine.quitNow();
|
}
|
|
public @ScanMode int getScanMode() {
|
return SCAN_NONE;
|
}
|
|
public int getIpMode() {
|
return mMode;
|
}
|
|
/**
|
* Dump info about this softap manager.
|
*/
|
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
|
pw.println("--Dump of SoftApManager--");
|
|
pw.println("current StateMachine mode: " + getCurrentStateName());
|
pw.println("mApInterfaceName: " + mApInterfaceName);
|
pw.println("mIfaceIsUp: " + mIfaceIsUp);
|
pw.println("mMode: " + mMode);
|
pw.println("mCountryCode: " + mCountryCode);
|
if (mApConfig != null) {
|
pw.println("mApConfig.SSID: " + mApConfig.SSID);
|
pw.println("mApConfig.apBand: " + mApConfig.apBand);
|
pw.println("mApConfig.hiddenSSID: " + mApConfig.hiddenSSID);
|
} else {
|
pw.println("mApConfig: null");
|
}
|
pw.println("mNumAssociatedStations: " + mNumAssociatedStations);
|
pw.println("mTimeoutEnabled: " + mTimeoutEnabled);
|
pw.println("mReportedFrequency: " + mReportedFrequency);
|
pw.println("mReportedBandwidth: " + mReportedBandwidth);
|
pw.println("mStartTimestamp: " + mStartTimestamp);
|
}
|
|
private String getCurrentStateName() {
|
IState currentState = mStateMachine.getCurrentState();
|
|
if (currentState != null) {
|
return currentState.getName();
|
}
|
|
return "StateMachine not active";
|
}
|
|
/**
|
* Update AP state.
|
* @param newState new AP state
|
* @param currentState current AP state
|
* @param reason Failure reason if the new AP state is in failure state
|
*/
|
private void updateApState(int newState, int currentState, int reason) {
|
mCallback.onStateChanged(newState, reason);
|
|
//send the AP state change broadcast
|
final Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
|
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
|
intent.putExtra(WifiManager.EXTRA_WIFI_AP_STATE, newState);
|
intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_AP_STATE, currentState);
|
if (newState == WifiManager.WIFI_AP_STATE_FAILED) {
|
//only set reason number when softAP start failed
|
intent.putExtra(WifiManager.EXTRA_WIFI_AP_FAILURE_REASON, reason);
|
}
|
|
intent.putExtra(WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME, mApInterfaceName);
|
intent.putExtra(WifiManager.EXTRA_WIFI_AP_MODE, mMode);
|
mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
|
}
|
|
/**
|
* Start a soft AP instance with the given configuration.
|
* @param config AP configuration
|
* @return integer result code
|
*/
|
private int startSoftAp(WifiConfiguration config) {
|
if (config == null || config.SSID == null) {
|
Log.e(TAG, "Unable to start soft AP without valid configuration");
|
return ERROR_GENERIC;
|
}
|
// Setup country code
|
if (TextUtils.isEmpty(mCountryCode)) {
|
if (config.apBand == WifiConfiguration.AP_BAND_5GHZ) {
|
// Country code is mandatory for 5GHz band.
|
Log.e(TAG, "Invalid country code, required for setting up "
|
+ "soft ap in 5GHz");
|
return ERROR_GENERIC;
|
}
|
// Absence of country code is not fatal for 2Ghz & Any band options.
|
} else if (!mWifiNative.setCountryCodeHal(
|
mApInterfaceName, mCountryCode.toUpperCase(Locale.ROOT))) {
|
if (config.apBand == WifiConfiguration.AP_BAND_5GHZ) {
|
// Return an error if failed to set country code when AP is configured for
|
// 5GHz band.
|
Log.e(TAG, "Failed to set country code, required for setting up "
|
+ "soft ap in 5GHz");
|
return ERROR_GENERIC;
|
}
|
// Failure to set country code is not fatal for 2Ghz & Any band options.
|
}
|
|
// Make a copy of configuration for updating AP band and channel.
|
WifiConfiguration localConfig = new WifiConfiguration(config);
|
|
int result = ApConfigUtil.updateApChannelConfig(
|
mWifiNative, mCountryCode,
|
mWifiApConfigStore.getAllowed2GChannel(), localConfig);
|
|
if (result != SUCCESS) {
|
Log.e(TAG, "Failed to update AP band and channel");
|
return result;
|
}
|
|
if (localConfig.hiddenSSID) {
|
Log.d(TAG, "SoftAP is a hidden network");
|
}
|
if (!mWifiNative.startSoftAp(mApInterfaceName, localConfig, mSoftApListener)) {
|
Log.e(TAG, "Soft AP start failed");
|
return ERROR_GENERIC;
|
}
|
mStartTimestamp = SystemClock.elapsedRealtime();
|
Log.d(TAG, "Soft AP is started");
|
|
return SUCCESS;
|
}
|
|
/**
|
* Teardown soft AP and teardown the interface.
|
*/
|
private void stopSoftAp() {
|
mWifiNative.teardownInterface(mApInterfaceName);
|
Log.d(TAG, "Soft AP is stopped");
|
}
|
|
private class SoftApStateMachine extends StateMachine {
|
// Commands for the state machine.
|
public static final int CMD_START = 0;
|
public static final int CMD_FAILURE = 2;
|
public static final int CMD_INTERFACE_STATUS_CHANGED = 3;
|
public static final int CMD_NUM_ASSOCIATED_STATIONS_CHANGED = 4;
|
public static final int CMD_NO_ASSOCIATED_STATIONS_TIMEOUT = 5;
|
public static final int CMD_TIMEOUT_TOGGLE_CHANGED = 6;
|
public static final int CMD_INTERFACE_DESTROYED = 7;
|
public static final int CMD_INTERFACE_DOWN = 8;
|
public static final int CMD_SOFT_AP_CHANNEL_SWITCHED = 9;
|
|
private final State mIdleState = new IdleState();
|
private final State mStartedState = new StartedState();
|
|
private final InterfaceCallback mWifiNativeInterfaceCallback = new InterfaceCallback() {
|
@Override
|
public void onDestroyed(String ifaceName) {
|
if (mApInterfaceName != null && mApInterfaceName.equals(ifaceName)) {
|
sendMessage(CMD_INTERFACE_DESTROYED);
|
}
|
}
|
|
@Override
|
public void onUp(String ifaceName) {
|
if (mApInterfaceName != null && mApInterfaceName.equals(ifaceName)) {
|
sendMessage(CMD_INTERFACE_STATUS_CHANGED, 1);
|
}
|
}
|
|
@Override
|
public void onDown(String ifaceName) {
|
if (mApInterfaceName != null && mApInterfaceName.equals(ifaceName)) {
|
sendMessage(CMD_INTERFACE_STATUS_CHANGED, 0);
|
}
|
}
|
};
|
|
SoftApStateMachine(Looper looper) {
|
super(TAG, looper);
|
|
addState(mIdleState);
|
addState(mStartedState);
|
|
setInitialState(mIdleState);
|
start();
|
}
|
|
private class IdleState extends State {
|
@Override
|
public void enter() {
|
mApInterfaceName = null;
|
mIfaceIsUp = false;
|
mIfaceIsDestroyed = false;
|
}
|
|
@Override
|
public boolean processMessage(Message message) {
|
switch (message.what) {
|
case CMD_START:
|
mApInterfaceName = mWifiNative.setupInterfaceForSoftApMode(
|
mWifiNativeInterfaceCallback);
|
if (TextUtils.isEmpty(mApInterfaceName)) {
|
Log.e(TAG, "setup failure when creating ap interface.");
|
updateApState(WifiManager.WIFI_AP_STATE_FAILED,
|
WifiManager.WIFI_AP_STATE_DISABLED,
|
WifiManager.SAP_START_FAILURE_GENERAL);
|
mWifiMetrics.incrementSoftApStartResult(
|
false, WifiManager.SAP_START_FAILURE_GENERAL);
|
break;
|
}
|
updateApState(WifiManager.WIFI_AP_STATE_ENABLING,
|
WifiManager.WIFI_AP_STATE_DISABLED, 0);
|
int result = startSoftAp((WifiConfiguration) message.obj);
|
if (result != SUCCESS) {
|
int failureReason = WifiManager.SAP_START_FAILURE_GENERAL;
|
if (result == ERROR_NO_CHANNEL) {
|
failureReason = WifiManager.SAP_START_FAILURE_NO_CHANNEL;
|
}
|
updateApState(WifiManager.WIFI_AP_STATE_FAILED,
|
WifiManager.WIFI_AP_STATE_ENABLING,
|
failureReason);
|
stopSoftAp();
|
mWifiMetrics.incrementSoftApStartResult(false, failureReason);
|
break;
|
}
|
transitionTo(mStartedState);
|
break;
|
default:
|
// Ignore all other commands.
|
break;
|
}
|
|
return HANDLED;
|
}
|
}
|
|
private class StartedState extends State {
|
private int mTimeoutDelay;
|
private WakeupMessage mSoftApTimeoutMessage;
|
private SoftApTimeoutEnabledSettingObserver mSettingObserver;
|
|
/**
|
* Observer for timeout settings changes.
|
*/
|
private class SoftApTimeoutEnabledSettingObserver extends ContentObserver {
|
SoftApTimeoutEnabledSettingObserver(Handler handler) {
|
super(handler);
|
}
|
|
public void register() {
|
mFrameworkFacade.registerContentObserver(mContext,
|
Settings.Global.getUriFor(Settings.Global.SOFT_AP_TIMEOUT_ENABLED),
|
true, this);
|
mTimeoutEnabled = getValue();
|
}
|
|
public void unregister() {
|
mFrameworkFacade.unregisterContentObserver(mContext, this);
|
}
|
|
@Override
|
public void onChange(boolean selfChange) {
|
super.onChange(selfChange);
|
mStateMachine.sendMessage(SoftApStateMachine.CMD_TIMEOUT_TOGGLE_CHANGED,
|
getValue() ? 1 : 0);
|
}
|
|
private boolean getValue() {
|
boolean enabled = mFrameworkFacade.getIntegerSetting(mContext,
|
Settings.Global.SOFT_AP_TIMEOUT_ENABLED, 1) == 1;
|
return enabled;
|
}
|
}
|
|
private int getConfigSoftApTimeoutDelay() {
|
int delay = mContext.getResources().getInteger(
|
R.integer.config_wifi_framework_soft_ap_timeout_delay);
|
if (delay < MIN_SOFT_AP_TIMEOUT_DELAY_MS) {
|
delay = MIN_SOFT_AP_TIMEOUT_DELAY_MS;
|
Log.w(TAG, "Overriding timeout delay with minimum limit value");
|
}
|
Log.d(TAG, "Timeout delay: " + delay);
|
return delay;
|
}
|
|
private void scheduleTimeoutMessage() {
|
if (!mTimeoutEnabled) {
|
return;
|
}
|
mSoftApTimeoutMessage.schedule(SystemClock.elapsedRealtime() + mTimeoutDelay);
|
Log.d(TAG, "Timeout message scheduled");
|
}
|
|
private void cancelTimeoutMessage() {
|
mSoftApTimeoutMessage.cancel();
|
Log.d(TAG, "Timeout message canceled");
|
}
|
|
/**
|
* Set number of stations associated with this soft AP
|
* @param numStations Number of connected stations
|
*/
|
private void setNumAssociatedStations(int numStations) {
|
if (mNumAssociatedStations == numStations) {
|
return;
|
}
|
mNumAssociatedStations = numStations;
|
Log.d(TAG, "Number of associated stations changed: " + mNumAssociatedStations);
|
|
if (mCallback != null) {
|
mCallback.onNumClientsChanged(mNumAssociatedStations);
|
} else {
|
Log.e(TAG, "SoftApCallback is null. Dropping NumClientsChanged event.");
|
}
|
mWifiMetrics.addSoftApNumAssociatedStationsChangedEvent(mNumAssociatedStations,
|
mMode);
|
|
if (mNumAssociatedStations == 0) {
|
scheduleTimeoutMessage();
|
} else {
|
cancelTimeoutMessage();
|
}
|
}
|
|
private void onUpChanged(boolean isUp) {
|
if (isUp == mIfaceIsUp) {
|
return; // no change
|
}
|
mIfaceIsUp = isUp;
|
if (isUp) {
|
Log.d(TAG, "SoftAp is ready for use");
|
updateApState(WifiManager.WIFI_AP_STATE_ENABLED,
|
WifiManager.WIFI_AP_STATE_ENABLING, 0);
|
mWifiMetrics.incrementSoftApStartResult(true, 0);
|
if (mCallback != null) {
|
mCallback.onNumClientsChanged(mNumAssociatedStations);
|
}
|
} else {
|
// the interface was up, but goes down
|
sendMessage(CMD_INTERFACE_DOWN);
|
}
|
mWifiMetrics.addSoftApUpChangedEvent(isUp, mMode);
|
}
|
|
@Override
|
public void enter() {
|
mIfaceIsUp = false;
|
mIfaceIsDestroyed = false;
|
onUpChanged(mWifiNative.isInterfaceUp(mApInterfaceName));
|
|
mTimeoutDelay = getConfigSoftApTimeoutDelay();
|
Handler handler = mStateMachine.getHandler();
|
mSoftApTimeoutMessage = new WakeupMessage(mContext, handler,
|
SOFT_AP_SEND_MESSAGE_TIMEOUT_TAG,
|
SoftApStateMachine.CMD_NO_ASSOCIATED_STATIONS_TIMEOUT);
|
mSettingObserver = new SoftApTimeoutEnabledSettingObserver(handler);
|
|
if (mSettingObserver != null) {
|
mSettingObserver.register();
|
}
|
|
mSarManager.setSapWifiState(WifiManager.WIFI_AP_STATE_ENABLED);
|
|
Log.d(TAG, "Resetting num stations on start");
|
mNumAssociatedStations = 0;
|
scheduleTimeoutMessage();
|
}
|
|
@Override
|
public void exit() {
|
if (!mIfaceIsDestroyed) {
|
stopSoftAp();
|
}
|
|
if (mSettingObserver != null) {
|
mSettingObserver.unregister();
|
}
|
Log.d(TAG, "Resetting num stations on stop");
|
setNumAssociatedStations(0);
|
cancelTimeoutMessage();
|
// Need this here since we are exiting |Started| state and won't handle any
|
// future CMD_INTERFACE_STATUS_CHANGED events after this point
|
mWifiMetrics.addSoftApUpChangedEvent(false, mMode);
|
updateApState(WifiManager.WIFI_AP_STATE_DISABLED,
|
WifiManager.WIFI_AP_STATE_DISABLING, 0);
|
|
mSarManager.setSapWifiState(WifiManager.WIFI_AP_STATE_DISABLED);
|
mApInterfaceName = null;
|
mIfaceIsUp = false;
|
mIfaceIsDestroyed = false;
|
mStateMachine.quitNow();
|
}
|
|
private void updateUserBandPreferenceViolationMetricsIfNeeded() {
|
boolean bandPreferenceViolated = false;
|
if (mApConfig.apBand == WifiConfiguration.AP_BAND_2GHZ
|
&& ScanResult.is5GHz(mReportedFrequency)) {
|
bandPreferenceViolated = true;
|
} else if (mApConfig.apBand == WifiConfiguration.AP_BAND_5GHZ
|
&& ScanResult.is24GHz(mReportedFrequency)) {
|
bandPreferenceViolated = true;
|
}
|
if (bandPreferenceViolated) {
|
Log.e(TAG, "Channel does not satisfy user band preference: "
|
+ mReportedFrequency);
|
mWifiMetrics.incrementNumSoftApUserBandPreferenceUnsatisfied();
|
}
|
}
|
|
@Override
|
public boolean processMessage(Message message) {
|
switch (message.what) {
|
case CMD_NUM_ASSOCIATED_STATIONS_CHANGED:
|
if (message.arg1 < 0) {
|
Log.e(TAG, "Invalid number of associated stations: " + message.arg1);
|
break;
|
}
|
Log.d(TAG, "Setting num stations on CMD_NUM_ASSOCIATED_STATIONS_CHANGED");
|
setNumAssociatedStations(message.arg1);
|
break;
|
case CMD_SOFT_AP_CHANNEL_SWITCHED:
|
mReportedFrequency = message.arg1;
|
mReportedBandwidth = message.arg2;
|
Log.d(TAG, "Channel switched. Frequency: " + mReportedFrequency
|
+ " Bandwidth: " + mReportedBandwidth);
|
mWifiMetrics.addSoftApChannelSwitchedEvent(mReportedFrequency,
|
mReportedBandwidth, mMode);
|
updateUserBandPreferenceViolationMetricsIfNeeded();
|
break;
|
case CMD_TIMEOUT_TOGGLE_CHANGED:
|
boolean isEnabled = (message.arg1 == 1);
|
if (mTimeoutEnabled == isEnabled) {
|
break;
|
}
|
mTimeoutEnabled = isEnabled;
|
if (!mTimeoutEnabled) {
|
cancelTimeoutMessage();
|
}
|
if (mTimeoutEnabled && mNumAssociatedStations == 0) {
|
scheduleTimeoutMessage();
|
}
|
break;
|
case CMD_INTERFACE_STATUS_CHANGED:
|
boolean isUp = message.arg1 == 1;
|
onUpChanged(isUp);
|
break;
|
case CMD_START:
|
// Already started, ignore this command.
|
break;
|
case CMD_NO_ASSOCIATED_STATIONS_TIMEOUT:
|
if (!mTimeoutEnabled) {
|
Log.wtf(TAG, "Timeout message received while timeout is disabled."
|
+ " Dropping.");
|
break;
|
}
|
if (mNumAssociatedStations != 0) {
|
Log.wtf(TAG, "Timeout message received but has clients. Dropping.");
|
break;
|
}
|
Log.i(TAG, "Timeout message received. Stopping soft AP.");
|
updateApState(WifiManager.WIFI_AP_STATE_DISABLING,
|
WifiManager.WIFI_AP_STATE_ENABLED, 0);
|
transitionTo(mIdleState);
|
break;
|
case CMD_INTERFACE_DESTROYED:
|
Log.d(TAG, "Interface was cleanly destroyed.");
|
updateApState(WifiManager.WIFI_AP_STATE_DISABLING,
|
WifiManager.WIFI_AP_STATE_ENABLED, 0);
|
mIfaceIsDestroyed = true;
|
transitionTo(mIdleState);
|
break;
|
case CMD_FAILURE:
|
Log.w(TAG, "hostapd failure, stop and report failure");
|
/* fall through */
|
case CMD_INTERFACE_DOWN:
|
Log.w(TAG, "interface error, stop and report failure");
|
updateApState(WifiManager.WIFI_AP_STATE_FAILED,
|
WifiManager.WIFI_AP_STATE_ENABLED,
|
WifiManager.SAP_START_FAILURE_GENERAL);
|
updateApState(WifiManager.WIFI_AP_STATE_DISABLING,
|
WifiManager.WIFI_AP_STATE_FAILED, 0);
|
transitionTo(mIdleState);
|
break;
|
default:
|
return NOT_HANDLED;
|
}
|
return HANDLED;
|
}
|
}
|
}
|
}
|