文章目录
本文建立在研读 WifiConnectivityManager.java 和 其打印的log 的基础上
WifiConnectivityManager 内置三种 ScanListener,分别是
- AllSingleScanListener implements WifiScanner.ScanListener
- SingleScanListener implements WifiScanner.ScanListener
- PnoScanListener implements WifiScanner.PnoScanListener
WifiScanner.ScanListener && WifiScanner.PnoScanListener
/**
* interface to get scan events on; specify this on {@link #startBackgroundScan} or
* {@link #startScan}
*/
// 说明 ScanListener 需要在 mScanner.startScan 或者 mScanner.startBackgroundScan 中被实例化
public interface ScanListener extends ActionListener {
@Deprecated
public void onPeriodChanged(int periodInMs);
public void onResults(ScanData[] results);
public void onFullResult(ScanResult fullScanResult);
}
/**
* interface to get PNO scan events on; specify this on {@link #startDisconnectedPnoScan} and
* {@link #startConnectedPnoScan}.
* {@hide}
*/
// 说明 PnoScanListener 需要在 mScanner.startDisconnectedPnoScan 或者 mScanner.startConnectedPnoScan 中被实例化
public interface PnoScanListener extends ScanListener {
void onPnoNetworkFound(ScanResult[] results);
}
AllSingleScanListener
只需要看它对 onResults(WifiScanner.ScanData[] results) 的实现即可
-
通过 WifiScanner.isFullBandScan(results[0].getBandScanned(), true) 判断 isFullBandScanResults
阅读相关代码可知,只要扫描了2.4G和5G频段,就认为返回 true -
判断 mWaitForFullBandScanResults ,如真,说明需要等待全频段扫描结果
判断上面得到的 isFullBandScanResults ,如假,说明本次扫描结果不是全频段扫描得到的,直接 return 掉
注意, mWaitForFullBandScanResults 只会在 forceConnectivityScan 中被置 true -
更新 WifiMetrics 相关信息
-
调用 handleScanResults 实际处理扫描结果,返回值赋值到 wasConnectAttempted 中,标识是否有网络被 WNS 选中
-
如果 Pno 扫描已开始,则更新 WifiMetrics 相关信息
-
判断 mInitialScanState , 分情况做处理
mInitialScanState 可能的取值有
// Initial scan state, used to manage performing partial scans in initial scans
// Initial scans are the first scan after enabling Wifi or turning on screen when disconnected
private static final int INITIAL_SCAN_STATE_START = 0;
private static final int INITIAL_SCAN_STATE_AWAITING_RESPONSE = 1;
private static final int INITIAL_SCAN_STATE_COMPLETE = 2;
initial scan 为打开 wifi 或者无连接状态下亮屏进行的扫描
这三个值用来跟踪 initial scan 进行的状态
在第6步的判断中
如果 initial scan 尚处于 等待回复 状态,则将状态改成 已完成
如果此时有网络被 WNS 选中,那么执行 schedulePeriodicScanTimer ,否则执行 startConnectivityScan
这里的处理是合理的,如果这次扫描触发了连接请求,那么就开启周期扫描
schedulePeriodicScanTimer(getScheduledSingleScanIntervalMs(mCurrentSingleScanScheduleIndex))
// Set up periodic scan timer
private void schedulePeriodicScanTimer(int intervalMs) {
localLog("schedulePeriodicScanTimer, intervalMs: " + intervalMs);
mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
mClock.getElapsedSinceBootMillis() + intervalMs,
PERIODIC_SCAN_TIMER_TAG,
mPeriodicScanTimerListener, mEventHandler);
mPeriodicScanTimerSet = true;
}
private final AlarmManager.OnAlarmListener mPeriodicScanTimerListener =
new AlarmManager.OnAlarmListener() {
public void onAlarm() {
periodicScanTimerHandler();
}
};
// Periodic scan timer handler
private void periodicScanTimerHandler() {
localLog("periodicScanTimerHandler");
// Schedule the next timer and start a single scan if screen is on.
if (mScreenOn) {
startPeriodicSingleScan();
}
}
由此可见,周期扫描只在亮屏时进行
我们再看 getScheduledSingleScanIntervalMs 方法
这部分内容被我师傅魔改了,我照着解读吧
// 距离上次灭屏或者断连 3 min内,返回 5s
// 距离上次灭屏或者断连 3 ~ 10 min内,返回 10s
long currentTimeStamp = mClock.getElapsedSinceBootMillis();
boolean qucikScanProcess = (currentTimeStamp - mScreenOnTimeStamp) < QUICK_SCAN_TIME
|| (currentTimeStamp - mDisconnectTimeStamp) < QUICK_SCAN_TIME;
boolean qucikScanProcessSecond = (currentTimeStamp - mScreenOnTimeStamp) < QUICK_SCAN_TIME_LONG
|| (currentTimeStamp - mDisconnectTimeStamp) < QUICK_SCAN_TIME_LONG;
if(qucikScanProcess){
mCurrentSingleScanScheduleIndex = 0;
return QUICK_PERIODIC_SCAN_INTERVAL_MS;
}else if(qucikScanProcessSecond){
mCurrentSingleScanScheduleIndex = 0;
return QUICK_PERIODIC_SCAN_INTERVAL_MS_LONG;
}
// mCurrentSingleScanScheduleSec 是一个 存周期扫描时间的 int 数组
// mCurrentSingleScanScheduleSec 为空的话 取用默认配置,也就是 20s
// private static final int[] DEFAULT_SCANNING_SCHEDULE_SEC = {20, 40, 80, 160};
if (mCurrentSingleScanScheduleSec == null) {
Log.e(TAG, "Invalid attempt to get schedule interval, Schedule array is null ");
// Use a default value
return DEFAULT_SCANNING_SCHEDULE_SEC[0] * 1000;
}
// 根据传参 index 取值
if (index >= mCurrentSingleScanScheduleSec.length) {
index = mCurrentSingleScanScheduleSec.length - 1;
}
return mCurrentSingleScanScheduleSec[index] * 1000;
这里顺便再看看 setSingleScanningSchedule 的调用
setSingleScanningSchedule(mDisconnectedSingleScanScheduleSec);
setSingleScanningSchedule(mConnectedSingleSavedNetworkSingleScanScheduleSec);
setSingleScanningSchedule(mConnectedSingleScanScheduleSec);
setSingleScanningSchedule(null);
// 由此可见,源码根据连接状态准备了多种 SingleScanScheduleSec
// 但是我师傅的改法导致这些至少在 10mins 后才会被用到
// 无论如何,简单看下这些 SingleScanScheduleSec
// mDisconnectedSingleScanScheduleSec -> [20, 40, 80, 160]
// mConnectedSingleSavedNetworkSingleScanScheduleSec -> [20, 40, 80, 160]
// mConnectedSingleScanScheduleSec -> [20, 40, 80, 160]
// 额,无语了,分析出来是这种配置
接下来看 startPeriodicSingleScan 方法的实现
-
距离上次扫描未过去规定时间,则跳过本次扫描,触发差距时间的 timer
-
检查以下情况以跳过这次扫描或者只做部分扫描
- 网络够用
- 连接极好,网络使用情况可接受且距离上次WNS选网只过去很小的一段时间
- 有活跃的网络流,那么就不应该进行扫描(扫描会导致数据收发业务的中断)
-
如果 firmware 支持漫游,那么就不进行扫描,否则进行部分频段的扫描
-
如果需要扫描
- 当前无连接且 initial scan 已开始时,则执行 startSingleScan 部分频段扫描
- 普通执行 startSingleScan,是否需要进行全频段扫描根据 firmware 是否支持漫游来决定
- 执行 schedulePeriodicScanTimer 方法
- mCurrentSingleScanScheduleIndex++
-
如果不需要扫描,执行 schedulePeriodicScanTimer 方法
我们再看 startSingleScan(实际上是 startForcedSingleScan) 方法的实现
-
填充 ScanSettings
-
通过 mScanner.startScan 触发 singleScanListener
SingleScanListener
根据注释,single scan 只在 DisconnectedPNO scan 发现有效网络 或者 被 watchdog timer 触发
此处存疑
SingleScanListener 的 onFailure 实现
-
重新计划扫描
mSingleScanRestartCount 记录 single scan 重试次数,如果小于预设的 MAX_SCAN_RESTART_ALLOWED
则执行 scheduleDelayedSingleScan 方法 -
如果已经超过了最大重试次数,则将 mSingleScanRestartCount 归零
scheduleDelayedSingleScan 方法实际上起了一个 RestartSingleScanListener 进行 startSingleScan 操作
PnoScanListener
PNO 扫描在灭屏后进行
PnoScanListener 的 onFailure 实现
-
重新计划扫描
mScanRestartCount 记录 PNO scan 重试次数,如果小于预设的 MAX_SCAN_RESTART_ALLOWED
则执行 scheduleDelayedConnectivityScan(RESTART_SCAN_DELAY_MS) 方法 -
如果已经超过了最大重试次数,则将 mScanRestartCount 归零
重点看 onPnoNetworkFound(ScanResult[] results) 的实现,该方法在 PNO 扫描发现有效网路时被调用
这里先摆出一个很重要的值,该值表示 PNO 扫描到的网络因信号太差被WNS拒绝后,PNO scan指数退避的时间
private int mLowRssiNetworkRetryDelay = LOW_RSSI_NETWORK_RETRY_START_DELAY_MS;
-
遍历扫描结果,如果 informationElements 为空,跳过,否则添加到 mScanDetails 中
-
调用 handleScanResults 得到 WNS 选择结果, 赋值到 wasConnectAttempted 中
-
WNS 未选中,如果 mLowRssiNetworkRetryDelay 大于 LOW_RSSI_NETWORK_RETRY_START_DELAY_MS ,则重置;执行 scheduleDelayedConnectivityScan 方法,将 mLowRssiNetworkRetryDelay * 2
-
WNS 选中,则重置 mLowRssiNetworkRetryDelay
scheduleDelayedConnectivityScan 方法实际上起了一个 RestartSingleScanListener 进行 startSingleScan 操作
这里看下 startConnectivityScan 方法
-
停止正在进行的 Connectivity Scan
-
如果是亮屏,触发周期扫描
-
如果是灭屏无连接状态,触发PNO扫描 startDisconnectedPnoScan()