最近一直在调试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. JavaScrip中 Array.reduce()

    数组的方法 reduce() reduce方法在数组的每一项元素上都会执行回调函数. 语法:array.reduce( callBack [ , init]  ) // 语法arrary.reduce ...

  2. C#中List是链表吗?为什么可以通过下标访问

    使用C#的同学对List应该并不陌生,我们不需要初始化它的大小,并且可以方便的使用Add和Remove方法执行添加和删除操作,但却可以使用下标来访问它的数据,它是我们常说的链表吗?     List& ...

  3. Stage 1 项目需求分析报告

    迷你商城后台管理系统-- 需求分析 1. 引言 作为互联网热潮的崛起,消费者们的普遍差异化,实体商城要想在互联网的浪潮中继续发展,就需要制定出针对用户以及消费者的消费习惯以及喜爱品种的消费方案.从而企 ...

  4. ecshop 加入购物车和直接购买同时存在的方法

    一.首先将直接购买的链接设置为 <a href="javascript:bool =1;addToCart({$goods.goods_id})"> bool值为1,g ...

  5. Java学习之随堂笔记系列——day04

    今日内容1.break和continue关键字以及循环嵌套    1.1 break和continue的区别?        continue表示跳过当前循环,继续执行下一次循环break表示结束整个 ...

  6. python 文件夹扫描

    扫描指定文件夹下的文件.或者匹配指定后缀和前缀的函数. 假设要扫描指定文件夹下的文件,包含子文件夹,调用scan_files("/export/home/test/") 假设要扫描 ...

  7. [转载]ssh证书登录

    转载链接 前言 本文基于实际Linux管理工作,实例讲解工作中使用ssh证书登录的实际流程,讲解ssh证书登录的配置原理,基于配置原理,解决实际工作中,windows下使用SecureCRT证书登录的 ...

  8. Loj#6503-「雅礼集训 2018 Day4」Magic【分治NTT】

    正题 题目链接:https://loj.ac/p/6503 题目大意 \(n\)张卡\(m\)种,第\(i\)种卡有\(a_i\)张,求所有排列中有\(k\)对相邻且相同的卡牌. \(1\leq n\ ...

  9. vue+element实现省区市三级联动以及详细地址的输入

    Vue+elementui实现省区市三级联动+详细地址的输入 详细需求,需要手动更改用户所在的地址. 安装依赖项 npm install element-china-area-data -S 在组建中 ...

  10. CefSharp基于.Net Framework 4.0 框架编译

    CefSharp基于.Net Framework 4.0 框架编译 本次源码使用的是Github上CefSharp官方的79版本源码 准备 IDE Visual Studio 2017 Enterpr ...