一 前言
這次接着講Wifi工程流程中的Wifi熱點掃描過程部分的擷取掃描結果的過程,也是Wifi掃描過程的延續,可以先看前面Wifi掃描的分析過程。
Wifi子產品—源碼分析Wifi熱點掃描(Android P)
二 圖示調用流程
這次的調用流程比較簡單就不畫流程圖了,而且流程是按三條不連貫的線路分析的。
三 代碼具體流程
1 底層擷取掃描結果
在上一篇我們分析了Wifi掃描的過程。在底層完成掃描之後會通知架構層,由架構層的WifiMonitor監聽該事件并發送消息SCAN_RESULTS_EVENT,通知WificondScannerImpl進行處理,再通過WifiNative從底層擷取wifi掃描結果,最後再廣播SCAN_RESULTS_AVAILABLE_ACTION通知掃描結果可擷取。
1.1 framework/opt/net/wifi/service/java/com/android/server/wifi/WifiMonitor.java
/**
* Broadcast scan result event to all the handlers registered for this event.
* @param iface Name of iface on which this occurred.
*/
public void broadcastScanResultEvent(String iface) {
sendMessage(iface, SCAN_RESULTS_EVENT);
}
1.2 framework/opt/net/wifi/service/java/com/android/server/wifi/scanner/WificondScannerImpl.java
@Override
public boolean handleMessage(Message msg) {
switch(msg.what) {
case WifiMonitor.SCAN_FAILED_EVENT:
Log.w(TAG, "Scan failed");
cancelScanTimeout();
reportScanFailure();
break;
case WifiMonitor.PNO_SCAN_RESULTS_EVENT:
pollLatestScanDataForPno();
break;
case WifiMonitor.SCAN_RESULTS_EVENT:
cancelScanTimeout();
pollLatestScanData();
break;
default:
// ignore unknown event
}
return true;
}
繼續看 pollLatestScanData。
private void pollLatestScanData() {
synchronized (mSettingsLock) {
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;
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++;
}
}
...
}
}
通過mWifiNative.getScanResults來從底層擷取wifi熱點掃描結果。
1.3 framework/opt/net/wifi/service/java/com/android/server/wifi/WifiNative.java
/**
* Fetch the latest scan result from kernel via wificond.
* @param ifaceName Name of the interface.
* @return Returns an ArrayList of ScanDetail.
* Returns an empty ArrayList on failure.
*/
public ArrayList<ScanDetail> getScanResults(@NonNull String ifaceName) {
return mWificondControl.getScanResults(
ifaceName, WificondControl.SCAN_TYPE_SINGLE_SCAN);
}
接着看mWificondControl.getScanResults。
1.4 framework/opt/net/wifi/service/java/com/android/server/wifi/WifiNative.java
/**
* Fetch the latest scan result from kernel via wificond.
* @param ifaceName Name of the interface.
* @return Returns an ArrayList of ScanDetail.
* Returns an empty ArrayList on failure.
*/
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();
}
...
}
...
return results;
}
看scannerImpl.getScanResults。
1.5 system/connectivity/wificond/aidl/android/net/wifi/IWifiScannerImpl.aidl
接下來就會調到C++層,暫時就不往下分析了。
2 通知掃描結果可擷取
接下來廣播SCAN_RESULTS_AVAILABLE_ACTION通知掃描結果可擷取。
2.1 frameworks/base/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
在這裡會接收到掃描結果可擷取的廣播WifiManager.SCAN_RESULTS_AVAILABLE_ACTION。
/**
* Receiver for handling broadcasts.
*
* This receiver is registered on the WorkHandler.
*/
@VisibleForTesting
final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) {
updateWifiState(
intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
WifiManager.WIFI_STATE_UNKNOWN));
} else if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(action)) {
mStaleScanResults = false;
fetchScansAndConfigsAndUpdateAccessPoints();
} else if (WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION.equals(action)
|| WifiManager.LINK_CONFIGURATION_CHANGED_ACTION.equals(action)) {
fetchScansAndConfigsAndUpdateAccessPoints();
} else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) {
// TODO(sghuman): Refactor these methods so they cannot result in duplicate
// onAccessPointsChanged updates being called from this intent.
NetworkInfo info = intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
updateNetworkInfo(info);
fetchScansAndConfigsAndUpdateAccessPoints();
} else if (WifiManager.RSSI_CHANGED_ACTION.equals(action)) {
NetworkInfo info =
mConnectivityManager.getNetworkInfo(mWifiManager.getCurrentNetwork());
updateNetworkInfo(info);
}
}
};
WifiTracker接收到這個廣播後,執行fetchScansAndConfigsAndUpdateAccessPoints。
/**
* Retrieves latest scan results and wifi configs, then calls
* {@link #updateAccessPoints(List, List)}.
*/
private void fetchScansAndConfigsAndUpdateAccessPoints() {
final List<ScanResult> newScanResults = mWifiManager.getScanResults();
if (isVerboseLoggingEnabled()) {
Log.i(TAG, "Fetched scan results: " + newScanResults);
}
List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks();
updateAccessPoints(newScanResults, configs);
}
走到mWifiManager.getScanResults。
2.2 framework/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();
}
}
又是通過aidl進行跨程序調用mService.getScanResults。
2.3 framework/opt/net/wifi/service/java/com/android/server/wifi/WifiServiceImpl.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
*/
@Override
public List<ScanResult> getScanResults(String callingPackage) {
enforceAccessPermission();
int uid = Binder.getCallingUid();
long ident = Binder.clearCallingIdentity();
if (mVerboseLoggingEnabled) {
mLog.info("getScanResults uid=%").c(uid).flush();
}
try {
mWifiPermissionsUtil.enforceCanAccessScanResults(callingPackage, uid);
final List<ScanResult> scanResults = new ArrayList<>();
boolean success = mWifiInjector.getWifiStateMachineHandler().runWithScissors(() -> {
scanResults.addAll(mScanRequestProxy.getScanResults());
}, RUN_WITH_SCISSORS_TIMEOUT_MILLIS);
if (!success) {
Log.e(TAG, "Failed to post runnable to fetch scan results");
}
return scanResults;
} catch (SecurityException e) {
return new ArrayList<ScanResult>();
} finally {
Binder.restoreCallingIdentity(ident);
}
}
傳回scanResults。
這裡和android O還是有一些差别的。看mScanRequestProxy.getScanResults。
2.4 framework/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;
}
看看mLastScanResults怎麼來的。
// Common scan listener for scan requests.
private class ScanRequestProxyScanListener implements WifiScanner.ScanListener {
...
@Override
public void onResults(WifiScanner.ScanData[] scanDatas) {
if (mVerboseLoggingEnabled) {
Log.d(TAG, "Scan results received");
}
// For single scans, the array size should always be 1.
if (scanDatas.length != 1) {
Log.wtf(TAG, "Found more than 1 batch of scan results, Failing...");
sendScanResultBroadcastIfScanProcessingNotComplete(false);
return;
}
WifiScanner.ScanData scanData = scanDatas[0];
ScanResult[] scanResults = scanData.getResults();
if (mVerboseLoggingEnabled) {
Log.d(TAG, "Received " + scanResults.length + " scan results");
}
// Store the last scan results & send out the scan completion broadcast.
mLastScanResults.clear();
mLastScanResults.addAll(Arrays.asList(scanResults));
sendScanResultBroadcastIfScanProcessingNotComplete(true);
}
...
};
将scanResults放進LastScanResults,看scanData.getResults。WifiScanner的服務端是WifiScanningServiceImpl。
2.5 framework/base/wifi/java/android/net/wifi//WifiScanner.java
/**
* all the information garnered from a single scan
*/
public static class ScanData implements Parcelable {
...
public ScanResult[] getResults() {
return mResults;
}
...
}
傳回mResults。
3 Settings應用層擷取掃描結果
應用層
3.1 packages/apps/Settings/src/com/android/settings/wifi/WifiSettings.java
在生命周期函數裡監聽Wifi的狀态。
@Override
public void onStart() {
super.onStart();
// On/off switch is hidden for Setup Wizard (returns null)
mWifiEnabler = createWifiEnabler();
if (mIsRestricted) {
restrictUi();
return;
}
onWifiStateChanged(mWifiManager.getWifiState());
}
當wifi狀态改變時會有相應的操作。
/** Called when the state of Wifi has changed. */
@Override
public void onWifiStateChanged(int state) {
if (mIsRestricted) {
return;
}
final int wifiState = mWifiManager.getWifiState();
switch (wifiState) {
case WifiManager.WIFI_STATE_ENABLED:
updateAccessPointPreferences();
break;
case WifiManager.WIFI_STATE_ENABLING:
removeConnectedAccessPointPreference();
mAccessPointsPreferenceCategory.removeAll();
addMessagePreference(R.string.wifi_starting);
setProgressBarVisible(true);
break;
case WifiManager.WIFI_STATE_DISABLING:
removeConnectedAccessPointPreference();
mAccessPointsPreferenceCategory.removeAll();
addMessagePreference(R.string.wifi_stopping);
break;
case WifiManager.WIFI_STATE_DISABLED:
setOffMessage();
setAdditionalSettingsSummaries();
setProgressBarVisible(false);
break;
}
}
監聽到wifi狀态已經開啟,看updateAccessPointPreferences。
private void updateAccessPointPreferences() {
// in case state has changed
if (!mWifiManager.isWifiEnabled()) {
return;
}
// AccessPoints are sorted by the WifiTracker
final List<AccessPoint> accessPoints = mWifiTracker.getAccessPoints();
if (isVerboseLoggingEnabled()) {
Log.i(TAG, "updateAccessPoints called for: " + accessPoints);
}
...
}
看mWifiTracker.getAccessPoints。
java架構層
3.2 frameworks/base/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
/**
* Gets the current list of access points.
*
* <p>This method is can be called on an abitrary thread by clients, but is normally called on
* the UI Thread by the rendering App.
*/
@AnyThread
public List<AccessPoint> getAccessPoints() {
synchronized (mLock) {
return new ArrayList<>(mInternalAccessPoints);
}
}
接下來的過程最終還是會走到WifiManager.getScanResults,WifiServiceImpl.getScanResults,細節上會有差異。