定义

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

  • 在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. Django之DRF-- API限速

    什么场景下需要限制访问频次呢? 1)防爬虫:爬虫可能会在短时间内大量的访问服务接口,增加服务器压力 2)对于需要限制访问频次的接口 具体使用配置如下: 1,settings.py加入配置 REST_F ...

  2. MySQL忘记密码了怎么解决

    前言:在不考虑到原来用户对关联数据库的授权问题的情况下,有以下三种思路解决 #1.登录状态下修改 说明:在登录状态的话,直接使用命令修改密码就行了 mysql> use mysql; mysql ...

  3. easyui中开始时间小于结束时间 不然无法点击

    <tr> <td align="right">用药开始时间:</td> <td><input id="time_fr ...

  4. Java学习日报7.29

    package student;import java.util.*;public class student { Scanner sc=new Scanner(System.in); private ...

  5. stm32之定时器彻底研究

    分类: C/C++ 这里介绍两种方式使用stm32的定时器:直接操作寄存器和使用st的官方的库文件. 相比较而言,直接操作定时器比较简洁,对着寄存器看十分明了.而使用库文件有一点晕头转向. (个人观点 ...

  6. [linux]makefile多目录

    在使用makefile多目录编写前需要掌握几个函数及符号 自定义变量 target=edit 引用的时候直接使用 $(target) 有点像C语言中的#define,这里的 $(target)会被替换 ...

  7. JVM个人总结一

    看了深入理解JAVA虚拟机已经有一段时间了  发现很多东西如果不总结  脑子里总是没有一条线贯穿起来,也比较模糊混乱,所以还是有必要利用逻辑思维图总结下. JVM看了下  大致比较重要的分内存区域划分 ...

  8. hdp、cdh版本大数据maven仓库

    Hdp版本: <repositories> <repository> <id>nexus</id> <name>local private ...

  9. 打算写一些Netty的文章了,先聊聊为什么要学习Netty

    微信搜索[阿丸笔记],关注Java/MySQL/中间件各系列原创实战笔记,干货满满. 2021年了,终于开始系统性总结Netty相关的东西了. 这会是Netty系列的第一篇,我想先聊聊 "为 ...

  10. swack的wiki站上线

    swack的个人wiki网址:www.swack.cn [服务器破旧,速度较慢,见谅!]