前言

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 自动连接评分机制的更多相关文章

  1. 最新发布树莓派2代Wi-Fi自动连接实战(适合初学者)

    话说天地会珠海分舵在上几天才刚给大家分享了个海外资讯说树莓派2已经发布且Windows10加盟之类的资讯,具体请查看<海外优秀资讯抢先看8 - Windows 10 for Raspberry ...

  2. Android实现推送方式解决方案 - 长连接+心跳机制(MQTT协议)

    本文介绍在Android中实现推送方式的基础知识及相关解决方案.推送功能在手机开发中应用的场景是越来起来了,不说别的,就我们手机上的新闻客户端就时不j时的推送过来新的消息,很方便的阅读最新的新闻信息. ...

  3. Android 6.0 中的 Wifi 连接

    Android 6.0 中的 Wifi 连接 这几天在写一个软件,结果被其中的 wifi 连接问题困扰了 3 天. 先描述下需求: usb 接口接了一根 usb2serial,通过这个接口接收命令 当 ...

  4. Android自动连接指定的wifi,免密码或指定密码

    一.运行时的状态 遇到一个这样的要求:“不进行扫描操作,怎么对指定的免密码WIFI进行连接(之前没有连接过)”,于是动手写了一个Demo,如图所示未连接成功时的状态,第一个编辑框让用户输入SSID,第 ...

  5. 转-Android中自动连接到指定SSID的Wi-Fi

    最近在做一个项目,其中涉及到一块“自动连接已存在的wifi热点”的功能,在网上查阅了大量资料,五花八门,但其中一些说的很简单,即不能实现傻瓜式的拿来就用,有些说的很详细,但其中不乏些许错误造成功能无法 ...

  6. 【转】Android中自动连接到指定SSID的Wi-Fi

    最近在做一个项目,其中涉及到一块“自动连接已存在的wifi热点”的功能,在网上查阅了大量资料,五花八门,但其中一些说的很简单,即不能实现傻瓜式的拿来就用,有些说的很详细,但其中不乏些许错误造成功能无法 ...

  7. Android wifi 从连接态自动断开的解决办法(dhcp导致)【转】

    本文转载自:http://blog.csdn.net/DKBDKBDKB/article/details/38490201 对wifi部分的代码流程已经看了段时间,前两天终于解决了工作中遇到的一个wi ...

  8. android.os.NetworkOnMainThreadException 在4.0之后谷歌强制要求连接网络不能在主线程进行访问

    谷歌在4.0系统以后就禁止在主线程中进行网络访问了,原因是: 主线程是负责UI的响应,如果在主线程进行网络访问,超过5秒的话就会引发强制关闭, 所以这种耗时的操作不能放在主线程里.放在子线程里,而子线 ...

  9. 【树莓派】【转】树莓派3装Android 6.0,支持Wi-Fi和蓝牙

    树莓派3装Android 6.0,支持Wi-Fi和蓝牙 相信对于许多树莓派初学者(包括我)来说,Android系统的确是一个不错的选择.但国内这方面资源稀缺,经本人FQ苦寻,找到了老外的树莓派Andr ...

随机推荐

  1. Centos7.6上利用docker搭建Jenkins来自动化部署Django项目

    一般情况下,将一个项目部署到生产环境的流程如下: 需求分析-原型设计-开发代码-内网部署-提交测试-确认上线-备份数据-外网更新-最终测试,如果发现外网部署的代码有异常,需要及时回滚. 整个过程相当复 ...

  2. JS中Generator的学习小记

    Generator的异步实现 整理了一下在学习和使用JS异步过程中的一些知识点.核心是在Generator实例的的回调中调度实例的下一步,同样的思想也能用于其它语言.比如Python中使用Genera ...

  3. idea2020 没有 Autoscroll from Source

    2018版本: 2020版本: 最后在官网的网站中找到了解决方案,原来是改名了: 网址:https://intellij-support.jetbrains.com/hc/en-us/communit ...

  4. 推荐系统中的nlp知识

    都是转自其他博客,好好学习! 概述: https://blog.csdn.net/starzhou/article/details/73930117 tf-idf https://blog.csdn. ...

  5. 手写系列:call、apply、bind、函数柯里化

    少废话,show my code call 原理都在注释里了 // 不覆盖原生call方法,起个别名叫myCall,接收this上下文context和参数params Function.prototy ...

  6. PowerCLI从模版批量新建虚拟机

    有一个需求,新建200个虚拟机,这要是一个一个去建那我可就疯了.想起之前听vmware原厂工程师说过的这个powercli,就着手研究了一下. 将以下文件存储为 *.ps1 #在使用时应使用VMwar ...

  7. Redis+LUA整合使用

    .前言 从本章节开始我们就开始讲解一些 Redis 的扩展应用了,之前讲的主从.哨兵和集群都相当重要,也许小公司用不到集群这么复杂的架构,但是也要了解各知识点的原理,只要了解了原理,无论什么时候是有, ...

  8. C++ string的内部究竟是什么样的?

    在C语言中,有两种方式表示字符串: 一种是用字符数组来容纳字符串,例如char str[10] = "abc",这样的字符串是可读写的: 一种是使用字符串常量,例如char *st ...

  9. 浅谈connect,withRouter,history,useState,useEffect

    1.connect in umi connect 可以链接不同的组件,从而在这个组件中使用其他组件的参数,常用于获取redux中存取的值. 2.withRouter in umi withRouter ...

  10. APACHE如何里一个站点绑定多个域名?用ServerAlias 转

    APACHE2如何里一个站点绑定多个域名?用ServerAlias以前很笨,要使多个域名指向同一站点总是这样写:<VirtualHost *:80>ServerAdmin i@kuigg. ...