WIFI启动过程,WifiStateMachine加载驱动固件,连接上wpa_s的socket并检查好配置文件后,进入到DisconnectedState状态。在DisconnectedState状态下启动了mWifiConnectivityManager.handleConnectionStateChanged(WifiConnectivityManager.WIFI_STATE_DISCONNECTED)。这是启动扫描的第一步。
handleConnectionStateChanged(WifiConnectivityManager.WIFI_STATE_DISCONNECTED)
startConnectivityScan(SCAN_ON_SCHEDULE) // SCAN_ON_SCHEDULE == fause
在startConnectivityScan(SCAN_ON_SCHEDULE)中看到:
if (mScreenOn) {
startPeriodicScan(scanImmediately); // 亮屏下周期扫描
} else { // screenOff
if (mWifiState == WIFI_STATE_CONNECTED) {
startConnectedPnoScan();
} else {
startDisconnectedPnoScan(); // 息屏下扫描wifi
}
}
以上代码的扫描过程根据屏幕是否点亮,分成两种情况。一种亮屏下,开始周期扫描。另一种是息屏状态,使用pno扫描。pno扫描又根据当前WifiState是连接状态还是断开状态分为两种情况。
这个方法用来在亮屏下进行周期扫描:
if (!ENABLE_BACKGROUND_SCAN) {
if (scanImmediately) {
resetLastPeriodicSingleScanTimeStamp();
}
Log.e(TAG, "PeriodicSingleScan by zhangdalei");
mPeriodicSingleScanInterval = PERIODIC_SCAN_INTERVAL_MS;
startPeriodicSingleScan();
}
因为ENABLE_CONNECTED_PNO_SCAN变量设置为false,因此这个方法将不被执行。
这是我们现在需要关注的PNO扫描过程:
private void startDisconnectedPnoScan() {
// Initialize PNO settings
PnoSettings pnoSettings = new PnoSettings();
ArrayList.PnoNetwork> pnoNetworkList =
mConfigManager.retrieveDisconnectedPnoNetworkList();
int listSize = pnoNetworkList.size();
if (listSize == 0) {
// No saved network
localLog("No saved network for starting disconnected PNO.");
return;
}
// 设置 pnoSetting 对象参数
pnoSettings.networkList = new PnoSettings.PnoNetwork[listSize];
pnoSettings.networkList = pnoNetworkList.toArray(pnoSettings.networkList);
pnoSettings.min5GHzRssi = mMin5GHzRssi;
pnoSettings.min24GHzRssi = mMin24GHzRssi;
pnoSettings.initialScoreMax = mInitialScoreMax;
pnoSettings.currentConnectionBonus = mCurrentConnectionBonus;
pnoSettings.sameNetworkBonus = mSameNetworkBonus;
pnoSettings.secureBonus = mSecureBonus;
pnoSettings.band5GHzBonus = mBand5GHzBonus;
// Initialize scan settings
ScanSettings scanSettings = new ScanSettings();
scanSettings.band = getScanBand();
scanSettings.reportEvents = WifiScanner.REPORT_EVENT_NO_BATCH;
scanSettings.numBssidsPerScan = 0;
scanSettings.periodInMs = DISCONNECTED_PNO_SCAN_INTERVAL_MS; // 20s
// TODO: enable exponential back off scan later to further save energy
// scanSettings.maxPeriodInMs = 8 * scanSettings.periodInMs;
mPnoScanListener.clearScanDetails();
// 调用Scanner的函数,启动断开wifi下的pno扫描
mScanner.startDisconnectedPnoScan(scanSettings, pnoSettings, mPnoScanListener);
mPnoScanStarted = true;
}
在startDisconnectedPnoScan方法中,将传入的mPnoScanListener放到一个map中,得到一个key。然后调用
startPnoScan(scanSettings, pnoSettings, key);
继续看:
@Scanner.java
private void startPnoScan(ScanSettings scanSettings, PnoSettings pnoSettings, int key) {
// Bundle up both the settings and send it across.
Bundle pnoParams = new Bundle();
// Set the PNO scan flag.
scanSettings.isPnoScan = true;
pnoParams.putParcelable(PNO_PARAMS_SCAN_SETTINGS_KEY, scanSettings); // 将setting保存到budle,用来发送
pnoParams.putParcelable(PNO_PARAMS_PNO_SETTINGS_KEY, pnoSettings);
// 调用mAsyncChannel发送消息
mAsyncChannel.sendMessage(CMD_START_PNO_SCAN, 0, key, pnoParams);
}
在WifiStateMachine::DriverStartedState中定义了mWifiScanner。初始化时,出入其中变量mService为WifiScanningServiceImpl。现在看看WifiScanningServiceImpl的初始化过程:
messager为WifiScanningServiceImpl对象,WifiScanner是一个client,WifiScanningServiceImpl是服务端,WifiScanner通过AsyncChannel向WifiScanningServiceImpl发送扫描的命令,在Scanner中完成扫描动作。
在WifiScanningServiceImpl::startService中创建了几个状态机对象并开启了状态机。现在关注mPnoScanStateMachine。
WifiPnoScanStateMachine(Looper looper) {
super("WifiPnoScanStateMachine", looper);
setLogRecSize(512);
setLogOnlyTransitions(false);
// CHECKSTYLE:OFF IndentationCheck
addState(mDefaultState);
addState(mStartedState, mDefaultState);
addState(mHwPnoScanState, mStartedState);
addState(mSingleScanState, mHwPnoScanState);
addState(mSwPnoScanState, mStartedState);
// CHECKSTYLE:ON IndentationCheck 初始化为mDefaultState并
setInitialState(mDefaultState);
CMD_START_PNO_SCAN将在SwPnoScanState这个状态中处理。
@WifiScanningServiceImpl.java::SwPnoScanState
public boolean processMessage(Message msg) {
ClientInfo ci = mClients.get(msg.replyTo);
switch (msg.what) {
case WifiScanner.CMD_START_PNO_SCAN:
Bundle pnoParams = (Bundle) msg.obj;
if (pnoParams == null) {
replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "params null");
return HANDLED;
}
pnoParams.setDefusable(true);
PnoSettings pnoSettings =pnoParams.getParcelable(WifiScanner.PNO_PARAMS_PNO_SETTINGS_KEY);
ScanSettings scanSettings =pnoParams.getParcelable(WifiScanner.PNO_PARAMS_SCAN_SETTIGS_KEY);
if (addSwPnoScanRequest(ci, msg.arg2, scanSettings,pnoSettings)) {
replySucceeded(msg);
} else {
replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "bad request");
transitionTo(mStartedState);
}
break;
在处理上次发送的CMD_START_PNO_SCAN消息时,在此处将调用StartedState()做进一步处理。现在看起实现:
@WifiScanningServiceImpl.java::StartedState
public boolean processMessage(Message msg) {
ClientInfo ci = mClients.get(msg.replyTo);
switch (msg.what) {
case WifiScanner.CMD_START_PNO_SCAN:
Bundle pnoParams = (Bundle) msg.obj;
if (pnoParams == null) {
replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "params null");
return HANDLED;
}
pnoParams.setDefusable(true);
PnoSettings pnoSettings =
pnoParams.getParcelable(WifiScanner.PNO_PARAMS_PNO_SETTINGS_KEY);
// This message is handled after the transition to SwPnoScan/HwPnoScan state
deferMessage(msg);
if (mScannerImpl.isHwPnoSupported(pnoSettings.isConnected)) {
transitionTo(mHwPnoScanState);
} else {
transitionTo(mSwPnoScanState);
}
break;
我们这里支持HardwarePno,将计入mHwPnoScanState状态,进行处理CMD_START_PNO_SCAN消息:
@HwPnoScanState
public boolean processMessage(Message msg) {
ClientInfo ci = mClients.get(msg.replyTo);
switch (msg.what) {
case WifiScanner.CMD_START_PNO_SCAN:
Bundle pnoParams = (Bundle) msg.obj;
if (pnoParams == null) {
replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "params null");
return HANDLED;
}
pnoParams.setDefusable(true);
PnoSettings pnoSettings = pnoParams.getParcelable(WifiScanner.PNO_PARAMS_PNO_SETTINGS_KEY);
ScanSettings scanSettings =
pnoParams.getParcelable(WifiScanner.PNO_PARAMS_SCAN_SETTINGS_KEY);
if (addHwPnoScanRequest(ci, msg.arg2, scanSettings, pnoSettings)) {
replySucceeded(msg);
} else {
replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "bad request");
transitionTo(mStartedState);
}
break;
上面代码进一步调用addHwPnoScanRequest()函数处理PNO扫描请求。
private boolean addHwPnoScanRequest(ClientInfo ci, int handler, ScanSettings scanSettings, PnoSettings pnoSettings) {
WifiNative.PnoSettings nativePnoSettings = convertPnoSettingsToNative(pnoSettings);
if (!mScannerImpl.setHwPnoList(nativePnoSettings,mPnoScanStateMachine)) {
return false;
}
}
这里的mScannerImpl实际是SupplicantWifiScannerImpl类。这里将调用到SupplicantWifiScannerImpl::setHwPnoList();
public boolean setHwPnoList(WifiNative.PnoSettings settings,
WifiNative.PnoEventHandler eventHandler) {
synchronized (mSettingsLock) {
if (mPnoSettings != null) {
Log.w(TAG, "Already running a PNO scan");
return false;
}
mPnoEventHandler = eventHandler;
mPnoSettings = settings;
if (!setNetworkPriorities(settings.networkList)) return false;
// For supplicant based PNO, we start the scan immediately when we set pno list.
processPendingScans();
return true;
}
}
调用processPendingScans()进一步处理扫描请求。processPendingScans()函数用来处理各种扫描请求,包括正常的扫描和PNO扫描。现在我们只看PNO扫描:
private void processPendingScans() {
else if (isHwPnoScanRequired()) {
newScanSettings.setHwPnoScan(mPnoSettings.networkList, mPnoEventHandler);
boolean status;
// If the PNO network list has changed from the previous request, ensure that
// we bypass the debounce logic and restart PNO scan.
if (isDifferentPnoScanSettings(newScanSettings)) {
// 启动PNO扫描
status = restartHwPnoScan();
} else {
// 启动PNO扫描
status = startHwPnoScan();
}
if (status) {
mLastScanSettings = newScanSettings;
} else {
Log.e(TAG, "Failed to start PNO scan");
// indicate scan failure async
mEventHandler.post(new Runnable() {
public void run() {
if (mPnoEventHandler != null) {
mPnoEventHandler.onPnoScanFailed();
}
// Clean up PNO state, we don't want to continue PNO scanning.
mPnoSettings = null;
mPnoEventHandler = null;
}
});
}
}
我们看restartHwPnoScan()的执行流程。不过可想到,接下来将要向wpa_supplicant发送执行了。
private boolean restartHwPnoScan() {
mHwPnoDebouncer.forceStopPnoScan();
return mHwPnoDebouncer.startPnoScan(mHwPnoDebouncerListener);
}
public boolean startPnoScan(Listener listener) {
if (DBG) Log.d(TAG, "Starting PNO scan");
mListener = listener;
if (!setPnoState(true)) {
mListener = null;
return false;
}
return true;
}
setPnoState()将调用updatePnoState()函数:
private boolean updatePnoState(boolean enable) {
if (mCurrentPnoState == enable) {
if (DBG) Log.d(TAG, "PNO state is already " + enable);
return true;
}
mLastPnoChangeTimeStamp = mClock.elapsedRealtime();
// 调用Native函数向wpa_supplicant发消息了
if (mWifiNative.setPnoScan(enable)) {
Log.d(TAG, "Changed PNO state from " + mCurrentPnoState + " to " + enable);
mCurrentPnoState = enable;
return true;
} else {
Log.e(TAG, "PNO state change to " + enable + " failed");
mCurrentPnoState = false;
return false;
}
}
public boolean setPnoScan(boolean enable) {
String cmd = enable ? "SET pno 1" : "SET pno 0";
return doBooleanCommand(cmd);
}
大体的扫描流程就是这样了。
Pno存在这样一个问题:
设置一个wifi密码为空,让设备连接。连接上后,设置wifi有密码。这个时候PNO能够匹配SSID找到wifi,但是加密方式改变了。这是PNO就会持续不断的扫描,导致功耗高。需要解决这个问题。
在SupplicantWifiScannerImpl.java中注册了接收WifiMonitor扫描结果的消息的handle。当扫描错误或是有扫描结果时将调用handle处理。
@SupplicantWifiScannerImpl.java
WifiMonitor.getInstance().registerHandler(mWifiNative.getInterfaceName(), WifiMonitor.SCAN_FAILED_EVENT, mEventHandler);
WifiMonitor.getInstance().registerHandler(mWifiNative.getInterfaceName(), WifiMonitor.SCAN_RESULTS_EVENT, mEventHandler);
@Override
public boolean handleMessage(Message msg) {
switch(msg.what) {
case WifiMonitor.SCAN_FAILED_EVENT:
Log.w(TAG, "Scan failed");
mAlarmManager.cancel(mScanTimeoutListener);
reportScanFailure();
processPendingScans();
break;
case WifiMonitor.SCAN_RESULTS_EVENT:
mAlarmManager.cancel(mScanTimeoutListener);
pollLatestScanData();
processPendingScans();
break;
default:
// ignore unknown event
}
return true;
}
不管是SCAN_FAILED_EVENT或是SCAN_RESULTS_EVENT都讲调用processPendingScans()进一步处理PNO扫描。
lse if (isHwPnoScanRequired()) {
newScanSettings.setHwPnoScan(mPnoSettings.networkList, mPnoEventHandler);
boolean status;
// If the PNO network list has changed from the previous request, ensure that
// we bypass the debounce logic and restart PNO scan.
if (isDifferentPnoScanSettings(newScanSettings)) {
status = restartHwPnoScan();
} else {
status = startHwPnoScan();
}
if (status) {
mLastScanSettings = newScanSettings;
} else {
Log.e(TAG, "Failed to start PNO scan");
// indicate scan failure async
mEventHandler.post(new Runnable() {
public void run() {
if (mPnoEventHandler != null) {
mPnoEventHandler.onPnoScanFailed();
}
// Clean up PNO state, we don't want to continue PNO scanning.
mPnoSettings = null;
mPnoEventHandler = null;
}
});
}
现在看上面代码的一些细节:
一是isHwPnoScanRequired()函数:
private boolean isHwPnoScanRequired(boolean isConnectedPno) {
return (!isConnectedPno & mHwPnoScanSupported);
}
private boolean isHwPnoScanRequired() {
if (mPnoSettings == null) return false;
return isHwPnoScanRequired(mPnoSettings.isConnected);
}
如果mPnoSettings为NULL,返回false,PNO不在执行。然后mPnoSettings.isConnected我们这里是断开wifi时的pno,为false. mPnoSettings在setHwPnoList时设置。
但是当我们PNO扫描成功,并不会把mPnoSettings设置为NULL,这将导致一次PNO扫描之后,将接着下一次PNO扫描。代码如下;
if (status) {
mLastScanSettings = newScanSettings;
} else {
Log.e(TAG, "Failed to start PNO scan");
// indicate scan failure async
mEventHandler.post(new Runnable() {
public void run() {
if (mPnoEventHandler != null) {
mPnoEventHandler.onPnoScanFailed();
}
// Clean up PNO state, we don't want to continue PNO scanning.
mPnoSettings = null;
mPnoEventHandler = null;
}
});
所以我们需要在扫描成功是吧mPnoSettings设置为NULL。解决问题。