最近一直在调试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失败的更多相关文章

  1. win7下设置 WiFi AP

    开启windows 7的隐藏功能:虚拟WiFi和SoftAP(即虚拟无线AP),就可以让计算机变成无线路由器.实现共享上网. 1.以管理员身份运行命令提示符: “开始”---在搜索栏输入“cmd”-- ...

  2. Arduino IDE for ESP8266教程(二) 创建WIFI AP模式

    创建WIFI热点 #include <ESP8266WiFi.h> void setup() { Serial.begin ( 115200 ); Serial.println(" ...

  3. vs2015启动iis express失败

    vs2015启动web项目失败,查看日志 IIS Express\aspnetcore.dll 未能加载 ,解决方法 下载 VSorVWDASPNETCore.exe (https://www.asp ...

  4. selenium webdriver启动IE浏览器失败的解决办法

    通过selenium webdriver启动IE浏览器失败,报错:selenium.common.exceptions.WebDriverException: Message: Unexpected ...

  5. WebSphere服务器已启动但是初始化失败问题

    --WebSphere服务器已启动但是初始化失败问题 -----------------------------------------------2014/03/06 经常有开发同事反映,环境用着用 ...

  6. 树莓派3启动wifi并且配置wifi

    概述 树莓派3内置了wifi和蓝牙模块,我们不用像以前的版本那样,再去购买一个外接的模块练到raspberry上. 当我们第一次启动了树莓派的时候,必然使用了网线,但是之后的每一次使用,我们当然更希望 ...

  7. 安装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 ...

  8. Activiti启动某个流程失败,页面报500

    现象:Activiti启动某个流程失败,页面报500,错误日志如下. 2017-06-19 10:50:09 [org.activiti.engine.impl.interceptor.Command ...

  9. “一键制作启动u盘失败”的主要原因是什么?

    一键制作启动u盘失败的主要原因是什么?今天u启动小编就和大家一起来分析原因并寻求答案吧!     原因分析:   1.u盘内有文件正在运行或者是打开:   2.u盘自身的质量问题:   3.最主要的原 ...

随机推荐

  1. AD学习笔记(基础)

    AD学习 1 学习思路 1.1 学什么 1.2 怎么学 2 AD本身 3 AD project 3.1 任务层级 3.2 PCB流程 4 原理图工作环境设置 5 开始 5.1工程创建 5.2 元件库介 ...

  2. base64原理,使用场景

    Base64编码,是我们程序开发中经常使用到的编码方法.它是一种基于用64个可打印字符来表示二进制数据的表示方法.它通常用作存储.传输一些二进制数据编码方法!也是MIME(多用途互联网邮件扩展,主要用 ...

  3. [转载]linux上用PHP读取WORD文档

    在linux上用PHP读取WORD文档,其实是使用了 antiword程序把word文档转化为txt文档. 再使用php执行系统命令调用而已. 具体操作如下: 1.安装antiword 官方站:htt ...

  4. 鸿蒙内核源码分析(fork篇) | 一次调用,两次返回 | 百篇博客分析OpenHarmony源码 | v45.03

    百篇博客系列篇.本篇为: v45.xx 鸿蒙内核源码分析(Fork篇) | 一次调用,两次返回 | 51.c.h .o 进程管理相关篇为: v02.xx 鸿蒙内核源码分析(进程管理篇) | 谁在管理内 ...

  5. CRM是什么,你有认真了解过CRM吗?

    这是CRM的一个简单定义 客户关系管理 (CRM)是一种用于管理公司与客户和潜在客户的所有关系和互动的技术.目标很简单:改善业务关系.CRM 系统可帮助公司与客户保持联系.简化流程并提高盈利能力. 当 ...

  6. STAR-CCM+使用教程(开坑)

    前言: 之前在项目中经常使用STAR-CCM+做数值模拟,中间也陆陆续续折腾过许久,踩过一些坑.未来考虑转行,以后可能也会不再用到这CFD软件,所以正好趁这个机会在这做一个教程.记录下自己STAR-C ...

  7. Springboot在有锁的情况下如何正确使用事务

    1. 概述 老话说的好:想要赚钱,就去看看有钱人有什么需求,因为有钱人钱多,所以赚的多. 言归正传,在Java项目的研发中,"锁"这个词并不陌生,最经典的使用场景是商品的超卖问题. ...

  8. 无法解析的外部符号之_cvLoadImage,_cvCreateMat,_cvReleaseImage之类

    一个错误可能是:附加依赖项少添加了库函数: 还有一个可能是:配置设置错误了,比如该是64位,却设置成win32了.改过来就好了. 要注意opencv的使用中 在Debug.Release模式以及x64 ...

  9. 安装 webstorm--->vue

    一.先去官网下载webstorm     https://www.jetbrains.com/ 不论是Mac的还是win得都有相应的版本, 二.再去官网下载git     https://git-sc ...

  10. luogu3888 GDOI2014拯救莫里斯 (状压dp)

    题目描述 莫莉斯·乔是圣域里一个叱咤风云的人物,他凭借着自身超强的经济头脑,牢牢控制了圣域的石油市场. 圣域的地图可以看成是一个n*m的矩阵.每个整数坐标点(x , y)表示一座城市\(( 1\le ...