(一百九十五)Android Q 学习WiFi的评分机制(一)

目录

1.ClientModeImpl

1.1 getWifiLinkLayerStats

1.2 fetchRssiLinkSpeedAndFrequencyNative

1.3  calculateAndReportScore

2.总结


 

前言:WiFi除了自动连接的评分还有网络的评分,学习下具体逻辑

 

1.ClientModeImpl

ClientModeImpl里面有如下涉及评分的代码梳理一下

        /**
         * Fetches link stats and updates Wifi Score Report.
         */
        private WifiLinkLayerStats updateLinkLayerStatsRssiAndScoreReportInternal() {
            WifiLinkLayerStats stats = getWifiLinkLayerStats();
            // Get Info and continue polling
            fetchRssiLinkSpeedAndFrequencyNative();
            // Send the update score to network agent.
            mWifiScoreReport.calculateAndReportScore(mWifiInfo, mNetworkAgent, mWifiMetrics);
            return stats;
        }

从api的注释就可以看到是获取链路层的状态和更新WiFi的分数

 

1.1 getWifiLinkLayerStats

    WifiLinkLayerStats getWifiLinkLayerStats() {
        if (mInterfaceName == null) {
            loge("getWifiLinkLayerStats called without an interface");
            return null;
        }
        mLastLinkLayerStatsUpdate = mClock.getWallClockMillis();
        WifiLinkLayerStats stats = mWifiNative.getWifiLinkLayerStats(mInterfaceName);
        if (stats != null) {
            mOnTime = stats.on_time;
            mTxTime = stats.tx_time;
            mRxTime = stats.rx_time;
            mRunningBeaconCount = stats.beacon_rx;
            mWifiInfo.updatePacketRates(stats, mLastLinkLayerStatsUpdate);
        } else {
            long mTxPkts = mFacade.getTxPackets(mInterfaceName);
            long mRxPkts = mFacade.getRxPackets(mInterfaceName);
            mWifiInfo.updatePacketRates(mTxPkts, mRxPkts, mLastLinkLayerStatsUpdate);
        }
        return stats;
    }

这个api大概是获取链路层的收发包情况做个统计

 

1.2 fetchRssiLinkSpeedAndFrequencyNative

    /*
     * Fetch RSSI, linkspeed, and frequency on current connection
     */
    private void fetchRssiLinkSpeedAndFrequencyNative() {
        WifiNative.SignalPollResult pollResult = mWifiNative.signalPoll(mInterfaceName);
        if (pollResult == null) {
            return;
        }

        int newRssi = pollResult.currentRssi;
        int newTxLinkSpeed = pollResult.txBitrate;
        int newFrequency = pollResult.associationFrequency;
        int newRxLinkSpeed = pollResult.rxBitrate;

        if (mVerboseLoggingEnabled) {
            logd("fetchRssiLinkSpeedAndFrequencyNative rssi=" + newRssi
                    + " TxLinkspeed=" + newTxLinkSpeed + " freq=" + newFrequency
                    + " RxLinkSpeed=" + newRxLinkSpeed);
        }

        if (newRssi > WifiInfo.INVALID_RSSI && newRssi < WifiInfo.MAX_RSSI) {
            // screen out invalid values
            /* some implementations avoid negative values by adding 256
             * so we need to adjust for that here.
             */
            if (newRssi > 0) {
                Log.wtf(TAG, "Error! +ve value RSSI: " + newRssi);
                newRssi -= 256;
            }
            mWifiInfo.setRssi(newRssi);
            /*
             * Rather then sending the raw RSSI out every time it
             * changes, we precalculate the signal level that would
             * be displayed in the status bar, and only send the
             * broadcast if that much more coarse-grained number
             * changes. This cuts down greatly on the number of
             * broadcasts, at the cost of not informing others
             * interested in RSSI of all the changes in signal
             * level.
             */
            int newSignalLevel = WifiManager.calculateSignalLevel(newRssi, WifiManager.RSSI_LEVELS);
            if (newSignalLevel != mLastSignalLevel) {
                updateCapabilities();
                sendRssiChangeBroadcast(newRssi);
            }
            mLastSignalLevel = newSignalLevel;
        } else {
            mWifiInfo.setRssi(WifiInfo.INVALID_RSSI);
            updateCapabilities();
        }
        /*
         * set Tx link speed only if it is valid
         */
        if (newTxLinkSpeed > 0) {
            mWifiInfo.setLinkSpeed(newTxLinkSpeed);
            mWifiInfo.setTxLinkSpeedMbps(newTxLinkSpeed);
        }
        /*
         * set Rx link speed only if it is valid
         */
        if (newRxLinkSpeed > 0) {
            mWifiInfo.setRxLinkSpeedMbps(newRxLinkSpeed);
        }
        if (newFrequency > 0) {
            mWifiInfo.setFrequency(newFrequency);
        }
        mWifiConfigManager.updateScanDetailCacheFromWifiInfo(mWifiInfo);
        /*
         * Increment various performance metrics
         */
        mWifiMetrics.handlePollResult(mWifiInfo);
    }

获取上传下载速度、当前连接的WiFi 频率和信号强度。

 

1.3  calculateAndReportScore

            // Send the update score to network agent.
            mWifiScoreReport.calculateAndReportScore(mWifiInfo, mNetworkAgent, mWifiMetrics);



WifiScoreReport

    /**
     * Calculate wifi network score based on updated link layer stats and send the score to
     * the provided network agent.
     *
     * If the score has changed from the previous value, update the WifiNetworkAgent.
     *
     * Called periodically (POLL_RSSI_INTERVAL_MSECS) about every 3 seconds.
     *
     * @param wifiInfo WifiInfo instance pointing to the currently connected network.
     * @param networkAgent NetworkAgent to be notified of new score.
     * @param wifiMetrics for reporting our scores.
     */
    public void calculateAndReportScore(WifiInfo wifiInfo, NetworkAgent networkAgent,
                                        WifiMetrics wifiMetrics) {
        if (wifiInfo.getRssi() == WifiInfo.INVALID_RSSI) {
            Log.d(TAG, "Not reporting score because RSSI is invalid");
            return;
        }
        int score;

        long millis = mClock.getWallClockMillis();
        int netId = 0;

        if (networkAgent != null) {
            netId = networkAgent.netId;
        }

        mAggressiveConnectedScore.updateUsingWifiInfo(wifiInfo, millis);
        mVelocityBasedConnectedScore.updateUsingWifiInfo(wifiInfo, millis);

        int s1 = mAggressiveConnectedScore.generateScore();
        int s2 = mVelocityBasedConnectedScore.generateScore();

        score = s2;

        if (wifiInfo.score > ConnectedScore.WIFI_TRANSITION_SCORE
                 && score <= ConnectedScore.WIFI_TRANSITION_SCORE
                 && wifiInfo.txSuccessRate >= mScoringParams.getYippeeSkippyPacketsPerSecond()
                 && wifiInfo.rxSuccessRate >= mScoringParams.getYippeeSkippyPacketsPerSecond()) {
            score = ConnectedScore.WIFI_TRANSITION_SCORE + 1;
        }

        if (wifiInfo.score > ConnectedScore.WIFI_TRANSITION_SCORE
                 && score <= ConnectedScore.WIFI_TRANSITION_SCORE) {
            // We don't want to trigger a downward breach unless the rssi is
            // below the entry threshold.  There is noise in the measured rssi, and
            // the kalman-filtered rssi is affected by the trend, so check them both.
            // TODO(b/74613347) skip this if there are other indications to support the low score
            int entry = mScoringParams.getEntryRssi(wifiInfo.getFrequency());
            if (mVelocityBasedConnectedScore.getFilteredRssi() >= entry
                    || wifiInfo.getRssi() >= entry) {
                // Stay a notch above the transition score to reduce ambiguity.
                score = ConnectedScore.WIFI_TRANSITION_SCORE + 1;
            }
        }

        if (wifiInfo.score >= ConnectedScore.WIFI_TRANSITION_SCORE
                 && score < ConnectedScore.WIFI_TRANSITION_SCORE) {
            mLastDownwardBreachTimeMillis = millis;
        } else if (wifiInfo.score < ConnectedScore.WIFI_TRANSITION_SCORE
                 && score >= ConnectedScore.WIFI_TRANSITION_SCORE) {
            // Staying at below transition score for a certain period of time
            // to prevent going back to wifi network again in a short time.
            long elapsedMillis = millis - mLastDownwardBreachTimeMillis;
            if (elapsedMillis < MIN_TIME_TO_KEEP_BELOW_TRANSITION_SCORE_MILLIS) {
                score = wifiInfo.score;
            }
        }

        //sanitize boundaries
        if (score > NetworkAgent.WIFI_BASE_SCORE) {
            score = NetworkAgent.WIFI_BASE_SCORE;
        }
        if (score < 0) {
            score = 0;
        }

        logLinkMetrics(wifiInfo, millis, netId, s1, s2, score);

        //report score
        if (score != wifiInfo.score) {
            if (mVerboseLoggingEnabled) {
                Log.d(TAG, "report new wifi score " + score);
            }
            wifiInfo.score = score;
            if (networkAgent != null) {
                networkAgent.sendNetworkScore(score);
            }
        }

        wifiMetrics.incrementWifiScoreCount(score);
        mScore = score;
    }

先不看评分逻辑,先看下初始化

        mWifiScoreReport = new WifiScoreReport(mWifiInjector.getScoringParams(), mClock);


    WifiScoreReport(ScoringParams scoringParams, Clock clock) {
        mScoringParams = scoringParams;
        mClock = clock;
        mAggressiveConnectedScore = new AggressiveConnectedScore(scoringParams, clock);
        mVelocityBasedConnectedScore = new VelocityBasedConnectedScore(scoringParams, clock);
    }

那再看下ScoringParams的初始化

    public ScoringParams(Context context, FrameworkFacade facade, Handler handler) {
        loadResources(context);
        setupContentObserver(context, facade, handler);
    }

    private void loadResources(Context context) {
        mVal.rssi2[EXIT] = context.getResources().getInteger(
                R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_24GHz);
        mVal.rssi2[ENTRY] = context.getResources().getInteger(
                R.integer.config_wifi_framework_wifi_score_entry_rssi_threshold_24GHz);
        mVal.rssi2[SUFFICIENT] = context.getResources().getInteger(
                R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_24GHz);
        mVal.rssi2[GOOD] = context.getResources().getInteger(
                R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_24GHz);
        mVal.rssi5[EXIT] = context.getResources().getInteger(
                R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_5GHz);
        mVal.rssi5[ENTRY] = context.getResources().getInteger(
                R.integer.config_wifi_framework_wifi_score_entry_rssi_threshold_5GHz);
        mVal.rssi5[SUFFICIENT] = context.getResources().getInteger(
                R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_5GHz);
        mVal.rssi5[GOOD] = context.getResources().getInteger(
                R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_5GHz);
        try {
            mVal.validate();
        } catch (IllegalArgumentException e) {
            Log.wtf(TAG, "Inconsistent config_wifi_framework_ resources: " + this, e);
        }
    }

对一些变量进行了初始化

    
    -80
    
    -77
    -70
    -57
    -83
    -80
    -73
    -60

变量定义在framework/base/core/res里

在一步一步看下分的计算

        mAggressiveConnectedScore.updateUsingWifiInfo(wifiInfo, millis);
        mVelocityBasedConnectedScore.updateUsingWifiInfo(wifiInfo, millis);

        int s1 = mAggressiveConnectedScore.generateScore();
        int s2 = mVelocityBasedConnectedScore.generateScore();

        score = s2;

mAggressiveConnectedScore看起来没啥用,类的注释里也写的实验性的分数计算

那我们就先看下VelocityBasedConnectedScore,基于速度的得分计算

    /**
     * Updates the state.
     */
    @Override
    public void updateUsingWifiInfo(WifiInfo wifiInfo, long millis) {
        int frequency = wifiInfo.getFrequency();
        if (frequency != mFrequency) {
            mLastMillis = 0; // Probably roamed; reset filter but retain threshold adjustment
            // Consider resetting or partially resetting threshold adjustment
            // Consider checking bssid
            mFrequency = frequency;
        }
        updateUsingRssi(wifiInfo.getRssi(), millis, mDefaultRssiStandardDeviation);
        adjustThreshold(wifiInfo);
    }

    /**
     * Updates scoring state using RSSI and measurement noise estimate
     * 

* This is useful if an RSSI comes from another source (e.g. scan results) and the * expected noise varies by source. * * @param rssi signal strength (dB). * @param millis millisecond-resolution time. * @param standardDeviation of the RSSI. */ @Override public void updateUsingRssi(int rssi, long millis, double standardDeviation) { if (millis <= 0) return; if (mLastMillis <= 0 || millis < mLastMillis || mFilter.mx == null) { double initialVariance = 9.0 * standardDeviation * standardDeviation; mFilter.mx = new Matrix(1, new double[]{rssi, 0.0}); mFilter.mP = new Matrix(2, new double[]{initialVariance, 0.0, 0.0, 0.0}); } else { double dt = (millis - mLastMillis) * 0.001; mFilter.mR.put(0, 0, standardDeviation * standardDeviation); setDeltaTimeSeconds(dt); mFilter.predict(); mFilter.update(new Matrix(1, new double[]{rssi})); } mLastMillis = millis; mFilteredRssi = mFilter.mx.get(0, 0); mEstimatedRateOfRssiChange = mFilter.mx.get(1, 0); } /** * Adjusts the threshold if appropriate *

* If the (filtered) rssi is near or below the current effective threshold, and the * rate of rssi change is small, and there is traffic, and the error rate is looking * reasonable, then decrease the effective threshold to keep from dropping a perfectly good * connection. * */ private void adjustThreshold(WifiInfo wifiInfo) { if (mThresholdAdjustment < -7) return; if (mFilteredRssi >= getAdjustedRssiThreshold() + 2.0) return; if (Math.abs(mEstimatedRateOfRssiChange) >= 0.2) return; double txSuccessPps = wifiInfo.txSuccessRate; double rxSuccessPps = wifiInfo.rxSuccessRate; if (txSuccessPps < mMinimumPpsForMeasuringSuccess) return; if (rxSuccessPps < mMinimumPpsForMeasuringSuccess) return; double txBadPps = wifiInfo.txBadRate; double txRetriesPps = wifiInfo.txRetriesRate; double probabilityOfSuccessfulTx = txSuccessPps / (txSuccessPps + txBadPps + txRetriesPps); if (probabilityOfSuccessfulTx > 0.2) { // May want this amount to vary with how close to threshold we are mThresholdAdjustment -= 0.5; } }

根据wifiInfo更新一些参数,其中

1)

    /** This is a typical STD for the connected RSSI for a phone sitting still */
    public double mDefaultRssiStandardDeviation = 2.0;

标准偏差

2) Matrix

矩阵

mFilter.mx = new Matrix(1, new double[]{rssi, 0.0});

这个表示生成一个列数为1的,成员数组为rssi,0.0的矩阵\begin{vmatrix} \\ rssi \\ 0.0 \end{vmatrix}

3) KalmanFilter

/**
 * Utility providiing a basic Kalman filter
 *
 * For background, see https://en.wikipedia.org/wiki/Kalman_filter
 */
public class KalmanFilter {
    public Matrix mF; // stateTransition
    public Matrix mQ; // processNoiseCovariance
    public Matrix mH; // observationModel
    public Matrix mR; // observationNoiseCovariance
    public Matrix mP; // aPosterioriErrorCovariance
    public Matrix mx; // stateEstimate

卡尔曼滤波

mp:后验误差协方差

mx:状态估计

 

    /**
     * Velocity scorer - predict the rssi a few seconds from now
     */
    @Override
    public int generateScore() {
        if (mFilter.mx == null) return WIFI_TRANSITION_SCORE + 1;
        double badRssi = getAdjustedRssiThreshold();
        double horizonSeconds = mScoringParams.getHorizonSeconds();
        Matrix x = new Matrix(mFilter.mx);
        double filteredRssi = x.get(0, 0);
        setDeltaTimeSeconds(horizonSeconds);
        x = mFilter.mF.dot(x);
        double forecastRssi = x.get(0, 0);
        if (forecastRssi > filteredRssi) {
            forecastRssi = filteredRssi; // Be pessimistic about predicting an actual increase
        }
        int score = (int) (Math.round(forecastRssi) - badRssi) + WIFI_TRANSITION_SCORE;
        return score;
    }

计算评分,算法有点懵逼。。。

略过,估计要把卡尔曼滤波知识补过才好看这算法

 

        //report score
        if (score != wifiInfo.score) {
            if (mVerboseLoggingEnabled) {
                Log.d(TAG, "report new wifi score " + score);
            }
            wifiInfo.score = score;
            if (networkAgent != null) {
                networkAgent.sendNetworkScore(score);
            }
        }

后面可以看到将计算好的得分更新到WifiInfo和NetworkAgent里去,NetworkAgent会通知CS更新网络。

 

2.总结

(一百九十五)Android Q 学习WiFi的评分机制(一)_第1张图片

 

你可能感兴趣的:(Wifi)