版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

本文链接:https://blog.csdn.net/zhangdaxia2/article/details/82530012

WIFI启动过程,WifiStateMachine加载驱动固件,连接上wpa_s的socket并检查好配置文件后,进入到DisconnectedState状态。在DisconnectedState状态下启动了mWifiConnectivityManager.handleConnectionStateChanged(WifiConnectivityManager.WIFI_STATE_DISCONNECTED)。这是启动扫描的第一步。

handleConnectionStateChanged(WifiConnectivityManager.WIFI_STATE_DISCONNECTED)startConnectivityScan(SCAN_ON_SCHEDULE)  // SCAN_ON_SCHEDULE == fause
  • 1
  • 2

在startConnectivityScan(SCAN_ON_SCHEDULE)中看到:

if (mScreenOn) {startPeriodicScan(scanImmediately); // 亮屏下周期扫描
} else { // screenOffif (mWifiState == WIFI_STATE_CONNECTED) {startConnectedPnoScan();} else {startDisconnectedPnoScan(); // 息屏下扫描wifi}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

以上代码的扫描过程根据屏幕是否点亮,分成两种情况。一种亮屏下,开始周期扫描。另一种是息屏状态,使用pno扫描。pno扫描又根据当前WifiState是连接状态还是断开状态分为两种情况。

一、startPeriodicScan方法

这个方法用来在亮屏下进行周期扫描:

if (!ENABLE_BACKGROUND_SCAN) {if (scanImmediately) {resetLastPeriodicSingleScanTimeStamp();}Log.e(TAG, "PeriodicSingleScan by zhangdalei");mPeriodicSingleScanInterval = PERIODIC_SCAN_INTERVAL_MS;startPeriodicSingleScan();
} 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

二、startConnectedPnoScan方法

因为ENABLE_CONNECTED_PNO_SCAN变量设置为false,因此这个方法将不被执行。

三、startDisconnectedPnoScan方法

这是我们现在需要关注的PNO扫描过程:

    private void startDisconnectedPnoScan() {// Initialize PNO settingsPnoSettings pnoSettings = new PnoSettings();ArrayList<PnoSettings.PnoNetwork> pnoNetworkList =mConfigManager.retrieveDisconnectedPnoNetworkList();int listSize = pnoNetworkList.size();if (listSize == 0) {// No saved networklocalLog("No saved network for starting disconnected PNO.");return;}// 设置 pnoSetting 对象参数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;    // 20s// TODO: enable exponential back off scan later to further save energy// scanSettings.maxPeriodInMs = 8 * scanSettings.periodInMs;mPnoScanListener.clearScanDetails();// 调用Scanner的函数,启动断开wifi下的pno扫描mScanner.startDisconnectedPnoScan(scanSettings, pnoSettings, mPnoScanListener);mPnoScanStarted = true;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

在startDisconnectedPnoScan方法中,将传入的mPnoScanListener放到一个map中,得到一个key。然后调用

startPnoScan(scanSettings, pnoSettings, key);
  • 1

继续看:

@Scanner.java
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);    // 将setting保存到budle,用来发送pnoParams.putParcelable(PNO_PARAMS_PNO_SETTINGS_KEY, pnoSettings);// 调用mAsyncChannel发送消息mAsyncChannel.sendMessage(CMD_START_PNO_SCAN, 0, key, pnoParams);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

在WifiStateMachine::DriverStartedState中定义了mWifiScanner。初始化时,出入其中变量mService为WifiScanningServiceImpl。现在看看WifiScanningServiceImpl的初始化过程:

messager为WifiScanningServiceImpl对象,WifiScanner是一个client,WifiScanningServiceImpl是服务端,WifiScanner通过AsyncChannel向WifiScanningServiceImpl发送扫描的命令,在Scanner中完成扫描动作。

在WifiScanningServiceImpl::startService中创建了几个状态机对象并开启了状态机。现在关注mPnoScanStateMachine。

WifiPnoScanStateMachine(Looper looper) {super("WifiPnoScanStateMachine", looper);setLogRecSize(512);setLogOnlyTransitions(false);// CHECKSTYLE:OFF IndentationCheckaddState(mDefaultState);addState(mStartedState, mDefaultState);addState(mHwPnoScanState, mStartedState);addState(mSingleScanState, mHwPnoScanState);addState(mSwPnoScanState, mStartedState);// CHECKSTYLE:ON IndentationCheck  初始化为mDefaultState并setInitialState(mDefaultState);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

CMD_START_PNO_SCAN将在SwPnoScanState这个状态中处理。

@WifiScanningServiceImpl.java::SwPnoScanStatepublic 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_SETTIGS_KEY);if (addSwPnoScanRequest(ci, msg.arg2, scanSettings,pnoSettings)) {replySucceeded(msg);} else {replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "bad request");transitionTo(mStartedState);}break;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

在处理上次发送的CMD_START_PNO_SCAN消息时,在此处将调用StartedState()做进一步处理。现在看起实现:

@WifiScanningServiceImpl.java::StartedState
public 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);// This message is handled after the transition to SwPnoScan/HwPnoScan statedeferMessage(msg);if (mScannerImpl.isHwPnoSupported(pnoSettings.isConnected)) {transitionTo(mHwPnoScanState);} else {transitionTo(mSwPnoScanState);}break;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

我们这里支持HardwarePno,将计入mHwPnoScanState状态,进行处理CMD_START_PNO_SCAN消息:

@HwPnoScanState
public 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;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

上面代码进一步调用addHwPnoScanRequest()函数处理PNO扫描请求。

private boolean addHwPnoScanRequest(ClientInfo ci, int handler, ScanSettings scanSettings, PnoSettings pnoSettings) {WifiNative.PnoSettings nativePnoSettings = convertPnoSettingsToNative(pnoSettings);if (!mScannerImpl.setHwPnoList(nativePnoSettings,mPnoScanStateMachine)) {return false;}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

这里的mScannerImpl实际是SupplicantWifiScannerImpl类。这里将调用到SupplicantWifiScannerImpl::setHwPnoList();

public boolean setHwPnoList(WifiNative.PnoSettings settings,WifiNative.PnoEventHandler eventHandler) {synchronized (mSettingsLock) {if (mPnoSettings != null) {Log.w(TAG, "Already running a PNO scan");return false;}mPnoEventHandler = eventHandler;mPnoSettings = settings;if (!setNetworkPriorities(settings.networkList)) return false;// For supplicant based PNO, we start the scan immediately when we set pno list.processPendingScans();return true;}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

调用processPendingScans()进一步处理扫描请求。processPendingScans()函数用来处理各种扫描请求,包括正常的扫描和PNO扫描。现在我们只看PNO扫描:

private void processPendingScans() {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)) {// 启动PNO扫描status = restartHwPnoScan();} else {// 启动PNO扫描status = startHwPnoScan();}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;}});}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

我们看restartHwPnoScan()的执行流程。不过可想到,接下来将要向wpa_supplicant发送执行了。

private boolean restartHwPnoScan() {mHwPnoDebouncer.forceStopPnoScan();return mHwPnoDebouncer.startPnoScan(mHwPnoDebouncerListener);
}public boolean startPnoScan(Listener listener) {if (DBG) Log.d(TAG, "Starting PNO scan");mListener = listener;if (!setPnoState(true)) {mListener = null;return false;}return true;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

setPnoState()将调用updatePnoState()函数:

private boolean updatePnoState(boolean enable) {if (mCurrentPnoState == enable) {if (DBG) Log.d(TAG, "PNO state is already " + enable);return true;}mLastPnoChangeTimeStamp = mClock.elapsedRealtime();// 调用Native函数向wpa_supplicant发消息了if (mWifiNative.setPnoScan(enable)) {Log.d(TAG, "Changed PNO state from " + mCurrentPnoState + " to " + enable);mCurrentPnoState = enable;return true;} else {Log.e(TAG, "PNO state change to " + enable + " failed");mCurrentPnoState = false;return false;}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
public boolean setPnoScan(boolean enable) {String cmd = enable ? "SET pno 1" : "SET pno 0";return doBooleanCommand(cmd);
}
  • 1
  • 2
  • 3
  • 4

大体的扫描流程就是这样了。

问题

Pno存在这样一个问题:

设置一个wifi密码为空,让设备连接。连接上后,设置wifi有密码。这个时候PNO能够匹配SSID找到wifi,但是加密方式改变了。这是PNO就会持续不断的扫描,导致功耗高。需要解决这个问题。

在SupplicantWifiScannerImpl.java中注册了接收WifiMonitor扫描结果的消息的handle。当扫描错误或是有扫描结果时将调用handle处理。

@SupplicantWifiScannerImpl.java
WifiMonitor.getInstance().registerHandler(mWifiNative.getInterfaceName(), WifiMonitor.SCAN_FAILED_EVENT, mEventHandler);
WifiMonitor.getInstance().registerHandler(mWifiNative.getInterfaceName(), WifiMonitor.SCAN_RESULTS_EVENT, mEventHandler);@Override
public boolean handleMessage(Message msg) {switch(msg.what) {case WifiMonitor.SCAN_FAILED_EVENT:Log.w(TAG, "Scan failed");mAlarmManager.cancel(mScanTimeoutListener);reportScanFailure();processPendingScans();break;case WifiMonitor.SCAN_RESULTS_EVENT:mAlarmManager.cancel(mScanTimeoutListener);pollLatestScanData();processPendingScans();break;default:// ignore unknown event}return true;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

不管是SCAN_FAILED_EVENT或是SCAN_RESULTS_EVENT都讲调用processPendingScans()进一步处理PNO扫描。

lse 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();} else {status = startHwPnoScan();}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;}});}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

现在看上面代码的一些细节:

一是isHwPnoScanRequired()函数:

private boolean isHwPnoScanRequired(boolean isConnectedPno) {return (!isConnectedPno & mHwPnoScanSupported);
}private boolean isHwPnoScanRequired() {if (mPnoSettings == null) return false;return isHwPnoScanRequired(mPnoSettings.isConnected);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

如果mPnoSettings为NULL,返回false,PNO不在执行。然后mPnoSettings.isConnected我们这里是断开wifi时的pno,为false. mPnoSettings在setHwPnoList时设置。

但是当我们PNO扫描成功,并不会把mPnoSettings设置为NULL,这将导致一次PNO扫描之后,将接着下一次PNO扫描。代码如下;

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;}});
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

所以我们需要在扫描成功是吧mPnoSettings设置为NULL。解决问题。

[转载]Android7 WIFI系统 PNO机制流程详解和隐藏BUG修改相关推荐

  1. Android7 WIFI系统 PNO机制流程详解和隐藏BUG修改

    WIFI启动过程,WifiStateMachine加载驱动固件,连接上wpa_s的socket并检查好配置文件后,进入到DisconnectedState状态.在DisconnectedState状态 ...

  2. 助创cms众筹 php,【教程】助创cms众筹系统完整测试流程详解

    原标题:[教程]助创cms众筹系统完整测试流程详解 这两年提到互联网金融,不得不提的一个词语:众筹.的确相比飘忽不定的股市和频发跑路P2P,众筹具备低风险,收益高,周期短等各方面的优势.为了帮助更多朋 ...

  3. 直销系统模式开发流程详解

    现在直销系统已经成为了直销企业的主要管理渠道,它不仅可以节省直销会员的管理成本,还能够大大提高直销商奖金结算的业务效率.那么,直销系统的开发流程是怎样的呢?下面就来详细说一下. 第一步--概要设计 其 ...

  4. 电商新零售系统划分及供应链系统流程详解

    [声明在先]:文中所有业务流程及系统设计均由电商标准流程改造,不具有任何商业倾向性. 前序文章讲解了产品经理从接到任务开始,到出具电商后台整体解决方案的过程,本文重点讲述电商后台核心系统的划分及主营供 ...

  5. 2010年系统架构师考试题详解

    原文地址为: 2010年系统架构师考试题详解 考试科目一:综合知识 采用微内核结构的操作系统提高了系统的灵活性和可扩展性,(1) (1)A.并增强了系统的可靠性和可移植性,可运行于分布式系统中 B.并 ...

  6. 2015年系统架构师考试题详解

    原文地址为: 2015年系统架构师考试题详解 考试科目一:综合知识 某航空公司机票销售系统有n个售票点,该系统为每个售票点创建一个进程Pi(i=1,2,-,n)管理机票销售.假设Tj(j=1,2,-, ...

  7. 2011年系统架构师考试题详解

    原文地址为: 2011年系统架构师考试题详解 考试科目一:综合知识 操作系统为用户提供了两类接口:操作一级和程序控制一级的接口,以下不属于操作一级的接口是(1). (1)A.操作控制命令 B.系统调用 ...

  8. Android事件流程详解

    Android事件流程详解 网络上有不少博客讲述了android的事件分发机制和处理流程机制,但是看过千遍,总还是觉得有些迷迷糊糊,因此特地抽出一天事件来亲测下,向像我一样的广大入门程序员详细讲述an ...

  9. View的绘制-draw流程详解

    目录 作用 根据 measure 测量出的宽高,layout 布局的位置,渲染整个 View 树,将界面呈现出来. 具体分析 以下源码基于版本27 DecorView 的draw 流程 在<Vi ...

最新文章

  1. window下lamp环境搭建
  2. 大有可为的GNN:DeepWalk
  3. java如何循环调用方法_Java:调用方法的“中断”循环?
  4. php获取url返回的json,【求助】本地页面如何取某个URL返回的json
  5. openwrt添加自己的iptables的targe编译报错
  6. python-json下载以及分析-案例
  7. python大家都是怎么学的_你们都是怎么学 Python 的?
  8. JavaScript + jQuery 知识复习总结(附超实用jQuery中文文档)
  9. 抽象代数学习笔记三《群:对称性变换与对称性群》
  10. 基于Python的Web开发
  11. 账务实时交易系统设计思考
  12. CPU占用100%的一般原因及解决办法
  13. 类继承和接口继承的差别
  14. 【Python】UnicodeDecodeError: 'gbk' codec can't decode byte 0xfe
  15. 计算机网络实验(Wireshark 抓包工具使用、WinPcap 编程、协议分析流量统计程序的编写)
  16. 狄利克雷分布公式_一文详解隐含狄利克雷分布(LDA)
  17. android 与后台实时视频,android选择视频文件上传到后台服务器
  18. 我对大学的憧憬||每个人都有自己的罗马
  19. jfif怎么改成jpg格式
  20. 『大牛公司机构近期研究报告大合集』第二版

热门文章

  1. webstrom怎么配置git并提交
  2. 增强现实入门实战,使用ArUco标记实现增强现实
  3. 解决LaTeX:!Package CJK Error:Invalid character code报错
  4. 为什么重写equals方法时一定要重写hashCode方法
  5. 多目标应用:多目标蜣螂优化算法求解多旅行商问题(Multiple Traveling Salesman Problem, MTSP)
  6. 阿里云Linux服务器新手入门(2核4G)
  7. 实验六软件包管理linux,oeasy教您玩转linux010202软件包管理apt
  8. php批量改名工具,批量更名大师下载_批量改名大师(Win-Tool之文件批量更名工具) 1.8.7 演示版_极速下载站_软件下载...
  9. snmpset对象不可写_写 I/O 路径 (FTT1/RF2) 对比 – Nutanix vs VMware vSAN
  10. 开发工具:第六章:Java开发者相关的所有软件安装包(35.55GB的资源)