




版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領
文檔簡介
1、.Android下實現(xiàn)非啟動界面Wifi連接Android的網絡功能和一般的linux并無太大的區(qū)別,我原來以為在Android上連接網絡和普通的linux連接網絡沒有很大區(qū)別,事實上區(qū)別還是有一些的。由于工程的需要,我的目的是在Android的界面沒有啟動之前連接wifi,于是本來的期待是直接在init.rc中參加一些腳本調用即可,但研究了一會兒發(fā)現(xiàn)沒有那么簡單。首先要感謝anly_junbaidu貼吧的幾篇博文,從 :/hi.baidu /anly_jun/blog/item/8ecb92d593d144cf50da4b6e.html開場,一共有七篇關于Android Wifi模塊分析,其
2、中有大量博主自己使用UML工具畫的調用講解,對于理解Android Wifi工作機制還是有很用的。其中最重要的是下面這幾幅圖。轉自 :/hi.baidu /anly_jun/blog/item/65e26e117e09ebcda6ef3f56.html在想要對wifi硬件動作之前,需要做兩件事情,一是要load wifi的driver,而是要翻開wpa_supplicant,其實假設是連接沒有加密的wifi,沒有必要翻開wpa_supplicant,但是為了講問題化為熟知的問題,此處還是先按照提示調用wifi_load_driver和wifi_start_supplicant按照上面的提示寫出
3、來的初始化代碼如下:int init_stage / load the wifi driver: insmod .ko int ret = wifi_load_driver; ifret < 0 LOGE"Failed to load Wi-Fi driver. %s",strerrorerrno;
4、 return -1; / start wpa_supplicant ret = wifi_start_supplicant; ifret < 0 LOGE"Failed to start supplicant daemon. %s",strerrorerrno;
5、; return -1; return 0; 接下來,便是連接的過程了,經過上面的步驟,wifi的driver已經載入,wpa_supplicant也已經翻開,那咱們就可以開場連接無線了吧。后來證實這是錯誤的,因為anly_jun的這篇Android wifi分析的粒度只在Java層面的函數(shù)級別,因此有一些細節(jié)并沒有提到。在下面我會提到這些細節(jié)。按照一般的linux中連接wifi的步驟,這時候就可以直接調用一個程序來連接某個ssid的無線網絡,然后調用dhcpd來分配ip了,我之前在
6、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 netw
7、orkId 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, *
8、 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
9、 code -1 on failure. */ public int addNetworkWifiConfiguration config if config = null return -1; workId = -1; return addOrUpdateNetworkconfig; /* * Internal method for doing the RP
10、C 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 f
11、ailure. */ private int addOrUpdateNetworkWifiConfiguration config try return mService.addOrUpdateNetworkconfig; catch RemoteException e return -1; 看其中確實是調用了s
12、ervice的函數(shù),于是又在frameworks/base/services/java/com/android/server/WifiService.java中找到了有關增加一個網絡配置的相關代碼/* * see link .wifi.WifiManager#addOrUpdateNetworkWifiConfiguration * return the supplicant-assigned identifier for the new or updated * network if the operation succeeds, or code -1 if it fails */ publ
13、ic int addOrUpdateNetworkWifiConfiguration config enforceChangePermission; /* * If the supplied networkId is -1, we create a new empty * network configuration. Otherwise, the networkId should
14、160; * refer to an existing configuration. */ int netId = workId; boolean newNetwork = netId = -1; boolean doReconfig = false; / networkId of -1 means we want to create a new network
15、0; synchronized mWifiStateTracker if newNetwork netId = mWifiStateTracker.addNetwork; if netId < 0
16、60; if DBG Slog.dTAG, "Failed to add a network!"
17、 return -1; doReconfig = true;
18、160; mNeedReconfig = mNeedReconfig | doReconfig; setVariables: /* * Note that if a networkId for a non-
19、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
20、 ID up front. */ if config.SSID != null && !mWifiStateTracker.setNetworkVariable
21、0; netId, WifiConfiguration.ssidVarName,
22、 config.SSID if DBG Slog.dTAG, "failed to set SSID: "+config.SSID;
23、; break setVariables; if config.BSSID != null &&
24、 !mWifiStateTracker.setNetworkVariable netId, &
25、#160; WifiConfiguration.bssidVarName, config.BSSID if DBG
26、 Slog.dTAG, "failed to set BSSID: "+config.BSSID; break setVariables;
27、160; 我們來到frameworks/base/wifi/java/android/net/wifi/WifiStateTracker.java中,看看addNetwork和setNetworkVariable函數(shù)都是什么樣的。/* * Add a network * * return network id of the new network */ public synchronized int addNetwork if mWifiState.get != WIFI_STATE_ENABLED
28、0; 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, c
29、ode false otherwise */ public synchronized boolean setNetworkVariableint netId, String name, String value if mWifiState.get != WIFI_STATE_ENABLED return false; return WifiNative.setNetworkVariableComm
30、andnetId, name, value; 似乎WifiNative就應該是最終boss了,于是來到frameworks/base/wifi/java/android/net/wifi/WifiNative.java中一看,這兩個函數(shù)都是這么定義的。 public native static int addNetworkCommand; public native static boolean setNetworkVariableCommandint netId, String name, String value; 熟悉Android NDK開發(fā)的朋友都應該知道,這里
31、的函數(shù)都是通過jni鏈接到c/c+代碼中,于是我們略微花了一點時間,找到了這個函數(shù)所對應的frameworks/base/core/jni/android_net_wifi_Wifi.cpp static int doCommandconst char *cmd, char *replybuf, int replybuflen size_t reply_len = replybuflen - 1; if :wifi_commandcmd, replybuf, &reply_len != 0
32、; return -1; else / Strip off trailing newline if reply_len > 0 && replybufreply_len-1 = 'n'
33、; replybufreply_len-1 = 'message' else replybufreply_len = 'message' return 0; static jint doI
34、ntCommandconst char *cmd char reply256; if doCommandcmd, reply, sizeofreply != 0 return jint-1; else return jintatoireply; static jboolean
35、 doBooleanCommandconst char *cmd, const char *expect char reply256; if doCommandcmd, reply, sizeofreply != 0 return jbooleanJNI_FALSE; else return jbooleanst
36、rcmpreply, expect = 0; static jint android_net_wifi_addNetworkCommandJNIEnv* env, jobject clazz return doIntCommand"ADD_NETWORK" static jboolean android_net_wifi_setNetworkVariableCommandJNIEnv* env,
37、0; job
38、ject clazz,
39、 jint netId,
40、; jstring name,
41、160; jstring value char cmdstr256; jboolean isCopy; const char
42、*nameStr = env->GetStringUTFCharsname, &isCopy; const char *valueStr = env->GetStringUTFCharsvalue, &isCopy; if nameStr = NULL | valueStr = NULL return JNI_FALSE; int cmdTooLong = snprin
43、tfcmdstr, sizeofcmdstr, "SET_NETWORK %d %s %s", netId, nameStr, valueStr >= intsizeofcmdstr; env->ReleaseStringUTFCharsname, nameStr; env->Release
44、StringUTFCharsvalue, valueStr; return jboolean!cmdTooLong && doBooleanCommandcmdstr, "OK" / - /* * JNI registration. */ static JNINativeMethod gWifiMethods = /* name, signature, funcPtr */ "loadDriver", "Z"
45、;, void *android_net_wifi_loadDriver , "unloadDriver", "Z", void *android_net_wifi_unloadDriver , "startSupplicant", "Z", void *android_net_wifi_startSupplicant , "stopSupplicant&q
46、uot;, "Z", void *android_net_wifi_stopSupplicant , "connectToSupplicant", "Z", void *android_net_wifi_connectToSupplicant , "closeSupplicantConnection", "V", void *android_net_wifi_closeSupplica
47、ntConnection , "listNetworksCommand", "Ljava/lang/String;", void* android_net_wifi_listNetworksCommand , "addNetworkCommand", "I", void* android_net_wifi_addNetworkCommand ,
48、; "setNetworkVariableCommand", "ILjava/lang/String;Ljava/lang/String;Z", void* android_net_wifi_setNetworkVariableCommand , "getNetworkVariableCommand", "ILjava/lang/String;Ljava/lang/String;
49、", void* android_net_wifi_getNetworkVariableCommand , 看來這下終于找到干活兒的函數(shù)了,可喜可賀,于是我們得出結論:在Android中,要連上一個無線網絡,首先需要添加一個網絡,然后設置這個網絡的配置,而這些步驟實際上都是通過向wpa_supplicant發(fā)送命令實現(xiàn)的。命令的格式例外的很簡單,比方添加一個網絡配置是ADD_NETWORK,設置一個網絡配置是SET_NETWORK netId varName var。于是根據(jù)Java代碼
50、我寫出了配置環(huán)節(jié)的代碼。int config_stage / Add a network config to supplicant mode int networkId = doIntCommand"ADD_NETWORK" / Add a new network id ifnetworkId < 0 LOGE"Failed
51、to add a network configuration. %s",strerrorerrno; return -1; LOGE"Add a network %d",networkId; / set the ssid of the destination wifi adhoc char cmdstr256;
52、60; snprintfcmdstr, sizeofcmdstr, "SET_NETWORK %d %s %s",networkId, SSID_NAME, SSID; if!doBooleanCommandcmdstr,"OK" LOGE"Failed to set network %d configuration ssid. %s", networkId, strerrorerrno;
53、; return -1; return networkId; 添加了一個配置的網絡之后,選擇某個網絡配置的命令是SELECT_NETWORK netId。在SELECT_NETWORK之后,wpa_supplicant就嘗試和該ssid的AP進行associate操作,在associate之后便調用dhcpd獲取ip。SELECT_NETWORK是立即返回的函數(shù),那么何以得知已經和無線AP連接上了呢?Android的界面里面連接/斷開Wifi都是可
54、以被承受到的事件,在WifiStateTracker中我們看到是由WifiMonitor來監(jiān)聽由wpa_supplicant發(fā)過來的消息的,于是我們來到framework/base/wifi/java/android/net/wifi/WifiMonitor.java,看到其中承受消息的Monitor線程 class MonitorThread extends Thread public MonitorThread super"WifiMonitor&
55、quot; public void run if connectToSupplicant / Send a message indicating that it is now possible to send commands
56、160; / to the supplicant mWifiStateTracker.notifySupplicantConnection; else mWifiStateTracker.notif
57、ySupplicantLost; return; /noinspection InfiniteLoopStatement for ; &
58、#160; String eventStr = WifiNative.waitForEvent; / Skip logging the common but mostly uninteresting scan-results event if Config.LOGD &&am
59、p; eventStr.indexOfscanResultsEvent = -1 Log.vTAG, "Event " + eventStr + ""
60、0; if !eventStr.startsWitheventPrefix if eventStr.startsWithwpaEventPrefix &&
61、60; 0 < eventStr.indexOfpasswordKeyMayBeIncorrectEvent handlePasswordKeyMayBeIncorrect;
62、0; continue; String eventName = eventStr.substringe
63、ventPrefixLen; int nameEnd = eventName.indexOf' ' if nameEnd != -1
64、eventName = eventName.substring0, nameEnd; if eventName.length = 0 if Config.LOGD Log.iTAG, "Received wpa_supplicant event with empty eve
65、nt name" WifiNative.wairForEvent在wifi.c中對應的函數(shù)是wifi_wait_event,用來阻塞的等待wpa_supplicant發(fā)來的消息。當wpa_supplicant連接上之后,便會發(fā)出一個CONTROL-EVENT-CONNECTED的消息,只要截獲這個消息,我們便可以進行dhcp的操作了。基于以上的考慮,我寫了connect階段的代碼。這里我偷了一下懶,沒有仔細的去分析過來的命令,只是匹配到有CONNECTED的命令,就當已經連接上了。#define CONNECTED "CONNECTED"
66、int connect_stageint networkId char cmdstr256; / enable the network snprintfcmdstr, sizeofcmdstr, "SELECT_NETWORK %d",networkId; if!doBooleanCommandcmdstr,"OK" L
67、OGE"Failed to select network %d. %s", networkId, strerrorerrno; return -1; / wait for connect char buf256; while1 int nread = wi
68、fi_wait_for_eventbuf, sizeofbuf; ifnread > 0 LOGE"receive buf:n %sn",buf; ifstrstrbuf,CONNECTED > 0 &
69、#160; break; / XXX danger of not going out of the loop!
70、160; continue; return 0; 在WifiStateTracer中我還找到了負責dhcp的DhcpHandler,按照其中的函數(shù)寫了dhcp階段的代碼int dhcp_stage int result; in_addr_t ipaddr, gateway, mask, dns1, dns2, server;
71、; uint32_t lease; char ifname256; char mDns1Name256; char mDns2Name256; property_get"erface", ifname ,"eth0" snprintfmDns1Name, sizeofmDns1Name, "net.%s.dns1",if
72、name; snprintfmDns2Name, sizeofmDns2Name, "net.%s.dns2",ifname; result = dhcp_do_requestifname, &ipaddr, &gateway, &mask, &dns1, &dns2, &server, &lease; ifresult != 0
73、160; LOGE"Failed to dhcp on interface %s. %s", ifname, strerrorerrno; return -1; struct in_addr dns_struct1, dns_struct2; dns_struct1.s_addr = dns1; dns_struct2.s_addr = d
74、ns2; property_setmDns1Name,inet_ntoadns_struct1; property_setmDns2Name,inet_ntoadns_struct2; return 0; 于是我用無線路由器建立了一個最簡單的無線網絡,ssid=TSever,沒有密碼,按照上面步驟進展聯(lián)網。結果在ADD_NETWORK的步驟就出錯了,無法獲得網絡id,這是怎么一回事呢?看到dhcp時的代碼要對特定的Network Interface做操作,突然想起來之前在代碼中似乎
75、看到有對Interface做使能操作的代碼WifiService在調用mWifiStateTracker.enableNetwork之前,還調用了String ifname = mWifiStateTracker.getInterfaceName; NetworkUtils.enableInterfaceifname;這兩句。于是在init階段,加上了下面這段。 char ifname256; property_get"erface", ifname ,"eth0"
76、 ret = ifc_enableifname; ifret < 0 LOGE"Failed to enable wifi interface %s. %s", ifname ,strerrorerrno; return -1; 但是運行起來還是出同樣的錯誤,不過這次busybox if
77、config之后可以發(fā)現(xiàn)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_suppl
78、icant; ifret < 0 LOGE"Failed to connect supplicant daemon. %s",strerrorerrno; return -1; 這樣編譯完運行之后,果然順利的分配到id了。運行一下程序,還是出現(xiàn)了錯誤,輸出的log如下。I/wpa_supplicant 618:
79、CTRL-EVENT-SCAN-RESULTS ReadyI/wpa_supplicant 618: Trying to associate with 00:24:b2:c1:16:b6 SSID='TServer' freq=2462 MHzI/wpa_supplicant 618: CTRL-EVENT-STATE-CHANGE id=-1 state=3E/ 612: Finished init stage.E/
80、160; 612: Add a network 2E/ 612: Finished config stage.I/wpa_supplicant 618: CTRL-EVENT-STATE-CHANGE id=1 state=0E/ 612: receive buf:E/
81、 612: CTRL-EVENT-STATE-CHANGE id=1 state=0I/wpa_supplicant 618: CTRL-EVENT-STATE-CHANGE id=-1 state=2E/ 612: receive buf:E/ 612: CTRL-EVENT-STATE-CHANGE id=-1 state=
82、2I/wpa_supplicant 618: CTRL-EVENT-SCAN-RESULTS ReadyE/ 612: receive buf:E/ 612: CTRL-EVENT-SCAN-RESULTS ReadyI/wpa_supplicant 618: CTRL-EVENT-STATE-CHANGE id=-1 state=4E/
83、160; 612: receive buf:E/ 612: CTRL-EVENT-STATE-CHANGE id=-1 state=4I/wpa_supplicant 618: Associated with 00:24:b2:c1:16:b6E/ 612: receive buf:E/
84、60; 612: Associated with 00:24:b2:c1:16:b6I/wpa_supplicant 618: CTRL-EVENT-SCAN-RESULTS ReadyE/ 612: receive buf:E/ 612: CTRL-EVENT-SCA
85、N-RESULTS ReadyD/NetworkLocationProvider 127: onCellLocationChanged 4517,30785I/wpa_supplicant 618: Authentication with 00:24:b2:c1:16:b6 timed out.E/ 612: receive buf:E/ 612: Au
86、thentication with 00:24:b2:c1:16:b6 timed out. 注意到紅色的那句log,我的wifi并沒有設置密碼,為什么還需要Authentication呢?想一想是不是wpa_supplicant的限制,于是去網上搜了到了這篇wpa_supplicant 工具使用,發(fā)現(xiàn)沒有密碼的情況下應該是這樣的配置# 明文連接方式不使用WPA和IEEE802.1Xnetwork= ssid="plaintext-test" key_mgmt=NONE于是在config階段加上了對key_mgmt的設置,這下就連上了Wifi了,正確的log如下b
87、ash-4.1# wificonnectI/wpa_supplicant 871: CTRL-EVENT-SCAN-RESULTS ReadyI/wpa_supplicant 871: Trying to associate with 00:24:b2:c1:16:b6 SSID='TServer' freq=2462 MHzI/wpa_supplicant 871: CTRL-EVENT-STATE-CHANGE id=-1 state=3E/
88、 865: Finished init stage.E/ 865: Add a network 2E/ 865: Finished config stage.I/wpa_supplicant 871: CTRL-EVENT-STATE-CHANGE id=1 state=0E/ 865: receive buf:E/ 865: CTRL-EVENT-STATE-CHANGE id=1 state=0I/wpa_supplicant 871: CTRL-EVENT-STATE-CHANGE id=-1 state=2E/
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
- 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
- 6. 下載文件中如有侵權或不適當內容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 度農業(yè)供應鏈合同:農副產品
- 度工程借款合同范本
- 設計公司內部培訓合同樣本
- 標準勞動合同模板合同
- 委托代理合同(公民類)范本
- 飾品定制合同范本
- 短期租賃合同格式
- 地下車庫車位承包合同轉讓協(xié)議
- 設備定期保養(yǎng)合同范文
- 大學生創(chuàng)新創(chuàng)業(yè)項目合同
- 大學生職業(yè)素養(yǎng)訓練(第六版)課件 第二單元學習職業(yè)禮儀
- 腦卒中-腦卒中的康復治療
- 2024至2030年中國超聲波加工機床行業(yè)深度調研及發(fā)展預測報告
- 疫情統(tǒng)計學智慧樹知到答案2024年浙江大學
- 三方資金轉換協(xié)議書范本
- 2024年對口升學真題模擬語文試卷及答案十四
- 2024年積分制管理實施方案及細則
- CJJ6-2009 城鎮(zhèn)排水管道維護安全技術規(guī)程
- 新媒體營銷:營銷方式+推廣技巧+案例實訓 微課版 第2版 思考與練習參考答案
- 2024年04月國家藥品監(jiān)督管理局藥品審評檢查長三角分中心招考聘用筆試筆試歷年典型考題及考點研判與答案解析
- 《互聯(lián)網金融》教案
評論
0/150
提交評論