Android SoftAp支持 (一)
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支持 (一)相关推荐
- android平板ifwi,Android SoftAp支持 (二)
SoftAp界面开启流程(让你的手机支持SoftAp功能) 市面上大多数手机支持SoftAp功能,有少数手机没有打开SoftAp的界面设置,所以无法开启此功能(当然有些山寨手机平板打开了此设置功能也不 ...
- Android SoftAP 实现框架
Android SoftAP 实现框架 1. SoftAP 简介 2. 功能模块与框架图 框架图 3. 功能简单分析 3.1 设备启动与管理 3.2 网络共享功能的实现 4. SoftAP 运行时序图 ...
- Windows Azure Mobile Services增加了对 Android的支持并扩展其适用范围至东亚地区
我们的Mobile Services使开发人员很容易地开发丰富多彩的移动应用程序.使用Mobile Services ,开发人员不仅能够连接其应用程序到 Windows Azure 上易扩展又安全的后 ...
- android alpha不起作用,API 28(P)的Android设计支持库不起作用
我已经成功配置了android-P SDK环境.当我尝试使用android设计支持库时,我遇到项目构建错误.项目配置为: IDE:3.2 Canary 17目标API:28编译API:28 apply ...
- Android WebView 支持H5图片上传input type=file
2019独角兽企业重金招聘Python工程师标准>>> Android WebView 缓存处理 Android WebView 支持H5图片上传<input type=&qu ...
- Android recovery支持adb shell
Android recovery支持adb shell 最近开发过程注意到recovery不支持adb shell,为了便于调试方便,决定增加此功能. 刚开始我们采用的是user版本系统,进入reco ...
- Android 测试支持库 1.0 现已发布!
我们非常高兴地宣布,Android 测试支持库 (ATSL) 1.0 版现已发布. ATSL 1.0 版对现有测试 API 进行了重要更新,不仅添加了许多新功能.还提升了性能和稳定性,同时还修复了若干 ...
- SAP BTP SDK for Android 已经支持 Kotlin 了
2011年7月19日,Kotlin公开亮相,目前已经成为公认的Android开发推荐使用的语言,也早已被SAP BTP SDK for Android所支持.推出Kotlin的JetBrains公司, ...
- Android ContentProvider支持跨进程数据共享与互斥、同步 杂谈
在开发中,假如,A.B进程有部分信息需要同步,这个时候怎么处理呢?设想这么一个场景,有个业务复杂的Activity非常占用内存,并引发OOM,所以,想要把这个Activity放到单独进程,以保证OOM ...
最新文章
- R语言使用ggpubr包的ggdotplot函数可视化水平棒棒糖图(自定义分组数据点色彩、自定义调色板、在两端添加点图的线段segments、整体排序从大到小、自定义数据点的大小、添加数值标签)
- 在VMWare上安装linux
- 自定义监听器 java
- PuTTY的下载、使用和设置,并推荐同类佳软——MobaXterm
- 洛谷P2679 子串
- ESPCMS基本导航操作
- sql2008 cet查询 所有层级_案例 | CET助力一汽实现动力设备系统智慧管理
- 使用Raphael实现html中绘图
- DeviceNet 消息类型
- 龙芯3A5000完成流片 同主频性能追平AMD Zen1
- 定时调度系列之Quartz.Net详解
- .9-浅析webpack源码之NodeEnvironmentPlugin模块总览
- 编写安全的代码-程序员头顶的达摩克利斯之剑
- [2018.10.18 T3] 小 G 的线段树
- 浏览器密码查看工具-WebBrowserPassView使用实验 ——合天网安实验室学习笔记
- 我国最早着手建设专用计算机广域网的是,计算机网络信息技术在畜牧养殖方面应用...
- 抖音iOS基础技术大揭秘!
- 二柱子卖的西瓜上315啦!一个故事读懂315危机公关
- memcached 源码分析
- C语言-------如何打印保留小数点后1,2,..位