lollipop_softap启动wifi ap失败
最近一直在调试lollipop,翻译成中文好像是棒棒糖的意思,就是个wifi控制管理工具,比如设置DLNA或者WFD模式等,其原理是通过本地通信工具sockets控制其他接口来启动wpa_suplicant或者hostapd,从而实现功能,所以这里面涉及的还有wpa_supplicant工具包,以及netd,netd就是个服务器,接受ndc(客户端)的指令来执行动作,比如设置hostapd需要的参数,生成hostapd.conf文件,最后启动hostapd应用等。。
lollipop里面包含好几个bin工具
lollipop lollipop_ota lollipop_p2p lollipop_reset lollipop_softap
然后在init.rc里面配置了相应的服务,其中只有lollipop是开机启动的,其他几个服务都是由lollipop接收到指令来切换不同模式的时候去start对应的服务。
service lollipop /system/bin/lollipop
disabled
oneshot
#class main service lollipop_p2p /system/bin/lollipop_p2p
disabled
oneshot service lollipop_softap /system/bin/lollipop_softap
disabled
oneshot service lollipop_ota /system/bin/lollipop_ota
disabled
oneshot service lollipop_reset /system/bin/lollipop_reset
disabled
oneshot
我的sdk是从andorid高度裁剪过的,砍掉了jni以上部分,只有c、c++部分以下,编译完成后img总共12m左右,烧录在16M的nor_flash的板子上。
拿到手的时候sdk中wpa_suplicant没有被包含编译,lollipop也没有源码包含,于是千辛万苦从别的sdk上挪了一个过来,加入各种mk文件中,还有各种依赖库,搞了两天终于编译完能在板子上跑起来了。
但是测试DLNA模式的手发现hostapd没有起来,所以根本不会有wifi热点出来。
查看了log,lollipop中没有打印什么错误信息。
要启动wifi热点一定要跑hostapd起来,有两种方式可以实现。
方式一:
在init.rc(或者类似xxx.rc)配置一个hostapd的service,然后在源代码中start这个服务
方式二:
直接在源代码中调用system或者用execl一类的函数来执行hostapd。
下面开始排查
第一步:
查看是否有配置调用hostapd的服务
果然在init.rc中看到一个hostapd服务,开机不运行,需要有人专门去start才行
service hostapd /system/bin/hostapd /data/misc/wifi/hostapd.conf
class main
disabled
oneshot
第二步
查看sdk中的源码,看下睡会去启动这个服务
第一个要找的就是lollipop部分,去里面cgrep一下
cgrep "hostapd"
./wifi/lollipop_softap.c:28:#define HOSTAPD_CONF_FILE "/data/misc/wifi/hostapd.conf"
./wifi/lollipop_softap.c:71: ALOGD("start hostapd ...");
./wifi/lollipop_softap.c:72: if(execl("/system/bin/hostapd", "/system/bin/hostapd",
./wifi/lollipop_softap.c:155: "/data/misc/wifi/hostapd\nssid=%s\nchannel=6\nhw_mode=g\nieee80211n=1\n"HT_CAPAB,
./wifi/lollipop_softap.c:268: /* TODO: implement over hostapd */
./socket_ipc/lollipop_socket_ipc.c:193: // chmod so hostapd which is wifi permission can send info to softap
这里没人启动hostapd服务,但是有人在调用hostapd执行档,进去
wifi/lollipop_softap.c
找到这个函数
int startSoftap(void)
50 int startSoftap(void) {
51 pid_t pid = 1;
52 int ret = 0;
53
54 if (mPid) {
55 ALOGE("Softap already started");
56 return 0;
57 }
58 #if NEED_SOCK
59 if (mSock < 0) {
60 ALOGE("Softap startap - failed to open socket");
61 return -1;
62 }
63 #endif
64 if ((pid = fork()) < 0) {
65 ALOGE("fork failed (%s)", strerror(errno));
66 return -1;
67 }
68
69 if (!pid) {
70 ensure_entropy_file_exists();
71 ALOGD("start rtl_hostapd ...");
72 if(execl("/system/bin/rtl_hostapd", "/system/bin/rtl_hostapd",
73 "-e", WIFI_ENTROPY_FILE,
74 HOSTAPD_CONF_FILE, (char *) NULL)) {
75 ALOGE("execl failed (%s)", strerror(errno));
76 }
77 ALOGE("Should never get here!");
78 return -1;
79 } else {
80 mPid = pid;
81 ALOGD("Softap startap - Ok");
82 usleep(AP_BSS_START_DELAY);
83 }
84 return ret;
85
86 }
接下来再去找谁会调用这个函数
cgrep "startSoftap"
./wifi/lollipop_softap.c:50:int startSoftap(void) {
./wifi/lollipop_softap.h:15:extern int startSoftap(void);
在lollipop里面是没人调用,croot回到android根目录查找
cgrep "startSoftap"
./system/netd/CommandListener.cpp:918: rc = sSoftapCtrl->startSoftap();
./system/netd/SoftapController.cpp:54:int SoftapController::startSoftap() {
./system/netd/SoftapController.h:51: int startSoftap();
./system/netd/SoftapController.h:58: virtual int startSoftap();
./system/netd/SoftapController.h:85: int startSoftap();
./system/netd/SoftapController.h:102: int startSoftap();
./external/lollipop_wifi/wifi/lollipop_softap.c:50:int startSoftap(void) {
./external/lollipop_wifi/wifi/lollipop_softap.h:15:extern int startSoftap(void);
发现至少有两个地方有可能调用,最后进去跟踪源码,确认了一下,里面是有调用hostapd,但是不是调用了lollipop里面的startSoftap函数而是自己用execl执行了hostapd,
system/netd/SoftapController.cpp
int SoftapController::startSoftap() {
55 pid_t pid = 1;
56
57 if (mPid) {
58 ALOGE("SoftAP is already running");
59 return ResponseCode::SoftapStatusResult;
60 }
61
62 if ((pid = fork()) < 0) {
63 ALOGE("fork failed (%s)", strerror(errno));
64 return ResponseCode::ServiceStartFailed;
65 }
66
67 if (!pid) {
68 ensure_entropy_file_exists();
69 if (execl(HOSTAPD_BIN_FILE, HOSTAPD_BIN_FILE,
70 "-e", WIFI_ENTROPY_FILE,
71 HOSTAPD_CONF_FILE, (char *) NULL)) {
72 ALOGE("execl failed (%s)", strerror(errno));
73 }
74 ALOGE("SoftAP failed to start");
75 return ResponseCode::ServiceStartFailed;
76 } else {
77 mPid = pid;
78 ALOGD("SoftAP started successfully");
79 usleep(AP_BSS_START_DELAY);
80 }
81 return ResponseCode::SoftapStatusResult;
82 }
这部分接口是netd源码包里面的,netd也是一个wifi服务,可以通过ndc客户端给netd发送指令叫他干活,netd就可以实现wifi热点。
现在的线索又转移到了查找谁会调用ndc来发送指令,首先还是回去lollipop查找
cgrep "bin\/ndc"
./p2p_main.c:171: sprintf(cmd, "/system/bin/ndc softap fwreload %s STA", P2P_IFACE);
./softap_main.c:114: system("/system/bin/ndc ipfwd disable");
./softap_main.c:115: system("/system/bin/ndc softap stopap");
./softap_main.c:318: sprintf(cmd, "/system/bin/ndc softap fwreload %s AP", SOFTAP_IFACE);
./softap_main.c:354: sprintf(cmd, "/system/bin/ndc softap fwreload %s AP", SOFTAP_IFACE);
./softap_main.c:358: sprintf(cmd, "/system/bin/ndc softap set %s %s open", SOFTAP_IFACE, deviceName);
./softap_main.c:360: sprintf(cmd, "/system/bin/ndc softap set %s %s broadcast 6 wpa2-psk %s", SOFTAP_IFACE, deviceName, passwd);
./softap_main.c:381: system("/system/bin/ndc softap startap");
./softap_main.c:417: system("/system/bin/ndc ipfwd enable");
./wifi/lollipop_wifiMonitor.c:429: sprintf(buf, "/system/bin/ndc interface clearaddrs %s", wlan_iface);
./wifi/lollipop_wifiMonitor.c:495: sprintf(buf, "/system/bin/ndc nat enable %s %s 1 192.168.49.1/24", softap_iface, wlan_iface);
./wifi/lollipop_wifiMonitor.c:521: sprintf(buf, "/system/bin/ndc interface clearaddrs %s", wlan_iface);
./wifi/lollipop_wifiMonitor.c:527: sprintf(buf, "/system/bin/ndc nat disable %s %s", softap_iface, wlan_iface);
果然还是lollipop调用了ndc而且是用system去运行的,进去看了下源码,发现每次调用system执行没有检查返回值报错,所以没有调用成功根本不会有报错的log。
最大的坑还是板子上根本没有ndc和netd的执行档,sdk源码中没有包含netd的编译。
又回去检查了一下lollipop里面的Android.mk里面没有写ndc的依赖,源码里面又是调用system执行,所有这种依赖错误也是检查不出来的。
下面开始修正
第一步:
编译net源码,并在init.rc中配置netd的启动服务
service netd /system/bin/netd
class main
socket netd stream 0660 root system
socket dnsproxyd stream 0660 root inet
socket mdns stream 0660 root system
第二步:
修改lollipop中的源码在调用system的地方检查返回值报错
重新编译pack,烧录。
本以为这次热点应该起来了,结果大失所望,依然没有热点出来。
ps看了下netd在运行但是hostapd没有。但是log上看到了hostapd打印出了几条有用的错误信息,说明hostapd曾经来过,但是异常退出来了。
E/hostapd ( 1414): Configuration file: /data/misc/wifi/hostapd.conf
E/hostapd ( 1414): Could not select hw_mode and channel. (-3)
E/hostapd ( 1414): p2p0: Unable to setup interface.
E/hostapd ( 1414): Failed to initialize interface
拿着log跟踪了一下hostapd的源码,错误是从第二条开始的,第一条是个提示信息。
现在最早报错的是在操作hw_mod和channel的时候,这两个参数是从hostap.conf获取的,现在检查一下hostapd.conf
interface=p2p0
driver=nl80211
ctrl_interface=/data/misc/wifi/hostapd
ssid=LOLLIPOP-ECF21E
channel=12345678
ieee80211n=1
hw_mode=g
ignore_broadcast_ssid=0
很明显这个conf是有问题的,至少channel是错误的,12345678是设置给hostapd的明文密码, 在增加的log的可以看到调用ndc设置的,而且conf中psk等信息也没配置完全。
接下来就去查找hostapd.conf是在哪里配置的,找到异常的地方
D/lollipop_softap( 1517): 248 run /system/bin/ndc softap fwreload p2p0 AP ok
D/lollipop_softap( 1517): 292 run /system/bin/ndc softap set p2p0 LOLLIPOP-ECF21E wpa2-psk 12345678 ok
D/lollipop_softap( 1517): 352 run /system/bin/ndc ipfwd enable ok
设置的最初入口是ndc,现在就去找ndc的入口函数
int main(int argc, char **argv) {
39 int sock;
40 int cmdOffset = 0;
41
42 if (argc < 2)
43 usage(argv[0]);
44
45 // try interpreting the first arg as the socket name - if it fails go back to netd
46
47 if ((sock = socket_local_client(argv[1],
48 ANDROID_SOCKET_NAMESPACE_RESERVED,
49 SOCK_STREAM)) < 0) {
50 if ((sock = socket_local_client("netd",
51 ANDROID_SOCKET_NAMESPACE_RESERVED,
52 SOCK_STREAM)) < 0) {
53 fprintf(stderr, "Error connecting (%s)\n", strerror(errno));
54 exit(4);
55 }
56 } else {
57 if (argc < 3) usage(argv[0]);
58 printf("Using alt socket %s\n", argv[1]);
59 cmdOffset = 1;
60 }
61
62 if (!strcmp(argv[1+cmdOffset], "monitor"))
63 exit(do_monitor(sock, 0));
64 exit(do_cmd(sock, argc-cmdOffset, &(argv[cmdOffset])));
65 }
这里是第一个参数当做一个socket本地通信文件,去连接服务器(自己是客户端),如果连接失败了就走默认的netd通信连接,然后把后面的参数以此解析发送给netd服务器。
很明显第一次用socket_local_client连接argv[1]肯定会失败,因为lollipop调用ndc softap的时候自己并没有去启动(可以调用socket_local_server)一个基于softap文件的本地socket服务,所以每次都是连接到了netd,这样才能连接到netd。
现在参数已经传递给netd了,就得去跟踪netd里面是怎么样配置hostapd.conf。
执行/system/bin/ndc softap set p2p0 LOLLIPOP-ECF21E wpa2-psk 12345678的时候netd最后扔给了下面这个
setSoftap
函数
int SoftapController::setSoftap(int argc, char *argv[]) {
115 char psk_str[2*SHA256_DIGEST_LENGTH+1];
116 int ret = ResponseCode::SoftapStatusResult;
117 int i = 0;
118 int fd;
119 int hidden = 0;
120 int channel = AP_CHANNEL_DEFAULT;
121 char *wbuf = NULL;
122 char *fbuf = NULL;
123
124 if (argc < 5) {
125 ALOGE("Softap set is missing arguments. Please use:");
126 ALOGE("softap <wlan iface> <SSID> <hidden/broadcast> <channel> <wpa2?-psk|open> <passphrase>");
127 return ResponseCode::CommandSyntaxError;
128 }
129
130 if (!strcasecmp(argv[4], "hidden"))
131 hidden = 1;
132
133 if (argc >= 5) {
134 channel = atoi(argv[5]);
135 if (channel <= 0)
136 channel = AP_CHANNEL_DEFAULT;
137 }
138
139 asprintf(&wbuf, "interface=%s\ndriver=nl80211\nctrl_interface="
140 "/data/misc/wifi/hostapd\nssid=%s\nchannel=%d\nieee80211n=1\n"
141 "hw_mode=g\nignore_broadcast_ssid=%d\n",
142 argv[2], argv[3], channel, hidden);
143
144 if (argc > 7) {
145 if (!strcmp(argv[6], "wpa-psk")) {
146 generatePsk(argv[3], argv[7], psk_str);
147 asprintf(&fbuf, "%swpa=1\nwpa_pairwise=TKIP CCMP\nwpa_psk=%s\n", wbuf, psk_str);
148 } else if (!strcmp(argv[6], "wpa2-psk")) {
149 generatePsk(argv[3], argv[7], psk_str);
150 asprintf(&fbuf, "%swpa=2\nrsn_pairwise=CCMP\nwpa_psk=%s\n", wbuf, psk_str);
151 } else if (!strcmp(argv[6], "open")) {
152 asprintf(&fbuf, "%s", wbuf);
153 }
从126行代码示例使用demo
ALOGE("softap <wlan iface> <SSID> <hidden/broadcast> <channel> <wpa2?-psk|open> <passphrase>");
以及133-137行代码获取channel来看这个参数解析并不是很智能的,只是根据第几个参数来设置
而lollipop调用
/system/bin/ndc softap set p2p0 LOLLIPOP-ECF21E wpa2-psk 12345678
省略掉了broadcast和channel参数,导致解析出现错位了,把psk当做channel,后面需要解析psk的时候已经没有参数可用了,所以conf文件中没有psk
所以问题的根本原因是lollipop调用ndc的时候参数设置错乱。
找到出错的源码
if (strlen(passwd) == 0) {
286 sprintf(cmd, "/system/bin/ndc softap set %s %s Broadcast %d open", SOFTAP_IFACE, deviceName, channel);
287 } else {
288 sprintf(cmd, "/system/bin/ndc softap set %s %s wpa2-psk %s",
289 SOFTAP_IFACE, deviceName, passwd);
290 }
291
292 if(system(cmd)){
293 ALOGE("%d run %s failed:%s",__LINE__, cmd, strerror(errno));
294 }
295 else
296 ALOGD("%d run %s ok",__LINE__, cmd);
改成如下
if (strlen(passwd) == 0) {
286 sprintf(cmd, "/system/bin/ndc softap set %s %s Broadcast %d open", SOFTAP_IFACE, deviceName, channel);
287 } else {
288 sprintf(cmd, "/system/bin/ndc softap set %s %s Broadcast %d wpa2-psk %s",
289 SOFTAP_IFACE, deviceName, channel, passwd);
290 }
291
292 if(system(cmd)){
293 ALOGE("%d run %s failed:%s",__LINE__, cmd, strerror(errno));
294 }
295 else
296 ALOGD("%d run %s ok",__LINE__, cmd);
其中channel可以设置一个默认的或者从其他地方获取。
重新编译打包烧录
这次热点已经起来了
查看hostapd.conf
interface=p2p0
driver=nl80211
ctrl_interface=/data/misc/wifi/hostapd
ssid=LOLLIPOP-ECF21E
channel=6
ieee80211n=1
hw_mode=g
ignore_broadcast_ssid=0
wpa=2
rsn_pairwise=CCMP
wpa_psk=164934815f6e071f26f8bb59db883daf3ef09bb7df3c6903c082a881370a5d42
这次是ok的了
E/hostapd ( 1538): Configuration file: /data/misc/wifi/hostapd.conf
E/hostapd ( 1538): Using interface p2p0 with hwaddr 7e:dd:90:ec:f2:1e and ssid "LOLLIPOP-ECF21E"
I/hostapd ( 1538): random: Only 15/20 bytes of strong random data available from /dev/random
I/hostapd ( 1538): random: Allow operation to proceed based on internal entropy
这次热点LOLLIPOP-ECF21E已经出来了,可以连接ok。
-----------------------------------------------------------------------------------
现在总结一下调用的流程
首先是lollipop解析lollipop.conf文件获取到DLNA的模式
然后启动了lollipop_softap服务(执行lollipop_softap)
lollipop_softap调用ndc执行档发送了设置和启动指令,设置了hostapd启动参数,并启动softap。
ndc将接收到的指令解析完通过本地socket netd发送参数及命令给netd服务。
netd服务配置生成了hostapd.conf,并调用/system/bin/hostapd启动了hostapd。
lollipop ---> lollipop_softap ---> ndc --> netd --->hostapd
lollipop_softap启动wifi ap失败的更多相关文章
- win7下设置 WiFi AP
开启windows 7的隐藏功能:虚拟WiFi和SoftAP(即虚拟无线AP),就可以让计算机变成无线路由器.实现共享上网. 1.以管理员身份运行命令提示符: “开始”---在搜索栏输入“cmd”-- ...
- Arduino IDE for ESP8266教程(二) 创建WIFI AP模式
创建WIFI热点 #include <ESP8266WiFi.h> void setup() { Serial.begin ( 115200 ); Serial.println(" ...
- vs2015启动iis express失败
vs2015启动web项目失败,查看日志 IIS Express\aspnetcore.dll 未能加载 ,解决方法 下载 VSorVWDASPNETCore.exe (https://www.asp ...
- selenium webdriver启动IE浏览器失败的解决办法
通过selenium webdriver启动IE浏览器失败,报错:selenium.common.exceptions.WebDriverException: Message: Unexpected ...
- WebSphere服务器已启动但是初始化失败问题
--WebSphere服务器已启动但是初始化失败问题 -----------------------------------------------2014/03/06 经常有开发同事反映,环境用着用 ...
- 树莓派3启动wifi并且配置wifi
概述 树莓派3内置了wifi和蓝牙模块,我们不用像以前的版本那样,再去购买一个外接的模块练到raspberry上. 当我们第一次启动了树莓派的时候,必然使用了网线,但是之后的每一次使用,我们当然更希望 ...
- 安装VisualSVN Server 报"Service 'VisualSVN Server' failed to start. Please check VisualSVN Server log in Event Viewer for more details"错误.原因是启动"VisualSVN Server"失败
安装VisualSVN Server 报"Service 'VisualSVN Server' failed to start. Please check VisualSVN Server ...
- Activiti启动某个流程失败,页面报500
现象:Activiti启动某个流程失败,页面报500,错误日志如下. 2017-06-19 10:50:09 [org.activiti.engine.impl.interceptor.Command ...
- “一键制作启动u盘失败”的主要原因是什么?
一键制作启动u盘失败的主要原因是什么?今天u启动小编就和大家一起来分析原因并寻求答案吧! 原因分析: 1.u盘内有文件正在运行或者是打开: 2.u盘自身的质量问题: 3.最主要的原 ...
随机推荐
- docker run配置参数
Usage: docker run [OPTIONS] IMAGE [COMMAND] [ARG...] -d, --detach=false 指定容器运行于前台还是后台,默认为false -i, - ...
- 洛谷P1208——P1208 [USACO1.3]Mixing Milk(贪心)
题目描述 由于乳制品产业利润很低,所以降低原材料(牛奶)价格就变得十分重要.帮助Marry乳业找到最优的牛奶采购方案. Marry乳业从一些奶农手中采购牛奶,并且每一位奶农为乳制品加工企业提供的价格是 ...
- 【PHP数据结构】图的遍历:深度优先与广度优先
在上一篇文章中,我们学习完了图的相关的存储结构,也就是 邻接矩阵 和 邻接表 .它们分别就代表了最典型的 顺序存储 和 链式存储 两种类型.既然数据结构有了,那么我们接下来当然就是学习对这些数据结构的 ...
- html 表单input disabled属性提交后台无法获得数据
在input上加入disabled属性后, 点击提交会遗漏该值, 有两个办法: 一 可以考虑readonly属性,一样的不可修改操作,但是可以提交 二 在提交时 js 代码操作去除input上的dis ...
- 深入剖析RocketMQ源码-NameServer
一.RocketMQ架构简介 1.1 逻辑部署图 (图片来自网络) 1.2 核心组件说明 通过上图可以看到,RocketMQ的核心组件主要包括4个,分别是NameServer.Broker.Produ ...
- 如何实现Orchard Core CMS的全文索引
Orchard Core提供了Lucene功能,允许您在网站上进行全文搜索.大多数情况下,在运行博客或简单的代理网站时,您可能需要在页面内容中进行搜索.在Orchard Core中,您可以使用Liqu ...
- mysql 不常用的存储引擎
csv 数据文件可以编辑;每一列不能为空,不支持索引:文件保存数据,cat可以查看数据;用处:数据交换中间表--excel表导入数据等; Archive 对表数据进行压缩,磁盘i/o减少:节省空间;只 ...
- filter_var() 验证邮箱、ip、url的格式 php
验证邮箱格式的正确与否:你的第一解决方案是什么呢? 不管你们怎么思考的:反正我首先想到的就是字符串查找看是否有@符号: 但是对于结尾的.com或者.net 亦或者.cn等等越来越多的域名验证感觉棘手: ...
- kubeadm 命令简介
kubeadm 命令 kubeadm init 启动一个kubernetes主节点 kubeadm join 启动一个kubernetes工作节点并加入到集群中 kubeadm upgrade 更新一 ...
- IdentityServer4[5]简化模式
Implicit简化模式(直接通过浏览器的链接跳转申请令牌) 简化模式是相对于授权码模式而言的.其不再需要[Client]的参与,所有的认证和授权都是通过浏览器来完成的. 创建项目 IdentityS ...