基于Android S 代码分析:http://aospxref.com/android-13.0.0_r3/
首先我们要知道两点,一般说道评分机制,需要知道是Wi-Fi内部的评分机制还是整个CS的评分机制,那这里我们记录的是Wi-Fi内部的评分机制,那什么时候触发这个机制呢?自动连接的时候就会触发,会评选出一个分数最高的 Candidate ,不知道Wi-Fi自动连接流程的看这里:Wi-Fi 自动连接流程
具体的看下代码:
http://aospxref.com/android-13.0.0_r3/xref/packages/modules/Wifi/service/java/com/android/server/wifi/WifiConnectivityManager.java
List<WifiCandidates.Candidate> candidates = mNetworkSelector.getCandidatesFromScan( ---> 看下这个的实现
scanDetails, bssidBlocklist, cmmStates,
mUntrustedConnectionAllowed,mOemPaidConnectionAllowed,
mOemPrivateConnectionAllowed,mRestrictedConnectionAllowedUids,
isMultiInternetConnectionRequested());
mLatestCandidates = candidates;
http://aospxref.com/android-13.0.0_r3/xref/packages/modules/Wifi/service/java/com/android/server/wifi/WifiNetworkSelector.java#getCandidatesFromScan
public List<WifiCandidates.Candidate> getCandidatesFromScan(
@NonNull List<ScanDetail> scanDetails, @NonNull Set<String> bssidBlocklist,
@NonNull List<ClientModeManagerState> cmmStates, boolean untrustedNetworkAllowed,
boolean oemPaidNetworkAllowed, boolean oemPrivateNetworkAllowed,
Set<Integer> restrictedNetworkAllowedUids, boolean multiInternetNetworkAllowed) {
// 清空集合
mFilteredNetworks.clear();
mConnectableNetworks.clear();
// 扫描到的结果不能没有
if (scanDetails.size() == 0) {
localLog("Empty connectivity scan result");
return null;
}
// Update the scan detail cache at the start, even if we skip network selection
updateScanDetailCache(scanDetails); ---> 目的在后续的网络选择和候选网络提名过程中,可以通过网络配置的网络ID从缓存中获取相应的扫描详情。避免频繁地重新扫描和解析扫描结果,提高操作的效率和响应速度
// Update the registered network nominators.
for (NetworkNominator registeredNominator : mNominators) {
registeredNominator.update(scanDetails);
}
// Shall we start network selection at all?
if (!multiInternetNetworkAllowed && !isNetworkSelectionNeeded(scanDetails, cmmStates)) {
return null;
}
// Filter out unwanted networks.
mFilteredNetworks = filterScanResults(scanDetails, bssidBlocklist, cmmStates); --->下面单独看下,标记 1
if (mFilteredNetworks.size() == 0) {
return null;
}
WifiCandidates wifiCandidates = new WifiCandidates(mWifiScoreCard, mContext);
for (ClientModeManagerState cmmState : cmmStates) {
// 始终从WifiInfo中获取当前BSSID,以防固件发起了漫游。
String currentBssid = cmmState.wifiInfo.getBSSID();
WifiConfiguration currentNetwork =
mWifiConfigManager.getConfiguredNetwork(cmmState.wifiInfo.getNetworkId());
// 将当前网络设置为候选网络
if (currentNetwork != null) {
wifiCandidates.setCurrent(currentNetwork.networkId, currentBssid);
MacAddress bssid = MacAddress.fromString(currentBssid);
SecurityParams params = currentNetwork.getNetworkSelectionStatus().getLastUsedSecurityParams();
// 如果当前网络没有已知的候选安全参数,则跳过
if (null == params) {
localLog("当前网络没有已知的候选安全参数。");
continue;
}
// 为当前网络创建一个键
WifiCandidates.Key key = new WifiCandidates.Key(
ScanResultMatchInfo.fromWifiConfiguration(currentNetwork),
bssid, currentNetwork.networkId,
params.getSecurityType());
// 查找当前BSSID的扫描详情
ScanDetail scanDetail = findScanDetailForBssid(mFilteredNetworks, currentBssid);
// 预测扫描详情的吞吐量
int predictedTputMbps = (scanDetail == null) ? 0 : predictThroughput(scanDetail);
// 将当前网络添加为候选网络
wifiCandidates.add(key, currentNetwork,
NetworkNominator.NOMINATOR_ID_CURRENT,
cmmState.wifiInfo.getRssi(),
cmmState.wifiInfo.getFrequency(),
ScanResult.CHANNEL_WIDTH_20MHZ, // 在WifiInfo中无法获取信道宽度
calculateLastSelectionWeight(currentNetwork.networkId),
WifiConfiguration.isMetered(currentNetwork, cmmState.wifiInfo),
isFromCarrierOrPrivilegedApp(currentNetwork),
predictedTputMbps);
}
}
// 在开始网络选择之前更新所有已配置的网络
updateConfiguredNetworks();
for (NetworkNominator registeredNominator : mNominators) {
localLog("About to run " + registeredNominator.getName() + " :");
registeredNominator.nominateNetworks(
new ArrayList<>(mFilteredNetworks),
untrustedNetworkAllowed, oemPaidNetworkAllowed, oemPrivateNetworkAllowed,
restrictedNetworkAllowedUids, (scanDetail, config) -> {
WifiCandidates.Key key = wifiCandidates.keyFromScanDetailAndConfig(
scanDetail, config);
if (key != null) {
boolean metered = false;
for (ClientModeManagerState cmmState : cmmStates) {
if (isEverMetered(config, cmmState.wifiInfo, scanDetail)) {
metered = true;
break;
}
}
// TODO(b/151981920) Saved passpoint candidates are marked ephemeral
boolean added = wifiCandidates.add(key, config,
registeredNominator.getId(),
scanDetail.getScanResult().level,
scanDetail.getScanResult().frequency,
scanDetail.getScanResult().channelWidth,
calculateLastSelectionWeight(config.networkId),
metered,
isFromCarrierOrPrivilegedApp(config),
predictThroughput(scanDetail));
if (added) {
mConnectableNetworks.add(Pair.create(scanDetail, config));
mWifiConfigManager.updateScanDetailForNetwork(
config.networkId, scanDetail);
mWifiMetrics.setNominatorForNetwork(config.networkId,
toProtoNominatorId(registeredNominator.getId()));
}
}
});
}
if (mConnectableNetworks.size() != wifiCandidates.size()) {
localLog("Connectable: " + mConnectableNetworks.size()
+ " Candidates: " + wifiCandidates.size());
}
return wifiCandidates.getCandidates();
}
看下标记 1:
private List<ScanDetail> filterScanResults(List<ScanDetail> scanDetails,
Set<String> bssidBlocklist, List<ClientModeManagerState> cmmStates) {
List<ScanDetail> validScanDetails = new ArrayList<>();
StringBuffer noValidSsid = new StringBuffer();
StringBuffer blockedBssid = new StringBuffer();
StringBuffer lowRssi = new StringBuffer();
StringBuffer mboAssociationDisallowedBssid = new StringBuffer();
StringBuffer adminRestrictedSsid = new StringBuffer();
List<String> currentBssids = cmmStates.stream()
.map(cmmState -> cmmState.wifiInfo.getBSSID())
.collect(Collectors.toList());
Set<String> scanResultPresentForCurrentBssids = new ArraySet<>();
int adminMinimumSecurityLevel = 0;
boolean adminSsidRestrictionSet = false;
Set<WifiSsid> adminSsidAllowlist = new ArraySet<>();
Set<WifiSsid> admindSsidDenylist = new ArraySet<>();
// 获取设备策略管理器
DevicePolicyManager devicePolicyManager =
WifiPermissionsUtil.retrieveDevicePolicyManagerFromContext(mContext);
if (devicePolicyManager != null && SdkLevel.isAtLeastT()) {
// 获取最低要求的 Wi-Fi 安全级别
adminMinimumSecurityLevel =
devicePolicyManager.getMinimumRequiredWifiSecurityLevel();
// 获取 Wi-Fi SSID 策略
WifiSsidPolicy policy = devicePolicyManager.getWifiSsidPolicy();
if (policy != null) {
adminSsidRestrictionSet = true;
if (policy.getPolicyType() == WifiSsidPolicy.WIFI_SSID_POLICY_TYPE_ALLOWLIST) {
// 获取允许的 SSID 列表
adminSsidAllowlist = policy.getSsids();
} else {
// 获取拒绝的 SSID 列表
admindSsidDenylist = policy.getSsids();
}
}
}
for (ScanDetail scanDetail : scanDetails) {
ScanResult scanResult = scanDetail.getScanResult();
if (TextUtils.isEmpty(scanResult.SSID)) {
// 扫描结果的 SSID 为空
noValidSsid.append(scanResult.BSSID).append(" / ");
continue;
}
// 检查扫描结果是否与当前连接的 BSSID 相同
if (currentBssids.contains(scanResult.BSSID)) {
scanResultPresentForCurrentBssids.add(scanResult.BSSID);
validScanDetails.add(scanDetail);
continue;
}
final String scanId = toScanId(scanResult);
if (bssidBlocklist.contains(scanResult.BSSID)) {
// 扫描结果的 BSSID 在黑名单中
blockedBssid.append(scanId).append(" / ");
numBssidFiltered++;
continue;
}
// 跳过信号强度过低的网络
if (isSignalTooWeak(scanResult)) {
lowRssi.append(scanId);
if (scanResult.is24GHz()) {
lowRssi.append("(2.4GHz)");
} else if (scanResult.is5GHz()) {
lowRssi.append("(5GHz)");
} else if (scanResult.is6GHz()) {
lowRssi.append("(6GHz)");
}
lowRssi.append(scanResult.level).append(" / ");
continue;
}
// 跳过不接受新连接的 BSS
NetworkDetail networkDetail = scanDetail.getNetworkDetail();
if (networkDetail != null) {
if (networkDetail.getMboAssociationDisallowedReasonCode()
!= MboOceConstants.MBO_OCE_ATTRIBUTE_NOT_PRESENT) {
// MBO (无线接入点之间的无线管理和优化) 禁止关联 BSSID
mWifiMetrics
.incrementNetworkSelectionFilteredBssidCountDueToMboAssocDisallowInd();
mboAssociationDisallowedBssid.append(scanId).append("(")
.append(networkDetail.getMboAssociationDisallowedReasonCode())
.append(")").append(" / ");
continue;
}
}
// 跳过不符合管理员设置的 SSID 限制的网络
if (adminSsidRestrictionSet) {
WifiSsid ssid = scanResult.getWifiSsid();
// 允许列表策略设置但网络不在列表中
if (!adminSsidAllowlist.isEmpty() && !adminSsidAllowlist.contains(ssid)) {
adminRestrictedSsid.append(scanId).append(" / ");
continue;
}
// 拒绝列表策略设置但网络在列表中
if (!admindSsidDenylist.isEmpty() && admindSsidDenylist.contains(ssid)) {
adminRestrictedSsid.append(scanId).append(" / ");
continue;
}
}
// 跳过不符合管理员设置的最低安全级别限制的网络
if (adminMinimumSecurityLevel != 0) {
boolean securityRestrictionPassed = false;
@WifiInfo.SecurityType int[] securityTypes = scanResult.getSecurityTypes();
for (int type : securityTypes) {
int securityLevel = WifiInfo.convertSecurityTypeToDpmWifiSecurity(type);
// 跳过未知安全类型,因为无法确定安全级别
// 如果所有安全类型都未知,并且设置了最低安全级别限制,则忽略扫描结果
if (securityLevel == WifiInfo.DPM_SECURITY_TYPE_UNKNOWN) continue;
if (adminMinimumSecurityLevel <= securityLevel) {
securityRestrictionPassed = true;
break;
}
}
if (!securityRestrictionPassed) {
adminRestrictedSsid.append(scanId).append(" / ");
continue;
}
}
validScanDetails.add(scanDetail);
}
mWifiMetrics.incrementNetworkSelectionFilteredBssidCount(numBssidFiltered);
// WNS 监听所有单次扫描结果。
// 有些扫描请求可能不包含当前连接网络的频道,
// 所以当前连接的网络不会出现在扫描结果中。
// 为了避免触发断开连接的频繁网络切换,不处理这些扫描结果。
// TODO:这可能不再需要(待修复的 bug)。
for (ClientModeManagerState cmmState : cmmStates) {
if (cmmState.connected && cmmState.wifiInfo.getScore() >= WIFI_POOR_SCORE
&& !scanResultPresentForCurrentBssids.contains(cmmState.wifiInfo.getBSSID())) {
localLog("Current connected BSSID " + cmmState.wifiInfo.getBSSID()
+ " is not in the scan results. Skip network selection.");
validScanDetails.clear();
return validScanDetails;
}
}
if (noValidSsid.length() != 0) {
localLog("Networks filtered out due to invalid SSID: " + noValidSsid);
}
if (blockedBssid.length() != 0) {
localLog("Networks filtered out due to blocklist: " + blockedBssid);
}
if (lowRssi.length() != 0) {
localLog("Networks filtered out due to low signal strength: " + lowRssi);
}
if (mboAssociationDisallowedBssid.length() != 0) {
localLog("Networks filtered out due to mbo association disallowed indication: "
+ mboAssociationDisallowedBssid);
}
if (adminRestrictedSsid.length() != 0) {
localLog("Networks filtered out due to admin restrictions: " + adminRestrictedSsid);
}
return validScanDetails;
}
上面已经返回一个Candidates的范围了,所以在继续回到WCM里面看下面的代码
http://aospxref.com/android-13.0.0_r3/xref/packages/modules/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);
}
// 更新最后一次网络选择的时间戳
mLastNetworkSelectionTimeStamp = mClock.getElapsedSinceBootMillis();
// 更新最后一次可用网络的信息
mWifiLastResortWatchdog.updateAvailableNetworks(
mNetworkSelector.getConnectableScanDetails());
// 统计扫描结果
mWifiMetrics.countScanResults(scanDetails);
// 如果没有候选网络,提前返回
if (candidates == null || candidates.size() == 0) {
localLog(listenerName + ": 没有候选网络");
handleScanResultsWithNoCandidate(handleScanResultsListener);
return;
}
// 如果设备支持STA + STA并且允许OEM有偿/私有网络连接请求
if ((mOemPaidConnectionAllowed || mOemPrivateConnectionAllowed)
&& mActiveModeWarden.isStaStaConcurrencySupportedForRestrictedConnections()) {
// 根据是否是OEM有偿/私有网络将候选网络进行分组
Map<Boolean, List<WifiCandidates.Candidate>> candidatesPartitioned =
candidates.stream()
.collect(Collectors.groupingBy(c -> c.isOemPaid() || c.isOemPrivate()));
List<WifiCandidates.Candidate> primaryCmmCandidates =
candidatesPartitioned.getOrDefault(false, Collections.emptyList());
List<WifiCandidates.Candidate> secondaryCmmCandidates =
candidatesPartitioned.getOrDefault(true, Collections.emptyList());
// 如果存在OEM有偿/私有网络的候选项,则使用辅助CMM流程
if (!secondaryCmmCandidates.isEmpty()) {
handleCandidatesFromScanResultsUsingSecondaryCmmIfAvailable(
listenerName, primaryCmmCandidates, secondaryCmmCandidates,
handleScanResultsListener);
return;
}
// 没有OEM有偿/私有网络的候选项,回退到传统流程
}
// 如果存在双互联网网络请求并且设备支持STA + STA
if (hasMultiInternetConnection() && mMultiInternetManager.hasPendingConnectionRequests()) {
// 尝试连接到多互联网网络
if (handleConnectToMultiInternetConnectionInternal(candidates,
listenerName, handleScanResultsListener)) {
return;
}
// 没有多互联网连接的候选项,回退到传统流程
}
// 使用Mbb(主CMM)来处理主要的候选项
handleCandidatesFromScanResultsForPrimaryCmmUsingMbbIfAvailable(listenerName, candidates, handleScanResultsListener);
---> 看下这个方法:
/**
* 从提供的候选项中选择最佳网络,并启动连接尝试。
*/
private void handleCandidatesFromScanResultsForPrimaryCmmUsingMbbIfAvailable(
@NonNull String listenerName, @NonNull List<WifiCandidates.Candidate> candidates,
@NonNull HandleScanResultsListener handleScanResultsListener) {
// 从候选项中选择最佳网络
WifiConfiguration candidate = mNetworkSelector.selectNetwork(candidates); ---> 看下这个方法
if (candidate != null) {
localLog(listenerName + ": WNS candidate-" + candidate.SSID);
// 使用Mbb(多模式并发)连接尝试连接到主要CMM的网络
connectToNetworkForPrimaryCmmUsingMbbIfAvailable(candidate);
// 处理扫描结果中找到了候选项
handleScanResultsWithCandidate(handleScanResultsListener);
} else {
localLog(listenerName + ": No candidate");
// 处理扫描结果中没有找到候选项
handleScanResultsWithNoCandidate(handleScanResultsListener);
}
}
http://aospxref.com/android-13.0.0_r3/xref/packages/modules/Wifi/service/java/com/android/server/wifi/WifiNetworkSelector.java#selectNetwork
/**
* 从提供的候选项列表中选择最佳网络配置。
*
* @param candidates 候选项列表
* @param overrideEnabled 是否允许用户连接选择覆盖
* @return 最佳网络配置,如果未找到候选项,则返回null
*/
public WifiConfiguration selectNetwork(@NonNull List<WifiCandidates.Candidate> candidates,
boolean overrideEnabled) {
if (candidates == null || candidates.size() == 0) {
return null;
}
// 创建WifiCandidates对象用于评分和选择候选项
WifiCandidates wifiCandidates = new WifiCandidates(mWifiScoreCard, mContext, candidates);
// 更新当前候选项的NetworkSelectionStatus
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;
WifiConfiguration config = mWifiConfigManager
.getConfiguredNetwork(choice.candidateKey.networkId);
if (config == null) continue;
updateNetworkCandidateSecurityParams(config, scanDetail);
}
// 打印候选项按信号强度降序排列的日志
for (Collection<WifiCandidates.Candidate> group : groupedCandidates) {
for (WifiCandidates.Candidate candidate : group.stream()
.sorted((a, b) -> (b.getScanRssi() - a.getScanRssi())) // 按信号强度降序排列
.collect(Collectors.toList())) {
localLog(candidate.toString());
}
}
ArrayMap<Integer, Integer> experimentNetworkSelections = new ArrayMap<>(); // 用于度量
int selectedNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
// 运行所有的CandidateScorers,在Android R的版本主要就是ThroughputScorer,这个S的代码不确定是否还有使用其他的CandidateScorers
boolean legacyOverrideWanted = true;
for (WifiCandidates.CandidateScorer candidateScorer : mCandidateScorers.values()) {
WifiCandidates.ScoredCandidate choice;
try {
choice = wifiCandidates.choose(candidateScorer); ---> 这个就是我们熟悉的老伙计了,评判都在这里
} catch (RuntimeException e) {
Log.wtf(TAG, "Exception running a CandidateScorer", e);
continue;
}
//选出候选者后,拿到对应的网络ID
int networkId = choice.candidateKey == null
? WifiConfiguration.INVALID_NETWORK_ID
: choice.candidateKey.networkId;
String chooses = " would choose ";
if (candidateScorer == activeScorer) {
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);
}
// 更新关于各种方法选择的差异的度量信息
final int activeExperimentId = experimentIdFromIdentifier(activeScorer.getIdentifier());
for (Map.Entry<Integer, Integer> entry :
experimentNetworkSelections.entrySet()) {
int experimentId = entry.getKey();
if (experimentId == activeExperimentId) continue;
int thisSelectedNetworkId = entry.getValue();
mWifiMetrics.logNetworkSelectionDecision(experimentId, activeExperimentId,
selectedNetworkId == thisSelectedNetworkId,
groupedCandidates.size());
}
// 获取反映任何扫描结果更新的新的WifiConfiguration副本
WifiConfiguration selectedNetwork =
mWifiConfigManager.getConfiguredNetwork(selectedNetworkId);
if (selectedNetwork != null && legacyOverrideWanted && overrideEnabled) {
// 根据用户连接选择覆盖,如果需要,修改候选项的用户优先级
selectedNetwork = overrideCandidateWithUserConnectChoice(selectedNetwork);
}
if (selectedNetwork != null) {
mLastNetworkSelectionTimeStamp = mClock.getElapsedSinceBootMillis();
}
return selectedNetwork;
}