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

在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 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的驱动吗?

你可能感兴趣的:(Android,SoftAP,(Tethering))