Android 8.0/9.0 wifi 自动连接评分机制
前言
Android N wifi auto connect流程分析
Android N selectQualifiedNetwork分析
Wifi自动连接时的评分机制
今天了解了一下Wifi自动连接时的评分机制,总结如下:
WifiConnectivityManager的初始化:
/frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiStateMachine.java
class SupplicantStartedState extends State {
if (mWifiScanner == null) {
mWifiScanner = mWifiInjector.getWifiScanner();
synchronized (mWifiReqCountLock) {
mWifiConnectivityManager =
mWifiInjector.makeWifiConnectivityManager(mWifiInfo,
hasConnectionRequests());
mWifiConnectivityManager.setUntrustedConnectionAllowed(mUntrustedReqCount > 0);
mWifiConnectivityManager.handleScreenStateChanged(mScreenOn);
}
}
/frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiInjector.java
public WifiConnectivityManager makeWifiConnectivityManager(WifiInfo wifiInfo,
boolean hasConnectionRequests) {
return new WifiConnectivityManager(mContext, mWifiStateMachine, getWifiScanner(),
mWifiConfigManager, wifiInfo, mWifiNetworkSelector, mWifiConnectivityHelper,
mWifiLastResortWatchdog, mOpenNetworkNotifier, mWifiMetrics,
mWifiStateMachineHandlerThread.getLooper(), mClock, mConnectivityLocalLog,
hasConnectionRequests, mFrameworkFacade, mSavedNetworkEvaluator,
mScoredNetworkEvaluator, mPasspointNetworkEvaluator);
}
WifiConnectivityManager中会注册三个Evaluator。
/frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiConnectivityManager.java
mNetworkSelector.registerNetworkEvaluator(savedNetworkEvaluator,
SAVED_NETWORK_EVALUATOR_PRIORITY);
if (hs2Enabled) {
mNetworkSelector.registerNetworkEvaluator(passpointNetworkEvaluator,
PASSPOINT_NETWORK_EVALUATOR_PRIORITY);
}
mNetworkSelector.registerNetworkEvaluator(scoredNetworkEvaluator,
SCORED_NETWORK_EVALUATOR_PRIORITY);
继续看下注册方法,其实就是初始化一个NetworkEvaluator数组,大小为6,即优先级从高到低0-5。
/frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiNetworkSelector.java
public static final int EVALUATOR_MIN_PRIORITY = 6;
private final NetworkEvaluator[] mEvaluators = new NetworkEvaluator[MAX_NUM_EVALUATORS];
public boolean registerNetworkEvaluator(NetworkEvaluator evaluator, int priority) {
if (priority < 0 || priority >= EVALUATOR_MIN_PRIORITY) {
localLog("Invalid network evaluator priority: " + priority);
return false;
}
if (mEvaluators[priority] != null) {
localLog("Priority " + priority + " is already registered by "
+ mEvaluators[priority].getName());
return false;
}
mEvaluators[priority] = evaluator;
return true;
}
WifiConnectivityManager的网络评估
private boolean handleScanResults(List<ScanDetail> scanDetails, String listenerName) {
if (mSupportCMCC && mWifiManagerEx.isAutoConnect() == false) {
Log.i(TAG, "WifiManagerEx isAutoConnect false, " + mWifiManagerEx.isAutoConnect());
return false;
}
if (!mP2pWifiCoexistSupported && WifiP2pServiceImpl.mP2pConnectingOrConnected) {
Log.i(TAG, "Do not auto connect when wifi and p2p should not coexsit");
return false;
}
refreshBssidBlacklist();
if (mStateMachine.isLinkDebouncing() || mStateMachine.isSupplicantTransientState()) {
localLog(listenerName + " onResults: No network selection because linkDebouncing is "
+ mStateMachine.isLinkDebouncing() + " and supplicantTransient is "
+ mStateMachine.isSupplicantTransientState());
return false;
}
localLog(listenerName + " onResults: start network selection");
WifiConfiguration candidate =
mNetworkSelector.selectNetwork(scanDetails, buildBssidBlacklist(), mWifiInfo,
mStateMachine.isConnected(), mStateMachine.isDisconnected(),
mUntrustedConnectionAllowed);
mWifiLastResortWatchdog.updateAvailableNetworks(
mNetworkSelector.getConnectableScanDetails());
mWifiMetrics.countScanResults(scanDetails);
if (candidate != null) {
localLog(listenerName + ": WNS candidate-" + candidate.SSID);
connectToNetwork(candidate);
return true;
} else {
if (mWifiState == WIFI_STATE_DISCONNECTED) {
mOpenNetworkNotifier.handleScanResults(
mNetworkSelector.getFilteredScanDetailsForOpenUnsavedNetworks());
}
return false;
}
}
如api注释所述,对周期性、单次和pno扫描的结果进行潜在网络候选者的选择,如果有合适的网络,则进行连接
看下candiate是如何产生的:
public WifiConfiguration selectNetwork(List<ScanDetail> scanDetails,
HashSet<String> bssidBlacklist, WifiInfo wifiInfo,
boolean connected, boolean disconnected, boolean untrustedNetworkAllowed)
mFilteredNetworks.clear();
mConnectableNetworks.clear();
if (scanDetails.size() == 0) {
localLog("Empty connectivity scan result");
return null;
WifiConfiguration currentNetwork =
mWifiConfigManager.getConfiguredNetwork(wifiInfo.getNetworkId());
String currentBssid = wifiInfo.getBSSID();
if (!isNetworkSelectionNeeded(scanDetails, wifiInfo, connected, disconnected)) {
return null;
}
for (NetworkEvaluator registeredEvaluator : mEvaluators) {
if (registeredEvaluator != null) {
registeredEvaluator.update(scanDetails);
}
}
mFilteredNetworks = filterScanResults(scanDetails, bssidBlacklist,
connected, currentBssid);
if (mFilteredNetworks.size() == 0) {
return null;
}
WifiConfiguration selectedNetwork = null;
for (NetworkEvaluator registeredEvaluator : mEvaluators) {
if (registeredEvaluator != null) {
localLog("About to run " + registeredEvaluator.getName() + " :");
selectedNetwork = registeredEvaluator.evaluateNetworks(
new ArrayList<>(mFilteredNetworks), currentNetwork, currentBssid, connected,
untrustedNetworkAllowed, mConnectableNetworks);
if (selectedNetwork != null) {
localLog(registeredEvaluator.getName() + " selects "
+ WifiNetworkSelector.toNetworkString(selectedNetwork) + " : "
+ selectedNetwork.getNetworkSelectionStatus().getCandidate().BSSID);
break;
}
if (selectedNetwork != null) {
selectedNetwork = overrideCandidateWithUserConnectChoice(selectedNetwork);
mLastNetworkSelectionTimeStamp = mClock.getElapsedSinceBootMillis();
}
return selectedNetwork;
}
看下是否可以进行网络选择:
1、 当前网络无效时
2、 当前没有连接网络时
private boolean isNetworkSelectionNeeded(List<ScanDetail> scanDetails, WifiInfo wifiInfo,
boolean connected, boolean disconnected) {
if (scanDetails.size() == 0) {
localLog("Empty connectivity scan results. Skip network selection.");
return false;
}
if (connected) {
if (isCurrentNetworkSufficient(wifiInfo, scanDetails)) {
localLog("Current connected network already sufficient. Skip network selection.");
return false;
} else {
localLog("Current connected network is not sufficient.");
return true;
}
} else if (disconnected) {
return true;
} else {
判断网络是否有效,主要是看以下几点要素:
- rssi(区分2.4G还是5G)
- packet rate
- ephemeral(短暂的)
- open network
- 5G优先级>2.4G
private boolean isCurrentNetworkSufficient(WifiInfo wifiInfo, List<ScanDetail> scanDetails) {
WifiConfiguration network =
mWifiConfigManager.getConfiguredNetwork(wifiInfo.getNetworkId());
int currentRssi = wifiInfo.getRssi();
boolean hasQualifiedRssi =
(wifiInfo.is24GHz() && (currentRssi > mThresholdQualifiedRssi24))
|| (wifiInfo.is5GHz() && (currentRssi > mThresholdQualifiedRssi5));
boolean hasActiveStream = (wifiInfo.getTxSuccessRatePps() > mStayOnNetworkMinimumTxRate)
|| (wifiInfo.getRxSuccessRatePps() > mStayOnNetworkMinimumRxRate);
if (hasQualifiedRssi && hasActiveStream) {
localLog("Stay on current network because of good RSSI and ongoing traffic");
return true;
}
if (network.ephemeral) {
localLog("Current network is an ephemeral one.");
return false;
}
if (WifiConfigurationUtil.isConfigForOpenNetwork(network)) {
localLog("Current network is a open one.");
return false;
}
if (wifiInfo.is24GHz()) {
if (is5GHzNetworkAvailable(scanDetails)) {
localLog("Current network is 2.4GHz. 5GHz networks available.");
return false;
}
}
if (!hasQualifiedRssi) {
localLog("Current network RSSI[" + currentRssi + "]-acceptable but not qualified.");
return false;
}
return true;
}
SavedNetworkEvaluator的筛选
首先遍历扫描结果:
for (ScanDetail scanDetail : scanDetails) {
ScanResult scanResult = scanDetail.getScanResult();
int highestScoreOfScanResult = Integer.MIN_VALUE;
int candidateIdOfScanResult = WifiConfiguration.INVALID_NETWORK_ID;
//过滤passpoint和ephemeral,如api所述这类网络交由PasspointNetworkEvaluator
//和ScoredNetworkEvaluator进行评估
if (network.isPasspoint() || network.isEphemeral()) {
continue;
}
接着过滤没有enabled的,scan bssid和config对不上的和eap-sim之类的网络但是没插卡的。
if (!status.isNetworkEnabled()) {
continue;
} else if (network.BSSID != null && !network.BSSID.equals("any")
&& !network.BSSID.equals(scanResult.BSSID)) {
localLog("Network " + WifiNetworkSelector.toNetworkString(network)+ " has specified BSSID " +
network.BSSID + ". Skip " + scanResult.BSSID);
continue;
} else if (TelephonyUtil.isSimConfig(network)
&& !mWifiConfigManager.isSimPresent()) {
localLog("isSimPresent");
continue;
}
网络评分的关键:计算BSSID的分数,评分几大要素如下:
- RSSI
- 5G
- lastUserSelectedNetworkId
- currentNetwork
- isFirmwareRoamingSupported
- isConfigForOpenNetwork
private int calculateBssidScore(ScanResult scanResult, WifiConfiguration network,
WifiConfiguration currentNetwork, String currentBssid,
StringBuffer sbuf) {
int score = 0;
boolean is5GHz = scanResult.is5GHz();
sbuf.append("[ ").append(scanResult.SSID).append(" ").append(scanResult.BSSID)
.append(" RSSI:").append(scanResult.level).append(" ] ");
int rssiSaturationThreshold = is5GHz ? mThresholdSaturatedRssi5 : mThresholdSaturatedRssi24;
int rssi = scanResult.level < rssiSaturationThreshold ? scanResult.level
: rssiSaturationThreshold;
score += (rssi + mRssiScoreOffset) * mRssiScoreSlope;
sbuf.append(" RSSI score: ").append(score).append(",");
//如果是5G频段,会有奖励。
if (is5GHz) {
score += mBand5GHzAward;
sbuf.append(" 5GHz bonus: ").append(mBand5GHzAward).append(",");
}
//之前连接过也有奖励
int lastUserSelectedNetworkId = mWifiConfigManager.getLastSelectedNetwork();
if (lastUserSelectedNetworkId != WifiConfiguration.INVALID_NETWORK_ID
&& lastUserSelectedNetworkId == network.networkId) {
long timeDifference = mClock.getElapsedSinceBootMillis()
- mWifiConfigManager.getLastSelectedTimeStamp();
if (timeDifference > 0) {
int bonus = mLastSelectionAward - (int) (timeDifference / 1000 / 60);
score += bonus > 0 ? bonus : 0;
sbuf.append(" User selection ").append(timeDifference / 1000 / 60)
.append(" minutes ago, bonus: ").append(bonus).append(",");
}
}
//如果是当前正在连接的网络,也会加分
if (currentNetwork != null
&& (network.networkId == currentNetwork.networkId
/* || network.isLinked(currentNetwork) */)) {
score += mSameNetworkAward;
sbuf.append(" Same network bonus: ").append(mSameNetworkAward).append(",");
//支持漫游也会有奖励
if (mConnectivityHelper.isFirmwareRoamingSupported()
&& currentBssid != null && !currentBssid.equals(scanResult.BSSID)) {
score += mSameBssidAward;
sbuf.append(" Equivalent BSSID bonus: ").append(mSameBssidAward).append(",");
}
}
//BSSID相同也会有奖励
if (currentBssid != null && currentBssid.equals(scanResult.BSSID)) {
score += mSameBssidAward;
sbuf.append(" Same BSSID bonus: ").append(mSameBssidAward).append(",");
}
//不是开放的网络,也会有奖励
if (!WifiConfigurationUtil.isConfigForOpenNetwork(network)) {
score += mSecurityAward;
sbuf.append(" Secure network bonus: ").append(mSecurityAward).append(",");
sbuf.append(" ## Total score: ").append(score).append("\n");
return score;
}
Android 8.0/9.0 wifi 自动连接评分机制的更多相关文章
- 最新发布树莓派2代Wi-Fi自动连接实战(适合初学者)
话说天地会珠海分舵在上几天才刚给大家分享了个海外资讯说树莓派2已经发布且Windows10加盟之类的资讯,具体请查看<海外优秀资讯抢先看8 - Windows 10 for Raspberry ...
- Android实现推送方式解决方案 - 长连接+心跳机制(MQTT协议)
本文介绍在Android中实现推送方式的基础知识及相关解决方案.推送功能在手机开发中应用的场景是越来起来了,不说别的,就我们手机上的新闻客户端就时不j时的推送过来新的消息,很方便的阅读最新的新闻信息. ...
- Android 6.0 中的 Wifi 连接
Android 6.0 中的 Wifi 连接 这几天在写一个软件,结果被其中的 wifi 连接问题困扰了 3 天. 先描述下需求: usb 接口接了一根 usb2serial,通过这个接口接收命令 当 ...
- Android自动连接指定的wifi,免密码或指定密码
一.运行时的状态 遇到一个这样的要求:“不进行扫描操作,怎么对指定的免密码WIFI进行连接(之前没有连接过)”,于是动手写了一个Demo,如图所示未连接成功时的状态,第一个编辑框让用户输入SSID,第 ...
- 转-Android中自动连接到指定SSID的Wi-Fi
最近在做一个项目,其中涉及到一块“自动连接已存在的wifi热点”的功能,在网上查阅了大量资料,五花八门,但其中一些说的很简单,即不能实现傻瓜式的拿来就用,有些说的很详细,但其中不乏些许错误造成功能无法 ...
- 【转】Android中自动连接到指定SSID的Wi-Fi
最近在做一个项目,其中涉及到一块“自动连接已存在的wifi热点”的功能,在网上查阅了大量资料,五花八门,但其中一些说的很简单,即不能实现傻瓜式的拿来就用,有些说的很详细,但其中不乏些许错误造成功能无法 ...
- Android wifi 从连接态自动断开的解决办法(dhcp导致)【转】
本文转载自:http://blog.csdn.net/DKBDKBDKB/article/details/38490201 对wifi部分的代码流程已经看了段时间,前两天终于解决了工作中遇到的一个wi ...
- android.os.NetworkOnMainThreadException 在4.0之后谷歌强制要求连接网络不能在主线程进行访问
谷歌在4.0系统以后就禁止在主线程中进行网络访问了,原因是: 主线程是负责UI的响应,如果在主线程进行网络访问,超过5秒的话就会引发强制关闭, 所以这种耗时的操作不能放在主线程里.放在子线程里,而子线 ...
- 【树莓派】【转】树莓派3装Android 6.0,支持Wi-Fi和蓝牙
树莓派3装Android 6.0,支持Wi-Fi和蓝牙 相信对于许多树莓派初学者(包括我)来说,Android系统的确是一个不错的选择.但国内这方面资源稀缺,经本人FQ苦寻,找到了老外的树莓派Andr ...
随机推荐
- Python批量创建word文档(2)- 加图片和表格
Python创建word文档,任务要求:小杨在一家公司上班,每天都需要给不同的客户发送word文档,以告知客户每日黄金价格.要求在文档开始处给出banner条,价格日期等用表格表示.最后贴上自己的联系 ...
- Excel 快速跳到表格最后一行/第一行
快速跳到表格的最后一行 首先鼠标选中一个带有数据的单元格,点击shift键,把鼠标放到该单元格底部的边缘地带,出现带四个方向的箭头为止,再连续点击鼠标左键两次,直接跳到表格的最后一行 快速跳到表格的最 ...
- PP模块的组织架构
组织架构的层次,从上而下依次是: (1),集团-->对应系统client级别,用户登录SAP系统时,每一个client就是对应一个集团:在数据库中,每一个client都对应一个唯一的标识. (2 ...
- 【Shiro学习之六】shiro编码/加密
apahce shiro:1.6.0 密码存储,应该加密/生成密码摘要存储,而不是存储明文密码. 1.编码/解码Shiro 提供了 base64和 16进制字符串编码/解码的API支持, 方便一些编码 ...
- Linux目录的慨念
1.路径 对文件进行访问时,要用到"路径"(Path)的慨念. 顾名思义,路径是指从树型目录中的某个目录层次到某个文件的一条道路. 此路径的主要构成是目录名称,中间用"/ ...
- TurtleBot3 Waffle (tx2版华夫)(3)opencr系统安装
3. 1. 安装 Arduino IDE Opencr的安装环境的安装包,双击打开即可.进入安装的过程. 在这里你可以选择自己的安装位置. 安装已经完成,点击CLOSE 即可. 3.2. opencr ...
- JavaScript CMD规范
CMD是另一种js模块化方案,它与AMD很类似,不同点在于:AMD 推崇依赖前置.提前执行,CMD推崇依赖就近.延迟执行.此规范其实是在sea.js推广过程中产生的. AMD规范请移步AMD 模块化规 ...
- 使用Lists.partition切分性能优化
项目实战 影拓邦电影同步中,使用Lists.partition按500条长度进行切分,来实现es的同步. 切分的List为 使用介绍及示例 将list集合按指定长度进行切分,返回新的List<L ...
- netty服务端客户端启动流程分析
服务端启动流程 我们回顾前面讲解的netty启动流程,服务端这边有两个EventLoopGroup,一个专门用来处理连接,一个用来处理后续的io事件 服务端启动还是跟nio一样,绑定端口进行监听,我们 ...
- VRP CommandLines
<> 用户视图 通过 system-view 进入系统视图 [] 系统视图 通过interface 0/0/0 进入接口视图 CTRL+Z 返回用户视图 CTRL+A 把光标移动到当前命令 ...