1. 程式人生 > >wifi連線流程分析

wifi連線流程分析

Wifi 連線部分

當用戶選擇一個AP時會彈出一個AP引數配置對話方塊,此對話方塊會顯示當前選擇的AP訊號強度,若此AP設定了密碼則需要使用者輸入密碼才能登入。WifiSettings中的onPreferenceTreeClick會被呼叫@Override

publicbooleanonPreferenceTreeClick(PreferenceScreenscreen,Preferencepreference){

//點選AP響應函式

if(preferenceinstanceofAccessPoint){

mSelected=(AccessPoint)preference;

showDialog(

mSelected,false);

}elseif(preference==mAddNetwork){

mSelected=null;

showDialog(null,true);

}elseif(preference==mNotifyOpenNetworks){

Secure.putInt(getContentResolver(),

Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON,

mNotifyOpenNetworks.isChecked()?1:0);

}else{

returnsuper.onPreferenceTreeClick(screen,

preference);

}

returntrue;

}

使用者配置好之後點選連線按鈕,onClick函式會被呼叫。

publicvoidonClick(DialogInterfacedialogInterface,intbutton){

//點選連線按鈕的響應函式

if(button==WifiDialog.BUTTON_FORGET&&mSelected!=null){

forget(mSelected.networkId);

}elseif(button==WifiDialog.BUTTON_SUBMIT&&mDialog!=null){

WifiConfigurationconfig=mDialog.getConfig();

if(config==null){

if(mSelected!=null&&!requireKeyStore(mSelected.getConfig())){

connect(mSelected.networkId);

}

}elseif(config.networkId!=-1){

if(mSelected!=null){

mWifiManager.updateNetwork(config);

saveNetworks();

}

}else{

intnetworkId=mWifiManager.addNetwork(config);

if(networkId!=-1){

mWifiManager.enableNetwork(networkId,false);

config.networkId=networkId;

if(mDialog.edit||requireKeyStore(config)){

saveNetworks();

}else{

connect(networkId);

}

}

}

}

連線請求部分

一.Settings的connect函式響應連線,更新網路儲存配置,更新設定當前選擇的優先順序最高,並

儲存。然後通過enableNetwork使得其他網路不可用來進行連線。最後呼叫WifiManager的

reconnect函式連線當前選擇的網路。

二.WifiManager的reconnect函式通過AIDL的Binder機制,呼叫WifiService的reconnect函式

三.然後會呼叫 WifiStateTracker的reconnectCommand函式,通過JNI(android_net_wifi_Wifi)的

android_net_wifi_reconnectCommand 函式向WPA_WPASUPPLICANT傳送 RECONNECT命令。

四. android_net_wifi_Wifi通過doCommand(命令名,響應緩衝,響應快取大小)呼叫wifi.c中的

wifi_command函式來發送命令。

五.最後通過 wpa_ctrl的wpa_ctrl_request函式向控制通道傳送連線命令。

返回請求部分

六.當連線上之後WPA_SUPPLICANT會向控制通道傳送連線成功命令。wifi.c的

wifi_wait_for_event函式阻塞呼叫並返回這個命令的字串(CONNECTED).

七.而後WifiMonitor會被執行來處理這個事件,WifiMonitor 再呼叫 WifiStateTracker的

notifyStateChange,WifiStateTracker 則接著會往自身傳送 EVENT_DHCP_START 訊息來啟動

DHCP 去獲取 IP 地址,然後廣播NETWORK_STATE_CHANGED_ACTION訊息,最後由

WifiSettings類來響應,改變狀態和介面資訊。

關鍵函式功能介紹

一.connect函式功能

1.updateNetwork:updateNetwork(config)會將當前選擇連線的AP配置資訊

資訊傳遞進去,配置資訊有(網路ID等)。如果網路ID-1則重新新增網路配置,然後向

wpa_supplicant 傳送SET_NETWORK命令(即通過這個網路ID設定其他一些相關資訊,設定

SSID,密碼等)如果網路配置不為-1則直接執行後面步驟即傳送SET_NETWORK命令。

2.saveNetwork:告訴supplicant儲存當前網路配置並更新列表。SaveNetwork會呼叫WifiService的

saveConfiguration向wpa_supplicant傳送SAVE_CONFIG命令儲存當前網路配置資訊,

如果返回false,則向wpa_supplicant重新發送RECONFIGURE命令獲取配置資訊,如果獲取信

息成功後,會Intent一NETWORK_IDS_CHANGED_ACTION事件WifiSettings會註冊接受

這個 時間並更新列表。

3.enableNetwork函式,向系統獲取介面名並使得該介面有效。由於之前傳遞的disableOthers

為true則向wpa_supplicant傳送SELECT_NETWORK(如果傳遞的為false則傳送

ENABLE_NETWORK命令),

4.reconnect函式:連線AP

二.reconnect函式功能:connect函式會呼叫WifiManager的reconnect然後通過Binder機制呼叫

WifiService的reconnect,再由WifiStateTracke呼叫WifiNative向wpa_supplicant傳送

RECONNECT命令去連線網路,當連線上wpa_supplicant之後會向控制通道傳送連線成功的命

令,

wifi_wait_for_event函式阻塞等待該事件的發生,並返回這個命令的字串(CONNECTED)

三.android_net_wifi_Wifi函式的doCommand函式會呼叫wifi.c的wifi_command函式將上層的命

令向wpa_supplicant傳送。

四.wifi_wait_for_event函式以阻塞的方式,等待控制通道傳遞的事件。當有事件傳遞過來的時候

該函式會通過wpa_ctrl的wpa_ctrl_recv函式讀取該事件,並以字串形式返回該事件名。

int wifi_wait_for_event(char *buf, size_t buflen)

{

.......

result = wpa_ctrl_recv(monitor_conn, buf, &nread);

if (result < 0) {

LOGD("wpa_ctrl_recv failed: %s/n", strerror(errno));

strncpy(buf, WPA_EVENT_TERMINATING " - recv error", buflen-1);

buf[buflen-1] = '/0';

return strlen(buf);

}

buf[nread] = '/0';

/* LOGD("wait_for_event: result=%d nread=%d string=/"%s/"/n", result, nread, buf); */

/* Check for EOF on the socket */

if (result == 0 && nread == 0) {

/* Fabricate an event to pass up */

LOGD("Received EOF on supplicant socket/n");

strncpy(buf, WPA_EVENT_TERMINATING " - signal 0 received", buflen-1);

buf[buflen-1] = '/0';

return strlen(buf);

}

/*

* Events strings are in the format

*

*<N>CTRL-EVENT-XXX

*

* where N is the message level in numerical form (0=VERBOSE, 1=DEBUG,

* etc.) and XXX is the event name. The level information is not useful

* to us, so strip it off.

*/

if (buf[0] == '<') {

char *match = strchr(buf, '>');

if (match != NULL) {

nread -= (match+1-buf);

memmove(buf, match+1, nread+1);

}

}

return nread;

}

五.wpa_ctrl_request,通過socket方式向wpa_supplicant傳送命令,以select模式阻塞在

wpa_supplicant傳送和接收。

int wpa_ctrl_request(struct wpa_ctrl *ctrl, constchar *cmd, size_t cmd_len,char *reply, size_t *reply_len,void (*msg_cb)(char *msg, size_t len))

{

.......

res = select(ctrl->s + 1, &rfds, NULL, NULL, &tv);

if (FD_ISSET(ctrl->s, &rfds)) {

res = recv(ctrl->s, reply, *reply_len, 0);

if (res < 0)

return res;

if (res > 0 && reply[0] == '<') {

/* This is an unsolicited message from

* wpa_supplicant, not the reply to the

* request. Use msg_cb to report this to the

* caller. */

if (msg_cb) {

/* Make sure the message is nul

* terminated. */

if ((size_t) res == *reply_len)

res = (*reply_len) - 1;

reply[res] = '/0';

msg_cb(reply, res);

}

continue;

}

*reply_len = res;

break;

} else {

return -2;

}

}

return 0;

}

六.WifiMonitor 維護一個監視執行緒分發處理底層返回上來的事件

voidhandleEvent(intevent,Stringremainder){

switch(event){

caseDISCONNECTED:

handleNetworkStateChange(NetworkInfo.DetailedState.DISCONNECTED,remainder);

break;

caseCONNECTED:

handleNetworkStateChange(NetworkInfo.DetailedState.CONNECTED,remainder);

break;

caseSCAN_RESULTS:

mWifiStateTracker.notifyScanResultsAvailable();

break;

caseUNKNOWN:

break;

}

}

此時返回的事件是CONNECTED因此 handleNetworkStateChange會被呼叫,驗證一下BSSID,重新獲得networkId

,然後呼叫WifiStateTrackenotifyStateChange通知狀態改變了的訊息(EVENT_NETWORK_STATE_CHANGED

接著處理這個訊息,會移除可用網路通告,然後通過configureInterface()的動態獲取IP地址。最後

傳送一個NETWORK_STATE_CHANGED_ACTION IntentWifiSetings註冊了此Intent因此會響應該它。由updateConnectionState函式響應。

.updateConnectionState 獲取連線資訊,更新列表狀態,設定為Connected,然後設定當前網路為可用狀態

privatevoidupdateConnectionState(DetailedStatestate){

/* sticky broadcasts can call this when wifi is disabled */

if(!mWifiManager.isWifiEnabled()){

mScanner.pause();

return;

}

if(state==DetailedState.OBTAINING_IPADDR){

mScanner.pause();

}else{

mScanner.resume();

}

mLastInfo=