Android Wi-Fi WifiAutoJoinController介紹

一.背景介紹


WifiAutoJoinController 類是在Android L 的版本上添加的。KK 之前的版本是不包含此類的。但是雖然是L 的版本上新添加的類但是它所實現的功能卻不是最新的。他要實現的自動選擇切換WIFI 的AP 的功能L 之前的版本就存在,只不過L 之前的版本這個功能的實現是在wpa_supplicant 中。但是從L 的版本開始google 將這個功能從wpa_sipplicant 中取出來單獨建立了一個類就形成了現在的WifiAutoJoinController 的功能。下面我將以google 原生L 代碼爲例對WifiAutoJoinController 類進行簡單的介紹。如有不同見解者請提出,也非常歡迎對此文檔記性修改。


二. 功能介紹

WifiAutoJoinController 類中核心的方法就是attemptAutoJoin();下面我將從attemptAutoJoin() 方法開始介紹;在attemptAutoJoin()中首先獲取了當前的AP 配置信息,然後又獲取了最近的使用過的AP 的配置信息列表。並且定義了一個候選者對象(candidate)但是並未進行實例化;

WifiConfiguration currentConfiguration = mWifiStateMachine.getCurrentWifiConfiguration();
......
WifiConfiguration candidate = null;
......
List<WifiConfiguration> list = mWifiConfigStore.getRecentConfiguredNetworks(3000, false);

爲了按照代碼順序介紹,我們暫時先擱置以上的三個獲取的對象具體引用。接下來代碼中用獲取了當前的AP 狀態信息,並且將狀態信息經過一系列的字符串處理(split、regionMatches、contains)

String val = mWifiNative.status(true);
String status[] = val.split("\\r?\\n");
int supplicantNetId = -1;
for (String key : status) {
	if (key.regionMatches(0, "id=", 0, 3)) {
		int idx = 3;
		supplicantNetId = 0;
		while (idx < key.length()) {
			char c = key.charAt(idx);
			if ((c >= 0x30) && (c <= 0x39)) {
				supplicantNetId *= 10;
				supplicantNetId += c - 0x30;
				idx++;
			} else {
				break;
			}
		}
	} else if (key.contains("wpa_state=ASSOCIATING")
				|| key.contains("wpa_state=ASSOCIATED")
				|| key.contains("wpa_state=FOUR_WAY_HANDSHAKE")
				|| key.contains("wpa_state=GROUP_KEY_HANDSHAKE")) {
		return;
	}
}

上面看似很複雜的處理過程其實它就一個目的:通過對當前的狀態值(wpa_state=ASSOCIATING、wpa_state=ASSOCIATED、wpa_state=FOUR_WAY_HANDSHAKE、wpa_state=GROUP_KEY_HANDSHAKE)進行判斷,如果當前wpa 的是ASSOCIATING、ASSOCIATED、FOUR_WAY_HANDSHAKE、GROUP_KEY_HANDSHAKE 中的一個狀態則直接返回。

經過上面這麼一折騰,那麼系統認爲我當前的環境是安全的那麼我就可以進行選擇性的判斷是否
可以進行切換了。接下來當然是繼續我們剛纔獲取到的當前( currentConfiguration)AP 的配置
信息的操作了。首先判斷我們是否成功獲取了currentConfiguration。

if (currentConfiguration != null) {
	if (supplicantNetId != currentConfiguration.networkId
	&& supplicantNetId != WifiConfiguration.INVALID_NETWORK_ID
	&& currentConfiguration.networkId != WifiConfiguration.INVALID_NETWORK_ID) {
		mWifiStateMachine.disconnectCommand();
		return;
	} else {
		mCurrentConfigurationKey = currentConfiguration.configKey();
	}
} else {
	if (supplicantNetId != WifiConfiguration.INVALID_NETWORK_ID) {
		// Maybe in the process of associating, skip this attempt
		return;
	}
}

int currentNetId = -1;
if (currentConfiguration != null) {
	currentNetId = currentConfiguration.networkId;
}


由上可以看出如果獲取的currentConfiguration 不爲空,那麼說明我們獲取currentConfiguration 成功了。接下來我麼就進一步對我們當前currentConfiguration 中的ID 值進行三個判斷:
(1)currentConfiguration 中的ID 不等於supplicantNetId;
(2)supplicantNetId 是有效的id;
(3)currentConfiguration 中的id 也是一個有效的ID;


如果這三個值同時成立,那麼將會發送斷開連接的消息。此處一般不會被斷開因爲這個三個條件
同時成立的可能性比較小;因爲currentConfiguration 是有效的不會是-1(INVALID_NETWORK_ID);並且之前我們也對supplicantNetId 的進行過賦值操作;所以此處這三個條件同時成立的可能性不大;如果上述三個條件不成立,那麼就會將當前網絡(currentConfiguration)的key 賦值給mCurrentConfigurationKey 以作後續備用;


如果說我們獲取currentConfiguration 不成功,那麼會判斷我們當前supplicantNetId 是不是有效的,
如果有效那麼返回,接下來的操作不在執行,如果無效那麼繼續執行。接着會對currentNetId 進行初始化賦值。將當前網絡配置的networkId 賦值給currentNetId。經過上面的對currentConfiguration 這麼一番判斷,系統暫時認爲currentConfiguration 是安全的。

那麼將會對最近的使用過的AP 的配置信息列表list 處理了。接下來我們首先看一下這一部分代
碼:

if (config.SSID == null) {
	continue;
}

對list 的判斷通過for 循環的形式實現的;進入for 循環之後,
(1)判斷目前的ssid 是不是null 的,如果是那麼不在進行判斷,直接跳到list 中下一個AP 的循環進行判斷。
if (config.autoJoinStatus >=
	WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE) {
		continue;
}

(2)判斷當前的AP 是不是之前認證失敗的,通過判斷autoJoinStatus 的值是不是
AUTO_JOIN_DISABLED_ON_AUTH_FAILURE(128)進行處理。如果是那麼也直接跳出本次循環接着進行list 中下一個AP 的判斷。

if (config.blackListTimestamp > 0) {
	long now = System.currentTimeMillis();
	if (now < config.blackListTimestamp) {
		config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_ENABLED);
	} else {
		if ((now - config.blackListTimestamp) > loseBlackListHardMilli) {
			config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_ENABLED);
		} else if ((now - config.blackListTimestamp) > loseBlackListSoftMilli) {
			config.setAutoJoinStatus(config.autoJoinStatus - 8);
		}
	}
}

(3)判斷當前AP 的黑名單時間,如果說大於0,也就是說AP 處於黑名單之中。如下的三中情況會將AP 從黑名單中清楚。
① AP 被拉入黑名單的時間比當前時間還要晚(雖然看起來這種情況有點不可思議),那麼將AP 的狀態值autoJoinStatus 設置爲AUTO_JOIN_ENABLED(0)。也就是所謂的將AP 從黑名單中清除。


② AP 在黑名單中的時間已經超過了8 個小時,同樣的autoJoinStatus 設置爲
AUTO_JOIN_ENABLED(0)。將這個AP 從黑名單中清除。


③ AP 由於連接情況比較差的時間已經超過了30 分鐘,那麼我們將起狀態值進行減8 操作。

if (config.visibility.rssi5 < mWifiConfigStore.thresholdUnblacklistThreshold5Soft
    && config.visibility.rssi24< mWifiConfigStore.thresholdUnblacklistThreshold24Soft) {
} else if (config.visibility.rssi5 < mWifiConfigStore.thresholdUnblacklistThreshold5Hard
           && config.visibility.rssi24< mWifiConfigStore.thresholdUnblacklistThreshold24Hard) {
    config.setAutoJoinStatus(config.autoJoinStatus - 1);
} else {
	config.setAutoJoinStatus(config.autoJoinStatus - 3);
}

(4)判斷visibility 的值。對autoJoinStatus 的值進行操作。


① visibility.rssi5 的值如果小於UNBLACKLIST_THRESHOLD_5_SOFT(-63 也就是
WifiConfigStore.thresholdUnblacklistThreshold5Soft 的值)的值並且visibility.rssi24 的值小於UNBLACKLIST_THRESHOLD_24_SOFT(-77 也就是WifiConfigStore.thresholdUnblacklistThreshold24Soft 的值)的值,那麼將會打印log 說由於太低的visibility 所以不能unblacklist。


② visibility.rssi5 的值如果小於UNBLACKLIST_THRESHOLD_5_HARD(-56 也就是
WifiConfigStore.thresholdUnblacklistThreshold5Hard 的值)的值並且visibility.rssi24 的值小於UNBLACKLIST_THRESHOLD_24_HARD(-68 也就是WifiConfigStore.thresholdUnblacklistThreshold24Hard 的值)的值,那麼將autoJoinStatus 的值進行減1 的操作。


③ 如果上述的兩種情況都不成立,那麼將autoJoinStatus 的值減3 的操作。

if (config.autoJoinStatus >= WifiConfiguration.AUTO_JOIN_TEMPORARY_DISABLED) {
	continue;
}

(5)判斷當前的AP 的autoJoinStatus 值是不是AUTO_JOIN_TEMPORARY_DISABLED(1);如果那麼說明當前AP 處於黑名單中,已經暫時被禁止。所以直接跳出本次AP 的循環判斷,將進入list 中下一個AP 的循環執行判斷操作。
if (config.networkId == currentNetId) {
	continue;
}
(6)判斷現在正在循環中判斷的這個AP 是不是我們當前的AP(currentConfiguration)。通過AP 的networkId 值進行判斷,如果相等那麼直接跳出本次AP 的循環判斷,將進入list 中下一個AP 的循環執行判斷操作。
boolean isLastSelected = false;
if (lastSelectedConfiguration != null &&
	config.configKey().equals(lastSelectedConfiguration)) {
	isLastSelected = true;
}
(7)對isLastSelected 進行初始化並且賦值。
if (config.visibility == null) {
	continue;
}

(8)判斷如果當前AP 的visibility 的值爲空;那麼直接跳出本次AP 的循環判斷,將進入list中下一個AP 的循環執行判斷操作。
int boost = config.autoJoinUseAggressiveJoinAttemptThreshold + weakRssiBailCount;
if ((config.visibility.rssi5 + boost) < mWifiConfigStore.thresholdInitialAutoJoinAttemptMin5RSSI
	&& (config.visibility.rssi24 + boost) < mWifiConfigStore.thresholdInitialAutoJoinAttemptMin24RSSI) {
		if (!isLastSelected) {
			config.autoJoinBailedDueToLowRssi = true;
			didBailDueToWeakRssi = true;
			continue;
		} else {
			if (config.autoJoinUseAggressiveJoinAttemptThreshold < WifiConfiguration.MAX_INITIAL_AUTO_JOIN_RSSI_BOOST
				&& config.autoJoinBailedDueToLowRssi) {
				config.autoJoinUseAggressiveJoinAttemptThreshold += 4;
			}
		}
}
(9)判斷如果visibility.rssi5 的值加上boost 值小於INITIAL_AUTO_JOIN_ATTEMPT_MIN_5(-70 也就是WifiConfigStore.thresholdInitialAutoJoinAttemptMin5RSSI 的值)的值並且visibility.rssi24 的值小於INITIAL_AUTO_JOIN_ATTEMPT_MIN_24(-80 也就WifiConfigStore.thresholdInitialAutoJoinAttemptMin24RSSI 的值)的值,那麼做如下的操作:
① 判斷當前AP 是不是用戶最後選擇的,如果不是那麼返回,跳出本次AP 的循環判斷,將進入list 中下一個AP 的循環執行判斷操作。
② 如果AP 是用戶最後選擇的,那麼判斷如果autoJoinUseAggressiveJoinAttemptThreshold 的值小於MAX_INITIAL_AUTO_JOIN_RSSI_BOOST(8)並且autoJoinBailedDueToLowRssi 的值爲真,那麼將autoJoinUseAggressiveJoinAttemptThreshold 的值加4;

if (config.noInternetAccess && !isLastSelected) {
	continue;
}

(10)判斷如果當前AP 不能上網(noInternetAccess 爲true)並且不是用戶最後選擇的AP,那麼跳出本次AP 的循環判斷,將進入list 中下一個AP 的循環執行判斷操作。
if (candidate == null) {
	candidate = config;
} else {
	int order = compareWifiConfigurationsTop(candidate, config);
	if ((lastSelectedConfiguration != null)
		&& candidate.configKey().equals(lastSelectedConfiguration)) {
		order = order - 100;
	} else if ((lastSelectedConfiguration != null)
		&& config.configKey().equals(lastSelectedConfiguration)) {
		order = order + 100;
	}
	if (order > 0) {
	// Ascending : candidate < config
	candidate = config;
	}
}
(11)判斷candidate 是否爲空;
① 如果爲空,那麼將經過上述10 個層層判斷仍然“活着”的AP(config)賦值給候選者candidate。
② 如果不爲空,那麼那麼通過調用compareWifiConfigurationsTop 方法比較candidate 中的AP和經過上述10 個層層判斷仍然“活着”的AP(config)做一個對比,得出一個得分order。接下來首先就會判斷用戶最後選擇的lastSelectedConfiguration 不爲空,並且當前(currentConfiguration)的AP 就是用戶最後選擇的AP(lastSelectedConfiguration),那麼將得分減100(order-100);再次判斷經過上述10 個層層判斷仍然“活着”的AP(config)如果這個config 是用戶最後選擇的並且lastSelectedConfiguration 不爲空,那麼將得分加100(order + 100);
③ 最後判斷order 是否任然大於0,如果大於0,那麼將將經過上述10 個層層判斷仍然“活着”的AP(config)賦值給候選者candidate。

經過上面的篩選,系統會將候選者(candidate)和用戶最後選擇的AP(lastSelectedConfiguration)進行對比。

int networkDelta = compareNetwork(candidate, lastSelectedConfiguration);

對比的結果分networkDelta 將會作爲參數傳給WifiStateMachine 的shouldSwitchNetwork;經過shouldSwitchNetwork 對分數networkDelta 增加或者刪除之後,如果shouldSwitchNetwork 的返回值爲ture(爲true 的意思將網絡切換到候選者candidate);之後就會向WifiStateMachine 發送CMD_AUTO_CONNECT 命令;mWifiStateMachine.sendMessage(WifiStateMachine.CMD_AUTO_CONNECT,candidate.networkId, networkSwitchType, candidate);這條命令首先會斷開當前(currentConfiguration)的AP,而後連接候選者(candidate)AP。到此網絡自動切換完成。
if (networkSwitchType == AUTO_JOIN_IDLE) {
	mWifiStateMachine.sendMessage(WifiStateMachine.CMD_AUTO_ROAM,
								currentConfiguration.networkId, 1, roamCandidate);
}

對於WifiAutoJoinController 來說最後還有一部分判斷就是判斷networkSwitchType 的值,如果等於AUTO_JOIN_IDLE,那麼發送CMD_AUTO_ROAM 消息進行操作。

到此WifiAutoJoinController 整體分析完成


三、流程圖


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