版权声明:本文为博主原创文章,未经博主允许不得转载。https://blog.csdn.net/huangweiqing80/article/details/82458874
1. wifi扫描结果简介
WiFi的扫描结果会在WiFi扫描后呈现在设置WiFi界面上的,每个扫描到的AP是以Preference组件的方式呈现的设置WiFi界面上,点击即可触发连接操作。
2. 流程分析
2.1前面提到mWifiTracker.startTracking();会去扫描,那么onWifiStateChanged(mWifiManager.getWifiState())就是在wifi状态改变的时候去获取扫描结果
/packages/apps/Settings/src/com/android/settings/wifi/WifiSettings.java
@Override
public void onStart() {
super.onStart();
// On/off switch is hidden for Setup Wizard (returns null)
mWifiEnabler = createWifiEnabler();
mWifiTracker.startTracking();
if (mIsRestricted) {
restrictUi();
return;
}
onWifiStateChanged(mWifiManager.getWifiState());
}
2.2 下面来看一下onWifiStateChanged源码
/** 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:
conditionallyForceUpdateAPs();
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;
}
}
2.3这里wifi已经打开了,所以wifiState对应的是WifiManager.WIFI_STATE_ENABLED,所以走的应该是conditionallyForceUpdateAPs();这句
/**
* Only update the AP list if there are not any APs currently shown.
*
* <p>Thus forceUpdate will only be called during cold start or when toggling between wifi on
* and off. In other use cases, the previous APs will remain until the next update is received
* from {@link WifiTracker}.
*/
private void conditionallyForceUpdateAPs() {
if (mAccessPointsPreferenceCategory.getPreferenceCount() > 0
&& mAccessPointsPreferenceCategory.getPreference(0) instanceof
AccessPointPreference) {
// Make sure we don't update due to callbacks initiated by sticky broadcasts in
// WifiTracker.
Log.d(TAG, "Did not force update APs due to existing APs displayed");
getView().removeCallbacks(mUpdateAccessPointsRunnable);
return;
}
setProgressBarVisible(true);
mWifiTracker.forceUpdate();
if (WifiTracker.sVerboseLogging) {
Log.i(TAG, "WifiSettings force update APs: " + mWifiTracker.getAccessPoints());
}
getView().removeCallbacks(mUpdateAccessPointsRunnable);
updateAccessPointPreferences();
}
这里关键的是
mWifiTracker.forceUpdate(); //获取扫描结果
updateAccessPointPreferences(); //更新扫描结果组件,即将扫描结果显示到WIFI Setting界面
下面分别来看一下这两行代码
2.3.1先来看一下mWifiTracker.forceUpdate();
frameworks/base/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
/** Synchronously update the list of access points with the latest information. */
@MainThread
public void forceUpdate() {
synchronized (mLock) {
mWorkHandler.removeMessages(WorkHandler.MSG_UPDATE_ACCESS_POINTS);
mLastInfo = mWifiManager.getConnectionInfo();
mLastNetworkInfo = mConnectivityManager.getNetworkInfo(mWifiManager.getCurrentNetwork());
final List<ScanResult> newScanResults = mWifiManager.getScanResults();
if (sVerboseLogging) {
Log.i(TAG, "Fetched scan results: " + newScanResults);
}
List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks();
mInternalAccessPoints.clear();
updateAccessPointsLocked(newScanResults, configs);
// Synchronously copy access points
mMainHandler.removeMessages(MainHandler.MSG_ACCESS_POINT_CHANGED);
mMainHandler.handleMessage(
Message.obtain(mMainHandler, MainHandler.MSG_ACCESS_POINT_CHANGED));
if (sVerboseLogging) {
Log.i(TAG, "force update - external access point list:\n" + mAccessPoints);
}
}
}
这个方法调用了WifiManager的getScanResults 和 getConfiguredNetworks。这两个函数分别是获取扫描结果和网络配置。
updateAccessPointsLocked(newScanResults, configs);这句是将扫描结果和网络配置放到哪里呢?
/**
* Update the internal list of access points.
*
* <p>Do not called directly (except for forceUpdate), use {@link #updateAccessPoints()} which
* respects {@link #mStaleScanResults}.
*/
@GuardedBy("mLock")
private void updateAccessPointsLocked(final List<ScanResult> newScanResults,
List<WifiConfiguration> configs) {
WifiConfiguration connectionConfig = null;
if (mLastInfo != null) {
connectionConfig = getWifiConfigurationForNetworkId(
mLastInfo.getNetworkId(), mWifiManager.getConfiguredNetworks());
}
// Swap the current access points into a cached list.
List<AccessPoint> cachedAccessPoints = new ArrayList<>(mInternalAccessPoints);
ArrayList<AccessPoint> accessPoints = new ArrayList<>();
// Clear out the configs so we don't think something is saved when it isn't.
for (AccessPoint accessPoint : cachedAccessPoints) {
accessPoint.clearConfig();
}
/* Lookup table to more quickly update AccessPoints by only considering objects with the
* correct SSID. Maps SSID -> List of AccessPoints with the given SSID. */
Multimap<String, AccessPoint> apMap = new Multimap<String, AccessPoint>();
final Collection<ScanResult> results = updateScanResultCache(newScanResults);
if (configs != null) {
for (WifiConfiguration config : configs) {
if (config.selfAdded && config.numAssociation == 0) {
continue;
}
AccessPoint accessPoint = getCachedOrCreate(config, cachedAccessPoints);
if (mLastInfo != null && mLastNetworkInfo != null) {
accessPoint.update(connectionConfig, mLastInfo, mLastNetworkInfo);
}
if (mIncludeSaved) {
// If saved network not present in scan result then set its Rssi to
// UNREACHABLE_RSSI
boolean apFound = false;
for (ScanResult result : results) {
if (result.SSID.equals(accessPoint.getSsidStr())) {
apFound = true;
break;
}
}
if (!apFound) {
accessPoint.setUnreachable();
}
accessPoints.add(accessPoint);
apMap.put(accessPoint.getSsidStr(), accessPoint);
} else {
// If we aren't using saved networks, drop them into the cache so that
// we have access to their saved info.
cachedAccessPoints.add(accessPoint);
}
}
}
final List<NetworkKey> scoresToRequest = new ArrayList<>();
if (results != null) {
for (ScanResult result : results) {
// Ignore hidden and ad-hoc networks.
if (result.SSID == null || result.SSID.length() == 0 ||
result.capabilities.contains("[IBSS]")) {
continue;
}
NetworkKey key = NetworkKey.createFromScanResult(result);
if (key != null && !mRequestedScores.contains(key)) {
scoresToRequest.add(key);
}
boolean found = false;
for (AccessPoint accessPoint : apMap.getAll(result.SSID)) {
// We want to evict old scan results if are current results are not stale
if (accessPoint.update(result, !mStaleScanResults)) {
found = true;
break;
}
}
if (!found && mIncludeScans) {
AccessPoint accessPoint = getCachedOrCreate(result, cachedAccessPoints);
if (mLastInfo != null && mLastNetworkInfo != null) {
accessPoint.update(connectionConfig, mLastInfo, mLastNetworkInfo);
}
if (result.isPasspointNetwork()) {
// Retrieve a WifiConfiguration for a Passpoint provider that matches
// the given ScanResult. This is used for showing that a given AP
// (ScanResult) is available via a Passpoint provider (provider friendly
// name).
try {
WifiConfiguration config = mWifiManager.getMatchingWifiConfig(result);
if (config != null) {
accessPoint.update(config);
}
} catch (UnsupportedOperationException e) {
// Passpoint not supported on the device.
}
}
accessPoints.add(accessPoint);
apMap.put(accessPoint.getSsidStr(), accessPoint);
}
}
}
requestScoresForNetworkKeys(scoresToRequest);
for (AccessPoint ap : accessPoints) {
ap.update(mScoreCache, mNetworkScoringUiEnabled, mMaxSpeedLabelScoreCacheAge);
}
// Pre-sort accessPoints to speed preference insertion
Collections.sort(accessPoints);
// Log accesspoints that were deleted
if (DBG()) {
Log.d(TAG, "------ Dumping SSIDs that were not seen on this scan ------");
for (AccessPoint prevAccessPoint : mInternalAccessPoints) {
if (prevAccessPoint.getSsid() == null)
continue;
String prevSsid = prevAccessPoint.getSsidStr();
boolean found = false;
for (AccessPoint newAccessPoint : accessPoints) {
if (newAccessPoint.getSsidStr() != null && newAccessPoint.getSsidStr()
.equals(prevSsid)) {
found = true;
break;
}
}
if (!found)
Log.d(TAG, "Did not find " + prevSsid + " in this scan");
}
Log.d(TAG, "---- Done dumping SSIDs that were not seen on this scan ----");
}
mInternalAccessPoints.clear();
mInternalAccessPoints.addAll(accessPoints);
mMainHandler.sendEmptyMessage(MainHandler.MSG_ACCESS_POINT_CHANGED);
}
代码有点多,但是我们只看重点的下面四行代码,简单来看就是分两大块WifiConfiguration 和 ScanResult进行accessPoints的添加,并将accessPoints添加到mInternalAccessPoints。更新完了发送一个MSG_ACCESS_POINT_CHANGED消息
AccessPoint accessPoint = getCachedOrCreate(config, cachedAccessPoints);
AccessPoint accessPoint = getCachedOrCreate(result, cachedAccessPoints);
mInternalAccessPoints.addAll(accessPoints);
mMainHandler.sendEmptyMessage(MainHandler.MSG_ACCESS_POINT_CHANGED);
这样我们便将扫描结果添加到mInternalAccessPoints中了,并且发送一个MSG_ACCESS_POINT_CHANGED消息,调用了copyAndNotifyListeners方法。(PS:如注释所述这个方法不建议直接调用,建议调用updateAccessPoints())
case MSG_ACCESS_POINT_CHANGED:
// Only notify listeners of changes if we have fresh scan results, otherwise the
// UI will be updated with stale results. We want to copy the APs regardless,
// for instances where forceUpdate was invoked by the caller.
if (mStaleScanResults) {
copyAndNotifyListeners(false /*notifyListeners*/);
} else {
copyAndNotifyListeners(true /*notifyListeners*/);
mListener.onAccessPointsChanged();
}
break;
下面来看看copyAndNotifyListeners这个方法里面做了什么
/**
* Responsible for copying access points from {@link #mInternalAccessPoints} and notifying
* accesspoint listeners.
*
* @param notifyListeners if true, accesspoint listeners are notified, otherwise notifications
* dropped.
*/
@MainThread
private void copyAndNotifyListeners(boolean notifyListeners) {
// Need to watch out for memory allocations on main thread.
SparseArray<AccessPoint> oldAccessPoints = new SparseArray<>();
SparseIntArray notificationMap = null;
List<AccessPoint> updatedAccessPoints = new ArrayList<>();
for (AccessPoint accessPoint : mAccessPoints) {
oldAccessPoints.put(accessPoint.mId, accessPoint);
}
synchronized (mLock) {
if (DBG()) {
Log.d(TAG, "Starting to copy AP items on the MainHandler. Internal APs: "
+ mInternalAccessPoints);
}
if (notifyListeners) {
notificationMap = mAccessPointListenerAdapter.mPendingNotifications.clone();
}
mAccessPointListenerAdapter.mPendingNotifications.clear();
for (AccessPoint internalAccessPoint : mInternalAccessPoints) {
AccessPoint accessPoint = oldAccessPoints.get(internalAccessPoint.mId);
if (accessPoint == null) {
accessPoint = new AccessPoint(mContext, internalAccessPoint);
} else {
accessPoint.copyFrom(internalAccessPoint);
}
updatedAccessPoints.add(accessPoint);
}
}
mAccessPoints.clear();
mAccessPoints.addAll(updatedAccessPoints);
if (notificationMap != null && notificationMap.size() > 0) {
for (AccessPoint accessPoint : updatedAccessPoints) {
int notificationType = notificationMap.get(accessPoint.mId);
AccessPoint.AccessPointListener listener = accessPoint.mAccessPointListener;
if (notificationType == 0 || listener == null) {
continue;
}
if ((notificationType & AccessPointListenerAdapter.AP_CHANGED) != 0) {
listener.onAccessPointChanged(accessPoint);
}
if ((notificationType & AccessPointListenerAdapter.LEVEL_CHANGED) != 0) {
listener.onLevelChanged(accessPoint);
}
}
}
}
这块代码暂只关注扫描结果,大致分为三步
1.将之前的扫描结果存到oldAccessPoints:oldAccessPoints.put(accessPoint.mId, accessPoint);
2.根据mInternalAccessPoints构造新的扫描结果集合updatedAccessPoints
internalAccessPoint : mInternalAccessPoints
AccessPoint accessPoint = oldAccessPoints.get(internalAccessPoint.mId);
updatedAccessPoints.add(accessPoint);
3.将新的扫描结果集合赋给mAccessPoints :mAccessPoints.addAll(updatedAccessPoints);
这就是对mAccessPoints这个集合进行增减操作啊
2.3.2下面我们在回到更新AccessPointsPreference组件的地方updateAccessPointPreferences()
/packages/apps/Settings/src/com/android/settings/wifi/WifiSettings.java
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 (WifiTracker.sVerboseLogging) {
Log.i(TAG, "updateAccessPoints called for: " + accessPoints);
}
boolean hasAvailableAccessPoints = false;
mAccessPointsPreferenceCategory.removePreference(mStatusMessagePreference);
cacheRemoveAllPrefs(mAccessPointsPreferenceCategory);
int index =
configureConnectedAccessPointPreferenceCategory(accessPoints) ? 1 : 0;
int numAccessPoints = accessPoints.size();
for (; index < numAccessPoints; index++) {
AccessPoint accessPoint = accessPoints.get(index);
// Ignore access points that are out of range.
if (accessPoint.isReachable()) {
String key = AccessPointPreference.generatePreferenceKey(accessPoint);
hasAvailableAccessPoints = true;
LongPressAccessPointPreference pref =
(LongPressAccessPointPreference) getCachedPreference(key);
if (pref != null) {
pref.setOrder(index);
continue;
}
LongPressAccessPointPreference preference =
createLongPressActionPointPreference(accessPoint);
preference.setKey(key);
preference.setOrder(index);
if (mOpenSsid != null && mOpenSsid.equals(accessPoint.getSsidStr())
&& accessPoint.getSecurity() != AccessPoint.SECURITY_NONE) {
if (!accessPoint.isSaved() || isDisabledByWrongPassword(accessPoint)) {
onPreferenceTreeClick(preference);
mOpenSsid = null;
}
}
mAccessPointsPreferenceCategory.addPreference(preference);
accessPoint.setListener(WifiSettings.this);
preference.refresh();
}
}
removeCachedPrefs(mAccessPointsPreferenceCategory);
mAddPreference.setOrder(index);
mAccessPointsPreferenceCategory.addPreference(mAddPreference);
setAdditionalSettingsSummaries();
if (!hasAvailableAccessPoints) {
setProgressBarVisible(true);
Preference pref = new Preference(getPrefContext());
pref.setSelectable(false);
pref.setSummary(R.string.wifi_empty_list_wifi_on);
pref.setOrder(index++);
pref.setKey(PREF_KEY_EMPTY_WIFI_LIST);
mAccessPointsPreferenceCategory.addPreference(pref);
} else {
// Continuing showing progress bar for an additional delay to overlap with animation
getView().postDelayed(mHideProgressBarRunnable, 1700 /* delay millis */);
}
}
如上是WifiSettings里更新AP preference的方法,主要调用了WifiTracker的getAccessPoint方法。
SettingsLib
/frameworks/base/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
/**
* Gets the current list of access points. Should be called from main thread, otherwise
* expect inconsistencies
*/
@MainThread
public List<AccessPoint> getAccessPoints() {
return new ArrayList<>(mAccessPoints);
}
返回的这个mAccessPoints不就是我们在2.3.1中的更新的AP列表吗
2.4 framework(getScanResults)
我们回到2.3.1开头讲到的两个重要方法,getScanResults获取扫描结果和getConfiguredNetworks获取网络配置
先简单看下getScanResults方法,getConfiguredNetworks方法类似这里就暂不分析
2.4.1 WifiManager
/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();
}
}
mService已经很熟悉了,对应的服务端是WifiServiceImpl
2.4.2 WifiServiceImpl
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();
try {
if (!mWifiPermissionsUtil.canAccessScanResults(callingPackage,
uid, Build.VERSION_CODES.M)) {
return new ArrayList<ScanResult>();
}
if (mWifiScanner == null) {
mWifiScanner = mWifiInjector.getWifiScanner();
}
return mWifiScanner.getSingleScanResults();
} finally {
Binder.restoreCallingIdentity(ident);
}
}
/**
* Obtain an instance of WifiScanner.
* If it was not already created, then obtain an instance. Note, this must be done lazily since
* WifiScannerService is separate and created later.
*/
public synchronized WifiScanner getWifiScanner() {
if (mWifiScanner == null) {
mWifiScanner = new WifiScanner(mContext,
IWifiScanner.Stub.asInterface(ServiceManager.getService(
Context.WIFI_SCANNING_SERVICE)),
mWifiStateMachineHandlerThread.getLooper());
}
return mWifiScanner;
}
可以看到前面调用了mWifiScanner.getSingleScanResults();另外getWifiScanner这边WifiScanner之前也梳理过了,IWifiScanner参数对应的服务端是WifiScanningServiceImpl了
下面来看看mWifiScanner.getSingleScanResults()
2.4.3 WifiScanner
framework/base/wifi/java/android/net/wifi/WifiScanner.java
/**
* Retrieve the most recent scan results from a single scan request.
* {@hide}
*/
public List<ScanResult> getSingleScanResults() {
validateChannel();
Message reply = mAsyncChannel.sendMessageSynchronously(CMD_GET_SINGLE_SCAN_RESULTS, 0);
if (reply.what == WifiScanner.CMD_OP_SUCCEEDED) {
return Arrays.asList(((ParcelableScanResults) reply.obj).getResults());
}
OperationResult result = (OperationResult) reply.obj;
Log.e(TAG, "Error retrieving SingleScan results reason: " + result.reason
+ " description: " + result.description);
return new ArrayList<ScanResult>();
}
这边之前都分析过了通过AsyncChannel的IPC调用到WifiScanningServiceImpl的ClientHandler来处理WifiScanner.CMD_GET_SINGLE_SCAN_RESULTS消息,搜索结果是存放在reply.obj里的。
AsyncTask原理参照:https://blog.csdn.net/u010961631/article/details/48179305
2.4.4 WifiScanningServiceImpl
framework/opt/net/wifi/service/java/com/android/server/wifi/scanner/WifiScanningServiceImpl.java
private class ClientHandler extends WifiHandler {
ClientHandler(String tag, Looper looper) {
super(tag, looper);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: {
ExternalClientInfo client = (ExternalClientInfo) mClients.get(msg.replyTo);
if (client != null) {
logw("duplicate client connection: " + msg.sendingUid + ", messenger="
+ msg.replyTo);
client.mChannel.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED,
AsyncChannel.STATUS_FULL_CONNECTION_REFUSED_ALREADY_CONNECTED);
return;
}
AsyncChannel ac = mFrameworkFacade.makeWifiAsyncChannel(TAG);
ac.connected(mContext, this, msg.replyTo);
client = new ExternalClientInfo(msg.sendingUid, msg.replyTo, ac);
client.register();
ac.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED,
AsyncChannel.STATUS_SUCCESSFUL);
localLog("client connected: " + client);
return;
}
case AsyncChannel.CMD_CHANNEL_DISCONNECT: {
ExternalClientInfo client = (ExternalClientInfo) mClients.get(msg.replyTo);
if (client != null) {
client.mChannel.disconnect();
}
return;
}
case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
ExternalClientInfo client = (ExternalClientInfo) mClients.get(msg.replyTo);
if (client != null && msg.arg1 != AsyncChannel.STATUS_SEND_UNSUCCESSFUL
&& msg.arg1
!= AsyncChannel.STATUS_FULL_CONNECTION_REFUSED_ALREADY_CONNECTED) {
localLog("client disconnected: " + client + ", reason: " + msg.arg1);
client.cleanup();
}
return;
}
}
try {
enforceLocationHardwarePermission(msg.sendingUid);
} catch (SecurityException e) {
localLog("failed to authorize app: " + e);
replyFailed(msg, WifiScanner.REASON_NOT_AUTHORIZED, "Not authorized");
return;
}
// Since the CMD_GET_SCAN_RESULTS and CMD_GET_SINGLE_SCAN_RESULTS messages are
// sent from WifiScanner using |sendMessageSynchronously|, handle separately since
// the |msg.replyTo| field does not actually correspond to the Messenger that is
// registered for that client.
if (msg.what == WifiScanner.CMD_GET_SCAN_RESULTS) {
mBackgroundScanStateMachine.sendMessage(Message.obtain(msg));
return;
}
if (msg.what == WifiScanner.CMD_GET_SINGLE_SCAN_RESULTS) {
mSingleScanStateMachine.sendMessage(Message.obtain(msg));
return;
}
if (msg.what == WifiScanner.CMD_GET_SINGLE_SCAN_RESULTS) {
mSingleScanStateMachine.sendMessage(Message.obtain(msg));
return;
}
2.4.5 继续交给SingleScanStateMachine来处理。这边稍微把这边的状态机状态梳理一下,分析一下状态机的状态变化。先来看一下状态机初始化添加状态,和初始化初始状态为mDefaultState
frameworks/opt/net/wifi/service/java/com/android/server/wifi/scanner/WifiScanningServiceImpl.java
WifiSingleScanStateMachine(Looper looper) {
super("WifiSingleScanStateMachine", looper);
setLogRecSize(128);
setLogOnlyTransitions(false);
// CHECKSTYLE:OFF IndentationCheck
addState(mDefaultState);
addState(mDriverStartedState, mDefaultState);
addState(mIdleState, mDriverStartedState);
addState(mScanningState, mDriverStartedState);
// CHECKSTYLE:ON IndentationCheck
setInitialState(mDefaultState);
}
在startService里会进行状态机的启动
public void startService() {
mClientHandler = new ClientHandler(TAG, mLooper);
mBackgroundScanStateMachine = new WifiBackgroundScanStateMachine(mLooper);
mSingleScanStateMachine = new WifiSingleScanStateMachine(mLooper);
mPnoScanStateMachine = new WifiPnoScanStateMachine(mLooper);
mContext.registerReceiver(
new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
int state = intent.getIntExtra(
WifiManager.EXTRA_SCAN_AVAILABLE, WifiManager.WIFI_STATE_DISABLED);
if (DBG) localLog("SCAN_AVAILABLE : " + state);
if (state == WifiManager.WIFI_STATE_ENABLED) {
mBackgroundScanStateMachine.sendMessage(CMD_DRIVER_LOADED);
mSingleScanStateMachine.sendMessage(CMD_DRIVER_LOADED);
mPnoScanStateMachine.sendMessage(CMD_DRIVER_LOADED);
} else if (state == WifiManager.WIFI_STATE_DISABLED) {
mBackgroundScanStateMachine.sendMessage(CMD_DRIVER_UNLOADED);
mSingleScanStateMachine.sendMessage(CMD_DRIVER_UNLOADED);
mPnoScanStateMachine.sendMessage(CMD_DRIVER_UNLOADED);
}
}
}, new IntentFilter(WifiManager.WIFI_SCAN_AVAILABLE));
mBackgroundScanStateMachine.start();
mSingleScanStateMachine.start();
mPnoScanStateMachine.start();
}
从startService中可以看到,如果WiFi已经打开,startService里面会调用mSingleScanStateMachine.sendMessage(CMD_DRIVER_LOADED);mSingleScanStateMachine里面的DefaultState 这边会监听到CMD_DRIVER_LOADED消息,然后我们切换到IdleState。
class DefaultState extends State {
@Override
public void enter() {
mActiveScans.clear();
mPendingScans.clear();
}
@Override
public boolean processMessage(Message msg) {
switch (msg.what) {
case CMD_DRIVER_LOADED:
transitionTo(mIdleState);
return HANDLED;
class IdleState extends State {
@Override
public void enter() {
tryToStartNewScan();
}
@Override
public boolean processMessage(Message msg) {
return NOT_HANDLED;
}
}
切换到IdleState,会调用enter(),继而调用tryToStartNewScan,但是此时我们走的是else。所以此时并不会迁移到ScanningState。
在处理扫描阶段发出的CMD_START_SINGLE_SCAN继而调用tryToStartNewScan的时候才会迁移到ScanningState
void tryToStartNewScan() {
...
if (mScannerImpl.startSingleScan(settings, this)) {
// 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");
}
}
2.4.6 前面说到发了一个WifiScanner.CMD_GET_SINGLE_SCAN_RESULTS消息交给SingleScanStateMachine状态机处理
这边现在已经是ScanningState,所以就由ScanningState处理获取扫描结果的WifiScanner.CMD_GET_SINGLE_SCAN_RESULTS消息。
ScanningState无法处理该消息,上抛给父类DefaultState状态处理。
class DefaultState extends State {
@Override
public void enter() {
mActiveScans.clear();
mPendingScans.clear();
}
@Override
public boolean processMessage(Message msg) {
switch (msg.what) {
case CMD_DRIVER_LOADED:
transitionTo(mIdleState);
return HANDLED;
case CMD_DRIVER_UNLOADED:
transitionTo(mDefaultState);
return HANDLED;
case WifiScanner.CMD_START_SINGLE_SCAN:
case WifiScanner.CMD_STOP_SINGLE_SCAN:
replyFailed(msg, WifiScanner.REASON_UNSPECIFIED, "not available");
return HANDLED;
case CMD_SCAN_RESULTS_AVAILABLE:
if (DBG) localLog("ignored scan results available event");
return HANDLED;
case CMD_FULL_SCAN_RESULTS:
if (DBG) localLog("ignored full scan result event");
return HANDLED;
case WifiScanner.CMD_GET_SINGLE_SCAN_RESULTS:
msg.obj = new WifiScanner.ParcelableScanResults(
filterCachedScanResultsByAge());
replySucceeded(msg);
return HANDLED;
default:
return NOT_HANDLED;
}
}
可以看到搜索结果就在这里了。
/**
* Filter out any scan results that are older than
* {@link #CACHED_SCAN_RESULTS_MAX_AGE_IN_MILLIS}.
*
* @return Filtered list of scan results.
*/
private ScanResult[] filterCachedScanResultsByAge() {
// Using ScanResult.timestamp here to ensure that we use the same fields as
// WificondScannerImpl for filtering stale results.
long currentTimeInMillis = mClock.getElapsedSinceBootMillis();
return mCachedScanResults.stream()
.filter(scanResult
-> ((currentTimeInMillis - (scanResult.timestamp / 1000))
< CACHED_SCAN_RESULTS_MAX_AGE_IN_MILLIS))
.toArray(ScanResult[]::new);
}
}
/**
* Maximum age of results that we return from our cache via
* {@link WifiScanner#getScanResults()}.
* This is currently set to 3 minutes to restore parity with the wpa_supplicant's scan
* result cache expiration policy. (See b/62253332 for details)
*/
@VisibleForTesting
public static final int CACHED_SCAN_RESULTS_MAX_AGE_IN_MILLIS = 180 * 1000;
可以看到最终是从mCachedScanResults得到扫描结果。
那么mCachedScanResults这个缓存的数据是从哪里来的?如何更新的呢?
……………………………………………………………………………………
2.4.7 在WifiScanningService服务启动后会构建一个 WificondScannerImpl 的binder对象保存到ServiceManger中。
同时mWificondScanner注册了一个Scan的Event Callback: mScanEventHandler
frameworks/opt/net/wifi/service/java/com/android/server/wifi/WificondControl.java
/**
* Setup driver for client mode via wificond.
* @return An IClientInterface as wificond client interface binder handler.
* Returns null on failure.
*/
public IClientInterface setupDriverForClientMode() {
Log.d(TAG, "Setting up driver for client mode");
mWificond = mWifiInjector.makeWificond();
if (mWificond == null) {
Log.e(TAG, "Failed to get reference to wificond");
return null;
}
IClientInterface clientInterface = null;
try {
clientInterface = mWificond.createClientInterface();
} catch (RemoteException e1) {
Log.e(TAG, "Failed to get IClientInterface due to remote exception");
return null;
}
if (clientInterface == null) {
Log.e(TAG, "Could not get IClientInterface instance from wificond");
return null;
}
Binder.allowBlocking(clientInterface.asBinder());
// Refresh Handlers
mClientInterface = clientInterface;
try {
mClientInterfaceName = clientInterface.getInterfaceName();
mWificondScanner = mClientInterface.getWifiScannerImpl();
if (mWificondScanner == null) {
Log.e(TAG, "Failed to get WificondScannerImpl");
return null;
}
Binder.allowBlocking(mWificondScanner.asBinder());
mScanEventHandler = new ScanEventHandler();
mWificondScanner.subscribeScanEvents(mScanEventHandler);
mPnoScanEventHandler = new PnoScanEventHandler();
mWificondScanner.subscribePnoScanEvents(mPnoScanEventHandler);
} catch (RemoteException e) {
Log.e(TAG, "Failed to refresh wificond scanner due to remote exception");
}
return clientInterface;
}
看看这句:mScanEventHandler = new ScanEventHandler();
mScanEventHandler就是在这里创建的,mScanEventHandler是一个Binder对象,当有Scan数据的时候底层通过调用mScanEventHandler的OnScanResultReady函数告诉Framework层有Wifi扫描的数据了。
system/connectivity/wificond/scanning/scanner_impl.cpp
void ScannerImpl::OnScanResultsReady(uint32_t interface_index, bool aborted,
vector<vector<uint8_t>>& ssids,
vector<uint32_t>& frequencies) {
if (!scan_started_) {
LOG(INFO) << "Received external scan result notification from kernel.";
}
scan_started_ = false;
if (scan_event_handler_ != nullptr) {
// TODO: Pass other parameters back once we find framework needs them.
if (aborted) {
LOG(WARNING) << "Scan aborted";
scan_event_handler_->OnScanFailed();
} else {
scan_event_handler_->OnScanResultReady();
}
} else {
LOG(WARNING) << "No scan event handler found.";
}
}
mScanEventHandler的OnScanResultReady函数:
这个函数就是直接调用了mWifiMonitor的broadcastScanResultEvent函数:
frameworks/opt/net/wifi/service/java/com/android/server/wifi/WificondControl.java
private class ScanEventHandler extends IScanEvent.Stub {
@Override
public void OnScanResultReady() {
Log.d(TAG, "Scan result ready event");
mWifiMonitor.broadcastScanResultEvent(mClientInterfaceName);
}
frameworks/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);
}
broadcastScanResultEvent比较简单,就是向WifiMonitor发送了一个消息 SCAN_RESULTS_EVENT
WifiMinitor最终会把这个消息发送到 WifiScanningServiceImpl的消息队列里面来。为什么呢?看看下面的分析
在mScannerImpl的构建过程中(也就是WificondScannerImpl构造函数中)会向WifiMonitor中注册一个Handler:具体如下:
public WificondScannerImpl(Context context, WifiNative wifiNative,
WifiMonitor wifiMonitor, ChannelHelper channelHelper,
Looper looper, Clock clock) {
mContext = context;
mWifiNative = wifiNative;
mChannelHelper = channelHelper;
mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
mEventHandler = new Handler(looper, this);
mClock = clock;
mHwPnoDebouncer = new HwPnoDebouncer(mWifiNative, mAlarmManager, mEventHandler, mClock);
// Check if the device supports HW PNO scans.
mHwPnoScanSupported = mContext.getResources().getBoolean(
R.bool.config_wifi_background_scan_support);
wifiMonitor.registerHandler(mWifiNative.getInterfaceName(),
WifiMonitor.SCAN_FAILED_EVENT, mEventHandler);
wifiMonitor.registerHandler(mWifiNative.getInterfaceName(),
WifiMonitor.PNO_SCAN_RESULTS_EVENT, mEventHandler);
wifiMonitor.registerHandler(mWifiNative.getInterfaceName(),
WifiMonitor.SCAN_RESULTS_EVENT, mEventHandler);
}
1.mEventHandler = new Handler(looper, this);
首先在WificondScannerImpl构造函数中新建了一个Handler
2在前面的broadcastScanResultEvent中已经广播了一个SCAN_RESULTS_EVENT消息,这样WifiMonitor就知道了有SCAN_RESULTS_EVENT这么一个消息了
.wifiMonitor.registerHandler(mWifiNative.getInterfaceName(), WifiMonitor.SCAN_RESULTS_EVENT, mEventHandler);
这段代码的意思就是,向WifiMonitor中注册一个Handler,让WifiMonitor如果知道有WifiMonitor.SCAN_RESULTS_EVENT消息,就用 mEventHandler 发送WifiMonitor.SCAN_RESULTS_EVENT消息;WificondScannerImpl中的Handler发送的WifiMonitor.SCAN_RESULTS_EVENT消息,自然是由 WificondScannerImpl的handleMessage函数来处理。
所以WifiMonitor.SCAN_RESULTS_EVENT这个消息最终是由WificondScannerImpl来处理的
frameworks/opt/net/wifi/service/java/com/android/server/wifi/scanner/WificondScannerImpl.java
public WificondScannerImpl(Context context, WifiNative wifiNative,
WifiMonitor wifiMonitor, ChannelHelper channelHelper,
Looper looper, Clock clock) {
...
@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.PNO_SCAN_RESULTS_EVENT:
pollLatestScanDataForPno();
processPendingScans();
break;
case WifiMonitor.SCAN_RESULTS_EVENT:
mAlarmManager.cancel(mScanTimeoutListener);
pollLatestScanData();
processPendingScans();
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;
}
if (DBG) Log.d(TAG, "Polling scan data for scan: " + mLastScanSettings.scanId);
mNativeScanResults = mWifiNative.getScanResults();
List<ScanResult> singleScanResults = new ArrayList<>();
List<ScanResult> backgroundScanResults = 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.backgroundScanActive) {
backgroundScanResults.add(result);
}
if (mLastScanSettings.singleScanActive
&& mLastScanSettings.singleScanFreqs.containsChannel(
result.frequency)) {
singleScanResults.add(result);
}
}
...
if (mLastScanSettings.singleScanActive
&& 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(mLastScanSettings.scanId, 0, 0,
isAllChannelsScanned(mLastScanSettings.singleScanFreqs),
singleScanResults.toArray(new ScanResult[singleScanResults.size()]));
mLastScanSettings.singleScanEventHandler
.onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE);
}
mLastScanSettings = null;
}
}
这个函数主要做了如下几件事情:
1 调用mWifiNative.getScanResults();获取扫描结果:nativeResults
2 循环处理后把扫描结果保存到singleScanResults
3 把得到的singleScanResults保存到mLatestSingleScanResult中,最后调用singleScanEventHandler的onScanStatus函数通知framework层处理。
这里singleScanEventHandler 就是 WifiScanningServiceImpl
frameworks/opt/net/wifi/service/java/com/android/server/wifi/scanner/WifiScanningServiceImpl.java
/**
* Called to indicate a change in state for the current scan.
* Will dispatch a coresponding event to the state machine
*/
@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;
case WifiNative.WIFI_SCAN_FAILED:
sendMessage(CMD_SCAN_FAILED);
break;
default:
Log.e(TAG, "Unknown scan status event: " + event);
break;
}
}
发送了一个消息sendMessage(CMD_SCAN_RESULTS_AVAILABLE);因为此时我们还是在ScanningState
class ScanningState extends State {
...
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;
reportScanResults(mScannerImpl.getLatestSingleScanResults());就是上报扫描结果了
最后都会调用到WificondScannerImpl的getLatestSingleScanResults方法
@Override
public WifiScanner.ScanData getLatestSingleScanResults() {
return mLatestSingleScanResult;
}
void reportScanResults(ScanData results) {
if (results != null && results.getResults() != null) {
if (results.getResults().length > 0) {
mWifiMetrics.incrementNonEmptyScanResultCount();
} else {
mWifiMetrics.incrementEmptyScanResultCount();
}
}
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);
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()));
sendScanResultBroadcast(true);
}
}
可以看到这里就是对mCachedScanResults这个集合做增减操作了。将传进的mScannerImpl.getLatestSingleScanResults()添加到mCachedScanResults中: mCachedScanResults.addAll(Arrays.asList(results.getResults()));如此我们便吧2.4.6中最后遗留的问题点解决了
前面说到pollLatestScanData主要做了三件事
1 调用mWifiNative.getScanResults();获取扫描结果:nativeResults
2 循环处理后把扫描结果保存到singleScanResults
3 把得到的singleScanResults保存到mLatestSingleScanResult中,最后调用singleScanEventHandler的onScanStatus函数通知framework层reportScanResults处理。
我们现在是把扫描结果nativeResults添加到mCachedScanResults中了,但是nativeResults又是怎么得到的呢?下面我们一起来看看
2.4.8 WifiNative
framework/opt/net/wifi/service/java/com/android/server/wifi/WifiNative.java
/**
* Fetch the latest scan result from kernel via wificond.
* @return Returns an ArrayList of ScanDetail.
* Returns an empty ArrayList on failure.
*/
public ArrayList<ScanDetail> getScanResults() {
return mWificondControl.getScanResults(WificondControl.SCAN_TYPE_SINGLE_SCAN);
}
2..4.9 WificondControl
framework/opt/net/wifi/service/java/com/android/server/wifi/WificondControl.java
/**
* Fetch the latest scan result from kernel via wificond.
* @return Returns an ArrayList of ScanDetail.
* Returns an empty ArrayList on failure.
*/
public ArrayList<ScanDetail> getScanResults(int scanType) {
ArrayList<ScanDetail> results = new ArrayList<>();
if (mWificondScanner == null) {
Log.e(TAG, "No valid wificond scanner interface handler");
return results;
}
try {
NativeScanResult[] nativeResults;
if (scanType == SCAN_TYPE_SINGLE_SCAN) {
nativeResults = mWificondScanner.getScanResults();
} else {
nativeResults = mWificondScanner.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);
// 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())) {
scanDetail.getScanResult().isCarrierAp = true;
scanDetail.getScanResult().carrierApEapType =
mCarrierNetworkConfig.getNetworkEapType(wifiSsid.toString());
scanDetail.getScanResult().carrierName =
mCarrierNetworkConfig.getCarrierName(wifiSsid.toString());
}
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;
}
2.4.10 scanner_impl
Status ScannerImpl::getScanResults(vector<NativeScanResult>* out_scan_results) {
if (!CheckIsValid()) {
return Status::ok();
}
if (!scan_utils_->GetScanResult(interface_index_, out_scan_results)) {
LOG(ERROR) << "Failed to get scan results via NL80211";
}
return Status::ok();
}
2.4.11 scan_utils
bool ScanUtils::GetScanResult(uint32_t interface_index,
vector<NativeScanResult>* out_scan_results) {
NL80211Packet get_scan(
netlink_manager_->GetFamilyId(),
NL80211_CMD_GET_SCAN,
netlink_manager_->GetSequenceNumber(),
getpid());
get_scan.AddFlag(NLM_F_DUMP);
NL80211Attr<uint32_t> ifindex(NL80211_ATTR_IFINDEX, interface_index);
get_scan.AddAttribute(ifindex);
vector<unique_ptr<const NL80211Packet>> response;
if (!netlink_manager_->SendMessageAndGetResponses(get_scan, &response)) {
LOG(ERROR) << "NL80211_CMD_GET_SCAN dump failed";
return false;
}
if (response.empty()) {
LOG(INFO) << "Unexpected empty scan result!";
return true;
}
for (auto& packet : response) {
if (packet->GetMessageType() == NLMSG_ERROR) {
LOG(ERROR) << "Receive ERROR message: "
<< strerror(packet->GetErrorCode());
continue;
}
if (packet->GetMessageType() != netlink_manager_->GetFamilyId()) {
LOG(ERROR) << "Wrong message type: "
<< packet->GetMessageType();
continue;
}
uint32_t if_index;
if (!packet->GetAttributeValue(NL80211_ATTR_IFINDEX, &if_index)) {
LOG(ERROR) << "No interface index in scan result.";
continue;
}
if (if_index != interface_index) {
LOG(WARNING) << "Uninteresting scan result for interface: " << if_index;
continue;
}
NativeScanResult scan_result;
if (!ParseScanResult(std::move(packet), &scan_result)) {
LOG(DEBUG) << "Ignore invalid scan result";
continue;
}
out_scan_results->push_back(std::move(scan_result));
}
return true;
}
3.总结