定义

当有两个或者两个以上的已经保存的无线网络可以连接时,系统通过选择算法来选择一个最优网络。

  • 在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流程分析的更多相关文章

  1. Android bluetooth介绍(四): a2dp connect流程分析

    关键词:蓝牙blueZ  A2DP.SINK.sink_connect.sink_disconnect.sink_suspend.sink_resume.sink_is_connected.sink_ ...

  2. Android 4.4 音量调节流程分析(二)

    之前在Android 4.4 音量调节流程分析(一)里已经有简单的分析音量控制的流程,今天想接着继续分析下音量大小计算的方法.对于任一播放文件而言其本身都有着固定大小的音量Volume_Max,而在A ...

  3. Android 7.1 屏幕旋转流程分析

    Android 7.1   屏幕旋转流程分析 一.概述 Android屏幕的旋转在framework主要涉及到三个类,结构如图 PhoneWindowManager:为屏幕的横竖屏转换的管理类. Wi ...

  4. Android之 MTP框架和流程分析

    概要 本文的目的是介绍Android系统中MTP的一些相关知识.主要的内容包括:第1部分 MTP简介            对Mtp协议进行简单的介绍.第2部分 MTP框架            介绍 ...

  5. Android系统之LK启动流程分析(一)

    1.前言 LK是Little Kernel的缩写,在Qualcomm平台的Android系统中普遍采用LK作为bootloader,它是一个开源项目,LK是整个系统的引导部分,所以不是独立存在的,但是 ...

  6. Android视图状态及重绘流程分析,带你一步步深入了解View(三)

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/17045157 在前面一篇文章中,我带着大家一起从源码的层面上分析了视图的绘制流程, ...

  7. Android 4.4 音量调节流程分析(一)

    最近在做Android Audio方面的工作,有需求是在调节Volume_Up_Key & Volume_Down_key时,Spearker or Headset每音阶的衰减变为3db左右. ...

  8. Android wpa_supplicant 四次握手 流程分析

    记录wpa_supplicant四次握手的过程. 相关log:https://www.cnblogs.com/helloworldtoyou/p/9633603.html 接收到第一次握手,会设置一个 ...

  9. Cocos2d-x3.3RC0的Android编译Activity启动流程分析

    本文将从引擎源代码Jni分析Cocos2d-x3.3RC0的Android Activity的启动流程,以下是具体分析. 1.引擎源代码Jni.部分Java层和C++层代码分析 watermark/2 ...

随机推荐

  1. 【Go语言绘图】图片添加文字(二)

    这一篇将继续介绍gg库中绘制文字相关的方法,主要包括:DrawStringAnchored().DrawStringWrapped().MeasureMultilineString().WordWra ...

  2. C# 生成6位短信验证码

    1 private string VerifyCode() 2 { 3 Random random = new Random(); 4 return random.Next(100000, 99999 ...

  3. C#随机生成不重复邀请码&创建登录Token&转换人民币大小金额

    /// <summary> /// 创建登陆Token /// </summary> /// <param name="length">< ...

  4. sql server 汉字转拼音首字母

    create function fun_getPY ( @str nvarchar(4000) ) returns nvarchar(4000) as begin declare @word ncha ...

  5. 迁移sqlserver数据到MongoDb

    前言 随着数据量的日积月累,数据库总有一天会不堪重负的,除了通过添加索引.分库分表,其实还可以考虑一下换个数据库.我强烈推荐使用MongoDb,我举例说一下我的经历:我的项目中有一张表的数据大概是30 ...

  6. Ubuntu系统的ifconfig命令不能执行

    新安装的Ubuntu想要用WinSCP传文件时发现,ifconfig命令用不了 ping www.baidu.com 获得回应,应该是ifconfig未安装 解决这个问题,首先如图(时间较长,获取:[ ...

  7. HBase内存配置及JVM优化

    前言 本文从HBase的内存布局说起,先充分了解HBase的内存区的使用与分配,随后给出了不同业务场景下的读写内存分配规划,并指导如何分析业务的内存使用情况,以及在使用当中写内存Memstore及读内 ...

  8. 【vue-1】vue-cli3.0以上的搭建与配置(2.X的版本是不一样的)

    为什么要使用 vue-cli Vue CLI 致力于将 Vue 生态中的工具基础标准化.它确保了各种构建工具能够基于智能的默认配置即可平稳衔接,这样你可以专注在撰写应用上,而不必花好几天去纠结配置的问 ...

  9. [leetcode]39combinationsum回溯法找几个数的和为目标值

    import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * Given a set of can ...

  10. SLA

    服务级别协议[编辑] 维基百科,自由的百科全书     跳到导航跳到搜索 本条目可参照外语维基百科相应条目来扩充. 若您熟悉来源语言和主题,请协助参考外语维基扩充条目.请勿直接提交机械翻译,也不要翻译 ...