前言:在之前的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
@Override
public 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().
*/
@MainThread
public 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有没有打开,如果没有打开就不继续进行扫描流程了。
@VisibleForTesting
class 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);
}
@VisibleForTesting
boolean isScanning() {
return hasMessages(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(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.
*/
@Override
public 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 settings
private 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 broadcast
long 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 identity
Binder.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 WifiManager
private 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 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<>();
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 scan
if (startScanNative(freqs, hiddenNetworks, workSource)) {
// a full scan covers everything, clearing scan request buffer
if (freqs == null)
mBufferedScanMsg.clear();
messageHandlingStatus = MESSAGE_HANDLING_STATUS_OK;
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
// 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
@Override
public void onSuccess() {
}
@Override
public void onFailure(int reason, String description) {
mIsScanOngoing = false;
mIsFullScanOngoing = false;
}
@Override
public void onResults(WifiScanner.ScanData[] results) {
}
@Override
public void onFullResult(ScanResult fullScanResult) {
}
@Override
public 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的一种方式。
AsyncChannel原理参照:
/**
* 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 initialized
if (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());
}
@Override
public 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方法:
@Override
public 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 {
@Override
public void exit() {
// clear scan results when scan mode is not active
mCachedScanResults.clear();
mWifiMetrics.incrementScanReturnEntry(
WifiMetricsProto.WifiLog.SCAN_FAILURE_INTERRUPTED,
mPendingScans.size());
sendOpFailedToAllAndClear(mPendingScans, WifiScanner.REASON_UNSPECIFIED,
"Scan was interrupted");
}
@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;
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
@Override
public void onSuccess() {
}
@Override
public void onFailure(int reason, String description) {
mIsScanOngoing = false;
mIsFullScanOngoing = false;
}
@Override
public void onResults(WifiScanner.ScanData[] results) {
}
@Override
public void onFullResult(ScanResult fullScanResult) {
}
@Override
public 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 scan
WifiNative.BucketSettings activeBucket = mActiveScanSettings.buckets[0];
// validate that all requested channels are being scanned
ChannelCollection 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 scan
if ((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日更新------------------------------------------------------------
参考了
这边代码意思是如果正在搜索那么就不重新开始新的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 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<>();
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 settings
mActiveScanSettings = settings;
// swap pending and active scan requests
RequestList<ScanSettings> tmp = mActiveScans;
mActiveScans = mPendingScans;
mPendingScans = tmp;
// make sure that the pending list is clear
mPendingScans.clear();
transitionTo(mScanningState);
} else {
mWifiMetrics.incrementScanReturnEntry(
WifiMetricsProto.WifiLog.SCAN_UNKNOWN, mPendingScans.size());
// notify and cancel failed scans
sendOpFailedToAllAndClear(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
@Override
public 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 scan
if (!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 batch
for (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 it
if ((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 timeout
if (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 async
mEventHandler.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 async
mEventHandler.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 Handlers
mClientInterface = 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();
3. 总结
本篇主要完成了WiFi 搜索命令从应用层下发到framework的梳理,后续到C++层的流程梳理见上面链接。