Android开发之Wifi扫描分析

1,140 阅读8分钟

使用

开始wifi扫描的代码很简单:

val wifiManager \= context.getSystemService(Context.WIFI\_SERVICE) as WifiManager  
val success \= wifiManager.startScan()  
if (!success) {  
// scan failure handling  
scanFailure()  
}

然后定义一个receiver接收结果

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的数据未上一次的扫描结果

版本差异

  • Android 8以下: 未限制

  • Android 8.0 和 Android 8.1:
    每个后台应用可以在 30 分钟内扫描一次。 需要申明以下任意一项权限即可: ACCESS_FINE_LOCATION ACCESS_COARSE_LOCATION CHANGE_WIFI_STATE

  • Android 9:
    每个前台应用可以在 2 分钟内扫描四次。这样便可在短时间内进行多次扫描。 所有后台应用组合可以在 30 分钟内扫描一次。 需要申明以下所有权限: ACCESS_FINE_LOCATION 或 ACCESS_COARSE_LOCATION CHANGE_WIFI_STATE 设备已启用位置服务 (Settings > Location)。

  • Android 10 及更高版本:
    用 Android 9 的节流限制。新增一个开发者选项,用户可以关闭节流功能以便进行本地测试(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,调用的是WifiServiceImpl类 不同系统版本有不同实现

1. Android 6.0,7.0系统:

 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判定设备是否处于空闲状态

如果处于空闲,则不再真正扫描,而是调用WifiStateMachine发送最近可用的扫描结果

我们看下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连接的各种状态,以及所有事件的处理

其中维护了ScanModeState,DriverStartedState,DriverStartingState,ConnectModeState等等

在ScanModeState中的processMessage方法调用了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发送到WifiScanningServiceImpl中

mWifiScanner.startScan最终调用的是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状态下接受到scan请求,该次扫描失败。
  • 如果在ScanningState状态下接受到scan请求: 如果当前正在进行的扫描能满足需求,将请求加入active队列,否则加入挂起队列
  • 如果是其他状态直接加入挂起队列,并立即调用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;  
        }  
    }

可见在调用了mWifiNative.scan后,还设置了timeout机制,交给AlarmManager去执行

WifiNative中的scan调用的是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,数据在 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生成的接口,具体的实现在IWificond.cpp中,cpp中的内容此处就不做深入

2. Android 8.0,8.1系统:

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进程的调用,距上次调用< mBackgroundThrottleInterval,则sendFailedScanBroadcast

mBackgroundThrottleInterval参数是通过读取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系统:

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去执行,相比于之前版本省去了WifiStateMachine状态管理
  • 如果一个应用拥有networksetting权限(就是android中的设置才有的权限,一般应用不可能有)则可以不受任何限制地扫描,如果没有这个权限,这Wi-Fi扫描将会执行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中,每次调用这个方法将移除这个链表2分钟以前的时间戳,如果移除以后,这个链表仍然大于4,则取消本次扫描,否则将当前时间戳加入链表。

3. Android 10.0系统:

和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

总结

  • WifiManager通过aidl调用WifiServiceImpl,由app进程发送到系统进程
  • 在scan之前会检查permission
  • 9.0以下通过WifiStateMachine管理状态,9.0以上通过ScanRequestProxy代理,并交于WifiScanner
  • WifiScanner通过AsyncChannel中的Messenger将message发送到WifiScanningServiceImpl中
  • WifiScanningServiceImpl自身也有状态管理,mScannerImpl调用scan后,启动了超时机制
  • 最终由IWificond.aidl实现者IWificond.cpp实现scan操作 整理了下流程图:Android开发之   Wifi扫描分析_移动开发

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 里注册了一个监听器

并将此接口加入到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);  
            }  
            ...  
         }

当调用WifiScanner.getScanResults时会发送WifiScanner.CMD_GET_SCAN_RESULTS的message,当接收到WifiScanner.CMD_GET_SCAN_RESULTS的message时,会调用reportScanResults方法。