使用
開始wifi掃描的代碼很簡單:
val wifiManager = context.getSystemService(Context.WIFI_SERVICE) as WifiManager
val success = wifiManager.startScan()
if (!success) {
// scan failure handling
scanFailure()
}
然后定義一個receiver接收結(jié)果
val wifiScanReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val success = intent.getBooleanExtra(WifiManager.EXTRA_RESULTS_UPDATED, false)
if (success) {
val results = wifiManager.scanResults
} else {
scanFailure()
}
}
}
val intentFilter = IntentFilter()
intentFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)
context.registerReceiver(wifiScanReceiver, intentFilter)
注意:scanFailure時,wifiManager.scanResults的數(shù)據(jù)未上一次的掃描結(jié)果
版本差異
Android 8以下:
未限制Android 8.0 和 Android 8.1:
每個后臺應用可以在 30 分鐘內(nèi)掃描一次驻襟。
需要申明以下任意一項權限即可:
ACCESS_FINE_LOCATION
ACCESS_COARSE_LOCATION
CHANGE_WIFI_STATEAndroid 9:
每個前臺應用可以在 2 分鐘內(nèi)掃描四次告组。這樣便可在短時間內(nèi)進行多次掃描抒抬。
所有后臺應用組合可以在 30 分鐘內(nèi)掃描一次置侍。
需要申明以下所有權限:
ACCESS_FINE_LOCATION 或 ACCESS_COARSE_LOCATION
CHANGE_WIFI_STATE
設備已啟用位置服務 (Settings > Location)。Android 10 及更高版本:
用 Android 9 的節(jié)流限制衰伯。新增一個開發(fā)者選項铡羡,用戶可以關閉節(jié)流功能以便進行本地測試(Developer Options > Networking > Wi-Fi scan throttling)
target>=29,必須有 ACCESS_FINE_LOCATION
target<29意鲸,ACCESS_FINE_LOCATION 或 ACCESS_COARSE_LOCATION都可以
CHANGE_WIFI_STATE
設備已啟用位置服務 (Settings > Location)烦周。
源碼解析
startScan
WifiManager類中的startScan方法:
/** @hide */
@SystemApi
@RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
public boolean startScan(WorkSource workSource) {
try {
String packageName = mContext.getOpPackageName();
mService.startScan(null, workSource, packageName);
return true;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
最終通過IWifiManager.aidl尽爆,調(diào)用的是WifiServiceImpl類
不同系統(tǒng)版本有不同實現(xiàn)
1. Android 6.0,7.0系統(tǒng):
public void startScan(ScanSettings settings, WorkSource workSource) {
enforceChangePermission();
synchronized (this) {
if (mInIdleMode) {
// Need to send an immediate scan result broadcast in case the
// caller is waiting for a result ..
// clear calling identity to send broadcast
long callingIdentity = Binder.clearCallingIdentity();
try {
mWifiStateMachine.sendScanResultsAvailableBroadcast(/* scanSucceeded = */ false);
} finally {
// restore calling identity
Binder.restoreCallingIdentity(callingIdentity);
}
mScanPending = true;
return;
}
}
...
mWifiStateMachine.startScan(Binder.getCallingUid(), scanRequestCounter++,
settings, workSource);
}
enforceChangePermission是檢查是否有CHANGE_WIFI_STATE的權限
mInIdleMode由powermanager判定設備是否處于空閑狀態(tài)
如果處于空閑,則不再真正掃描读慎,而是調(diào)用WifiStateMachine發(fā)送最近可用的掃描結(jié)果
我們看下WifiStateMachine的代碼:
/**
* Track the state of Wifi connectivity. All event handling is done here,
* and all changes in connectivity state are initiated here.
*
* Wi-Fi now supports three modes of operation: Client, SoftAp and p2p
* In the current implementation, we support concurrent wifi p2p and wifi operation.
* The WifiStateMachine handles SoftAp and Client operations while WifiP2pService
* handles p2p operation.
*
* @hide
*/
public class WifiStateMachine extends StateMachine implements WifiNative.WifiRssiEventHandler {
...
public void startScan(int callingUid, int scanCounter,
ScanSettings settings, WorkSource workSource) {
Bundle bundle = new Bundle();
bundle.putParcelable(CUSTOMIZED_SCAN_SETTING, settings);
bundle.putParcelable(CUSTOMIZED_SCAN_WORKSOURCE, workSource);
bundle.putLong(SCAN_REQUEST_TIME, System.currentTimeMillis());
sendMessage(CMD_START_SCAN, callingUid, scanCounter, bundle);
}
...
}
這個類主要維護Wifi連接的各種狀態(tài)漱贱,以及所有事件的處理
其中維護了ScanModeState,DriverStartedState夭委,DriverStartingState,ConnectModeState等等
在ScanModeState中的processMessage方法調(diào)用了handleScanRequest方法:
class ScanModeState extends State {
...
@Override
public boolean processMessage(Message message) {
// Handle scan. All the connection related commands are
// handled only in ConnectModeState
case CMD_START_SCAN:
handleScanRequest(message);
break;
}
}
private void handleScanRequest(Message message) {
...
// call wifi native to start the scan
if (startScanNative(freqs, hiddenNetworkIds, workSource)) {
// a full scan covers everything, clearing scan request buffer
if (freqs == null)
mBufferedScanMsg.clear();
messageHandlingStatus = MESSAGE_HANDLING_STATUS_OK;
if (workSource != null) {
// External worksource was passed along the scan request,
// hence always send a broadcast
mSendScanResultsBroadcast = true;
}
return;
}
....
}
private boolean startScanNative(final Set<Integer> freqs, Set<Integer> hiddenNetworkIds,
WorkSource workSource) {
...
WifiScanner.ScanListener nativeScanListener = new WifiScanner.ScanListener() {
// ignore all events since WifiStateMachine is registered for the supplicant events
public void onSuccess() {
}
public void onFailure(int reason, String description) {
mIsScanOngoing = false;
mIsFullScanOngoing = false;
}
public void onResults(WifiScanner.ScanData[] results) {
}
public void onFullResult(ScanResult fullScanResult) {
}
public void onPeriodChanged(int periodInMs) {
}
};
mWifiScanner.startScan(settings, nativeScanListener, workSource);
...
}
WifiScanner中startScan方法幅狮,通過AsyncChannel中的Messenger將message發(fā)送到WifiScanningServiceImpl中
mWifiScanner.startScan最終調(diào)用的是WifiScanningServiceImpl中:
public class WifiScanningServiceImpl extends IWifiScanner.Stub {
...
class DriverStartedState extends State {
@Override
public boolean processMessage(Message msg) {
case WifiScanner.CMD_START_SINGLE_SCAN:
if (validateScanRequest(ci, handler, scanSettings)) {
...
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 {
...
}
}
}
void tryToStartNewScan() {
...
if (mScannerImpl.startSingleScan(settings, this)) {
...
} else {
....
}
}
...
}
- 如果在DefaultState狀態(tài)下接受到scan請求,該次掃描失敗株灸。
- 如果在ScanningState狀態(tài)下接受到scan請求: 如果當前正在進行的掃描能滿足需求崇摄,將請求加入active隊列,否則加入掛起隊列
- 如果是其他狀態(tài)直接加入掛起隊列慌烧,并立即調(diào)用tryToStartNewScan()
mScannerImpl通過工廠方法生成的實例為WificondScannerImpl逐抑,在WificondScannerImpl中startSingleScan:
@Override
public boolean startSingleScan(WifiNative.ScanSettings settings,
WifiNative.ScanEventHandler eventHandler) {
synchronized (mSettingsLock) {
...
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) {
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 {
...
}
return true;
}
}
可見在調(diào)用了mWifiNative.scan后,還設置了timeout機制屹蚊,交給AlarmManager去執(zhí)行
WifiNative中的scan調(diào)用的是WificondControl中的scan方法厕氨,我們看下WificondControl中:
public boolean scan(@NonNull String ifaceName,
int scanType,
Set<Integer> freqs,
List<String> hiddenNetworkSSIDs) {
IWifiScannerImpl scannerImpl = getScannerImpl(ifaceName);
...
try {
return scannerImpl.scan(settings);
} catch (RemoteException e1) {
Log.e(TAG, "Failed to request scan due to remote exception");
}
return false;
}
private IWifiScannerImpl getScannerImpl(@NonNull String ifaceName) {
return mWificondScanners.get(ifaceName);
}
mWificondScanners是個hashmap,數(shù)據(jù)在 setupInterfaceForClientMode方法中put進去:
public IClientInterface setupInterfaceForClientMode(@NonNull String ifaceName) {
IClientInterface clientInterface = null;
try {
clientInterface = mWificond.createClientInterface(ifaceName);
} catch (RemoteException e1) {
Log.e(TAG, "Failed to get IClientInterface due to remote exception");
return null;
}
...
try {
IWifiScannerImpl wificondScanner = clientInterface.getWifiScannerImpl();
if (wificondScanner == null) {
Log.e(TAG, "Failed to get WificondScannerImpl");
return null;
}
mWificondScanners.put(ifaceName, wificondScanner);
...
} catch (RemoteException e) {
Log.e(TAG, "Failed to refresh wificond scanner due to remote exception");
}
return clientInterface;
}
mWificond的類型為IWificond汹粤,clientInterface的類型為IClientInterface命斧,這兩個都是aidl生成的接口,具體的實現(xiàn)在IWificond.cpp中嘱兼,cpp中的內(nèi)容此處就不做深入
2. Android 8.0,8.1系統(tǒng):
WifiServiceImpl類中:
@Override
public void startScan(ScanSettings settings, WorkSource workSource, String packageName) {
enforceChangePermission();
mLog.trace("startScan uid=%").c(Binder.getCallingUid()).flush();
// Check and throttle background apps for wifi scan.
if (isRequestFromBackground(packageName)) {
long lastScanMs = mLastScanTimestamps.getOrDefault(packageName, 0L);
long elapsedRealtime = mClock.getElapsedSinceBootMillis();
if (lastScanMs != 0 && (elapsedRealtime - lastScanMs) < mBackgroundThrottleInterval) {
sendFailedScanBroadcast();
return;
}
// Proceed with the scan request and record the time.
mLastScanTimestamps.put(packageName, elapsedRealtime);
}
synchronized (this) {
if (mWifiScanner == null) {
mWifiScanner = mWifiInjector.getWifiScanner();
}
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;
}
}
...
mWifiStateMachine.startScan(Binder.getCallingUid(), scanRequestCounter++,
settings, workSource);
}
可見多了一個isRequestFromBackground操作国葬,如果是background進程的調(diào)用,距上次調(diào)用< mBackgroundThrottleInterval遭京,則sendFailedScanBroadcast
mBackgroundThrottleInterval參數(shù)是通過讀取framework中的設置,這個值默認為30分鐘
private void updateBackgroundThrottleInterval() {
mBackgroundThrottleInterval = mFrameworkFacade.getLongSetting(
mContext,
Settings.Global.WIFI_SCAN_BACKGROUND_THROTTLE_INTERVAL_MS,
DEFAULT_SCAN_BACKGROUND_THROTTLE_INTERVAL_MS);
}
3. Android 9.0系統(tǒng):
WifiServiceImpl類中:
@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.getWifiStateMachineHandler()
.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) {
return false;
} finally {
Binder.restoreCallingIdentity(ident);
}
return true;
}
mWifiPermissionsUtil.enforceCanAccessScanResults 胃惜,permission判斷對比8.0有更新,WifiPermissionsUtil會判斷各種所需權限
scan交給mScanRequestProxy去做:
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 from apps without NETWORK_SETTINGS permission.
if (!fromSettingsOrSetupWizard
&& 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);
// 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);
mIsScanProcessingComplete = false;
return true;
return true;
}
- 這里直接交給WifiScanner去執(zhí)行哪雕,相比于之前版本省去了WifiStateMachine狀態(tài)管理
- 如果一個應用擁有networksetting權限(就是android中的設置才有的權限船殉,一般應用不可能有)則可以不受任何限制地掃描,如果沒有這個權限斯嚎,這Wi-Fi掃描將會執(zhí)行shouldScanRequestBeThrottledForApp利虫,如果返回為ture,則sendScanResultFailureBroadcastToPackage
看下shouldScanRequestBeThrottledForApp做了什么:
private boolean shouldScanRequestBeThrottledForApp(int callingUid, String packageName) {
boolean isThrottled;
if (isRequestFromBackground(callingUid, packageName)) {
isThrottled = shouldScanRequestBeThrottledForBackgroundApp();
if (isThrottled) {
if (mVerboseLoggingEnabled) {
Log.v(TAG, "Background scan app request [" + callingUid + ", "
+ packageName + "]");
}
mWifiMetrics.incrementExternalBackgroundAppOneshotScanRequestsThrottledCount();
}
} else {
isThrottled = shouldScanRequestBeThrottledForForegroundApp(callingUid, packageName);
if (isThrottled) {
if (mVerboseLoggingEnabled) {
Log.v(TAG, "Foreground scan app request [" + callingUid + ", "
+ packageName + "]");
}
mWifiMetrics.incrementExternalForegroundAppOneshotScanRequestsThrottledCount();
}
}
mWifiMetrics.incrementExternalAppOneshotScanRequestsCount();
return isThrottled;
}
private boolean shouldScanRequestBeThrottledForBackgroundApp() {
long lastScanMs = mLastScanTimestampForBgApps;
long elapsedRealtime = mClock.getElapsedSinceBootMillis();
if (lastScanMs != 0
&& (elapsedRealtime - lastScanMs) < SCAN_REQUEST_THROTTLE_INTERVAL_BG_APPS_MS) {
return true;
}
// Proceed with the scan request and record the time.
mLastScanTimestampForBgApps = elapsedRealtime;
return false;
}
private boolean shouldScanRequestBeThrottledForForegroundApp(
int callingUid, String packageName) {
LinkedList<Long> scanRequestTimestamps =
getOrCreateScanRequestTimestampsForForegroundApp(callingUid, packageName);
long currentTimeMillis = mClock.getElapsedSinceBootMillis();
// First evict old entries from the list.
trimPastScanRequestTimesForForegroundApp(scanRequestTimestamps, currentTimeMillis);
if (scanRequestTimestamps.size() >= SCAN_REQUEST_THROTTLE_MAX_IN_TIME_WINDOW_FG_APPS) {
return true;
}
// Proceed with the scan request and record the time.
scanRequestTimestamps.addLast(currentTimeMillis);
return false;
}
- 判斷應用為前臺應該還是后臺應用(前后臺限制在這里)
- 后臺應用限制SCAN_REQUEST_THROTTLE_INTERVAL_BG_APPS_MS未30分鐘
- 前臺應用方法獲取了一個保存每次掃描時間戳的鏈表scanRequestTimestamps 堡僻,每個應用又以不同的鏈表uid為鍵將自己的鏈表保存在一個設備全局的map中糠惫,每次調(diào)用這個方法將移除這個鏈表2分鐘以前的時間戳,如果移除以后钉疫,這個鏈表仍然大于4硼讽,則取消本次掃描,否則將當前時間戳加入鏈表牲阁。
3. Android 10.0系統(tǒng):
和Android 9相比固阁,Android10 在enforceCanAccessScanResults 中作了更多判斷:
public class WifiPermissionsUtil {
public void enforceCanAccessScanResults(String pkgName, int uid) throws SecurityException {
checkPackage(uid, pkgName);
// Apps with NETWORK_SETTINGS, NETWORK_SETUP_WIZARD, NETWORK_MANAGED_PROVISIONING,
// NETWORK_STACK & MAINLINE_NETWORK_STACK are granted a bypass.
if (checkNetworkSettingsPermission(uid) || checkNetworkSetupWizardPermission(uid)
|| checkNetworkManagedProvisioningPermission(uid)
|| checkNetworkStackPermission(uid) || checkMainlineNetworkStackPermission(uid)) {
return;
}
// Location mode must be enabled
if (!isLocationModeEnabled()) {
// Location mode is disabled, scan results cannot be returned
throw new SecurityException("Location mode is disabled for the device");
}
// Check if the calling Uid has CAN_READ_PEER_MAC_ADDRESS permission.
boolean canCallingUidAccessLocation = checkCallerHasPeersMacAddressPermission(uid);
// LocationAccess by App: caller must have Coarse/Fine Location permission to have access to
// location information.
boolean canAppPackageUseLocation = checkCallersLocationPermission(pkgName,
uid, /* coarseForTargetSdkLessThanQ */ true);
// If neither caller or app has location access, there is no need to check
// any other permissions. Deny access to scan results.
if (!canCallingUidAccessLocation && !canAppPackageUseLocation) {
throw new SecurityException("UID " + uid + " has no location permission");
}
// Check if Wifi Scan request is an operation allowed for this App.
if (!isScanAllowedbyApps(pkgName, uid)) {
throw new SecurityException("UID " + uid + " has no wifi scan permission");
}
// If the User or profile is current, permission is granted
// Otherwise, uid must have INTERACT_ACROSS_USERS_FULL permission.
if (!isCurrentProfile(uid) && !checkInteractAcrossUsersFull(uid)) {
throw new SecurityException("UID " + uid + " profile not permitted");
}
}
}
多了checkNetworkManagedProvisioningPermission壤躲,checkNetworkStackPermission和checkMainlineNetworkStackPermission
總結(jié)
- WifiManager通過aidl調(diào)用WifiServiceImpl,由app進程發(fā)送到系統(tǒng)進程
- 在scan之前會檢查permission
- 9.0以下通過WifiStateMachine管理狀態(tài)备燃,9.0以上通過ScanRequestProxy代理碉克,并交于WifiScanner
- WifiScanner通過AsyncChannel中的Messenger將message發(fā)送到WifiScanningServiceImpl中
- WifiScanningServiceImpl自身也有狀態(tài)管理,mScannerImpl調(diào)用scan后并齐,啟動了超時機制
-
最終由IWificond.aidl實現(xiàn)者IWificond.cpp實現(xiàn)scan操作
整理了下流程圖:
getScanResults
WifiServiceImpl中:
@Override
public List<ScanResult> getScanResults(String callingPackage) {
enforceAccessPermission();
...
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);
}
}
也做了permisson的判斷
ScanRequestProxy中的mLastScanResults是如何set的呢:
private class GlobalScanListener implements WifiScanner.ScanListener {
@Override
public void onResults(WifiScanner.ScanData[] scanDatas) {
...
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);
}
}
}
private boolean retrieveWifiScannerIfNecessary() {
if (mWifiScanner == null) {
mWifiScanner = mWifiInjector.getWifiScanner();
// Start listening for throttle settings change after we retrieve scanner instance.
mThrottleEnabledSettingObserver.initialize();
// Register the global scan listener.
if (mWifiScanner != null) {
mWifiScanner.registerScanListener(new GlobalScanListener());
}
}
return mWifiScanner != null;
}
在WifiScanningServiceImpl 里注冊了一個監(jiān)聽器
并將此接口加入到mSingleScanListeners中:
case WifiScanner.CMD_REGISTER_SCAN_LISTENER:
logScanRequest("registerScanListener", ci, msg.arg2, null, null, null);
mSingleScanListeners.addRequest(ci, msg.arg2, null, null);
replySucceeded(msg);
break;
在方法reportScanResults中會遍歷mSingleScanListeners:
void reportScanResults(ScanData results) {
...
for (RequestInfo<Void> entry : mSingleScanListeners) {
logCallback("singleScanResults", entry.clientInfo, entry.handlerId,
describeForLog(allResults));
entry.reportEvent(WifiScanner.CMD_SCAN_RESULT, 0, parcelableAllResults);
}
...
}
當調(diào)用WifiScanner.getScanResults時會發(fā)送WifiScanner.CMD_GET_SCAN_RESULTS的message漏麦,當接收到WifiScanner.CMD_GET_SCAN_RESULTS的message時,會調(diào)用reportScanResults方法