当我们在设置中打开热点时,会调用WifiManager::setWifiApEnabled(),参数enabled为true;间接调用同名的WifiServiceImpl::setWifiApEnabled()
/**
* Start AccessPoint mode with the specified
* configuration. If the radio is already running in
* AP mode, update the new configuration
* Note that starting in access point mode disables station
* mode operation
* @param wifiConfig SSID, security and channel details as
* part of WifiConfiguration
* @return {@code true} if the operation succeeds, {@code false} otherwise
*
* @hide
*/
@SystemApi
public boolean setWifiApEnabled(WifiConfiguration wifiConfig, boolean enabled) {
try {
mService.setWifiApEnabled(wifiConfig, enabled);
return true;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* see {@link android.net.wifi.WifiManager#setWifiApEnabled(WifiConfiguration, boolean)}
* @param wifiConfig SSID, security and channel details as
* part of WifiConfiguration
* @param enabled true to enable and false to disable
*/
public void setWifiApEnabled(WifiConfiguration wifiConfig, boolean enabled) {
enforceChangePermission();
ConnectivityManager.enforceTetherChangePermission(mContext);
if (mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING)) {
throw new SecurityException("DISALLOW_CONFIG_TETHERING is enabled for this user.");
}
// null wifiConfig is a meaningful input for CMD_SET_AP
if (wifiConfig == null || isValid(wifiConfig)) {
mWifiController.obtainMessage(CMD_SET_AP, enabled ? 1 : 0, 0, wifiConfig).sendToTarget();
} else {
Slog.e(TAG, "Invalid WifiConfiguration");
}
}
case CMD_SET_AP:
if (msg.arg1 == 1) {
if (msg.arg2 == 0) { // previous wifi state has not been saved yet
mSettingsStore.setWifiSavedState(WifiSettingsStore.WIFI_DISABLED);
}
mWifiStateMachine.setHostApRunning((WifiConfiguration) msg.obj, true);
transitionTo(mApEnabledState);
}
break;
由此转入WifiStateMachine进行打开流程
/**
* TODO: doc
*/
public void setHostApRunning(WifiConfiguration wifiConfig, boolean enable) {
if (enable) {
sendMessage(CMD_START_AP, wifiConfig);
} else {
sendMessage(CMD_STOP_AP);
}
}
WifiStateMachine::InitialState会处理该消息
case CMD_START_AP:
if (setupDriverForSoftAp()) { //1. 第一个分支
transitionTo(mSoftApState);
} else {
setWifiApState(WIFI_AP_STATE_FAILED,
WifiManager.SAP_START_FAILURE_GENERAL);
/**
* Transition to InitialState (current state) to reset the
* driver/HAL back to the initial state.
*/
transitionTo(mInitialState); //2. 第二个分支
}
break;
1. 第一个分支
/* Driver/firmware setup for soft AP. */
private boolean setupDriverForSoftAp() {
if (!mWifiNative.loadDriver()) { //加载驱动
Log.e(TAG, "Failed to load driver for softap");
return false;
}
int index = mWifiNative.queryInterfaceIndex(mInterfaceName); //绑定接口
if (index != -1) {
if (!mWifiNative.setInterfaceUp(false)) { //启动接口
Log.e(TAG, "toggleInterface failed");
return false;
} else {
if (DBG) Log.d(TAG, "No interfaces to bring down");
}
try {
mNwService.wifiFirmwareReload(mInterfaceName, "AP"); //加载Firmware
if (DBG) Log.d(TAG, "Firmware reloaded in AP mode");
} catch (Exception e) {
Log.e(TAG, "Failed to reload AP firmware " + e);
}
if (!mWifiNative.startHal()) { //启动HAL
/* starting HAL is optional */
Log.e(TAG, "Failed to start HAL");
}
return true;
}
首先肯定是先加载Driver,Driver加载成功Wifi热点需要绑定端口信息,再以AP模式通过NetworkManagementService在wlan0端口下加载固件;同时热点功能也需要HAL层的支持。setWifiApState()会发送广播,告知当前热点打开的过程信息;同理,也有setWifiState(),告知外界当前Wifi打开的过程信息;如果我们有必要知道当前热点打开的过程进行到什么阶段了,可以监听WifiManager.WIFI_AP_STATE_CHANGED_ACTION广播。最后状态切换到SoftApState,如果流程有误,则会重新进入InitialState。接着看SoftApState::enter()
public void enter() {
final Message message = getCurrentMessage();
if (message.what == CMD_START_AP) {
WifiConfiguration config = (WifiConfiguration) message.obj;
if (config == null) {
/**
* Configuration not provided in the command, fallback to use the current
* configuration.
*/
config = mWifiApConfigStore.getApConfiguration(); //获取一个Config,不一定是默认的,有可能是之前保存下去的.
} else {
/* Update AP configuration. */
mWifiApConfigStore.setApConfiguration(config); //将Config文件备份到之前的Config中
}
checkAndSetConnectivityInstance(); //这方法就是获取一个Connectivity服务
mSoftApManager = mFacade.makeSoftApManager(
mContext, getHandler().getLooper(), mWifiNative, mNwService,
mCm, mCountryCode.getCurrentCountryCode(),
mWifiApConfigStore.getAllowed2GChannel(),
new SoftApListener());
mSoftApManager.start(config); //根据配置文件启动SoftAp
} else {
throw new RuntimeException("Illegal transition to SoftApState: " + message);
}
}
/**
* Update the current soft access point configuration.
* Restore to default AP configuration if null is provided.
* This can be invoked under context of binder threads (WifiManager.setWifiApConfiguration)
* and WifiStateMachine thread (CMD_START_AP).
*/
public synchronized void setApConfiguration(WifiConfiguration config) {
if (config == null) {
mWifiApConfig = getDefaultApConfiguration(); //获取默认的ApConfig
} else {
mWifiApConfig = config;
}
//将得到的ApConfig(默认或者已配置的config)备份到另外一个配置文件中.
//这样做是确保下次开启时能够获取到上次保存的配置.如果有新的Config将会更新备份中的config.
writeApConfiguration(mApConfigFile, mWifiApConfig); // Stage the backup of the SettingsProvider package which backs this up mBackupManagerProxy.notifyDataChanged(); }/**
* Write AP configuration to persistent storage.
*/
private static void writeApConfiguration(final String filename,
final WifiConfiguration config) {
try {DataOutputStream out = new DataOutputStream(new BufferedOutputStream(
new FileOutputStream(filename)))) {
out.writeInt(AP_CONFIG_FILE_VERSION);
out.writeUTF(config.SSID);
out.writeInt(config.apBand);
out.writeInt(config.apChannel);
//NOTE: Bug #474462 Add For SoftAp advance Feature BEG-->
out.writeInt(config.softApMaxNumSta);
//<-- Add for SoftAp Advance Feature END
int authType = config.getAuthType();
out.writeInt(authType);
if (authType != KeyMgmt.NONE) {
out.writeUTF(config.preSharedKey);
}
} catch (IOException e) {
Log.e(TAG, "Error writing hotspot configuration" + e);
}
}
/**
* Generate a default WPA2 based configuration with a random password.
* We are changing the Wifi Ap configuration storage from secure settings to a
* flat file accessible only by the system. A WPA2 based default configuration
* will keep the device secure after the update.
*/
private WifiConfiguration getDefaultApConfiguration() {
WifiConfiguration config = new WifiConfiguration();
config.SSID = mContext.getResources().getString(
R.string.wifi_tether_configure_ssid_default);
config.allowedKeyManagement.set(KeyMgmt.WPA2_PSK);
String randomUUID = UUID.randomUUID().toString();
//first 12 chars from xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx
config.preSharedKey = randomUUID.substring(0, 8) + randomUUID.substring(9, 13);
config.softApMaxNumSta = 8;
config.apBand = 0;
config.apChannel = 11;
return config;
}
}
class InitialState extends State {
@Override
public void enter() {
mWifiNative.stopHal();
mWifiNative.unloadDriver();
if (mWifiP2pChannel == null) {
mWifiP2pChannel = new AsyncChannel();
mWifiP2pChannel.connect(mContext, getHandler(),mWifiP2pServiceImpl.getP2pStateMachineMessenger());
}
if (mWifiApConfigStore == null) {
//WifiApConfigStore也是一个小的状态机,此时会构建mWifiApConfigStore对戏,并启动状态机
mWifiApConfigStore = mFacade.makeApConfigStore(mContext, mBackupManagerProxy);
}
}
......
}
/**
* Start soft AP with given configuration.
* @param config AP configuration
*/
public void start(WifiConfiguration config) {
mStateMachine.sendMessage(SoftApStateMachine.CMD_START, config);
}
在SoftApManager.java文件中的IdleState的状态中处理CMD_START消息
case CMD_START:
updateApState(WifiManager.WIFI_AP_STATE_ENABLING, 0);
int result = startSoftAp((WifiConfiguration) message.obj); //启动SoftAp
if (result == SUCCESS) {
updateApState(WifiManager.WIFI_AP_STATE_ENABLED, 0); //启动成功之后更新Ap的状态
transitionTo(mStartedState);
} else {
int reason = WifiManager.SAP_START_FAILURE_GENERAL;
if (result == ERROR_NO_CHANNEL) {
reason = WifiManager.SAP_START_FAILURE_NO_CHANNEL;
}
updateApState(WifiManager.WIFI_AP_STATE_FAILED, reason); //如果启动失败将会更新失败的消息,并且附带原因
}
break;
启动SoftAp继续看startSoftAp方法
/**
* 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) {
Log.e(TAG, "Unable to start soft AP without configuration");
return ERROR_GENERIC;
}
/* Make a copy of configuration for updating AP band and channel. */
WifiConfiguration localConfig = new WifiConfiguration(config);
int result = ApConfigUtil.updateApChannelConfig(
mWifiNative, mCountryCode, mAllowed2GChannels, localConfig); //根据配置文件更新国家码和信道信息
if (result != SUCCESS) {
Log.e(TAG, "Failed to update AP band and channel");
return result;
}
/* Setup country code if it is provide. */
if (mCountryCode != null) {
/**
* Country code is mandatory for 5GHz band, return an error if failed to set
* country code when AP is configured for 5GHz band.
*/
if (!mWifiNative.setCountryCodeHal(mCountryCode.toUpperCase(Locale.ROOT)) //设置国家码
&& config.apBand == WifiConfiguration.AP_BAND_5GHZ) {
Log.e(TAG, "Failed to set country code, required for setting up "
+ "soft ap in 5GHz");
return ERROR_GENERIC;
}
}
try {
mNmService.startAccessPoint(localConfig, mInterfaceName); //通过NetworkManagerService,在无线端口上,按传入的配置信息开启SoftAP;
} catch (Exception e) {
Log.e(TAG, "Exception in starting soft AP: " + e);
return ERROR_GENERIC;
}
Log.d(TAG, "Soft AP is started");
return SUCCESS;
}
/**
* Update AP band and channel based on the provided country code and band.
* This will also set
* @param wifiNative reference to WifiNative
* @param countryCode country code
* @param allowed2GChannels list of allowed 2GHz channels
* @param config configuration to update
* @return an integer result code
*/
public static int updateApChannelConfig(WifiNative wifiNative,
String countryCode,
ArrayList allowed2GChannels,
WifiConfiguration config) {
/* Use default band and channel for device without HAL. */
if (!wifiNative.isHalStarted()) { //因为SoftAp需要HAL层的支持,所有首先要进行确定,再继续配置
config.apBand = DEFAULT_AP_BAND;
//config.apChannel = DEFAULT_AP_CHANNEL;
if (config.apChannel <= 0 || config.apChannel > 14)
config.apChannel = DEFAULT_AP_CHANNEL;
return SUCCESS;
}
/* Country code is mandatory for 5GHz band. */
if (config.apBand == WifiConfiguration.AP_BAND_5GHZ
&& countryCode == null) {
Log.e(TAG, "5GHz band is not allowed without country code");
return ERROR_GENERIC;
}
/* Select a channel if it is not specified. */
if (config.apChannel == 0) {
config.apChannel = chooseApChannel(
config.apBand, allowed2GChannels,
wifiNative.getChannelsForBand(WifiScanner.WIFI_BAND_5_GHZ));
if (config.apChannel == -1) {
if (wifiNative.isGetChannelsForBandSupported()) {
/* We're not able to get channel when it is supported by HAL. */
Log.e(TAG, "Failed to get available channel.");
return ERROR_NO_CHANNEL;
}
/* Use the default for HAL without get channel support. */
config.apBand = DEFAULT_AP_BAND;
config.apChannel = DEFAULT_AP_CHANNEL;
}
}
return SUCCESS;
}
public void startAccessPoint(WifiConfiguration wifiConfig, String wlanIface) {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
Object[] args;
String logMsg = "startAccessPoint Error setting up softap";
try {
if (wifiConfig == null) {
args = new Object[] {"set", wlanIface};
} else {
args = new Object[] {"set", wlanIface, wifiConfig.SSID,
//"broadcast", Integer.toString(wifiConfig.apChannel),
(wifiConfig.hiddenSSID?"hidden":"broadcast"), Integer.toString(wifiConfig.apChannel),
//getSecurityType(wifiConfig), new SensitiveArg(wifiConfig.preSharedKey)};
getSecurityType(wifiConfig), new SensitiveArg(wifiConfig.preSharedKey), wifiConfig.softApMaxNumSta};
}
executeOrLogWithMessage(SOFT_AP_COMMAND, args, NetdResponseCode.SoftapStatusResult,
SOFT_AP_COMMAND_SUCCESS, logMsg);
logMsg = "startAccessPoint Error starting softap";
args = new Object[] {"startap"};
executeOrLogWithMessage(SOFT_AP_COMMAND, args, NetdResponseCode.SoftapStatusResult,
SOFT_AP_COMMAND_SUCCESS, logMsg);
} catch (NativeDaemonConnectorException e) {
throw e.rethrowAsParcelableException();
}
}
/**
* Private method used to call execute for a command given the provided arguments.
*
* This function checks the returned NativeDaemonEvent for the provided expected response code
* and message. If either of these is not correct, an error is logged.
*
* @param String command The command to execute.
* @param Object[] args If needed, arguments for the command to execute.
* @param int expectedResponseCode The code expected to be returned in the corresponding event.
* @param String expectedResponseMessage The message expected in the returned event.
* @param String logMsg The message to log as an error (TAG will be applied).
*/
private void executeOrLogWithMessage(String command, Object[] args,
int expectedResponseCode, String expectedResponseMessage, String logMsg)
throws NativeDaemonConnectorException {
NativeDaemonEvent event = mConnector.execute(command, args);
if (event.getCode() != expectedResponseCode
|| !event.getMessage().equals(expectedResponseMessage)) {
Log.e(TAG, logMsg + ": event = " + event);
}
}
int CommandListener::SoftapCmd::runCommand(SocketClient *cli,
int argc, char **argv) {
int rc = ResponseCode::SoftapStatusResult;
char *retbuf = NULL;
if (gCtls == nullptr) {
cli->sendMsg(ResponseCode::ServiceStartFailed, "SoftAP is not available", false);
return -1;
}
if (argc < 2) {
cli->sendMsg(ResponseCode::CommandSyntaxError,
"Missing argument in a SoftAP command", false);
return 0;
}
if (!strcmp(argv[1], "startap")) {
rc = gCtls->softapCtrl.startSoftap(); //开启SoftAp
} else if (!strcmp(argv[1], "stopap")) {
rc = gCtls->softapCtrl.stopSoftap();
} else if (!strcmp(argv[1], "fwreload")) {
rc = gCtls->softapCtrl.fwReloadSoftap(argc, argv);
} else if (!strcmp(argv[1], "status")) {
asprintf(&retbuf, "Softap service %s running",
(gCtls->softapCtrl.isSoftapStarted() ? "is" : "is not"));
cli->sendMsg(rc, retbuf, false);
free(retbuf);
return 0;
} else if (!strcmp(argv[1], "set")) {
rc = gCtls->softapCtrl.setSoftap(argc, argv);
#ifdef CONFIG_HOSTAPD_ADVANCE
} else if (!strcmp(argv[1], "whitelist")) {//add for change softap white list enable
if ((argc >= 3) && (!strcmp(argv[2], "true"))) { //enable white list
rc =gCtls->softapCtrl.setSoftapWhiteListEnable(true);
} else {
rc = gCtls->softapCtrl.setSoftapWhiteListEnable(false);
}
#endif
} else {
cli->sendMsg(ResponseCode::CommandSyntaxError, "Unrecognized SoftAP command", false);
return 0;
}
if (rc >= 400 && rc < 600) //此处为返回SoftAp的执行结果.成功此值为214.异常为402
cli->sendMsg(rc, "SoftAP command has failed", false);
else
cli->sendMsg(rc, "Ok", false);
return 0;
}
int SoftapController::startSoftap() {
pid_t pid = 1;
if (mPid) {
ALOGE("SoftAP is already running");
return ResponseCode::SoftapStatusResult;
}
if (ensure_entropy_file_exists() < 0) {
ALOGE("Wi-Fi entropy file was not created");
}
if ((pid = fork()) < 0) {
ALOGE("fork failed (%s)", strerror(errno));
return ResponseCode::ServiceStartFailed;
}
if (!pid) {
ensure_entropy_file_exists();
if (execl(HOSTAPD_BIN_FILE, HOSTAPD_BIN_FILE,
"-e", WIFI_ENTROPY_FILE,
HOSTAPD_CONF_FILE, (char *) NULL)) {
ALOGE("execl failed (%s)", strerror(errno));
}
ALOGE("SoftAP failed to start");
return ResponseCode::ServiceStartFailed;
} else {
mPid = pid;
ALOGD("SoftAP started successfully"); //此处证明SoftAp启动成功
usleep(AP_BSS_START_DELAY);
}
return ResponseCode::SoftapStatusResult;
}
二、SoftAp关闭流程
关闭SoftAp的方法调用与打开SoftAp一致,不过enabled此时是为false.
/**
* Start AccessPoint mode with the specified
* configuration. If the radio is already running in
* AP mode, update the new configuration
* Note that starting in access point mode disables station
* mode operation
* @param wifiConfig SSID, security and channel details as
* part of WifiConfiguration
* @return {@code true} if the operation succeeds, {@code false} otherwise
*
* @hide
*/
@SystemApi
public boolean setWifiApEnabled(WifiConfiguration wifiConfig, boolean enabled) {
try {
mService.setWifiApEnabled(wifiConfig, enabled);
return true;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
case CMD_SET_AP:
if (msg.arg1 == 0) {
mWifiStateMachine.setHostApRunning(null, false); //在WifiStateMachine中开始热点关闭流程
mPendingState = getNextWifiState(); //切换到初始状态
}
break;
/**
* TODO: doc
*/
public void setHostApRunning(WifiConfiguration wifiConfig, boolean enable) {
if (enable) {
sendMessage(CMD_START_AP, wifiConfig);
} else {
sendMessage(CMD_STOP_AP);
}
}
发送CMD_STOP_AP消息;已知SoftAp成功打开后,WifiStateMachine停留在SoftApState,看其处理
case CMD_STOP_AP:
mSoftApManager.stop(); //直接关闭SoftAp
break;
/**
* Stop soft AP.
*/
public void stop() {
mStateMachine.sendMessage(SoftApStateMachine.CMD_STOP);
}
case CMD_STOP:
updateApState(WifiManager.WIFI_AP_STATE_DISABLING, 0); //更新关闭SoftAp的状态为关闭中
stopSoftAp(); //停止SoftAp
updateApState(WifiManager.WIFI_AP_STATE_DISABLED, 0); //更新关闭SoftAp的状态为已关闭
transitionTo(mIdleState);
mConnectedStations = "";
if(mConnectedToHostapd) {
if (DBG) log("Stopping Soft AP Monitor");
mWifiMonitor.stopSoftApMonitoring(mInterfaceName);
}
if (DBG) log("WifiMonitor: Close to hostapd sockets!");
mWifiNative.closeHostapdConnection(); //关闭Hostapd的关联
mConnectedToHostapd = false;
unregisterSoftapEventHandler(); //取消注册SoftAp的事件
/*
quit();
*/
}
break;
/**
* Teardown soft AP.
*/
private void stopSoftAp() {
try {
mNmService.stopAccessPoint(mInterfaceName);
} catch (Exception e) {
Log.e(TAG, "Exception in stopping soft AP: " + e);
return;
}
Log.d(TAG, "Soft AP is stopped");
}
public void stopAccessPoint(String wlanIface) {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
Object[] args = {"stopap"}; //要执行的命令
String logMsg = "stopAccessPoint Error stopping softap";
try {
executeOrLogWithMessage(SOFT_AP_COMMAND, args, NetdResponseCode.SoftapStatusResult,
SOFT_AP_COMMAND_SUCCESS, logMsg);//将命令通过Socket发送出去
wifiFirmwareReload(wlanIface, "STA");
} catch (NativeDaemonConnectorException e) {
throw e.rethrowAsParcelableException();
}
}
/**
* Private method used to call execute for a command given the provided arguments.
*
* This function checks the returned NativeDaemonEvent for the provided expected response code
* and message. If either of these is not correct, an error is logged.
*
* @param String command The command to execute.
* @param Object[] args If needed, arguments for the command to execute.
* @param int expectedResponseCode The code expected to be returned in the corresponding event.
* @param String expectedResponseMessage The message expected in the returned event.
* @param String logMsg The message to log as an error (TAG will be applied).
*/
private void executeOrLogWithMessage(String command, Object[] args,
int expectedResponseCode, String expectedResponseMessage, String logMsg)
throws NativeDaemonConnectorException {
NativeDaemonEvent event = mConnector.execute(command, args);
if (event.getCode() != expectedResponseCode
|| !event.getMessage().equals(expectedResponseMessage)) {
Log.e(TAG, logMsg + ": event = " + event);
}
}
CommandListener::SoftapCmd::SoftapCmd() :
NetdCommand("softap") {
}
int CommandListener::SoftapCmd::runCommand(SocketClient *cli,
int argc, char **argv) {
int rc = ResponseCode::SoftapStatusResult;
char *retbuf = NULL;
if (gCtls == nullptr) {
cli->sendMsg(ResponseCode::ServiceStartFailed, "SoftAP is not available", false);
return -1;
}
if (argc < 2) {
cli->sendMsg(ResponseCode::CommandSyntaxError,
"Missing argument in a SoftAP command", false);
return 0;
}
if (!strcmp(argv[1], "startap")) {
rc = gCtls->softapCtrl.startSoftap();
} else if (!strcmp(argv[1], "stopap")) {
rc = gCtls->softapCtrl.stopSoftap(); //关闭SoftAp
} else if (!strcmp(argv[1], "fwreload")) {
rc = gCtls->softapCtrl.fwReloadSoftap(argc, argv);
} else if (!strcmp(argv[1], "status")) {
asprintf(&retbuf, "Softap service %s running",
(gCtls->softapCtrl.isSoftapStarted() ? "is" : "is not"));
cli->sendMsg(rc, retbuf, false);
free(retbuf);
return 0;
} else if (!strcmp(argv[1], "set")) {
rc = gCtls->softapCtrl.setSoftap(argc, argv);
#ifdef CONFIG_HOSTAPD_ADVANCE
} else if (!strcmp(argv[1], "whitelist")) {//add for change softap white list enable
if ((argc >= 3) && (!strcmp(argv[2], "true"))) { //enable white list
rc =gCtls->softapCtrl.setSoftapWhiteListEnable(true);
} else {
rc = gCtls->softapCtrl.setSoftapWhiteListEnable(false);
}
#endif
} else {
cli->sendMsg(ResponseCode::CommandSyntaxError, "Unrecognized SoftAP command", false);
return 0;
}
if (rc >= 400 && rc < 600)
cli->sendMsg(rc, "SoftAP command has failed", false);
else
cli->sendMsg(rc, "Ok", false);
return 0;
}
int SoftapController::stopSoftap() {
if (mPid == 0) {
ALOGE("SoftAP is not running");
return ResponseCode::SoftapStatusResult;
}
ALOGD("Stopping the SoftAP service...");
kill(mPid, SIGTERM);
waitpid(mPid, NULL, 0);
mPid = 0;
ALOGD("SoftAP stopped successfully");
usleep(AP_BSS_STOP_DELAY);
return ResponseCode::SoftapStatusResult; //此值为214,返回为214时代表stopSoftap()执行成功
}