我们从 WifiNetworkSelector 入手研究 Android R WiFi 选网机制
在 WifiInjector 的构造过程中, 会初始化 WifiNetworkSelector 对象,并进行 registerCandidateScorer 动作, 之后注册了三个 NetworkNominator 对象
mWifiNetworkSelector.registerCandidateScorer(compatibilityScorer);
mWifiNetworkSelector.registerCandidateScorer(scoreCardBasedScorer);
mWifiNetworkSelector.registerCandidateScorer(bubbleFunScorer);
mWifiNetworkSelector.registerCandidateScorer(throughputScorer);
mWifiMetrics.setWifiNetworkSelector(mWifiNetworkSelector);
// Register the various network Nominators with the network selector.
mWifiNetworkSelector.registerNetworkNominator(mSavedNetworkNominator);
mWifiNetworkSelector.registerNetworkNominator(mNetworkSuggestionNominator);
mWifiNetworkSelector.registerNetworkNominator(mScoredNetworkNominator);
- CompatibilityScorer: 不知道干啥的,从名字看 兼容性评分器
- ScoreCardBasedScorer: 基于 ScoreCard 的评分器
- BubbleFunScorer: 不知道啥意思
- ThroughputScorer: 综合考虑 rssi 和 吞吐 的评分器
这些评分器被添加进 WifiNetworkSelector 的 mCandidateScorers 变量
private final Map<String, WifiCandidates.CandidateScorer> mCandidateScorers = new ArrayMap<>();
至于这些评分器会怎么被应用到,我们后面再看
- SavedNetworkNominator:用于保存的网络
- NetworkSuggestionNominator:用于提供最高可用性候选建议
- ScoredNetworkNominator:用于通过评分机制选择网络连接,用到了 NetworkScoreManager 用于管理网络评分
这三个 NetworkNominator 被添加进 WifiNetworkSelector 的 mNominators 变量
private final List<NetworkNominator> mNominators = new ArrayList<>(3);
至于这三个 NetworkNominator 会被怎么应用到,我们后面再看
WifiConnectivityManager 内部类 AllSingleScanListener 的 onResults 方法会在接收到扫描结果后被调用
onResults 的其中这一行调用 handleScanResults 方法连接选出的网络
boolean wasConnectAttempted = handleScanResults(mScanDetails, ALL_SINGLE_SCAN_LISTENER, isFullBandScanResults);
我们来看 handleScanResults 方法原型及实现
private boolean handleScanResults(List<ScanDetail> scanDetails, String listenerName, boolean isFullScan)
handleScanResults 做了以下事情
- 更新和释放 Block 的 ssid 列表信息;涉及黑名单机制,日后可以再研究研究
// Check if any blocklisted BSSIDs can be freed.
Set<String> bssidBlocklist = mBssidBlocklistMonitor.updateAndGetBssidBlocklist();
-
判断当前是否处于 transient state,如果是的话则不再进一步处理;
-
开始进行 WiFi网络选择
-
通过 WifiNetworkSelector 类的 getCandidatesFromScan 方法对扫描结果进行处理,获取 List<WifiCandidates.Candidate> candidates
-
更新 mLatestCandidates 为最新,并且记录其 timestamp 信息(mLatestCandidatesTimestampMs);
-
如果设备处于高速移动(WifiManager.DEVICE_MOBILITY_STATE_HIGH_MVMT)且可以进行高速移动下的优化选择,则调用 filterCandidatesHighMovement 对获取到的 candidates 进行过滤;
-
继续调用 WifiNetworkSelector 的 selectNetwork 方法选择网络,结果为 WifiConfiguration 类型,同时更新选择时间戳;
-
如果存在被选中的 candidates,则进行连接过程 connectToNetwork;
-
否则如果当前处于 WIFI_STATE_DISCONNECTED 状态,则调用 OpenNetworkNotifier 的 handleScanResults 接口进行处理,主要是在扫描结果中选择网络推荐给应用进行连接
-
下面主要讲讲 getCandidatesFromScan 和 selectNetwork 两个方法
public List<WifiCandidates.Candidate> getCandidatesFromScan(
List<ScanDetail> scanDetails, Set<String> bssidBlacklist, WifiInfo wifiInfo,
boolean connected, boolean disconnected, boolean untrustedNetworkAllowed)
getCandidatesFromScan 做的事情有
-
通过 isNetworkSelectionNeeded 判断是否需要进行网络选择,这个没什么好说的,主要是两个判断条件
- 当前选网需与上次间隔10s,否则不会进行
- 对当前网络进行 sufficiency check
-
遍历 mNominators ,调用其 update 方法
-
通过 filterScanResults 获取 mFilteredNetworks
-
new 一个 WifiCandidates 对象, add 当前网络(我们总是希望当前连接网络也参与选网)
-
遍历所有已注册的 NetworkNominator 对象,执行其 nominateNetworks 接口
需要注重的点是 每个NetworkNominator 的 nominateNetworks 方法
nominateNetworks 方法原型
void nominateNetworks(List<ScanDetail> scanDetails,
WifiConfiguration currentNetwork, String currentBssid,
boolean connected, boolean untrustedNetworkAllowed,
OnConnectableListener onConnectableListener)
OnConnectableListener 定义
public interface OnConnectableListener {
void onConnectable(ScanDetail scanDetail, WifiConfiguration config);
}
在实际的 getCandidatesFromScan 对 nominateNetworks 的调用中,使用了匿名写法
registeredNominator.nominateNetworks(
new ArrayList<>(mFilteredNetworks), currentNetwork, currentBssid, connected,
untrustedNetworkAllowed,
(scanDetail, config) -> {
WifiCandidates.Key key = wifiCandidates.keyFromScanDetailAndConfig(
scanDetail, config);
if (key != null) {
boolean metered = isEverMetered(config, wifiInfo, scanDetail);
// TODO(b/151981920) Saved passpoint candidates are marked ephemeral
boolean added = wifiCandidates.add(key, config,
registeredNominator.getId(),
scanDetail.getScanResult().level,
scanDetail.getScanResult().frequency,
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()));
}
}
});
所以疑点是 WifiCandidates 的一些操作和各个 NetworkNominator 的 nominateNetworks 具体做了些什么
WifiCandidates 见我的这篇解读 WifiCandidates
看完之后就知道是,每个 NetworkNominator nominateNetwork 完之后,onConnectable 会被调用,这里是匿名写法
做的事情就是 把这个参选网络加入到 WifiCandidates 的 mCandidates 中
最后 getCandidatesFromScan 返回 WifiCandidates 的 mCandidates
这样,所有扫描到的网络都经过了 NetworkNominator 的 nominateNetwork, 至于这三个 NetworkNominator 的 nominateNetwork 具体做了什么,后面我们再看
selectNetwork 做的事情有
-
使用传进来的 Candidate 列表重新创建新的 WifiCandidates 对象, 依赖的是 WifiCandidates 重载的构造方法
-
通过 getActiveCandidateScorer 获取 activeScorer
-
生成根据 configuration id 组织的 groupedCandidates ;
-
对每个 group 成员使用 activeScorer 进行 scoreCandidates 处理,生成对应的 ScoredCandidate 对象 choice;
-
调用 WifiConfigManager 的 setNetworkCandidateScanResult 进行 NetworkSelectionStatus更新;
-
接下来遍历所有的 CandidateScorer 采用其 scoreCandidates 方法,对 WifiCandidates 进行计算获得对应的 ScoredCandidate 对象 choice;
-
如果 CandidateScorer 是当前的 activeScorer 则说明是当前最佳选择,记录其 networkId 信息到 selectedNetworkId;
-
根据 selectedNetworkId 记录信息从 WifiConfigManager 通过 getConfiguredNetwork 获取到 WifiConfiguration 信息返回
这里对上述步骤做一些解释:
- 关于 activeScorer : 默认的参选网络评分器是 ThroughputScorer
至于这三个 CandidateScorer 怎么评分的,我们后面再看