定义

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

  • 在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. Windows锁定屏幕然后关闭显示器,可执行程序

    有时候我们需要关闭屏幕来休息一下或者在本上写东西,但是屏幕亮着的时候会分心,但是关闭显示器又太麻烦了,所以直接来一个小程序(非微信小程序).还有一种情况,有时候晚上要离开电脑旁了,但是电脑还在做事情, ...

  2. Android OpenGL ES 开发:绘制图形

    OpenGL 绘制图形步骤 上一篇介绍了 OpenGL 的相关概念,今天来实际操作,使用 OpenGL 绘制出图形,对其过程有一个初步的了解. OpenGL 绘制图形主要概括成以下几个步骤: 创建程序 ...

  3. javaweb基本知识(eclipse环境)

    javascrip有两种方式: ①外链式 1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 < ...

  4. swap是干嘛的?

    本文截取自:http://hbasefly.com/2017/05/24/hbase-linux/ swap是干嘛的? 在Linux下,SWAP的作用类似Windows系统下的"虚拟内存&q ...

  5. 使用 SOS 对 Linux 中运行的 .NET Core 进行问题诊断

    目录 说明 准备一个方便的学习环境 2.x 配置内容 3.x 配置内容 工具介绍 lldb sos plugin 1. attach 到进程上进行调试 2. 分析core dump文件 SOS 案例分 ...

  6. Logstash学习之路(三)Logstash处理时区、类型转换、删除字段的案例配置

    #输入 input { file { path => ["文件路径"] #自定义类型 type => "自定义" start_position =& ...

  7. Study_way

    一.Study 学习通Java基础视频.语法 开源中国 (Git)版本控制 读懂程序.源代码 相关资源 百度网盘 程序:方法(数学) 二.参数传递 基本数据的传参:虚参改变影响实参 引用数据的传参:数 ...

  8. Java安全之Weblogic 2016-3510 分析

    Java安全之Weblogic 2016-3510 分析 首发安全客:Java安全之Weblogic 2016-3510 分析 0x00 前言 续前面两篇文章的T3漏洞分析文章,继续来分析CVE-20 ...

  9. Linux SSH , SCP 建立信任关系(免密传输)

    最近有个需求,Jenkins需要将war传输到各个项目节点中,所以需要远程执行各个节点的shell脚本.但是中间有个输入密码的过程,在自动化部署中是行不通的,故需要增加免密登录.具体如下: 如果想在  ...

  10. 【Spring】Spring JdbcTemplate

    Spring JdbcTemplate 文章源码 JdbcTemplate 概述 它是 Spring 框架中提供的一个对象,是对原始 Jdbc API 对象的简单封装.Spring 框架提供了很多的操 ...