前言:之前在(五十) Android O WiFi的扫描流程梳理 已经梳理过扫描流程了,那扫描完的结果会呈现在设置的WiFi界面,那扫描结果是如何获取的呢?
WiFi的扫描结果是WiFi扫描后呈现在设置WiFi界面上的,每个AP是以设置比较特色的组件Preference呈现的,点击即可触发连接操作。同样扫描结果的获取也是Settings+SettingsLib+framework获取的,本文也按这个顺序往下梳理。
Settings+SettingsLib+framework
/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 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方法。
/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 getAccessPoints() {
return new ArrayList<>(mAccessPoints);
}
接着看了下只有下面一处会对mAccessPoint这个集合进行增减操作
/**
* 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 oldAccessPoints = new SparseArray<>();
SparseIntArray notificationMap = null;
List 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);
}
}
}
}
这块代码暂只关注扫描结果,大致分为三步
这时候就再看下mInternalAccessPoints 是如何赋值的:
/**
* Update the internal list of access points.
*
* Do not called directly (except for forceUpdate), use {@link #updateAccessPoints()} which
* respects {@link #mStaleScanResults}.
*/
@GuardedBy("mLock")
private void updateAccessPointsLocked(final List newScanResults,
List configs) {
WifiConfiguration connectionConfig = null;
if (mLastInfo != null) {
connectionConfig = getWifiConfigurationForNetworkId(
mLastInfo.getNetworkId(), mWifiManager.getConfiguredNetworks());
}
// Swap the current access points into a cached list.
List cachedAccessPoints = new ArrayList<>(mInternalAccessPoints);
ArrayList 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 apMap = new Multimap();
final Collection 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 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);
}
@VisibleForTesting
AccessPoint getCachedOrCreate(ScanResult result, List cache) {
final int N = cache.size();
for (int i = 0; i < N; i++) {
if (cache.get(i).matches(result)) {
AccessPoint ret = cache.remove(i);
// evict old scan results only if we have fresh results
ret.update(result, !mStaleScanResults);
return ret;
}
}
final AccessPoint accessPoint = new AccessPoint(mContext, result);
accessPoint.setListener(mAccessPointListenerAdapter);
return accessPoint;
}
@VisibleForTesting
AccessPoint getCachedOrCreate(WifiConfiguration config, List cache) {
final int N = cache.size();
for (int i = 0; i < N; i++) {
if (cache.get(i).matches(config)) {
AccessPoint ret = cache.remove(i);
ret.loadConfig(config);
return ret;
}
}
final AccessPoint accessPoint = new AccessPoint(mContext, config);
accessPoint.setListener(mAccessPointListenerAdapter);
return accessPoint;
}
一大串代码简单来看就是分两大块WifiConfiguration 和 ScanResult进行accessPoints的添加,充分利用了之前缓存的AP,最后赋值给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;
再往上继续看哪边有调用updateAccessPointsLocked
/**
* Safely modify {@link #mInternalAccessPoints} by acquiring {@link #mLock} first.
*
* Will not perform the update if {@link #mStaleScanResults} is true
*/
private void updateAccessPoints() {
List configs = mWifiManager.getConfiguredNetworks();
final List newScanResults = mWifiManager.getScanResults();
if (sVerboseLogging) {
Log.i(TAG, "Fetched scan results: " + newScanResults);
}
synchronized (mLock) {
if(!mStaleScanResults) {
updateAccessPointsLocked(newScanResults, configs);
}
}
}
/** 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 newScanResults = mWifiManager.getScanResults();
if (sVerboseLogging) {
Log.i(TAG, "Fetched scan results: " + newScanResults);
}
List 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。
先简单看下getScanResults方法
/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 getScanResults() {
try {
return mService.getScanResults(mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
mService已经很熟悉了,对应的服务端是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 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();
}
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;
}
这边WifiScanner之前也梳理过了,IWifiScanner参数对应的服务端是WifiScanningServiceImpl了(分析见(五十) Android O WiFi的扫描流程梳理 2.3.4节)
framework/base/wifi/java/android/net/wifi/WifiScanner.java
/**
* Retrieve the most recent scan results from a single scan request.
* {@hide}
*/
public List 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();
}
这边之前都分析过了通过AsyncChannel的IPC调用到WifiScanningServiceImpl的ClientHandler来处理WifiScanner.CMD_GET_SINGLE_SCAN_RESULTS消息,搜索结果是存放在reply.obj里的。
AsyncTask原理参照:https://blog.csdn.net/u010961631/article/details/48179305
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;
}
继续交给SingleScanStateMachine来处理。这边稍微把这边的状态机状态梳理一下。
// CHECKSTYLE:OFF IndentationCheck
addState(mDefaultState);
addState(mDriverStartedState, mDefaultState);
addState(mIdleState, mDriverStartedState);
addState(mScanningState, mDriverStartedState);
// CHECKSTYLE:ON IndentationCheck
setInitialState(mDefaultState);
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打开的时候这边会监听到发送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;
接着处理扫描阶段发出的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 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");
}
}
这边后面就由ScanningState处理获取扫描结果的WifiScanner.CMD_GET_SINGLE_SCAN_RESULTS消息。
ScanningState无法处理该消息,上抛给父类DefaultState状态处理。
case WifiScanner.CMD_GET_SINGLE_SCAN_RESULTS:
msg.obj = new WifiScanner.ParcelableScanResults(
filterCachedScanResultsByAge());
replySucceeded(msg);
return 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;
获取时限在3min中内的搜索结果,这种代码的写法一脸懵逼。。。简单看来应该是mCachedScanResults中过滤下时间然后返回回来的。其中reportScanResult方法中有该集合的操作。
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 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 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);
}
}
然后看下这个方法是哪里被调用的:
class ScanningState extends State {
private WorkSource mScanWorkSource;
...
@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;
继续看下mScannerImpl是什么,这个之前也看过了。
mScannerImpl = mScannerImplFactory.create(mContext, mLooper, mClock);
/**
* Factory that create the implementation that is most appropriate for the system.
* This factory should only ever be used once.
*/
public static final WifiScannerImplFactory DEFAULT_FACTORY = new WifiScannerImplFactory() {
public WifiScannerImpl create(Context context, Looper looper, Clock clock) {
WifiNative wifiNative = WifiInjector.getInstance().getWifiNative();
WifiMonitor wifiMonitor = WifiInjector.getInstance().getWifiMonitor();
if (wifiNative.getBgScanCapabilities(new WifiNative.ScanCapabilities())) {
return new HalWifiScannerImpl(context, wifiNative, wifiMonitor, looper, clock);
} else {
return new WificondScannerImpl(context, wifiNative, wifiMonitor, looper,
clock);
}
}
};
最后都会调用到WificondScannerImpl的getLatestSingleScanResults方法
@Override
public WifiScanner.ScanData getLatestSingleScanResults() {
return mLatestSingleScanResult;
}
framework/opt/net/wifi/service/java/com/android/server/wifi/scanner/WificondScannerImpl.java
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 singleScanResults = new ArrayList<>();
List 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);
}
} else {
numFilteredScanResults++;
}
}
if (numFilteredScanResults != 0) {
Log.d(TAG, "Filtering out " + numFilteredScanResults + " scan results.");
}
if (mLastScanSettings.backgroundScanActive) {
if (mBackgroundScanEventHandler != null) {
if ((mLastScanSettings.reportEvents
& WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT) != 0) {
for (ScanResult scanResult : backgroundScanResults) {
// TODO(b/27506257): Fill in correct bucketsScanned value
mBackgroundScanEventHandler.onFullScanResult(scanResult, 0);
}
}
}
Collections.sort(backgroundScanResults, SCAN_RESULT_SORT_COMPARATOR);
ScanResult[] scanResultsArray = new ScanResult[Math.min(mLastScanSettings.maxAps,
backgroundScanResults.size())];
for (int i = 0; i < scanResultsArray.length; ++i) {
scanResultsArray[i] = backgroundScanResults.get(i);
}
if ((mLastScanSettings.reportEvents & WifiScanner.REPORT_EVENT_NO_BATCH) == 0) {
// TODO(b/27506257): Fill in correct bucketsScanned value
mBackgroundScanBuffer.add(new WifiScanner.ScanData(mLastScanSettings.scanId, 0,
scanResultsArray));
}
if (mBackgroundScanEventHandler != null) {
if ((mLastScanSettings.reportEvents
& WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT) != 0
|| (mLastScanSettings.reportEvents
& WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN) != 0
|| (mLastScanSettings.reportEvents
== WifiScanner.REPORT_EVENT_AFTER_BUFFER_FULL
&& (mBackgroundScanBuffer.size()
>= (mBackgroundScanBuffer.capacity()
* mLastScanSettings.reportPercentThreshold
/ 100)
|| mBackgroundScanBuffer.size()
>= mLastScanSettings.reportNumScansThreshold))) {
mBackgroundScanEventHandler
.onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE);
}
}
}
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;
}
}
搜索结果看来是WifiNative来的。
mNativeScanResults = mWifiNative.getScanResults();
List singleScanResults = new ArrayList<>();
List backgroundScanResults = new ArrayList<>();
int numFilteredScanResults = 0;
for (int i = 0; i < mNativeScanResults.size(); ++i) {
ScanResult result = mNativeScanResults.get(i).getScanResult();
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 getScanResults() {
return mWificondControl.getScanResults(WificondControl.SCAN_TYPE_SINGLE_SCAN);
}
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 getScanResults(int scanType) {
ArrayList 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;
}
Status ScannerImpl::getScanResults(vector* 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();
}
bool ScanUtils::GetScanResult(uint32_t interface_index,
vector* out_scan_results) {
NL80211Packet get_scan(
netlink_manager_->GetFamilyId(),
NL80211_CMD_GET_SCAN,
netlink_manager_->GetSequenceNumber(),
getpid());
get_scan.AddFlag(NLM_F_DUMP);
NL80211Attr ifindex(NL80211_ATTR_IFINDEX, interface_index);
get_scan.AddAttribute(ifindex);
vector> 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;
}
和梳理getScanResults应该差不多。。。
/**
* Return a list of all the networks configured for the current foreground
* user.
* Not all fields of WifiConfiguration are returned. Only the following
* fields are filled in:
*
* - networkId
* - SSID
* - BSSID
* - priority
* - allowedProtocols
* - allowedKeyManagement
* - allowedAuthAlgorithms
* - allowedPairwiseCiphers
* - allowedGroupCiphers
*
* @return a list of network configurations in the form of a list
* of {@link WifiConfiguration} objects. Upon failure to fetch or
* when Wi-Fi is turned off, it can be null.
*/
public List getConfiguredNetworks() {
try {
ParceledListSlice parceledList =
mService.getConfiguredNetworks();
if (parceledList == null) {
return Collections.emptyList();
}
return parceledList.getList();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* see {@link android.net.wifi.WifiManager#getConfiguredNetworks()}
* @return the list of configured networks
*/
@Override
public ParceledListSlice getConfiguredNetworks() {
enforceAccessPermission();
mLog.info("getConfiguredNetworks uid=%").c(Binder.getCallingUid()).flush();
if (mWifiStateMachineChannel != null) {
List configs = mWifiStateMachine.syncGetConfiguredNetworks(
Binder.getCallingUid(), mWifiStateMachineChannel);
if (configs != null) {
return new ParceledListSlice(configs);
}
} else {
Slog.e(TAG, "mWifiStateMachineChannel is not initialized");
}
return null;
}
这边不一样了,用了WifiStateMachine的接口了,传入了一个AsyncChannel参数,看下这个mWifiStateMachineChannel
/**
* Handles interaction with WifiStateMachine
*/
private class WifiStateMachineHandler extends WifiHandler {
private AsyncChannel mWsmChannel;
WifiStateMachineHandler(String tag, Looper looper, AsyncChannel asyncChannel) {
super(tag, looper);
mWsmChannel = asyncChannel;
mWsmChannel.connect(mContext, this, mWifiStateMachine.getHandler());
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: {
if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
mWifiStateMachineChannel = mWsmChannel;
} else {
Slog.e(TAG, "WifiStateMachine connection failure, error=" + msg.arg1);
mWifiStateMachineChannel = null;
}
break;
}
可以看到这个AsyncChannel是完成WifiStateMachine和WifiServiceImpl IPC交互的,看下哪里调用的构造函数:
public WifiServiceImpl(Context context, WifiInjector wifiInjector, AsyncChannel asyncChannel) {
mContext = context;
mWifiInjector = wifiInjector;
mClock = wifiInjector.getClock();
mFacade = mWifiInjector.getFrameworkFacade();
mWifiMetrics = mWifiInjector.getWifiMetrics();
mTrafficPoller = mWifiInjector.getWifiTrafficPoller();
mUserManager = mWifiInjector.getUserManager();
mCountryCode = mWifiInjector.getWifiCountryCode();
mWifiStateMachine = mWifiInjector.getWifiStateMachine();
mWifiStateMachine.enableRssiPolling(true);
mSettingsStore = mWifiInjector.getWifiSettingsStore();
mPowerManager = mContext.getSystemService(PowerManager.class);
mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
mCertManager = mWifiInjector.getWifiCertManager();
mWifiLockManager = mWifiInjector.getWifiLockManager();
mWifiMulticastLockManager = mWifiInjector.getWifiMulticastLockManager();
HandlerThread wifiServiceHandlerThread = mWifiInjector.getWifiServiceHandlerThread();
mClientHandler = new ClientHandler(TAG, wifiServiceHandlerThread.getLooper());
mWifiStateMachineHandler = new WifiStateMachineHandler(TAG,
wifiServiceHandlerThread.getLooper(), asyncChannel);
mWifiStateMachineHandler = new WifiStateMachineHandler(TAG,
wifiServiceHandlerThread.getLooper(), asyncChannel);
哪里调用WifiServiceImpl已经很熟悉了,看下WifiService
public final class WifiService extends SystemService {
private static final String TAG = "WifiService";
final WifiServiceImpl mImpl;
public WifiService(Context context) {
super(context);
mImpl = new WifiServiceImpl(context, new WifiInjector(context), new WifiAsyncChannel(TAG));
}
/**
* Get configured networks synchronously
*
* @param channel
* @return
*/
public List syncGetConfiguredNetworks(int uuid, AsyncChannel channel) {
Message resultMsg = channel.sendMessageSynchronously(CMD_GET_CONFIGURED_NETWORKS, uuid);
if (resultMsg == null) { // an error has occurred
return null;
} else {
List result = (List) resultMsg.obj;
resultMsg.recycle();
return result;
}
}
这边channel发出来的消息也是发给自己的,即WifiStateMachine的mSmHandler负责处理。
搜了下只有DefaultState这个所有状态的父类负责处理。
case CMD_GET_CONFIGURED_NETWORKS:
replyToMessage(message, message.what, mWifiConfigManager.getSavedNetworks());
break;
/**
* Retrieves the list of all configured networks with the passwords masked.
*
* @return List of WifiConfiguration objects representing the networks.
*/
public List getSavedNetworks() {
return getConfiguredNetworks(true, true);
}
/**
* Fetch the list of currently configured networks maintained in WifiConfigManager.
*
* This retrieves a copy of the internal configurations maintained by WifiConfigManager and
* should be used for any public interfaces.
*
* @param savedOnly Retrieve only saved networks.
* @param maskPasswords Mask passwords or not.
* @return List of WifiConfiguration objects representing the networks.
*/
private List getConfiguredNetworks(
boolean savedOnly, boolean maskPasswords) {
List networks = new ArrayList<>();
for (WifiConfiguration config : getInternalConfiguredNetworks()) {
if (savedOnly && config.ephemeral) {
continue;
}
networks.add(createExternalWifiConfiguration(config, maskPasswords));
}
return networks;
}
/**
* Helper method to retrieve all the internal WifiConfiguration objects corresponding to all
* the networks in our database.
*/
private Collection getInternalConfiguredNetworks() {
return mConfiguredNetworks.valuesForCurrentUser();
}
看下这个mConfiguredNetworks的成员是如何加入的:
/**
* Add a network or update a network configuration to our database.
* If the supplied networkId is INVALID_NETWORK_ID, we create a new empty
* network configuration. Otherwise, the networkId should refer to an existing configuration.
*
* @param config provided WifiConfiguration object.
* @param uid UID of the app requesting the network addition/deletion.
* @return NetworkUpdateResult object representing status of the update.
*/
private NetworkUpdateResult addOrUpdateNetworkInternal(WifiConfiguration config, int uid) {
if (mVerboseLoggingEnabled) {
Log.v(TAG, "Adding/Updating network " + config.getPrintableSsid());
}
WifiConfiguration newInternalConfig = null;
// First check if we already have a network with the provided network id or configKey.
WifiConfiguration existingInternalConfig = getInternalConfiguredNetwork(config);
// No existing network found. So, potentially a network add.
if (existingInternalConfig == null) {
if (!WifiConfigurationUtil.validate(config, WifiConfigurationUtil.VALIDATE_FOR_ADD)) {
Log.e(TAG, "Cannot add network with invalid config");
return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
}
newInternalConfig = createNewInternalWifiConfigurationFromExternal(config, uid);
// Since the original config provided may have had an empty
// {@link WifiConfiguration#allowedKeyMgmt} field, check again if we already have a
// network with the the same configkey.
existingInternalConfig = getInternalConfiguredNetwork(newInternalConfig.configKey());
}
// Existing network found. So, a network update.
if (existingInternalConfig != null) {
if (!WifiConfigurationUtil.validate(
config, WifiConfigurationUtil.VALIDATE_FOR_UPDATE)) {
Log.e(TAG, "Cannot update network with invalid config");
return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
}
// Check for the app's permission before we let it update this network.
if (!canModifyNetwork(existingInternalConfig, uid, DISALLOW_LOCKDOWN_CHECK_BYPASS)) {
Log.e(TAG, "UID " + uid + " does not have permission to update configuration "
+ config.configKey());
return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
}
newInternalConfig =
updateExistingInternalWifiConfigurationFromExternal(
existingInternalConfig, config, uid);
}
// Only add networks with proxy settings if the user has permission to
if (WifiConfigurationUtil.hasProxyChanged(existingInternalConfig, newInternalConfig)
&& !canModifyProxySettings(uid)) {
Log.e(TAG, "UID " + uid + " does not have permission to modify proxy Settings "
+ config.configKey() + ". Must have NETWORK_SETTINGS,"
+ " or be device or profile owner.");
return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
}
// Update the keys for non-Passpoint enterprise networks. For Passpoint, the certificates
// and keys are installed at the time the provider is installed.
if (config.enterpriseConfig != null
&& config.enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.NONE
&& !config.isPasspoint()) {
if (!(mWifiKeyStore.updateNetworkKeys(newInternalConfig, existingInternalConfig))) {
return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
}
}
boolean newNetwork = (existingInternalConfig == null);
// This is needed to inform IpClient about any IP configuration changes.
boolean hasIpChanged =
newNetwork || WifiConfigurationUtil.hasIpChanged(
existingInternalConfig, newInternalConfig);
boolean hasProxyChanged =
newNetwork || WifiConfigurationUtil.hasProxyChanged(
existingInternalConfig, newInternalConfig);
// Reset the |hasEverConnected| flag if the credential parameters changed in this update.
boolean hasCredentialChanged =
newNetwork || WifiConfigurationUtil.hasCredentialChanged(
existingInternalConfig, newInternalConfig);
if (hasCredentialChanged) {
newInternalConfig.getNetworkSelectionStatus().setHasEverConnected(false);
}
// Add it to our internal map. This will replace any existing network configuration for
// updates.
try {
mConfiguredNetworks.put(newInternalConfig);
} catch (IllegalArgumentException e) {
Log.e(TAG, "Failed to add network to config map", e);
return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
}
if (mDeletedEphemeralSSIDs.remove(config.SSID)) {
if (mVerboseLoggingEnabled) {
Log.v(TAG, "Removed from ephemeral blacklist: " + config.SSID);
}
}
// Stage the backup of the SettingsProvider package which backs this up.
mBackupManagerProxy.notifyDataChanged();
NetworkUpdateResult result =
new NetworkUpdateResult(hasIpChanged, hasProxyChanged, hasCredentialChanged);
result.setIsNewNetwork(newNetwork);
result.setNetworkId(newInternalConfig.networkId);
localLog("addOrUpdateNetworkInternal: added/updated config."
+ " netId=" + newInternalConfig.networkId
+ " configKey=" + newInternalConfig.configKey()
+ " uid=" + Integer.toString(newInternalConfig.creatorUid)
+ " name=" + newInternalConfig.creatorName);
return result;
}
这边再看下是哪里调用的这个getInternalConfiguredNetwork方法
/**
* Add a network or update a network configuration to our database.
* If the supplied networkId is INVALID_NETWORK_ID, we create a new empty
* network configuration. Otherwise, the networkId should refer to an existing configuration.
*
* @param config provided WifiConfiguration object.
* @param uid UID of the app requesting the network addition/modification.
* @return NetworkUpdateResult object representing status of the update.
*/
public NetworkUpdateResult addOrUpdateNetwork(WifiConfiguration config, int uid) {
if (!doesUidBelongToCurrentUser(uid)) {
Log.e(TAG, "UID " + uid + " not visible to the current user");
return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
}
if (config == null) {
Log.e(TAG, "Cannot add/update network with null config");
return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
}
if (mPendingStoreRead) {
Log.e(TAG, "Cannot add/update network before store is read!");
return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
}
NetworkUpdateResult result = addOrUpdateNetworkInternal(config, uid);
if (!result.isSuccess()) {
Log.e(TAG, "Failed to add/update network " + config.getPrintableSsid());
return result;
}
WifiConfiguration newConfig = getInternalConfiguredNetwork(result.getNetworkId());
sendConfiguredNetworkChangedBroadcast(
newConfig,
result.isNewNetwork()
? WifiManager.CHANGE_REASON_ADDED
: WifiManager.CHANGE_REASON_CONFIG_CHANGE);
// Unless the added network is ephemeral or Passpoint, persist the network update/addition.
if (!config.ephemeral && !config.isPasspoint()) {
saveToStore(true);
if (mListener != null) {
if (result.isNewNetwork()) {
mListener.onSavedNetworkAdded(newConfig.networkId);
} else {
mListener.onSavedNetworkUpdated(newConfig.networkId);
}
}
}
return result;
}
这边再往上找没线索了,只能全搜了
这些地方都会更新,我就偷懒只看下WifiStateMachine中的逻辑吧。感觉在瞎看了,还是有手机的时候加个堆栈比较好。
case CMD_ADD_OR_UPDATE_NETWORK:
WifiConfiguration config = (WifiConfiguration) message.obj;
NetworkUpdateResult result =
mWifiConfigManager.addOrUpdateNetwork(config, message.sendingUid);
if (!result.isSuccess()) {
messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
}
replyToMessage(message, message.what, result.getNetworkId());
break;
/**
* Add a network synchronously
*
* @return network id of the new network
*/
public int syncAddOrUpdateNetwork(AsyncChannel channel, WifiConfiguration config) {
Message resultMsg = channel.sendMessageSynchronously(CMD_ADD_OR_UPDATE_NETWORK, config);
int result = resultMsg.arg1;
resultMsg.recycle();
return result;
}
这个方法是WifiServiceImpl的addOrUpdateNetwork调用的
/**
* see {@link android.net.wifi.WifiManager#addOrUpdateNetwork(WifiConfiguration)}
* @return the supplicant-assigned identifier for the new or updated
* network if the operation succeeds, or {@code -1} if it fails
*/
@Override
public int addOrUpdateNetwork(WifiConfiguration config) {
enforceChangePermission();
mLog.info("addOrUpdateNetwork uid=%").c(Binder.getCallingUid()).flush();
// Previously, this API is overloaded for installing Passpoint profiles. Now
// that we have a dedicated API for doing it, redirect the call to the dedicated API.
if (config.isPasspoint()) {
PasspointConfiguration passpointConfig =
PasspointProvider.convertFromWifiConfig(config);
if (passpointConfig.getCredential() == null) {
Slog.e(TAG, "Missing credential for Passpoint profile");
return -1;
}
// Copy over certificates and keys.
passpointConfig.getCredential().setCaCertificate(
config.enterpriseConfig.getCaCertificate());
passpointConfig.getCredential().setClientCertificateChain(
config.enterpriseConfig.getClientCertificateChain());
passpointConfig.getCredential().setClientPrivateKey(
config.enterpriseConfig.getClientPrivateKey());
if (!addOrUpdatePasspointConfiguration(passpointConfig)) {
Slog.e(TAG, "Failed to add Passpoint profile");
return -1;
}
// There is no network ID associated with a Passpoint profile.
return 0;
}
if (config != null) {
//TODO: pass the Uid the WifiStateMachine as a message parameter
Slog.i("addOrUpdateNetwork", " uid = " + Integer.toString(Binder.getCallingUid())
+ " SSID " + config.SSID
+ " nid=" + Integer.toString(config.networkId));
if (config.networkId == WifiConfiguration.INVALID_NETWORK_ID) {
config.creatorUid = Binder.getCallingUid();
} else {
config.lastUpdateUid = Binder.getCallingUid();
}
if (mWifiStateMachineChannel != null) {
return mWifiStateMachine.syncAddOrUpdateNetwork(mWifiStateMachineChannel, config);
} else {
Slog.e(TAG, "mWifiStateMachineChannel is not initialized");
return -1;
}
} else {
Slog.e(TAG, "bad network configuration");
return -1;
}
}
然后就逻辑就到WifiManager那边去了。。。
/**
* Add a new network description to the set of configured networks.
* The {@code networkId} field of the supplied configuration object
* is ignored.
*
* The new network will be marked DISABLED by default. To enable it,
* called {@link #enableNetwork}.
*
* @param config the set of variables that describe the configuration,
* contained in a {@link WifiConfiguration} object.
* If the {@link WifiConfiguration} has an Http Proxy set
* the calling app must be System, or be provisioned as the Profile or Device Owner.
* @return the ID of the newly created network description. This is used in
* other operations to specified the network to be acted upon.
* Returns {@code -1} on failure.
*/
public int addNetwork(WifiConfiguration config) {
if (config == null) {
return -1;
}
config.networkId = -1;
return addOrUpdateNetwork(config);
}
/**
* Update the network description of an existing configured network.
*
* @param config the set of variables that describe the configuration,
* contained in a {@link WifiConfiguration} object. It may
* be sparse, so that only the items that are being changed
* are non-null
. The {@code networkId} field
* must be set to the ID of the existing network being updated.
* If the {@link WifiConfiguration} has an Http Proxy set
* the calling app must be System, or be provisioned as the Profile or Device Owner.
* @return Returns the {@code networkId} of the supplied
* {@code WifiConfiguration} on success.
*
* Returns {@code -1} on failure, including when the {@code networkId}
* field of the {@code WifiConfiguration} does not refer to an
* existing network.
*/
public int updateNetwork(WifiConfiguration config) {
if (config == null || config.networkId < 0) {
return -1;
}
return addOrUpdateNetwork(config);
}
/**
* Internal method for doing the RPC that creates a new network description
* or updates an existing one.
*
* @param config The possibly sparse object containing the variables that
* are to set or updated in the network description.
* @return the ID of the network on success, {@code -1} on failure.
*/
private int addOrUpdateNetwork(WifiConfiguration config) {
try {
return mService.addOrUpdateNetwork(config);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
所以说这个config的配置项还是由应用传来的?
看起来扫描到的ap,一部分是应用add的config,一部分是扫描到的result构成的。更新一下,add的configuration可以由Settings或者第三方apk来实现,只要调用framework添加configuration的接口即可,这边更新ap用到config主要是将已保存ap的configuration和扫描结果一起更新到AccessPoint中去,方便应用进行查询。
WiFi的扫描结果从梳理结果来看,一部分是应用add的config,一部分是扫描到的result构成的,但是具体如何扫描到的还要后续继续学习wpa_supplicant和/system/connectivity/wificond下的代码。如下是已梳理好的到wificond的时序图: