xref: /packages/apps/Settings/src/com/android/settings/wifi/WifiSettings.java
/** 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();
removeAccessPointPreference();
addMessagePreference(R.string.wifi_starting);
setProgressBarVisible(true);
break;
case WifiManager.WIFI_STATE_DISABLING:
removeConnectedAccessPointPreference();
removeAccessPointPreference();
addMessagePreference(R.string.wifi_stopping);
break;
case WifiManager.WIFI_STATE_DISABLED:
setOffMessage();
setAdditionalSettingsSummaries();
setProgressBarVisible(false);
break;
}
}
.........................................................................................
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);
}
boolean hasAvailableAccessPoints = false;
mStatusMessagePreference.setVisible(false);
mConnectedAccessPointPreferenceCategory.setVisible(true);
mAccessPointsPreferenceCategory.setVisible(true);
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 = accessPoint.getKey();
hasAvailableAccessPoints = true;
LongPressAccessPointPreference pref =
(LongPressAccessPointPreference) getCachedPreference(key);
if (pref != null) {
pref.setOrder(index);
continue;
}
LongPressAccessPointPreference preference =
createLongPressAccessPointPreference(accessPoint);
preference.setKey(key);
preference.setOrder(index);
if (mOpenSsid != null && mOpenSsid.equals(accessPoint.getSsidStr())
&& (accessPoint.getSecurity() != AccessPoint.SECURITY_NONE &&
accessPoint.getSecurity() != AccessPoint.SECURITY_OWE &&
accessPoint.getSecurity() != AccessPoint.SECURITY_OWE_TRANSITION)) {
if (!accessPoint.isSaved() || isDisabledByWrongPassword(accessPoint)) {
onPreferenceTreeClick(preference);
mOpenSsid = null;
}
}
mAccessPointsPreferenceCategory.addPreference(preference);
accessPoint.setListener(WifiSettings.this);
preference.refresh();
}
}
removeCachedPrefs(mAccessPointsPreferenceCategory);
mAddWifiNetworkPreference.setOrder(index);
mAccessPointsPreferenceCategory.addPreference(mAddWifiNetworkPreference);
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 */);
}
}
在onWifiStateChanged方法里面处理wifi的开关,当我们打开wifi按钮的时候会走WifiManager.WIFI_STATE_ENABLED分支,调用updateAccessPointPreferences方法完成wifi的添加。accessPoints是通过WifiTracker的getAccessPoints获得的。
xref: /frameworks/base/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
@AnyThread
public List<AccessPoint> getAccessPoints() {
synchronized (mLock) {
return new ArrayList<>(mInternalAccessPoints);
}
}
这一部分是打开wifi按钮的时候主动获取accessPoints,接下来我们分析accessPoints是如果从系统底层推送的。
xref: /frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiServiceImpl.java
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(Intent.ACTION_USER_REMOVED)) {
int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
mClientModeImpl.removeUserConfigs(userHandle);
} else if (action.equals(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED)) {
int state = intent.getIntExtra(BluetoothAdapter.EXTRA_CONNECTION_STATE,
BluetoothAdapter.STATE_DISCONNECTED);
mClientModeImpl.sendBluetoothAdapterStateChange(state);
} else if (action.equals(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED)) {
boolean emergencyMode = intent.getBooleanExtra("phoneinECMState", false);
mWifiController.sendMessage(CMD_EMERGENCY_MODE_CHANGED, emergencyMode ? 1 : 0, 0);
} else if (action.equals(TelephonyIntents.ACTION_EMERGENCY_CALL_STATE_CHANGED)) {
boolean inCall = intent.getBooleanExtra(PhoneConstants.PHONE_IN_EMERGENCY_CALL, false);
mWifiController.sendMessage(CMD_EMERGENCY_CALL_STATE_CHANGED, inCall ? 1 : 0, 0);
} else if (action.equals(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED)) {
handleIdleModeChanged();
}
}
};
..................................................................................................
void handleIdleModeChanged() {
boolean doScan = false;
synchronized (this) {
boolean idle = mPowerManager.isDeviceIdleMode();
if (mInIdleMode != idle) {
mInIdleMode = idle;
if (!idle) {
if (mScanPending) {
mScanPending = false;
doScan = true;
}
}
}
}
if (doScan) {
// Someone requested a scan while we were idle; do a full scan now.
// A security check of the caller's identity was made when the request arrived via
// Binder. Now we'll pass the current process's identity to startScan().
startScan(mContext.getOpPackageName());
}
}
..................................................................................................
@Override
public boolean startScan(String packageName) {
if (enforceChangePermission(packageName) != MODE_ALLOWED) {
return false;
}
int callingUid = Binder.getCallingUid();
long ident = Binder.clearCallingIdentity();
mLog.info("startScan uid=%").c(callingUid).flush();
synchronized (this) {
if (mInIdleMode) {
// Need to send an immediate scan result broadcast in case the
// caller is waiting for a result ..
// TODO: investigate if the logic to cancel scans when idle can move to
// WifiScanningServiceImpl. This will 1 - clean up WifiServiceImpl and 2 -
// avoid plumbing an awkward path to report a cancelled/failed scan. This will
// be sent directly until b/31398592 is fixed.
sendFailedScanBroadcast();
mScanPending = true;
return false;
}
}
try {
mWifiPermissionsUtil.enforceCanAccessScanResults(packageName, callingUid);
Mutable<Boolean> scanSuccess = new Mutable<>();
boolean runWithScissorsSuccess = mWifiInjector.getClientModeImplHandler()
.runWithScissors(() -> {
scanSuccess.value = mScanRequestProxy.startScan(callingUid, packageName);
}, RUN_WITH_SCISSORS_TIMEOUT_MILLIS);
if (!runWithScissorsSuccess) {
Log.e(TAG, "Failed to post runnable to start scan");
sendFailedScanBroadcast();
return false;
}
if (!scanSuccess.value) {
Log.e(TAG, "Failed to start scan");
return false;
}
} catch (SecurityException e) {
Slog.e(TAG, "Permission violation - startScan not allowed for"
+ " uid=" + callingUid + ", packageName=" + packageName + ", reason=" + e);
return false;
} finally {
Binder.restoreCallingIdentity(ident);
}
return true;
}
注册广播,当收到ACTION_DEVICE_IDLE_MODE_CHANGED广播通知时就开始扫描。在startScan方法里会调用ScanRequestProxy类的startScan方法开启扫描。
xref: /frameworks/opt/net/wifi/service/java/com/android/server/wifi/ScanRequestProxy.java
public boolean startScan(int callingUid, String packageName) {
if (!retrieveWifiScannerIfNecessary()) {
Log.e(TAG, "Failed to retrieve wifiscanner");
sendScanResultFailureBroadcastToPackage(packageName);
return false;
}
boolean fromSettingsOrSetupWizard =
mWifiPermissionsUtil.checkNetworkSettingsPermission(callingUid)
|| mWifiPermissionsUtil.checkNetworkSetupWizardPermission(callingUid);
// Check and throttle scan request unless,
// a) App has either NETWORK_SETTINGS or NETWORK_SETUP_WIZARD permission.
// b) Throttling has been disabled by user.
if (!fromSettingsOrSetupWizard && mThrottleEnabledSettingObserver.isEnabled()
&& shouldScanRequestBeThrottledForApp(callingUid, packageName)) {
Log.i(TAG, "Scan request from " + packageName + " throttled");
sendScanResultFailureBroadcastToPackage(packageName);
return false;
}
// Create a worksource using the caller's UID.
WorkSource workSource = new WorkSource(callingUid, packageName);
// Create the scan settings.
WifiScanner.ScanSettings settings = new WifiScanner.ScanSettings();
// Scan requests from apps with network settings will be of high accuracy type.
if (fromSettingsOrSetupWizard) {
settings.type = WifiScanner.TYPE_HIGH_ACCURACY;
}
// always do full scans
settings.band = WifiScanner.WIFI_BAND_BOTH_WITH_DFS;
settings.reportEvents = WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN
| WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT;
if (mScanningForHiddenNetworksEnabled) {
// retrieve the list of hidden network SSIDs to scan for, if enabled.
List<WifiScanner.ScanSettings.HiddenNetwork> hiddenNetworkList =
mWifiConfigManager.retrieveHiddenNetworkList();
settings.hiddenNetworks = hiddenNetworkList.toArray(
new WifiScanner.ScanSettings.HiddenNetwork[hiddenNetworkList.size()]);
}
mWifiScanner.startScan(settings, new ScanRequestProxyScanListener(), workSource);
return true;
}
mWifiScanner.startScan(settings, new ScanRequestProxyScanListener(), workSource)调用WifiScanner类的startScan方法扫描,同时设置扫描回调监听,当扫描结束后通过监听回调。
我先分析如何扫描,然后再分析ScanRequestProxyScanListener扫描监听回调。
xref: /frameworks/base/wifi/java/android/net/wifi/WifiScanner.java
@RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
public void startScan(ScanSettings settings, ScanListener listener, WorkSource workSource) {
Preconditions.checkNotNull(listener, "listener cannot be null");
int key = addListener(listener);
if (key == INVALID_KEY) return;
validateChannel();
Bundle scanParams = new Bundle();
scanParams.putParcelable(SCAN_PARAMS_SCAN_SETTINGS_KEY, settings);
scanParams.putParcelable(SCAN_PARAMS_WORK_SOURCE_KEY, workSource);
scanParams.putString(REQUEST_PACKAGE_NAME_KEY, mContext.getOpPackageName());
mAsyncChannel.sendMessage(CMD_START_SINGLE_SCAN, 0, key, scanParams);
}
通过AsyncChannel发送message(CMD_START_SINGLE_SCAN),最终在WifiScanningServiceImpl.java里面处理
xref: /frameworks/opt/net/wifi/service/java/com/android/server/wifi/scanner/WifiScanningServiceImpl.java
class WifiSingleScanStateMachine extends StateMachine implements WifiNative.ScanEventHandler {
/**
* 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;
private final DefaultState mDefaultState = new DefaultState();
private final DriverStartedState mDriverStartedState = new DriverStartedState();
private final IdleState mIdleState = new IdleState();
private final ScanningState mScanningState = new ScanningState();
private WifiNative.ScanSettings mActiveScanSettings = null;
private RequestList<ScanSettings> mActiveScans = new RequestList<>();
private RequestList<ScanSettings> mPendingScans = new RequestList<>();
// Scan results cached from the last full single scan request.
private final List<ScanResult> mCachedScanResults = new ArrayList<>();
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);
}
.....................................................................................
/**
* State representing when the driver is running. This state is not meant to be transitioned
* directly, but is instead intended as a parent state of ScanningState and IdleState
* to hold common functionality and handle cleaning up scans when the driver is shut down.
*/
class DriverStartedState extends State {
.....................................................................................................
@Override
public boolean processMessage(Message msg) {
ClientInfo ci = mClients.get(msg.replyTo);
switch (msg.what) {
case CMD_DRIVER_LOADED:
// Ignore if we're already in driver loaded state.
return HANDLED;
case WifiScanner.CMD_START_SINGLE_SCAN:
int handler = msg.arg2;
Bundle scanParams = (Bundle) msg.obj;
if (scanParams == null) {
logCallback("singleScanInvalidRequest", ci, handler, "null params");
replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "params null");
return HANDLED;
}
scanParams.setDefusable(true);
ScanSettings scanSettings =
scanParams.getParcelable(WifiScanner.SCAN_PARAMS_SCAN_SETTINGS_KEY);
WorkSource workSource =
scanParams.getParcelable(WifiScanner.SCAN_PARAMS_WORK_SOURCE_KEY);
if (validateScanRequest(ci, handler, scanSettings)) {
mWifiMetrics.incrementOneshotScanCount();
if (scanSettings.band == WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY
|| scanSettings.band == WifiScanner.WIFI_BAND_5_GHZ_WITH_DFS
|| scanSettings.band == WifiScanner.WIFI_BAND_BOTH_WITH_DFS) {
mWifiMetrics.incrementOneshotScanWithDfsCount();
}
logScanRequest("addSingleScanRequest", ci, handler, workSource,
scanSettings, null);
replySucceeded(msg);
// If there is an active scan that will fulfill the scan request then
// mark this request as an active scan, otherwise mark it pending.
// If were not currently scanning then try to start a scan. Otherwise
// this scan will be scheduled when transitioning back to IdleState
// after finishing the current scan.
if (getCurrentState() == mScanningState) {
if (activeScanSatisfies(scanSettings)) {
mActiveScans.addRequest(ci, handler, workSource, scanSettings);
} else {
mPendingScans.addRequest(ci, handler, workSource, scanSettings);
}
} else {
mPendingScans.addRequest(ci, handler, workSource, scanSettings);
tryToStartNewScan();
}
} else {
logCallback("singleScanInvalidRequest", ci, handler, "bad request");
replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "bad request");
mWifiMetrics.incrementScanReturnEntry(
WifiMetricsProto.WifiLog.SCAN_FAILURE_INVALID_CONFIGURATION, 1);
}
return HANDLED;
case WifiScanner.CMD_STOP_SINGLE_SCAN:
removeSingleScanRequest(ci, msg.arg2);
return HANDLED;
default:
return NOT_HANDLED;
}
}
}
..........................................................................................................
void tryToStartNewScan() {
if (mPendingScans.size() == 0) { // no pending requests
return;
}
mChannelHelper.updateChannels();
// TODO move merging logic to a scheduler
WifiNative.ScanSettings settings = new WifiNative.ScanSettings();
settings.num_buckets = 1;
WifiNative.BucketSettings bucketSettings = new WifiNative.BucketSettings();
bucketSettings.bucket = 0;
bucketSettings.period_ms = 0;
bucketSettings.report_events = WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN;
ChannelCollection channels = mChannelHelper.createChannelCollection();
List<WifiNative.HiddenNetwork> hiddenNetworkList = new ArrayList<>();
for (RequestInfo<ScanSettings> entry : mPendingScans) {
settings.scanType =
mergeScanTypes(settings.scanType, getNativeScanType(entry.settings.type));
channels.addChannels(entry.settings);
if (entry.settings.hiddenNetworks != null) {
for (int i = 0; i < entry.settings.hiddenNetworks.length; i++) {
WifiNative.HiddenNetwork hiddenNetwork = new WifiNative.HiddenNetwork();
hiddenNetwork.ssid = entry.settings.hiddenNetworks[i].ssid;
hiddenNetworkList.add(hiddenNetwork);
}
}
if ((entry.settings.reportEvents & WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT)
!= 0) {
bucketSettings.report_events |= WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT;
}
}
if (hiddenNetworkList.size() > 0) {
settings.hiddenNetworks = new WifiNative.HiddenNetwork[hiddenNetworkList.size()];
int numHiddenNetworks = 0;
for (WifiNative.HiddenNetwork hiddenNetwork : hiddenNetworkList) {
settings.hiddenNetworks[numHiddenNetworks++] = hiddenNetwork;
}
}
channels.fillBucketSettings(bucketSettings, Integer.MAX_VALUE);
settings.buckets = new WifiNative.BucketSettings[] {bucketSettings};
if (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");
}
}
}
在tryToStartNewScan方法里面会调用用WifiScannerImpl类的startSingleScan方法。WifiScannerImpl是个抽象类,具体实现方法是在WificondScannerImpl.java类中。
xref: /frameworks/opt/net/wifi/service/java/com/android/server/wifi/scanner/WificondScannerImpl.java
public boolean startSingleScan(WifiNative.ScanSettings settings,
WifiNative.ScanEventHandler eventHandler) {
if (eventHandler == null || settings == null) {
Log.w(TAG, "Invalid arguments for startSingleScan: settings=" + settings
+ ",eventHandler=" + eventHandler);
return false;
}
synchronized (mSettingsLock) {
if (mLastScanSettings != null) {
Log.w(TAG, "A single scan is already running");
return false;
}
ChannelCollection allFreqs = mChannelHelper.createChannelCollection();
boolean reportFullResults = false;
for (int i = 0; i < settings.num_buckets; ++i) {
WifiNative.BucketSettings bucketSettings = settings.buckets[i];
if ((bucketSettings.report_events
& WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT) != 0) {
reportFullResults = true;
}
allFreqs.addChannels(bucketSettings);
}
List<String> hiddenNetworkSSIDSet = new ArrayList<>();
if (settings.hiddenNetworks != null) {
int numHiddenNetworks =
Math.min(settings.hiddenNetworks.length, MAX_HIDDEN_NETWORK_IDS_PER_SCAN);
for (int i = 0; i < numHiddenNetworks; i++) {
hiddenNetworkSSIDSet.add(settings.hiddenNetworks[i].ssid);
}
}
mLastScanSettings = new LastScanSettings(
mClock.getElapsedSinceBootMillis(),
reportFullResults, allFreqs, eventHandler);
boolean success = false;
Set<Integer> freqs;
if (!allFreqs.isEmpty()) {
freqs = allFreqs.getScanFreqs();
success = mWifiNative.scan(
mIfaceName, settings.scanType, freqs, hiddenNetworkSSIDSet);
if (!success) {
Log.e(TAG, "Failed to start scan, freqs=" + freqs);
}
} else {
// There is a scan request but no available channels could be scanned for.
// We regard it as a scan failure in this case.
Log.e(TAG, "Failed to start scan because there is no available channel to scan");
}
if (success) {
if (DBG) {
Log.d(TAG, "Starting wifi scan for freqs=" + freqs);
}
mScanTimeoutListener = new AlarmManager.OnAlarmListener() {
@Override public void onAlarm() {
handleScanTimeout();
}
};
mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
mClock.getElapsedSinceBootMillis() + SCAN_TIMEOUT_MS,
TIMEOUT_ALARM_TAG, mScanTimeoutListener, mEventHandler);
} else {
// indicate scan failure async
mEventHandler.post(new Runnable() {
@Override public void run() {
reportScanFailure();
}
});
}
return true;
}
}
在startSingleScan方法里面会调用WifiNative里面的scan方法(mWifiNative.scan)。
xref: /frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiNative.java
public boolean scan(
@NonNull String ifaceName, int scanType, Set<Integer> freqs,
List<String> hiddenNetworkSSIDs) {
return mWificondControl.scan(ifaceName, scanType, freqs, hiddenNetworkSSIDs);
}
调用WificondControl类的scan方法
xref: /frameworks/opt/net/wifi/service/java/com/android/server/wifi/WificondControl.java
public boolean scan(@NonNull String ifaceName,
int scanType,
Set<Integer> freqs,
List<String> hiddenNetworkSSIDs) {
IWifiScannerImpl scannerImpl = getScannerImpl(ifaceName);
if (scannerImpl == null) {
Log.e(TAG, "No valid wificond scanner interface handler");
return false;
}
SingleScanSettings settings = new SingleScanSettings();
try {
settings.scanType = getScanType(scanType);
} catch (IllegalArgumentException e) {
Log.e(TAG, "Invalid scan type ", e);
return false;
}
settings.channelSettings = new ArrayList<>();
settings.hiddenNetworks = new ArrayList<>();
if (freqs != null) {
for (Integer freq : freqs) {
ChannelSettings channel = new ChannelSettings();
channel.frequency = freq;
settings.channelSettings.add(channel);
}
}
if (hiddenNetworkSSIDs != null) {
for (String ssid : hiddenNetworkSSIDs) {
HiddenNetwork network = new HiddenNetwork();
try {
network.ssid = NativeUtil.byteArrayFromArrayList(NativeUtil.decodeSsid(ssid));
} catch (IllegalArgumentException e) {
Log.e(TAG, "Illegal argument " + ssid, e);
continue;
}
// settings.hiddenNetworks is expected to be very small, so this shouldn't cause
// any performance issues.
if (!settings.hiddenNetworks.contains(network)) {
settings.hiddenNetworks.add(network);
}
}
}
try {
return scannerImpl.scan(settings);
} catch (RemoteException e1) {
Log.e(TAG, "Failed to request scan due to remote exception");
}
return false;
}
调用scanner_impl.cpp中的scan方法
xref: /system/connectivity/wificond/scanning/scanner_impl.cpp
Status ScannerImpl::scan(const SingleScanSettings& scan_settings,
bool* out_success) {
if (!CheckIsValid()) {
*out_success = false;
return Status::ok();
}
if (scan_started_) {
LOG(WARNING) << "Scan already started";
}
// Only request MAC address randomization when station is not associated.
bool request_random_mac =
wiphy_features_.supports_random_mac_oneshot_scan &&
!client_interface_->IsAssociated();
int scan_type = scan_settings.scan_type_;
if (!IsScanTypeSupported(scan_settings.scan_type_, wiphy_features_)) {
LOG(DEBUG) << "Ignoring scan type because device does not support it";
scan_type = SCAN_TYPE_DEFAULT;
}
// Initialize it with an empty ssid for a wild card scan.
vector<vector<uint8_t>> ssids = {{}};
vector<vector<uint8_t>> skipped_scan_ssids;
for (auto& network : scan_settings.hidden_networks_) {
if (ssids.size() + 1 > scan_capabilities_.max_num_scan_ssids) {
skipped_scan_ssids.emplace_back(network.ssid_);
continue;
}
ssids.push_back(network.ssid_);
}
LogSsidList(skipped_scan_ssids, "Skip scan ssid for single scan");
vector<uint32_t> freqs;
for (auto& channel : scan_settings.channel_settings_) {
freqs.push_back(channel.frequency_);
}
int error_code = 0;
if (!scan_utils_->Scan(interface_index_, request_random_mac, scan_type,
ssids, freqs, &error_code)) {
CHECK(error_code != ENODEV) << "Driver is in a bad state, restarting wificond";
*out_success = false;
return Status::ok();
}
scan_started_ = true;
*out_success = true;
return Status::ok();
}
Status ScannerImpl::startPnoScan(const PnoSettings& pno_settings,
bool* out_success) {
pno_settings_ = pno_settings;
pno_scan_results_from_offload_ = false;
LOG(VERBOSE) << "startPnoScan";
if (offload_scan_supported_ && StartPnoScanOffload(pno_settings)) {
// scanning over offload succeeded
*out_success = true;
} else {
*out_success = StartPnoScanDefault(pno_settings);
}
return Status::ok();
}
调用scan_utils.app的Scan方法
xref: /system/connectivity/wificond/scanning/scan_utils.cpp
bool ScanUtils::Scan(uint32_t interface_index,
bool request_random_mac,
int scan_type,
const vector<vector<uint8_t>>& ssids,
const vector<uint32_t>& freqs,
int* error_code) {
NL80211Packet trigger_scan(
netlink_manager_->GetFamilyId(),
NL80211_CMD_TRIGGER_SCAN,
netlink_manager_->GetSequenceNumber(),
getpid());
// If we do not use NLM_F_ACK, we only receive a unicast repsonse
// when there is an error. If everything is good, scan results notification
// will only be sent through multicast.
// If NLM_F_ACK is set, there will always be an unicast repsonse, either an
// ERROR or an ACK message. The handler will always be called and removed by
// NetlinkManager.
trigger_scan.AddFlag(NLM_F_ACK);
NL80211Attr<uint32_t> if_index_attr(NL80211_ATTR_IFINDEX, interface_index);
NL80211NestedAttr ssids_attr(NL80211_ATTR_SCAN_SSIDS);
for (size_t i = 0; i < ssids.size(); i++) {
ssids_attr.AddAttribute(NL80211Attr<vector<uint8_t>>(i, ssids[i]));
}
NL80211NestedAttr freqs_attr(NL80211_ATTR_SCAN_FREQUENCIES);
for (size_t i = 0; i < freqs.size(); i++) {
freqs_attr.AddAttribute(NL80211Attr<uint32_t>(i, freqs[i]));
}
trigger_scan.AddAttribute(if_index_attr);
trigger_scan.AddAttribute(ssids_attr);
// An absence of NL80211_ATTR_SCAN_FREQUENCIES attribue informs kernel to
// scan all supported frequencies.
if (!freqs.empty()) {
trigger_scan.AddAttribute(freqs_attr);
}
uint32_t scan_flags = 0;
if (request_random_mac) {
scan_flags |= NL80211_SCAN_FLAG_RANDOM_ADDR;
}
switch (scan_type) {
case IWifiScannerImpl::SCAN_TYPE_LOW_SPAN:
scan_flags |= NL80211_SCAN_FLAG_LOW_SPAN;
break;
case IWifiScannerImpl::SCAN_TYPE_LOW_POWER:
scan_flags |= NL80211_SCAN_FLAG_LOW_POWER;
break;
case IWifiScannerImpl::SCAN_TYPE_HIGH_ACCURACY:
scan_flags |= NL80211_SCAN_FLAG_HIGH_ACCURACY;
break;
case IWifiScannerImpl::SCAN_TYPE_DEFAULT:
break;
default:
CHECK(0) << "Invalid scan type received: " << scan_type;
}
if (scan_flags) {
trigger_scan.AddAttribute(
NL80211Attr<uint32_t>(NL80211_ATTR_SCAN_FLAGS,
scan_flags));
}
// We are receiving an ERROR/ACK message instead of the actual
// scan results here, so it is OK to expect a timely response because
// kernel is supposed to send the ERROR/ACK back before the scan starts.
vector<unique_ptr<const NL80211Packet>> response;
if (!netlink_manager_->SendMessageAndGetAckOrError(trigger_scan,
error_code)) {
// Logging is done inside |SendMessageAndGetAckOrError|.
return false;
}
if (*error_code != 0) {
LOG(ERROR) << "NL80211_CMD_TRIGGER_SCAN failed: " << strerror(*error_code);
return false;
}
return true;
}
通过trigger_scan方法将数据发送给内核开启wifi扫描。接下来我们分析ScanRequestProxyScanListener扫描监听回调。
xref: /frameworks/opt/net/wifi/service/java/com/android/server/wifi/ScanRequestProxy.java
private class GlobalScanListener implements WifiScanner.ScanListener {
@Override
public void onSuccess() {
// Ignore. These will be processed from the scan request listener.
}
@Override
public void onFailure(int reason, String description) {
// Ignore. These will be processed from the scan request listener.
}
@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...");
sendScanResultBroadcast(false);
return;
}
WifiScanner.ScanData scanData = scanDatas[0];
ScanResult[] scanResults = scanData.getResults();
if (mVerboseLoggingEnabled) {
Log.d(TAG, "Received " + scanResults.length + " scan results");
}
// Only process full band scan results.
if (scanData.getBandScanned() == WifiScanner.WIFI_BAND_BOTH_WITH_DFS) {
// Store the last scan results & send out the scan completion broadcast.
mLastScanResults.clear();
mLastScanResults.addAll(Arrays.asList(scanResults));
sendScanResultBroadcast(true);
}
}
@Override
public void onFullResult(ScanResult fullScanResult) {
// Ignore for single scans.
}
@Override
public void onPeriodChanged(int periodInMs) {
// Ignore for single scans.
}
};
// Common scan listener for scan requests initiated by this class.
private class ScanRequestProxyScanListener implements WifiScanner.ScanListener {
@Override
public void onSuccess() {
// Scan request succeeded, wait for results to report to external clients.
if (mVerboseLoggingEnabled) {
Log.d(TAG, "Scan request succeeded");
}
}
@Override
public void onFailure(int reason, String description) {
Log.e(TAG, "Scan failure received. reason: " + reason + ",description: " + description);
sendScanResultBroadcast(false);
}
@Override
public void onResults(WifiScanner.ScanData[] scanDatas) {
// Ignore. These will be processed from the global listener.
}
@Override
public void onFullResult(ScanResult fullScanResult) {
// Ignore for single scans.
}
@Override
public void onPeriodChanged(int periodInMs) {
// Ignore for single scans.
}
};
...................................................................................
/**
* Helper method to send the scan request status broadcast.
*/
private void sendScanResultBroadcast(boolean scanSucceeded) {
Intent intent = new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
intent.putExtra(WifiManager.EXTRA_RESULTS_UPDATED, scanSucceeded);
mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
}
在ScanRequestProxyScanListener的onResults方法里面有一行这样的注释(Ignore. These will be processed from the global listener.),这个方法已经丢弃了,我们看GlobalScanListener的onResults方法,onResult是底层完成扫描后的上报,把扫描结果添加到mLastScanResult中。调用sendScanResultBroadcast方法发送广播SCAN_RESULTS_AVAILABLE_ACTION通知WifiTracker获取扫描结果。
xref: /frameworks/base/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
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;
mLastScanSucceeded =
intent.getBooleanExtra(WifiManager.EXTRA_RESULTS_UPDATED, true);
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);
}
}
};
........................................................................................
private void fetchScansAndConfigsAndUpdateAccessPoints() {
List<ScanResult> newScanResults = mWifiManager.getScanResults();
// Filter all unsupported networks from the scan result list
final List<ScanResult> filteredScanResults =
filterScanResultsByCapabilities(newScanResults);
if (isVerboseLoggingEnabled()) {
Log.i(TAG, "Fetched scan results: " + filteredScanResults);
}
List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks();
updateAccessPoints(filteredScanResults, configs);
}
在mReceiver中走SCAN_RESULTS_AVAILABLE_ACTION分支,调用fetchScansAndConfigsAndUpdateAccessPoints()方法。通过mWifiManager.getScanResults获取扫描数据。
xref: /frameworks/base/wifi/java/android/net/wifi/WifiManager.java
public List<ScanResult> getScanResults() {
try {
return mService.getScanResults(mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
mService的具体实现类是WifiServiceImpl.java。所有我们直接去看WifiServiceImpl.java类里面的getScanResults方法
xref: /frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiServiceImpl.java
@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.getClientModeImplHandler().runWithScissors(() -> {
scanResults.addAll(mScanRequestProxy.getScanResults());
}, RUN_WITH_SCISSORS_TIMEOUT_MILLIS);
if (!success) {
Log.e(TAG, "Failed to post runnable to fetch scan results");
return new ArrayList<ScanResult>();
}
return scanResults;
} catch (SecurityException e) {
Slog.e(TAG, "Permission violation - getScanResults not allowed for uid="
+ uid + ", packageName=" + callingPackage + ", reason=" + e);
return new ArrayList<ScanResult>();
} finally {
Binder.restoreCallingIdentity(ident);
}
}
通过mScanRequestProxy.getScanResults()我们又回到了ScanRequestProxy.java里面。
xref: /frameworks/opt/net/wifi/service/java/com/android/server/wifi/ScanRequestProxy.java
public List<ScanResult> getScanResults() {
return mLastScanResults;
}
mLastScanResults就是我们之前在onResults方法里面遇到过的,就是之前广播接收到的扫描结果,mLastScanResults.addAll(Arrays.asList(scanResults))。
xref: /frameworks/base/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
private void fetchScansAndConfigsAndUpdateAccessPoints() {
List<ScanResult> newScanResults = mWifiManager.getScanResults();
// Filter all unsupported networks from the scan result list
final List<ScanResult> filteredScanResults =
filterScanResultsByCapabilities(newScanResults);
if (isVerboseLoggingEnabled()) {
Log.i(TAG, "Fetched scan results: " + filteredScanResults);
}
List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks();
updateAccessPoints(filteredScanResults, configs);
}
...........................................................................................
private void updateAccessPoints(final List<ScanResult> newScanResults,
List<WifiConfiguration> configs) {
// Map configs and scan results necessary to make AccessPoints
final Map<String, WifiConfiguration> configsByKey = new ArrayMap(configs.size());
if (configs != null) {
for (WifiConfiguration config : configs) {
configsByKey.put(AccessPoint.getKey(config), config);
}
}
WifiConfiguration connectionConfig = null;
if (mLastInfo != null) {
connectionConfig = getWifiConfigurationForNetworkId(mLastInfo.getNetworkId(), configs);
}
// Rather than dropping and reacquiring the lock multiple times in this method, we lock
// once for efficiency of lock acquisition time and readability
synchronized (mLock) {
ArrayMap<String, List<ScanResult>> scanResultsByApKey =
updateScanResultCache(newScanResults);
// Swap the current access points into a cached list for maintaining AP listeners
List<AccessPoint> cachedAccessPoints;
cachedAccessPoints = new ArrayList<>(mInternalAccessPoints);
ArrayList<AccessPoint> accessPoints = new ArrayList<>();
final List<NetworkKey> scoresToRequest = new ArrayList<>();
for (Map.Entry<String, List<ScanResult>> entry : scanResultsByApKey.entrySet()) {
for (ScanResult result : entry.getValue()) {
NetworkKey key = NetworkKey.createFromScanResult(result);
if (key != null && !mRequestedScores.contains(key)) {
scoresToRequest.add(key);
}
}
AccessPoint accessPoint =
getCachedOrCreate(entry.getValue(), cachedAccessPoints);
// Update the matching config if there is one, to populate saved network info
accessPoint.update(configsByKey.get(entry.getKey()));
accessPoints.add(accessPoint);
}
List<ScanResult> cachedScanResults = new ArrayList<>(mScanResultCache.values());
// Add a unique Passpoint AccessPoint for each Passpoint profile's FQDN.
accessPoints.addAll(updatePasspointAccessPoints(
mWifiManager.getAllMatchingWifiConfigs(cachedScanResults), cachedAccessPoints));
// Add OSU Provider AccessPoints
accessPoints.addAll(updateOsuAccessPoints(
mWifiManager.getMatchingOsuProviders(cachedScanResults), cachedAccessPoints));
if (mLastInfo != null && mLastNetworkInfo != null) {
for (AccessPoint ap : accessPoints) {
ap.update(connectionConfig, mLastInfo, mLastNetworkInfo);
}
}
// If there were no scan results, create an AP for the currently connected network (if
// it exists).
if (accessPoints.isEmpty() && connectionConfig != null) {
AccessPoint activeAp = new AccessPoint(mContext, connectionConfig);
activeAp.update(connectionConfig, mLastInfo, mLastNetworkInfo);
accessPoints.add(activeAp);
scoresToRequest.add(NetworkKey.createFromWifiInfo(mLastInfo));
}
requestScoresForNetworkKeys(scoresToRequest);
for (AccessPoint ap : accessPoints) {
ap.update(mScoreCache, mNetworkScoringUiEnabled, mMaxSpeedLabelScoreCacheAge);
}
// Pre-sort accessPoints to speed preference insertion
Collections.sort(accessPoints);
// Log accesspoints that are being removed
if (DBG()) {
Log.d(TAG,
"------ Dumping AccessPoints that were not seen on this scan ------");
for (AccessPoint prevAccessPoint : mInternalAccessPoints) {
String prevTitle = prevAccessPoint.getTitle();
boolean found = false;
for (AccessPoint newAccessPoint : accessPoints) {
if (newAccessPoint.getTitle() != null && newAccessPoint.getTitle()
.equals(prevTitle)) {
found = true;
break;
}
}
if (!found)
Log.d(TAG, "Did not find " + prevTitle + " in this scan");
}
Log.d(TAG,
"---- Done dumping AccessPoints that were not seen on this scan ----");
}
mInternalAccessPoints.clear();
mInternalAccessPoints.addAll(accessPoints);
}
conditionallyNotifyListeners();
}
再回到WifiTracker.java类里面的fetchScansAndConfigsAndUpdateAccessPoints方法。通过fetchScansAndConfigsAndUpdateAccessPoints拿到的扫描结果其实就是mLastScanResults。捋清楚这个流程很重要。接下来调用updateAccessPoints方法,将扫描结果添加到mInternalAccessPoints中。