Wi-Fi PNO扫描流程(Android P)

article/2025/11/6 6:25:00

简介:当手机灭屏情况下,有保存网络时,若已连接,不扫描,否则,PNO扫描,即只扫描已保存的网络。最小间隔min=20s,最大间隔max=20s*3=60s

PNO 即Preferred Network Offload,用于系统在休眠的时候连接WiFi

Wi-Fi PNO扫描流程

WifiConnectivityManager.java
灭屏时,WifiConnectivityManager中handleScreenStateChanged函数会判断是否触发PNO扫描。并且进行PNO扫描列表设置、PNO扫描间隔(20s)设置等PNO扫描的参数设置。所以先从处理屏幕状态改变开始。

public void handleScreenStateChanged(boolean screenOn) {localLog("handleScreenStateChanged: screenOn=" + screenOn);mScreenOn = screenOn;mOpenNetworkNotifier.handleScreenStateChanged(screenOn);mCarrierNetworkNotifier.handleScreenStateChanged(screenOn);startConnectivityScan(SCAN_ON_SCHEDULE);
}

开始连接扫描。 扫描方式根据当前的屏幕状态和WiFi状态选择。这里会选择PNO扫描。

private void startConnectivityScan(boolean scanImmediately) {localLog("startConnectivityScan: screenOn=" + mScreenOn+ " wifiState=" + stateToString(mWifiState)+ " scanImmediately=" + scanImmediately+ " wifiEnabled=" + mWifiEnabled+ " wifiConnectivityManagerEnabled="+ mWifiConnectivityManagerEnabled);if (!mWifiEnabled || !mWifiConnectivityManagerEnabled) {return;}if (mWifiState != WIFI_STATE_CONNECTED && mWifiState != WIFI_STATE_DISCONNECTED) {return;}if (mScreenOn) {startPeriodicScan(scanImmediately);} else {if (mWifiState == WIFI_STATE_DISCONNECTED && !mPnoScanStarted) {startDisconnectedPnoScan();}}}

当屏幕关闭且WiFI未连接时,开启PNO扫描,初始化scan的设置。
可以看到扫描间隔为20s:scanSettings.periodInMs = DISCONNECTED_PNO_SCAN_INTERVAL_MS

private static final int DISCONNECTED_PNO_SCAN_INTERVAL_MS = 20 * 1000; // 20 seconds
private void startDisconnectedPnoScan() {// Initialize PNO settingsPnoSettings pnoSettings = new PnoSettings();List<PnoSettings.PnoNetwork> pnoNetworkList = mConfigManager.retrievePnoNetworkList();int listSize = pnoNetworkList.size();pnoSettings.networkList = new PnoSettings.PnoNetwork[listSize];pnoSettings.networkList = pnoNetworkList.toArray(pnoSettings.networkList);pnoSettings.min5GHzRssi = mMin5GHzRssi;pnoSettings.min24GHzRssi = mMin24GHzRssi;pnoSettings.initialScoreMax = mInitialScoreMax;pnoSettings.currentConnectionBonus = mCurrentConnectionBonus;pnoSettings.sameNetworkBonus = mSameNetworkBonus;pnoSettings.secureBonus = mSecureBonus;pnoSettings.band5GHzBonus = mBand5GHzBonus;// Initialize scan settingsScanSettings scanSettings = new ScanSettings();scanSettings.band = getScanBand();scanSettings.reportEvents = WifiScanner.REPORT_EVENT_NO_BATCH;scanSettings.numBssidsPerScan = 0;scanSettings.periodInMs = DISCONNECTED_PNO_SCAN_INTERVAL_MS;mPnoScanListener.clearScanDetails();mScanner.startDisconnectedPnoScan(scanSettings, pnoSettings, mPnoScanListener);mPnoScanStarted = true;
}

WifiScanner.java
startDisconnectedPnoScan --> startPnoScan 开启PNO扫描

public void startDisconnectedPnoScan(ScanSettings scanSettings, PnoSettings pnoSettings,PnoScanListener listener) {Preconditions.checkNotNull(listener, "listener cannot be null");Preconditions.checkNotNull(pnoSettings, "pnoSettings cannot be null");int key = addListener(listener);if (key == INVALID_KEY) return;validateChannel();pnoSettings.isConnected = false;startPnoScan(scanSettings, pnoSettings, key);
}

startPnoScan发送PNO扫描(CMD_START_PNO_SCAN)的消息,由WifiScanningServiceImpl处理。

private void startPnoScan(ScanSettings scanSettings, PnoSettings pnoSettings, int key) {// Bundle up both the settings and send it across.Bundle pnoParams = new Bundle();// Set the PNO scan flag.scanSettings.isPnoScan = true;pnoParams.putParcelable(PNO_PARAMS_SCAN_SETTINGS_KEY, scanSettings);pnoParams.putParcelable(PNO_PARAMS_PNO_SETTINGS_KEY, pnoSettings);mAsyncChannel.sendMessage(CMD_START_PNO_SCAN, 0, key, pnoParams);
}

WifiScanningServiceImpl.java
从StartedState状态开始,获取到PNO扫描的设置以后,进入HwPnoScanState状态。

class StartedState extends State {.......@Overridepublic boolean processMessage(Message msg) {ClientInfo ci = mClients.get(msg.replyTo);switch (msg.what) {case WifiScanner.CMD_START_PNO_SCAN:........pnoParams.setDefusable(true);PnoSettings pnoSettings =pnoParams.getParcelable(WifiScanner.PNO_PARAMS_PNO_SETTINGS_KEY);if (mScannerImpl.isHwPnoSupported(pnoSettings.isConnected)) {deferMessage(msg);transitionTo(mHwPnoScanState);} else {replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "not supported");}.................}return HANDLED;}
}

如果已经有PNO扫描,则进入StartedState。如果没有,则addHwPnoScanRequest。
注意:这里的StartedState是WifiBackgroundScanStateMachine中的StartedState。

class HwPnoScanState extends State {@Overridepublic boolean processMessage(Message msg) {ClientInfo ci = mClients.get(msg.replyTo);switch (msg.what) {case WifiScanner.CMD_START_PNO_SCAN:Bundle pnoParams = (Bundle) msg.obj;if (pnoParams == null) {replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "params null");return HANDLED;}pnoParams.setDefusable(true);PnoSettings pnoSettings =pnoParams.getParcelable(WifiScanner.PNO_PARAMS_PNO_SETTINGS_KEY);ScanSettings scanSettings =pnoParams.getParcelable(WifiScanner.PNO_PARAMS_SCAN_SETTINGS_KEY);if (addHwPnoScanRequest(ci, msg.arg2, scanSettings, pnoSettings)) {replySucceeded(msg);} else {replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "bad request");transitionTo(mStartedState);}break;...........return HANDLED;}
}

addHwPnoScanRequest --> addPnoScanRequest

private boolean addHwPnoScanRequest(ClientInfo ci, int handler, ScanSettings scanSettings,PnoSettings pnoSettings) {if (ci == null) {Log.d(TAG, "Failing scan request ClientInfo not found " + handler);return false;}if (!mActivePnoScans.isEmpty()) {loge("Failing scan request because there is already an active scan");return false;}WifiNative.PnoSettings nativePnoSettings =convertSettingsToPnoNative(scanSettings, pnoSettings);if (!mScannerImpl.setHwPnoList(nativePnoSettings, mPnoScanStateMachine)) {return false;}logScanRequest("addHwPnoScanRequest", ci, handler, null, scanSettings, pnoSettings);addPnoScanRequest(ci, handler, scanSettings, pnoSettings);return true;
}
private void addPnoScanRequest(ClientInfo ci, int handler, ScanSettings scanSettings,PnoSettings pnoSettings) {mActivePnoScans.addRequest(ci, handler, WifiStateMachine.WIFI_WORK_SOURCE,Pair.create(pnoSettings, scanSettings));addInternalClient(ci);
}

WifiBackgroundScanStateMachine --> StartedState

未完待续。。。。

关注公众号,获取更多开发必备知识
在这里插入图片描述


http://chatgpt.dhexx.cn/article/UhlGE8sc.shtml

相关文章

[RK3288][Android6.0] WiFi之PNO功能了解

Platform: Rockchip OS: Android 6.0 Kernel: 3.10.92 概念: PNO 即Preferred Network Offload,用于系统在休眠的时候连接WiFi 此功能是在Android3.1加入的 缺陷: 在使用PNO时,有潜在泄露个人隐私的风险。这里没明白,意思是说PNO会发送之前的连接过的网络给AP,然…

二值化的方法

1.二值化 图像二值化的目的是最大限度的将图像中感兴趣部分保留下来&#xff0c;在很多情况下&#xff0c;也是进行图像分析、特征提取与模式识别之前的必要的图像预处理过程。在这些庞大的图像二值化分类方法中&#xff0c;基于直方图的全局二值算法占有了绝对的市场份额&…

OpenCV-Python学习(10)—— OpenCV 图像二值化处理(cv.threshold)

1. 学习目标 理解图像的分类&#xff0c;不同类型的图像的区别&#xff1b;对图像进行二值化处理&#xff0c;对【 cv.threshold 】函数的理解。 2. 图像分类 2.1 不同类型图像说明 按照颜色对图像进行分类&#xff0c;可以分为二值图像、灰度图像和彩色图像。 二值图像&…

python 图像二值化处理

python 图像二值化处理 import os from PIL import Image import shutilfile_path ./5-crack919/mask/ save_path ./5-crack919/mask01/ if not os.path.exists(save_path):os.makedirs(save_path) ###二值化 for filename in os.listdir(file_path):print(filename)img …

图像处理之二值化图像

图像二值化就是将图像上的像素点的灰度值设置为0或255&#xff0c;也就是将整个图像呈现出明显的黑白效果。将所有灰度大于或等于阈值的像素被判定为属于特定物体&#xff0c;其灰度值为255表示&#xff0c;否则这些像素点被排除在物体区域以外&#xff0c;灰度值为0&#xff0…

二值化之阈值处理

写于开头的废话&#xff1a;哒哒哒.......这应该是马蹄的声音&#xff01;我在告诉你&#xff0c;我又来了&#xff01;不得不说的还是自己的悲伤&#xff0c;经营了那么久的CSDN居然说关就给我关了&#xff0c;过去的还是没法过去&#xff0c;我始终不能从失去的痛苦之中走出来…

图像处理之二值化

图像处理之二值化 二值化方式 二值化算法 二值化方式 二值化方式分为五种&#xff1a; THRESH_BINARY&#xff1a;将大于某一个阈值的变成最大值&#xff0c;其他为0 THRESH_BINARY_INV&#xff1a;和THRESH_BINARY恰好相反 THRESH_TRUNC&#xff1a; trunc就是截断的意思&…

二值化方法

一、全局阈值法 1.固定阈值方法 该方法是对于输入图像中的所有像素点统一使用同一个固定阈值。其基本思想如下: 其中&#xff0c;T为全局阈值。 缺点:很难为不同的输入图像确定最佳阈值。 2.Otsu算法 Otsu算法又称最大类间方差法 先明确两个概念: (1)均值 (2)方差 图像的阈…

图像处理一之-摄像头二值化处理-(什么是二值化)

图像二值化 binary image 什么是二值化&#xff1a; 二值化是图像分割的一种最简单的方法。二值化可以把灰度图像转换成二值图像。把大于某个临界灰度值(阈值)的像素灰度设为灰度极大值(255)&#xff0c;把小于这个值的像素灰度设为灰度极小值&#xff08;0&#xff09;&#…

C#多线程详解(一) Thread.Join()的详解

什么是进程&#xff1f; 当一个程序开始运行时&#xff0c;它就是一个进程&#xff0c;进程包括运行中的程序和程序所使用到的内存和系统资源。 而一个进程又是由多个线程所组成的。 什么是线程&#xff1f; 线程是程序中的一个执行流&#xff0c;每个线程都有自己的专有寄存器…

1.java的协程_虚拟线程

盼过了春天盼秋天&#xff0c;从2017年到今天五年了&#xff0c;终于盼到loom转正了&#xff0c;当看到jdk19预览api的时候心情那个激动。。。期待已久的协程终于来临&#xff0c;再也不羡慕别人家的go孩子&#xff0c;咱终于可以理直气壮的说一句&#xff1a;咱也有&#xff0…

Kotlin协程到底是怎么切换线程的?你是否知晓?

好文推荐&#xff1a; 作者&#xff1a;RicardoMJiang 前言 之前对协程做了一个简单的介绍&#xff0c;回答了协程到底是什么的问题,感兴趣的同学可以了解下&#xff1a;【带着问题学】协程到底是什么? 通过上文&#xff0c;我们了解了以下内容 1.kotlin协程本质上对线程池的…

进程和线程、协程的区别

一、进程 进程是程序一次动态执行的过程&#xff0c;是程序运行的基本单位。每个进程都有自己的独立内存空间&#xff0c;不同进程通过进程间通信来通信。进程占据独立的内存&#xff0c;所以上下文进程间的切换开销&#xff08;栈、寄存器、页表、文件句柄等&#xff09;比较…

进程、线程与协程的比较

进程、线程和协程是三个在多任务处理中常听到的概念&#xff0c;三者各有区别又相互联系。 一、并行和并发 在介绍进程、线程和协程这三个概念之前&#xff0c;有两个操作系统中的相关概念需要简单解释一下&#xff1a;并行和并发。 并行&#xff1a;指多个任务同时执行。 并…

进程、线程、协程

进程、线程、协程 一、概念与区分 1、进程 进程是程序一次动态执行的过程&#xff0c;是程序运行的基本单位。每个进程都有自己的独立内存空间&#xff0c;不同进程通过进程间通信来通信。进程占据独立的内存&#xff0c;所以上下文进程间的切换开销&#xff08;栈、寄存器、…

终于明白:有了线程,为什么还要有协程?

并发的发展历史 其实&#xff0c;在早期计算机并没有包含操作系统&#xff0c;这个时候&#xff0c;这个计算机只跑一个程序&#xff0c;这个程序独享计算机的所有资源&#xff0c;这个时候不存在什么并发问题&#xff0c;但是对计算机的资源来说&#xff0c;确实是一种浪费。…

Java中的多线程(线程间通信)

/学习笔记/ 线程间通信&#xff1a; 多个线程在处理同一资源&#xff0c;但是任务却不同。 先看一个例子&#xff0c;采用两个线程执行进行输入和输出任务&#xff1a; //资源class Resource{String name;String sex;}//输入class Input implements Runnable{Resource r ;// …

协程和线程的区别、协程原理与优缺点分析、在Java中使用协程

文章目录 什么是协程协程的优点与缺点协程实现原理.协程与线程在不同编程语言的实现在Java中使用协程Kilim介绍Kilim整合Java,使用举例 小总结 什么是协程 相对于协程&#xff0c;你可能对进程和线程更为熟悉。进程一般代表一个应用服务&#xff0c;在一个应用服务中可以创建多…

进程、线程和协程之间的区别和联系

文章目录 一、进程二、线程三、进程和线程的区别与联系四、一个形象的例子解释进程和线程的区别五、进程/线程之间的亲缘性六、协程 一、进程 进程&#xff0c;直观点说&#xff0c;保存在硬盘上的程序运行以后&#xff0c;会在内存空间里形成一个独立的内存体&#xff0c;这个…

简单了解线程和协程(C#)

1.为什么需要线程和协程&#xff1a; &#xff08;1&#xff09;使程序中的任务可以并发执行&#xff0c;让程序同时处理多个任务&#xff0c;提高程序的运行效率和响应速度 &#xff08;2&#xff09;线程和协程可以共享同一个进程的资源&#xff0c;避免多个进程之间的资源浪…