Android N wifi auto connect流程分析
定义
当有两个或者两个以上的已经保存的无线网络可以连接时,系统通过选择算法来选择一个最优网络。
- 在Android L,wifi的自动重连机制是由WifiAutoJoinController 类来实现,核心的方法就是attemptAutoJoin(),
- 然而,android L这个机制和用户connect的flow会产生冲突,出现了很多的bug,很鸡肋。
- 因此,android N对这个auto connect的部分做了大改
实现
auto connect在许多场景都会用到,如开机自动连接、亮屏扫描连接等等,这里我们看亮屏扫描时如何自动重连以及选择最优网络的
startPeriodicScan,这个是当屏幕是亮屏的时候,后台一直做scan的操作。
/frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiConnectivityManager.java
// Start a periodic scan when screen is on
private void startPeriodicScan(boolean scanImmediately) {
mPnoScanListener.resetLowRssiNetworkRetryDelay();
// No connectivity scan if auto roaming is disabled.
if (mWifiState == WIFI_STATE_CONNECTED
&& !mConfigManager.getEnableAutoJoinWhenAssociated()) {
return;
}
// Due to b/28020168, timer based single scan will be scheduled
// to provide periodic scan in an exponential backoff fashion.
if (!ENABLE_BACKGROUND_SCAN) {
if (scanImmediately) {
resetLastPeriodicSingleScanTimeStamp();
}
mPeriodicSingleScanInterval = PERIODIC_SCAN_INTERVAL_MS;
startPeriodicSingleScan();
} else {
ScanSettings settings = new ScanSettings();
settings.band = getScanBand();
settings.reportEvents = WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT
| WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN;
settings.numBssidsPerScan = 0;
settings.periodInMs = PERIODIC_SCAN_INTERVAL_MS;
mPeriodicScanListener.clearScanDetails();
mScanner.startBackgroundScan(settings, mPeriodicScanListener, WIFI_WORK_SOURCE);
}
}
看下这里传入的监听器:mPeriodicScanListener
/*
WifiConnectivityManager.java (frameworks\opt\net\wifi\service\java\com\android\server\wifi)
*/
// Periodic scan results listener. A periodic scan is initiated when
// screen is on.
private class PeriodicScanListener implements WifiScanner.ScanListener {
private List<ScanDetail> mScanDetails = new ArrayList<ScanDetail>();
public void clearScanDetails() {
mScanDetails.clear();
}
@Override
public void onSuccess() {
localLog("PeriodicScanListener onSuccess");
// reset the count
mScanRestartCount = 0;
}
@Override
public void onFailure(int reason, String description) {
Log.e(TAG, "PeriodicScanListener onFailure:"
+ " reason: " + reason
+ " description: " + description);
// reschedule the scan
if (mScanRestartCount++ < MAX_SCAN_RESTART_ALLOWED) {
scheduleDelayedConnectivityScan(RESTART_SCAN_DELAY_MS);
} else {
mScanRestartCount = 0;
Log.e(TAG, "Failed to successfully start periodic scan for "
+ MAX_SCAN_RESTART_ALLOWED + " times");
}
}
@Override
public void onPeriodChanged(int periodInMs) {
localLog("PeriodicScanListener onPeriodChanged: "
+ "actual scan period " + periodInMs + "ms");
}
@Override
public void onResults(WifiScanner.ScanData[] results) {
//当scan到了结果之后,跑handleScanResults去处理
handleScanResults(mScanDetails, "PeriodicScanListener");
clearScanDetails();
}
@Override
public void onFullResult(ScanResult fullScanResult) {
if (mDbg) {
localLog("PeriodicScanListener onFullResult: "
+ fullScanResult.SSID + " capabilities "
+ fullScanResult.capabilities);
}
mScanDetails.add(ScanDetailUtil.toScanDetail(fullScanResult));
}
}
handleScanResults会去调用QualifiedNetworkSelector.selectQualifiedNetwork去筛选目标ssid,比如:
- RSSI
- 5G or 2.4G
- lastUserSelectedNetworkId
- 等等
我写了一篇关于selectQualifiedNetwork的详细分析,大家需要详细了解评分质量算法的,可以看Android N selectQualifiedNetwork分析
/**
* Handles 'onResult' callbacks for the Periodic, Single & Pno ScanListener.
* Executes selection of potential network candidates, initiation of connection attempt to that
* network.
*
* @return true - if a candidate is selected by QNS
* false - if no candidate is selected by QNS
*/
private boolean handleScanResults(List<ScanDetail> scanDetails, String listenerName) {
localLog(listenerName + " onResults: start QNS");
//调用QualifiedNetworkSelector去筛选目标ssid,这个算法有点复杂。我看下参数就好
①WifiConfiguration candidate =
mQualifiedNetworkSelector.selectQualifiedNetwork(false,
mUntrustedConnectionAllowed, scanDetails,
mStateMachine.isLinkDebouncing(), mStateMachine.isConnected(),
mStateMachine.isDisconnected(),
mStateMachine.isSupplicantTransientState());
mWifiLastResortWatchdog.updateAvailableNetworks(
mQualifiedNetworkSelector.getFilteredScanDetails());
if (candidate != null) {
localLog(listenerName + ": QNS candidate-" + candidate.SSID);
②connectToNetwork(candidate);
return true;
} else {
return false;
}
}
selectQualifiedNetwork:
当它获得新的扫描结果时,应该在连通性管理器中调用它
检查是否需要进行网络选择。如果需要,检查所有新的扫描结果和
选择一个新的符合条件的网络/BSSID来连接
/**
* ToDo: This should be called in Connectivity Manager when it gets new scan result
* check whether a network slection is needed. If need, check all the new scan results and
* select a new qualified network/BSSID to connect to
*
* @param forceSelectNetwork true -- start a qualified network selection anyway,no matter
* current network is already qualified or not.
* false -- if current network is already qualified, do not do new
* selection
* @param isUntrustedConnectionsAllowed true -- user allow to connect to untrusted network
* false -- user do not allow to connect to untrusted
* network
* @param scanDetails latest scan result obtained (should be connectivity scan only)
* @param isLinkDebouncing true -- Link layer is under debouncing
* false -- Link layer is not under debouncing
* @param isConnected true -- device is connected to an AP currently
* false -- device is not connected to an AP currently
* @param isDisconnected true -- WifiStateMachine is at disconnected state
* false -- WifiStateMachine is not at disconnected state
* @param isSupplicantTransient true -- supplicant is in a transient state
* false -- supplicant is not in a transient state
* @return the qualified network candidate found. If no available candidate, return null
*/
public WifiConfiguration selectQualifiedNetwork(boolean forceSelectNetwork ,
boolean isUntrustedConnectionsAllowed, List<ScanDetail> scanDetails,
boolean isLinkDebouncing, boolean isConnected, boolean isDisconnected,
boolean isSupplicantTransient) {
connectToNetwork(candidate);对我们通过算法算出来的目标ssid发起连接,candidate是一个WifiConfiguration对象。
/**
* Attempt to connect to a network candidate.
*
* Based on the currently connected network, this menthod determines whether we should
* connect or roam to the network candidate recommended by QNS.
*/
private void connectToNetwork(WifiConfiguration candidate) {
ScanResult scanResultCandidate = candidate.getNetworkSelectionStatus().getCandidate();
if (scanResultCandidate == null) {
Log.e(TAG, "connectToNetwork: bad candidate - " + candidate
+ " scanResult: " + scanResultCandidate);
return;
}
String targetBssid = scanResultCandidate.BSSID;
String targetAssociationId = candidate.SSID + " : " + targetBssid;
//如果我们筛选出来的ssid正好是上一次attempt连接的ssid,或者是supplicant现在正在连接的目标ssid,则放弃
// Check if we are already connected or in the process of connecting to the target
// BSSID. mWifiInfo.mBSSID tracks the currently connected BSSID. This is checked just
// in case the firmware automatically roamed to a BSSID different from what QNS
// selected.
if (targetBssid != null
&& (targetBssid.equals(mLastConnectionAttemptBssid)
|| targetBssid.equals(mWifiInfo.getBSSID()))
&& SupplicantState.isConnecting(mWifiInfo.getSupplicantState())) {
localLog("connectToNetwork: Either already connected "
+ "or is connecting to " + targetAssociationId);
return;
}
Long elapsedTimeMillis = mClock.elapsedRealtime();
/**
* This checks the connection attempt rate and recommends whether the connection attempt
* should be skipped or not. This attempts to rate limit the rate of connections to
* prevent us from flapping between networks and draining battery rapidly.
*/
//控制attempt连接速率,如果间隔时间太快,那就放弃
if (!mScreenOn && shouldSkipConnectionAttempt(elapsedTimeMillis)) {
localLog("connectToNetwork: Too many connection attempts. Skipping this attempt!");
mTotalConnectivityAttemptsRateLimited++;
return;
}
//终于要开始连接了,把时间记录下来noteConnectionAttempt
noteConnectionAttempt(elapsedTimeMillis);
mLastConnectionAttemptBssid = targetBssid;
WifiConfiguration currentConnectedNetwork = mConfigManager
.getWifiConfiguration(mWifiInfo.getNetworkId());
String currentAssociationId = (currentConnectedNetwork == null) ? "Disconnected" :
(mWifiInfo.getSSID() + " : " + mWifiInfo.getBSSID());
//做漫游操作还是autoconnect的操作
if (currentConnectedNetwork != null
&& (currentConnectedNetwork.networkId == candidate.networkId
|| currentConnectedNetwork.isLinked(candidate))) {
localLog("connectToNetwork: Roaming from " + currentAssociationId + " to "
+ targetAssociationId);
mStateMachine.autoRoamToNetwork(candidate.networkId, scanResultCandidate);
} else {
localLog("connectToNetwork: Reconnect from " + currentAssociationId + " to "
+ targetAssociationId);
mStateMachine.autoConnectToNetwork(candidate.networkId, scanResultCandidate.BSSID);
}
}
接下来就交给Wifistatemachine去处理了。
/**
* Automatically connect to the network specified
*
* @param networkId ID of the network to connect to
* @param bssid BSSID of the network
*/
public void autoConnectToNetwork(int networkId, String bssid) {
sendMessage(CMD_AUTO_CONNECT, networkId, 0, bssid);
}
/**
* Automatically roam to the network specified
*
* @param networkId ID of the network to roam to
* @param scanResult scan result which identifies the network to roam to
*/
public void autoRoamToNetwork(int networkId, ScanResult scanResult) {
sendMessage(CMD_AUTO_ROAM, networkId, 0, scanResult);
}
Android N wifi auto connect流程分析的更多相关文章
- Android bluetooth介绍(四): a2dp connect流程分析
关键词:蓝牙blueZ A2DP.SINK.sink_connect.sink_disconnect.sink_suspend.sink_resume.sink_is_connected.sink_ ...
- Android 4.4 音量调节流程分析(二)
之前在Android 4.4 音量调节流程分析(一)里已经有简单的分析音量控制的流程,今天想接着继续分析下音量大小计算的方法.对于任一播放文件而言其本身都有着固定大小的音量Volume_Max,而在A ...
- Android 7.1 屏幕旋转流程分析
Android 7.1 屏幕旋转流程分析 一.概述 Android屏幕的旋转在framework主要涉及到三个类,结构如图 PhoneWindowManager:为屏幕的横竖屏转换的管理类. Wi ...
- Android之 MTP框架和流程分析
概要 本文的目的是介绍Android系统中MTP的一些相关知识.主要的内容包括:第1部分 MTP简介 对Mtp协议进行简单的介绍.第2部分 MTP框架 介绍 ...
- Android系统之LK启动流程分析(一)
1.前言 LK是Little Kernel的缩写,在Qualcomm平台的Android系统中普遍采用LK作为bootloader,它是一个开源项目,LK是整个系统的引导部分,所以不是独立存在的,但是 ...
- Android视图状态及重绘流程分析,带你一步步深入了解View(三)
转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/17045157 在前面一篇文章中,我带着大家一起从源码的层面上分析了视图的绘制流程, ...
- Android 4.4 音量调节流程分析(一)
最近在做Android Audio方面的工作,有需求是在调节Volume_Up_Key & Volume_Down_key时,Spearker or Headset每音阶的衰减变为3db左右. ...
- Android wpa_supplicant 四次握手 流程分析
记录wpa_supplicant四次握手的过程. 相关log:https://www.cnblogs.com/helloworldtoyou/p/9633603.html 接收到第一次握手,会设置一个 ...
- Cocos2d-x3.3RC0的Android编译Activity启动流程分析
本文将从引擎源代码Jni分析Cocos2d-x3.3RC0的Android Activity的启动流程,以下是具体分析. 1.引擎源代码Jni.部分Java层和C++层代码分析 watermark/2 ...
随机推荐
- 【Go语言绘图】图片添加文字(二)
这一篇将继续介绍gg库中绘制文字相关的方法,主要包括:DrawStringAnchored().DrawStringWrapped().MeasureMultilineString().WordWra ...
- C# 生成6位短信验证码
1 private string VerifyCode() 2 { 3 Random random = new Random(); 4 return random.Next(100000, 99999 ...
- C#随机生成不重复邀请码&创建登录Token&转换人民币大小金额
/// <summary> /// 创建登陆Token /// </summary> /// <param name="length">< ...
- sql server 汉字转拼音首字母
create function fun_getPY ( @str nvarchar(4000) ) returns nvarchar(4000) as begin declare @word ncha ...
- 迁移sqlserver数据到MongoDb
前言 随着数据量的日积月累,数据库总有一天会不堪重负的,除了通过添加索引.分库分表,其实还可以考虑一下换个数据库.我强烈推荐使用MongoDb,我举例说一下我的经历:我的项目中有一张表的数据大概是30 ...
- Ubuntu系统的ifconfig命令不能执行
新安装的Ubuntu想要用WinSCP传文件时发现,ifconfig命令用不了 ping www.baidu.com 获得回应,应该是ifconfig未安装 解决这个问题,首先如图(时间较长,获取:[ ...
- HBase内存配置及JVM优化
前言 本文从HBase的内存布局说起,先充分了解HBase的内存区的使用与分配,随后给出了不同业务场景下的读写内存分配规划,并指导如何分析业务的内存使用情况,以及在使用当中写内存Memstore及读内 ...
- 【vue-1】vue-cli3.0以上的搭建与配置(2.X的版本是不一样的)
为什么要使用 vue-cli Vue CLI 致力于将 Vue 生态中的工具基础标准化.它确保了各种构建工具能够基于智能的默认配置即可平稳衔接,这样你可以专注在撰写应用上,而不必花好几天去纠结配置的问 ...
- [leetcode]39combinationsum回溯法找几个数的和为目标值
import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * Given a set of can ...
- SLA
服务级别协议[编辑] 维基百科,自由的百科全书 跳到导航跳到搜索 本条目可参照外语维基百科相应条目来扩充. 若您熟悉来源语言和主题,请协助参考外语维基扩充条目.请勿直接提交机械翻译,也不要翻译 ...