Android N Wi-Fi 扫描流程分析
2017-08-30 15:46
519 查看
上一篇我们分析了wifi的启动流程,最终 WifiStateMachine 是停留在了DisconnectedState 状态中。 接下来我们就来分析wifi是怎么进行扫描的。这就要从 WifiSettings 的 onResume 函数说起。
@Override public void onResume() { final Activity activity = getActivity(); super.onResume(); removePreference("dummy"); if (mWifiEnabler != null) { mWifiEnabler.resume(activity); } mWifiTracker.startTracking(); activity.invalidateOptionsMenu(); /// M: register no WAPI certification action receiver activity.registerReceiver(mReceiver, mFilter); mWifiSettingsExt.onResume(); }
在这个函数中,执行了 WifiTracker.startTracking() 函数。
public void startTracking() { resumeScanning(); if (!mRegistered) { mContext.registerReceiver(mReceiver, mFilter); // NetworkCallback objects cannot be reused. http://b/20701525 . mNetworkCallback = new WifiTrackerNetworkCallback(); mConnectivityManager.registerNetworkCallback(mNetworkRequest, mNetworkCallback); mRegistered = true; } } public void resumeScanning() { if (mScanner == null) { mScanner = new Scanner(); } mWorkHandler.sendEmptyMessage(WorkHandler.MSG_RESUME); if (mWifiManager.isWifiEnabled()) { mScanner.resume(); } mWorkHandler.sendEmptyMessage(WorkHandler.MSG_UPDATE_ACCESS_POINTS); }
其中 resumeScanning() 函数发送了一条消息 orkHandler.MSG_RESUME 。
case MSG_RESUME: handleResume(); break; private void handleResume() { mScanResultCache.clear(); mSeenBssids.clear(); mScanId = 0; }
这个函数是为了清空当前 ScanResultCache 中的信息,紧接着将执行 mScanner.resume() 函数。
void resume() { if (!hasMessages(MSG_SCAN)) { sendEmptyMessage(MSG_SCAN); } } @Override public void handleMessage(Message message) { if (message.what != MSG_SCAN) return; if (mWifiManager.startScan()) { mRetry = 0; } else if (++mRetry >= 3) { mRetry = 0; if (mContext != null) { Toast.makeText(mContext, R.string.wifi_fail_to_scan, Toast.LENGTH_LONG).show(); } return; } sendEmptyMessageDelayed(0, WIFI_RESCAN_INTERVAL_MS); }
这个函数将先发送一条 MSG_SCAN 消息,然后将执行 mWifiManager.startScan() 函数。
public boolean startScan() { try { mService.startScan(null, null); return true; } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } }
而 WifiManager 将通过aidl 调用 wifiserviceimpl 的同名函数。
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; } } if (settings != null) { settings = new ScanSettings(settings); if (!settings.isValid()) { Slog.e(TAG, "invalid scan setting"); return; } } if (workSource != null) { enforceWorkSourcePermission(); // WifiManager currently doesn't use names, so need to clear names out of the // supplied WorkSource to allow future WorkSource combining. workSource.clearNames(); } if (workSource == null && Binder.getCallingUid() >= 0) { workSource = new WorkSource(Binder.getCallingUid()); } mWifiStateMachine.startScan(Binder.getCallingUid(), scanRequestCounter++, settings, workSource); }
此函数最后调用 WifiStateMachine.startScan()。
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); }
此函数将往 WifiStateMachine 中发送 CMD_START_SCAN 消息。由于 DisconnectedState 状态不处理, 此消息将在 DriverStartedState 中被处理。
case CMD_START_SCAN: handleScanRequest(message); break; private void handleScanRequest(Message message) { ScanSettings settings = null; WorkSource workSource = null; // unbundle parameters Bundle bundle = (Bundle) message.obj; if (bundle != null) { settings = bundle.getParcelable(CUSTOMIZED_SCAN_SETTING); workSource = bundle.getParcelable(CUSTOMIZED_SCAN_WORKSOURCE); } Set<Integer> freqs = null; if (settings != null && settings.channelSet != null) { freqs = new HashSet<Integer>(); for (WifiChannel channel : settings.channelSet) { freqs.add(channel.freqMHz); } } // Retrieve the list of hidden networkId's to scan for. Set<Integer> hiddenNetworkIds = mWifiConfigManager.getHiddenConfiguredNetworkIds(); // 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; } // if reach here, scan request is rejected if (!mIsScanOngoing) { // if rejection is NOT due to ongoing scan (e.g. bad scan parameters), // discard this request and pop up the next one if (mBufferedScanMsg.size() > 0) { sendMessage(mBufferedScanMsg.remove()); } messageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD; } else if (!mIsFullScanOngoing) { // if rejection is due to an ongoing scan, and the ongoing one is NOT a full scan, // buffer the scan request to make sure specified channels will be scanned eventually if (freqs == null) mBufferedScanMsg.clear(); if (mBufferedScanMsg.size() < SCAN_REQUEST_BUFFER_MAX_SIZE) { Message msg = obtainMessage(CMD_START_SCAN, message.arg1, message.arg2, bundle); mBufferedScanMsg.add(msg); } else { // if too many requests in buffer, combine them into a single full scan bundle = new Bundle(); bundle.putParcelable(CUSTOMIZED_SCAN_SETTING, null); bundle.putParcelable(CUSTOMIZED_SCAN_WORKSOURCE, workSource); Message msg = obtainMessage(CMD_START_SCAN, message.arg1, message.arg2, bundle); mBufferedScanMsg.clear(); mBufferedScanMsg.add(msg); } messageHandlingStatus = MESSAGE_HANDLING_STATUS_LOOPED; } else { // mIsScanOngoing and mIsFullScanOngoing messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL; } }
这里 startScanNative 函数将 调用 WifiScanner.startScan 函数。
public void startScan(ScanSettings settings, ScanListener listener, WorkSource workSource) { Log.d(TAG, "startScan, pid:" + Process.myPid() + ", tid:" + Process.myTid() + ", uid:" + Process.myUid()); 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); }
此函数将发送 CMD_START_SINGLE_SCAN 消息到 WifiScanningServiceImpl 中实现。
case WifiScanner.CMD_START_SINGLE_SCAN: case WifiScanner.CMD_STOP_SINGLE_SCAN: mSingleScanStateMachine.sendMessage(Message.obtain(msg)); break;
最终将调用 mWifiNative.scan进行扫描网络热入点。扫描结束后,WifiMonitor.handleEvent 将收到SCAN_RESULTS 消息。
case SCAN_RESULTS: sendMessage(iface, SCAN_RESULTS_EVENT); break;
WifiMonitor 将向 WifiStateMachine 中发送 SCAN_RESULTS_EVENT 消息。 由于 DisconnectedState 状态不执行此消息,最终 由 SupplicantStartedState 状态来执行此消息。
case WifiMonitor.SCAN_RESULTS_EVENT: case WifiMonitor.SCAN_FAILED_EVENT: maybeRegisterNetworkFactory(); // Make sure our NetworkFactory is registered setScanResults(); ///M: for operator plugin @{ if (hasCustomizedAutoConnect()) { mShowReselectDialog = false; Log.d(TAG, "SCAN_RESULTS_EVENT, mScanForWeakSignal:" + mScanForWeakSignal); if (mScanForWeakSignal) { showReselectionDialog(); } mDisconnectNetworkId = INVALID_NETWORK_ID; } ///@} loge("mIsFullScanOngoing: " + mIsFullScanOngoing + ", mSendScanResultsBroadcast: " + mSendScanResultsBroadcast); ///M: ALPS02028415 the last two times scan results should be broadcasted @{ if (mIsFullScanOngoing || mSendScanResultsBroadcast || mWifiOnScanCount < 2) { loge("mWifiOnScanCount: " + mWifiOnScanCount); /* Just updated results from full scan, let apps know a c440 bout this */ boolean scanSucceeded = message.what == WifiMonitor.SCAN_RESULTS_EVENT; sendScanResultsAvailableBroadcast(scanSucceeded);//发送扫描成功广播 } mWifiOnScanCount ++; ///@} mSendScanResultsBroadcast = false; mIsScanOngoing = false; mIsFullScanOngoing = false; if (mBufferedScanMsg.size() > 0) sendMessage(mBufferedScanMsg.remove()); break;
setScanResults()函数将会调用 WifiNative.getScanResults() 去获取扫描结果, sendScanResultsAvailableBroadcast(scanSucceeded); 随后将发送 WifiManager.SCAN_RESULTS_AVAILABLE_ACTION 广播。 WifiTracker 接收到此广播后,将发送 MSG_UPDATE_ACCESS_POINTS 消息。
@VisibleForTesting 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) || WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION.equals(action) || WifiManager.LINK_CONFIGURATION_CHANGED_ACTION.equals(action)) { mWorkHandler.sendEmptyMessage(WorkHandler.MSG_UPDATE_ACCESS_POINTS); } else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) { NetworkInfo info = (NetworkInfo) intent.getParcelableExtra( WifiManager.EXTRA_NETWORK_INFO); mConnected.set(info.isConnected()); mMainHandler.sendEmptyMessage(MainHandler.MSG_CONNECTED_CHANGED); mWorkHandler.sendEmptyMessage(WorkHandler.MSG_UPDATE_ACCESS_POINTS); mWorkHandler.obtainMessage(WorkHandler.MSG_UPDATE_NETWORK_INFO, info) .sendToTarget(); } } };
WifiTracker 的 WorkHandler 接收到此消息将进行 更新AP操作。
private void updateAccessPoints() { // Swap the current access points into a cached list. List<AccessPoint> cachedAccessPoints = getAccessPoints(); ArrayList<AccessPoint> accessPoints = new ArrayList<>(); // Clear out the configs so we don't think something is saved when it isn't. for (AccessPoint accessPoint : cachedAccessPoints) { accessPoint.clearConfig(); } /// M: ALPS02334170: Use ReadWriteLock to prevent multi-thread JE @{ mReadWriteLock.readLock().lock(); try { /** Lookup table to more quickly update AccessPoints by only considering objects with * the correct SSID. Maps SSID -> List of AccessPoints with the given SSID. */ Multimap<String, AccessPoint> apMap = new Multimap<String, AccessPoint>(); WifiConfiguration connectionConfig = null; if (mLastInfo != null) { connectionConfig = getWifiConfigurationForNetworkId(mLastInfo.getNetworkId()); } final Collection<ScanResult> results = fetchScanResults(); final List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks(); if (configs != null) { mSavedNetworksExist = configs.size() != 0; for (WifiConfiguration config : configs) { if (config.selfAdded && config.numAssociation == 0) { continue; } AccessPoint accessPoint = getCachedOrCreate(config, cachedAccessPoints); if (mLastInfo != null && mLastNetworkInfo != null) { if (config.isPasspoint() == false) { accessPoint.update(connectionConfig, mLastInfo, mLastNetworkInfo); } } if (mIncludeSaved) { if (!config.isPasspoint() || mIncludePasspoints) { // If saved network not present in scan result then set its Rssi // to MAX_VALUE boolean apFound = false; for (ScanResult result : results) { if (result.SSID.equals(accessPoint.getSsidStr())) { apFound = true; break; } } if (!apFound) { accessPoint.setRssi(Integer.MAX_VALUE); } accessPoints.add(accessPoint); } if (config.isPasspoint() == false) { apMap.put(accessPoint.getSsidStr(), accessPoint); } } else { // If we aren't using saved networks, drop them into the cache so that // we have access to their saved info. cachedAccessPoints.add(accessPoint); } } } if (results != null) { for (ScanResult result : results) { // Ignore hidden and ad-hoc networks. if (result.SSID == null || result.SSID.length() == 0 || result.capabilities.contains("[IBSS]")) { continue; } boolean found = false; for (AccessPoint accessPoint : apMap.getAll(result.SSID)) { if (accessPoint.update(result)) { found = true; break; } } if (!found && mIncludeScans) { AccessPoint accessPoint = getCachedOrCreate(result, cachedAccessPoints); if (mLastInfo != null && mLastNetworkInfo != null) { accessPoint.update(connectionConfig, mLastInfo, mLastNetworkInfo); } if (result.isPasspointNetwork()) { WifiConfiguration config = mWifiManager.getMatchingWifiConfig(result); if (config != null) { accessPoint.update(config); } } if (mLastInfo != null && mLastInfo.getBSSID() != null && mLastInfo.getBSSID().equals(result.BSSID) && connectionConfig != null && connectionConfig.isPasspoint()) { /* This network is connected via this passpoint config */ /* SSID match is not going to work for it; so update explicitly */ accessPoint.update(connectionConfig); } accessPoints.add(accessPoint); apMap.put(accessPoint.getSsidStr(), accessPoint); } } } } finally { mReadWriteLock.readLock().unlock(); } /// @} // Pre-sort accessPoints to speed preference insertion Collections.sort(accessPoints); // Log accesspoints that were deleted if (DBG) Log.d(TAG, "------ Dumping SSIDs that were not seen on this scan ------"); for (AccessPoint prevAccessPoint : mAccessPoints) { if (prevAccessPoint.getSsid() == null) continue; String prevSsid = prevAccessPoint.getSsidStr(); boolean found = false; for (AccessPoint newAccessPoint : accessPoints) { if (newAccessPoint.getSsid() != null && newAccessPoint.getSsid().equals(prevSsid)) { found = true; break; } } if (!found) if (DBG) Log.d(TAG, "Did not find " + prevSsid + " in this scan"); } if (DBG) Log.d(TAG, "---- Done dumping SSIDs that were not seen on this scan ----"); mAccessPoints = accessPoints; mMainHandler.sendEmptyMessage(MainHandler.MSG_ACCESS_POINT_CHANGED); }
此函数将会把扫描到的AP信息更新到 WifiSetting 中的wifi列表里。至此整个wifi扫描流程就结束了。
相关文章推荐
- Android N Wi-Fi 启动流程分析
- Android Wi-Fi wifi scan流程分析(Android 7.0)
- Android Wi-Fi connect & auto connect流程分析(Android 7.0)
- Android Wi-Fi源码分析之WifiService操作Wi-Fi(二):WifiStateMachine.java中的SUP_CONNECTION_EVENT分析
- android Wi-Fi集成之分析
- Android Wi-Fi 获取RSSI的代码流程
- Android Wi-Fi 设置wifi Only 11a、b、g、n 扫描(以QCOM为平台)
- Android PackageManagerService流程详细分析(七)之监控扫描指定APP目录
- Android Wi-Fi源码分析之WifiService操作Wi-Fi(一):分析Wifi.c中的wifi_load_driver()函数
- android framework MediaScanner等sd卡u盘扫描流程简要跟踪分析
- Android -- Wifi扫描流程分析
- Android Wi-Fi 设置带宽代码流程
- android拨打电话流程分析
- Android中View绘制流程以及invalidate()等相关方法分析
- Android 进阶学习:Android视图状态及重绘流程分析,带你一步步深入了解View(三)
- 【Win10 应用开发】扫描和连接Wi-fi网络
- Android的Input流程分析(好文)
- android系统重启流程分析
- android google 分屏 多窗口 按home键界面错乱故障分析(一)分屏的整个流程分析
- Android系统Recovery工作原理之使用update.zip升级过程分析(六)---Recovery服务流程细节