Android N selectQualifiedNetwork分析
前言:
参考:Android N wifi auto connect流程分析
后续
分析
前面说了,handleScanResults会去调QualifiedNetworkSelector.selectQualifiedNetwork去筛选目标ssid,selectQualifiedNetwork是网络的选择算法,我们分析下它的原理
1、判断是否需要needQualifiedNetworkSelection,如果返回false,代表
- scanDetails=null or isLinkDebouncing
- or skipQualifiedNetworkSelectionForAutoConnect(auto connect disabled)
- or connecting/disconnecting or WifiConfiguration=null
- or not allowed to switch network
- or short time pass since last select
2、迭代所有扫描结果,找到得分最高的最佳候选人
- ssid=null, add to noValidSsid, ignore
- isBlackListed() ignore, addToBlacklist(bssid) will add to blackList
- too weak signal strength add to lowSignalScan, ignore 2.4G level<-85 5G level<-85
- 未保存的网络(添加到notSavedScan)或保存但短暂,忽略
- 计算每个关联网络不是短暂的扫描结果的得分。由于一个扫描结果可以关联多个网络,我们需要计算所有,并使用最高的一个作为扫描结果的分数。
3、现在我们需要遍历整个用户偏好来选择一个用户最喜欢的
public WifiConfiguration selectQualifiedNetwork(boolean forceSelectNetwork ,
boolean isUntrustedConnectionsAllowed, List<ScanDetail> scanDetails,
boolean isLinkDebouncing, boolean isConnected, boolean isDisconnected,
boolean isSupplicantTransient) {
localLog("==========start qualified Network Selection==========");
mScanDetails = scanDetails;
List<Pair<ScanDetail, WifiConfiguration>> filteredScanDetails = new ArrayList<>();
if (mCurrentConnectedNetwork == null) {
mCurrentConnectedNetwork =
mWifiConfigManager.getWifiConfiguration(mWifiInfo.getNetworkId());
}
// Always get the current BSSID from WifiInfo in case that firmware initiated roaming
// happened.
mCurrentBssid = mWifiInfo.getBSSID();
/*1.判断是否需要needQualifiedNetworkSelection,如果返回false,代表
* -scanDetails=null or isLinkDebouncing
* -or skipQualifiedNetworkSelectionForAutoConnect(auto connect disabled)
* -or connecting/disconnecting or WifiConfiguration=null
* -or not allowed to switch network
* -or short time pass since last select
*/
if (!forceSelectNetwork && !needQualifiedNetworkSelection(isLinkDebouncing, isConnected,
isDisconnected, isSupplicantTransient)) {
localLog("Quit qualified Network Selection since it is not forced and current network"
+ " is qualified already");
mFilteredScanDetails = filteredScanDetails;
return null;
}
int currentHighestScore = Integer.MIN_VALUE;
ScanResult scanResultCandidate = null;
WifiConfiguration networkCandidate = null;
final ExternalScoreEvaluator externalScoreEvaluator =
new ExternalScoreEvaluator(mLocalLog, mDbg);
String lastUserSelectedNetWorkKey = mWifiConfigManager.getLastSelectedConfiguration();
WifiConfiguration lastUserSelectedNetwork =
mWifiConfigManager.getWifiConfiguration(lastUserSelectedNetWorkKey);
if (lastUserSelectedNetwork != null) {
localLog("Last selection is " + lastUserSelectedNetwork.SSID + " Time to now: "
+ ((mClock.elapsedRealtime() - mWifiConfigManager.getLastSelectedTimeStamp())
/ 1000 / 60 + " minutes"));
}
updateSavedNetworkSelectionStatus();
updateBssidBlacklist();
StringBuffer lowSignalScan = new StringBuffer();
StringBuffer notSavedScan = new StringBuffer();
StringBuffer noValidSsid = new StringBuffer();
StringBuffer scoreHistory = new StringBuffer();
ArrayList<NetworkKey> unscoredNetworks = new ArrayList<NetworkKey>();
//iterate all scan results and find the best candidate with the highest score
//2.迭代所有扫描结果,找到得分最高的最佳候选人
for (ScanDetail scanDetail : mScanDetails) {
ScanResult scanResult = scanDetail.getScanResult();
//skip bad scan result
//2.1 ssid=null, add to noValidSsid, ignore
if (scanResult.SSID == null || TextUtils.isEmpty(scanResult.SSID)) {
if (mDbg) {
//We should not see this in ePNO
noValidSsid.append(scanResult.BSSID + " / ");
}
continue;
}
final String scanId = toScanId(scanResult);
//check whether this BSSID is blocked or not
//2.2 isBlackListed() ignore, addToBlacklist(bssid) will add to blackList
if (mWifiConfigManager.isBssidBlacklisted(scanResult.BSSID)
|| isBssidDisabled(scanResult.BSSID)) {
//We should not see this in ePNO
Log.e(TAG, scanId + " is in blacklist.");
continue;
}
//skip scan result with too weak signals
//2.3 too weak signal strength add to lowSignalScan, ignore 2.4G level<-85 5G level<-85
if ((scanResult.is24GHz() && scanResult.level
< mWifiConfigManager.mThresholdMinimumRssi24.get())
|| (scanResult.is5GHz() && scanResult.level
< mWifiConfigManager.mThresholdMinimumRssi5.get())) {
if (mDbg) {
lowSignalScan.append(scanId + "(" + (scanResult.is24GHz() ? "2.4GHz" : "5GHz")
+ ")" + scanResult.level + " / ");
}
continue;
}
//check if there is already a score for this network
if (mNetworkScoreCache != null && !mNetworkScoreCache.isScoredNetwork(scanResult)) {
//no score for this network yet.
WifiKey wifiKey;
try {
wifiKey = new WifiKey("\"" + scanResult.SSID + "\"", scanResult.BSSID);
NetworkKey ntwkKey = new NetworkKey(wifiKey);
//add to the unscoredNetworks list so we can request score later
unscoredNetworks.add(ntwkKey);
} catch (IllegalArgumentException e) {
Log.w(TAG, "Invalid SSID=" + scanResult.SSID + " BSSID=" + scanResult.BSSID
+ " for network score. Skip.");
}
}
//check whether this scan result belong to a saved network
//2.4 未保存的网络(添加到notSavedScan)或保存但短暂,忽略
boolean potentiallyEphemeral = false;
// Stores WifiConfiguration of potential connection candidates for scan result filtering
WifiConfiguration potentialEphemeralCandidate = null;
List<WifiConfiguration> associatedWifiConfigurations =
mWifiConfigManager.updateSavedNetworkWithNewScanDetail(scanDetail,
isSupplicantTransient || isConnected || isLinkDebouncing);
if (associatedWifiConfigurations == null) {
potentiallyEphemeral = true;
if (mDbg) {
notSavedScan.append(scanId + " / ");
}
} else if (associatedWifiConfigurations.size() == 1) {
//if there are more than 1 associated network, it must be a passpoint network
WifiConfiguration network = associatedWifiConfigurations.get(0);
if (network.ephemeral) {
potentialEphemeralCandidate = network;
potentiallyEphemeral = true;
}
}
// Evaluate the potentially ephemeral network as a possible candidate if untrusted
// connections are allowed and we have an external score for the scan result.
if (potentiallyEphemeral) {
if (isUntrustedConnectionsAllowed) {
Integer netScore = getNetworkScore(scanResult, false);
if (netScore != null
&& !mWifiConfigManager.wasEphemeralNetworkDeleted(scanResult.SSID)) {
externalScoreEvaluator.evalUntrustedCandidate(netScore, scanResult);
// scanDetail is for available ephemeral network
filteredScanDetails.add(Pair.create(scanDetail,
potentialEphemeralCandidate));
}
}
continue;
}
// 2.5 计算每个关联网络不是短暂的扫描结果的得分。由于一个扫描结果可以关联多个网络,
//我们需要计算所有,并使用最高的一个作为扫描结果的分数。
// calculate the score of each scanresult whose associated network is not ephemeral. Due
// to one scan result can associated with more than 1 network, we need calculate all
// the scores and use the highest one as the scanresults score.
int highestScore = Integer.MIN_VALUE;
int score;
WifiConfiguration configurationCandidateForThisScan = null;
WifiConfiguration potentialCandidate = null;
for (WifiConfiguration network : associatedWifiConfigurations) {
WifiConfiguration.NetworkSelectionStatus status =
network.getNetworkSelectionStatus();
status.setSeenInLastQualifiedNetworkSelection(true);
if (potentialCandidate == null) {
potentialCandidate = network;
}
if (!status.isNetworkEnabled()) {
continue;
} else if (network.BSSID != null && !network.BSSID.equals("any")
&& !network.BSSID.equals(scanResult.BSSID)) {
//in such scenario, user (APP) has specified the only BSSID to connect for this
// configuration. So only the matched scan result can be candidate
localLog("Network: " + getNetworkString(network) + " has specified" + "BSSID:"
+ network.BSSID + ". Skip " + scanResult.BSSID);
continue;
}
// If the network is marked to use external scores then attempt to fetch the score.
// These networks will not be considered alongside the other saved networks.
if (network.useExternalScores) {
Integer netScore = getNetworkScore(scanResult, false);
externalScoreEvaluator.evalSavedCandidate(netScore, network, scanResult);
continue;
}
score = calculateBssidScore(scanResult, network, mCurrentConnectedNetwork,
(mCurrentBssid == null ? false : mCurrentBssid.equals(scanResult.BSSID)),
(lastUserSelectedNetwork == null ? false : lastUserSelectedNetwork.networkId
== network.networkId), scoreHistory);
if (score > highestScore) {
highestScore = score;
configurationCandidateForThisScan = network;
potentialCandidate = network;
}
//update the cached candidate
if (score > status.getCandidateScore() || (score == status.getCandidateScore()
&& status.getCandidate() != null
&& scanResult.level > status.getCandidate().level)) {
status.setCandidate(scanResult);
status.setCandidateScore(score);
}
}
// Create potential filteredScanDetail entry
filteredScanDetails.add(Pair.create(scanDetail, potentialCandidate));
if (highestScore > currentHighestScore || (highestScore == currentHighestScore
&& scanResultCandidate != null
&& scanResult.level > scanResultCandidate.level)) {
currentHighestScore = highestScore;
scanResultCandidate = scanResult;
networkCandidate = configurationCandidateForThisScan;
networkCandidate.getNetworkSelectionStatus().setCandidate(scanResultCandidate);
}
}
mFilteredScanDetails = filteredScanDetails;
//kick the score manager if there is any unscored network
if (mScoreManager != null && unscoredNetworks.size() != 0) {
NetworkKey[] unscoredNetworkKeys =
unscoredNetworks.toArray(new NetworkKey[unscoredNetworks.size()]);
mScoreManager.requestScores(unscoredNetworkKeys);
}
if (mDbg) {
localLog(lowSignalScan + " skipped due to low signal\n");
localLog(notSavedScan + " skipped due to not saved\n ");
localLog(noValidSsid + " skipped due to not valid SSID\n");
localLog(scoreHistory.toString());
}
//we need traverse the whole user preference to choose the one user like most now
//2.6 现在我们需要遍历整个用户偏好来选择一个用户最喜欢的
if (scanResultCandidate != null) {
WifiConfiguration tempConfig = networkCandidate;
while (tempConfig.getNetworkSelectionStatus().getConnectChoice() != null) {
String key = tempConfig.getNetworkSelectionStatus().getConnectChoice();
tempConfig = mWifiConfigManager.getWifiConfiguration(key);
if (tempConfig != null) {
WifiConfiguration.NetworkSelectionStatus tempStatus =
tempConfig.getNetworkSelectionStatus();
if (tempStatus.getCandidate() != null && tempStatus.isNetworkEnabled()) {
scanResultCandidate = tempStatus.getCandidate();
networkCandidate = tempConfig;
}
} else {
//we should not come here in theory
localLoge("Connect choice: " + key + " has no corresponding saved config");
break;
}
}
localLog("After user choice adjust, the final candidate is:"
+ getNetworkString(networkCandidate) + " : " + scanResultCandidate.BSSID);
}
// At this point none of the saved networks were good candidates so we fall back to
// externally scored networks if any are available.
if (scanResultCandidate == null) {
localLog("Checking the externalScoreEvaluator for candidates...");
networkCandidate = getExternalScoreCandidate(externalScoreEvaluator);
if (networkCandidate != null) {
scanResultCandidate = networkCandidate.getNetworkSelectionStatus().getCandidate();
}
}
if (scanResultCandidate == null) {
localLog("Can not find any suitable candidates");
return null;
}
String currentAssociationId = mCurrentConnectedNetwork == null ? "Disconnected" :
getNetworkString(mCurrentConnectedNetwork);
String targetAssociationId = getNetworkString(networkCandidate);
//In passpoint, saved configuration has garbage SSID. We need update it with the SSID of
//the scan result.
if (networkCandidate.isPasspoint()) {
// This will update the passpoint configuration in WifiConfigManager
networkCandidate.SSID = "\"" + scanResultCandidate.SSID + "\"";
}
//For debug purpose only
if (scanResultCandidate.BSSID.equals(mCurrentBssid)) {
localLog(currentAssociationId + " is already the best choice!");
} else if (mCurrentConnectedNetwork != null
&& (mCurrentConnectedNetwork.networkId == networkCandidate.networkId
|| mCurrentConnectedNetwork.isLinked(networkCandidate))) {
localLog("Roaming from " + currentAssociationId + " to " + targetAssociationId);
} else {
localLog("reconnect from " + currentAssociationId + " to " + targetAssociationId);
}
mCurrentBssid = scanResultCandidate.BSSID;
mCurrentConnectedNetwork = networkCandidate;
mLastQualifiedNetworkSelectionTimeStamp = mClock.elapsedRealtime();
return networkCandidate;
}
Android N selectQualifiedNetwork分析的更多相关文章
- Android APP性能分析方法及工具
近期读到<Speed up your app>一文.这是一篇关于Android APP性能分析.优化的文章.在这篇文章中,作者介绍他的APP分析优化规则.使用的工具和方法.我觉得值得大家借 ...
- Android之mtklog分析
Android之mtklog分析 [海外场测反馈][xxx]动态测试时对比机xxxx拨打测试机xxxxx自动挂断电话 工作中遇到一个掉话的问题,需要分析log,log比较大,我也没法上传,就简答的讲讲 ...
- Android源码分析-全面理解Context
前言 Context在android中的作用不言而喻,当我们访问当前应用的资源,启动一个新的activity的时候都需要提供Context,而这个Context到底是什么呢,这个问题好像很好回答又好像 ...
- cocos2d-x for android:SimpleGame分析
cocos2d-x for android:SimpleGame分析 作为cocos2d-x的标配DEMO,SimpleGame可算是给入门学cocos2d-x的俺们这些新手门学习的对象了,那么来分析 ...
- Android内存机制分析1——了解Android堆和栈
//----------------------------------------------------------------------------------- Android内存机制分析1 ...
- Android 内存管理分析(四)
尊重原创作者,转载请注明出处: http://blog.csdn.net/gemmem/article/details/8920039 最近在网上看了不少Android内存管理方面的博文,但是文章大多 ...
- Qualcomm Android display架构分析
Android display架构分析(一) http://blog.csdn.net/BonderWu/archive/2010/08/12/5805961.aspx http://hi.baidu ...
- Android 消息处理源代码分析(1)
Android 消息处理源代码分析(1) 在Android中,通常被使用的消息队列的代码在文件夹\sources\android-22\android\os下,涉及到下面几个类文件 Handler.j ...
- Android平台APK分析工具包androguard的部署使用和原理分析
原创文章,转载请注明出处,谢谢. Android应用程序分析主要有静态分析和动态分析两种,常见的静态分析工具是Apktool.dex2jar以及jdgui.今天突然主要到Google code上有个叫 ...
随机推荐
- Autofac官方文档翻译--一、注册组件--1注册概念
官方文档:http://docs.autofac.org/en/latest/register/registration.html 一.注册概念 使用Autofac 注册组件,通过创建一个Contai ...
- Apache Cassandra——可扩展微服务应用程序的持久数据存储
通过使用微服务,团队可以更快地响应变化,而无需改动整个应用程序.利用微服务,开发团队可以构建出具有鲁棒性和可扩展性的系统,从而适应当今应用程序的需求. 然而,使用微服务也带来了一系列挑战.在本文中 ...
- 【分布式锁的演化】“超卖场景”,MySQL分布式锁篇
前言 之前的文章中通过电商场景中秒杀的例子和大家分享了单体架构中锁的使用方式,但是现在很多应用系统都是相当庞大的,很多应用系统都是微服务的架构体系,那么在这种跨jvm的场景下,我们又该如何去解决并发. ...
- MobPush厂商通道排查
开启log 一.添加开启log代码 有AndroidManifest.xml的 //在AndroidManifest.xml的application标签中添加 <meta-data androi ...
- 改进你的c#代码的5个技巧(二)
在本文中,我将向你展示c#编程的5个最佳实践.我从日常编程经验中学到了这些实践.我在release模式下测试了所有的代码,并在开发环境稳定后进行了截屏.我想你会喜欢这些建议的. 在使用数据类型之前选择 ...
- umi3.2+ targets ie不生效的问题
直接创建umi项目 config.ts对targets{ie:11}不生效 错误方法1: 在入口页app.tsx新增 import @babel/polyfill 错误方法2: 在入口页app.tsx ...
- JS 字符串比较"=="与"==="区别
最近课程油js的课程,课后习题有道关于下面 1 类似的一道题,叫比较然后判断结果,最开始看了网上的知识点,还是有点不太懂,个人感觉模模糊糊的(当然我自己菜,是正常的),就用依稀还记得的java对象与引 ...
- 基于nginx实现web服务器的双机热备
1.适用场景 对于部署重要的服务,会使用两台服务器,互相备份,共同执行同一服务.当一台服务器出现故障时,可以由另一台服务器承担服务任务,从而在不需要人工干预的情况下,自动保证系统能持续提供服务.双机热 ...
- VsCode/Pycharm配合python env 使用
前言 用惯了vscode,这几天试了一下pycharm,还是回来了. pycharm一个好处就是python env 环境支持的比较好, vscode虽然也支持但是要改一些东西 env的使用查看我的上 ...
- docker+mysql集群+读写分离+mycat管理+垂直分库+负载均衡
依然如此,只要大家跟着我的步骤一步步来,100%是可以测试成功的 centos6.8已不再维护,可能很多人的虚拟机中无法使用yum命令下载docker, 但是阿里源还是可以用的 因为他的centos- ...