继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

(五十) Android O WiFi的扫描流程梳理

慕斯王
关注TA
已关注
手记 210
粉丝 110
获赞 512

前言:在之前的WiFi博客中主要梳理了WiFi的启动流程,WiFi启动后给人最直观的感觉就是开始扫描出周围的AP了,那扫描流程是怎么样的呢?


aosp : android O


1. WiFi扫描流程简介

wifi的扫描流程和启动流程一样,也是设置将扫描命令下发到framework,再往下我就不大梳理地下去了,所以本篇梳理也是Settings+framework组合的模式开始梳理。Settings在Android 8.0代码同样分为两部分,packages/apps/Settings和framework/base/packages/SettingsLib。至于为什么SettingsLib不放在packages/apps/Settings下呢,我理解是代码共享的考虑,SystemUi和开机向导中的蓝牙WiFi流程也会用到对应代码。


2. 流程分析

Settings+SettingsLib+framework

2.1 Settings

/packages/apps/Settings/src/com/android/settings/wifi/WifiSettings.java

@Overridepublic void onStart() {super.onStart();// On/off switch is hidden for Setup Wizard (returns null)mWifiEnabler = createWifiEnabler();mWifiTracker.startTracking();if (mIsRestricted) {restrictUi();return;}onWifiStateChanged(mWifiManager.getWifiState());}

这里会调用到SettingsLib的WifiTracker,WifiTracker中包含了wifi扫描的操作。比较奇怪的是,或者说违背常理,这里没有走监听wifi打开消息,待WiFi打开后开始扫描,而是只要在WiFi界面就开始扫描了。


2.2 SettingsLib

/frameworks/base/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java

/**     * Start tracking wifi networks and scores.     *     * <p>Registers listeners and starts scanning for wifi networks. If this is not called     * then forceUpdate() must be called to populate getAccessPoints().     */@MainThreadpublic void startTracking() {synchronized (mLock) {registerScoreCache();mNetworkScoringUiEnabled =Settings.Global.getInt(mContext.getContentResolver(),Settings.Global.NETWORK_SCORING_UI_ENABLED, 0) == 1;mMaxSpeedLabelScoreCacheAge =Settings.Global.getLong(mContext.getContentResolver(),Settings.Global.SPEED_LABEL_CACHE_EVICTION_AGE_MILLIS,DEFAULT_MAX_CACHED_SCORE_AGE_MILLIS);resumeScanning();if (!mRegistered) {mContext.registerReceiver(mReceiver, mFilter);// NetworkCallback objects cannot be reused. http://b/20701525 .mNetworkCallback = new WifiTrackerNetworkCallback();mConnectivityManager.registerNetworkCallback(mNetworkRequest, mNetworkCallback);mRegistered = true;}}}
/**     * Resume scanning for wifi networks after it has been paused.     *     * <p>The score cache should be registered before this method is invoked.     */public void resumeScanning() {if (mScanner == null) {mScanner = new Scanner();}mWorkHandler.sendEmptyMessage(WorkHandler.MSG_RESUME);if (mWifiManager.isWifiEnabled()) {mScanner.resume();}}

哇,这里可以看到resumeScanning()会判断下wifi有没有打开,如果没有打开就不继续进行扫描流程了。

@VisibleForTestingclass Scanner extends Handler {static final int MSG_SCAN = 0;private int mRetry = 0;void resume() {if (!hasMessages(MSG_SCAN)) {sendEmptyMessage(MSG_SCAN);}}void forceScan() {removeMessages(MSG_SCAN);sendEmptyMessage(MSG_SCAN);}void pause() {mRetry = 0;removeMessages(MSG_SCAN);}@VisibleForTestingboolean isScanning() {return hasMessages(MSG_SCAN);}@Overridepublic 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(MSG_SCAN, WIFI_RESCAN_INTERVAL_MS);}}

这里可以看到如果扫描失败3次就会导致扫描失败的toast的提醒,如果扫描成功则会进行扫描间隔为10s的持续扫描。

// TODO: Allow control of this?// Combo scans can take 5-6s to complete - set to 10s.private static final int WIFI_RESCAN_INTERVAL_MS = 10 * 1000;

这里扫描流程就走到WifiManager里去了


2.3 WiFi framework

2.3.1 WifiManager

/framework/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.     * @return {@code true} if the operation succeeded, i.e., the scan was initiated     */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();mService.startScan(null, workSource, packageName);return true;} catch (RemoteException e) {throw e.rethrowFromSystemServer();}}

从代码角度来看startScan要么返回true要么抛异常呀,false不可能。。。

之前分析过mService对应的服务端是WifiServiceImpl


2.3.2 WifiServiceImpl

framework/opt/net/wifi/service/java/com/android/server/wifi/WifiServiceImpl.java

/**     * see {@link android.net.wifi.WifiManager#startScan}     * and {@link android.net.wifi.WifiManager#startCustomizedScan}     *     * @param settings If null, use default parameter, i.e. full scan.     * @param workSource If null, all blame is given to the calling uid.     * @param packageName Package name of the app that requests wifi scan.     */@Overridepublic void startScan(ScanSettings settings, WorkSource workSource, String packageName) {enforceChangePermission();mLog.info("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;}}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);}

注意这个方法前两个参数传进来的都是null,这边一开始会对wifi扫描的请求对象做个过滤,毕竟WiFi扫描是耗电的,不能谁都来请求一下。不在白名单的后台应用的请求扫描时间间隔如果短于30min,则通报批评=-=

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);}


// Check if the request comes from background.private boolean isRequestFromBackground(String packageName) {// Requests from system or wifi are not background.if (Binder.getCallingUid() == Process.SYSTEM_UID|| Binder.getCallingUid() == Process.WIFI_UID) {return false;}mAppOps.checkPackage(Binder.getCallingUid(), packageName);if (mBackgroundThrottlePackageWhitelist.contains(packageName)) {return false;}// getPackageImportance requires PACKAGE_USAGE_STATS permission, so clearing the incoming// identify so the permission check can be done on system process where wifi runs in.long callingIdentity = Binder.clearCallingIdentity();try {return mActivityManager.getPackageImportance(packageName)> BACKGROUND_IMPORTANCE_CUTOFF;} finally {Binder.restoreCallingIdentity(callingIdentity);}}
// Default scan background throttling interval if not overriden in settingsprivate static final long DEFAULT_SCAN_BACKGROUND_THROTTLE_INTERVAL_MS = 30 * 60 * 1000;
// Send a failed scan broadcast to indicate the current scan request failed.private void sendFailedScanBroadcast() {// clear calling identity to send broadcastlong callingIdentity = Binder.clearCallingIdentity();try {Intent intent = new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);intent.putExtra(WifiManager.EXTRA_RESULTS_UPDATED, false);mContext.sendBroadcastAsUser(intent, UserHandle.ALL);} finally {// restore calling identityBinder.restoreCallingIdentity(callingIdentity);}}

由于传入的前两个参数是null,则直接走到WifiStateMachine里去了。

if (workSource == null && Binder.getCallingUid() >= 0) {workSource = new WorkSource(Binder.getCallingUid());}mWifiStateMachine.startScan(Binder.getCallingUid(), scanRequestCounter++,settings, workSource);


// Debug counter tracking scan requests sent by WifiManagerprivate int scanRequestCounter = 0;



2.3.3 WifiStateMachine

framework/opt/net/wifi/service/java/com/android/server/wifi/WifiStateMachine.java

/**     * Initiate a wifi scan. If workSource is not null, blame is given to it, otherwise blame is     * given to callingUid.     *     * @param callingUid The uid initiating the wifi scan. Blame will be given here unless     *                   workSource is specified.     * @param workSource If not null, blame is given to workSource.     * @param settings   Scan settings, see {@link ScanSettings}.     */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, mClock.getWallClockMillis());sendMessage(CMD_START_SCAN, callingUid, scanCounter, bundle);}

SupplicantStartedState 会处理这个消息

case CMD_START_SCAN:// TODO: remove scan request path (b/31445200)handleScanRequest(message);break;
private void handleScanRequest(Message message) {ScanSettings settings = null;WorkSource workSource = null;// unbundle parametersBundle 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<>();for (WifiChannel channel : settings.channelSet) {freqs.add(channel.freqMHz);}}// Retrieve the list of hidden network SSIDs to scan for.List<WifiScanner.ScanSettings.HiddenNetwork> hiddenNetworks =mWifiConfigManager.retrieveHiddenNetworkList();// call wifi native to start the scanif (startScanNative(freqs, hiddenNetworks, workSource)) {// a full scan covers everything, clearing scan request bufferif (freqs == null)mBufferedScanMsg.clear();messageHandlingStatus = MESSAGE_HANDLING_STATUS_OK;return;}// if reach here, scan request is rejectedif (!mIsScanOngoing) {// if rejection is NOT due to ongoing scan (e.g. bad scan parameters),// discard this request and pop up the next oneif (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 eventuallyif (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 scanbundle = 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 mIsFullScanOngoingmessageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;}}

继续调用startScanNative

// TODO this is a temporary measure to bridge between WifiScanner and WifiStateMachine until// scan functionality is refactored out of WifiStateMachine./**     * return true iff scan request is accepted     */private boolean startScanNative(final Set<Integer> freqs,            List<WifiScanner.ScanSettings.HiddenNetwork> hiddenNetworkList,            WorkSource workSource) {WifiScanner.ScanSettings settings = new WifiScanner.ScanSettings();if (freqs == null) {settings.band = WifiScanner.WIFI_BAND_BOTH_WITH_DFS;} else {settings.band = WifiScanner.WIFI_BAND_UNSPECIFIED;int index = 0;settings.channels = new WifiScanner.ChannelSpec[freqs.size()];for (Integer freq : freqs) {settings.channels[index++] = new WifiScanner.ChannelSpec(freq);}}settings.reportEvents = WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN| WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT;settings.hiddenNetworks =hiddenNetworkList.toArray(new WifiScanner.ScanSettings.HiddenNetwork[hiddenNetworkList.size()]);WifiScanner.ScanListener nativeScanListener = new WifiScanner.ScanListener() {// ignore all events since WifiStateMachine is registered for the supplicant events@Overridepublic void onSuccess() {}@Overridepublic void onFailure(int reason, String description) {mIsScanOngoing = false;mIsFullScanOngoing = false;}@Overridepublic void onResults(WifiScanner.ScanData[] results) {}@Overridepublic void onFullResult(ScanResult fullScanResult) {}@Overridepublic void onPeriodChanged(int periodInMs) {}};mWifiScanner.startScan(settings, nativeScanListener, workSource);mIsScanOngoing = true;mIsFullScanOngoing = (freqs == null);lastScanFreqs = freqs;return true;}


由于freqs和workSource这两个参数为null,所以settings.band是7,即频段为7

/** Both 2.4 GHz band and 5 GHz band; with DFS channels */public static final int WIFI_BAND_BOTH_WITH_DFS = 7;    /* both bands with DFS channels */

这边继续调用WifiScanner的startScan,settings不是null了,已经被默认初始化过了。另外这边初始化了一个nativeScanListener委托给WifiScanner,从方法来看是有结果了会回调这个Listner,使用了委托模式。


2.3.4 WifiScanner

framework/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,和aidl一样,也是IPC的一种方式。

/**     * Create a new WifiScanner instance.     * Applications will almost always want to use     * {@link android.content.Context#getSystemService Context.getSystemService()} to retrieve     * the standard {@link android.content.Context#WIFI_SERVICE Context.WIFI_SERVICE}.     * @param context the application context     * @param service the Binder interface     * @param looper the Looper used to deliver callbacks     * @hide     */public WifiScanner(Context context, IWifiScanner service, Looper looper) {mContext = context;mService = service;Messenger messenger = null;try {messenger = mService.getMessenger();} catch (RemoteException e) {throw e.rethrowFromSystemServer();}if (messenger == null) {throw new IllegalStateException("getMessenger() returned null!  This is invalid.");}mAsyncChannel = new AsyncChannel();mInternalHandler = new ServiceHandler(looper);mAsyncChannel.connectSync(mContext, mInternalHandler, messenger);// We cannot use fullyConnectSync because it sends the FULL_CONNECTION message// synchronously, which causes WifiScanningService to receive the wrong replyTo value.mAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);}

从WifiScanner的构造方法里可以看到InternalHandler/AsyncChannel/Messenger是完成IPC的桥梁。

另外WifiScanner的初始化是经由WifiStateMachine在WifiInjector里完成的。

// We can't do this in the constructor because WifiStateMachine is created before the// wifi scanning service is initializedif (mWifiScanner == null) {mWifiScanner = mWifiInjector.getWifiScanner();

framework/opt/net/wifi/service/java/com/android/server/wifi/WifiInjector.java

/**     * Obtain an instance of WifiScanner.     * If it was not already created, then obtain an instance.  Note, this must be done lazily since     * WifiScannerService is separate and created later.     */public synchronized WifiScanner getWifiScanner() {if (mWifiScanner == null) {mWifiScanner = new WifiScanner(mContext,IWifiScanner.Stub.asInterface(ServiceManager.getService(Context.WIFI_SCANNING_SERVICE)),mWifiStateMachineHandlerThread.getLooper());}return mWifiScanner;}

现在我有经验了,看下SystemServer的服务启动

traceBeginAndSlog("StartWifiScanning");mSystemServiceManager.startService("com.android.server.wifi.scanner.WifiScanningService");traceEnd();

再看下WifiScanningService,果不其然和WifiService一个套路,里面有个WifiScanningServiceImpl,经由SystemService的publishBinderService将自己加入到ServiceManager管理的Service中去,那么这里WifiScanner对应的服务端就是WifiScanningServiceImpl了。

public class WifiScanningService extends SystemService {static final String TAG = "WifiScanningService";private final WifiScanningServiceImpl mImpl;private final HandlerThread mHandlerThread;public WifiScanningService(Context context) {super(context);Log.i(TAG, "Creating " + Context.WIFI_SCANNING_SERVICE);mHandlerThread = new HandlerThread("WifiScanningService");mHandlerThread.start();mImpl = new WifiScanningServiceImpl(getContext(), mHandlerThread.getLooper(),WifiScannerImpl.DEFAULT_FACTORY, BatteryStatsService.getService(),WifiInjector.getInstance());}
@Overridepublic void onStart() {Log.i(TAG, "Publishing " + Context.WIFI_SCANNING_SERVICE);publishBinderService(Context.WIFI_SCANNING_SERVICE, mImpl);}


/**     * Publish the service so it is accessible to other services and apps.     */protected final void publishBinderService(String name, IBinder service) {publishBinderService(name, service, false);}/**     * Publish the service so it is accessible to other services and apps.     */protected final void publishBinderService(String name, IBinder service,boolean allowIsolated) {ServiceManager.addService(name, service, allowIsolated);}


2.3.5 WifiScanningServiceImpl

framework/opt/net/wifi/service/java/com/android/server/wifi/scanner/WifiScanningServiceImpl.java

继而再看下getMessenger方法:

@Overridepublic Messenger getMessenger() {if (mClientHandler != null) {mLog.trace("getMessenger() uid=%").c(Binder.getCallingUid()).flush();return new Messenger(mClientHandler);}loge("WifiScanningServiceImpl trying to get messenger w/o initialization");return null;}

AsyncChannel的机理忘了差不多了,后面补一下,这里梳理出来WifiScanningServiceImpl的ClientHander是处理WifiScanner发出的CMD_START_SINGLE_SCAN消息

case WifiScanner.CMD_START_SINGLE_SCAN:case WifiScanner.CMD_STOP_SINGLE_SCAN:mSingleScanStateMachine.sendMessage(Message.obtain(msg));break;
        mSingleScanStateMachine = new WifiSingleScanStateMachine(mLooper);

其中有个DriverStartedState继续处理,这里又是状态模式。

/**         * State representing when the driver is running. This state is not meant to be transitioned         * directly, but is instead indented 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 {@Overridepublic void exit() {// clear scan results when scan mode is not activemCachedScanResults.clear();mWifiMetrics.incrementScanReturnEntry(WifiMetricsProto.WifiLog.SCAN_FAILURE_INTERRUPTED,mPendingScans.size());sendOpFailedToAllAndClear(mPendingScans, WifiScanner.REASON_UNSPECIFIED,"Scan was interrupted");}@Overridepublic 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;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, workSource)) {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;

这里可以看到对上了,把我们千辛万苦传来的参数取了出来,虽然workSource就是个null,但是scanSettings的band是7

ScanSettings scanSettings =scanParams.getParcelable(WifiScanner.SCAN_PARAMS_SCAN_SETTINGS_KEY);WorkSource workSource =scanParams.getParcelable(WifiScanner.SCAN_PARAMS_WORK_SOURCE_KEY);

之后返回一个搜索成功的消息

void replySucceeded(Message msg) {if (msg.replyTo != null) {Message reply = Message.obtain();reply.what = WifiScanner.CMD_OP_SUCCEEDED;reply.arg2 = msg.arg2;if (msg.obj != null) {reply.obj = msg.obj;}try {msg.replyTo.send(reply);mLog.trace("replySucceeded recvdMessage=%").c(msg.what).flush();} catch (RemoteException e) {// There's not much we can do if reply can't be sent!}} else {// locally generated message; doesn't need a reply!}}

这边WifiScanner的ServiceHandler会收到并处理

switch (msg.what) {/* ActionListeners grouped together */case CMD_OP_SUCCEEDED :((ActionListener) listener).onSuccess();

最后再调用到我们在WifiStateMachine里初始化好的listener的success方法,虽然是个空的=-=

WifiScanner.ScanListener nativeScanListener = new WifiScanner.ScanListener() {// ignore all events since WifiStateMachine is registered for the supplicant events@Overridepublic void onSuccess() {}@Overridepublic void onFailure(int reason, String description) {mIsScanOngoing = false;mIsFullScanOngoing = false;}@Overridepublic void onResults(WifiScanner.ScanData[] results) {}@Overridepublic void onFullResult(ScanResult fullScanResult) {}@Overridepublic void onPeriodChanged(int periodInMs) {}};

之后会和WifiStateMachine的状态扯上关系

// 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();}


boolean activeScanSatisfies(ScanSettings settings) {if (mActiveScanSettings == null) {return false;}// there is always one bucket for a single scanWifiNative.BucketSettings activeBucket = mActiveScanSettings.buckets[0];// validate that all requested channels are being scannedChannelCollection activeChannels = mChannelHelper.createChannelCollection();activeChannels.addChannels(activeBucket);if (!activeChannels.containsSettings(settings)) {return false;}// if the request is for a full scan, but there is no ongoing full scanif ((settings.reportEvents & WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT) != 0&& (activeBucket.report_events & WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT)== 0) {return false;}if (!ArrayUtils.isEmpty(settings.hiddenNetworks)) {if (ArrayUtils.isEmpty(mActiveScanSettings.hiddenNetworks)) {return false;}List<WifiNative.HiddenNetwork> activeHiddenNetworks = new ArrayList<>();for (WifiNative.HiddenNetwork hiddenNetwork : mActiveScanSettings.hiddenNetworks) {activeHiddenNetworks.add(hiddenNetwork);}for (ScanSettings.HiddenNetwork hiddenNetwork : settings.hiddenNetworks) {WifiNative.HiddenNetwork nativeHiddenNetwork = new WifiNative.HiddenNetwork();nativeHiddenNetwork.ssid = hiddenNetwork.ssid;if (!activeHiddenNetworks.contains(nativeHiddenNetwork)) {return false;}}}return true;}

没梳理完,待续。。。怎么感觉流程是对的,但是功能没了,只剩个架子了。。。

------------------------------------------------------------6月24日更新------------------------------------------------------------

参考了https://blog.csdn.net/xiaoxiangyuhai/article/details/75159403

这边代码意思是如果正在搜索那么就不重新开始新的scan了,否则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();}

然后看下tryToStartNewScan()

void tryToStartNewScan() {if (mPendingScans.size() == 0) { // no pending requestsreturn;}mChannelHelper.updateChannels();// TODO move merging logic to a schedulerWifiNative.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) {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 settingsmActiveScanSettings = settings;// swap pending and active scan requestsRequestList<ScanSettings> tmp = mActiveScans;mActiveScans = mPendingScans;mPendingScans = tmp;// make sure that the pending list is clearmPendingScans.clear();transitionTo(mScanningState);} else {mWifiMetrics.incrementScanReturnEntry(WifiMetricsProto.WifiLog.SCAN_UNKNOWN, mPendingScans.size());// notify and cancel failed scanssendOpFailedToAllAndClear(mPendingScans, WifiScanner.REASON_UNSPECIFIED,"Failed to start single scan");}}

其中下面这句就是关键了

mScannerImpl.startSingleScan

看下这是个啥:

private WifiScannerImpl mScannerImpl;mScannerImpl = mScannerImplFactory.create(mContext, mLooper, mClock);
public abstract class WifiScannerImpl {/**     * A factory that create a {@link com.android.server.wifi.scanner.WifiScannerImpl}     */public static interface WifiScannerImplFactory {WifiScannerImpl create(Context context, Looper looper, Clock clock);}/**     * 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();if (wifiNative.getBgScanCapabilities(new WifiNative.ScanCapabilities())) {return new HalWifiScannerImpl(context, wifiNative, wifiMonitor, looper, clock);} else {return new WificondScannerImpl(context, wifiNative, wifiMonitor, looper,clock);}}};


接着看下这个factory如何创建的

WifiScanningServiceImpl(Context context, Looper looper,WifiScannerImpl.WifiScannerImplFactory scannerImplFactory, IBatteryStats batteryStats,WifiInjector wifiInjector) {mContext = context;mLooper = looper;mScannerImplFactory = scannerImplFactory;mBatteryStats = batteryStats;mClients = new ArrayMap<>();mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);mWifiMetrics = wifiInjector.getWifiMetrics();mClock = wifiInjector.getClock();mLog = wifiInjector.makeLog(TAG);mFrameworkFacade = wifiInjector.getFrameworkFacade();mPreviousSchedule = null;}


mImpl = new WifiScanningServiceImpl(getContext(), mHandlerThread.getLooper(),WifiScannerImpl.DEFAULT_FACTORY, BatteryStatsService.getService(),WifiInjector.getInstance());

这里可以看到factory用的是默认实现WifiScannerImpl.DEFAULT_FACTORY

if (wifiNative.getBgScanCapabilities(new WifiNative.ScanCapabilities())) {return new HalWifiScannerImpl(context, wifiNative, wifiMonitor, looper, clock);} else {return new WificondScannerImpl(context, wifiNative, wifiMonitor, looper,clock);}

分别看下这两个实现的startSingleScan方法

1. HalWifiScannerImpl

public boolean startSingleScan(WifiNative.ScanSettings settings,            WifiNative.ScanEventHandler eventHandler) {return mWificondScannerDelegate.startSingleScan(settings, eventHandler);}
mWificondScannerDelegate =new WificondScannerImpl(context, wifiNative, wifiMonitor, mChannelHelper,looper, clock);

2.WificondScannerImpl:这边发现调用到最后两个实现调到一起去了。


2.3.6 WificondScannerImpl

@Overridepublic 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;}if (mPendingSingleScanSettings != null|| (mLastScanSettings != null && mLastScanSettings.singleScanActive)) {Log.w(TAG, "A single scan is already running");return false;}synchronized (mSettingsLock) {mPendingSingleScanSettings = settings;mPendingSingleScanEventHandler = eventHandler;processPendingScans();return true;}}
private void processPendingScans() {synchronized (mSettingsLock) {// Wait for the active scan result to come back to reschedule other scans,// unless if HW pno scan is running. Hw PNO scans are paused it if there// are other pending scans,if (mLastScanSettings != null && !mLastScanSettings.hwPnoScanActive) {return;}ChannelCollection allFreqs = mChannelHelper.createChannelCollection();Set<String> hiddenNetworkSSIDSet = new HashSet<>();final LastScanSettings newScanSettings =new LastScanSettings(mClock.getElapsedSinceBootMillis());// Update scan settings if there is a pending scanif (!mBackgroundScanPaused) {if (mPendingBackgroundScanSettings != null) {mBackgroundScanSettings = mPendingBackgroundScanSettings;mBackgroundScanEventHandler = mPendingBackgroundScanEventHandler;mNextBackgroundScanPeriod = 0;mPendingBackgroundScanSettings = null;mPendingBackgroundScanEventHandler = null;mBackgroundScanPeriodPending = true;}if (mBackgroundScanPeriodPending && mBackgroundScanSettings != null) {int reportEvents = WifiScanner.REPORT_EVENT_NO_BATCH; // default to no batchfor (int bucket_id = 0; bucket_id < mBackgroundScanSettings.num_buckets;++bucket_id) {WifiNative.BucketSettings bucket =mBackgroundScanSettings.buckets[bucket_id];if (mNextBackgroundScanPeriod % (bucket.period_ms/ mBackgroundScanSettings.base_period_ms) == 0) {if ((bucket.report_events& WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN) != 0) {reportEvents |= WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN;}if ((bucket.report_events& WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT) != 0) {reportEvents |= WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT;}// only no batch if all buckets specify itif ((bucket.report_events& WifiScanner.REPORT_EVENT_NO_BATCH) == 0) {reportEvents &= ~WifiScanner.REPORT_EVENT_NO_BATCH;}allFreqs.addChannels(bucket);}}if (!allFreqs.isEmpty()) {newScanSettings.setBackgroundScan(mNextBackgroundScanId++,mBackgroundScanSettings.max_ap_per_scan, reportEvents,mBackgroundScanSettings.report_threshold_num_scans,mBackgroundScanSettings.report_threshold_percent);}mNextBackgroundScanPeriod++;mBackgroundScanPeriodPending = false;mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,mClock.getElapsedSinceBootMillis()+ mBackgroundScanSettings.base_period_ms,BACKGROUND_PERIOD_ALARM_TAG, mScanPeriodListener, mEventHandler);}}if (mPendingSingleScanSettings != null) {boolean reportFullResults = false;ChannelCollection singleScanFreqs = mChannelHelper.createChannelCollection();for (int i = 0; i < mPendingSingleScanSettings.num_buckets; ++i) {WifiNative.BucketSettings bucketSettings =mPendingSingleScanSettings.buckets[i];if ((bucketSettings.report_events& WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT) != 0) {reportFullResults = true;}singleScanFreqs.addChannels(bucketSettings);allFreqs.addChannels(bucketSettings);}newScanSettings.setSingleScan(reportFullResults, singleScanFreqs,mPendingSingleScanEventHandler);WifiNative.HiddenNetwork[] hiddenNetworks =mPendingSingleScanSettings.hiddenNetworks;if (hiddenNetworks != null) {int numHiddenNetworks =Math.min(hiddenNetworks.length, MAX_HIDDEN_NETWORK_IDS_PER_SCAN);for (int i = 0; i < numHiddenNetworks; i++) {hiddenNetworkSSIDSet.add(hiddenNetworks[i].ssid);}}mPendingSingleScanSettings = null;mPendingSingleScanEventHandler = null;}if (newScanSettings.backgroundScanActive || newScanSettings.singleScanActive) {boolean success = false;Set<Integer> freqs;if (!allFreqs.isEmpty()) {pauseHwPnoScan();freqs = allFreqs.getScanFreqs();success = mWifiNative.scan(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 for");}if (success) {// TODO handle scan timeoutif (DBG) {Log.d(TAG, "Starting wifi scan for freqs=" + freqs+ ", background=" + newScanSettings.backgroundScanActive+ ", single=" + newScanSettings.singleScanActive);}mLastScanSettings = newScanSettings;mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,mClock.getElapsedSinceBootMillis() + SCAN_TIMEOUT_MS,TIMEOUT_ALARM_TAG, mScanTimeoutListener, mEventHandler);} else {// indicate scan failure asyncmEventHandler.post(new Runnable() {public void run() {if (newScanSettings.singleScanEventHandler != null) {newScanSettings.singleScanEventHandler.onScanStatus(WifiNative.WIFI_SCAN_FAILED);}}});// TODO(b/27769665) background scans should be failed too if scans fail enough}} else if (isHwPnoScanRequired()) {newScanSettings.setHwPnoScan(mPnoSettings.networkList, mPnoEventHandler);boolean status;// If the PNO network list has changed from the previous request, ensure that// we bypass the debounce logic and restart PNO scan.if (isDifferentPnoScanSettings(newScanSettings)) {status = restartHwPnoScan(mPnoSettings);} else {status = startHwPnoScan(mPnoSettings);}if (status) {mLastScanSettings = newScanSettings;} else {Log.e(TAG, "Failed to start PNO scan");// indicate scan failure asyncmEventHandler.post(new Runnable() {public void run() {if (mPnoEventHandler != null) {mPnoEventHandler.onPnoScanFailed();}// Clean up PNO state, we don't want to continue PNO scanning.mPnoSettings = null;mPnoEventHandler = null;}});}}}}

代码挺长的,但我看到这一句我就知道流程是对的了=-=

success = mWifiNative.scan(freqs, hiddenNetworkSSIDSet);

最终还是会调用到WifiNative的scan方法,毕竟上层的搜索都是空的,还要底层配合。


2.3.7     WifiNative

/**     * Start a scan using wificond for the given parameters.     * @param freqs list of frequencies to scan for, if null scan all supported channels.     * @param hiddenNetworkSSIDs List of hidden networks to be scanned for.     * @return Returns true on success.     */public boolean scan(Set<Integer> freqs, Set<String> hiddenNetworkSSIDs) {return mWificondControl.scan(freqs, hiddenNetworkSSIDs);}


2.3.8 WificondControl

/**     * Start a scan using wificond for the given parameters.     * @param freqs list of frequencies to scan for, if null scan all supported channels.     * @param hiddenNetworkSSIDs List of hidden networks to be scanned for.     * @return Returns true on success.     */public boolean scan(Set<Integer> freqs, Set<String> hiddenNetworkSSIDs) {if (mWificondScanner == null) {Log.e(TAG, "No valid wificond scanner interface handler");return false;}SingleScanSettings settings = new SingleScanSettings();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.add(network);}}try {return mWificondScanner.scan(settings);} catch (RemoteException e1) {Log.e(TAG, "Failed to request scan due to remote exception");}return false;}

这边又调用到WificondScanner里去了,看下是啥

/**    * Setup driver for client mode via wificond.    * @return An IClientInterface as wificond client interface binder handler.    * Returns null on failure.    */public IClientInterface setupDriverForClientMode() {Log.d(TAG, "Setting up driver for client mode");mWificond = mWifiInjector.makeWificond();if (mWificond == null) {Log.e(TAG, "Failed to get reference to wificond");return null;}IClientInterface clientInterface = null;try {clientInterface = mWificond.createClientInterface();} catch (RemoteException e1) {Log.e(TAG, "Failed to get IClientInterface due to remote exception");return null;}if (clientInterface == null) {Log.e(TAG, "Could not get IClientInterface instance from wificond");return null;}Binder.allowBlocking(clientInterface.asBinder());// Refresh HandlersmClientInterface = clientInterface;try {mClientInterfaceName = clientInterface.getInterfaceName();mWificondScanner = mClientInterface.getWifiScannerImpl();if (mWificondScanner == null) {Log.e(TAG, "Failed to get WificondScannerImpl");return null;}Binder.allowBlocking(mWificondScanner.asBinder());mScanEventHandler = new ScanEventHandler();mWificondScanner.subscribeScanEvents(mScanEventHandler);mPnoScanEventHandler = new PnoScanEventHandler();mWificondScanner.subscribePnoScanEvents(mPnoScanEventHandler);} catch (RemoteException e) {Log.e(TAG, "Failed to refresh wificond scanner due to remote exception");}return clientInterface;}

这边的几步看不下去了。。。

        mWificond = mWifiInjector.makeWificond();
public IWificond makeWificond() {// We depend on being able to refresh our binder in WifiStateMachine, so don't cache it.IBinder binder = ServiceManager.getService(WIFICOND_SERVICE_NAME);return IWificond.Stub.asInterface(binder);}
            clientInterface = mWificond.createClientInterface();
            mWificondScanner = mClientInterface.getWifiScannerImpl();

感觉要到驱动层了=-=
待续。。。

搜了下

./system/connectivity/wificond/aidl/android/net/wifi/IWifiScannerImpl.aidl

./system/connectivity/wificond/aidl/android/net/wifi/IWificond.aidl


---------------------------------------------------------6月27日更新---------------------------------------------------------

请教下了,这块Wificond对应的服务端确实是放在system/connectivity下面,是cpp文件,逻辑是从之前的wpa_supplicant里迁出来的

---------------------------------------------------------6月27日更新--------------

原文出处

打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP