我是靠谱客的博主 坦率小丸子,这篇文章主要介绍(一百三十五)Android O探索WLAN扫描(WIFI SCAN ALWAYS)1.界面信息2.流程梳理3.总结,现在分享给大家,希望可以做个参考。

1.界面信息

小米mix2 WLAN扫描开关位于设置-更多设置-系统安全-位置信息-扫描,截图如下

 

2.流程梳理

搜索字符串

jiatai@jiatai:~/expand/aosp/aosp/packages/apps/Settings/res/values-zh-rCN$ grep "WLAN.*扫描" ./ -nr
./arrays.xml:250:
<item msgid="8281201165558093009">"WLAN扫描"</item>
./strings.xml:738:
<string name="wifi_scan_notify_text" msgid="5593805423071186757">"为了提高位置信息的精确度,系统应用和服务仍然会扫描 WLAN 网络。您可以在<xliff:g id="LINK_BEGIN_0">LINK_BEGIN</xliff:g>扫描设置<xliff:g id="LINK_END_1">LINK_END</xliff:g>中更改此设置。"</string>
./strings.xml:739:
<string name="wifi_scan_notify_text_scanning_off" msgid="3426075479272242098">"要提高位置信息的精确度,请在<xliff:g id="LINK_BEGIN_0">LINK_BEGIN</xliff:g>扫描设置<xliff:g id="LINK_END_1">LINK_END</xliff:g>中开启 WLAN 扫描功能。"</string>
./strings.xml:827:
<string name="wifi_scan_always_turnon_message" msgid="203123538572122989">"为了提高位置信息精确度以及其他目的,“<xliff:g id="APP_NAME">%1$s</xliff:g>”请求启用网络扫描功能(在关闭了WLAN时也可进行扫描)。nn是否对所有需要进行扫描的应用批准这项请求?"</string>
./strings.xml:1430:
<string name="location_scanning_wifi_always_scanning_title" msgid="6216705505621183645">"WLAN 扫描"</string>
jiatai@jiatai:~/expand/aosp/aosp/packages/apps/Settings/res/xml$ grep "location_scanning_wifi_always_scanning_title" ./ -nr
./location_scanning.xml:22:
android:title="@string/location_scanning_wifi_always_scanning_title"
jiatai@jiatai:~/expand/aosp/aosp/packages/apps/Settings/res/xml$ vim ./location_scanning.xml
jiatai@jiatai:~/expand/aosp/aosp/packages/apps/Settings/res/xml$ cd ../../src/
jiatai@jiatai:~/expand/aosp/aosp/packages/apps/Settings/src$ grep "wifi_always_scanning" ./ -nr
./com/android/settings/location/ScanningSettings.java:32:
private static final String KEY_WIFI_SCAN_ALWAYS_AVAILABLE = "wifi_always_scanning";

之后定位到ScanningSettings这个类对应于开关的逻辑处理

2.1 ScanningSettings

 68
@Override
69
public boolean onPreferenceTreeClick(Preference preference) {
70
String key = preference.getKey();
71
if (KEY_WIFI_SCAN_ALWAYS_AVAILABLE.equals(key)) {
72
Global.putInt(getContentResolver(),
73
Global.WIFI_SCAN_ALWAYS_AVAILABLE,
74
((SwitchPreference) preference).isChecked() ? 1 : 0);
75
} else if (KEY_BLUETOOTH_SCAN_ALWAYS_AVAILABLE.equals(key)) {
76
Global.putInt(getContentResolver(),
77
Global.BLE_SCAN_ALWAYS_AVAILABLE,
78
((SwitchPreference) preference).isChecked() ? 1 : 0);
79
} else {
80
return super.onPreferenceTreeClick(preference);
81
}
82
return true;
83
}

从如上代码可以看到该开关其实就是对应了一个数据库值(Global.WIFI_SCAN_ALWAYS_AVAILABLE)的变化

在framework搜索相关数据库监听

挨个看了下WifiServiceImpl中有对该数据库值(Global.WIFI_SCAN_ALWAYS_AVAILABLE)的监听

 

2.2 WifiServiceImpl


/**
* Observes settings changes to scan always mode.
*/
private void registerForScanModeChange() {
ContentObserver contentObserver = new ContentObserver(null) {
@Override
public void onChange(boolean selfChange) {
mSettingsStore.handleWifiScanAlwaysAvailableToggled();
mWifiController.sendMessage(CMD_SCAN_ALWAYS_MODE_CHANGED);
}
};
mFrameworkFacade.registerContentObserver(mContext,
Settings.Global.getUriFor(Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE),
false, contentObserver);
}

数据库值(Global.WIFI_SCAN_ALWAYS_AVAILABLE)的变化会触发


mSettingsStore.handleWifiScanAlwaysAvailableToggled();
mWifiController.sendMessage(CMD_SCAN_ALWAYS_MODE_CHANGED);

依次看下

 

2.3 WifiSettingsStore


synchronized void handleWifiScanAlwaysAvailableToggled() {
mScanAlwaysAvailable = getPersistedScanAlwaysAvailable();
}
private boolean getPersistedScanAlwaysAvailable() {
return Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE,
0) == 1;
}

WifiSettingsStore就是更新一下自己保存的局部变量mScanAlwaysAvailable与当前开关保存一致。

 

2.4 WifiController

搜索代码看了下只有三个状态会对消息CMD_SCAN_ALWAYS_MODE_CHANGED进行处理,分别是

DefaultState(不作处理)

ApStaDisabledState(若开关打开,切换到StaDisabledWithScanState)

StaDisabledWithScanState(若开关关闭,切换到ApStaDisabledState)

 

2.4.1 wifi scan always打开


class ApStaDisabledState extends State {
...
case CMD_SCAN_ALWAYS_MODE_CHANGED:
if (mSettingsStore.isScanAlwaysAvailable()) {
transitionTo(mStaDisabledWithScanState);
}
break;

 


class StaDisabledWithScanState extends State {
private int mDeferredEnableSerialNumber = 0;
private boolean mHaveDeferredEnable = false;
private long mDisabledTimestamp;
@Override
public void enter() {
// need to set the mode before starting supplicant because WSM will assume we are going
// in to client mode
mWifiStateMachine.setOperationalMode(WifiStateMachine.SCAN_ONLY_WITH_WIFI_OFF_MODE);
mWifiStateMachine.setSupplicantRunning(true);
// Supplicant can't restart right away, so not the time we switched off
mDisabledTimestamp = SystemClock.elapsedRealtime();
mDeferredEnableSerialNumber++;
mHaveDeferredEnable = false;
mWifiStateMachine.clearANQPCache();
}

从如上代码及状态之间图示可以看出状态切换只走了StaDisabledWithScanState的enter方法

简单看了就是切换到了SCAN_ONLY_WITH_WIFI_OFF_MODE并且启动了supplicant

WSM

i 切换SCAN_ONLY_WITH_WIFI_OFF_MODE


/**
* TODO: doc
*/
public void setOperationalMode(int mode) {
if (mVerboseLoggingEnabled) log("setting operational mode to " + String.valueOf(mode));
sendMessage(CMD_SET_OPERATIONAL_MODE, mode, 0);
}

InitialState


case CMD_SET_OPERATIONAL_MODE:
mOperationalMode = message.arg1;
if (mOperationalMode != DISABLED_MODE) {
sendMessage(CMD_START_SUPPLICANT);
}
break;

ii 启动supplicant


/**
* TODO: doc
*/
public void setSupplicantRunning(boolean enable) {
if (enable) {
sendMessage(CMD_START_SUPPLICANT);
} else {
sendMessage(CMD_STOP_SUPPLICANT);
}
}

可以看到两步其实都下发了启动supplicant的命令,简单看起来有冗余,之后WiFi启动流程是差不多的,加载驱动-启动supplicant,从InitialState-SupplicantStartingState-SupplicantStartedState


class SupplicantStartedState extends State {
@Override
public void enter() {
if (mVerboseLoggingEnabled) {
logd("SupplicantStartedState enter");
}
mWifiNative.setExternalSim(true);
setRandomMacOui();
mCountryCode.setReadyForChange(true);
// 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();
synchronized (mWifiReqCountLock) {
mWifiConnectivityManager =
mWifiInjector.makeWifiConnectivityManager(mWifiInfo,
hasConnectionRequests());
mWifiConnectivityManager.setUntrustedConnectionAllowed(mUntrustedReqCount > 0);
mWifiConnectivityManager.handleScreenStateChanged(mScreenOn);
}
}
mWifiDiagnostics.startLogging(mVerboseLoggingEnabled);
mIsRunning = true;
updateBatteryWorkSource(null);
/**
* Enable bluetooth coexistence scan mode when bluetooth connection is active.
* When this mode is on, some of the low-level scan parameters used by the
* driver are changed to reduce interference with bluetooth
*/
mWifiNative.setBluetoothCoexistenceScanMode(mBluetoothConnectionActive);
// Check if there is a voice call on-going and set/reset the tx power limit
// appropriately.
if (mEnableVoiceCallSarTxPowerLimit) {
if (getTelephonyManager().isOffhook()) {
sendMessage(CMD_SELECT_TX_POWER_SCENARIO,
WifiNative.TX_POWER_SCENARIO_VOICE_CALL);
} else {
sendMessage(CMD_SELECT_TX_POWER_SCENARIO,
WifiNative.TX_POWER_SCENARIO_NORMAL);
}
}
// initialize network state
setNetworkDetailedState(DetailedState.DISCONNECTED);
// Disable legacy multicast filtering, which on some chipsets defaults to enabled.
// Legacy IPv6 multicast filtering blocks ICMPv6 router advertisements which breaks IPv6
// provisioning. Legacy IPv4 multicast filtering may be re-enabled later via
// IpClient.Callback.setFallbackMulticastFilter()
mWifiNative.stopFilteringMulticastV4Packets();
mWifiNative.stopFilteringMulticastV6Packets();
if (mOperationalMode == SCAN_ONLY_MODE ||
mOperationalMode == SCAN_ONLY_WITH_WIFI_OFF_MODE) {
mWifiNative.disconnect();
setWifiState(WIFI_STATE_DISABLED);
transitionTo(mScanModeState);
} else if (mOperationalMode == CONNECT_MODE) {
setWifiState(WIFI_STATE_ENABLING);
// Transitioning to Disconnected state will trigger a scan and subsequently AutoJoin
transitionTo(mDisconnectedState);
} else if (mOperationalMode == DISABLED_MODE) {
transitionTo(mSupplicantStoppingState);
}
。。。

之前梳理WiFi启动流程的时候OperationalMode都是CONNECT_MODE,之后会切换到DisconnectedState

而这次wifi scan always的OperationalMode是SCAN_ONLY_WITH_WIFI_OFF_MODE,这时会切换到ScanModeState


class ScanModeState extends State {
private int mLastOperationMode;
@Override
public void enter() {
mLastOperationMode = mOperationalMode;
mWifiStateTracker.updateState(WifiStateTracker.SCAN_MODE);
}
@Override
public boolean processMessage(Message message) {
logStateAndMessage(message, this);
switch(message.what) {
case CMD_SET_OPERATIONAL_MODE:
if (message.arg1 == CONNECT_MODE) {
mOperationalMode = CONNECT_MODE;
setWifiState(WIFI_STATE_ENABLING);
transitionTo(mDisconnectedState);
} else if (message.arg1 == DISABLED_MODE) {
transitionTo(mSupplicantStoppingState);
}
// Nothing to do
break;
// Handle scan. All the connection related commands are
// handled only in ConnectModeState
case CMD_START_SCAN:
handleScanRequest(message);
break;
default:
return NOT_HANDLED;
}
return HANDLED;
}
}

这个状态很简单,就处理两个消息,CMD_SET_OPERATIONAL_MODE和CMD_START_SCAN,主要看下CMD_START_SCAN的处理

(对比ConnectModeState可以处理CMD_START_CONNECT CMD_START_ROAM CMD_SAVE_CONFIG等等连接相关的命令)


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

这里还可以看到


// Retrieve the list of hidden network SSIDs to scan for.
List<WifiScanner.ScanSettings.HiddenNetwork> hiddenNetworks =
mWifiConfigManager.retrieveHiddenNetworkList();

这边应该是可以让扫描可以扫描到隐藏网络的相关代码,梳理待续

可以理解为ScanModeState主要就处理扫描请求,和其所处模式SCAN_ONLY_WITH_WIFI_OFF_MODE的描述很相符,WiFi关闭情况下只进行扫描。

 

2.4.1 wifi scan always关闭

WifiController


class ApStaDisabledState extends State {
private int mDeferredEnableSerialNumber = 0;
private boolean mHaveDeferredEnable = false;
private long mDisabledTimestamp;
@Override
public void enter() {
mWifiStateMachine.setSupplicantRunning(false);
// Supplicant can't restart right away, so not the time we switched off
mDisabledTimestamp = SystemClock.elapsedRealtime();
mDeferredEnableSerialNumber++;
mHaveDeferredEnable = false;
mWifiStateMachine.clearANQPCache();
}

这边看来就是将supplicant关闭即可

WSM ScanModeState的父状态为SupplicantStartedState,由于ScanModeState无法处理CMD_STOP_SUPPLICANT,由其父状态SupplicantStartedState处理。


class SupplicantStartedState extends State {
...
@Override
public boolean processMessage(Message message) {
logStateAndMessage(message, this);
switch(message.what) {
case CMD_STOP_SUPPLICANT:
/* Supplicant stopped by user */
if (mP2pSupported) {
transitionTo(mWaitForP2pDisableState);
} else {
transitionTo(mSupplicantStoppingState);
}
break;
...
@Override
public void exit() {
mWifiDiagnostics.stopLogging();
mIsRunning = false;
updateBatteryWorkSource(null);
mScanResults = new ArrayList<>();
final Intent intent = new Intent(WifiManager.WIFI_SCAN_AVAILABLE);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
intent.putExtra(WifiManager.EXTRA_SCAN_AVAILABLE, WIFI_STATE_DISABLED);
mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
mBufferedScanMsg.clear();
mNetworkInfo.setIsAvailable(false);
if (mNetworkAgent != null) mNetworkAgent.sendNetworkInfo(mNetworkInfo);
mCountryCode.setReadyForChange(false);
}
}

切换到SupplicantStoppingState


class SupplicantStoppingState extends State {
@Override
public void enter() {
/* Send any reset commands to supplicant before shutting it down */
handleNetworkDisconnect();
String suppState = System.getProperty("init.svc.wpa_supplicant");
if (suppState == null) suppState = "unknown";
setWifiState(WIFI_STATE_DISABLING);
mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE);
logd("SupplicantStoppingState: disableSupplicant "
+ " init.svc.wpa_supplicant=" + suppState);
if (mWifiNative.disableSupplicant()) {
mWifiNative.closeSupplicantConnection();
sendSupplicantConnectionChangedBroadcast(false);
setWifiState(WIFI_STATE_DISABLED);
} else {
// Failed to disable supplicant
handleSupplicantConnectionLoss(true);
}
transitionTo(mInitialState);
}
}

关闭supplicant后切换到InitialState状态


class InitialState extends State {
private void cleanup() {
// Tearing down the client interfaces below is going to stop our supplicant.
mWifiMonitor.stopAllMonitoring();
mDeathRecipient.unlinkToDeath();
mWifiNative.tearDown();
}
@Override
public void enter() {
mWifiStateTracker.updateState(WifiStateTracker.INVALID);
cleanup();
}

流程处理完毕。

 

3.总结

WIFI SCAN ALWAYS对应模式为SCAN_ONLY_WITH_WIFI_OFF_MODE,SCAN_ONLY_WITH_WIFI_OFF_MODE顾名思义WiFi关闭情况下只处理扫描请求。

最后

以上就是坦率小丸子最近收集整理的关于(一百三十五)Android O探索WLAN扫描(WIFI SCAN ALWAYS)1.界面信息2.流程梳理3.总结的全部内容,更多相关(一百三十五)Android内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(131)

评论列表共有 0 条评论

立即
投稿
返回
顶部