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 整体分析完成
三、流程图