欢迎大家一起学习探讨通信之WLAN。本节我们讨论关于Android设备的WiFi Background 扫描机制。通过使用WiFi设备的经验可知。WiFi设备连接上WiFi网络,必要条件是需要扫描到目标网络。我们在使用Android设备时,设备不处于WiFi设置界面时,移动到已保存的WiFi网络覆盖范围内,设备会自动连接上网络。细心的使用者,可能已发现,有时设备很短时间内就连接上已保存网络,有时设备需要过几分钟才能连接上已保存网络。当不处于WiFi设置界面时,WiFi自动连接功能主要依赖BG Scan机制。这里我们主要探讨下Android 9.0 WiFi BG Scan机制背后代码实现的故事。
好。我们先看下打开WiFi相关状态初始化流程。Android 框架中维护了一个WiFi状态机(WiFiStateMachine),当用户点击打开WiFi按钮后,状态机最终要停留到DisconnectedState状态。在状态转变期间,状态机需经过ConnectModeState节点,在该状态的enter()中,调用setupClientMode(),启动一些client mode相关服务和获取其相关状态。在setupClientMode()方法中初始化了WifiConnectivityManager对象,为WiFi BG Scan奠定了基础。
framework/opt/net/wifi/service/java/com/android/server/wifi/WifiStateMachine.java
class ConnectModeState extends State {
public void enter() {
setupClientMode(); //设置client Mode,即WiFi Station
}
}
private void setupClientMode() {
if (mWifiConnectivityManager == null) {
synchronized (mWifiReqCountLock) {
//创建注册WifiConnectivityManager对象
mWifiConnectivityManager =mWifiInjector.makeWifiConnectivityManager(mWifiInfo,hasConnectionRequests());
mWifiConnectivityManager.setUntrustedConnectionAllowed(mUntrustedReqCount > 0);
mWifiConnectivityManager.handleScreenStateChanged(mScreenOn);
}
}
}
framework/opt/net/wifi/service/java/com/android/server/wifi/WifiInjector.java
public WifiConnectivityManager makeWifiConnectivityManager(WifiInfo wifiInfo,
boolean hasConnectionRequests) {
return new WifiConnectivityManager(mContext, getScoringParams(),
mWifiStateMachine, getWifiScanner(),
mWifiConfigManager, wifiInfo, mWifiNetworkSelector, mWifiConnectivityHelper,
mWifiLastResortWatchdog, mOpenNetworkNotifier, mCarrierNetworkNotifier,
mCarrierNetworkConfig, mWifiMetrics, mWifiStateMachineHandlerThread.getLooper(),
mClock, mConnectivityLocalLog, hasConnectionRequests, mFrameworkFacade,
mSavedNetworkEvaluator, mScoredNetworkEvaluator, mPasspointNetworkEvaluator);
}
经过状态机多次转移,终于来到了DisconnectedState状态。Android状态机有个特性:状态机要进入某个状态,必须先进入其enter();要退出某个状态,必须执行状态的exit()。在进入DisconnectedState状态时,触发了WiFi BG Scan 的流程。进入到WifiConnectivityManager这个类中,将BG Scan周期扫描逻辑的真正实现。
framework/opt/net/wifi/service/java/com/android/server/wifi/WifiStateMachine.java
class DisconnectedState extends State {
@Override
//DisconnectedState 状态中调用mWifiConnectivityManager通知WiFi状态变化。
public void enter() {
mWifiConnectivityManager.handleConnectionStateChanged(
WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
}
}
framework/opt/net/wifi/service/java/com/android/server/wifi/WifiConnectivityManager.java
public void handleConnectionStateChanged(int state) {
if (mWifiState == WIFI_STATE_DISCONNECTED) {
mLastConnectionAttemptBssid = null;
scheduleWatchdogTimer();
startConnectivityScan(SCAN_IMMEDIATELY);//开始WiFi扫描
}
}
private void startConnectivityScan(boolean scanImmediately) {
if (mScreenOn) {
startPeriodicScan(scanImmediately);//亮屏下,执行正常WiFi扫描
} else {
if (mWifiState == WIFI_STATE_DISCONNECTED && !mPnoScanStarted) {
startDisconnectedPnoScan();//熄屏下,执行正常PNO wifi扫描
}
}
}
private void startPeriodicScan(boolean scanImmediately) {
mPeriodicSingleScanInterval = PERIODIC_SCAN_INTERVAL_MS;
startPeriodicSingleScan(); //开始周期扫描,并赋值mPeriodicSingleScanInterval=20S。
}
到这里,WiFi BG Scan已经进入其机制具体实现中,详细学习下这块代码逻辑的实现,调用逻辑相对还是比较清晰简单。
framework/opt/net/wifi/service/java/com/android/server/wifi/WifiConnectivityManager.java
正常扫描,在亮屏状态下进行,从代码中可看到对mScreen的判断。
public static final int PERIODIC_SCAN_INTERVAL_MS = 20 * 1000; // 最小扫描间隔20s
public static final int MAX_PERIODIC_SCAN_INTERVAL_MS = 160 * 1000; //最大扫描间隔160s
这里解释下,PNO扫描只扫描已保存的网络,在熄屏状态下执行的。
private static final int DISCONNECTED_PNO_SCAN_INTERVAL_MS = 20 * 1000; // 最小PNO扫描间隔20s
private static final int CONNECTED_PNO_SCAN_INTERVAL_MS = 160 * 1000; // 最大PNO扫描间隔160s
private int mPeriodicSingleScanInterval = PERIODIC_SCAN_INTERVAL_MS;
创建Alarm,时间到期执行periodicScanTimerHandler()方法。
private final AlarmManager.OnAlarmListener mPeriodicScanTimerListener =
new AlarmManager.OnAlarmListener() {
public void onAlarm() {
periodicScanTimerHandler();
}
};
// 设置周期扫描Timer。并将周期扫描标志变量设置mPeriodicScanTimerSet=true。
private void schedulePeriodicScanTimer(int intervalMs) {
mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
mClock.getElapsedSinceBootMillis() + intervalMs,
PERIODIC_SCAN_TIMER_TAG,
mPeriodicScanTimerListener, mEventHandler);
mPeriodicScanTimerSet = true;
}
private void periodicScanTimerHandler() {
localLog("periodicScanTimerHandler");
//安排下一个定时器,如亮屏,则开始扫描。
if (mScreenOn) {
startPeriodicSingleScan();
}
}
framework/opt/net/wifi/service/java/com/android/server/wifi/WifiConnectivityManager.java
// 启动扫描并设置下一次扫描的间隔。
private void startPeriodicSingleScan() {
long currentTimeStamp = mClock.getElapsedSinceBootMillis();
如果距上次扫描时间小于20s,在重新设置到时扫描Timer.
if (mLastPeriodicSingleScanTimeStamp != RESET_TIME_STAMP) {
long msSinceLastScan = currentTimeStamp - mLastPeriodicSingleScanTimeStamp;
if (msSinceLastScan < PERIODIC_SCAN_INTERVAL_MS) {
localLog("Last periodic single scan started " + msSinceLastScan
+ "ms ago, defer this new scan request.");
schedulePeriodicScanTimer(PERIODIC_SCAN_INTERVAL_MS - (int) msSinceLastScan);
return;
}
}
if (isScanNeeded) {
//判断是否需要执行扫描,
//如果为true。在执行扫描,并重新设置周期扫描Timer。
//如果为false。则跳过本次扫描,重新设置周期扫描Timer同上次时长。
mLastPeriodicSingleScanTimeStamp = currentTimeStamp;
startSingleScan(isFullBandScan, WIFI_WORK_SOURCE);
schedulePeriodicScanTimer(mPeriodicSingleScanInterval);
//以指数回退方式设置下一个扫描间隔。
//mPeriodicSingleScanInterval = mPeriodicSingleScanInterval * 2
//单次执行Timer时间为:20S 40S 80S 160S。后续一直保存为160S
mPeriodicSingleScanInterval *= 2;
if (mPeriodicSingleScanInterval > MAX_PERIODIC_SCAN_INTERVAL_MS) {
mPeriodicSingleScanInterval = MAX_PERIODIC_SCAN_INTERVAL_MS;
}
} else {
schedulePeriodicScanTimer(mPeriodicSingleScanInterval);
}
}
具体执行扫描实现如下。调用startSingleScan方法,将扫描动作传递到Scanner中。
framework/opt/net/wifi/service/java/com/android/server/wifi/WifiConnectivityManager.java
// Start a single scan
private void startSingleScan(boolean isFullBandScan, WorkSource workSource) {
mScanner.startScan(settings, singleScanListener, workSource);
}
./base/wifi/java/android/net/wifi/WifiScanner.java
public void startScan(ScanSettings settings, ScanListener listener, WorkSource workSource) {
//通过AsyncChannel发送消息CMD_START_SINGLE_SCAN,给到扫描服务。
mAsyncChannel.sendMessage(CMD_START_SINGLE_SCAN, 0, key, scanParams);
}
//扫描服务处理WifiScanner发送的消息
framework/opt/net/wifi/service/java/com/android/server/wifi/scanner/WifiScanningServiceImpl.java
class DriverStartedState extends State {
public boolean processMessage(Message msg) {
case WifiScanner.CMD_START_SINGLE_SCAN:
tryToStartNewScan();
}
}
void tryToStartNewScan() {
if (mScannerImpl.startSingleScan(settings, this)) {
}
}
framework/opt/net/wifi/service/java/com/android/server/wifi/scanner/WifiScannerImpl.java
public abstract boolean startSingleScan(WifiNative.ScanSettings settings,
WifiNative.ScanEventHandler eventHandler);
framework/opt/net/wifi/service/java/com/android/server/wifi/scanner/WificondScannerImpl.java
public boolean startSingleScan(WifiNative.ScanSettings settings,
WifiNative.ScanEventHandler eventHandler) {
success = mWifiNative.scan(
mIfaceName, settings.scanType, freqs, hiddenNetworkSSIDSet);
}
WiFiNative下发扫描命令给到WiFiCond,在WiFiCond进程中将消息发送至kernel nl802.11中,最后发送至driver执行WiFi扫描动作。
framework/opt/net/wifi/service/java/com/android/server/wifi/WifiNative.java
public boolean scan(@NonNull String ifaceName, int scanType, Set<Integer> freqs,
Set<String> hiddenNetworkSSIDs) {
return mWificondControl.scan(ifaceName, scanType, freqs, hiddenNetworkSSIDs);
}
到此,我们已经分析完了WiFi BG scan的代码实现流程。但WiFiNative->kernel->驱动执行还有比较复杂的调用关系,后续再进行分析梳理。
注:
对以上所述专业知识有修正意见或建议,可随时留言反馈。如感兴趣更多通信知识,可关注“通信之WLAN”微信公众号。
谢谢大家支持~!