WifiConnectivityManager 管理扫描

写这篇博文前,参考了不少优秀的博客,对照着Android 11的源码,做了一次不大精细的走读
本文源自WifiConnectivityManager 管理扫描

WifiConnectivityManager 通过 WifiScanner.java 中 WifiScanner 类的 registerScanListener 方法注册 Scan 结果的回调监听句柄,实际上内部通过 AsyncChannel 向 WifiScanningService 层发送了 CMD_REGISTER_SCAN_LISTENER 消息:

/**
 * Helper method to populate WifiScanner handle. This is done lazily because
 * WifiScanningService is started after WifiService.
 */
private void retrieveWifiScanner() {
    if (mScanner != null) return;
    mScanner = mWifiInjector.getWifiScanner();
    checkNotNull(mScanner);
    // Register for all single scan results
    mScanner.registerScanListener(new HandlerExecutor(mEventHandler), mAllSingleScanListener);
}

/**
 * Start WifiConnectivityManager
 */
private void start() {
    if (mRunning) return;
    retrieveWifiScanner();
    ......
}

// All single scan results listener.
//
// Note: This is the listener for all the available single scan results,
//       including the ones initiated by WifiConnectivityManager and
//       other modules.
private class AllSingleScanListener implements WifiScanner.ScanListener {...}

WifiScanner 源码见 /frameworks/base/wifi/java/android/net/wifi/WifiScanner.java

/**
 * Register a listener that will receive results from all single scans.
 * Either the {@link ScanListener#onSuccess()} or  {@link ScanListener#onFailure(int, String)}
 * method will be called once when the listener is registered.
 * Afterwards (assuming onSuccess was called), all subsequent single scan results will be
 * delivered to the listener. It is possible that onFullResult will not be called for all
 * results of the first scan if the listener was registered during the scan.
 *
 * @param executor the Executor on which to run the callback.
 * @param listener specifies the object to report events to. This object is also treated as a
 *                 key for this request, and must also be specified to cancel the request.
 *                 Multiple requests should also not share this object.
 */
@RequiresPermission(Manifest.permission.NETWORK_STACK)
public void registerScanListener(@NonNull @CallbackExecutor Executor executor,
        @NonNull ScanListener listener) {
    Objects.requireNonNull(executor, "executor cannot be null");
    Objects.requireNonNull(listener, "listener cannot be null");
    int key = addListener(listener, executor);
    if (key == INVALID_KEY) return;
    validateChannel();
    mAsyncChannel.sendMessage(CMD_REGISTER_SCAN_LISTENER, 0, key);
}

mAllSingleScanListener 为 AllSingleScanListener 类对象,实现了 WifiScanner 的 ScanListener 接口类,在服务端进行 reportScanResults 等操作时对应的方法被调用用来处理回调事件

class WifiScanningService 定义在 WifiScanningService.java 文件中,其实际的实现在 WifiScanningServiceImpl 类中,向系统注册并提供 WIFI_SCANNING_SERVICE 服务:

服务的实际注册发生在 WifiFrameworkInitializer 中的 registerServiceWrappers 方法中,与 WIFI_SERVICE 等一起注册。客户端则在 WifiScanner 类中实现,在文件 WifiScanner.java 文件中

接下来看在服务端 WifiScanningServiceImpl 中的处理过程:

  1. 消息处理发生在 ClientHandler 内部类中,其 handleMessage 接口进行处理,实际上就是将对应的请求信息添加到 mSingleScanListeners 链中,其类型为 RequestList<>,添加方法为 addRequest,也是内部私有实现的类;

  2. 消息回送发生在 reportFullScanResult 和 reportScanResults 接口中,通知消息有 CMD_SCAN_RESULT,CMD_SINGLE_SCAN_COMPLETED 和 CMD_FULL_SCAN_RESULT 三种;

WifiConnectivityManager 类中通过 retrieveWifiScanner 方法创建 WifiScanner 对象 mScanner,使其指向 WIFI_SCANNING_SERVICE 服务,同时向服务端注册 Scan 监听者

/**
 * Service implementing Wi-Fi scanning functionality. Delegates actual interface
 * implementation to WifiScanningServiceImpl.
 */
public class WifiScanningService extends SystemService {

    static final String TAG = "WifiScanningService";
    private final WifiScanningServiceImpl mImpl;
    private final HandlerThread mHandlerThread;

    public WifiScanningService(Context contextBase) {
        super(new WifiContext(contextBase));
        Log.i(TAG, "Creating " + Context.WIFI_SCANNING_SERVICE);
        mHandlerThread = new HandlerThread("WifiScanningService");
        mHandlerThread.start();
        mImpl = new WifiScanningServiceImpl(getContext(), mHandlerThread.getLooper(),
                WifiScannerImpl.DEFAULT_FACTORY,
                getContext().getSystemService(BatteryStatsManager.class),
                WifiInjector.getInstance());
    }

    @Override
    public void onStart() {
        Log.i(TAG, "Publishing " + Context.WIFI_SCANNING_SERVICE);
        publishBinderService(Context.WIFI_SCANNING_SERVICE, mImpl);
    }

    @Override
    public void onBootPhase(int phase) {
        if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
            Log.i(TAG, "Starting " + Context.WIFI_SCANNING_SERVICE);
            mImpl.startService();
        }
    }
}

回到 WifiConnectivityManager 中的消息处理过程,假设是报告扫描结果,则 AllSingleScanListener 类中的 onResults 方法被调用,其处理过程如下:

@Override
public void onResults(WifiScanner.ScanData[] results) {
    if (!mWifiEnabled || !mAutoJoinEnabled) {
        clearScanDetails();
        mWaitForFullBandScanResults = false;
        return;
    }

    // We treat any full band scans (with DFS or not) as "full".
    boolean isFullBandScanResults = false;
    if (results != null && results.length > 0) {
        isFullBandScanResults =
                WifiScanner.isFullBandScan(results[0].getBandScanned(), true);
    }
    // Full band scan results only.
    if (mWaitForFullBandScanResults) {
        if (!isFullBandScanResults) {
            localLog("AllSingleScanListener waiting for full band scan results.");
            clearScanDetails();
            return;
        } else {
            mWaitForFullBandScanResults = false;
        }
    }
    if (results != null && results.length > 0) {
        mWifiMetrics.incrementAvailableNetworksHistograms(mScanDetails,
                isFullBandScanResults);
    }
    if (mNumScanResultsIgnoredDueToSingleRadioChain > 0) {
        Log.i(TAG, "Number of scan results ignored due to single radio chain scan: "
                + mNumScanResultsIgnoredDueToSingleRadioChain);
    }
    boolean wasConnectAttempted = handleScanResults(mScanDetails,
            ALL_SINGLE_SCAN_LISTENER, isFullBandScanResults);
    clearScanDetails();

    // Update metrics to see if a single scan detected a valid network
    // while PNO scan didn't.
    // Note: We don't update the background scan metrics any more as it is
    //       not in use.
    if (mPnoScanStarted) {
        if (wasConnectAttempted) {
            mWifiMetrics.incrementNumConnectivityWatchdogPnoBad();
        } else {
            mWifiMetrics.incrementNumConnectivityWatchdogPnoGood();
        }
    }

    // Check if we are in the middle of initial partial scan
    if (mInitialScanState == INITIAL_SCAN_STATE_AWAITING_RESPONSE) {
        // Done with initial scan
        setInitialScanState(INITIAL_SCAN_STATE_COMPLETE);

        if (wasConnectAttempted) {
            Log.i(TAG, "Connection attempted with the reduced initial scans");
            schedulePeriodicScanTimer(
                    getScheduledSingleScanIntervalMs(mCurrentSingleScanScheduleIndex));
            mWifiMetrics.reportInitialPartialScan(mInitialPartialScanChannelCount, true);
            mInitialPartialScanChannelCount = 0;
        } else {
            Log.i(TAG, "Connection was not attempted, issuing a full scan");
            startConnectivityScan(SCAN_IMMEDIATELY);
            mFailedInitialPartialScan = true;
        }
    } else if (mInitialScanState == INITIAL_SCAN_STATE_COMPLETE) {
        if (mFailedInitialPartialScan && wasConnectAttempted) {
            // Initial scan failed, but following full scan succeeded
            mWifiMetrics.reportInitialPartialScan(mInitialPartialScanChannelCount, false);
        }
        mFailedInitialPartialScan = false;
        mInitialPartialScanChannelCount = 0;
    }
}

具体的处理过程分析如下:

  1. 判断 WiFi 开启(mWifiEnabled),并且打开了自动链接配置(mAutoJoinEnabled),否则退出;

  2. 确定是否 FullBandScan 的结果(isFullBandScanResults);

  3. 判断是否需要等带 FullBandScan 的结果,并根据第 2 步判断的结果来确定是否继续处理或者等待;

  4. 更新 mWifiMetrics 的相关信息,关于 WifiMetrics 后面再分析;

  5. 记录一些 Log 信息;

  6. 最重要的 handleScanResults 登场,实际的扫描结果处理发生在该接口中,稍后详细分析该函数实现;

  7. 处理之后清楚扫描的结果内容;

  8. 继续更新 mWifiMetrics 相关的信息;

  9. 查询当前的扫描状态,并进行相应的更新以及 mWifiMetrics 处理,如果链接过程并未进行,则触发 WiFi 的连接过程( startConnectivityScan ,并且立即连接 SCAN_IMMEIDATELY);

/**
 * Handles 'onResult' callbacks for the Periodic, Single & Pno ScanListener.
 * Executes selection of potential network candidates, initiation of connection attempt to that
 * network.
 *
 * @return true - if a candidate is selected by WifiNetworkSelector
 *         false - if no candidate is selected by WifiNetworkSelector
 */
private boolean handleScanResults(List<ScanDetail> scanDetails, String listenerName,
        boolean isFullScan) {
    mWifiChannelUtilization.refreshChannelStatsAndChannelUtilization(
            mStateMachine.getWifiLinkLayerStats(), WifiChannelUtilization.UNKNOWN_FREQ);

    updateUserDisabledList(scanDetails);

    // Check if any blocklisted BSSIDs can be freed.
    Set<String> bssidBlocklist = mBssidBlocklistMonitor.updateAndGetBssidBlocklist();

    if (mStateMachine.isSupplicantTransientState()) {
        localLog(listenerName
                + " onResults: No network selection because supplicantTransientState is "
                + mStateMachine.isSupplicantTransientState());
        return false;
    }

    localLog(listenerName + " onResults: start network selection");

    List<WifiCandidates.Candidate> candidates = mNetworkSelector.getCandidatesFromScan(
            scanDetails, bssidBlocklist, mWifiInfo, mStateMachine.isConnected(),
            mStateMachine.isDisconnected(), mUntrustedConnectionAllowed);
    mLatestCandidates = candidates;
    mLatestCandidatesTimestampMs = mClock.getElapsedSinceBootMillis();

    if (mDeviceMobilityState == WifiManager.DEVICE_MOBILITY_STATE_HIGH_MVMT
            && mContext.getResources().getBoolean(
                    R.bool.config_wifiHighMovementNetworkSelectionOptimizationEnabled)) {
        candidates = filterCandidatesHighMovement(candidates, listenerName, isFullScan);
    }

    WifiConfiguration candidate = mNetworkSelector.selectNetwork(candidates);
    mLastNetworkSelectionTimeStamp = mClock.getElapsedSinceBootMillis();
    mWifiLastResortWatchdog.updateAvailableNetworks(
            mNetworkSelector.getConnectableScanDetails());
    mWifiMetrics.countScanResults(scanDetails);
    //OS : Modify by chenyang 20200805 start
    boolean isGameMode = Settings.Global.getInt(
        mContext.getContentResolver(),
        "transsion_game_mode", 0) == 1;
    isGameMode = isGameMode && (Settings.Global.getInt(
        mContext.getContentResolver(),
        "os_change_network_protection", 0) == 1) && isMobileConnected();
    if (candidate != null && !isGameMode) {
    //OS : Modify by chenyang 20200805 end
        localLog(listenerName + ":  WNS candidate-" + candidate.SSID);
        connectToNetwork(candidate);
        return true;
    } else {
        if (mWifiState == WIFI_STATE_DISCONNECTED) {
            mOpenNetworkNotifier.handleScanResults(
                    mNetworkSelector.getFilteredScanDetailsForOpenUnsavedNetworks());
        }
        return false;
    }
}

处理过程分析如下:

  1. 更新 WifiChannelUtilization 统计信息;

  2. 更新和释放 Block 的 ssid 列表信息;

  3. 判断当前是否处理 transient 状态,如果是的话则不再进一步处理;

  4. 开始进行 WiFi 网络选择处理过程:

① 通过 NetworkSelector 类的 getCandidatesFromScan 方法对扫描结果进行处理,获取 candidates 列表,类型为 WifiCandidates.Candidate;

② 更新 mLatestCandidates 为最新,并且记录其 timestamp 信息(mLatestCandidatesTimestampMs);

③ 如果设备处于高速移动(WifiManager.DEVICE_MOBILITY_STATE_HIGH_MVMT)且可以进行高速移动下的优化选择,则调用 filterCandidatesHighMovement 对获取到的 candidates 进行过滤;

④ 继续调用 NetworkSelector 的 selectNetwork 方法选择网络,结果为 WifiConfiguration 类型,同时更新选择时间戳;

⑤ 更新 WifiLastResortWatchdog 中记录的可用网络信息,用于更准确的网络模块重启操作;

⑥ 更新 WifiMetrics 信息;

⑦ 如果存在被选中的 candidates,则进行连接过程 connectToNetwork;

⑧ 否则如果当前处于 WIFI_STATE_DISCONNECTED 状态,则调用 OpenNetworkNotifier 的 handleScanResults 接口进行处理,主要是在扫描结果中选择网络推荐给应用进行连接;

这里最重要的是三个点:WifiMetrics 类相关方法和功能的实现,NetworkSelector 的实现以及 connectToNetwork,也包括 WifiConfiguration 类作为参数传递给 connectToNetwork,下面分别进行分析。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值