请求WLAN芯片开始扫描,然后获取扫描结果,整个过程是分两个阶段:
前者在上一篇中已经梳理过了,并且已经知晓,扫描完成后,App可以通过广播``,因此本篇会来梳理一下获取扫描结果的一个流程;
//frameworks/base/wifi/java/android/net/wifi/WifiManager.java
/**
* Return the results of the latest access point scan.
* @return the list of access points found in the most recent scan. An app must hold
* {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION} or
* {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION} permission
* in order to get valid results. If there is a remote exception (e.g., either a communication
* problem with the system service or an exception within the framework) an empty list will be
* returned.
*/
public List<ScanResult> getScanResults() {
try {
return mService.getScanResults(mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
注意:
null
;//frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiServiceImpl.java
@Override
public List<ScanResult> getScanResults(String callingPackage) {
...
try {
...
final List<ScanResult> scanResults = new ArrayList<>();
//切换到WifiStateMachine线程执行,并在当前线程等待其返回结果,超时设定默认4s;
boolean success = mWifiInjector.getWifiStateMachineHandler().runWithScissors(() -> {
scanResults.addAll(mScanRequestProxy.getScanResults());
}, RUN_WITH_SCISSORS_TIMEOUT_MILLIS);
...
//如果超时或者其他原因导致没获取到结果,此处返回一个空的ArrayList,而不是null;
return scanResults;
} catch (SecurityException e) {
return new ArrayList<ScanResult>();
} ...
}
抛开线程切换部分不讨论,上面主要的业务逻辑是将ScanRequestProxy.getScanResults()
返回结果添加到scanResults
变量中;
那么来看ScanRequestProxy.getScanResults()
的实现:
//frameworks/opt/net/wifi/service/java/com/android/server/wifi/ScanRequestProxy.java
/**
* Return the results of the most recent access point scan, in the form of
* a list of {@link ScanResult} objects.
* @return the list of results
*/
public List<ScanResult> getScanResults() {
return mLastScanResults;
}
可见这里是直接从缓存数据中获取并返回,因此同步调用的逻辑到此就结束了;
接下来,我们要从ScanRequestProxy
这里开始,调查这个mLastScanResults
是如何来的;
private class ScanRequestProxyScanListener implements WifiScanner.ScanListener {
...
@Override
public void onResults(WifiScanner.ScanData[] scanDatas) {
...
WifiScanner.ScanData scanData = scanDatas[0];
ScanResult[] scanResults = scanData.getResults();
...
//将数据保存,并发送广播,提醒APP可以通过WifiManager.getScanResults()获取到新数据了;
mLastScanResults.clear();
mLastScanResults.addAll(Arrays.asList(scanResults));
sendScanResultBroadcastIfScanProcessingNotComplete(true);
}
...
};
这个ScanRequestProxyScanListener
内部类的实例对象,是在startScan()
是,构造并传递给WifiScanner
的:
...
public boolean startScan(int callingUid, String packageName) {
...
mWifiScanner.startScan(settings, new ScanRequestProxyScanListener(), workSource);
...
}
//frameworks/base/wifi/java/android/net/wifi/WifiScanner.java
private static final int INVALID_KEY = 0;
private int mListenerKey = 1;
private final SparseArray mListenerMap = new SparseArray();
private final Object mListenerMapLock = new Object();
...
public void startScan(ScanSettings settings, ScanListener listener, WorkSource workSource) {
...
int key = addListener(listener);
if (key == INVALID_KEY) return;
...
}
...
private int addListener(ActionListener listener) {
synchronized (mListenerMapLock) {
boolean keyExists = (getListenerKey(listener) != INVALID_KEY);
//无论是否存在重复listener,先添加
int key = putListener(listener);
if (keyExists) {
//如果存在,通知mInternalHandler执行removeListener逻辑;
OperationResult operationResult = new OperationResult(REASON_DUPLICATE_REQEUST,
"Outstanding request with same key not stopped yet");
Message message = Message.obtain(mInternalHandler, CMD_OP_FAILED, 0, key,
operationResult);
message.sendToTarget();
return INVALID_KEY;
} else {
return key;
}
}
}
private int putListener(Object listener) {
if (listener == null) return INVALID_KEY;
int key;
synchronized (mListenerMapLock) {
do {
key = mListenerKey++;
} while (key == INVALID_KEY);
mListenerMap.put(key, listener);
}
return key;
}
...
上面其实就是一个结论:所有扫描请求传递过来的ScanListener
均添加在WifiScanner
内维护的SparseArray mListenerMap
这个Map中;
鉴于成员变量mListenerMap
为私有变量,且并未提供外部获取接口,因此我们可以认为,其触发onResults
也应该是在WifiScanner
内完成的;
比较简单,整个类中就只有ServiceHandler.handleMessage()
中调用了ScanListener.onResults()
,而成员变量mInternalHandler
也就是ServiceHandler
的实例对象:
private final Handler mInternalHandler;
public WifiScanner(Context context, IWifiScanner service, Looper looper) {
...
mInternalHandler = new ServiceHandler(looper);
...
}
...
private class ServiceHandler extends Handler {
ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
...
Object listener = getListener(msg.arg2);
if (listener == null) {
if (DBG) Log.d(TAG, "invalid listener key = " + msg.arg2);
return;
} else {
if (DBG) Log.d(TAG, "listener key = " + msg.arg2);
}
switch (msg.what) {
//这就是上面提到的针对重复Listener的removeListener逻辑:
case CMD_OP_FAILED : {
OperationResult result = (OperationResult)msg.obj;
((ActionListener) listener).onFailure(result.reason, result.description);
removeListener(msg.arg2);
}
break;
//这就是通知到ScanRequestProxyScanListener的逻辑:
case CMD_SCAN_RESULT :
((ScanListener) listener).onResults(
((ParcelableScanData) msg.obj).getResults());
return;
...
}
}
因为WifiScanner
与其IWifiScanner
的接口实现类之间采用AsyncChannel
完成通信,那么我们仅需在对端搜索发送CMD_SCAN_RESULT
消息的逻辑,即可继续追踪:
//frameworks/opt/net/wifi/service/java/com/android/server/wifi/scanner/WifiScanningServiceImpl.java
...
void reportScanResults(ScanData results) {
...
ScanData[] allResults = new ScanData[] {results};
//当前扫描伴随而来的监听,会在此处分发回调
for (RequestInfo<ScanSettings> entry : mActiveScans) {
//根据请求本身的参数,过滤掉他们不该获取到的扫描结果
//因为扫描请求是有做合并的,因此此处需要判断一下:
//例如:某个扫描请求未指定扫描隐藏网络的参数,而此次扫描结果中包含隐藏网络,就应该在此处过滤掉
ScanData[] resultsToDeliver = ScanScheduleUtil.filterResultsForSettings(
mChannelHelper, allResults, entry.settings, -1);
WifiScanner.ParcelableScanData parcelableResultsToDeliver =
new WifiScanner.ParcelableScanData(resultsToDeliver);
logCallback("singleScanResults", entry.clientInfo, entry.handlerId,
describeForLog(resultsToDeliver));
entry.reportEvent(WifiScanner.CMD_SCAN_RESULT, 0, parcelableResultsToDeliver);
// make sure the handler is removed
entry.reportEvent(WifiScanner.CMD_SINGLE_SCAN_COMPLETED, 0, null);
}
WifiScanner.ParcelableScanData parcelableAllResults =
new WifiScanner.ParcelableScanData(allResults);
//通过WifiScanner.registerScanListener()注册的监听,会在此处分发回调
for (RequestInfo<Void> entry : mSingleScanListeners) {
logCallback("singleScanResults", entry.clientInfo, entry.handlerId,
describeForLog(allResults));
entry.reportEvent(WifiScanner.CMD_SCAN_RESULT, 0, parcelableAllResults);
}
if (results.isAllChannelsScanned()) {
mCachedScanResults.clear();
mCachedScanResults.addAll(Arrays.asList(results.getResults()));
}
}
...
而调用reportScanResults
的调用在WifiSingleScanStateMachine
与WifiPnoScanStateMachine
两个状态机中;
由于我们到目前为止一直讨论的是前者,因此以WifiSingleScanStateMachine
继续分析:
//frameworks/opt/net/wifi/service/java/com/android/server/wifi/scanner/WifiScanningServiceImpl.java
...
class ScanningState extends State {
...
@Override
public boolean processMessage(Message msg) {
switch (msg.what) {
case CMD_SCAN_RESULTS_AVAILABLE:
mWifiMetrics.incrementScanReturnEntry(
WifiMetricsProto.WifiLog.SCAN_SUCCESS,
mActiveScans.size());
reportScanResults(mScannerImpl.getLatestSingleScanResults());
mActiveScans.clear();
transitionTo(mIdleState);
return HANDLED;
...
}
}
}
...
从这里,我们需要将代码分两个方向梳理:
发送CMD_SCAN_RESULTS_AVAILABLE
消息的逻辑;
class WifiSingleScanStateMachine extends StateMachine implements WifiNative.ScanEventHandler {
...
@Override
public void onScanStatus(int event) {
if (DBG) localLog("onScanStatus event received, event=" + event);
switch(event) {
case WifiNative.WIFI_SCAN_RESULTS_AVAILABLE:
case WifiNative.WIFI_SCAN_THRESHOLD_NUM_SCANS:
case WifiNative.WIFI_SCAN_THRESHOLD_PERCENT:
sendMessage(CMD_SCAN_RESULTS_AVAILABLE);
break;
...
}
}
...
}
由于当前设备不支持后台扫描(Background Scan)因此会回调onScanStatus(WIFI_SCAN_RESULTS_AVAILABLE)
的逻辑来自WificondScannerImpl.pollLatestScanData()
,这里我们先在此打住,因为后面那条分析方向,也会走到这里来;
调用mScannerImpl.getLatestSingleScanResults()
的逻辑;
成员变量mScannerImpl
的类型为WifiScannerImpl
,由上一篇我们知道,WifiScannerImpl
的实现类是按照工厂设计模式来选择的,当前手边设备普遍构造的是WificondScannerImpl
,因此我们查看其内部getLatestSingleScanResults()
的实现:
//frameworks/opt/net/wifi/service/java/com/android/server/wifi/scanner/WificondScannerImpl.java
@Override
public WifiScanner.ScanData getLatestSingleScanResults() {
return mLatestSingleScanResult;
}
再一次,直接返回的缓存数据,那么我们以此为基础,再来看看成员变量mLatestSingleScanResult
的数据填充逻辑:
private void pollLatestScanData() {
synchronized (mSettingsLock) {
//上一节我们提到过,startSingleScan时,如果该成员变量为null,会构造该成员变量,避免重复请求
if (mLastScanSettings == null) {
// got a scan before we started scanning or after scan was canceled
return;
}
//关键调用
mNativeScanResults = mWifiNative.getScanResults(mIfaceName);
List<ScanResult> singleScanResults = new ArrayList<>();
int numFilteredScanResults = 0;
//将Native返回数据进行重新封装、过滤;
for (int i = 0; i < mNativeScanResults.size(); ++i) {
ScanResult result = mNativeScanResults.get(i).getScanResult();
long timestamp_ms = result.timestamp / 1000; // convert us -> ms
//时间戳过滤:必须是上次请求之后的扫描结果,且信道在扫描请求的参数覆盖之内
if (timestamp_ms > mLastScanSettings.startTime) {
if (mLastScanSettings.singleScanFreqs.containsChannel(
result.frequency)) {
singleScanResults.add(result);
}
} else {
numFilteredScanResults++;
}
}
if (numFilteredScanResults != 0) {
Log.d(TAG, "Filtering out " + numFilteredScanResults + " scan results.");
}
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的数据
mLatestSingleScanResult = new WifiScanner.ScanData(0, 0, 0,
isAllChannelsScanned(mLastScanSettings.singleScanFreqs),
singleScanResults.toArray(new ScanResult[singleScanResults.size()]));
mLastScanSettings.singleScanEventHandler
.onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE);
}
mLastScanSettings = null;
}
}
同样,我们分两个方向梳理:
调用pollLatestScanData
的逻辑;
@Override
public boolean handleMessage(Message msg) {
switch(msg.what) {
...
case WifiMonitor.SCAN_RESULTS_EVENT:
cancelScanTimeout();
pollLatestScanData();
break;
...
}
return true;
}
发送WifiMonitor.SCAN_RESULTS_EVENT
消息的逻辑来自WifiMonitor
//frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiMonitor.java
public void broadcastScanResultEvent(String iface) {
sendMessage(iface, SCAN_RESULTS_EVENT);
}
调用broadcastScanResultEvent
则来自WificondControl
//frameworks/opt/net/wifi/service/java/com/android/server/wifi/WificondControl.java
private class ScanEventHandler extends IScanEvent.Stub {
private String mIfaceName;
ScanEventHandler(@NonNull 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);
}
}
关于ScanEventHandler
的实例对象,会在setupInterfaceForClientMode
时被构造,并通过HIDL接口注册到HAL层:
public IClientInterface setupInterfaceForClientMode(@NonNull String ifaceName) {
...
IClientInterface clientInterface = null;
try {
clientInterface = mWificond.createClientInterface(ifaceName);
} catch (RemoteException e1) {
Log.e(TAG, "Failed to get IClientInterface due to remote exception");
return null;
}
...
// Refresh Handlers
mClientInterfaces.put(ifaceName, clientInterface);
try {
IWifiScannerImpl wificondScanner = clientInterface.getWifiScannerImpl();
if (wificondScanner == null) {
Log.e(TAG, "Failed to get WificondScannerImpl");
return null;
}
mWificondScanners.put(ifaceName, wificondScanner);
Binder.allowBlocking(wificondScanner.asBinder());
ScanEventHandler scanEventHandler = new ScanEventHandler(ifaceName);
mScanEventHandlers.put(ifaceName, scanEventHandler);
wificondScanner.subscribeScanEvents(scanEventHandler);
...
} catch (RemoteException e) {
Log.e(TAG, "Failed to refresh wificond scanner due to remote exception");
}
return clientInterface;
}
setupInterfaceForClientMode
会在打开WLAN时调用,这里就不赘述了;
而IWifiScannerImpl.subscribeScanEvents()
及其回调ScanEventHandler.OnScanResultReady
也是在wificond
进程内实现并完成的,这里也暂不讨论;
综上,这条逻辑链路已经梳理完毕,我们回到pollLatestScanData
方法,看看向下调用的逻辑:
pollLatestScanData
方法内部核心业务逻辑WifiNative.getScanResults()
的实现;
//frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiNative.java
public ArrayList<ScanDetail> getScanResults(@NonNull String ifaceName) {
return mWificondControl.getScanResults(
ifaceName, WificondControl.SCAN_TYPE_SINGLE_SCAN);
}
//frameworks/opt/net/wifi/service/java/com/android/server/wifi/WificondControl.java
public ArrayList<ScanDetail> getScanResults(@NonNull String ifaceName, int scanType) {
ArrayList<ScanDetail> results = new ArrayList<>();
IWifiScannerImpl scannerImpl = getScannerImpl(ifaceName);
if (scannerImpl == null) {
Log.e(TAG, "No valid wificond scanner interface handler");
return results;
}
try {
NativeScanResult[] nativeResults;
if (scanType == SCAN_TYPE_SINGLE_SCAN) {
nativeResults = scannerImpl.getScanResults();
} else {
nativeResults = scannerImpl.getPnoScanResults();
}
for (NativeScanResult result : nativeResults) {
WifiSsid wifiSsid = WifiSsid.createFromByteArray(result.ssid);
String bssid;
try {
bssid = NativeUtil.macAddressFromByteArray(result.bssid);
} catch (IllegalArgumentException e) {
Log.e(TAG, "Illegal argument " + result.bssid, e);
continue;
}
if (bssid == null) {
Log.e(TAG, "Illegal null bssid");
continue;
}
ScanResult.InformationElement[] ies =
InformationElementUtil.parseInformationElements(result.infoElement);
InformationElementUtil.Capabilities capabilities =
new InformationElementUtil.Capabilities();
capabilities.from(ies, result.capability);
String flags = capabilities.generateCapabilitiesString();
NetworkDetail networkDetail;
try {
networkDetail = new NetworkDetail(bssid, ies, null, result.frequency);
} catch (IllegalArgumentException e) {
Log.e(TAG, "Illegal argument for scan result with bssid: " + bssid, e);
continue;
}
ScanDetail scanDetail = new ScanDetail(networkDetail, wifiSsid, bssid, flags,
result.signalMbm / 100, result.frequency, result.tsf, ies, null);
ScanResult scanResult = scanDetail.getScanResult();
// Update carrier network info if this AP's SSID is associated with a carrier Wi-Fi
// network and it uses EAP.
if (ScanResultUtil.isScanResultForEapNetwork(scanDetail.getScanResult())
&& mCarrierNetworkConfig.isCarrierNetwork(wifiSsid.toString())) {
scanResult.isCarrierAp = true;
scanResult.carrierApEapType =
mCarrierNetworkConfig.getNetworkEapType(wifiSsid.toString());
scanResult.carrierName =
mCarrierNetworkConfig.getCarrierName(wifiSsid.toString());
}
// Fill up the radio chain info.
if (result.radioChainInfos != null) {
scanResult.radioChainInfos =
new ScanResult.RadioChainInfo[result.radioChainInfos.size()];
int idx = 0;
for (RadioChainInfo nativeRadioChainInfo : result.radioChainInfos) {
scanResult.radioChainInfos[idx] = new ScanResult.RadioChainInfo();
scanResult.radioChainInfos[idx].id = nativeRadioChainInfo.chainId;
scanResult.radioChainInfos[idx].level = nativeRadioChainInfo.level;
idx++;
}
}
results.add(scanDetail);
}
} catch (RemoteException e1) {
Log.e(TAG, "Failed to create ScanDetail ArrayList");
}
if (mVerboseLoggingEnabled) {
Log.d(TAG, "get " + results.size() + " scan results from wificond");
}
return results;
}
由上一篇我们知道,IWifiScannerImpl
的实现类,在wificond
进程内,已经超出了Framework的范畴,故在此打住,后续有需求,再从wificond
触发来梳理;
截至目前的逻辑链路已经能满足日常工作需要的知识储备;
简单总结一下:
wificond
会通过AIDL接口ScanEventHandler.OnScanResultReady
回调给到WifiCondControl
,并通知其调用IWifiScannerImpl.getScanResult()
获取扫描结果,将其保存到WificondScannerImpl.mLatestSingleScanResult
;wificond
获取的;ScanRequestProxy
内也会维护一份扫描结果mLastScanResults
;ScanRequestProxy.mLastScanResults
相对而言是作为前端缓存,直接对接WifiServiceImpl
的,用于直接返回,缓存的目的是快速返回;WificondScannerImpl.mLatestSingleScanResult
则更像一个后端缓存,用来缓存上一次从wificond
获取到的扫描结果,其缓存的目的也不太一样:
最后老规矩,附上流程图一份: