平时和wifi打交道还算比较多吧,wifi出了问题就只能找大神解决,这是件很令人伤感的事情。所以就想自己分析下android源码中wifi的框架,以后wifi出了问题,自己也能尝试的解一解。分析过程必定会有很多理解不到位或者错误的地方,欢迎大家不吝啬恶毒之词,使劲拍砖。

一.认识wpa_supplicant

wpa_supplicant是Android平台使用的用来管理wifi的应用程序,它可以支持WEP,WPA/WPA2和WAPI无线协议和加密认证。wpa_supplicant会操作驱动程序和wifi网卡交互,它同时是一个服务器,通过socket,我们可以和它建立连接,然后就可以通过命令和它交互,让它帮我们实现wifi连接断开等操作。
为了更好的认识和理解wpa_supplicant的功能,我们可以先尝试使用命令来连接wifi,感受下wpa_supplicant的作用。
在android系统中,当我们打开wifi后,wpa_supplicant就启动了,
我们不需要手动来启动。这点可以通过ps | grep wpa_supplicant来查看,结果如下:
wifi 2955 2947 4932 2596 poll_sched b6e22138 S wpa_supplicant
与wpa_supplicant同时存在的是wpl_cli,它是一个客户端,它实现了和wap_supplicant连接等功能,我们可以通过它和wap_supplicant交互。
要使用wpa_cli,首先得过得root权限,没有root权限的话连接会失败。
使用wap_cli的第一步就是启动wpa_cli:
wpa_cli -i wlan0 -p /data/misc/wifi/sockets/

-i指的是网卡接口
-p值得是wap_supplicant.conf所在的路径,具体的平台会有不同。

启动以后进入交互模式,我们可以使用如下命令完成wifi的连接:

1.scan

扫描附近的热点
/> scan
OK

2.scan_results

获取扫描的结果
/> scan_results
bssid / frequency / signal level / flags / ssid
20:f4:1b:83:40:e4 2462 -63 [WPA-PSK-CCMP+TKIP][WPA2-PSK-CCMP+TKIP][ESS] ROC
80:89:17:a1:a8:ee 2437 -64 [WPA-PSK-CCMP][WPA2-PSK-CCMP][ESS] samuel
80:89:17:a1:a8:e4 2437 -67 [WPA-PSK-CCMP][WPA2-PSK-CCMP][ESS] TV-WIFI
80:89:17:9f:d3:ba 2412 -73 [WPA-PSK-CCMP][WPA2-PSK-CCMP][ESS] xrr
f4:ec:38:7b:2c:96 2437 -75 [WPA-PSK-CCMP][WPA2-PSK-CCMP][WPS][ESS] TP-LINK_7B2C96
1c:fa:68:50:8a:76 2462 -71 [WPA-PSK-CCMP+TKIP][WPA2-PSK-CCMP+TKIP][WPS][ESS] RDSPEED100M
d0:c7:c0:fd:86:64 2437 -73 [WPA-PSK-CCMP][WPA2-PSK-CCMP][ESS] yingyunguanli-huiyishi
4e:e0:10:c2:44:57 2462 -59 [WPA2-PSK-CCMP][ESS] hellowifi
4e:e0:10:c2:48:5d 2462 -70 [WPA2-PSK-CCMP][ESS] ADESKTOP-93DNJ6F
ac:e0:10:c2:63:bc 2412 -66 [WPA-PSK-CCMP+TKIP][WPA2-PSK-CCMP+TKIP][WPS][ESS] wifi
cc:34:29:2c:7f:e8 2412 -76 [WPA-PSK-TKIP][WPA2-PSK-TKIP][ESS] dlink

3.add_network

添加网络
通过查看扫描的结果,我们选择一个要连接的wifi,比如hellowifi
这个时候,我们首先要添加一个网络。
/> add_network
2
这个2是wpa_supplicant给我们返回的一个值,代表了这个网络,以后我们需要操作这个网络就要使用这个值。

4.设置ssid和密码

ssid就是热点的名字,这里就是hellowifi.
/> set_network 2 ssid “hellowifi”
OK
/> set_network 2 psk “11223344”
OK
psk是WPA2-PSK模式下标示wifi密码的名字。如果是没有加密:
/>set_network 2 key_mgmt NONE
如果是WEP安全模式:
/>set_network 2 wep_key0 “your ap passwork”

5.enable_network

使能wifi

/> select_network 2
OK
选择网络是可选的,如果当前已经连接了一个wifi,你应该重新选择一个网络。
/> enable_network 2
OK

6.切换wifi

如果我们当前有多个网络,我们可以切换:
/> select_network 0
OK
/> enable_network 0
OK
网络0中的wifi将会被连接

二.wifi整体框架

尝试了使用命令行连接和断开wifi后,我们可以开始思考android源码中是怎么和wpa_supplicant交互的?Android系统中wifi框架是怎样的?
下面我根据自己的理解绘制的一张简单的示意图:

从中可以看出,wifi.c是和wpa_supplicant直接交互的文件。client通过套接字和wap_supplicant交互,向wpa_supplicant发送命令并接受wpa_supplicant反馈的信息。wifi.c是C代码,必须通过jni和JVM交互。wifi的整体框架非常好理解。

wifi.c

wifi.c在\hardware\libhardware_legacy\wifi目录下。
在wifi.c中,声明了如下和wpa_supplicant交互的函数:

void wpa_ctrl_cleanup(void) {}
struct wpa_ctrl *wpa_ctrl_open(const char *ctrl_path) { return NULL; }
void wpa_ctrl_close(struct wpa_ctrl *ctrl) {}
int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len,char *reply, size_t *reply_len, void (*msg_cb)(char *msg, size_t len)){ return 0; }
int wpa_ctrl_attach(struct wpa_ctrl *ctrl) { return 0; }
int wpa_ctrl_detach(struct wpa_ctrl *ctrl) { return 0; }
int wpa_ctrl_recv(struct wpa_ctrl *ctrl, char *reply, size_t *reply_len){ return 0; }
int wpa_ctrl_get_fd(struct wpa_ctrl *ctrl) { return 0; }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

这几个函数的作用如下:
wpa_ctrl_open接口用来打开wpa_supplicant的控制接口,在UNIX系统里使用UNIX domain sockets,而在Windows里则是使用UDP sockets,当然接口的路径并不是固定的,可以根据配置文件内的路径设置来改变。

wpa_ctrl_close接口自然是用于关闭控制接口。

wpa_ctrl_request接口是用来发送控制命令至wpa_supplicant,并且会接受命令成功执行与否的反馈消息。这是一个堵塞的动作,一般会至少等待2秒钟用来接受反馈的回复消息。如果有未经主动请求的消息接受,堵塞的时间则会更长。

wpa_ctrl_attach接口是为控制接口注册一个事件监视,但注册成功后就可以开始接口事件消息。

wpa_ctrl_detach接口则是取消控制接口的事件监视。

wpa_ctrl_recv接口是在控制接口的事件监视注册成功后,用来接受事件消息,这是一个堵塞的操作,当没有可用的消息时,就会一直堵塞。

wpa_ctrl_pending接口是用来检测是否有即将到来的事件消息。

wpa_ctrl_get_fd接口则是来获得控制接口的文件描述符号。

因此,通过如上函数,我们就可以和wpa_supplicant进行交互,从而实现wifi的管理了。
wifi.c使用如上函数,对wifi的操作做了进一步的封装,比如,我们要扫描附近的热点,需要发送scan命令,而发送命令的函数在wifi.c中为:

int wifi_send_command(const char *cmd, char *reply, size_t *reply_len)
{int ret;if (ctrl_conn == NULL) {ALOGV("Not connected to wpa_supplicant - \"%s\" command dropped.\n", cmd);return -1;}ret = wpa_ctrl_request(ctrl_conn, cmd, strlen(cmd), reply, reply_len, NULL);if (ret == -2) {ALOGD("'%s' command timed out.\n", cmd);/* unblocks the monitor receive socket for termination */TEMP_FAILURE_RETRY(write(exit_sockets[0], "T", 1));return -2;} else if (ret < 0 || strncmp(reply, "FAIL", 4) == 0) {return -1;}if (strncmp(cmd, "PING", 4) == 0) {reply[*reply_len] = '\0';}return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

可以看到它内部就是使用wpa_ctrl_request实现的。

com_android_server_wifi_WifiNative.cpp

wifi.c是native code,它不能被java虚拟机直接使用,必须通过jni来与java虚拟机交互,jni自然就是在com_android_server_wifi_WifiNative.cpp文件中实现的了,该文件在frameworks\opt\net\wifi\service\jni目录下。
这个文件这里也不展开,我们知道jni动态注册的时候需要一个函数映射表,从表中我们可以看到java层使用的方法和本地的函数之间的对应关系:

/** JNI registration.*/
static JNINativeMethod gWifiMethods[] = {/* name, signature, funcPtr */{ "loadDriver", "()Z",  (void *)android_net_wifi_loadDriver },{ "isDriverLoaded", "()Z",  (void *)android_net_wifi_isDriverLoaded },{ "unloadDriver", "()Z",  (void *)android_net_wifi_unloadDriver },{ "startSupplicant", "(Z)Z",  (void *)android_net_wifi_startSupplicant },{ "killSupplicant", "(Z)Z",  (void *)android_net_wifi_killSupplicant },{ "connectToSupplicantNative", "()Z", (void *)android_net_wifi_connectToSupplicant },{ "closeSupplicantConnectionNative", "()V",(void *)android_net_wifi_closeSupplicantConnection },{ "waitForEventNative", "()Ljava/lang/String;", 。。。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

比如说,java中想要启动wpa_supplicant的时候,会调用startSupplicant方法,对应的jni方法就是android_net_wifi_startSupplicant ,然后android_net_wifi_startSupplicant 会被调用,这个方法定义如下:

static jboolean android_net_wifi_startSupplicant(JNIEnv* env, jobject, jboolean p2pSupported)
{return (::wifi_start_supplicant(p2pSupported) == 0);
}
  • 1
  • 2
  • 3
  • 4

结果是wifi.c中的wifi_start_supplicant被调用,定义如下:

int wifi_start_supplicant(int p2p_supported)
{// MStar Android Patch BeginALOGD("enter func %s p2p_supported = %d\n",__func__,p2p_supported);char wlan_driver[PROPERTY_VALUE_MAX] = {'\0'};property_get(WLAN_DRIVER,wlan_driver, NULL);char daemon_cmd[PROPERTY_VALUE_MAX * 2];char supp_status[PROPERTY_VALUE_MAX] = {'\0'};int count = 200; /* wait at most 20 seconds for completion */const prop_info *pi;unsigned serial = 0, i;。。。/* Clear out any stale socket files that might be left over. */wpa_ctrl_cleanup();/* Reset sockets used for exiting from hung state */exit_sockets[0] = exit_sockets[1] = -1;/** Get a reference to the status property, so we can distinguish* the case where it goes stopped => running => stopped (i.e.,* it start up, but fails right away) from the case in which* it starts in the stopped state and never manages to start* running at all.*/pi = __system_property_find(supplicant_prop_name);if (pi != NULL) {serial = __system_property_serial(pi);}property_get("wifi.interface", primary_iface, WIFI_TEST_INTERFACE);// MStar Android Patch Beginsnprintf(daemon_cmd,sizeof(daemon_cmd),"%s:%s",supplicant_name,wlan_driver);property_set("ctl.start", daemon_cmd);// MStar Android Patch Endsched_yield();while (count-- > 0) {if (pi == NULL) {pi = __system_property_find(supplicant_prop_name);}if (pi != NULL) {/** property serial updated means that init process is scheduled* after we sched_yield, further property status checking is based on this */if (__system_property_serial(pi) != serial) {__system_property_read(pi, NULL, supp_status);if (strcmp(supp_status, "running") == 0) {return 0;} else if (strcmp(supp_status, "stopped") == 0) {return -1;}}}// MStar Android Patch BeginALOGD("func %s count = %d ",__func__,count);// MStar Android Patch Endusleep(100000);}return -1;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63

这里启动wpa_supplicant使用的是property_set(“ctl.start”, daemon_cmd);设置ctl.start属性会导致这个属性所对应的值的同名服务被启动,这项服务启动的结果会被放在init.svc.<服务名>“属性中,后面的while循环就是不断查询给向服务的启动结果,来判断它是否成功启动。

java层

java层直接和jni交互的类是WifiNative.java,从名字也可以看出来,它里面声明了很多native方法:

    private static native int registerNatives();public native static boolean loadDriver();public native static boolean isDriverLoaded();public native static boolean unloadDriver();public native static boolean startSupplicant(boolean p2pSupported);/* Sends a kill signal to supplicant. To be used when we have lost connectionor when the supplicant is hung */public native static boolean killSupplicant(boolean p2pSupported);
。。。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

java层就比较复杂了,暂时点到为止。下一节将着手分析java层对wifi的管理。

[转载]Android wifi探究一:初步认识wpa_supplicant与wifi框架梳理相关推荐

  1. Android wifi探究一:初步认识wpa_supplicant与wifi框架梳理

    http://blog.csdn.net/u011913612/article/details/52671436 平时和wifi打交道还算比较多吧,wifi出了问题就只能找大神解决,这是件很令人伤感的 ...

  2. Android wifi探究二:Wifi framework层源码分析

    上一篇博客初步认识了wpa_supplicant的作用和使用方法,并且尝试着梳理了wifi的大框架,不过,java层的框架我们忽略了,没有分析,也就是说上一篇博客简单的指出了wifi代码的大框架,那么 ...

  3. android 切换连接wifi,Android代码连接Wifi时被系统切换到其他Wifi的问题

    首先说下Android代码连接Wifi的几个步骤:(以下涉及到具体API函数自查哈,写的时候凭借印象大致写了下) 转载请注明出处: 1.首先要开启Wifi连接开关,mWifiManager.setWi ...

  4. Android深入探究笔记--手势识别

    知识不分你我.转载自:http://blog.163.com/gpf1987@126/blog/static/140401677201212921237657/ Android深入探究笔记--手势识别 ...

  5. android wifi 问题是什么意思,如何解决WiFi连接问题故障在Android中为更好的互联网...

    您的工作时间后到家,打开无线网络连接你的手机,等待 - 但是什么也没有发生! 你试图将其关闭并重新打开它,而且也没有改变. 这听起来很可悲,不是吗? 好了,不用担心. 你是不是谁在Android的这些 ...

  6. 转-Android中自动连接到指定SSID的Wi-Fi

    最近在做一个项目,其中涉及到一块"自动连接已存在的wifi热点"的功能,在网上查阅了大量资料,五花八门,但其中一些说的很简单,即不能实现傻瓜式的拿来就用,有些说的很详细,但其中不乏 ...

  7. 转载.Android HAL实现的三种方式(1) - 基于JNI的简单HAL设计

    现在在Android上的HAL开发总的来说还是随意性比较大,Android也并没有规范好一个具体的框架,下面我将根据Jollen的Mokoid工程,自己做了一些改动,分别给大家介绍一下三种实现方式. ...

  8. android开启热点softap模式,[RK3288][Android6.0] Wifi开启热点(SoftAP)流程小结

    Platform: Rockchip OS: Android 6.0 Kernel: 3.10.92 在Settings中选择要打开wifi热点功能: 调用流程如下: onPreferenceChan ...

  9. Android抓包方法(三) 之Win7笔记本Wifi热点+WireShark工具

    前言 做前端测试,基本要求会抓包,会分析请求数据包,查看接口是否调用正确,数据返回是否正确,问题产生是定位根本原因等. 第一篇介绍Fiddler代理,如果APP不支持代理,则不适用:第二篇介绍的Tcp ...

最新文章

  1. cython安装ubuntu_ubuntu上的Cython独立可执行文件
  2. leetcode--200. 岛屿的个数
  3. 前端学习(2460):粉丝管理
  4. html 地址 点击召唤高德,高德地图api 点聚合+海量点+点击事件(根据地区或坐标进行定位)...
  5. 哪个类型的B端产品经理有前景?
  6. 【白皮书分享】2020年度薪酬白皮书.pdf(附下载链接)
  7. linux下mysql的忘记root密码的解决办法
  8. 【语音合成】基于matlab重叠相加法的信号分帧与还原【含Matlab源码 568期】
  9. 【书】软件设计师教程(第4版)(百度云免费下载链接)
  10. 配眼镜走过的那些坑。
  11. 2020年总结:敏而多思,宁静致远——纪念这风雨兼程的一年
  12. AddressBook
  13. Android攻城狮ScrollView
  14. SQL Sever 2012
  15. 零基础学Java语言--第6周编程题
  16. sqlite3数据库的使用及其对应的API函数接口的使用
  17. 国际法学19春在线作业1-0005
  18. 【推荐系统】POLY2、FM、FFM模型的进化之路
  19. 【操作系统之考前垂死挣扎】01操作系统引论
  20. 好好一个985,怎么被学生戏称为全国最“水”的高校?

热门文章

  1. DB9串及交叉与直通线
  2. ISO9001质量管理体系认证咨询22步流程——上篇
  3. java计算机毕业设计专业课程教学计划进程管理系统源程序+mysql+系统+lw文档+远程调试
  4. wincc获取系统时间
  5. vue 使用element 单选框 怎么同时获取value和label值
  6. java32位玩我的世界卡_玩我的世界卡顿怎么设置
  7. 德国商标“Black Friday”黑色星期五被取消
  8. 远程管理服务SSH配置文件说明
  9. 微信退款出现No appropriate protocol (protocol is disabled or cipher suites are inappropriate)
  10. 快手小店赠品有什么规范?赠品管理规范