wpa_supplicant學習

    wpa_supplicant是開源項目源碼,被谷歌修改後加入android移動平臺,它主要是用來支持WEP,WPA/WPA2和WAPI無線協議和加密認證的,而實際上是通過socket與驅動交互以及上報數據給用戶(不管是wpa_supplicant與上層還是wpa_supplicant與驅動都採用socket通訊).
    用戶可以通過socket發送命令給wpa_supplicant調動驅動來對WiFi芯片操作。簡單的說,wpa_supplicant就是WiFi驅動和用戶的中轉站,另外對協議和加密認證的支持。
    從framework到wpa_supplicant的適配層(wifi.c),其中framework部分需要注意的是wifiService和wifiMoniter兩部分,這兩塊一個是轉發AP的CMD,另一個是接收來自wpa_supplicant的CMD。他們與本地庫的連接都是通過JNI方法,具體實現方法在android_net_wifi_Wifi.cpp中。

    android_net_wifi_Wifi.cpp中可以看出AP給wpa_supplicant發送的命令。這些命令通過wifi.c的wifi_command發送給wpa_supplicant,發送命令調用wpa_ctrl_request來完成,wpa_ctrl_request是通過socket的方式與wpa_supplicant進行通信的,然後通過wpa_ctrl_recv來接收來自wpa_supplicant的命令,並返回標識給wifi_wait_for_event。

   WifiStateTracker會創建WifiMonitor接收來自底層的事件,WifiService和WifiMonitor是整個模塊的核心。
   WifiService負責啓動關閉 wpa_supplicant、啓動關閉 WifiMonitor監視線程和把命令下發給wpa_supplicant。
   WifiMonitor則負責從wpa_supplicant接收事件通知。
連接 AP
1.使能 WIFI
WirelessSettings在初始化的時候配置了由WifiEnabler來處理 Wifi按鈕,
private void initToggles() {
mWifiEnabler = new WifiEnabler(
this,(WifiManager) getSystemService(WIFI_SERVICE),
(CheckBoxPreference) findPreference(KEY_TOGGLE_WIFI));
    當用戶按下Wifi按鈕後,Android會調用 WifiEnabler的onPreferenceChange,再由 WifiEnabler調用 WifiManager的setWifiEnabled接口函數,通過 AIDL,實際調用的是 WifiService的setWifiEnabled函數,WifiService接着向自身發送一條 MESSAGE_ENABLE_WIFI消息,在處理該消息的代碼中做真正的使能工作:

    首先裝載 WIFI內核模塊(該模塊的位置硬編碼爲"/system/lib/modules/wlan.ko" ),
    然後啓動wpa_supplicant (配置文件硬編碼爲"/data/misc/wifi/wpa_supplicant.conf")
    再通過 WifiStateTracker來啓動WifiMonitor中的監視線程。
private boolean setWifiEnabledBlocking(boolean enable) {
final int eventualWifiState = enable ?WIFI_STATE_ENABLED :WIFI_STATE_DISABLED;
updateWifiState(enable ? WIFI_STATE_ENABLING:WIFI_STATE_DISABLING);
if (enable) {
if (!WifiNative.loadDriver()) {
Log.e(TAG, "Failed to load Wi-Fidriver.");
updateWifiState(WIFI_STATE_UNKNOWN);
return false;
}
if (!WifiNative.startSupplicant()) {
WifiNative.unloadDriver();
Log.e(TAG, "Failed to start supplicantdaemon.");
updateWifiState(WIFI_STATE_UNKNOWN);
return false;
}
mWifiStateTracker.startEventLoop();
}
// Success!
persistWifiEnabled(enable);
updateWifiState(eventualWifiState);
return true;
}
    當使能成功後,會廣播發送 WIFI_STATE_CHANGED_ACTION 這個Intent通知外界WIFI已經成功使能了。WifiEnabler創建的時候就會向Android註冊接收WIFI_STATE_CHANGED_ACTION,因此它會收到該 Intent,從而開始掃描。
   private void handleWifiStateChanged(intwifiState) {
if (wifiState == WIFI_STATE_ENABLED) {
loadConfiguredAccessPoints();
attemptScan();
}

2.查找 AP
掃描的入口函數是 WifiService的 startScan,它其實也就是往 wpa_supplicant發送 SCAN命令。
static jbooleanandroid_net_wifi_scanCommand(JNIEnv* env, jobjectclazz)
{
jboolean result;
// Ignore any error from setting the scanmode.
// The scan will still work.
(void)doBooleanCommand("DRIVERSCAN-ACTIVE", "OK");
result = doBooleanCommand("SCAN", "OK");
(void)doBooleanCommand("DRIVERSCAN-PASSIVE", "OK");
return result;
}

當wpa_supplicant處理完SCAN命令後,它會向控制通道發送事件通知掃描完成,從而wifi_wait_for_event函數會接收到該事件,由此 WifiMonitor 中的MonitorThread會被執行來出來這個事件,
void handleEvent(int event, Stringremainder) {

case SCAN_RESULTS:

mWifiStateTracker.notifyScanResultsAvailable();

break;

WifiStateTracker則接着廣播發送 SCAN_RESULTS_AVAILABLE_ACTION這個Intent

case EVENT_SCAN_RESULTS_AVAILABLE:

mContext.sendBroadcast(new

Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));

WifiLayer註冊了接收 SCAN_RESULTS_AVAILABLE_ACTION這個Intent,所以它的相關處理函數handleScanResultsAvailable會被調用,在該函數中,先會去拿到 SCAN 的結果(最終是往 wpa_supplicant發送 SCAN_RESULT命令並讀取返回值來實現的),

List<ScanResult> list=mWifiManager.getScanResults();

對每一個掃描返回的AP,WifiLayer會調用 WifiSettings的onAccessPointSetChanged函數,從而最終把該 AP加到 GUI顯示列表中。

public voidonAccessPointSetChanged(AccessPointState ap, booleanadded) {
AccessPointPreference pref = mAps.get(ap);
if (added) {
if (pref == null) {
pref = new AccessPointPreference(this, ap);
mAps.put(ap, pref);
} else {
pref.setEnabled(true);
}
mApCategory.addPreference(pref);
}
}
3.配置 AP參數
當用戶在WifiSettings界面上選擇了一個AP後,會顯示配置 AP參數的一個對話框,
public boolean onPreferenceTreeClick(PreferenceScreenpreferenceScreen,Preference preference) {
if (preference instanceofAccessPointPreference) {
AccessPointState state =((AccessPointPreference)
preference).getAccessPointState();
showAccessPointDialog(state,AccessPointDialog.MODE_INFO);
}
}
4.連接
當用戶在AcessPointDialog中選擇好加密方式和輸入密鑰之後,再點擊連接按鈕,Android就會去連接這個AP。
private void handleConnect() {
String password = getEnteredPassword();
if (!TextUtils.isEmpty(password)) {
mState.setPassword(password);
}
mWifiLayer.connectToNetwork(mState);
}
WifiLayer會先檢測這個AP是不是之前被配置過,這個是通過向 wpa_supplicant 發送LIST_NETWORK命令並且比較返回值來實現的,
// Need WifiConfiguration for the AP
WifiConfiguration config =findConfiguredNetwork(state);
如果wpa_supplicant沒有這個AP的配置信息,則會向wpa_supplicant發送 ADD_NETWORK命令來添加該AP,
if (config == null) {
// Connecting for the first time, need to create it
config =addConfiguration(state,ADD_CONFIGURATION_ENABLE|ADD_CONFIGURATION_SAVE);
}
ADD_NETWORK命令會返回一個 ID , WifiLayer再用這個返回的 ID作爲參數向wpa_supplicant發送ENABLE_NETWORK命令,從而讓 wpa_supplicant 去連接該AP。
// Make sure that network is enabled, anddisable others
mReenableApsOnNetworkStateChange = true;
if(!mWifiManager.enableNetwork(state.networkId, true)) {
Log.e(TAG, "Could not enable networkID " + state.networkId);
error(R.string.error_connecting);
return false;
}
5.配置 IP地址
當wpa_supplicant成功連接上AP之後,它會向控制通道發送事件通知連接上AP了,從而wifi_wait_for_event函數會接收到該事件,由此 WifiMonitor 中的 MonitorThread會被執行來出來這個事件,
void handleEvent(int event, Stringremainder) {
case CONNECTED:
handleNetworkStateChange(NetworkInfo.DetailedState.CONNECTED,remainder);
break;
WifiMonitor再調用WifiStateTracker的notifyStateChange,WifiStateTracker則接着會往自身發送EVENT_DHCP_START消息來啓動DHCP去獲取 IP地址。
private void handleConnectedState() {
setPollTimer();
mLastSignalLevel = -1;
if (!mHaveIPAddress&&!mObtainingIPAddress) {
mObtainingIPAddress = true;
mDhcpTarget.obtainMessage(EVENT_DHCP_START).sendToTarget();
}
}
然後再廣播發送NETWORK_STATE_CHANGED_ACTION這個Intent
case EVENT_NETWORK_STATE_CHANGED:
if (result.state != DetailedState.DISCONNECTED ||!mDisconnectPending) {
intent = new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION);
intent.putExtra(WifiManager.EXTRA_NETWORK_INFO,mNetworkInfo);
if (result.BSSID != null)
intent.putExtra(WifiManager.EXTRA_BSSID,result.BSSID);
mContext.sendStickyBroadcast(intent);
}
break;
WifiLayer註冊了接收 NETWORK_STATE_CHANGED_ACTION這個 Intent,所以它的相關處理函數handleNetworkStateChanged會被調用,

當DHCP拿到IP地址之後,會再發送 EVENT_DHCP_SUCCEEDED消息,
private class DhcpHandler extends Handler {
public void handleMessage(Message msg) {
switch (msg.what) {
case EVENT_DHCP_START:
if (NetworkUtils.runDhcp(mInterfaceName,mDhcpInfo)) {
event = EVENT_DHCP_SUCCEEDED;
}
WifiLayer處理 EVENT_DHCP_SUCCEEDED消息 ,會再次廣播發送
NETWORK_STATE_CHANGED_ACTION這個 Intent,這次帶上完整的 IP地址信息。
case EVENT_DHCP_SUCCEEDED:
mWifiInfo.setIpAddress(mDhcpInfo.ipAddress);
setDetailedState(DetailedState.CONNECTED);
intent = new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION);
intent.putExtra(WifiManager.EXTRA_NETWORK_INFO,mNetworkInfo);
mContext.sendStickyBroadcast(intent);
break;

至此爲止,整個連接過程完成。
問題:
目前的實現不支持 Ad-hoc方式。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章