赞
踩
请求WLAN芯片开始扫描,然后获取扫描结果,整个过程是分两个阶段:
本篇会介绍请求扫描阶段的流程,而后者会在下一篇中梳理;
//frameworks/base/wifi/java/android/net/wifi/WifiManager.java /** * Request a scan for access points. Returns immediately. The availability * of the results is made known later by means of an asynchronous event sent * on completion of the scan. * <p> * To initiate a Wi-Fi scan, declare the * {@link android.Manifest.permission#CHANGE_WIFI_STATE} * permission in the manifest, and perform these steps: * </p> * <ol style="1"> * <li>Invoke the following method: * {@code ((WifiManager) getSystemService(WIFI_SERVICE)).startScan()}</li> * <li> * Register a BroadcastReceiver to listen to * {@code SCAN_RESULTS_AVAILABLE_ACTION}.</li> * <li>When a broadcast is received, call: * {@code ((WifiManager) getSystemService(WIFI_SERVICE)).getScanResults()}</li> * </ol> * @return {@code true} if the operation succeeded, i.e., the scan was initiated. * @deprecated The ability for apps to trigger scan requests will be removed in a future * release. */ @Deprecated public boolean startScan() { return startScan(null); } /** @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public boolean startScan(WorkSource workSource) { try { String packageName = mContext.getOpPackageName(); return mService.startScan(packageName); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } }
注意:
SCAN_RESULTS_AVAILABLE_ACTION
广播通知;deprecated
,且表示,后续Android版本不再允许APP侧发起扫描请求(这个后面会尝试解释其原因)然后,我们来到IWifiManager.startScan()
的接口实现中(略去不重要的逻辑):
//frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiServiceImpl.java public boolean startScan(String packageName) { ... long ident = Binder.clearCallingIdentity(); ... try { //权限检查 mWifiPermissionsUtil.enforceCanAccessScanResults(packageName, callingUid); //用于在lambda代码块中更新字段(基本数据类型若需要在lambda代码块中使用,必须声明为final,这无法满足重新赋值的需求) Mutable<Boolean> scanSuccess = new Mutable<>(); //切换到WifiStateMachine执行,但在当前线程等待其完成,超时设定默认4s 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; } //如果runWithScissorsSuccess,则表示scanSuccess.value不可能为null,因此可以直接拆箱判断 if (!scanSuccess.value) { Log.e(TAG, "Failed to start scan"); return false; } } catch (SecurityException e) { return false; } finally { Binder.restoreCallingIdentity(ident); } return true; }
小结:
ScanRequestProxy.startScan()
中;WifiStateMachine
线程,但当前调用线程会等待其执行完成;startScan
方法的最终返回值;//frameworks/opt/net/wifi/service/java/com/android/server/wifi/ScanRequestProxy.java /** * Initiate a wifi scan. * * @param callingUid The uid initiating the wifi scan. Blame will be given to this uid. * @return true if the scan request was placed or a scan is already ongoing, false otherwise. */ public boolean startScan(int callingUid, String packageName) { //获取WifiScanner的实例对象 if (!retrieveWifiScannerIfNecessary()) { Log.e(TAG, "Failed to retrieve wifiscanner"); sendScanResultFailureBroadcastToPackage(packageName); return false; } //判断调用方是否具备android.Manifest.permission.NETWORK_SETTINGS或android.Manifest.permission.NETWORK_SETUP_WIZARD权限 boolean fromSettingsOrSetupWizard = mWifiPermissionsUtil.checkNetworkSettingsPermission(callingUid) || mWifiPermissionsUtil.checkNetworkSetupWizardPermission(callingUid); // 如果不具备上述两个权限,限制其在短时间内频繁请求扫描 if (!fromSettingsOrSetupWizard && shouldScanRequestBeThrottledForApp(callingUid, packageName)) { Log.i(TAG, "Scan request from " + packageName + " throttled"); sendScanResultFailureBroadcastToPackage(packageName); return false; } // 如果允许请求扫描,则继续 WorkSource workSource = new WorkSource(callingUid); WifiScanner.ScanSettings settings = new WifiScanner.ScanSettings(); // 具备上面两条权限的进程,使用高精度扫描 if (fromSettingsOrSetupWizard) { settings.type = WifiScanner.TYPE_HIGH_ACCURACY; } // 扫描包含所有支持的频段 settings.band = WifiScanner.WIFI_BAND_BOTH_WITH_DFS; settings.reportEvents = WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN | WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT; // 如果需要扫描隐藏网络,则将其添加到hiddenNetworks成员变量中; 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,表示有一个扫描请求正在进行; mIsScanProcessingComplete = false; return true; }
小结:
WifiScanner.startScan
发起扫描请求;ScanRequestProxyScanListener
用于监听扫描实际返回状态与结果;mIsScanProcessingComplete
的作用是,对于连续两次扫描请求的结果,只广播第一次的结果;(是否合理存疑)那么接下来,我们来到WifiScanner.startScan
:
//frameworks/base/wifi/java/android/net/wifi/WifiScanner.java /** * starts a single scan and reports results asynchronously * @param settings specifies various parameters for the scan; for more information look at * {@link ScanSettings} * @param listener specifies the object to report events to. This object is also treated as a * key for this scan, and must also be specified to cancel the scan. Multiple * scans should also not share this object. */ @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void startScan(ScanSettings settings, ScanListener listener) { startScan(settings, listener, null); } /** * starts a single scan and reports results asynchronously * @param settings specifies various parameters for the scan; for more information look at * {@link ScanSettings} * @param workSource WorkSource to blame for power usage * @param listener specifies the object to report events to. This object is also treated as a * key for this scan, and must also be specified to cancel the scan. Multiple * scans should also not share this object. */ @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); mAsyncChannel.sendMessage(CMD_START_SINGLE_SCAN, 0, key, scanParams); }
小结:
AsyncChannel
通知服务端响应消息CMD_START_SINGLE_SCAN
;ScanRequestProxy.startScan()
中封装的两个参数;AsyncChannel
的实现这里就不展开了,具体实现的讲解已经在准备中了,结论就是,这里的CMD_START_SINGLE_SCAN
,会在WifiScanningServiceImpl.ClientHandler.handleMessage()
中被处理;//frameworks/opt/net/wifi/service/java/com/android/server/wifi/scanner/WifiScanningServiceImpl.java private class ClientHandler extends WifiHandler { ... @Override public void handleMessage(Message msg) { super.handleMessage(msg); ... switch (msg.what) { ... case WifiScanner.CMD_START_SINGLE_SCAN: case WifiScanner.CMD_STOP_SINGLE_SCAN: mSingleScanStateMachine.sendMessage(Message.obtain(msg)); break; ... } }
略去了大量不相关的代码,结果发现实际上是通过WifiSingleScanStateMachine.sendMessage
将其转发到了其他线程处理(WifiScanningService
线程);
这里需要将WifiSingleScanStateMachine
状态机的工作情况介绍一下了:
DefaultState
;WifiManager.WIFI_SCAN_AVAILABLE
时,如果Intent
中key为WifiManager.EXTRA_SCAN_AVAILABLE
的值等于WifiManager.WIFI_STATE_ENABLED
,则发送消息CMD_DRIVER_LOADED
;WifiManager.WIFI_SCAN_AVAILABLE
时,如果Intent
中key为WifiManager.EXTRA_SCAN_AVAILABLE
的值等于WifiManager.WIFI_STATE_DISABLED
,则发送消息CMD_DRIVER_UNLOADED
;(此处不涉及,就不过多展开)CMD_DRIVER_LOADED
在DefaultState
中收到,会将状态机拨到IdleState
;IdleState
与ScanningState
两者之间切换;IdleState
拨到ScanningState
,扫描结束后复位到IdleState
;回到上面的内容,WifiScanningServiceImpl.ClientHandler
在发送的CMD_START_SINGLE_SCAN
,在如下几个状态内会被处理:
DefaultState
class DefaultState extends State { ... @Override public boolean processMessage(Message msg) { switch (msg.what) { ... case WifiScanner.CMD_START_SINGLE_SCAN: case WifiScanner.CMD_STOP_SINGLE_SCAN: replyFailed(msg, WifiScanner.REASON_UNSPECIFIED, "not available"); return HANDLED; ... default: return NOT_HANDLED; } } ... }
DriverStartedState
class DriverStartedState extends State { ... @Override public boolean processMessage(Message msg) { ClientInfo ci = mClients.get(msg.replyTo); switch (msg.what) { ... case WifiScanner.CMD_START_SINGLE_SCAN: mWifiMetrics.incrementOneshotScanCount(); int handler = msg.arg2; Bundle scanParams = (Bundle) msg.obj; ... 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)) { logScanRequest("addSingleScanRequest", ci, handler, workSource, scanSettings, null); replySucceeded(msg); // 如果当前正在进行扫描,则: // 1. 当前扫描可以满足这次请求(参数匹配),则会在此次扫描结果返回后,直接将其返回给请求方; // 2. 当前扫描不能满足这次请求,则会将其添加到等待队列中,待状态机拨回IdleState后,再触发扫描; // 如果当前没有进行扫描,则调用tryToStartNewScan请求扫描; 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 { ... } return HANDLED; case WifiScanner.CMD_STOP_SINGLE_SCAN: removeSingleScanRequest(ci, msg.arg2); return HANDLED; default: return NOT_HANDLED; } } ... }
不难看出,上面逻辑最终有效的调用是tryToStartNewScan()
//frameworks/opt/net/wifi/service/java/com/android/server/wifi/scanner/WifiScanningServiceImpl.java 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<>(); //将mPendingScans中所有保存的请求做参数合并(取并集) 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)) { mActiveScanSettings = settings; // 交换mActiveScans与mPendingScans变量的引用,并在交换后将mPendingScans清空 RequestList<ScanSettings> tmp = mActiveScans; mActiveScans = mPendingScans; mPendingScans = tmp; mPendingScans.clear(); // 状态拨至ScanningState transitionTo(mScanningState); } else { ... } }
以上逻辑梳理如下:
IdleState
还是ScanningState
,都没有在自己子状态内处理WifiScanner.CMD_START_SINGLE_SCAN
,而是在父状态DriverStartedState
中处理的;IdleState
状态,则将扫描请求封装后添加到mPendingScans
中,并调用tryToStartNewScan()
触发扫描;tryToStartNewScan()
主要调用ScanningState
状态,需要视情况而定:
mActiveScans
的扫描参数满足这次请求,则不再另行请求;mPendingScans
中;ScanningState
会清空mActiveScans
,并将状态拨回IdleState
;IdleState
在enter()
方法处,会再次调用tryToStartNewScan
,来将mPendingScans
中的请求通知到WifiScannerImpl
;最后,我们终于来到了WifiScannerImpl.startSingleScan()
这个抽象方法的实现:
WifiScannerImpl
有两个子类:
HalWifiScannerImpl
WificondScannerImpl
//frameworks/opt/net/wifi/service/java/com/android/server/wifi/scanner/WifiScannerImpl.java /** * 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(); String ifaceName = wifiNative.getClientInterfaceName(); if (TextUtils.isEmpty(ifaceName)) { return null; } if (wifiNative.getBgScanCapabilities( ifaceName, new WifiNative.ScanCapabilities())) { return new HalWifiScannerImpl(context, ifaceName, wifiNative, wifiMonitor, looper, clock); } else { return new WificondScannerImpl(context, ifaceName, wifiNative, wifiMonitor, new WificondChannelHelper(wifiNative), looper, clock); } } };
由于目前手边设备均是走的else
逻辑分支,即构造WificondScannerImpl
作为其实现,因此后面均以WificondScannerImpl
的逻辑为主;
//frameworks/opt/net/wifi/service/java/com/android/server/wifi/scanner/WificondScannerImpl.java @Override public boolean startSingleScan(WifiNative.ScanSettings settings, WifiNative.ScanEventHandler eventHandler) { synchronized (mSettingsLock) { //不允许多个扫描请求同时运行 if (mLastScanSettings != null) { Log.w(TAG, "A single scan is already running"); return false; } //处理参数,合并所有需要扫描的频段(根据之前逻辑,single scan应该只有一个bucket,且为全频段) 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); } //如果有需要扫描的隐藏网络,在此处处理添加 Set<String> hiddenNetworkSSIDSet = new HashSet<>(); 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,会在扫描失败,或扫描成功,且扫描结果成功取回后置为null 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); ... } else { ... } 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 { // indicate scan failure async mEventHandler.post(new Runnable() { @Override public void run() { reportScanFailure(); } }); } return true; } }
这个方法内也是进行了参数处理、合并,以及异常处理,而正常逻辑主要在WifiNative.scan()
:
//frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiNative.java
public boolean scan(
@NonNull String ifaceName, int scanType, Set<Integer> freqs,
Set<String> hiddenNetworkSSIDs) {
return mWificondControl.scan(ifaceName, scanType, freqs, hiddenNetworkSSIDs);
}
//frameworks/opt/net/wifi/service/java/com/android/server/wifi/WificondControl.java
public boolean scan(@NonNull String ifaceName,
int scanType,
Set<Integer> freqs,
Set<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;
}
关于getScannerImpl()
:
/** Helper function to look up the scanner impl handle using name */
private IWifiScannerImpl getScannerImpl(@NonNull String ifaceName) {
return mWificondScanners.get(ifaceName);
}
这里简单介绍下mWificondScanners
这个数据结构怎么来的:
在打开WLAN时,会调用到WificondControl.setupInterfaceForClientMode()
:
/** * Setup interface for client mode via wificond. * @return An IClientInterface as wificond client interface binder handler. * Returns null on failure. */ 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; } ... // Refresh Handlers mClientInterfaces.put(ifaceName, clientInterface); try { IWifiScannerImpl wificondScanner = clientInterface.getWifiScannerImpl(); ... mWificondScanners.put(ifaceName, wificondScanner); ... } catch (RemoteException e) { Log.e(TAG, "Failed to refresh wificond scanner due to remote exception"); } return clientInterface; }
这里涉及到wificond
了,也就是说上面的IWifiScannerImpl.scan()
已经是由wificond
在负责完成了;
篇幅有限,这里我们就不继续深入了;
以上是Android Framework层对于应用请求WLAN扫描的处理逻辑,主要是参数封装与合并,线程切换很多,并且也涉及到多个类的跳转;
整体来看,对于第一次了解的开发人员来看,是比较绕的;
但是梳理下来发现,大部分逻辑,都是针对一些特殊情况的处理(频繁重复请求)与优化(相同参数请求合并);
而对于有效的扫描请求,实际上逻辑分叉并不会多;
最后,老规矩,附上一张流程图:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。