目录
1.ClientModeImpl
1.1 getWifiLinkLayerStats
1.2 fetchRssiLinkSpeedAndFrequencyNative
1.3 calculateAndReportScore
2.总结
前言:WiFi除了自动连接的评分还有网络的评分,学习下具体逻辑
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的分数
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大概是获取链路层的收发包情况做个统计
/*
* 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 频率和信号强度。
// 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的矩阵
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更新网络。