SoftAP 打开时调用的相关函数解析

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 firstbreak;case USB_TETHERING:setUsbTethering(true);break;default://should not happenbreak;}}

这里 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 {@Overridepublic void enter() {new Thread(new Runnable() {public void run() {mWakeLock.acquire();//enabling stateswitch(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();}@Overridepublic 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 {@Overridepublic void enter() {if (DBG) log(getName() + "\n");EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());}@Overridepublic 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 {@Overridepublic 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);}}@Overridepublic 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 threadnew 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 的一些东东

Android SoftAp支持 (一)相关推荐

  1. android平板ifwi,Android SoftAp支持 (二)

    SoftAp界面开启流程(让你的手机支持SoftAp功能) 市面上大多数手机支持SoftAp功能,有少数手机没有打开SoftAp的界面设置,所以无法开启此功能(当然有些山寨手机平板打开了此设置功能也不 ...

  2. Android SoftAP 实现框架

    Android SoftAP 实现框架 1. SoftAP 简介 2. 功能模块与框架图 框架图 3. 功能简单分析 3.1 设备启动与管理 3.2 网络共享功能的实现 4. SoftAP 运行时序图 ...

  3. Windows Azure Mobile Services增加了对 Android的支持并扩展其适用范围至东亚地区

    我们的Mobile Services使开发人员很容易地开发丰富多彩的移动应用程序.使用Mobile Services ,开发人员不仅能够连接其应用程序到 Windows Azure 上易扩展又安全的后 ...

  4. android alpha不起作用,API 28(P)的Android设计支持库不起作用

    我已经成功配置了android-P SDK环境.当我尝试使用android设计支持库时,我遇到项目构建错误.项目配置为: IDE:3.2 Canary 17目标API:28编译API:28 apply ...

  5. Android WebView 支持H5图片上传input type=file

    2019独角兽企业重金招聘Python工程师标准>>> Android WebView 缓存处理 Android WebView 支持H5图片上传<input type=&qu ...

  6. Android recovery支持adb shell

    Android recovery支持adb shell 最近开发过程注意到recovery不支持adb shell,为了便于调试方便,决定增加此功能. 刚开始我们采用的是user版本系统,进入reco ...

  7. Android 测试支持库 1.0 现已发布!

    我们非常高兴地宣布,Android 测试支持库 (ATSL) 1.0 版现已发布. ATSL 1.0 版对现有测试 API 进行了重要更新,不仅添加了许多新功能.还提升了性能和稳定性,同时还修复了若干 ...

  8. SAP BTP SDK for Android 已经支持 Kotlin 了

    2011年7月19日,Kotlin公开亮相,目前已经成为公认的Android开发推荐使用的语言,也早已被SAP BTP SDK for Android所支持.推出Kotlin的JetBrains公司, ...

  9. Android ContentProvider支持跨进程数据共享与互斥、同步 杂谈

    在开发中,假如,A.B进程有部分信息需要同步,这个时候怎么处理呢?设想这么一个场景,有个业务复杂的Activity非常占用内存,并引发OOM,所以,想要把这个Activity放到单独进程,以保证OOM ...

最新文章

  1. R语言使用ggpubr包的ggdotplot函数可视化水平棒棒糖图(自定义分组数据点色彩、自定义调色板、在两端添加点图的线段segments、整体排序从大到小、自定义数据点的大小、添加数值标签)
  2. 在VMWare上安装linux
  3. 自定义监听器 java
  4. PuTTY的下载、使用和设置,并推荐同类佳软——MobaXterm
  5. 洛谷P2679 子串
  6. ESPCMS基本导航操作
  7. sql2008 cet查询 所有层级_案例 | CET助力一汽实现动力设备系统智慧管理
  8. 使用Raphael实现html中绘图
  9. DeviceNet 消息类型
  10. 龙芯3A5000完成流片 同主频性能追平AMD Zen1
  11. 定时调度系列之Quartz.Net详解
  12. .9-浅析webpack源码之NodeEnvironmentPlugin模块总览
  13. 编写安全的代码-程序员头顶的达摩克利斯之剑
  14. [2018.10.18 T3] 小 G 的线段树
  15. 浏览器密码查看工具-WebBrowserPassView使用实验 ——合天网安实验室学习笔记
  16. 我国最早着手建设专用计算机广域网的是,计算机网络信息技术在畜牧养殖方面应用...
  17. 抖音iOS基础技术大揭秘!
  18. 二柱子卖的西瓜上315啦!一个故事读懂315危机公关
  19. memcached 源码分析
  20. C语言-------如何打印保留小数点后1,2,..位

热门文章

  1. vmware证书劫持网页解决方案
  2. 网页***深度剖析以及手工清除的方法
  3. 怎么取消苹果手机自动续费_手机QQ会员每月自动续费,怎么关闭
  4. iphone计算机如何打字速度,关于提升iPhone手机打字速度的6个小技巧
  5. Photoshop 之 改变画笔圆圈大小的快捷键
  6. 【from zero to zero】noip2017
  7. word文档如何合并单元格?
  8. MMORPG设计方案(第一版)
  9. YGG 公会发展计划(GAP)第二季总结
  10. python将pdf转成excel_如何将pdf版的表格高效的转换成excel形式?