Android -- Wifi扫描流程分析
Wifi扫描的调用函数是WifiManager中的startScan()函数:
/**
* Request a scan for access points. Returns immediately. The availability
* of the results is made known later by means of an asynchronous event sent
* on completion of the scan.
* @return {@code true} if the operation succeeded, i.e., the scan was initiated
*/
public boolean startScan() {
try {
mService.startScan(null, null);
return true;
} catch (RemoteException e) {
return false;
}
}
/** @hide */
@SystemApi
public boolean startScan(WorkSource workSource) {
try {
mService.startScan(null, workSource);
return true;
} catch (RemoteException e) {
return false;
}
}
通过AIDL调用WifiService中的同名函数:
/**
* see {@link android.net.wifi.WifiManager#startScan}
* and {@link android.net.wifi.WifiManager#startCustomizedScan}
*
* @param settings If null, use default parameter, i.e. full scan.
* @param workSource If null, all blame is given to the calling uid.
*/
public void startScan(ScanSettings settings, WorkSource workSource) {
enforceChangePermission();
synchronized (this) {
if (mInIdleMode) {
// Need to send an immediate scan result broadcast in case the
// caller is waiting for a result ..
// clear calling identity to send broadcast
long callingIdentity = Binder.clearCallingIdentity();
try {
mWifiStateMachine.sendScanResultsAvailableBroadcast(/* scanSucceeded = */ false);
} finally {
// restore calling identity
Binder.restoreCallingIdentity(callingIdentity);
}
mScanPending = true;
return;
}
}
if (settings != null) {
settings = new ScanSettings(settings);
if (!settings.isValid()) {
Slog.e(TAG, "invalid scan setting");
return;
}
}
if (workSource != null) {
enforceWorkSourcePermission();
// WifiManager currently doesn't use names, so need to clear names out of the
// supplied WorkSource to allow future WorkSource combining.
workSource.clearNames();
}
mWifiStateMachine.startScan(Binder.getCallingUid(), scanRequestCounter++,
settings, workSource);
}
进入WifiStateMachine,向状态机中发送CMD_START_SCAN消息开启扫描流程DriverStartedState处理,开始扫描:
private void handleScanRequest(int type, Message message) {
...
// call wifi native to start the scan
if (startScanNative(type, freqs)) {
// only count battery consumption if scan request is accepted
noteScanStart(message.arg1, workSource);
// a full scan covers everything, clearing scan request buffer
if (freqs == null)
mBufferedScanMsg.clear();
messageHandlingStatus = MESSAGE_HANDLING_STATUS_OK;
if (workSource != null) {
// External worksource was passed along the scan request,
// hence always send a broadcast
mSendScanResultsBroadcast = true;
}
return;
}
...
}
startScanStar():
/**
* return true iff scan request is accepted
*/
private boolean startScanNative(int type, String freqs) {
if (mWifiNative.scan(type, freqs)) {
mIsScanOngoing = true;
mIsFullScanOngoing = (freqs == null);
lastScanFreqs = freqs;
return true;
}
return false;
}
调用native方法scan()向wpa_supplicant发送扫描指令;当wpa_s扫描结束后,WifiMonitor就会收到wpa_s上报的事件,framework层就知道扫描已经结束,可以去获取扫描结果了。WifiMonitor中:
/**
* Handle all supplicant events except STATE-CHANGE
* @param event the event type
* @param remainder the rest of the string following the
* event name and " — "
*/
void handleEvent(int event, String remainder) {
if (DBG) {
logDbg("handleEvent " + Integer.toString(event) + " " + remainder);
}
switch (event) {
case DISCONNECTED:
handleNetworkStateChange(NetworkInfo.DetailedState.DISCONNECTED, remainder);
break;
case CONNECTED:
handleNetworkStateChange(NetworkInfo.DetailedState.CONNECTED, remainder);
break;
case SCAN_RESULTS:
mStateMachine.sendMessage(SCAN_RESULTS_EVENT);
break;
case SCAN_FAILED:
mStateMachine.sendMessage(SCAN_FAILED_EVENT);
break;
case UNKNOWN:
if (DBG) {
logDbg("handleEvent unknown: " + Integer.toString(event) + " " + remainder);
}
break;
default:
break;
}
}
向WifiStateMachine发送SCAN_RESULTS_EVENT消息,告知现在可以获取扫描结果。
SupplicantStartedState中处理该消息:
case WifiMonitor.SCAN_RESULTS_EVENT:
case WifiMonitor.SCAN_FAILED_EVENT:
maybeRegisterNetworkFactory(); // Make sure our NetworkFactory is registered
closeRadioScanStats();
noteScanEnd();
setScanResults();
if (mIsFullScanOngoing || mSendScanResultsBroadcast) {
/* Just updated results from full scan, let apps know about this */
boolean scanSucceeded = message.what == WifiMonitor.SCAN_RESULTS_EVENT;
sendScanResultsAvailableBroadcast(scanSucceeded);
}
mSendScanResultsBroadcast = false;
mIsScanOngoing = false;
mIsFullScanOngoing = false;
if (mBufferedScanMsg.size() > 0)
sendMessage(mBufferedScanMsg.remove());
break;
setScanResults()从wpa_s获取扫描结果,并保存;同时,也会进入autojoin流程,进行重连操作。最后发送ScanResults的广播,通知上层获取扫描结果。
本文讲述了基本的扫描处理流程,wpa_s中的处理,大家有兴趣可以自己研究。目前的工作中,wpa_s的代码基本都不涉及改动,所以这部分了解不多。