(一百三十五)Android O探索WLAN扫描(WIFI SCAN ALWAYS)

2023-12-19 07:38

本文主要是介绍(一百三十五)Android O探索WLAN扫描(WIFI SCAN ALWAYS),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

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时也可进行扫描)。\n\n是否对所有需要进行扫描的应用批准这项请求?"</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     @Override69     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) {@Overridepublic 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;@Overridepublic void enter() {// need to set the mode before starting supplicant because WSM will assume we are going// in to client modemWifiStateMachine.setOperationalMode(WifiStateMachine.SCAN_ONLY_WITH_WIFI_OFF_MODE);mWifiStateMachine.setSupplicantRunning(true);// Supplicant can't restart right away, so not the time we switched offmDisabledTimestamp = 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 {@Overridepublic 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 initializedif (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 statesetNetworkDetailedState(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 AutoJointransitionTo(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;@Overridepublic void enter() {mLastOperationMode = mOperationalMode;mWifiStateTracker.updateState(WifiStateTracker.SCAN_MODE);}@Overridepublic 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 dobreak;// Handle scan. All the connection related commands are// handled only in ConnectModeStatecase 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 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;}}

这里还可以看到

        // 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;@Overridepublic void enter() {mWifiStateMachine.setSupplicantRunning(false);// Supplicant can't restart right away, so not the time we switched offmDisabledTimestamp = SystemClock.elapsedRealtime();mDeferredEnableSerialNumber++;mHaveDeferredEnable = false;mWifiStateMachine.clearANQPCache();}

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

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

    class SupplicantStartedState extends State {
...@Overridepublic 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;
...@Overridepublic 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 {@Overridepublic 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 supplicanthandleSupplicantConnectionLoss(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();}@Overridepublic 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)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/511390

相关文章

深入探索协同过滤:从原理到推荐模块案例

文章目录 前言一、协同过滤1. 基于用户的协同过滤(UserCF)2. 基于物品的协同过滤(ItemCF)3. 相似度计算方法 二、相似度计算方法1. 欧氏距离2. 皮尔逊相关系数3. 杰卡德相似系数4. 余弦相似度 三、推荐模块案例1.基于文章的协同过滤推荐功能2.基于用户的协同过滤推荐功能 前言     在信息过载的时代,推荐系统成为连接用户与内容的桥梁。本文聚焦于

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

Android平台播放RTSP流的几种方案探究(VLC VS ExoPlayer VS SmartPlayer)

技术背景 好多开发者需要遴选Android平台RTSP直播播放器的时候,不知道如何选的好,本文针对常用的方案,做个大概的说明: 1. 使用VLC for Android VLC Media Player(VLC多媒体播放器),最初命名为VideoLAN客户端,是VideoLAN品牌产品,是VideoLAN计划的多媒体播放器。它支持众多音频与视频解码器及文件格式,并支持DVD影音光盘,VCD影

android-opencv-jni

//------------------start opencv--------------------@Override public void onResume(){ super.onResume(); //通过OpenCV引擎服务加载并初始化OpenCV类库,所谓OpenCV引擎服务即是 //OpenCV_2.4.3.2_Manager_2.4_*.apk程序包,存

从状态管理到性能优化:全面解析 Android Compose

文章目录 引言一、Android Compose基本概念1.1 什么是Android Compose?1.2 Compose的优势1.3 如何在项目中使用Compose 二、Compose中的状态管理2.1 状态管理的重要性2.2 Compose中的状态和数据流2.3 使用State和MutableState处理状态2.4 通过ViewModel进行状态管理 三、Compose中的列表和滚动

AI(文生语音)-TTS 技术线路探索学习:从拼接式参数化方法到Tacotron端到端输出

AI(文生语音)-TTS 技术线路探索学习:从拼接式参数化方法到Tacotron端到端输出 在数字化时代,文本到语音(Text-to-Speech, TTS)技术已成为人机交互的关键桥梁,无论是为视障人士提供辅助阅读,还是为智能助手注入声音的灵魂,TTS 技术都扮演着至关重要的角色。从最初的拼接式方法到参数化技术,再到现今的深度学习解决方案,TTS 技术经历了一段长足的进步。这篇文章将带您穿越时

Android 10.0 mtk平板camera2横屏预览旋转90度横屏拍照图片旋转90度功能实现

1.前言 在10.0的系统rom定制化开发中,在进行一些平板等默认横屏的设备开发的过程中,需要在进入camera2的 时候,默认预览图像也是需要横屏显示的,在上一篇已经实现了横屏预览功能,然后发现横屏预览后,拍照保存的图片 依然是竖屏的,所以说同样需要将图片也保存为横屏图标了,所以就需要看下mtk的camera2的相关横屏保存图片功能, 如何实现实现横屏保存图片功能 如图所示: 2.mtk

android应用中res目录说明

Android应用的res目录是一个特殊的项目,该项目里存放了Android应用所用的全部资源,包括图片、字符串、颜色、尺寸、样式等,类似于web开发中的public目录,js、css、image、style。。。。 Android按照约定,将不同的资源放在不同的文件夹中,这样可以方便的让AAPT(即Android Asset Packaging Tool , 在SDK的build-tools目

Android fill_parent、match_parent、wrap_content三者的作用及区别

这三个属性都是用来适应视图的水平或者垂直大小,以视图的内容或尺寸为基础的布局,比精确的指定视图的范围更加方便。 1、fill_parent 设置一个视图的布局为fill_parent将强制性的使视图扩展至它父元素的大小 2、match_parent 和fill_parent一样,从字面上的意思match_parent更贴切一些,于是从2.2开始,两个属性都可以使用,但2.3版本以后的建议使

Android Environment 获取的路径问题

1. 以获取 /System 路径为例 /*** Return root of the "system" partition holding the core Android OS.* Always present and mounted read-only.*/public static @NonNull File getRootDirectory() {return DIR_ANDR