一 前言

这次接着讲Wifi工程流程中的Wifi热点扫描过程部分的获取扫描结果的过程,也是Wifi扫描过程的延续,可以先看前面Wifi扫描的分析过程。

Wifi模块—源码分析Wifi热点扫描(Android P)

二 图示调用流程

这次的调用流程比较简单就不画流程图了,而且流程是按三条不连贯的线路分析的。

三 代码具体流程

1 底层获取扫描结果

在上一篇我们分析了Wifi扫描的过程。在底层完成扫描之后会通知框架层,由框架层的WifiMonitor监听该事件并发送消息SCAN_RESULTS_EVENT,通知WificondScannerImpl进行处理,再通过WifiNative从底层获取wifi扫描结果,最后再广播SCAN_RESULTS_AVAILABLE_ACTION通知扫描结果可获取。

1.1 framework/opt/net/wifi/service/java/com/android/server/wifi/WifiMonitor.java

/**
* Broadcast scan result event to all the handlers registered for this event.
* @param iface Name of iface on which this occurred.
*/
public void broadcastScanResultEvent(String iface) {
    sendMessage(iface, SCAN_RESULTS_EVENT);
}

1.2 framework/opt/net/wifi/service/java/com/android/server/wifi/scanner/WificondScannerImpl.java

@Override
public 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;
}
继续看 pollLatestScanData。

private void pollLatestScanData() {
    synchronized (mSettingsLock) {
        if (mLastScanSettings == null) {
            // got a scan before we started scanning or after scan was canceled
            return;
        }
 
        mNativeScanResults = mWifiNative.getScanResults(mIfaceName);
        List<ScanResult> singleScanResults = new ArrayList<>();
        int numFilteredScanResults = 0;
        for (int i = 0; i < mNativeScanResults.size(); ++i) {
            ScanResult result = mNativeScanResults.get(i).getScanResult();
            long timestamp_ms = result.timestamp / 1000; // convert us -> ms
            if (timestamp_ms > mLastScanSettings.startTime) {
                if (mLastScanSettings.singleScanFreqs.containsChannel(
                                result.frequency)) {
                    singleScanResults.add(result);
                }
            } else {
                numFilteredScanResults++;
            }
        }
        ...
    }
}
 通过mWifiNative.getScanResults来从底层获取wifi热点扫描结果。

1.3 framework/opt/net/wifi/service/java/com/android/server/wifi/WifiNative.java

/**
* Fetch the latest scan result from kernel via wificond.
* @param ifaceName Name of the interface.
* @return Returns an ArrayList of ScanDetail.
* Returns an empty ArrayList on failure.
*/
public ArrayList<ScanDetail> getScanResults(@NonNull String ifaceName) {
    return mWificondControl.getScanResults(
            ifaceName, WificondControl.SCAN_TYPE_SINGLE_SCAN);
}
接着看mWificondControl.getScanResults。

1.4 framework/opt/net/wifi/service/java/com/android/server/wifi/WifiNative.java

/**
* Fetch the latest scan result from kernel via wificond.
* @param ifaceName Name of the interface.
* @return Returns an ArrayList of ScanDetail.
* Returns an empty ArrayList on failure.
*/
public ArrayList<ScanDetail> getScanResults(@NonNull String ifaceName, int scanType) {
    ArrayList<ScanDetail> results = new ArrayList<>();
    IWifiScannerImpl scannerImpl = getScannerImpl(ifaceName);
    if (scannerImpl == null) {
        Log.e(TAG, "No valid wificond scanner interface handler");
        return results;
    }
    try {
        NativeScanResult[] nativeResults;
        if (scanType == SCAN_TYPE_SINGLE_SCAN) {
            nativeResults = scannerImpl.getScanResults();
        } else {
            nativeResults = scannerImpl.getPnoScanResults();
        }
        ...
    }
    ...
 
    return results;
}
看scannerImpl.getScanResults。

1.5 system/connectivity/wificond/aidl/android/net/wifi/IWifiScannerImpl.aidl

接下来就会调到C++层,暂时就不往下分析了。

2 通知扫描结果可获取

接下来广播SCAN_RESULTS_AVAILABLE_ACTION通知扫描结果可获取。

2.1 frameworks/base/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java

在这里会接收到扫描结果可获取的广播WifiManager.SCAN_RESULTS_AVAILABLE_ACTION。

/**
*  Receiver for handling broadcasts.
*
*  This receiver is registered on the WorkHandler.
*/
@VisibleForTesting
final BroadcastReceiver mReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
 
        if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) {
            updateWifiState(
                    intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
                            WifiManager.WIFI_STATE_UNKNOWN));
        } else if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(action)) {
            mStaleScanResults = false;
 
            fetchScansAndConfigsAndUpdateAccessPoints();
        } else if (WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION.equals(action)
                || WifiManager.LINK_CONFIGURATION_CHANGED_ACTION.equals(action)) {
            fetchScansAndConfigsAndUpdateAccessPoints();
        } else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) {
            // TODO(sghuman): Refactor these methods so they cannot result in duplicate
            // onAccessPointsChanged updates being called from this intent.
            NetworkInfo info = intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
            updateNetworkInfo(info);
            fetchScansAndConfigsAndUpdateAccessPoints();
        } else if (WifiManager.RSSI_CHANGED_ACTION.equals(action)) {
            NetworkInfo info =
                    mConnectivityManager.getNetworkInfo(mWifiManager.getCurrentNetwork());
            updateNetworkInfo(info);
        }
    }
};
WifiTracker接收到这个广播后,执行fetchScansAndConfigsAndUpdateAccessPoints。

/**
* Retrieves latest scan results and wifi configs, then calls
* {@link #updateAccessPoints(List, List)}.
*/
private void fetchScansAndConfigsAndUpdateAccessPoints() {
    final List<ScanResult> newScanResults = mWifiManager.getScanResults();
    if (isVerboseLoggingEnabled()) {
        Log.i(TAG, "Fetched scan results: " + newScanResults);
    }
 
    List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks();
    updateAccessPoints(newScanResults, configs);
}
走到mWifiManager.getScanResults。

2.2 framework/base/wifi/java/android/net/wifi/WifiManager.java

/**
* Return the results of the latest access point scan.
* @return the list of access points found in the most recent scan. An app must hold
* {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION} or
* {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION} permission
* in order to get valid results.  If there is a remote exception (e.g., either a communication
* problem with the system service or an exception within the framework) an empty list will be
* returned.
*/
public List<ScanResult> getScanResults() {
    try {
        return mService.getScanResults(mContext.getOpPackageName());
    } catch (RemoteException e) {
        throw e.rethrowFromSystemServer();
    }
}
又是通过aidl进行跨进程调用mService.getScanResults。

2.3 framework/opt/net/wifi/service/java/com/android/server/wifi/WifiServiceImpl.java

/**
* Return the results of the most recent access point scan, in the form of
* a list of {@link ScanResult} objects.
* @return the list of results
*/
@Override
public List<ScanResult> getScanResults(String callingPackage) {
    enforceAccessPermission();
    int uid = Binder.getCallingUid();
    long ident = Binder.clearCallingIdentity();
    if (mVerboseLoggingEnabled) {
        mLog.info("getScanResults uid=%").c(uid).flush();
    }
    try {
        mWifiPermissionsUtil.enforceCanAccessScanResults(callingPackage, uid);
        final List<ScanResult> scanResults = new ArrayList<>();
        boolean success = mWifiInjector.getWifiStateMachineHandler().runWithScissors(() -> {
            scanResults.addAll(mScanRequestProxy.getScanResults());
        }, RUN_WITH_SCISSORS_TIMEOUT_MILLIS);
        if (!success) {
            Log.e(TAG, "Failed to post runnable to fetch scan results");
        }
        return scanResults;
    } catch (SecurityException e) {
        return new ArrayList<ScanResult>();
    } finally {
        Binder.restoreCallingIdentity(ident);
    }
}
返回scanResults。

这里和android O还是有一些差别的。看mScanRequestProxy.getScanResults。

2.4 framework/opt/net/wifi/service/java/com/android/server/wifi/ScanRequestProxy.java

/**
* Return the results of the most recent access point scan, in the form of
* a list of {@link ScanResult} objects.
* @return the list of results
*/
public List<ScanResult> getScanResults() {
    return mLastScanResults;
}
看看mLastScanResults怎么来的。

// Common scan listener for scan requests.
private class ScanRequestProxyScanListener implements WifiScanner.ScanListener {
    ...
    @Override
    public void onResults(WifiScanner.ScanData[] scanDatas) {
        if (mVerboseLoggingEnabled) {
            Log.d(TAG, "Scan results received");
        }
        // For single scans, the array size should always be 1.
        if (scanDatas.length != 1) {
            Log.wtf(TAG, "Found more than 1 batch of scan results, Failing...");
            sendScanResultBroadcastIfScanProcessingNotComplete(false);
            return;
        }
        WifiScanner.ScanData scanData = scanDatas[0];
        ScanResult[] scanResults = scanData.getResults();
        if (mVerboseLoggingEnabled) {
            Log.d(TAG, "Received " + scanResults.length + " scan results");
        }
        // Store the last scan results & send out the scan completion broadcast.
        mLastScanResults.clear();
        mLastScanResults.addAll(Arrays.asList(scanResults));
        sendScanResultBroadcastIfScanProcessingNotComplete(true);
    }
    ...
};
将scanResults放进LastScanResults,看scanData.getResults。WifiScanner的服务端是WifiScanningServiceImpl。

2.5 framework/base/wifi/java/android/net/wifi//WifiScanner.java

/**
* all the information garnered from a single scan
*/
public static class ScanData implements Parcelable {
 
    ...
    public ScanResult[] getResults() {
        return mResults;
    }
    ...
}
返回mResults。

3 Settings应用层获取扫描结果

应用层

3.1 packages/apps/Settings/src/com/android/settings/wifi/WifiSettings.java

在生命周期函数里监听Wifi的状态。

@Override
public void onStart() {
    super.onStart();
 
    // On/off switch is hidden for Setup Wizard (returns null)
    mWifiEnabler = createWifiEnabler();
 
    if (mIsRestricted) {
        restrictUi();
        return;
    }
 
    onWifiStateChanged(mWifiManager.getWifiState());
}
当wifi状态改变时会有相应的操作。

/** Called when the state of Wifi has changed. */
@Override
public void onWifiStateChanged(int state) {
    if (mIsRestricted) {
        return;
    }
 
    final int wifiState = mWifiManager.getWifiState();
    switch (wifiState) {
        case WifiManager.WIFI_STATE_ENABLED:
            updateAccessPointPreferences();
            break;
 
        case WifiManager.WIFI_STATE_ENABLING:
            removeConnectedAccessPointPreference();
            mAccessPointsPreferenceCategory.removeAll();
            addMessagePreference(R.string.wifi_starting);
            setProgressBarVisible(true);
            break;
 
        case WifiManager.WIFI_STATE_DISABLING:
            removeConnectedAccessPointPreference();
            mAccessPointsPreferenceCategory.removeAll();
            addMessagePreference(R.string.wifi_stopping);
            break;
 
        case WifiManager.WIFI_STATE_DISABLED:
            setOffMessage();
            setAdditionalSettingsSummaries();
            setProgressBarVisible(false);
            break;
    }
}
监听到wifi状态已经开启,看updateAccessPointPreferences。

private void updateAccessPointPreferences() {
    // in case state has changed
    if (!mWifiManager.isWifiEnabled()) {
        return;
    }
    // AccessPoints are sorted by the WifiTracker
    final List<AccessPoint> accessPoints = mWifiTracker.getAccessPoints();
    if (isVerboseLoggingEnabled()) {
        Log.i(TAG, "updateAccessPoints called for: " + accessPoints);
    }
    ...
}
看mWifiTracker.getAccessPoints。

java框架层

3.2 frameworks/base/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java

/**
* Gets the current list of access points.
*
* <p>This method is can be called on an abitrary thread by clients, but is normally called on
* the UI Thread by the rendering App.
*/
@AnyThread
public List<AccessPoint> getAccessPoints() {
    synchronized (mLock) {
        return new ArrayList<>(mInternalAccessPoints);
    }
}
接下来的过程最终还是会走到WifiManager.getScanResults,WifiServiceImpl.getScanResults,细节上会有差异。

————————————————
版权声明:本文为CSDN博主「BrighterLi」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_42093428/article/details/82834234

Wifi模块—源码分析Wifi热点扫描2(Android P)相关推荐

  1. Wifi模块—源码分析Wifi热点扫描(Android P)

    一 前言 这次接着讲Wifi工程流程中的Wifi热点查找过程,也是Wifi启动的过程延续,Wifi启动过程中会更新Wifi的状态,框架层也有相应广播发出,应用层接收到广播后开始进行热点的扫描.可以先看 ...

  2. Wifi模块—源码分析Wifi启动(Android P)

    一.前言 Android P在wifi这块改动挺大的,Wifi到AndoidO之后不再使用jni,所以AndroidP也一样不再使用jni来实现Java代码与本地的C/C++代码交互,而是使用HIDL ...

  3. Wifi模块—源码分析Wifi启动2(Android P)

    一 前言 在上一篇分析了wifi启动的流程,从Android应用层一直分析到了Java框架层,这次我们接着往下走流程.如果没有看上一篇的建议先回头看看   Wifi模块-源码分析Wifi启动1(And ...

  4. java wifi模块源码_Wifi模块

    一 前言 在上一篇分析了wifi启动的流程,从Android应用层一直分析到了Java框架层,这次我们接着往下走流程.如果没有看上一篇的建议先回头看看   Wifi模块-源码分析Wifi启动1(And ...

  5. Python 的 heapq 模块源码分析

    作者:weapon 来源:https://zhuanlan.zhihu.com/p/54260935 起步 heapq 模块实现了适用于Python列表的最小堆排序算法. 堆是一个树状的数据结构,其中 ...

  6. python树状节点 可拖拽_Python 的 heapq 模块源码分析

    原文链接:Python 的 heapq 模块源码分析 起步 heapq 模块实现了适用于Python列表的最小堆排序算法. 堆是一个树状的数据结构,其中的子节点都与父母排序顺序关系.因为堆排序中的树是 ...

  7. dubbo源码分析系列——dubbo-cluster模块源码分析

    2019独角兽企业重金招聘Python工程师标准>>> 模块功能介绍 该模块的使用介绍请参考dubbo官方用户手册如下章节内容. 集群容错 负载均衡 路由规则 配置规则 注册中心参考 ...

  8. kafka源码愫读(5)、ReplicaManager模块源码分析

    1.ReplicaManager模块简介 replicaManager主要用来管理topic在本broker上的副本信息.并且读写日志的请求都是通过replicaManager进行处理的. 每个rep ...

  9. java wifi模块源码_android 一键配置WIFI模块 源码

    [实例简介] WIFI模块转串口   一键配置 [实例截图] [核心代码] butpost.setOnClickListener(new OnClickListener() { @Override p ...

最新文章

  1. Win10自动息屏太快解决方法
  2. json过滤特殊字符
  3. html列表用标记,html标记列表应用
  4. 启动页面和各设备的宽高比及像素
  5. leetcode89 (2022.1.8)
  6. orgmode导出html,含有python代码块的ORG-MODE导出为HTML时出错
  7. 求抛物线和直线交点_关于抛物线大题的参考经验(5):浙江历年学考题回顾...
  8. CRLF line terminators导致shell脚本报错:command not found --转载
  9. Bartender编辑数据小标题中嵌入的数据更改无效,无法在条码中显示已经扫描的条码号
  10. arcgis里面如何删除标记_ArcGIS技术篇——标记符号制作
  11. SetWindowsHookEx全局钩子
  12. R 语言数据处理入门-2(缺失值处理)
  13. 爬虫之Scrapy文件爬取
  14. openwrt 怎么进入串口_OpenWrt路由器读取串口数据,建立tcp服务
  15. 快速实现M5311NBIOT MQTT通信
  16. vsCode好用插件记录
  17. 云端守望者(下):十八般武艺
  18. halcon学习之回形针方向检测(一)
  19. 实验吧决斗场刷新刷新快刷新
  20. PG据库备份与恢复实验 (PITR)

热门文章

  1. KRPano JS 场景编辑器源码
  2. HDU 1108.最小公倍数-辗转相除法
  3. Java 第7章 数组
  4. Python正则表达式指南
  5. ASP.Net MVC的学习
  6. [leetcode] Restore IP Addresses
  7. 20个强大的jQuery翻书插件【 jQuery flipbook】
  8. Git学习笔记:分支管理3
  9. 使用 SCons 代替 Makefile 快速构建应用程序
  10. 九月免费手账分享-【奶油星云】