2019独角兽企业重金招聘Python工程师标准>>>
Softap字面意思是用软件实现AP的功能,让你的移动设备可以作为一个路由,让别的站点链接。比如让别人的手机连上你的已经打开AP功能的手机,玩联机游戏或者上网等等
但事实上此功能是需要硬件以及驱动的支持才能真正的实现的。
Softap打开流程。
在Android系统的Setting界面的wireless配置项中会看到一个“Portable Wi-Fi hotspot” 跟一个"Configure Wi-Fi hotspot setting"选项,可以进入系统配置AP的名称,加密方式,密码等。 如下图
当你做完这些设置,系统接受的AP设置界面变化打开的响应,从此开启了整个Android SoftAP的序幕。
首先./packages/apps/Settings/src/com/android/settings/TetherSettings.java 的onPreferenceChange 函数接收到Softap状态改变信息
- public boolean onPreferenceChange(Preference preference, Object value) {
- boolean enable = (Boolean) value;
- if (enable) {
- startProvisioningIfNecessary(WIFI_TETHERING);
- } else {
- mWifiApEnabler.setSoftapEnabled(false);
- }
- return false;
- }
Softap开启时,enable 为真,因而执行startProvisioningIfNecessary(WIFI_TETHERING);
- private void startProvisioningIfNecessary(int choice) {
- mTetherChoice = choice;
- if (isProvisioningNeeded()) {
- Intent intent = new Intent(Intent.ACTION_MAIN);
- intent.setClassName(mProvisionApp[0], mProvisionApp[1]);
- startActivityForResult(intent, PROVISION_REQUEST);
- } else {
- startTethering();
- }
- }
isProvisioningNeeded 用来检测是否需要进行一些准备工作
如果无需准备工作则执行startTethering 大戏即将上演了 期待ing
- private void startTethering() {
- switch (mTetherChoice) {
- case WIFI_TETHERING:
- mWifiApEnabler.setSoftapEnabled(true);
- break;
- case BLUETOOTH_TETHERING:
- // turn on Bluetooth first
- break;
- case USB_TETHERING:
- setUsbTethering(true);
- break;
- default:
- //should not happen
- break;
- }
- }
这里 mTetherChoice == WIFI_TETHERING 所以继而执行WiFiApEnable.java中的setSoftapEnabled(true)函数
也从此处也跳出了Setting的代码 跳入了Android WIFI 子系统的framework层
./packages/apps/Settings/src/com/android/settings/wifi/WifiApEnabler.java
- public void setSoftapEnabled(boolean enable) {
- final ContentResolver cr = mContext.getContentResolver();
- /**
- * Disable Wifi if enabling tethering
- */
- int wifiState = mWifiManager.getWifiState(); //获取当前wifi的状态 如果开启则关闭且保存状态信息到变量中
- if (enable && ((wifiState == WifiManager.WIFI_STATE_ENABLING) ||
- (wifiState == WifiManager.WIFI_STATE_ENABLED))) {
- mWifiManager.setWifiEnabled(false);
- Settings.Global.putInt(cr, Settings.Global.WIFI_SAVED_STATE, 1);
- }
- if (mWifiManager.setWifiApEnabled(null, enable)) {
- /* Disable here, enabled on receiving success broadcast */
- mCheckBox.setEnabled(false);
- } else {
- mCheckBox.setSummary(R.string.wifi_error);
- }
- /**
- * If needed, restore Wifi on tether disable
- */
- if (!enable) {
- int wifiSavedState = 0;
- try {
- wifiSavedState = Settings.Global.getInt(cr, Settings.Global.WIFI_SAVED_STATE);
- } catch (Settings.SettingNotFoundException e) {
- ;
- }
- if (wifiSavedState == 1) {
- mWifiManager.setWifiEnabled(true);
- Settings.Global.putInt(cr, Settings.Global.WIFI_SAVED_STATE, 0);
- }
- }
- }
上面的代码中我们看到了Google人的考虑事情的周全。首先检测Wifi当前状态如果正在打开或者已经打开则关闭WIFI并将此状态记录下来,以便关闭softap时它能自动恢复到之前打开wifi的状态。 Android代码不愧牛X,这些都能想到... 崇拜那些大牛。
这里调用mWifiManager.setWifiApEnabled(null, enable) "frameworks/base/wifi/java/android/net/wifi/WifiManager.java"
- public boolean setWifiApEnabled(WifiConfiguration wifiConfig, boolean enabled) {
- try {
- mService.setWifiApEnabled(wifiConfig, enabled);
- return true;
- } catch (RemoteException e) {
- return false;
- }
- }
转向服务层的 setWifiApEnabled "frameworks/base/services/java/com/android/server/WifiService.java"
- public void setWifiApEnabled(WifiConfiguration wifiConfig, boolean enabled) {
- enforceChangePermission();
- mWifiStateMachine.setWifiApEnabled(wifiConfig, enabled);
- }
从而调用到最基础的也是最重要的Wifi状态机中的 setWifiApEnabled 实例 其实我真搞不懂为什么Android代码要嵌套这么多层去调用,为了安全、方便... 哪个牛人解释一下。
"frameworks/base/wifi/java/android/net/wifi/WifiStateMachine.java"
- public void setWifiApEnabled(WifiConfiguration wifiConfig, boolean enable) {
- mLastApEnableUid.set(Binder.getCallingUid());
- if (enable) {
- /* Argument is the state that is entered prior to load */
- sendMessage(obtainMessage(CMD_LOAD_DRIVER, WIFI_AP_STATE_ENABLING, 0));
- sendMessage(obtainMessage(CMD_START_AP, wifiConfig));
- } else {
- sendMessage(CMD_STOP_AP);
- /* Argument is the state that is entered upon success */
- sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_AP_STATE_DISABLED, 0));
- }
- }
发送CMD_LOAD_DRIVER状态迁移到mDriverLoadingState 加载AP对应的驱动 这里把WIFI的驱动跟 AP的驱动做了区分,可见SoftAP不仅仅是软件实现的,需要硬件驱动的相应支持。
- class DriverLoadingState extends State {
- @Override
- public void enter() {
- new Thread(new Runnable() {
- public void run() {
- mWakeLock.acquire();
- //enabling state
- switch(message.arg1) {
- case WIFI_STATE_ENABLING:
- setWifiState(WIFI_STATE_ENABLING);
- break;
- case WIFI_AP_STATE_ENABLING:
- setWifiApState(WIFI_AP_STATE_ENABLING);
- break;
- }
- if(mWifiNative.loadDriver()) {
- if (DBG) log("Driver load successful");
- sendMessage(CMD_LOAD_DRIVER_SUCCESS);
- } else {
- loge("Failed to load driver!");
- switch(message.arg1) {
- case WIFI_STATE_ENABLING:
- setWifiState(WIFI_STATE_UNKNOWN);
- break;
- case WIFI_AP_STATE_ENABLING:
- setWifiApState(WIFI_AP_STATE_FAILED);
- break;
- }
- sendMessage(CMD_LOAD_DRIVER_FAILURE);
- }
- mWakeLock.release();
- }
- }).start();
- }
- @Override
- public boolean processMessage(Message message) {
- if (DBG) log(getName() + message.toString() + "\n");
- switch (message.what) {
- case CMD_LOAD_DRIVER_SUCCESS:
- transitionTo(mDriverLoadedState);
- break;
- case CMD_LOAD_DRIVER_FAILURE:
- transitionTo(mDriverFailedState);
- break;
- default:
- return NOT_HANDLED;
- }
- return HANDLED;
- }
- }
加载驱动成功后 系统迁移到mDriverLoadedState 状态
接收到 CMD_START_AP消息 状态又被迁移至mSoftApStartingState
- class DriverLoadedState extends State {
- @Override
- public void enter() {
- if (DBG) log(getName() + "\n");
- EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
- }
- @Override
- public boolean processMessage(Message message) {
- if (DBG) log(getName() + message.toString() + "\n");
- switch(message.what) {
- /*
- ******
- */
- case CMD_START_AP:
- transitionTo(mSoftApStartingState);
- break;
- default:
- return NOT_HANDLED;
- }
- return HANDLED;
- }
- }
SoftApStartingState 会检测上层传下的参数的有效性并调用startSoftApWithConfig 配置、打开SoftAP
- class SoftApStartingState extends State {
- @Override
- public void enter() {
- if (DBG) log(getName() + "\n");
- EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
- final Message message = getCurrentMessage();
- if (message.what == CMD_START_AP) {
- final WifiConfiguration config = (WifiConfiguration) message.obj;
- if (config == null) {
- mWifiApConfigChannel.sendMessage(CMD_REQUEST_AP_CONFIG);
- } else {
- mWifiApConfigChannel.sendMessage(CMD_SET_AP_CONFIG, config);
- startSoftApWithConfig(config);
- }
- } else {
- throw new RuntimeException("Illegal transition to SoftApStartingState: " + message);
- }
- }
- @Override
- public boolean processMessage(Message message) {
- if (DBG) log(getName() + message.toString() + "\n");
- switch(message.what) {
- case CMD_LOAD_DRIVER:
- case CMD_UNLOAD_DRIVER:
- //....
- case CMD_STOP_SUPPLICANT:
- case CMD_START_AP:
- //....
- }
- }
- }
获取SoftAp的网络配置AP名称 加密方式密码....
进行系统驱动(硬件)的配置。
- private void startSoftApWithConfig(final WifiConfiguration config) {
- // start hostapd on a seperate thread
- new Thread(new Runnable() {
- public void run() {
- try {
- mNwService.startAccessPoint(config, mInterfaceName);
- } catch (Exception e) {
- loge("Exception in softap start " + e);
- try {
- mNwService.stopAccessPoint(mInterfaceName);
- mNwService.startAccessPoint(config, mInterfaceName);
- } catch (Exception e1) {
- loge("Exception in softap re-start " + e1);
- sendMessage(CMD_START_AP_FAILURE);
- return;
- }
- }
- if (DBG) log("Soft AP start successful");
- sendMessage(CMD_START_AP_SUCCESS);
- }
- }).start();
- }
- //...
这里调用到了"frameworks/base/services/java/com/android/server/NetworkManagementService.java" 中的startAccessPoint函数
函数如下:
- public void startAccessPoint(
- WifiConfiguration wifiConfig, String wlanIface) {
- mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
- try {
- wifiFirmwareReload(wlanIface, "AP");
- if (wifiConfig == null) {
- mConnector.execute("softap", "set", wlanIface);
- } else {
- mConnector.execute("softap", "set", wlanIface, wifiConfig.SSID,
- getSecurityType(wifiConfig), wifiConfig.preSharedKey);
- }
- mConnector.execute("softap", "startap");
- } catch (NativeDaemonConnectorException e) {
- throw e.rethrowAsParcelableException();
- }
- }
1、下载AP对应的 firmware
wifiFirmwareReload(wlanIface, "AP");
2、设置ap的ssid 加密方式 以及密码
mConnector.execute("softap", "set", wlanIface, wifiConfig.SSID, getSecurityType(wifiConfig), wifiConfig.preSharedKey);
3、运行softap
mConnector.execute("softap", "startap");
这里通过一个NativeDaemonConnector的实例mConnector 调用c++程序 具体的实现我是没看懂 但是知道最后实际调用的函数, 想深入了解可以找一些其他的资料看
实际调用到了 "./system/netd/CommandListener.cpp" 中的CommandListener::SoftapCmd::runCommand
- int CommandListener::SoftapCmd::runCommand(SocketClient *cli,
- int argc, char **argv) {
- int rc = 0, flag = 0;
- char *retbuf = NULL;
- if (argc < 2) {
- cli->sendMsg(ResponseCode::CommandSyntaxError, "Softap Missing argument", false);
- return 0;
- }
- if (!strcmp(argv[1], "startap")) {
- rc = sSoftapCtrl->startSoftap();
- } else if (!strcmp(argv[1], "stopap")) {
- rc = sSoftapCtrl->stopSoftap();
- } else if (!strcmp(argv[1], "fwreload")) {
- rc = sSoftapCtrl->fwReloadSoftap(argc, argv);
- } else if (!strcmp(argv[1], "clients")) {
- rc = sSoftapCtrl->clientsSoftap(&retbuf);
- if (!rc) {
- cli->sendMsg(ResponseCode::CommandOkay, retbuf, false);
- free(retbuf);
- return 0;
- }
- } else if (!strcmp(argv[1], "status")) {
- asprintf(&retbuf, "Softap service %s",
- (sSoftapCtrl->isSoftapStarted() ? "started" : "stopped"));
- cli->sendMsg(ResponseCode::SoftapStatusResult, retbuf, false);
- free(retbuf);
- return 0;
- } else if (!strcmp(argv[1], "set")) {
- rc = sSoftapCtrl->setSoftap(argc, argv);
- } else {
- cli->sendMsg(ResponseCode::CommandSyntaxError, "Softap Unknown cmd", false);
- return 0;
- }
- if (!rc) {
- cli->sendMsg(ResponseCode::CommandOkay, "Softap operation succeeded", false);
- } else {
- cli->sendMsg(ResponseCode::OperationFailed, "Softap operation failed", true);
- }
- return 0;
- }
首先是"set“ 命令, 调用到c = sSoftapCtrl->setSoftap(argc, argv); 来配置网络
配置即将所有上层的网络设置写到HOSTAPD_CONF_FILE[] = "/data/misc/wifi/hostapd.conf" 中
("system/netd/SoftapController.cpp")
- /*
- * Arguments:
- * argv[2] - wlan interface
- * argv[3] - SSID
- * argv[4] - Security
- * argv[5] - Key
- * argv[6] - Channel
- * argv[7] - Preamble
- * argv[8] - Max SCB
- */
- int SoftapController::setSoftap(int argc, char *argv[]) {
- char psk_str[2*SHA256_DIGEST_LENGTH+1];
- int ret = 0, i = 0, fd;
- char *ssid, *iface;
- /* ..... */
- iface = argv[2];
- char *wbuf = NULL;
- char *fbuf = NULL;
- if (argc > 3) {
- ssid = argv[3];
- } else {
- ssid = (char *)"AndroidAP";
- }
- if (argc > 4) {
- if (!strcmp(argv[4], "wpa-psk")) {
- generatePsk(ssid, argv[5], psk_str);
- asprintf(&fbuf, "%swpa=1\nwpa_pairwise=TKIP CCMP\nwpa_psk=%s\n", wbuf, psk_str);
- } else if (!strcmp(argv[4], "wpa2-psk")) {
- generatePsk(ssid, argv[5], psk_str);
- asprintf(&fbuf, "%swpa=2\nrsn_pairwise=CCMP\nwpa_psk=%s\n", wbuf, psk_str);
- } else if (!strcmp(argv[4], "open")) {
- asprintf(&fbuf, "%s", wbuf);
- }
- } else {
- asprintf(&fbuf, "%s", wbuf);
- }
- fd = open(HOSTAPD_CONF_FILE, O_CREAT | O_TRUNC | O_WRONLY | O_NOFOLLOW, 0660);
- /*............*/
- if (write(fd, fbuf, strlen(fbuf)) < 0) {
- ALOGE("Cannot write to \"%s\": %s", HOSTAPD_CONF_FILE, strerror(errno));
- ret = -1;
- }
- free(wbuf);
- free(fbuf);
- /* Note: apparently open can fail to set permissions correctly at times */
- // .......
- }
然后是"startap"命令调用rc = sSoftapCtrl->startSoftap(); 真正开启Softap
- int SoftapController::startSoftap() {
- pid_t pid = 1;
- int ret = 0;
- if (mPid) {
- ALOGE("Softap already started");
- return 0;
- }
- if (mSock < 0) {
- ALOGE("Softap startap - failed to open socket");
- return -1;
- }
- if ((pid = fork()) < 0) {
- ALOGE("fork failed (%s)", strerror(errno));
- return -1;
- }
- if (!pid) {
- ensure_entropy_file_exists();
- if (execl("/system/bin/hostapd", "/system/bin/hostapd",
- "-e", WIFI_ENTROPY_FILE,
- HOSTAPD_CONF_FILE, (char *) NULL)) {
- ALOGE("execl failed (%s)", strerror(errno));
- }
- ALOGE("Should never get here!");
- return -1;
- } else {
- mPid = pid;
- ALOGD("Softap startap - Ok");
- usleep(AP_BSS_START_DELAY);
- }
- return ret;
- }
在startSoftap函数中调用了
execl("/system/bin/hostapd", "/system/bin/hostapd", "-e", WIFI_ENTROPY_FILE, HOSTAPD_CONF_FILE, (char *) NULL)
这里hostapd就是softap的deamon 程序 类似于wifi的的wpa_supplicant
至此所有wifi子系统从界面打开softap 到如何运行调用到deamon程序打开Softap的流程就是这样的
之后会介绍到Setting 界面"Portable Wi-Fi"的开启 以及 Hostapd 的一些东东