(一百九十五)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);
        }
    }

对一些变量进行了初始化

    <!-- Integer threshold for low network score, should be somewhat less than the entry threshhold -->
    <integer translatable="false" name="config_wifi_framework_wifi_score_bad_rssi_threshold_5GHz">-80</integer>
    <!-- Integer threshold, do not connect to APs with RSSI lower than the entry threshold -->
    <integer translatable="false" name="config_wifi_framework_wifi_score_entry_rssi_threshold_5GHz">-77</integer>
    <integer translatable="false" name="config_wifi_framework_wifi_score_low_rssi_threshold_5GHz">-70</integer>
    <integer translatable="false" name="config_wifi_framework_wifi_score_good_rssi_threshold_5GHz">-57</integer>
    <integer translatable="false" name="config_wifi_framework_wifi_score_bad_rssi_threshold_24GHz">-83</integer>
    <integer translatable="false" name="config_wifi_framework_wifi_score_entry_rssi_threshold_24GHz">-80</integer>
    <integer translatable="false" name="config_wifi_framework_wifi_score_low_rssi_threshold_24GHz">-73</integer>
    <integer translatable="false" name="config_wifi_framework_wifi_score_good_rssi_threshold_24GHz">-60</integer>

变量定义在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
     * <p>
     * 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
     * <p>
     * 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.总结

发布了198 篇原创文章 · 获赞 65 · 访问量 16万+

猜你喜欢

转载自blog.csdn.net/sinat_20059415/article/details/103639106