Android下实现非启动界面Wifi连接

简介:

Android的网络功能和一般的linux并无太大的区别,我原来以为在Android上连接网络和普通的linux连接网络没有很大区别,事实上区别还是有一些的。

由于项目的需要,我的目标是在Android的界面没有启动之前连接wifi,于是本来的期待是直接在init.rc中加入一些脚本调用即可,但研究了一会儿发现没有那么简单。

首先要感谢anly_jun@baidu贴吧的几篇博文,从http://hi.baidu.com/anly_jun/blog/item/8ecb92d593d144cf50da4b6e.html开始,一共有七篇关于Android Wifi模块分析,其中有大量博主自己使用UML工具画的调用讲解,对于理解Android Wifi工作机制还是有很用的。其中最重要的是下面这几幅图。

Android下实现非启动界面Wifi连接 - angelbear - angelbear的博客(转自http://hi.baidu.com/anly_jun/blog/item/65e26e117e09ebcda6ef3f56.html

在想要对wifi硬件动作之前,需要做两件事情,一是要load wifi的driver,而是要打开wpa_supplicant,其实如果是连接没有加密的wifi,没有必要打开wpa_supplicant,但是为了讲问题化为熟知的问题,此处还是先按照提示调用wifi_load_driver()和wifi_start_supplicant()

按照上面的提示写出来的初始化代码如下:

int   init_stage ()  {  
     // load the wifi driver: insmod .ko  
     int   ret   =   wifi_load_driver (); 
     if ( ret   <   0 {  
         LOGE ( "Failed to load Wi-Fi driver. %s" , strerror ( errno )); 
         return   - 1
     }  

     // start wpa_supplicant  
     ret   =    wifi_start_supplicant (); 
     if ( ret   <   0 {  
         LOGE ( "Failed to start supplicant daemon. %s" , strerror ( errno )); 
         return   - 1
     }  
     return   0
}  

接下来,便是连接的过程了,经过上面的步骤,wifi的driver已经载入,wpa_supplicant也已经打开,那咱们就可以开始连接无线了吧。后来证实这是错误的,因为anly_jun的这篇Android wifi分析的粒度只在Java层面的函数级别,因此有一些细节并没有提到。在下面我会提到这些细节。

按照一般的linux中连接wifi的步骤,这时候就可以直接调用一个程序来连接某个ssid的无线网络,然后调用dhcpd来分配ip了,我之前在eeepc上连接wifi就非常简单,调用iwconfig [ssid],再调用dhcpd就可以了。但很遗憾,Android上并没有iwconfig这样方便的工具。

这下线索似乎就断了,天无绝人之路,既然在Android的Java code中都可以添加一个无线网络并且连接,那我们就去Android的Java源代码中找一找。在Android中,程序员是使用WifiManager这个类来进行Wifi操作的,其中关于添加一个网络的代码如下:

/** 
* Add a new network description to the set of configured networks. 
* The {@code networkId} field of the supplied configuration object 
* is ignored. 
* <p/> 
* The new network will be marked DISABLED by default. To enable it, 
* called {@link #enableNetwork}. 
* 
* @param config the set of variables that describe the configuration, 
*            contained in a {@link WifiConfiguration} object. 
* @return the ID of the newly created network description. This is used in 
*         other operations to specified the network to be acted upon. 
*         Returns {@code -1} on failure. 
*/ 
public  int addNetwork(WifiConfiguration config) { 
     if (config ==  null) { 
         return - 1
    } 
    config. networkId = - 1
     return addOrUpdateNetwork(config); 


/** 
* Internal method for doing the RPC that creates a new network description 
* or updates an existing one. 
* 
* @param config The possibly sparse object containing the variables that 
*         are to set or updated in the network description. 
* @return the ID of the network on success, {@code -1} on failure. 
*/ 
private  int addOrUpdateNetwork(WifiConfiguration config) { 
     try { 
         return mService. addOrUpdateNetwork(config); 
    }  catch (RemoteException e) { 
         return - 1
    } 

看其中确实是调用了service的函数,于是又在frameworks/base/services/java/com/android/server/WifiService.java中找到了有关增加一个网络配置的相关代码

/** 
* see {@link android.net.wifi.WifiManager#addOrUpdateNetwork(WifiConfiguration)} 
* @return the supplicant-assigned identifier for the new or updated 
* network if the operation succeeds, or {@code -1} if it fails 
*/ 
public  int addOrUpdateNetwork(WifiConfiguration config) { 
    enforceChangePermission(); 

     /* 
     * If the supplied networkId is -1, we create a new empty 
     * network configuration. Otherwise, the networkId should 
     * refer to an existing configuration. 
     */ 
     int netId = config. networkId
     boolean newNetwork = netId == - 1
     boolean doReconfig =  false
     // networkId of -1 means we want to create a new network
     synchronized (mWifiStateTracker) { 
         if (newNetwork) { 
            netId = mWifiStateTracker. addNetwork(); 
             if (netId <  0) { 
                 if (DBG) { 
                    Slog. d(TAG,  "Failed to add a network!");
                } 
                 return - 1
            } 
            doReconfig =  true
        } 
        mNeedReconfig = mNeedReconfig || doReconfig; 
    } 

    setVariables: { 
         /* 
         * Note that if a networkId for a non-existent network 
         * was supplied, then the first setNetworkVariable()
         * will fail, so we don't bother to make a separate check 
         * for the validity of the ID up front. 
         */ 
         if (config. SSID !=  null && 
                !mWifiStateTracker. setNetworkVariable
                    netId, 
                    WifiConfiguration. ssidVarName
                    config. SSID)) { 
             if (DBG) { 
                Slog. d(TAG,  "failed to set SSID: "+config. SSID); 
            } 
             break setVariables; 
        } 

         if (config. BSSID !=  null && 
                !mWifiStateTracker. setNetworkVariable
                    netId, 
                    WifiConfiguration. bssidVarName
                    config. BSSID)) { 
             if (DBG) { 
                Slog. d(TAG,  "failed to set BSSID: "+config. BSSID); 
            } 
             break setVariables; 
        }

我们来到frameworks/base/wifi/java/android/net/wifi/WifiStateTracker.java中,看看addNetwork和setNetworkVariable函数都是什么样的。

/** 
* Add a network 
* 
* @return network id of the new network 
*/ 
public  synchronized  int addNetwork() { 
     if (mWifiState. get() != WIFI_STATE_ENABLED) { 
         return - 1
    } 
     return WifiNative. addNetworkCommand(); 


/** 
* Set network setting by name 
* 
* @param netId network id of the network 
* @param name network variable key 
* @param value network variable value 
* @return {@code true} if the operation succeeds, {@code false} otherwise 
*/ 
public  synchronized  boolean setNetworkVariable( int netId,String name, String value) { 
     if (mWifiState. get() != WIFI_STATE_ENABLED) { 
         return  false
    } 
     return WifiNative. setNetworkVariableCommand(netId, name,value); 

似乎WifiNative就应该是最终boss了,于是来到frameworks/base/wifi/java/android/net/wifi/WifiNative.java中一看,这两个函数都是这么定义的。
 
public  native  static  int addNetworkCommand(); 

public  native  static  boolean setNetworkVariableCommand( intnetId, String name, String value); 
 
熟悉Android NDK开发的朋友都应该知道,这里的函数都是通过jni链接到c/c++代码中,于是我们稍微花了一点时间,找到了这个函数所对应的frameworks/base/core/jni/android_net_wifi_Wifi.cpp
 
static  int doCommand( const  char *cmd,  char *replybuf,  intreplybuflen) 

     size_t reply_len = replybuflen -  1

     if (::wifi_command(cmd, replybuf, &reply_len) !=  0
         return - 1
     else { 
         // Strip off trailing newline 
         if (reply_len >  0 && replybuf[reply_len- 1] ==  '\n'
            replybuf[reply_len- 1] =  '\0'
         else 
            replybuf[reply_len] =  '\0'
         return  0
    } 


static jint doIntCommand( const  char *cmd) 

     char reply[ 256]; 

     if (doCommand(cmd, reply,  sizeof(reply)) !=  0) { 
         return (jint)- 1
    }  else { 
         return (jint)atoi(reply); 
    } 


static jboolean doBooleanCommand( const  char *cmd,  const  char*expect) 

     char reply[ 256]; 

     if (doCommand(cmd, reply,  sizeof(reply)) !=  0) { 
         return (jboolean)JNI_FALSE; 
    }  else { 
         return (jboolean)(strcmp(reply, expect) ==  0); 
    } 


…… 

static jint android_net_wifi_addNetworkCommand(JNIEnv* env,jobject clazz) 

     return doIntCommand( "ADD_NETWORK"); 


static jbooleanandroid_net_wifi_setNetworkVariableCommand(JNIEnv* env, 
                                                          jobject clazz, 
                                                          jint netId, 
                                                          jstring name, 
                                                          jstring value) 

     char cmdstr[ 256]; 
    jboolean isCopy; 

     const  char *nameStr = env->GetStringUTFChars(name,&isCopy); 
     const  char *valueStr = env->GetStringUTFChars(value,&isCopy); 

     if (nameStr == NULL || valueStr == NULL) 
         return JNI_FALSE; 

     int cmdTooLong = snprintf(cmdstr,  sizeof(cmdstr), "SET_NETWORK %d %s %s"
                 netId, nameStr, valueStr) >=( int) sizeof(cmdstr); 

    env->ReleaseStringUTFChars(name, nameStr); 
    env->ReleaseStringUTFChars(value, valueStr); 

     return (jboolean)!cmdTooLong && doBooleanCommand(cmdstr, "OK"); 


…… 

// ---------------------------------------------------------------------------- 

/* 
* JNI registration. 
*/ 
static JNINativeMethod gWifiMethods[] = { 
     /* name, signature, funcPtr */ 

    {  "loadDriver""()Z",  ( void*)android_net_wifi_loadDriver }, 
    {  "unloadDriver""()Z",  ( void*)android_net_wifi_unloadDriver }, 
    {  "startSupplicant""()Z",  ( void*)android_net_wifi_startSupplicant }, 
    {  "stopSupplicant""()Z",  ( void*)android_net_wifi_stopSupplicant }, 
    {  "connectToSupplicant""()Z",  ( void*)android_net_wifi_connectToSupplicant }, 
    {  "closeSupplicantConnection""()V",  ( void*)android_net_wifi_closeSupplicantConnection }, 

    {  "listNetworksCommand""()Ljava/lang/String;"
        ( void*) android_net_wifi_listNetworksCommand }, 
    {  "addNetworkCommand""()I", ( void*)android_net_wifi_addNetworkCommand }, 
    {  "setNetworkVariableCommand""(ILjava/lang/String;Ljava/lang/String;)Z"
        ( void*) android_net_wifi_setNetworkVariableCommand}, 
    {  "getNetworkVariableCommand""(ILjava/lang/String;)Ljava/lang/String;"
        ( void*) android_net_wifi_getNetworkVariableCommand}, 
…… 
 
看来这下终于找到干活儿的函数了,可喜可贺,于是我们得出结论:在Android中,要连上一个无线网络,首先需要添加一个网络,然后设置这个网络的配置,而这些步骤实际上都是通过向wpa_supplicant发送命令实现的。命令的格式例外的很简单,比如添加一个网络配置是ADD_NETWORK,设置一个网络配置是SET_NETWORK [netId] [varName] [var]。于是根据Java代码我写出了配置环节的代码。
int config_stage(){    
     // Add a network config to supplicant mode 
     int networkId = doIntCommand( "ADD_NETWORK");  // Add a new network id 
     if(networkId <  0) { 
        LOGE( "Failed to add a network configuration. %s",strerror(errno)); 
         return - 1
    } 

    LOGE( "Add a network %d",networkId); 

     // set the ssid of the destination wifi adhoc 
     char cmdstr[ 256]; 
    snprintf(cmdstr,  sizeof(cmdstr),  "SET_NETWORK %d %s %s",networkId, SSID_NAME, SSID); 
     if(!doBooleanCommand(cmdstr, "OK")) { 
        LOGE( "Failed to set network %d configuration ssid. %s", networkId, strerror(errno)); 
         return - 1
    } 
     return networkId; 

添加了一个配置的网络之后,选择某个网络配置的命令是SELECT_NETWORK [netId]。在SELECT_NETWORK之后,wpa_supplicant就尝试和该ssid的AP进行associate操作,在associate之后便调用dhcpd获取ip。SELECT_NETWORK是立即返回的函数,那么何以得知已经和无线AP连接上了呢?Android的界面里面连接/断开Wifi都是可以被接受到的事件,在WifiStateTracker中我们看到是由WifiMonitor来监听由wpa_supplicant发过来的消息的,于是我们来到framework/base/wifi/java/android/net/wifi/WifiMonitor.java,看到其中接受消息的Monitor线程
 
class MonitorThread  extends Thread { 
     public MonitorThread() { 
         super( "WifiMonitor"); 
    } 

     public  void run() { 

         if (connectToSupplicant()) { 
             // Send a message indicating that it is now possible to send commands 
             // to the supplicant 
            mWifiStateTracker. notifySupplicantConnection(); 
        }  else { 
            mWifiStateTracker. notifySupplicantLost(); 
             return
        } 

         //noinspection InfiniteLoopStatement 
         for (;;) { 
            String eventStr = WifiNative. waitForEvent(); 

             // Skip logging the common but mostly uninteresting scan-results event 
             if (Config. LOGD &&eventStr. indexOf(scanResultsEvent) == - 1) { 
                Log. v(TAG,  "Event [" + eventStr +  "]"); 
            } 
             if (!eventStr. startsWith(eventPrefix)) { 
                 if (eventStr. startsWith(wpaEventPrefix) && 
                         0 <eventStr. indexOf(passwordKeyMayBeIncorrectEvent)) { 
                    handlePasswordKeyMayBeIncorrect(); 
                } 
                 continue
            } 

            String eventName =eventStr. substring(eventPrefixLen); 
             int nameEnd = eventName. indexOf( ' '); 
             if (nameEnd != - 1
                eventName = eventName. substring( 0, nameEnd);
             if (eventName. length() ==  0) { 
                 if (Config. LOGD) Log. i(TAG,  "Received wpa_supplicant event with empty event name"); 
 
 
WifiNative.wairForEvent在wifi.c中对应的函数是wifi_wait_event(),用来阻塞的等待wpa_supplicant发来的消息。当wpa_supplicant连接上之后,便会发出一个CONTROL-EVENT-CONNECTED的消息,只要截获这个消息,我们便可以进行dhcp的操作了。
基于以上的考虑,我写了connect阶段的代码。这里我偷了一下懒,没有仔细的去分析过来的命令,只是匹配到有CONNECTED的命令,就当已经连接上了。

#define CONNECTED "CONNECTED" 
int connect_stage(int networkId{ 
    char cmdstr[256]; 
    // enable the network 
    snprintf(cmdstr, sizeof(cmdstr), "SELECT_NETWORK %d",networkId); 
    if(!doBooleanCommand(cmdstr,"OK")) { 
        LOGE("Failed to select network %d. %s", networkId, strerror(errno)); 
        return -1
    } 

    // wait for connect 
    char buf[256]; 
    while(1{ 
        int nread = wifi_wait_for_event(buf, sizeof(buf)); 
        if(nread > 0{ 
            LOGE("receive buf:\n %s\n",buf); 
            if(strstr(buf,CONNECTED> 0{ 
                break
            } 
            // XXX danger of not going out of the loop!!! 
        } 
        continue
    } 
    return 0
}

在WifiStateTracer中我还找到了负责dhcp的DhcpHandler,按照其中的函数写了dhcp阶段的代码

int   dhcp_stage (){  
     int   result
     in_addr_t   ipaddr ,   gateway ,   mask ,   dns1 ,   dns2 ,   server
     uint32_t   lease

     char   ifname [ 256 ];  
     char   mDns1Name [ 256 ];  
     char   mDns2Name [ 256 ];  
     property_get ( "wifi.interface" ,   ifname   , "eth0" ); 
     snprintf ( mDns1Name ,   sizeof ( mDns1Name ),   "net.%s.dns1" , ifname ); 
     snprintf ( mDns2Name ,   sizeof ( mDns2Name ),   "net.%s.dns2" , ifname ); 

     result   =   dhcp_do_request ( ifname ,   & ipaddr ,   & gateway ,   & mask ,   & dns1 , & dns2 ,   & server ,   & lease ); 
     if ( result   !=   0 {  
         LOGE ( "Failed to dhcp on interface %s. %s" ,   ifname ,   strerror ( errno )); 
         return   - 1
     }  
     struct   in_addr   dns_struct1 ,   dns_struct2
     dns_struct1 . s_addr   =   dns1
     dns_struct2 . s_addr   =   dns2
     property_set ( mDns1Name , inet_ntoa ( dns_struct1 )); 
     property_set ( mDns2Name , inet_ntoa ( dns_struct2 )); 
     return   0
}  



于是我用无线路由器建立了一个最简单的无线网络,ssid=TSever,没有密码,按照上面步骤进行联网。结果在ADD_NETWORK的步骤就出错了,无法获得网络id,这是怎么一回事呢?看到dhcp时的代码要对特定的Network Interface做操作,突然想起来之前在代码中似乎看到有对Interface做使能操作的代码——WifiService在调用mWifiStateTracker.enableNetwork之前,还调用了String ifname = mWifiStateTracker.getInterfaceName(); NetworkUtils.enableInterface(ifname);这两句。于是在init阶段,加上了下面这段。

    char ifname[256]; 
    property_get("wifi.interface", ifname ,"eth0"); 
    ret = ifc_enable(ifname); 
    if(ret < 0{ 
        LOGE("Failed to enable wifi interface %s. %s", ifname ,strerror(errno)); 
        return -1
    }

但是运行起来还是出同样的错误,不过这次busybox ifconfig之后可以发现eth0的端口了 ,至少没有做无用功。倒回去看在WifiMonitor的MonitorThread中有connectToSupplicant()的调用,本以为此调用不是非常关键,然后去hardware/libhardware_legacy/wifi/wifi.c文件里面看到全局有一个static struct wpa_ctrl *ctrl_conn;并且在wifi_command中需要用到这个ctrl_conn,那么如果这个ctrl_conn是NULL的话,所有的wifi_command命令自然都无法完成了,于是又加上了

ret   =   wifi_connect_to_supplicant (); 
     if ( ret   <   0 {  
         LOGE ( "Failed to connect supplicant daemon. %s" , strerror ( errno )); 
         return   - 1
     }  

这样编译完运行之后,果然顺利的分配到id了。运行一下程序,还是出现了错误,输出的log如下。

I/wpa_supplicant(  618): CTRL-EVENT-SCAN-RESULTS  Ready
I/wpa_supplicant(  618): Trying to associate with 00:24:b2:c1:16:b6 (SSID='TServer' freq=2462 MHz)
I/wpa_supplicant(  618): CTRL-EVENT-STATE-CHANGE id=-1 state=3
E/        (  612): Finished init stage.
E/        (  612): Add a network 2
E/        (  612): Finished config stage.
I/wpa_supplicant(  618): CTRL-EVENT-STATE-CHANGE id=1 state=0
E/        (  612): receive buf:
E/        (  612):  CTRL-EVENT-STATE-CHANGE id=1 state=0
I/wpa_supplicant(  618): CTRL-EVENT-STATE-CHANGE id=-1 state=2
E/        (  612): receive buf:
E/        (  612):  CTRL-EVENT-STATE-CHANGE id=-1 state=2
I/wpa_supplicant(  618): CTRL-EVENT-SCAN-RESULTS  Ready
E/        (  612): receive buf:
E/        (  612):  CTRL-EVENT-SCAN-RESULTS  Ready
I/wpa_supplicant(  618): CTRL-EVENT-STATE-CHANGE id=-1 state=4
E/        (  612): receive buf:
E/        (  612):  CTRL-EVENT-STATE-CHANGE id=-1 state=4
I/wpa_supplicant(  618): Associated with 00:24:b2:c1:16:b6
E/        (  612): receive buf:
E/        (  612):  Associated with 00:24:b2:c1:16:b6
I/wpa_supplicant(  618): CTRL-EVENT-SCAN-RESULTS  Ready
E/        (  612): receive buf:
E/        (  612):  CTRL-EVENT-SCAN-RESULTS  Ready
D/NetworkLocationProvider(  127): onCellLocationChanged [4517,30785]
I/wpa_supplicant(  618): Authentication with 00:24:b2:c1:16:b6 timed out.
E/        (  612): receive buf:
E/        (  612):  Authentication with 00:24:b2:c1:16:b6 timed out.

 
注意到红色的那句log,我的wifi并没有设置密码,为什么还需要Authentication呢?想一想是不是wpa_supplicant的限制,于是去网上搜了到了这篇 wpa_supplicant 工具使用,发现没有密码的情况下应该是这样的配置
# 明文连接方式(不使用WPA和IEEE802.1X)
network={
  ssid="plaintext-test"
  key_mgmt=NONE
}
于是在config阶段加上了对key_mgmt的设置,这下就连上了Wifi了,正确的log如下

bash-4.1# wificonnect
I/wpa_supplicant(  871): CTRL-EVENT-SCAN-RESULTS  Ready
I/wpa_supplicant(  871): Trying to associate with 00:24:b2:c1:16:b6 (SSID='TServer' freq=2462 MHz)
I/wpa_supplicant(  871): CTRL-EVENT-STATE-CHANGE id=-1 state=3
E/        (  865): Finished init stage.
E/        (  865): Add a network 2
E/        (  865): Finished config stage.
I/wpa_supplicant(  871): CTRL-EVENT-STATE-CHANGE id=1 state=0
E/        (  865): receive buf:
E/        (  865):  CTRL-EVENT-STATE-CHANGE id=1 state=0
I/wpa_supplicant(  871): CTRL-EVENT-STATE-CHANGE id=-1 state=2
E/        (  865): receive buf:
E/        (  865):  CTRL-EVENT-STATE-CHANGE id=-1 state=2
I/wpa_supplicant(  871): CTRL-EVENT-SCAN-RESULTS  Ready
E/        (  865): receive buf:
E/        (  865):  CTRL-EVENT-SCAN-RESULTS  Ready
I/wpa_supplicant(  871): Trying to associate with 00:24:b2:c1:16:b6 (SSID='TServer' freq=2462 MHz)
E/        (  865): receive buf:
E/        (  865):  Trying to associate with 00:24:b2:c1:16:b6 (SSID='TServer' freq=2462 MHz)
I/wpa_supplicant(  871): CTRL-EVENT-STATE-CHANGE id=-1 state=3
E/        (  865): receive buf:
E/        (  865):  CTRL-EVENT-STATE-CHANGE id=-1 state=3
I/wpa_supplicant(  871): CTRL-EVENT-STATE-CHANGE id=2 state=4
E/        (  865): receive buf:
E/        (  865):  CTRL-EVENT-STATE-CHANGE id=2 state=4
I/wpa_supplicant(  871): Associated with 00:24:b2:c1:16:b6
I/wpa_supplicant(  871): CTRL-EVENT-STATE-CHANGE id=2 state=7
I/wpa_supplicant(  871): CTRL-EVENT-CONNECTED - Connection to 00:24:b2:c1:16:b6 completed (auth) [id=2 id_str=]
E/        (  865): receive buf:
E/        (  865):  Associated with 00:24:b2:c1:16:b6
E/        (  865): receive buf:
E/        (  865):  CTRL-EVENT-STATE-CHANGE id=2 state=7
E/        (  865): receive buf:
E/        (  865):  CTRL-EVENT-CONNECTED - Connection to 00:24:b2:c1:16:b6 completed (auth) [id=2 id_str=]
E/        (  865): Finished dhcp stage.

 
Android下实现非启动界面Wifi连接就算是完成了,以上工作是在界面启动并且Wifi设置关闭的情况下进行的,但和在没有界面的时候应该差别不大,下面是程序的源代码
/* 
** 
** Copyright 2006, The Android Open Source Project 
** 
** Licensed under the Apache License, Version 2.0 (the "License"); 
** you may not use this file except in compliance with the License. 
** You may obtain a copy of the License at 
** 
**     http://www.apache.org/licenses/LICENSE-2.0 
** 
** Unless required by applicable law or agreed to in writing, software 
** distributed under the License is distributed on an "AS IS" BASIS, 
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
** See the License for the specific language governing permissions and 
** limitations under the License. 
*/ 

#include <stdio.h> 
#include <stdlib.h> 
#include <unistd.h> 
#include <errno.h> 
#include "string.h" 
#include "hardware_legacy/wifi.h" 
#include "cutils/log.h" 
#include "cutils/properties.h" 
#include <arpa/inet.h> 
#include <netutils/ifc.h> 
#include <netutils/dhcp.h> 

static int doCommand(const char *cmd, char *replybuf, int replybuflen) 
{ 
    size_t reply_len = replybuflen - 1; 

    if (wifi_command(cmd, replybuf, &reply_len) != 0) 
        return -1; 
    else { 
        // Strip off trailing newline 
        if (reply_len > 0 && replybuf[reply_len-1] == '\n') 
            replybuf[reply_len-1] = '\0'; 
        else 
            replybuf[reply_len] = '\0'; 
        return 0; 
    } 
} 

static int doIntCommand(const char *cmd) 
{ 
    char reply[256]; 

    if (doCommand(cmd, reply, sizeof(reply)) != 0) { 
        return -1; 
    } else { 
        return atoi(reply); 
    } 
} 

static int doBooleanCommand(const char *cmd, const char *expect) 
{ 
    char reply[256]; 

    if (doCommand(cmd, reply, sizeof(reply)) != 0) { 
        return 0; 
    } else { 
        return (strcmp(reply, expect) == 0); 
    } 
} 

// Send a command to the supplicant, and return the reply as a String 
static char* doStringCommand(const char *cmd) 
{ 
    char reply[4096]; 

    if (doCommand(cmd, reply, sizeof(reply)) != 0) { 
        return NULL; 
    } else { 
        return reply; 
    } 
} 

int init_stage() { 
    // load the wifi driver: insmod .ko 
    int ret = wifi_load_driver(); 
    if(ret < 0) { 
        LOGE("Failed to load Wi-Fi driver. %s",strerror(errno)); 
        return -1; 
    } 

    // start wpa_supplicant 
    ret =  wifi_start_supplicant(); 
    if(ret < 0) { 
        LOGE("Failed to start supplicant daemon. %s",strerror(errno)); 
        return -1; 
    } 

    ret = wifi_connect_to_supplicant(); 
    if(ret < 0) { 
        LOGE("Failed to connect supplicant daemon. %s",strerror(errno)); 
        return -1; 
    } 

    char ifname[256]; 
    property_get("wifi.interface", ifname ,"eth0"); 
    ret = ifc_enable(ifname); 
    if(ret < 0) { 
        LOGE("Failed to enable wifi interface %s. %s", ifname ,strerror(errno)); 
        return -1; 
    } 

    return 0; 
} 

int scan_stage(){ 
    // XXX we don't need to really scan the wifi 
    return 0; 
} 

#define SSID_NAME "ssid" 
#define KEY_MGMT "key_mgmt" 
#define SSID "\"xxxxx\"" 
#define PASS "NONE" 

int config_stage(){    
    // Add a network config to supplicant mode 
    int networkId = doIntCommand("ADD_NETWORK"); // Add a new network id 
    if(networkId < 0) { 
        LOGE("Failed to add a network configuration. %s",strerror(errno)); 
        return -1; 
    } 

    LOGE("Add a network %d",networkId); 

    // set the ssid of the destination wifi adhoc 
    char cmdstr[256]; 
    snprintf(cmdstr, sizeof(cmdstr), "SET_NETWORK %d %s %s",networkId, SSID_NAME, SSID); 
    if(!doBooleanCommand(cmdstr,"OK")) { 
        LOGE("Failed to set network %d configuration ssid. %s", networkId, strerror(errno)); 
        return -1; 
    } 
    snprintf(cmdstr,  sizeof(cmdstr),  "SET_NETWORK %d %s %s",networkId, KEY_MGMT ,PASS); 
     if(!doBooleanCommand(cmdstr, "OK")) { 
        LOGE( "Failed to set network %d configuration key_mgmr. %s", networkId, strerror(errno)); 
         return - 1
    } 


     return networkId; 


#define CONNECTED "CONNECTED" 
int connect_stage( int networkId) { 
     char cmdstr[ 256]; 
     // enable the network 
    snprintf(cmdstr,  sizeof(cmdstr),  "SELECT_NETWORK %d",networkId); 
     if(!doBooleanCommand(cmdstr, "OK")) { 
        LOGE( "Failed to select network %d. %s", networkId,strerror(errno)); 
         return - 1
    } 

     // wait for connect 
     char buf[ 256]; 
     while( 1) { 
         int nread = wifi_wait_for_event(buf,  sizeof(buf)); 
         if(nread >  0) { 
            LOGE( "receive buf: \n  %s \n ",buf); 
             if(strstr(buf,CONNECTED) >  0) { 
                 break
            } 
             // XXX danger of not going out of the loop!!! 
        } 
         continue
    } 
     return  0


int dhcp_stage(){ 
     int result; 
    in_addr_t ipaddr, gateway, mask, dns1, dns2, server; 
     uint32_t lease; 

     char ifname[ 256]; 
     char mDns1Name[ 256]; 
     char mDns2Name[ 256]; 
    property_get( "wifi.interface", ifname , "eth0"); 
    snprintf(mDns1Name,  sizeof(mDns1Name), "net.%s.dns1",ifname); 
    snprintf(mDns2Name,  sizeof(mDns2Name), "net.%s.dns2",ifname); 

    result = dhcp_do_request(ifname, &ipaddr, &gateway,&mask, &dns1, &dns2, &server, &lease); 
     if(result !=  0) { 
        LOGE( "Failed to dhcp on interface %s. %s", ifname,strerror(errno)); 
         return - 1
    } 
     struct in_addr dns_struct1, dns_struct2; 
    dns_struct1.s_addr = dns1; 
    dns_struct2.s_addr = dns2; 
    property_set(mDns1Name,inet_ntoa(dns_struct1)); 
    property_set(mDns2Name,inet_ntoa(dns_struct2)); 
     return  0


int main( int argc,  char *argv[]) 

     int ret = init_stage(); 
     if(ret <  0) { 
        LOGE( "Failed init stage. %s",strerror(errno)); 
        exit(- 1); 
    } 

    LOGE( "Finished init stage."); 

    ret = config_stage(); 
     if(ret <  0) { 
        LOGE( "Failed config stage. %s",strerror(errno)); 
        exit(- 1); 
    } 
    LOGE( "Finished config stage."); 

    ret = connect_stage(ret); 
     if(ret <  0) { 
        LOGE( "Failed connect stage. %s",strerror(errno)); 
        exit(- 1); 
    } 
    LOGV( "Finished connect stage."); 

    ret = dhcp_stage(); 
     if(ret <  0) { 
        LOGE( "Failed dhcp stage. %s",strerror(errno)); 
        exit(- 1); 
    } 
    LOGE( "Finished dhcp stage."); 
     return  0
相关文章
|
2月前
|
Android开发
如何用Airtest脚本无线连接Android设备?
如何用Airtest脚本无线连接Android设备?
|
2月前
|
存储 网络安全 Python
WiFi连接树莓派【2月更文挑战第2天】
要通过WiFi连接树莓派并显示其摄像头视频信息,您需要按照以下步骤操作: 1. 确保您的电脑和树莓派已连接到同一个WiFi网络。 2. 安装Python和必要的库:在您的电脑上安装Python 3.6或更高版本,并安装pip(Python包管理器)。然后,通过pip安装以下库: 【2月更文挑战第2天】
24 1
|
3月前
|
Android开发 Python
Python封装ADB获取Android设备wifi地址的方法
Python封装ADB获取Android设备wifi地址的方法
61 0
|
3月前
|
定位技术 Android开发
uniapp获取wifi连接状态
uniapp获取wifi连接状态
122 0
|
4月前
|
XML API Android开发
Android WIFI使用简述(上)
Android WIFI使用简述(上)
|
3天前
|
运维 网络协议 Linux
Android 双网卡配置为连接到Android主机的PC提供外网访问(1)
Android 双网卡配置为连接到Android主机的PC提供外网访问(1)
16 0
|
1月前
|
前端开发 安全 数据安全/隐私保护
WPA_CLI 的介绍:介绍如何使用wpa_cli连接WiFi的方法
WPA_CLI 的介绍:介绍如何使用wpa_cli连接WiFi的方法
50 2
|
4月前
|
XML 物联网 API
Android Ble蓝牙App(二)连接与发现服务
Android Ble蓝牙App(二)连接与发现服务
|
4月前
|
API 数据安全/隐私保护 Android开发
Android WIFI使用简述(下)
Android WIFI使用简述(下)
|
4月前
|
Android开发 网络架构
【Android App】检查手机连接WiFi信息以及扫描周围WiFi的讲解及实战(附源码和演示 超详细必看)
【Android App】检查手机连接WiFi信息以及扫描周围WiFi的讲解及实战(附源码和演示 超详细必看)
204 1