Android PNO扫描实现

1. PNO扫描的发起

在前面介绍WiFi打开流程的时候有提到过,ClientModeImpl被创建后,进入初始状态DisconnectedState,会立即发起Connectivity Scan.

// packages/modules/Wifi/service/java/com/android/server/wifi/WifiConnectivityManager.java
startConnectivityScan(SCAN_IMMEDIATELY);
private void startConnectivityScan(boolean scanImmediately) {
    // ... ...
    if (mScreenOn) {
        startPeriodicScan(scanImmediately);
    } else {
        if (mWifiState == WIFI_STATE_DISCONNECTED && !mPnoScanStarted) {
            startDisconnectedPnoScan();
        }
    }
}

如果是亮屏状态,那么发起周期性扫描;如果是灭屏状态,并且WiFi未连接,就发起PNO扫描。这里我们只关注PNO扫描。

// packages/modules/Wifi/service/java/com/android/server/wifi/WifiConnectivityManager.java
// Start a DisconnectedPNO scan when screen is off and Wifi is disconnected
private void startDisconnectedPnoScan() {
    // Initialize PNO settings
    PnoSettings pnoSettings = new PnoSettings();
    // 1. 获取preferred network list. 
    List<PnoSettings.PnoNetwork> pnoNetworkList = retrievePnoNetworkList();
    int listSize = pnoNetworkList.size();

    if (listSize == 0) {
        // No saved network
        localLog("No saved network for starting disconnected PNO.");
        return;
    }

    // 2. pno设置
    pnoSettings.networkList = new PnoSettings.PnoNetwork[listSize];
    pnoSettings.networkList = pnoNetworkList.toArray(pnoSettings.networkList);
    // 设置rssi的阈值,低于阈值的网络不会上报
    pnoSettings.min6GHzRssi = mScoringParams.getEntryRssi(ScanResult.BAND_6_GHZ_START_FREQ_MHZ);
    pnoSettings.min5GHzRssi = mScoringParams.getEntryRssi(ScanResult.BAND_5_GHZ_START_FREQ_MHZ);
    pnoSettings.min24GHzRssi = mScoringParams.getEntryRssi(
            ScanResult.BAND_24_GHZ_START_FREQ_MHZ);

    // 3.扫描设置
    // Initialize scan settings
    ScanSettings scanSettings = new ScanSettings();
    scanSettings.band = getScanBand();
    scanSettings.reportEvents = WifiScanner.REPORT_EVENT_NO_BATCH;
    scanSettings.numBssidsPerScan = 0;
    scanSettings.periodInMs = deviceMobilityStateToPnoScanIntervalMs(mDeviceMobilityState);

    mScanner.startDisconnectedPnoScan(
            scanSettings, pnoSettings, new HandlerExecutor(mEventHandler), mPnoScanListener);
    mPnoScanStarted = true;
    mWifiMetrics.logPnoScanStart();
}

这个函数首先进行PNO扫描的参数设置,然后交给WifiScanner进行处理

// packages/modules/Wifi/framework/java/android/net/wifi/WifiScanner.java
public void startDisconnectedPnoScan(ScanSettings scanSettings, PnoSettings pnoSettings,
        @NonNull @CallbackExecutor Executor executor, PnoScanListener listener) {
    Objects.requireNonNull(listener, "listener cannot be null");
    Objects.requireNonNull(pnoSettings, "pnoSettings cannot be null");
    int key = addListener(listener, executor);
    if (key == INVALID_KEY) return;
    validateChannel();
    pnoSettings.isConnected = false;
    startPnoScan(scanSettings, pnoSettings, key);
}
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);
}

WifiScanner实际上只是提供了接口文件,最终还是会发消息CMD_START_PNO_SCAN给WifiScanningServiceImpl去处理.

这里WifiScanner和WifiScanningServiceImpl是通过AsyncChannel通信的。

WifiScanningServiceImpl的处理如下

// packages/modules/Wifi/service/java/com/android/server/wifi/scanner/WifiScanningServiceImpl.java
private class ClientHandler extends Handler {
    public void handleMessage(Message msg) {
        switch (msg.what) {
            // ... ...
            case WifiScanner.CMD_START_PNO_SCAN:
            case WifiScanner.CMD_STOP_PNO_SCAN:
                mPnoScanStateMachine.sendMessage(Message.obtain(msg));
            break;
            // ... ...
        }
    }
}

PnoScanStateMachine的StartedState收到消息后不做什么处理,只是从msg中取出参数并检查是否支持PNO扫描,如果支持的就会进入HwPnoScanState,并且消息交给HwPnoScanState继续处理。

// HwPnoScanState.processMessage.CMD_START_PNO_SCAN
addHwPnoScanRequest(ci, msg.arg2, scanSettings, pnoSettings);

addHwPnoScanRequest的实现如下:

private boolean addHwPnoScanRequest(ClientInfo ci, int handler, ScanSettings scanSettings,
                PnoSettings pnoSettings) {
    // ... ...
    WifiNative.PnoSettings nativePnoSettings =
            convertSettingsToPnoNative(scanSettings, pnoSettings);
    if (!mScannerImplsTracker.setHwPnoList(nativePnoSettings)) {
        return false;
    }
    logScanRequest("addHwPnoScanRequest", ci, handler, null, scanSettings, pnoSettings);
    addPnoScanRequest(ci, handler, scanSettings, pnoSettings);
    
    return true;
}

首先将scanSettings和pnoSettings转化成nativePnoSettings;接着调用mScannerImplsTracker.setHwPnoList往下层继续调用;最后记录pno扫描请求。

setHwPnoList的实现如下

public boolean setHwPnoList(WifiNative.PnoSettings pnoSettings) {
    mStatusPerImpl.clear();
    boolean anySuccess = false;
    // 遍历所有的scannerimpl(每个interface对应一个)
    for (Map.Entry<String, WifiScannerImpl> entry : mScannerImpls.entrySet()) {
        String ifaceName = entry.getKey();
        WifiScannerImpl impl = entry.getValue();
        boolean success = impl.setHwPnoList(
                pnoSettings, new PnoEventHandler(ifaceName));
        if (!success) {
            Log.e(TAG, "Failed to start pno on " + ifaceName);
            continue;
        }
        mStatusPerImpl.put(ifaceName, STATUS_PENDING);
        anySuccess = true;
    }
    return anySuccess;
}

mScannerImpls中的WifiScannerImpl都是在WifiScanningServiceImpl 的 setupScannerImpls()中创建的,每个iface对应一个

private void setupScannerImpls() {
    // ... ...
    Set<String> ifaceNamesOfImplsToSetup = new ArraySet<>(ifaceNames);
        ifaceNamesOfImplsToSetup.removeAll(ifaceNamesOfImplsAlreadySetup);
    // ... ...
    for (String ifaceName : ifaceNamesOfImplsToSetup) {
        WifiScannerImpl impl = mScannerImplFactory.create(mContext, mLooper, mClock, ifaceName);
        // If this new scanner impl does not offer any new bands to scan, then we should
        // ignore it.
        if (!doesAnyExistingImplSatisfy(impl)) {
            mScannerImpls.put(ifaceName, impl);
        }
        // ... ...
    }
}

WifiScannerImpl的创建由WifiScannerImpl.DEFAULT_FACTORY负责:

public static final WifiScannerImplFactory DEFAULT_FACTORY = new WifiScannerImplFactory() {
        public WifiScannerImpl create(Context context, Looper looper, Clock clock,
                @NonNull String ifaceName) {
            WifiNative wifiNative = WifiInjector.getInstance().getWifiNative();
            WifiMonitor wifiMonitor = WifiInjector.getInstance().getWifiMonitor();
            if (TextUtils.isEmpty(ifaceName)) {
                return null;
            }
            // Background Scan
            if (wifiNative.getBgScanCapabilities(
                    ifaceName, new WifiNative.ScanCapabilities())) {
                return new HalWifiScannerImpl(context, ifaceName, wifiNative, wifiMonitor,
                        looper, clock);
            } else {
                return new WificondScannerImpl(context, ifaceName, wifiNative, wifiMonitor,
                        new WificondChannelHelper(wifiNative), looper, clock);
            }
        }
    };

如果底层支持getBgScanCapabilities,那么就返回HalWifiScannerImpl实例,否则,返回WificondScannerImpl实例。但其实对于singlescan和pnoscan,都还是调用的WificondScannerImpl的接口,只有BatchedScan才会通过HAL去请求。

所以,对于PNO扫描,我们继续分析WificondScannerImpl

// packages/modules/Wifi/service/java/com/android/server/wifi/scanner/WificondScannerImpl.java
public boolean setHwPnoList(WifiNative.PnoSettings settings,
        WifiNative.PnoEventHandler eventHandler) {
    synchronized (mSettingsLock) {
        if (mLastPnoScanSettings != null) {
            Log.w(TAG, "Already running a PNO scan");
            return false;
        }
        if (!isHwPnoScanRequired(settings.isConnected)) {
            return false;
        }

        mLastPnoScanSettings = new LastPnoScanSettings(
                mClock.getElapsedSinceBootNanos(),
                settings.networkList, eventHandler);

        if (!startHwPnoScan(settings)) {
            Log.e(TAG, "Failed to start PNO scan");
            reportPnoScanFailure();
        }
        return true;
    }
}
  1. PNO扫描只能发起一次,如果已经存在,那么不能重复发起,必须先stop

  2. startHwPnoScan(settings)

    调用WifiNativestartPnoScan()方法

public boolean startPnoScan(@NonNull String ifaceName, PnoSettings pnoSettings) {
    return mWifiCondManager.startPnoScan(ifaceName, pnoSettings.toNativePnoSettings(),
            Runnable::run,
            new WifiNl80211Manager.PnoScanRequestCallback() {
                @Override
                public void onPnoRequestSucceeded() {
                    // ... ...
                }

                @Override
                public void onPnoRequestFailed() {
                    // ... ...
                }
            });
}

这里又回到了熟悉的WifiNl80211Manager,在之前WiFi打开的流程中我们也介绍过。它负责与wificond交互,而wificond负责通过nl80211接口与驱动交互。

public boolean startPnoScan(@NonNull String ifaceName, @NonNull PnoSettings pnoSettings,
        @NonNull @CallbackExecutor Executor executor,
        @NonNull PnoScanRequestCallback callback) {
    IWifiScannerImpl scannerImpl = getScannerImpl(ifaceName);
    // ... ...
    try {
        boolean success = scannerImpl.startPnoScan(pnoSettings);
        if (success) {
            executor.execute(callback::onPnoRequestSucceeded);
        } else {
            executor.execute(callback::onPnoRequestFailed);
        }
        return success;
    } catch (RemoteException e1) {
        Log.e(TAG, "Failed to start pno scan due to remote exception");
    }
    return false;
}

下面就交给wificond去处理了

pno scan的wificond的入口在文件system/connectivity/wificond/scanning/scanner_impl.cpp

// system/connectivity/wificond/scanning/scanner_impl.cpp
Status ScannerImpl::startPnoScan(const PnoSettings& pno_settings,
                                 bool* out_success) {
  pno_settings_ = pno_settings;
  LOG(VERBOSE) << "startPnoScan";
  *out_success = StartPnoScanDefault(pno_settings);
  return Status::ok();
}

StartPnoScanDefault()方法理解起来比较容易:首先是参数转换,然后添加一些必要的参数,最后交给scan_utils_->StartScheduledScan处理

bool ScannerImpl::StartPnoScanDefault(const PnoSettings& pno_settings) {
    // ... ...
    // 解析扫描参数
    ParsePnoSettings(pno_settings, &scan_ssids, &match_ssids, &freqs, &unused);
    // 随机MAC地址
    bool request_random_mac = wiphy_features_.supports_random_mac_sched_scan && !client_interface_->IsAssociated();
    // 低功耗模式,如果驱动支持,那么就是必选的
    bool request_low_power = wiphy_features_.supports_low_power_oneshot_scan;
    bool request_sched_scan_relative_rssi = wiphy_features_.supports_ext_sched_scan_relative_rssi;
    // 启动扫描
    struct SchedScanReqFlags req_flags = {};
    req_flags.request_random_mac = request_random_mac;
    req_flags.request_low_power = request_low_power;
    req_flags.request_sched_scan_relative_rssi = request_sched_scan_relative_rssi;
    scan_utils_->StartScheduledScan(interface_index_,
                                       GenerateIntervalSetting(pno_settings),
                                       pno_settings.min_2g_rssi_,
                                       pno_settings.min_5g_rssi_,
                                       pno_settings.min_6g_rssi_,
                                       req_flags,
                                       scan_ssids,
                                       match_ssids,
                                       freqs,
                                       &error_code)
}

StartScheduledScan 通过NL80211接口向驱动发起周期性扫描接口。简要介绍一下设置的参数(SchedScanIntervalSetting)

Scheduled Scan 是一种机制,允许Wi-Fi设备按计划定期执行扫描操作,以搜索附近的Wi-Fi网络

Scheduled Scan 是一种机制,允许Wi-Fi设备按计划定期执行扫描操作,以搜索附近的Wi-Fi网络

  • interface_index
    interface编号
  • SchedScanIntervalSetting
    • ScanPlan
      • interval_ms
        扫描间隔时间
      • n_iterations
        扫描次数
    • plans
      ScanPlan的集合,依次执行。
    • final_interval_ms
      当所有plans结束后,以final_interval_ms的频率继续扫描,默认为0(不扫描)
  • rssi_threshold_2g
    2G网络的RSSI阈值,低于rssi_threshold_2g的2.4G网络不会上报
  • rssi_threshold_5g
    与rssi_threshold_2g类似
  • rssi_threshold_6g
    与rssi_threshold_2g类似
  • SchedScanReqFlags
    计划扫描/周期扫描(SchedScan)参数
    • request_random_mac
      如果是true,要求设备在扫描时使用随机MAC地址。
    • request_low_power
      低功耗扫描。PNO扫描默认设置为true。
    • request_sched_scan_relative_rssi
      该参数用于获取与当前已连接BSS相比具有更好信号强度(RSSI)的BSS。
      假设已连接到A(rssi=-60),但有其他网络B(-50)/C(-59)/D(-70)在附近;此时如果设置了NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST后,并且delta=3,那么只会扫描到B,C和D 不会包含在扫描结果里。
      pno扫描默认设置为true。前提是驱动要支持> supports_ext_sched_scan_relative_rssi.
      android只设置了2.4G,5G可能驱动有默认值。
       if (req_flags.request_sched_scan_relative_rssi) {
       struct nl80211_bss_select_rssi_adjust rssi_adjust;
       rssi_adjust.band = NL80211_BAND_2GHZ;
       // RSSI 差值
       rssi_adjust.delta = static_cast<int8_t>(rssi_threshold_2g - rssi_threshold_5g);
       NL80211Attr<vector<uint8_t>> rssi_adjust_attr(
           NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST,
           vector<uint8_t>(
               reinterpret_cast<uint8_t*>(&rssi_adjust),
               reinterpret_cast<uint8_t*>(&rssi_adjust) + sizeof(rssi_adjust)));
       start_sched_scan.AddAttribute(rssi_adjust_attr);
       }
      
  • scan_ssids
    可以设置扫描的ssid。扫描时设置ssid主要是为了扫描隐藏热点。
    只有在network是hidden时,才会将其ssid加入到集合里面。
    一般情况下,隐藏的无线访问点(AP)在受到probe request时,有两种选择:要么忽略不响应,要么响应但是不广播自己的ssid。
    如果我们设置了scan_ssids,那么设备发送probe request帧时,会附带上ssid,以请求符合该ssid的网络信息;AP收到后,会比较帧内的ssid列表,如果跟自己的ssid相符,那么就会响应probe response,并且带上自己的网络信息(ssid等)。
  • match_ssids
    只扫描列表中的网络。NL80211_ATTR_SCHED_SCAN_MATCH
  • freqs
    扫描哪些信道,对于PNO扫描,通常只会在少数的几个信道上扫描。如果是singlescan,那么是全信道扫描。

到这里,PNO扫描相关的参数已经设置到驱动了。那么扫描结果是如何上报的呢,我们从下往上看

2. 扫描结果上报

wificond的ScannerImpl在构造时,会注册两个listener: OnScanResultsReadyOnSchedScanResultsReady

// system/connectivity/wificond/scanning/scanner_impl.cpp
ScannerImpl::ScannerImpl(uint32_t interface_index,
                         const ScanCapabilities& scan_capabilities,
                         const WiphyFeatures& wiphy_features,
                         ClientInterfaceImpl* client_interface,
                         ScanUtils* scan_utils) {
    // ... ...
    scan_utils_->SubscribeScanResultNotification(
      interface_index_,
      std::bind(&ScannerImpl::OnScanResultsReady, this, _1, _2, _3, _4));
  // Subscribe scheduled scan result notification from kernel.
  scan_utils_->SubscribeSchedScanResultNotification(
      interface_index_,
      std::bind(&ScannerImpl::OnSchedScanResultsReady,
                this,
                _1, _2));
}

// system/connectivity/wificond/scanning/scan_utils.cpp
void ScanUtils::SubscribeScanResultNotification(
    uint32_t interface_index,
    OnScanResultsReadyHandler handler) {
  netlink_manager_->SubscribeScanResultNotification(interface_index, handler);
}
// system/connectivity/wificond/scanning/scan_utils.cpp
void ScanUtils::SubscribeSchedScanResultNotification(
    uint32_t interface_index,
    OnSchedScanResultsReadyHandler handler) {
  netlink_manager_->SubscribeSchedScanResultNotification(interface_index,
                                                         handler);
}

向netlink_manager注册了两个handler,分别是 扫描 和 计划扫描。其实PNO扫描和普通扫描流程是一样的。

netlink_manager.cpp中的ReceivePacketAndRunHandler()方法阻塞读取netlink消息并解些

// system/connectivity/wificond/net/netlink_manager.cpp
void NetlinkManager::ReceivePacketAndRunHandler(int fd) {
    ssize_t len = read(fd, ReceiveBuffer, kReceiveBufferSize);
    // ... ...
    uint8_t* ptr = ReceiveBuffer;
  	while (ptr < ReceiveBuffer + len) {
        // ... ...
        // Handle multicasts.
        if (sequence_number == kBroadcastSequenceNumber) {
          BroadcastHandler(std::move(packet));
          continue;
        }
        // ... ...
    }
}

void NetlinkManager::BroadcastHandler(unique_ptr<const NL80211Packet> packet) {
    uint32_t command = packet->GetCommand();

    if (command == NL80211_CMD_NEW_SCAN_RESULTS ||
      	// Scan was aborted, for unspecified reasons.partial scan results may be
      	// available.
      	command == NL80211_CMD_SCAN_ABORTED) {
        OnScanResultsReady(std::move(packet));
        return;
    }

    if (command == NL80211_CMD_SCHED_SCAN_RESULTS ||
      	command == NL80211_CMD_SCHED_SCAN_STOPPED) {
        // 计划扫描结果
    	OnSchedScanResultsReady(std::move(packet));
    	return;
    }
    // ... ...
}

接着就调用回到了ScannerImpl了

void ScannerImpl::OnSchedScanResultsReady(uint32_t interface_index,
                                          bool scan_stopped) {
  if (pno_scan_event_handler_ != nullptr) {
    if (scan_stopped) {
      // If |pno_scan_started_| is false.
      // This stop notification might result from our own request.
      // See the document for NL80211_CMD_SCHED_SCAN_STOPPED in nl80211.h.
      if (pno_scan_started_) {
        LOG(WARNING) << "Unexpected pno scan stopped event";
        pno_scan_event_handler_->OnPnoScanFailed();
      }
      pno_scan_started_ = false;
    } else {
      LOG(INFO) << "Pno scan result ready event";
      pno_scan_event_handler_->OnPnoNetworkFound();
    }
  }
}

继续回到java层

在wifi打开时,setupInterfaceForClientMode方法会向wificond注册scanresults的回调函数

// frameworks/base/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java
public boolean setupInterfaceForClientMode(/*.. ...*/) {
    // ... ...
    PnoScanEventHandler pnoScanEventHandler = new PnoScanEventHandler(executor,
                    pnoScanCallback);
    mPnoScanEventHandlers.put(ifaceName,  pnoScanEventHandler);
    wificondScanner.subscribePnoScanEvents(pnoScanEventHandler);
}

PnoScanEventHandler收到回调后,继续往上传

private class PnoScanEventHandler extends IPnoScanEvent.Stub {
    @Override
    public void OnPnoNetworkFound() {
        Log.d(TAG, "Pno scan result event");
        final long token = Binder.clearCallingIdentity();
        try {
            mExecutor.execute(() -> mCallback.onScanResultReady());
        } finally {
            Binder.restoreCallingIdentity(token);
        }
    }
}

到了WifiNative,现看WifiNative是什么时候注册的

// packages/modules/Wifi/service/java/com/android/server/wifi/WifiNative.java
public String setupInterfaceForClientInConnectivityMode(/* ... ... */) {
    mWifiCondManager.setupInterfaceForClientMode(iface.name, Runnable::run,
                    new NormalScanEventCallback(iface.name),
                    new PnoScanEventCallback(iface.name))
}
private class PnoScanEventCallback implements WifiNl80211Manager.ScanEventCallback {
    public void onScanResultReady() {
        Log.d(TAG, "Pno scan result event");
        mWifiMonitor.broadcastPnoScanResultEvent(mIfaceName);
        mWifiMetrics.incrementPnoFoundNetworkEventCount();
    }
}

WifiMonitor

public void broadcastPnoScanResultEvent(String iface) {
    sendMessage(iface, PNO_SCAN_RESULTS_EVENT);
}

WifiCondScannerImpl进行处理,它再通过WifiNative -> WifiNl80211Manager -> wificond 从驱动获取扫描结果。

public boolean handleMessage(Message msg) {
    case WifiMonitor.PNO_SCAN_RESULTS_EVENT:
        pollLatestScanDataForPno();
        break;
}

private void pollLatestScanDataForPno() {
    synchronized (mSettingsLock) {
        if (mLastPnoScanSettings == null) {
             // got a scan before we started scanning or after scan was canceled
            return;
        }
        mNativePnoScanResults = mWifiNative.getPnoScanResults(getIfaceName());
        List<ScanResult> hwPnoScanResults = new ArrayList<>();
        int numFilteredScanResults = 0;
        for (int i = 0; i < mNativePnoScanResults.size(); ++i) {
            ScanResult result = mNativePnoScanResults.get(i).getScanResult();
            // nanoseconds -> microseconds
            if (result.timestamp >= mLastPnoScanSettings.startTimeNanos / 1_000) {
                hwPnoScanResults.add(result);
            } else {
                numFilteredScanResults++;
            }
        }

        if (numFilteredScanResults != 0) {
            Log.d(TAG, "Filtering out " + numFilteredScanResults + " pno scan results.");
        }

        if (mLastPnoScanSettings.pnoScanEventHandler != null) {
            ScanResult[] pnoScanResultsArray =
                    hwPnoScanResults.toArray(new ScanResult[hwPnoScanResults.size()]);
            mLastPnoScanSettings.pnoScanEventHandler.onPnoNetworkFound(pnoScanResultsArray);
        }
    }
}

从驱动获取到扫描结果列表后,回调给WifiScanningServiceImplpnoScanEventHandler,回调方法为onPnoNetworkFound.

WifiScanningServiceImpl再发消息CMD_PNO_NETWORK_FOUNDWifiScanner

WifiScanner收到后,将把结果传递给WifiConnectivityManager

case CMD_PNO_NETWORK_FOUND: {
    PnoScanListener pnoScanListener = (PnoScanListener) listener;
    ParcelableScanResults parcelableScanResults = (ParcelableScanResults) msg.obj;
    Binder.clearCallingIdentity();
    executor.execute(() ->
            pnoScanListener.onPnoNetworkFound(parcelableScanResults.getResults()));
} break;

最终WifiConnectivityManager拿着扫描结果去选择合适的网络进行连接。

下面是我手机上的一段日志,可以对理解流程有一定帮助

09-18 15:56:12.659  1296  1296 V wificond: startPnoScan
09-18 15:56:12.661  1296  1296 I wificond: Pno scan started for frequencies: 2412, 2437, 2462, 5180, 5220, 5260, 5300, 5745, 5785,
09-18 15:56:13.355  1296  1296 I wificond: Pno scan result ready event
09-18 15:56:13.356  1259  2580 D WifiNl80211Manager: Pno scan result event
09-18 15:56:13.356  1259  2580 D WifiNative: Pno scan result event
09-18 15:56:13.377  1259  2250 V WifiConnectivityManager: PnoScanListener onResults: start network selection
09-18 15:56:13.410  1259  2250 V WifiConnectivityManager: PnoScanListener:  WNS candidate-"mobile"

3. 总结

PNO扫描由WifiConnectivityManager负责发起(Disconnected并且Screen OFF),将请求传递给wificondwificond通过netlink设置相关扫描参数后请求驱动进行周期扫描(ScheduledScan)。驱动扫描结束后,现上报扫描结束事件NL80211_CMD_SCHED_SCAN_RESULTS,Framework层的WificondScannerImpl收到通知后再主动通过wificond从驱动获取扫描结果列表并继续上报,直到WifiConnectivityManager收到扫描结果的回调并处理。

下面是一个完整的流程图: 包括PNO扫描的发起,回调函数的注册,扫描结果的上报。
请添加图片描述

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值