文章目录
- 系列文章目录
- 前言
- 一、wifi扫描流程(单次扫描)
- 1、wifi扫描源码流程分析
- 2、wifi扫描流程图
- 二、wifi扫描中的扫描模式
- 1、单次扫描
- 2、周期扫描
- 3、PNO扫描
- 总结
前言
之前有分析学习过wifi的开启【安卓Framework学习】Wifi框架学习之开启与关闭流程和连接过程【安卓Framework学习】Wifi框架学习之连接与断开流程,但在没有连接的时候wifi模组又是如何扫描运作的呢?本篇将从wifi的扫描过程中分析其源码,并分析wifi扫描模式的不同。本篇代码主要基于安卓11源码进行分析。
一、wifi扫描流程(单次扫描)
1、wifi扫描源码流程分析
在wifi框架中,触发扫描的地方有很多,比如刚开启wifi就会触发扫描,所以这里只考虑从WifiManager
调用下去的扫描流程。依旧是从上层调用扫描代码开始。
public boolean startScan() {
return startScan(null);
}
public boolean startScan(WorkSource workSource) {
try {
String packageName = mContext.getOpPackageName();
String attributionTag = mContext.getAttributionTag();
return mService.startScan(packageName, attributionTag);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
直接调用到系统服务中的WifiServiceImpl.startScan
.
public boolean startScan(String packageName, String featureId) {
if (enforceChangePermission(packageName) != MODE_ALLOWED) {
return false;
}
int callingUid = Binder.getCallingUid();
long ident = Binder.clearCallingIdentity();
/*省略部分代码*/
try {
mWifiPermissionsUtil.enforceCanAccessScanResults(packageName, featureId, callingUid,
null);
Boolean scanSuccess = mWifiThreadRunner.call(() ->
mScanRequestProxy.startScan(callingUid, packageName), null);
if (scanSuccess == null) {
sendFailedScanBroadcast();
return false;
}
if (!scanSuccess) {
Log.e(TAG, "Failed to start scan");
return false;
}
} /*省略部分代码*/
return true;
}
其中mWifiThreadRunner
对象就是一个通过Handler
对象执行任务的一个类,那么这里调用到了ScanRequestProxy.startScan
.
public boolean startScan(int callingUid, String packageName) {
/*省略部分代码*/
WorkSource workSource = new WorkSource(callingUid, packageName);
// Create the scan settings.
WifiScanner.ScanSettings settings = new WifiScanner.ScanSettings();
// Scan requests from apps with network settings will be of high accuracy type.
if (fromSettingsOrSetupWizard) {
settings.type = WifiScanner.SCAN_TYPE_HIGH_ACCURACY;
}
// always do full scans
settings.band = WifiScanner.WIFI_BAND_ALL;
settings.reportEvents = WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN
| WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT;
if (mScanningForHiddenNetworksEnabled) {
settings.hiddenNetworks.clear();
// retrieve the list of hidden network SSIDs from saved network to scan for, if enabled.
settings.hiddenNetworks.addAll(mWifiConfigManager.retrieveHiddenNetworkList());
// retrieve the list of hidden network SSIDs from Network suggestion to scan for.
settings.hiddenNetworks.addAll(
mWifiInjector.getWifiNetworkSuggestionsManager().retrieveHiddenNetworkList());
}
mWifiScanner.startScan(settings, new HandlerExecutor(mHandler),
new ScanRequestProxyScanListener(), workSource);
return true;
}
前面设置了一些扫描必要的内容和参数后,调用了WifiScanner.startScan
,这个是和WifiManager
一样的一个代理类。
public void startScan(ScanSettings settings, @Nullable @CallbackExecutor Executor executor,
ScanListener listener, WorkSource workSource) {
Objects.requireNonNull(listener, "listener cannot be null");
int key = addListener(listener, executor);
if (key == INVALID_KEY) return;
validateChannel();
Bundle scanParams = new Bundle();
scanParams.putParcelable(SCAN_PARAMS_SCAN_SETTINGS_KEY, settings);
scanParams.putParcelable(SCAN_PARAMS_WORK_SOURCE_KEY, workSource);
scanParams.putString(REQUEST_PACKAGE_NAME_KEY, mContext.getOpPackageName());
scanParams.putString(REQUEST_FEATURE_ID_KEY, mContext.getAttributionTag());
mAsyncChannel.sendMessage(CMD_START_SINGLE_SCAN, 0, key, scanParams);
}
这里用到了异步通道AsyncChannel
跨进程与系统服务进行跨进程通信。这里不赘述连接过程,直接得出结论,跨进程通信的系统服务是WifiScannerServiceImpl
是IWifiScanner
的实现类。在WifiScannerServiceImpl
中对异步通道过来的消息是由其内部类ClientHandler
来处理,所以看ClientHandler.handleMessage
.
public void handleMessage(Message msg) {
/*省略部分代码*/
switch (msg.what) {
/*省略部分代码*/
case WifiScanner.CMD_START_SINGLE_SCAN:
case WifiScanner.CMD_STOP_SINGLE_SCAN:
mSingleScanStateMachine.sendMessage(Message.obtain(msg));
break;
/*省略部分代码*/
}
}
ClientHandler
在收到CMD_START_SINGLE_SCAN
消息后直接给WifiScannerServiceImpl
内部状态机WifiSingleScanStateMachine
发送了消息,状态机这里之前有详细分析过,直接看DriverStartedState
对消息的处理。
public boolean processMessage(Message msg) {
ClientInfo ci = mClients.get(msg.replyTo);
switch (msg.what) {
/*省略部分代码*/
case WifiScanner.CMD_START_SINGLE_SCAN:
int handler = msg.arg2;
Bundle scanParams = (Bundle) msg.obj;
/*省略部分代码*/
if (validateScanRequest(ci, handler, scanSettings)) {
/*省略部分代码*/
if (getCurrentState() == mScanningState) {
if (activeScanSatisfies(scanSettings)) {
mActiveScans.addRequest(ci, handler, workSource, scanSettings);
} else {
mPendingScans.addRequest(ci, handler, workSource, scanSettings);
}
} else {
mPendingScans.addRequest(ci, handler, workSource, scanSettings);
tryToStartNewScan();
}
} /*省略部分代码*/
return HANDLED;
/*省略部分代码*/
}
}
这里简单说明一下扫描过程中,WifiSingleScanStateMachine
状态机中状态的切换其实就是在ScanningState
和IdleState
之间切换。在向下发送扫描指令后,会切换到ScanningState
,在接收到有扫描结果的消息后会切换到IdleState
,所以其实不管当前状态是否为ScanningState
,都会调用到tryToStartNewScan
方法,这个方法才是真正开启扫描的方法。直接看tryToStartNewScan
方法。
void tryToStartNewScan() {
if (mPendingScans.size() == 0) { // no pending requests
return;
}
mChannelHelper.updateChannels();
// TODO move merging logic to a scheduler
WifiNative.ScanSettings settings = new WifiNative.ScanSettings();
settings.num_buckets = 1;
WifiNative.BucketSettings bucketSettings = new WifiNative.BucketSettings();
bucketSettings.bucket = 0;
bucketSettings.period_ms = 0;
bucketSettings.report_events = WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN;
ChannelCollection channels = mChannelHelper.createChannelCollection();
List<WifiNative.HiddenNetwork> hiddenNetworkList = new ArrayList<>();
for (RequestInfo<ScanSettings> entry : mPendingScans) {
settings.scanType = mergeScanTypes(settings.scanType, entry.settings.type);
channels.addChannels(entry.settings);
for (ScanSettings.HiddenNetwork srcNetwork : entry.settings.hiddenNetworks) {
WifiNative.HiddenNetwork hiddenNetwork = new WifiNative.HiddenNetwork();
hiddenNetwork.ssid = srcNetwork.ssid;
hiddenNetworkList.add(hiddenNetwork);
}
if ((entry.settings.reportEvents & WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT)
!= 0) {
bucketSettings.report_events |= WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT;
}
}
if (hiddenNetworkList.size() > 0) {
settings.hiddenNetworks = new WifiNative.HiddenNetwork[hiddenNetworkList.size()];
int numHiddenNetworks = 0;
for (WifiNative.HiddenNetwork hiddenNetwork : hiddenNetworkList) {
settings.hiddenNetworks[numHiddenNetworks++] = hiddenNetwork;
}
}
channels.fillBucketSettings(bucketSettings, Integer.MAX_VALUE);
settings.buckets = new WifiNative.BucketSettings[] {bucketSettings};
if (mScannerImplsTracker.startSingleScan(settings)) {
// store the active scan settings
mActiveScanSettings = settings;
// swap pending and active scan requests
RequestList<ScanSettings> tmp = mActiveScans;
mActiveScans = mPendingScans;
mPendingScans = tmp;
// make sure that the pending list is clear
mPendingScans.clear();
transitionTo(mScanningState);
} else {
mWifiMetrics.incrementScanReturnEntry(
WifiMetricsProto.WifiLog.SCAN_UNKNOWN, mPendingScans.size());
// notify and cancel failed scans
sendOpFailedToAllAndClear(mPendingScans, WifiScanner.REASON_UNSPECIFIED,
"Failed to start single scan");
}
}
这个方法中,前面部分其实就是设置一些扫描的信道个数,扫描周期,和隐藏列表等,后面主要的是调用ScannerImplsTracker.startSingleScan
往下走进行扫描的。
public boolean startSingleScan(WifiNative.ScanSettings scanSettings) {
mStatusPerImpl.clear();
boolean anySuccess = false;
for (Map.Entry<String, WifiScannerImpl> entry : mScannerImpls.entrySet()) {
String ifaceName = entry.getKey();
WifiScannerImpl impl = entry.getValue();
boolean success = impl.startSingleScan(
scanSettings, new ScanEventHandler(ifaceName));
if (!success) {
Log.e(TAG, "Failed to start single scan on " + ifaceName);
continue;
}
mStatusPerImpl.put(ifaceName, STATUS_PENDING);
anySuccess = true;
}
return anySuccess;
}
这个方法中其实就是看系统有几种WifiScannerImpl
类的实现方式,分别调用他们的startSingleScan
方法。在安卓11源码中,有一个子类WificondScannerImpl
继承了WifiScannerImpl
,再分析WificondScannerImpl.startSingleScan
.
public boolean startSingleScan(WifiNative.ScanSettings settings,
WifiNative.ScanEventHandler eventHandler) {
/*省略部分代码*/
synchronized (mSettingsLock) {
/*省略部分代码*/
if (!allFreqs.isEmpty()) {
freqs = allFreqs.getScanFreqs();
success = mWifiNative.scan(
getIfaceName(), settings.scanType, freqs, hiddenNetworkSSIDSet);
if (!success) {
Log.e(TAG, "Failed to start scan, freqs=" + freqs);
}
} else {
/*省略部分代码*/
}
/*省略部分代码*/
return true;
}
}
前面一堆代码都是添加扫描的频段和隐藏的热点,重要的是后面调用的WifiNative.scan
方法,这个方法才是往下走的。
public boolean scan(
@NonNull String ifaceName, @WifiAnnotations.ScanType int scanType, Set<Integer> freqs,
List<String> hiddenNetworkSSIDs) {
List<byte[]> hiddenNetworkSsidsArrays = new ArrayList<>();
for (String hiddenNetworkSsid : hiddenNetworkSSIDs) {
try {
hiddenNetworkSsidsArrays.add(
NativeUtil.byteArrayFromArrayList(
NativeUtil.decodeSsid(hiddenNetworkSsid)));
} catch (IllegalArgumentException e) {
Log.e(TAG, "Illegal argument " + hiddenNetworkSsid, e);
continue;
}
}
return mWifiCondManager.startScan(ifaceName, scanType, freqs, hiddenNetworkSsidsArrays);
}
在对隐藏热点再次处理后,调用了WifiNl80211Manager.startScan
,接着看WifiNl80211Manager
.
public boolean startScan(@NonNull String ifaceName, @WifiAnnotations.ScanType int scanType,
@Nullable Set<Integer> freqs, @Nullable List<byte[]> hiddenNetworkSSIDs) {
IWifiScannerImpl scannerImpl = getScannerImpl(ifaceName);
/*省略部分代码*/
SingleScanSettings settings = new SingleScanSettings();
try {
settings.scanType = getScanType(scanType);
} catch (IllegalArgumentException e) {
Log.e(TAG, "Invalid scan type ", e);
return false;
}
settings.channelSettings = new ArrayList<>();
settings.hiddenNetworks = new ArrayList<>();
if (freqs != null) {
for (Integer freq : freqs) {
ChannelSettings channel = new ChannelSettings();
channel.frequency = freq;
settings.channelSettings.add(channel);
}
}
if (hiddenNetworkSSIDs != null) {
for (byte[] ssid : hiddenNetworkSSIDs) {
HiddenNetwork network = new HiddenNetwork();
network.ssid = ssid;
// settings.hiddenNetworks is expected to be very small, so this shouldn't cause
// any performance issues.
if (!settings.hiddenNetworks.contains(network)) {
settings.hiddenNetworks.add(network);
}
}
}
try {
return scannerImpl.scan(settings);
} catch (RemoteException e1) {
Log.e(TAG, "Failed to request scan due to remote exception");
}
return false;
}
方法前面部分确定了扫描类型、通道的扫描频率和隐藏热点,然后调用了IWifiScannerImpl
接口的scan
方法。IWifiScannerImpl
接口为一个AIDL,其实现在下面的C++代码中。到这里整个扫描流程都分析完了,下面要继续分析是如何上报扫描结果。
在【安卓Framework学习】Wifi框架学习之开启与关闭流程中分析到,在开启wifi时,会调用WifiNative
中的setupInterfaceForClientInScanMode()
方法,这个方法再来分析一次。
public String setupInterfaceForClientInScanMode(
@NonNull InterfaceCallback interfaceCallback) {
synchronized (mLock) {
/*省略部分代码*/
if (!mWifiCondManager.setupInterfaceForClientMode(iface.name, Runnable::run,
new NormalScanEventCallback(iface.name),
new PnoScanEventCallback(iface.name))) {
Log.e(TAG, "Failed to setup iface in wificond=" + iface.name);
teardownInterface(iface.name);
mWifiMetrics.incrementNumSetupClientInterfaceFailureDueToWificond();
return null;
}
/*省略部分代码*/
return iface.name;
}
}
在WifiNative
方法中,向WifiNl80211Manager
注册了NormalScanEventCallback
回调类,再看WifiNl80211Manager.setupInterfaceForClientMode
方法。
public boolean setupInterfaceForClientMode(@NonNull String ifaceName,
@NonNull @CallbackExecutor Executor executor,
@NonNull ScanEventCallback scanCallback, @NonNull ScanEventCallback pnoScanCallback)
/*省略部分代码*/
try {
IWifiScannerImpl wificondScanner = clientInterface.getWifiScannerImpl();
if (wificondScanner == null) {
Log.e(TAG, "Failed to get WificondScannerImpl");
return false;
}
mWificondScanners.put(ifaceName, wificondScanner);
Binder.allowBlocking(wificondScanner.asBinder());
ScanEventHandler scanEventHandler = new ScanEventHandler(executor, scanCallback);
mScanEventHandlers.put(ifaceName, scanEventHandler);
wificondScanner.subscribeScanEvents(scanEventHandler);
/*省略部分代码*/
} catch (RemoteException e) {
Log.e(TAG, "Failed to refresh wificond scanner due to remote exception");
}
return true;
}
可以看到,调用了IWifiScannerImpl
接口的实现类中注册了回调函数,之前有提到过此接口的实现在下面C++代码中,这里不往下分析。在看注册的回调类NormalScanEventCallback
.
private class NormalScanEventCallback implements WifiNl80211Manager.ScanEventCallback {
private String mIfaceName;
NormalScanEventCallback(String ifaceName) {
mIfaceName = ifaceName;
}
@Override
public void onScanResultReady() {
Log.d(TAG, "Scan result ready event");
mWifiMonitor.broadcastScanResultEvent(mIfaceName);
}
@Override
public void onScanFailed() {
Log.d(TAG, "Scan failed event");
mWifiMonitor.broadcastScanFailedEvent(mIfaceName);
}
}
那么在扫描成功有扫描结果后,变回调用onScanResultReady
回调方法,方法中通过WifiMonitor.broadcastScanResultEvent
对外发送了监听消息,监听对应消息的对象都能收到此消息。再来看WifiMonitor.broadcastScanResultEvent
方法。
public void broadcastScanResultEvent(String iface) {
sendMessage(iface, SCAN_RESULTS_EVENT);
}
这时,需要知道哪些类向WifiMonitor
注册监听了SCAN_RESULTS_EVENT
消息,回到WificondScannerImpl
也就是WifiScannerImpl
的子类,其构造方法如下。
public WificondScannerImpl(Context context, String ifaceName, WifiNative wifiNative,
WifiMonitor wifiMonitor, ChannelHelper channelHelper,
Looper looper, Clock clock) {
/*省略部分代码*/
mEventHandler = new Handler(looper, this);
mClock = clock;
wifiMonitor.registerHandler(getIfaceName(),
WifiMonitor.SCAN_FAILED_EVENT, mEventHandler);
wifiMonitor.registerHandler(getIfaceName(),
WifiMonitor.PNO_SCAN_RESULTS_EVENT, mEventHandler);
wifiMonitor.registerHandler(getIfaceName(),
WifiMonitor.SCAN_RESULTS_EVENT, mEventHandler);
}
可以看到是WificondScannerImpl
注册监听了此消息,那么在看其是如何处理SCAN_RESULTS_EVENT
消息的。
public boolean handleMessage(Message msg) {
switch(msg.what) {
/*省略部分代码*/
case WifiMonitor.SCAN_RESULTS_EVENT:
cancelScanTimeout();
pollLatestScanData();
break;
default:
// ignore unknown event
}
return true;
}
首先调用了cancelScanTimeout
取消了在AlarmManager
中注册的定时任务,然后进入pollLatestScanData
方法中。
private void pollLatestScanData() {
synchronized (mSettingsLock) {
/*省略部分代码*/
/*到这里其实扫描已经完成,然后扫描结果还存在hal层,这部分省略的代码其实就是从hal层取扫描结果然后处理成java层需要的样子*/
if (mLastScanSettings.singleScanEventHandler != null) {
if (mLastScanSettings.reportSingleScanFullResults) {
for (ScanResult scanResult : singleScanResults) {
// ignore buckets scanned since there is only one bucket for a single scan
mLastScanSettings.singleScanEventHandler.onFullScanResult(scanResult,
/* bucketsScanned */ 0);
}
}
Collections.sort(singleScanResults, SCAN_RESULT_SORT_COMPARATOR);
mLatestSingleScanResult = new WifiScanner.ScanData(0, 0, 0,
getBandScanned(mLastScanSettings.singleScanFreqs),
singleScanResults.toArray(new ScanResult[singleScanResults.size()]));
mLastScanSettings.singleScanEventHandler
.onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE);
}
mLastScanSettings = null;
}
}
这个方法主要是从HAL层拉去新扫描的数据,然后处理后调用回调通知上层。在前面调用WificondScannerImpl
中的startSingleScan
方法时,将一个LastScanSettings
对象赋值给了mLastScanSettings
,在mLastScanSettings
内部有一个WifiNative.ScanEventHandler
类的回调对象,其实现类为WifiScanningServiceImpl
中的ScanEventHandler
。这里在处理完扫描结果后,调用了WifiNative.ScanEventHandler
类的回调对象的onScanStatus
方法,所以这里我们直接看WifiScanningServiceImpl
中的ScanEventHandler.onScanStatus
.
public void onScanStatus(int event) {
switch (event) {
case WifiNative.WIFI_SCAN_RESULTS_AVAILABLE:
case WifiNative.WIFI_SCAN_THRESHOLD_NUM_SCANS:
case WifiNative.WIFI_SCAN_THRESHOLD_PERCENT:
reportScanStatusForImpl(mImplIfaceName, STATUS_SUCCEEDED);
break;
case WifiNative.WIFI_SCAN_FAILED:
reportScanStatusForImpl(mImplIfaceName, STATUS_FAILED);
break;
default:
Log.e(TAG, "Unknown scan status event: " + event);
break;
}
}
扫描成功的话在pollLatestScanData
会发送WIFI_SCAN_RESULTS_AVAILABLE
消息,然后调用reportScanStatusForImpl
.
private void reportScanStatusForImpl(@NonNull String implIfaceName, int newStatus) {
Integer currentStatus = mStatusPerImpl.get(implIfaceName);
if (currentStatus != null && currentStatus == STATUS_PENDING) {
mStatusPerImpl.put(implIfaceName, newStatus);
}
// Now check if all the scanner impls scan status is available.
int consolidatedStatus = getConsolidatedStatus();
if (consolidatedStatus == STATUS_SUCCEEDED) {
sendMessage(CMD_SCAN_RESULTS_AVAILABLE);
} else if (consolidatedStatus == STATUS_FAILED) {
sendMessage(CMD_SCAN_FAILED);
}
}
这里其实做的操作是检验是否所有的wifi芯片开启的接口扫描都是成功了,这里就先认为是都成功了,其实也会都成功,那么会继续给状态机发送CMD_SCAN_RESULTS_AVAILABLE
消息,这时候根据前面的分析内容,状态机正处于ScanningState
状态,那么转入ScanningState
状态的处理方法。
public boolean processMessage(Message msg) {
switch (msg.what) {
case CMD_SCAN_RESULTS_AVAILABLE:
ScanData latestScanResults =
mScannerImplsTracker.getLatestSingleScanResults();
if (latestScanResults != null) {
/*省略部分代码*/
reportScanResults(latestScanResults);
mActiveScans.clear();
} else {
Log.e(TAG, "latest scan results null unexpectedly");
}
transitionTo(mIdleState);
return HANDLED;
/*省略部分代码*/
}
}
主要看reportScanResults
方法中做了什么操作。
void reportScanResults(@NonNull ScanData results) {
/*省略部分代码*/
for (RequestInfo<Void> entry : mSingleScanListeners) {
logCallback("singleScanResults", entry.clientInfo, entry.handlerId,
describeForLog(allResults));
entry.reportEvent(WifiScanner.CMD_SCAN_RESULT, 0, parcelableAllResults);
}
/*省略部分代码*/
}
这里主要关注mSingleScanListeners
变量中存的回调对象,会通过异步通道向WifiScanner
发送CMD_SCAN_RESULT
消息。那我们需要关注在什么时候向mSingleScanListeners
变量中注册了回调对象。可以回看前面的分析,在开启扫描的时候调用了ScanRequestProxy.startScan
,在此方法中第一行就调用了retrieveWifiScannerIfNecessary
,转入分析ScanRequestProxy.retrieveWifiScannerIfNecessary
。
private boolean retrieveWifiScannerIfNecessary() {
if (mWifiScanner == null) {
/*省略部分代码*/
if (mWifiScanner != null) {
mWifiScanner.registerScanListener(
new HandlerExecutor(mHandler), new GlobalScanListener());
}
}
return mWifiScanner != null;
}
可以看到将一个监听类注册到了WifiScanner
中,并且记住这里的回调类是GlobalScanListener
。在看WifiScanner.registerScanListener
.
public void registerScanListener(@NonNull @CallbackExecutor Executor executor,
@NonNull ScanListener listener) {
int key = addListener(listener, executor);
if (key == INVALID_KEY) return;
validateChannel();
mAsyncChannel.sendMessage(CMD_REGISTER_SCAN_LISTENER, 0, key);
}
WifiScanner
通过异步通道向WifiScanningServiceImpl
发送了CMD_REGISTER_SCAN_LISTENER
消息,再看WifiScanningServiceImpl
是如何处理CMD_REGISTER_SCAN_LISTENER
消息。在WifiScanningServiceImpl
中的ClientHandler.handleMessage
方法中。
public void handleMessage(Message msg) {
/*省略部分代码*/
switch (msg.what) {
/*省略部分代码*/
case WifiScanner.CMD_REGISTER_SCAN_LISTENER:
logScanRequest("registerScanListener", ci, msg.arg2, null, null, null);
mSingleScanListeners.addRequest(ci, msg.arg2, null, null);
replySucceeded(msg);
break;
}
/*省略部分代码*/
}
这时候可以看到,向mSingleScanListeners
添加了对应的回调信息,再次回到前面分析的reportScanResults
方法,遍历了所有mSingleScanListeners
中的回调,并发送CMD_SCAN_RESULT
消息给WifiScanner
。再看WifiScanner
是如何处理CMD_SCAN_RESULT
消息的。
public void handleMessage(Message msg) {
/*省略部分代码*/
switch (msg.what) {
/*省略部分代码*/
case CMD_SCAN_RESULT: {
ScanListener scanListener = (ScanListener) listener;
ParcelableScanData parcelableScanData = (ParcelableScanData) msg.obj;
Binder.clearCallingIdentity();
executor.execute(() -> scanListener.onResults(parcelableScanData.getResults()));
} break;
}
/*省略部分代码*/
}
根据WifiScanningServiceImpl
返回过来的回调信息,找出对应的回调对象listener
,然后调用其onResults
方法。由于在前面注册的回调类是GlobalScanListener
,所以直接看GlobalScanListener.onResults
.
public void onResults(WifiScanner.ScanData[] scanDatas) {
/*省略部分代码*/
if (WifiScanner.isFullBandScan(scanData.getBandScanned(), false)) {
// Store the last scan results & send out the scan completion broadcast.
mLastScanResults.clear();
mLastScanResults.addAll(Arrays.asList(scanResults));
sendScanResultBroadcast(true);
sendScanResultsAvailableToCallbacks();
}
}
在判断为全频段扫描后,直接调用sendScanResultBroadcast
对外发送广播。
private void sendScanResultBroadcast(boolean scanSucceeded) {
Intent intent = new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
intent.putExtra(WifiManager.EXTRA_RESULTS_UPDATED, scanSucceeded);
mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
}
当传入的参数scanSucceeded
为false
时,则表示没有新的扫描结果,所以一般获取EXTRA_RESULTS_UPDATED
值为true
时才去拿扫描结果数据。到这里整个扫描开始到有新的扫描结果然后对上发送广播的过程全部分析完了。
2、wifi扫描流程图
二、wifi扫描中的扫描模式
1、单次扫描
上半部分的分析内容其实就是单次扫描的整个流程,这里不做赘述了。
2、周期扫描
周期扫描其实是基于单次扫描的基础上,在java层Framework中设定一定的时间间隔,在屏幕亮起的情况下做的周期性单次扫描。原则上还是单次扫描,只是系统设定了定时任务,所以形成了周期扫描。周期扫描在wifi功能打开的时候会触发。在【安卓Framework学习】Wifi框架学习之开启与关闭流程中在开启wifi模块的过程中,ClientModeManager.ClientModeStateMachine
会将状态机切换到ConnectModeState
,看ConnectModeState.enter
方法。
public void enter() {
Log.d(TAG, "entering ConnectModeState");
mClientModeImpl.setOperationalMode(ClientModeImpl.CONNECT_MODE, mClientInterfaceName);
/*省略部分代码*/
}
调用了ClientModeImpl.setOperationalMode
,再看这个方法。
public void setOperationalMode(int mode, String ifaceName) {
/*省略部分代码*/
if (mode != CONNECT_MODE) {
/*省略部分代码*/
} else {
// do a quick sanity check on the iface name, make sure it isn't null
if (ifaceName != null) {
mInterfaceName = ifaceName;
updateInterfaceCapabilities(ifaceName);
transitionTo(mDisconnectedState);
mWifiScoreReport.setInterfaceName(ifaceName);
} /*省略部分代码*/
}
sendMessageAtFrontOfQueue(CMD_SET_OPERATIONAL_MODE);
}
由于传入的是CONNECT_MODE
,所以会走下面的分支,传入的ifaceName
,在这个状态之前底层就已经分配好了,所以不会为空。那么在这个分支里面会将ClientModeImpl
的状态切换到DisconnectedState
,但由于这个方法不是在ClientModeImpl
的状态机的Handler
中处理的,所以调用了sendMessageAtFrontOfQueue
触发一次处理消息的机制,这样在这个方法后会将状态真实切换到DisconnectedState
,进入DisconnectedState.enter
.
public void enter() {
/*省略部分代码*/
mWifiConnectivityManager.handleConnectionStateChanged(WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
}
这里可以看到用到了WifiConnectivityManager
,这个类在【安卓Framework学习】Wifi框架学习之核心类中有介绍到过,主要是管理wifi的扫描功能,那么进入这个方法。
public void handleConnectionStateChanged(int state) {
/*省略部分代码*/
if (mDisconnectedSingleScanScheduleSec == null) {
mDisconnectedSingleScanScheduleSec =
initializeScanningSchedule(WIFI_STATE_DISCONNECTED);
}
/*省略部分代码*/
mWifiState = state;
if (mWifiState == WIFI_STATE_DISCONNECTED) {
mLastConnectionAttemptBssid = null;
scheduleWatchdogTimer();
setSingleScanningSchedule(mDisconnectedSingleScanScheduleSec);
startConnectivityScan(SCAN_IMMEDIATELY);
} /*省略部分代码*/
}
mDisconnectedSingleScanScheduleSec
这个就是一个存着一组数字,这组数字代表着间隔多少秒触发一次扫描,实现周期性扫描,具体这组数据是写在配置文件中,不做详细分析。主要看startConnectivityScan
.
private void startConnectivityScan(boolean scanImmediately) {
/*省略部分代码*/
stopConnectivityScan();
/*省略部分代码*/
if (mScreenOn) {
startPeriodicScan(scanImmediately);
} else {
if (mWifiState == WIFI_STATE_DISCONNECTED && !mPnoScanStarted) {
startDisconnectedPnoScan();
}
}
}
方法中,首先停止了其他的所有扫描活动,然后判断当前屏幕是否亮起,这里我们认为屏幕是亮起的,因为屏幕关闭的情况下会走PNO扫描。进入startPeriodicScan
方法中。
private void startPeriodicScan(boolean scanImmediately) {
/*省略部分代码*/
if (scanImmediately) {
resetLastPeriodicSingleScanTimeStamp();
}
mCurrentSingleScanScheduleIndex = 0;
startPeriodicSingleScan();
}
首先会重置一下周期扫描的时间戳,然后进入周期性扫描的方法中,mCurrentSingleScanScheduleIndex
变量就是用来存前面得到的数组的索引值,表示当前的周期到了哪个值。再看startPeriodicSingleScan
方法。
private void startPeriodicSingleScan() {
/*省略部分代码*/
if (isScanNeeded) {
mLastPeriodicSingleScanTimeStamp = currentTimeStamp;
if (mWifiState == WIFI_STATE_DISCONNECTED
&& mInitialScanState == INITIAL_SCAN_STATE_START) {
startSingleScan(false, WIFI_WORK_SOURCE);
if (mInitialScanState == INITIAL_SCAN_STATE_START) {
setInitialScanState(INITIAL_SCAN_STATE_AWAITING_RESPONSE);
mWifiMetrics.incrementInitialPartialScanCount();
}
return;
}
startSingleScan(isFullBandScan, WIFI_WORK_SOURCE);
schedulePeriodicScanTimer(
getScheduledSingleScanIntervalMs(mCurrentSingleScanScheduleIndex));
mCurrentSingleScanScheduleIndex++;
} else {
/*省略部分代码*/
}
}
这里startSingleScan
方法不做详细分析,因为随后马上调用到了WifiScanner.startScan
方法中了接上了单次扫描的流程,所以不做分析了。主要看schedulePeriodicScanTimer
方法是如何实现周期性的触发单次扫描。
private void schedulePeriodicScanTimer(int intervalMs) {
mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
mClock.getElapsedSinceBootMillis() + intervalMs,
PERIODIC_SCAN_TIMER_TAG,
mPeriodicScanTimerListener, mEventHandler);
mPeriodicScanTimerSet = true;
}
getScheduledSingleScanIntervalMs
这个方法其实就是从之前的数组中取出当前所需要的那个时间,在这个方法中通过AlarmManager
向系统中注册了一个定时任务在传入参数的毫秒数之后执行mPeriodicScanTimerListener
,再看看mPeriodicScanTimerListener
的实现。
private final AlarmManager.OnAlarmListener mPeriodicScanTimerListener =
new AlarmManager.OnAlarmListener() {
public void onAlarm() {
periodicScanTimerHandler();
}
};
当时间到时,调用periodicScanTimerHandler
方法,再看periodicScanTimerHandler
方法。
private void periodicScanTimerHandler() {
localLog("periodicScanTimerHandler");
// Schedule the next timer and start a single scan if screen is on.
if (mScreenOn) {
startPeriodicSingleScan();
}
}
这里判断了当屏幕亮的时候又调用了startPeriodicSingleScan
方法,到这里又回到了之前的流程循环了,实现了周期性扫描。
3、PNO扫描
PNO扫描其实是需要软硬件结合的,是安卓为了功耗问题提供的一种更节能的后台扫描模式。主要是在没有连接wifi的情况下,并且屏幕是息屏状态,然后只去扫描已保存的热点配置。这样保证了设备在不开屏使用的情况下有最好的节能效果。PNO扫描的流程相对简单了,同样在startConnectivityScan
方法中触发,如果这时候屏幕是处于熄灭状态并且wifi没有连接,就会调用进入startDisconnectedPnoScan
方法。
private void startDisconnectedPnoScan() {
// Initialize PNO settings
PnoSettings pnoSettings = new PnoSettings();
/*省略部分代码*/
mScanner.startDisconnectedPnoScan(
scanSettings, pnoSettings, new HandlerExecutor(mEventHandler), mPnoScanListener);
mPnoScanStarted = true;
mWifiMetrics.logPnoScanStart();
}
调用了WifiScanner.startDisconnectedPnoScan
.
public void startDisconnectedPnoScan(ScanSettings scanSettings, PnoSettings pnoSettings,
@NonNull @CallbackExecutor Executor executor, PnoScanListener listener) {
/*省略部分代码*/
pnoSettings.isConnected = false;
startPnoScan(scanSettings, pnoSettings, key);
}
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);
pnoParams.putParcelable(PNO_PARAMS_PNO_SETTINGS_KEY, pnoSettings);
mAsyncChannel.sendMessage(CMD_START_PNO_SCAN, 0, key, pnoParams);
}
最后给WifiScanningServiceImpl
发送了CMD_START_PNO_SCAN
消息,这里WifiScanningServiceImpl
直接将消息转发给了WifiPnoScanStateMachine
状态机处理,如果硬件支持PNO扫描,则会切换到HwPnoScanState
进行处理。再看HwPnoScanState.processMessage
方法。
public boolean processMessage(Message msg) {
ClientInfo ci = mClients.get(msg.replyTo);
switch (msg.what) {
case WifiScanner.CMD_START_PNO_SCAN:
/*省略部分代码*/
PnoSettings pnoSettings = null;
ScanSettings scanSettings = null;
try {
pnoSettings =
pnoParams.getParcelable(
WifiScanner.PNO_PARAMS_PNO_SETTINGS_KEY);
scanSettings =
pnoParams.getParcelable(
WifiScanner.PNO_PARAMS_SCAN_SETTINGS_KEY);
} catch (BadParcelableException e) {
Log.e(TAG, "Failed to get parcelable params", e);
replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST,
"bad parcel params");
return HANDLED;
}
if (addHwPnoScanRequest(ci, msg.arg2, scanSettings, pnoSettings)) {
replySucceeded(msg);
} else {
replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "bad request");
transitionTo(mStartedState);
}
break;
/*省略部分代码*/
}
return HANDLED;
}
其中调用到了addHwPnoScanRequest
方法。
private boolean addHwPnoScanRequest(ClientInfo ci, int handler, ScanSettings scanSettings,
PnoSettings pnoSettings) {
/*省略部分代码*/
WifiNative.PnoSettings nativePnoSettings =
convertSettingsToPnoNative(scanSettings, pnoSettings);
if (!mScannerImplsTracker.setHwPnoList(nativePnoSettings)) {
return false;
}
/*省略部分代码*/
return true;
}
随后调用了ScannerImplsTracker.setHwPnoList
.
public boolean setHwPnoList(WifiNative.PnoSettings pnoSettings) {
mStatusPerImpl.clear();
boolean anySuccess = false;
for (Map.Entry<String, WifiScannerImpl> entry : mScannerImpls.entrySet()) {
String ifaceName = entry.getKey();
WifiScannerImpl impl = entry.getValue();
boolean success = impl.setHwPnoList(
pnoSettings, new PnoEventHandler(ifaceName));
if (!success) {
Log.e(TAG, "Failed to start pno on " + ifaceName);
continue;
}
mStatusPerImpl.put(ifaceName, STATUS_PENDING);
anySuccess = true;
}
return anySuccess;
}
这里又调用到了WifiScannerImpl
的实现类中,再看实现类WificondScannerImpl.setHwPnoList
.
public boolean setHwPnoList(WifiNative.PnoSettings settings,
WifiNative.PnoEventHandler eventHandler) {
synchronized (mSettingsLock) {
/*省略部分代码*/
mLastPnoScanSettings = new LastPnoScanSettings(
mClock.getElapsedSinceBootMillis(),
settings.networkList, eventHandler);
if (!startHwPnoScan(settings)) {
Log.e(TAG, "Failed to start PNO scan");
reportPnoScanFailure();
}
return true;
}
}
private boolean startHwPnoScan(WifiNative.PnoSettings pnoSettings) {
return mWifiNative.startPnoScan(getIfaceName(), pnoSettings);
}
调用了startHwPnoScan
方法,这个方法直接调用到了WifiNative
中,就不再继续往下分析了,后续和单次扫描大同小异,主要的还是得硬件能够支持这种省电的扫描模式。到这里PNO扫描就分析完了。
总结
安卓wifi框架中扫描功能与wifi开启关闭和连接断开有着较紧密的联系,并且wifi扫描本身也存在着较多的扫描模式,所以交织起来就相对较为复杂。相比起来连接和断开等其他的功能流程相对清晰单一,因此wifi的扫描功能还是需要结合场景来分析,这样思路就会清晰许多。