zoukankan      html  css  js  c++  java
  • 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
    ssid=%s
    channel=6
    hw_mode=g
    ieee80211n=1
    "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)
    ", strerror(errno));      
      54             exit(4);                                                          
      55         }                                                                     
      56     } else {                                                                  
      57         if (argc < 3) usage(argv[0]);                                         
      58         printf("Using alt socket %s
    ", 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
    driver=nl80211
    ctrl_interface="           
     140             "/data/misc/wifi/hostapd
    ssid=%s
    channel=%d
    ieee80211n=1
    "    
     141             "hw_mode=g
    ignore_broadcast_ssid=%d
    ",                          
     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
    wpa_pairwise=TKIP CCMP
    wpa_psk=%s
    ", wbuf, psk_str);
     148         } else if (!strcmp(argv[6], "wpa2-psk")) {                            
     149             generatePsk(argv[3], argv[7], psk_str);                           
     150             asprintf(&fbuf, "%swpa=2
    rsn_pairwise=CCMP
    wpa_psk=%s
    ", 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

  • 相关阅读:
    CF1137C Museums Tour(tarjan+DP)
    Educational Codeforces Round 65 (Rated for Div. 2)
    Codeforces Round #559(Div.1)
    委托
    类库
    is 和 as 运算符
    面向对象 接口
    抽象类
    面向对象 多态
    访问修饰符 程序集 静态方法
  • 原文地址:https://www.cnblogs.com/tid-think/p/10938805.html
Copyright © 2011-2022 走看看