您的位置:首页 > 移动开发 > Android开发

Android SoftAp SoftAp打开/关闭代码流程(基于android 7.0)

2016-12-15 16:04 1731 查看
在Android手机中,SoftAp也是一个较为常用的功能。对于framework开发者来说,要开发、维护SoftAp,了解framework中热点开关的具体流程是非常有必要的。下面就对这部分内容做一些介绍,以供后续查阅。

一、SoftAp打开流程

当我们在设置中打开热点时,会调用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");
}
}


参数中的wifiConfig对象保存了在Settings中操作时保留的热点信息,如热点名称、密钥和加密方式等等。与Wifi本身的打开和关闭类似,Wifi热点的打开流程也是通过WifiController状态机向WifiStateMachine转发消息的。与前面介绍的Wifi打开流程类似,CMD_SET_AP消息在ApStaDisabledState状态处理.

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();
}


writeApConfiguration()将mWifiApConfig的信息更新到/misc/wifi/softap.conf文件中

/**
* 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;
}
}


首先会判断打开热点时传入的WifiConfiguration对象是否为null;如果为空,则会向WifiApConfigStore获取默认的Config或者已配置的config,如果上层配置了热点;我们也会将新的配置信息更新到softap.conf中,以供下次再次加载。

2. 第二个分支

我们一起介绍这两个分支过程。对于CMD_START_AP消息的处理再看第二个分支,InitialState状态的enter()

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);
}
}
......
}


主要是从/misc/wifi/softap.conf文件中读取其中的信息,并赋给WifiApConfigStore的成员变量mWifiApConfig,这个变量保存的就是当前SoftAp的配置信息。该文件一开始会有默认的信息保存其中,如果我们从没配置过热点,拿到的就是系统默认的信息;然后会重新去处理CMD_START_AP消息.就走到了第一个分支的路径.

经过这么一番折腾,目的其实就是为了获取一个合法的WifiApConfig,获取到之后终于就是启动SoftAp了.接下来进入mSoftApManager.start(config);处理.

/**
* 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<Integer> 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;
}


经过信道和国家吗信息的更新终于要进入Netd启动SoftAp了.在NetworkManagementService.java文件中的startAccessPoint中进行处理.代码如下

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();
}
}


从这个方法中可以看出最终就是执行executeOrLogWithMessage方法.

/**
* 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);
}
}


此之后就是在Netd进行SotAp的开启操作."startap"命令会在CommandListener.cpp文件中的SoftapCmd::runCommand函数进行处理.并且调用startSoftap()函数,进行继续开启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(); //开启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;
}


在SoftapController.cpp文件中的startSoftap()函数中会启动Hostapd进程.根据/data/misc/wifi/hostapd.conf文件 /data/misc/wifi/entropy.bin文件启动/system/bin/hostapd进程.至此Softap开启完成.

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的方法调用与打开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();
}
}


由第一部分的内容可知WifiController状态机在处理完SoftAp打开后,停在WifiController.java文件中的ApEnabledState状态,那么我们看它是怎么处理CMD_SET_AP消息

case CMD_SET_AP:
if (msg.arg1 == 0) {
mWifiStateMachine.setHostApRunning(null, false); //在WifiStateMachine中开始热点关闭流程
mPendingState = getNextWifiState(); //切换到初始状态
}
break;


有前述可知,如果参数enabled为false,mag.arg1就应该为0,调用setHostApRunning()走关闭流程,并将WifiController中的状态重置为ApStaDisabledState,等待下一次流程的开始。看setHostApRunning()

/**
* 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);
}


发送CMD_STOP消息;已知Softap成功开启之后,SoftApManager状态机停留在StartedState状态.看其处理

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;


从上代码可以看出真正关闭SoftAp的代码为stopSoftAp().看其实现如下:

/**
* 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");
}


代码也由此进入了Netd进行处理.接下来看一下stopAccessPoint()函数.NetworkManagementService文件中的处理如下

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();
}
}


从上面的代码可以看出,SoftAp关闭过程中要执行的命令为"stopap".然后通过Socket发送出去.下面看一下通过Socket发送"stopap"命令的实现.

/**
* 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);
}
}


由此通过execute方法将命令"stopap"通过Socket发送出去.在CommandListener.cpp文件中监听"stopap"命令.看其实现

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;
}


当CommandListener.cpp文件中监听到opap"命令时,会调用SoftapController.cpp文件中的stopSoftap()函数进行实现.

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()执行成功
}


此函数中通过kill命令杀死Hostapd进程.至此SoftAp关闭成功.奇怪的是在Android 7.0上没有卸载SoftAp的驱动吗?
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: