alps/frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiConnectivityManager.java
handleScanResults //wifi扫描完会回调各个onResults
//这里和黑名单机制相关
Set<String> bssidBlocklist = mBssidBlocklistMonitor.updateAndGetBssidBlocklist();
if (mStateMachine.isSupplicantTransientState()) { //检查supplicant当前状态
localLog(listenerName
+ " onResults: No network selection because supplicantTransientState is "
+ mStateMachine.isSupplicantTransientState());
return false;
}
alps/frameworks/opt/net/wifi/service/java/com/android/server/wifi/ClientModeImpl.java
public boolean isSupplicantTransientState() {
SupplicantState supplicantState = mWifiInfo.getSupplicantState();
if (mVerboseLoggingEnabled) {
Log.d(TAG, "Supplicant is under steady state: " + supplicantState);
} //可以看到,当supplicant处于以下状态时,选网会直接终止
if (supplicantState == SupplicantState.ASSOCIATING
|| supplicantState == SupplicantState.AUTHENTICATING
|| supplicantState == SupplicantState.FOUR_WAY_HANDSHAKE
|| supplicantState == SupplicantState.GROUP_HANDSHAKE
|| getCurrentState() == mObtainingIpState) {
return true;
alps/frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiConnectivityManager.java
localLog(listenerName + " onResults: start network selection");
List<WifiCandidates.Candidate> candidates =
mNetworkSelector.getCandidatesFromScan(
scanDetails, bssidBlocklist, mWifiInfo, mStateMachine.isConnected(),
mStateMachine.isDisconnected(), mUntrustedConnectionAllowed);
alps/frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiNetworkSelector.java
getCandidatesFromScan
//从所有nominator中挑选出符合要求的network
//nominator也是一个接口,fwk实现并注册了三个
for (NetworkNominator registeredNominator : mNominators) {
registeredNominator.nominateNetworks
.
alps/frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiInjector.java
//可以看到wifiinjector中注册了三个nominator
mWifiNetworkSelector.registerNetworkNominator(mSavedNetworkNominator); mWifiNetworkSelector.registerNetworkNominator(mNetworkSuggestionNominator); mWifiNetworkSelector.registerNetworkNominator(mScoredNetworkNominator);
alps/frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiConnectivityManager.java
if (mDeviceMobilityState == WifiManager.DEVICE_MOBILITY_STATE_HIGH_MVMT
&& mContext.getResources().getBoolean(
R.bool.config_wifiHighMovementNetworkSelectionOptimizationEnabled)) { candidates = filterCandidatesHighMovement(candidates, listenerName, isFullScan);
}
//开始对以上挑选出的candidates进行评分选最优
WifiConfiguration candidate = mNetworkSelector.selectNetwork(candidates);
mLastNetworkSelectionTimeStamp = mClock.getElapsedSinceBootMillis();
mWifiLastResortWatchdog.updateAvailableNetworks(
mNetworkSelector.getConnectableScanDetails());
mWifiMetrics.countScanResults(scanDetails); //埋点
if (candidate != null) {
localLog(listenerName + ": WNS candidate-" + candidate.SSID);
connectToNetwork(candidate); //连接至评分最高的网络
return true;
alps/frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiNetworkSelector.jc WifiConfiguration selectNetwork(List<WifiCandidates.Candidate> candidates) {
if (candidates == null || candidates.size() == 0) {
return null;
}
WifiCandidates wifiCandidates = new WifiCandidates(mWifiScoreCard, mContext, candidates);
//这里获取到的就是默认的ThroughputScorer
final WifiCandidates.CandidateScorer activeScorer = getActiveCandidateScorer();
Collection<Collection<WifiCandidates.Candidate>> groupedCandidates =
wifiCandidates.getGroupedCandidates();
for (Collection<WifiCandidates.Candidate> group : groupedCandidates) {
WifiCandidates.ScoredCandidate choice = activeScorer.scoreCandidates(group);
if (choice == null) continue;
ScanDetail scanDetail = getScanDetailForCandidateKey(choice.candidateKey);
if (scanDetail == null) continue;
mWifiConfigManager.setNetworkCandidateScanResult(choice.candidateKey.networkId, scanDetail.getScanResult(), 0); //分数先清零?
//疑点:分数难道一直在更新?or用户点击或者网络情况变更时,分数也会更新?
//更新到配置文件?下次打开wifi会读取。
}
for (Collection<WifiCandidates.Candidate> group : groupedCandidates) {
for (WifiCandidates.Candidate candidate : group.stream()
.sorted((a, b) -> (b.getScanRssi() - a.getScanRssi()))
//这个根据rssi进行排序后面看起来只有埋点有用到,先不追究
.collect(Collectors.toList())) {
localLog(candidate.toString());
}
}
alps/frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiNetworkSelector.java
private WifiCandidates.CandidateScorer getActiveCandidateScorer() {
WifiCandidates.CandidateScorer ans =
mCandidateScorers.get(PRESET_CANDIDATE_SCORER_NAME);
alps/frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiNetworkSelector.java
public static final String PRESET_CANDIDATE_SCORER_NAME =
"ThroughputScorer";
alps/frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiNetworkSelector.java
boolean legacyOverrideWanted = true;
for (WifiCandidates.CandidateScorer candidateScorer :
mCandidateScorers.values()) {
//WifiCandidates.CandidateScorer是一个接口,fwk层总共实现了四个Scorer;
//这里是把四个candidateScorer全部遍历所有的network并返回最高分的那个
//但其实只有ThroughputScorer返回的才有效,不明白这个设计意图
WifiCandidates.ScoredCandidate choice;
try {
choice = wifiCandidates.choose(candidateScorer);
} catch (RuntimeException e) {
Log.wtf(TAG, "Exception running a CandidateScorer", e);
continue;
}
int networkId = choice.candidateKey == null
? WifiConfiguration.INVALID_NETWORK_ID
: choice.candidateKey.networkId;
String chooses = " would choose ";
if (candidateScorer == activeScorer) {
//这里的activeScorer就是ThroughputScorer,可以看到后续用到的只有selectedNetworkId
chooses = " chooses ";
legacyOverrideWanted = choice.userConnectChoiceOverride;
selectedNetworkId = networkId;
updateChosenPasspointNetwork(choice);
}
String id = candidateScorer.getIdentifier();
int expid = experimentIdFromIdentifier(id);
localLog(id + chooses + networkId
+ " score " + choice.value + "+/-" + choice.err
+ " expid " + expid);
experimentNetworkSelections.put(expid, networkId);
}
alps/frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiCandidates.java
public @NonNull ScoredCandidate choose(@NonNull CandidateScorer
candidateScorer) {
Preconditions.checkNotNull(candidateScorer);
Collection<Candidate> candidates = new ArrayList<>(mCandidates.values());
//调用所有注册了的WifiCandidates.CandidateScorer的scoreCandidates接口
ScoredCandidate choice = candidateScorer.scoreCandidates(candidates);
return choice == null ? ScoredCandidate.NONE : choice;
}
alps/frameworks/opt/net/wifi/service/java/com/android/server/wifi/ThroughputScorer.java
public ScoredCandidate scoreCandidates(@NonNull Collection<Candidate>
candidates) {
ScoredCandidate choice = ScoredCandidate.NONE;
for (Candidate candidate : candidates) {
ScoredCandidate scoredCandidate = scoreCandidate(candidate);
if (scoredCandidate.value > choice.value) {
//可以看到,就是选出评分最高的网络
choice = scoredCandidate;
}
}
return choice;
}
private ScoredCandidate scoreCandidate(Candidate candidate) {
// ThroughputScorer里具体的评分细节,可以看到于信号强度、是否保存过,
//加密方式等等都有关系,看不是很懂
int rssiSaturationThreshold =
mScoringParams.getSufficientRssi(candidate.getFrequency());
int rssi = Math.min(candidate.getScanRssi(), rssiSaturationThreshold);
int rssiBaseScore = (rssi + RSSI_SCORE_OFFSET) * RSSI_SCORE_SLOPE_IS_4;
int throughputBonusScore = calculateThroughputBonusScore(candidate);
int rssiAndThroughputScore = rssiBaseScore + throughputBonusScore;
boolean unExpectedNoInternet = candidate.hasNoInternetAccess()
&& !candidate.isNoInternetAccessExpected();
int currentNetworkBonusMin = mScoringParams.getCurrentNetworkBonusMin();
int currentNetworkBonus = Math.max(currentNetworkBonusMin,
rssiAndThroughputScore * mScoringParams.getCurrentNetworkBonusPercent() / 100);
int currentNetworkBoost = (candidate.isCurrentNetwork() && !unExpectedNoInternet)
? currentNetworkBonus : 0;
int securityAward = candidate.isOpenNetwork() ? 0
: mScoringParams.getSecureNetworkBonus();
int unmeteredAward = candidate.isMetered() ? 0
: mScoringParams.getUnmeteredNetworkBonus();
int savedNetworkAward = candidate.isEphemeral() ? 0 :
mScoringParams.getSavedNetworkBonus();
int trustedAward = TRUSTED_AWARD;
if (!candidate.isTrusted()) {
savedNetworkAward = 0; // Saved networks are not untrusted, but clear anyway
unmeteredAward = 0; // Ignore metered for untrusted networks
if (candidate.isCarrierOrPrivileged()) {
trustedAward = HALF_TRUSTED_AWARD;
} else if (candidate.getNominatorId() == NOMINATOR_ID_SCORED) {
Log.e(TAG, "ScoredNetworkNominator is not carrier or privileged!");
trustedAward = 0;
} else {
trustedAward = 0;
}
}
int score = rssiBaseScore + throughputBonusScore +
currentNetworkBoost + securityAward + unmeteredAward +
savedNetworkAward + trustedAward;
if (candidate.getLastSelectionWeight() > 0.0) {
// Put a recently-selected network in a tier above everything else,
// but include rssi and throughput contributions for BSSID selection.
score = TOP_TIER_BASE_SCORE + rssiBaseScore + throughputBonusScore;
}
if (DBG) {
Log.d(TAG, " rssiScore: " + rssiBaseScore
+ " throughputScore: " + throughputBonusScore
+ " currentNetworkBoost: " + currentNetworkBoost
+ " securityAward: " + securityAward
+ " unmeteredAward: " + unmeteredAward
+ " savedNetworkAward: " + savedNetworkAward
+ " trustedAward: " + trustedAward
+ " final score: " + score);
}
double tieBreaker = candidate.getScanRssi() / 1000.0;
return new ScoredCandidate(score + tieBreaker, 10,
USE_USER_CONNECT_CHOICE, candidate);
}
大体流程不算复杂,更多是细节性的东西。
小结:
1、 handleScanResults是扫描完成后开始选网的入口;
2、 根据黑名单机制过滤掉部分扫描结果,并判断当前supplicant的状态,空闲则开始选网;
3、 根据在WifiInjector中注册的三个nominator,遍历scandetails并依次调用niminatorNetwork方法,挑选出符合要求的network返回candidates;
4、 根据在WifiInjector中注册的四个scorer,遍历candidates调用choose方法,选取分数最高的一个network并返回(只有ThroughputScorer生效)
5、 connectToNetwork回连至该网络。
add:nominator和scorer中涉及到很多具体的评分项,比如rssi,加密方式,吞吐量等等,都有各自的权重和分数;这里只贴出了部分代码,有需要请自行查看。
另wifi配置最后会保存在WifiConfigStore.xml中,/data/misc/....路径下,启动
WifiServiceImpl时会在onBootPhase中读取并解析xml,用于下一次选网的参考,具体还有待分析。
注意:对于Android系统。上层的自动选网是这样。但是底层各家厂商还会做进一步处理,比如对于上层下发以ssid的连接方式来连接ESS网络,底层还会进一步进行选网(比如打分机制)然后在连接。
WiFi选网机制
于 2021-08-02 13:58:55 首次发布