套接字初始化和报文收取


1. netlink_manager.cpp 中 NetlinkManager 类的 start 函数中通过 WatchSocket 将 async_netlink_fd_ 绑定到 netlink 套接字打开,并通过 SubscribeToEvents 订阅对应的广播事件(NL80211_MULTICAST_GROUP_REG 和 NL80211_MULTICAST_GROUP_SCAN),从而该套接字可以收到 SCAN 的广播报文;

2. 套接字的处理函数 ReceivePacketAndRunHandler 通过 read 报文进行收包处理,广播报文调用 BroadcastHandler 进行处理;

3. BroadcastHandler 中判断 command 类型,如果是 NL80211_CMD_NEW_SCAN_RESULTS 或者 NL80211_CMD_SCAN_ABORTED 则调用 OnScanReaultReady 进行向上回调广播

4. 需要特别关注 WatchSocket,将其打开的套接字文件描述放到 event_loop_ 的 event_poll 中等到事件触发报文收取,实际的操作在新的线程中发生;

5. OnScanResultsReady 中对报文进行处理,解析对应的 ssids 和 freqs 然后调用所有注册了相关事件的 handlers

回调事件注册过程


1. 在 netlink_manager.cpp 中 NetlinkManager 类的 OnScanResultsReady 中调用的回调实际上实在 ScannerImpl 类创建和初始化的时候由其构造函数调用了 scan_utils_ 的 SubscribeScanResultNotification 将其 OnScanResultsReady 注册进来的,因此其在此被调用;

2. 同样地 ScannerImpl 类地 OnScanResultsReady 处理过程为调用其 scan_event_handler_ 的 OnScanResultReady 方法;

3. scan_event_handler_ 的初始化是显式调用了 subscribeScanEvents 进行指针赋值的,其调用发生在 WifiNl80211Manager.java 中的 setupInterfaceForClientMode 接口中,其内部通过 wificondScanner 的 subscribeScanEvents 将 scanEventHandler 注册进来:

    /*** Set up an interface for client (STA) mode.** @param ifaceName Name of the interface to configure.* @param executor The Executor on which to execute the callbacks.* @param scanCallback A callback for framework initiated scans.* @param pnoScanCallback A callback for PNO (offloaded) scans.* @return true on success.*/public boolean setupInterfaceForClientMode(@NonNull String ifaceName,@NonNull @CallbackExecutor Executor executor,@NonNull ScanEventCallback scanCallback, @NonNull ScanEventCallback pnoScanCallback) {Log.d(TAG, "Setting up interface for client mode");if (!retrieveWificondAndRegisterForDeath()) {return false;}if (scanCallback == null || pnoScanCallback == null || executor == null) {Log.e(TAG, "setupInterfaceForClientMode invoked with null callbacks");return false;}IClientInterface clientInterface = null;try {clientInterface = mWificond.createClientInterface(ifaceName);} catch (RemoteException e1) {Log.e(TAG, "Failed to get IClientInterface due to remote exception");return false;}if (clientInterface == null) {Log.e(TAG, "Could not get IClientInterface instance from wificond");return false;}Binder.allowBlocking(clientInterface.asBinder());// Refresh HandlersmClientInterfaces.put(ifaceName, clientInterface);try {IWifiScannerImpl wificondScanner = clientInterface.getWifiScannerImpl();if (wificondScanner == null) {Log.e(TAG, "Failed to get WificondScannerImpl");return false;}mWificondScanners.put(ifaceName, wificondScanner);Binder.allowBlocking(wificondScanner.asBinder());ScanEventHandler scanEventHandler = new ScanEventHandler(executor, scanCallback);mScanEventHandlers.put(ifaceName,  scanEventHandler);wificondScanner.subscribeScanEvents(scanEventHandler);PnoScanEventHandler pnoScanEventHandler = new PnoScanEventHandler(executor,pnoScanCallback);mPnoScanEventHandlers.put(ifaceName,  pnoScanEventHandler);wificondScanner.subscribePnoScanEvents(pnoScanEventHandler);} catch (RemoteException e) {Log.e(TAG, "Failed to refresh wificond scanner due to remote exception");}return true;}

4. wificondScanner 则是在由 mWifiCond 通过 createClientInterface 创建的 clientInterface 通过其 getWifiScannerImpl 获取到的;

5. getWifiScannerImpl 通过 binder 机制透过 HIDL 调用到了 client_interface_impl.cpp 中 ClientInterfaceImpl 类的 GetScanner 方法,返回了对应的 scanner_;

6. scanner_ 的创建就是在 ClientInterfaceImpl 的构造函数中进行的,而其创建是在 mWifiCond 的 HIDL server 端通过 createClientInterface 中;

7. mWifiCond 则是在 retrieveWificondAndRegisterForDeath 中通过获取 HIDL 服务 WIFI_NL80211_SERVICE 而初始化的,同样发生在 setupInterfaceForClientMode 接口中;

8. 再回过头来看 scanEventHandler,其类型为 ScanEventHandler,通过传入的 executor 和 scanCallback 来进行初始化,scanCallback 的类型是 ScanEventCallback:

/* ScanEventCallback *//*** Interface used when waiting for scans to be completed (with results).*/public interface ScanEventCallback {/*** Called when scan results are available. Scans results should then be obtained from* {@link #getScanResults(String, int)}.*/void onScanResultReady();/*** Called when a scan has failed.*/void onScanFailed();}/* ScanEventHandler */private class ScanEventHandler extends IScanEvent.Stub {private Executor mExecutor;private ScanEventCallback mCallback;ScanEventHandler(@NonNull Executor executor, @NonNull ScanEventCallback callback) {mExecutor = executor;mCallback = callback;}@Overridepublic void OnScanResultReady() {Log.d(TAG, "Scan result ready event");Binder.clearCallingIdentity();mExecutor.execute(() -> mCallback.onScanResultReady());}@Overridepublic void OnScanFailed() {Log.d(TAG, "Scan failed event");Binder.clearCallingIdentity();mExecutor.execute(() -> mCallback.onScanFailed());}}

通过对比可以发现其实 ScanEventHandler 就是再 ScanEventCallback 的基础之上加入了 executor 线程,将 callback 调用过程放到该线程中执行,将二者进行了绑定而已;

9. 在 WifiNative.java 的 WifiNative 类中通过 setupInterfaceForClientInScanMode 或者 setupInterfaceForClientInConnectivityMode 而调用了 WifiCondManager 类的 setupInterfaceForClientMode 接口,其内部以接口名为参数新创建 NormalScanEventCallback 作为回调参数传入:

    private class NormalScanEventCallback implements WifiNl80211Manager.ScanEventCallback {private String mIfaceName;NormalScanEventCallback(String ifaceName) {mIfaceName = ifaceName;}@Overridepublic void onScanResultReady() {Log.d(TAG, "Scan result ready event");mWifiMonitor.broadcastScanResultEvent(mIfaceName);}@Overridepublic void onScanFailed() {Log.d(TAG, "Scan failed event");mWifiMonitor.broadcastScanFailedEvent(mIfaceName);}}

10. 如上面所示,NormalScanEventCallback 实际上以类初始化时传入的接口为参数,均调用了 WifiMonitor 类的广播处理函数 broadcastScanResultEvent 和 broadcastScanFailedEvent 进行广播处理;

11. broadcastScanResultEvent 接口调用sendMessage 将 SCAN_RESULTS_EVENT 消息发送出去,最终的发送其实是在 mHandlerMap 中查找每个 iface 对应的 Handler 对象,将消息发给 Target:

    /*** Similar functions to Handler#sendMessage that send the message to the registered handler* for the given interface and message what.* All of these should be called with the WifiMonitor class lock*/private void sendMessage(String iface, int what) {sendMessage(iface, Message.obtain(null, what));}private void sendMessage(String iface, int what, Object obj) {sendMessage(iface, Message.obtain(null, what, obj));}private void sendMessage(String iface, int what, int arg1) {sendMessage(iface, Message.obtain(null, what, arg1, 0));}private void sendMessage(String iface, int what, int arg1, int arg2) {sendMessage(iface, Message.obtain(null, what, arg1, arg2));}private void sendMessage(String iface, int what, int arg1, int arg2, Object obj) {sendMessage(iface, Message.obtain(null, what, arg1, arg2, obj));}private void sendMessage(String iface, Message message) {SparseArray<Set<Handler>> ifaceHandlers = mHandlerMap.get(iface);if (iface != null && ifaceHandlers != null) {if (isMonitoring(iface)) {Set<Handler> ifaceWhatHandlers = ifaceHandlers.get(message.what);if (ifaceWhatHandlers != null) {for (Handler handler : ifaceWhatHandlers) {if (handler != null) {sendMessage(handler, Message.obtain(message));}}}} else {if (mVerboseLoggingEnabled) {Log.d(TAG, "Dropping event because (" + iface + ") is stopped");}}} else {if (mVerboseLoggingEnabled) {Log.d(TAG, "Sending to all monitors because there's no matching iface");}for (Map.Entry<String, SparseArray<Set<Handler>>> entry : mHandlerMap.entrySet()) {if (isMonitoring(entry.getKey())) {Set<Handler> ifaceWhatHandlers = entry.getValue().get(message.what);for (Handler handler : ifaceWhatHandlers) {if (handler != null) {sendMessage(handler, Message.obtain(message));}}}}}message.recycle();}private void sendMessage(Handler handler, Message message) {message.setTarget(handler);message.sendToTarget();}

最终在 message.sendToTarget 中其实调用了 Handler 的 sendMessage 接口进行消息发送

Handler 的源头


12. 在 WifiCondScannerImpl 的构造函数中通过 wifiMonitor 的 registerHandler 接口将 mEventHandler 注册到 WifiMonitor 类的对象中,而 mEventHandler 则是新创建的 Handler 对象,其中 Looper 对象则由调用者传入;

13. Looper 是在 WifiScannerImpl 类的创建过程中传入的,WifiScannerImpl 类有两个继承类,实际上为 WificondScannerImpl 和 HalWifiScannerImpl 类,根据 wifiNative 的 getBgScanCapabilities 类型区分,其实际创建过程发生在 WifiScanningServiceImpl.java 中,WifiScanningServiceImpl 类的 setupScannerImpls 中通过 wifiNative 获取到 ifaces,针对每个 iface 创建对应的 Scanner,Looper 对象为其自身的 mLooper 成员;

14. mLooper 的初始化发生在 WifiScanningServiceImpl 类的初始化构造函数中,由入参 looper 进行初始化,在 WifiScanningService 类的初始化过程中新创建了 HandlerThread,由其对应的 Looper 成员来进行初始化,实际上为专门用于 Scanning 服务的 Looper 成员;

Message 的处理


15. Message 实际上最终是在 Looper 类的 loop 函数中处理,如果 Handler 在构建过程中有传入 callback 对象且其 handleMessage 方法不为空,则会调用该接口进行处理,这里 WificondScannerImpl 类实现了 Handler.Callback 接口,其内部实现了 handleMessage 方法,因此该方法被调用进行处理:

    @Overridepublic boolean handleMessage(Message msg) {switch(msg.what) {case WifiMonitor.SCAN_FAILED_EVENT:Log.w(TAG, "Scan failed");cancelScanTimeout();reportScanFailure();break;case WifiMonitor.PNO_SCAN_RESULTS_EVENT:pollLatestScanDataForPno();break;case WifiMonitor.SCAN_RESULTS_EVENT:cancelScanTimeout();pollLatestScanData();break;default:// ignore unknown event}return true;}

16. pollLatestScanData 接口被调用,如果发现设置变更,scanning 被取消,则直接返回,否则首先调用 WifiNative 的 getScanResults 方法进行扫描结果的获取;

17. 进而调用了 WifiCondManager 的 getScanResults 方法进行扫描结果的获取:

    /*** Fetch the latest scan results of the indicated type for the specified interface. Note that* this method fetches the latest results - it does not initiate a scan. Initiating a scan can* be done using {@link #startScan(String, int, Set, List)} or* {@link #startPnoScan(String, PnoSettings, Executor, PnoScanRequestCallback)}.** Note: The interface must have been already set up using* {@link #setupInterfaceForClientMode(String, Executor, ScanEventCallback, ScanEventCallback)}* or {@link #setupInterfaceForSoftApMode(String)}.** @param ifaceName Name of the interface.* @param scanType The type of scan result to be returned, can be* {@link #SCAN_TYPE_SINGLE_SCAN} or {@link #SCAN_TYPE_PNO_SCAN}.* @return Returns an array of {@link NativeScanResult} or an empty array on failure (e.g. when* called before the interface has been set up).*/@NonNull public List<NativeScanResult> getScanResults(@NonNull String ifaceName,@ScanResultType int scanType) {IWifiScannerImpl scannerImpl = getScannerImpl(ifaceName);if (scannerImpl == null) {Log.e(TAG, "No valid wificond scanner interface handler");return new ArrayList<>();}List<NativeScanResult> results = null;try {if (scanType == SCAN_TYPE_SINGLE_SCAN) {results = Arrays.asList(scannerImpl.getScanResults());} else {results = Arrays.asList(scannerImpl.getPnoScanResults());}} catch (RemoteException e1) {Log.e(TAG, "Failed to create ScanDetail ArrayList");}if (results == null) {results = new ArrayList<>();}if (mVerboseLoggingEnabled) {Log.d(TAG, "get " + results.size() + " scan results from wificond");}return results;}

18. 根据接口查找到对应的 WifiScannerImpl 对象,调用其方法 getScanResults 进行扫描结果查询,实际上走到了 HIDL 后面 scanner_impl.cpp 中 ScannerImpl 类实现方法 getScanResults:

Status ScannerImpl::getScanResults(vector<NativeScanResult>* out_scan_results) {if (!CheckIsValid()) {return Status::ok();}if (!scan_utils_->GetScanResult(interface_index_, out_scan_results)) {LOG(ERROR) << "Failed to get scan results via NL80211";}return Status::ok();
}

19. 进而调用了 scan_utils_ 的 GetScanResult 方法获取扫描结果:

bool ScanUtils::GetScanResult(uint32_t interface_index,vector<NativeScanResult>* out_scan_results) {NL80211Packet get_scan(netlink_manager_->GetFamilyId(),NL80211_CMD_GET_SCAN,netlink_manager_->GetSequenceNumber(),getpid());get_scan.AddFlag(NLM_F_DUMP);NL80211Attr<uint32_t> ifindex(NL80211_ATTR_IFINDEX, interface_index);get_scan.AddAttribute(ifindex);vector<unique_ptr<const NL80211Packet>> response;if (!netlink_manager_->SendMessageAndGetResponses(get_scan, &response))  {LOG(ERROR) << "NL80211_CMD_GET_SCAN dump failed";return false;}if (response.empty()) {LOG(INFO) << "Unexpected empty scan result!";return true;}for (auto& packet : response) {if (packet->GetMessageType() == NLMSG_ERROR) {LOG(ERROR) << "Receive ERROR message: "<< strerror(packet->GetErrorCode());continue;}if (packet->GetMessageType() != netlink_manager_->GetFamilyId()) {LOG(ERROR) << "Wrong message type: "<< packet->GetMessageType();continue;}uint32_t if_index;if (!packet->GetAttributeValue(NL80211_ATTR_IFINDEX, &if_index)) {LOG(ERROR) << "No interface index in scan result.";continue;}if (if_index != interface_index) {LOG(WARNING) << "Uninteresting scan result for interface: " << if_index;continue;}NativeScanResult scan_result;if (!ParseScanResult(std::move(packet), &scan_result)) {LOG(DEBUG) << "Ignore invalid scan result";continue;}out_scan_results->push_back(std::move(scan_result));}return true;
}

WiFi 扫描结果的通知过程相关推荐

  1. android p wifi一直在扫描_(一百六十八)Android P wifi 扫描失败结果上报流程梳理-扫描上报梳理②...

    接(一百五十五)Android P wifi 扫描失败结果上报流程梳理-扫描上报梳理 扫描失败上报梳理发现梳理的差了很多,特补充 1.WificondScannerImpl @Override pub ...

  2. 第六章 Wi-Fi扫描流程

    系列文章目录 第一章 国内下载AOSP最新源码的方法 第二章 下载AOSP WiFi相关的代码 第三章 将源码导入Android Studio(无需编译idegen) 文章目录 系列文章目录 前言 一 ...

  3. ESP Wi-Fi 扫描入门

    1 前言 在使用 Wi-Fi 功能时,我们的操作往往是打开手机上的 Wi-Fi 设备,搜索到想要连接的热点,输入密码,联网成功,其实这个过程是有对应的专业术语的,分别是:扫描(Scanning).认证 ...

  4. Android-O wifi扫描机制及功耗优化

    一. Android O wifi扫描场景 Android O上的wifi扫面场景可以归结为以下四种: 1. 亮屏情况下,在Wifi settings界面,固定扫描,时间间隔为10s. 2. 亮屏情况 ...

  5. Android R Framework wifi扫描场景总结

    文章目录 Android Framework wifi扫描 Android R wifi扫描场景(包含客制化项) 亮屏情况下,在 WifiSetting 界面 亮屏情况下,在非 WifiSetting ...

  6. android WiFi扫描并连接

    wifi扫描并显示 获取列表 获取Wifi列表并不难,网上有一个WifiAdmin的工具类,一找一大堆.但是这个工具类其中还是有很多问题的,并不建议直接使用.在使用过程中还是踩到了其中的一些坑,然后修 ...

  7. Android wifi扫描机制(Android O)

    版权声明:本文为博主原创文章,博客地址:https://blog.csdn.net/h784707460/article/details/79658950,未经博主允许不得转载. 一. Android ...

  8. android p wifi一直在扫描_在Android上的每次WiFi扫描之间我应该使用什么时间间隔?...

    我需要定期执行Wifi扫描.当时间间隔设置为1-2秒时,我遇到了问题.好像我没有得到任何ScanResult.是否有最短的时间设置,以便WifiManager能够执行成功的WiFi扫描? 这是代码.我 ...

  9. android p wifi一直在扫描_Android Wifi 扫描及自动连接

    缘起 最近有个需求,要求App能够自动扫描到某个热点然后自动连接上热点.背景是我们公司属于IoT行业,这个热点是设备发出的,有固定的前缀(比如设备热点名为SmartLife-xx),可以直接连接无需密 ...

  10. Qcom平台测试wifi TIS需要关闭wifi扫描、漫游和休眠

    在测试QCA系列IC wifi功能时,需要禁用wifi扫描.漫游和休眠: 1.禁止wifi扫描 : 描述:因为需要开启wifi时测试wifi TlS,需求是关闭wifi scan . 需要的工具:iw ...

最新文章

  1. [转]PDO防注入原理分析以及使用PDO的注意事项
  2. 74芯片引脚真值表汇总
  3. DM8168心得之SD卡快速分区制作
  4. 数据库毗连过多的错误,年夜概的启事分解及措置惩罚行动
  5. JAVA程序设计----关于字符串的一些基本问题处理
  6. hexo 修改yelee文章标题字体颜色
  7. duilib环境配置以及简单入门介绍
  8. 大数据之-Hadoop3.x_MapReduce_分区数与reduce个数总结---大数据之hadoop3.x工作笔记0113
  9. 数据有为 智在决策 | 观远数据2019智能决策峰会圆满落幕
  10. iOS 15 通知的新功能
  11. redis-hmmap-go
  12. 【知识图谱系列】清华大学基于对比学习的图谱预训练模型GCC
  13. php ddos攻击代码,PHP拦截网站DDOS攻击防御代码
  14. 移动应用开发学习通测试题答案
  15. Spring实战(第4版)阅读笔记(一)
  16. Java Eclipse部分图标汇总
  17. 常用英语前缀和全部英语前缀——138个
  18. 威尔逊定理 及其拓展
  19. 2018VMware虚拟机安装ghost win7系统正确方法
  20. speedoffice(Word)怎么添加页码

热门文章

  1. DIV+CSS两种盒子模型
  2. stm32驱动ssd1306配置_自制FOC控制驱动器
  3. python 定义一个学生类、包含三个属性并打印输出_Python3.x基础学习-类--面向对象...
  4. centos7 安装mysql_第02期:ClickHouse 单机部署以及从 MySQL 增量同步数据
  5. 我国启动新型数据保护密码算法研究
  6. Linux C enum
  7. java中构造方法的执行过程
  8. PHP如何大幅度提升运行效率? -- 把它编译成机器码!
  9. android获取用户点击的坐标
  10. 互联网行业常见的需要考虑的问题