• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.wifi;
18 
19 import android.net.wifi.ScanResult;
20 import android.net.wifi.WifiInfo;
21 import android.util.Log;
22 
23 import com.android.server.wifi.util.KalmanFilter;
24 import com.android.server.wifi.util.Matrix;
25 
26 /**
27  * Class used to calculate scores for connected wifi networks and report it to the associated
28  * network agent.
29  */
30 public class VelocityBasedConnectedScore extends ConnectedScore {
31 
32     public static final String TAG = "WifiVelocityBasedConnectedScore";
33     private final ScoringParams mScoringParams;
34 
35     private int mFrequency = ScanResult.BAND_5_GHZ_START_FREQ_MHZ;
36     private double mThresholdAdjustment;
37     private final KalmanFilter mFilter;
38     private long mLastMillis;
39 
VelocityBasedConnectedScore(ScoringParams scoringParams, Clock clock)40     public VelocityBasedConnectedScore(ScoringParams scoringParams, Clock clock) {
41         super(clock);
42         mScoringParams = scoringParams;
43         mFilter = new KalmanFilter();
44         mFilter.mH = new Matrix(2, new double[]{1.0, 0.0});
45         mFilter.mR = new Matrix(1, new double[]{1.0});
46     }
47 
48     /**
49      * Set the Kalman filter's state transition matrix F and process noise covariance Q given
50      * a time step.
51      *
52      * @param dt delta time, in seconds
53      */
setDeltaTimeSeconds(double dt)54     private void setDeltaTimeSeconds(double dt) {
55         mFilter.mF = new Matrix(2, new double[]{1.0, dt, 0.0, 1.0});
56         Matrix tG = new Matrix(1, new double[]{0.5 * dt * dt, dt});
57         double stda = 0.02; // standard deviation of modelled acceleration
58         mFilter.mQ = tG.dotTranspose(tG).dot(new Matrix(2, new double[]{
59                 stda * stda, 0.0,
60                 0.0, stda * stda}));
61     }
62     /**
63      * Reset the filter state.
64      */
65     @Override
reset()66     public void reset() {
67         mLastMillis = 0;
68         mThresholdAdjustment = 0;
69         mFilter.mx = null;
70     }
71 
72     /**
73      * Updates scoring state using RSSI and measurement noise estimate
74      * <p>
75      * This is useful if an RSSI comes from another source (e.g. scan results) and the
76      * expected noise varies by source.
77      *
78      * @param rssi              signal strength (dB).
79      * @param millis            millisecond-resolution time.
80      * @param standardDeviation of the RSSI.
81      */
82     @Override
updateUsingRssi(int rssi, long millis, double standardDeviation)83     public void updateUsingRssi(int rssi, long millis, double standardDeviation) {
84         if (millis <= 0) return;
85         try {
86             if (mLastMillis <= 0 || millis < mLastMillis || mFilter.mx == null) {
87                 double initialVariance = 9.0 * standardDeviation * standardDeviation;
88                 mFilter.mx = new Matrix(1, new double[]{rssi, 0.0});
89                 mFilter.mP = new Matrix(2, new double[]{initialVariance, 0.0, 0.0, 0.0});
90             } else {
91                 double dt = (millis - mLastMillis) * 0.001;
92                 mFilter.mR.put(0, 0, standardDeviation * standardDeviation);
93                 setDeltaTimeSeconds(dt);
94                 mFilter.predict();
95                 mFilter.update(new Matrix(1, new double[]{rssi}));
96             }
97             mLastMillis = millis;
98             mFilteredRssi = mFilter.mx.get(0, 0);
99             mEstimatedRateOfRssiChange = mFilter.mx.get(1, 0);
100         } catch (RuntimeException e) {
101             Log.wtf(TAG, e);
102             reset();
103         }
104     }
105 
106     /**
107      * Updates the state.
108      */
109     @Override
updateUsingWifiInfo(WifiInfo wifiInfo, long millis)110     public void updateUsingWifiInfo(WifiInfo wifiInfo, long millis) {
111         int frequency = wifiInfo.getFrequency();
112         if (frequency != mFrequency) {
113             mLastMillis = 0; // Probably roamed; reset filter but retain threshold adjustment
114             // Consider resetting or partially resetting threshold adjustment
115             // Consider checking bssid
116             mFrequency = frequency;
117         }
118         updateUsingRssi(wifiInfo.getRssi(), millis, mDefaultRssiStandardDeviation);
119         adjustThreshold(wifiInfo);
120     }
121 
122     private double mFilteredRssi;
123     private double mEstimatedRateOfRssiChange;
124 
125     /**
126      * Returns the most recently computed estimate of the RSSI.
127      */
getFilteredRssi()128     public double getFilteredRssi() {
129         return mFilteredRssi;
130     }
131 
132     /**
133      * Returns the estimated rate of change of RSSI, in dB/second
134      */
getEstimatedRateOfRssiChange()135     public double getEstimatedRateOfRssiChange() {
136         return mEstimatedRateOfRssiChange;
137     }
138 
139     /**
140      * Returns the adjusted RSSI threshold
141      */
getAdjustedRssiThreshold()142     public double getAdjustedRssiThreshold() {
143         return mScoringParams.getExitRssi(mFrequency) + mThresholdAdjustment;
144     }
145 
146     private double mMinimumPpsForMeasuringSuccess = 2.0;
147 
148     /**
149      * Adjusts the threshold if appropriate
150      * <p>
151      * If the (filtered) rssi is near or below the current effective threshold, and the
152      * rate of rssi change is small, and there is traffic, and the error rate is looking
153      * reasonable, then decrease the effective threshold to keep from dropping a perfectly good
154      * connection.
155      *
156      */
adjustThreshold(WifiInfo wifiInfo)157     private void adjustThreshold(WifiInfo wifiInfo) {
158         if (mThresholdAdjustment < -7) return;
159         if (mFilteredRssi >= getAdjustedRssiThreshold() + 2.0) return;
160         if (Math.abs(mEstimatedRateOfRssiChange) >= 0.2) return;
161         double txSuccessPps = wifiInfo.getSuccessfulTxPacketsPerSecond();
162         double rxSuccessPps = wifiInfo.getSuccessfulRxPacketsPerSecond();
163         if (txSuccessPps < mMinimumPpsForMeasuringSuccess) return;
164         if (rxSuccessPps < mMinimumPpsForMeasuringSuccess) return;
165         double txBadPps = wifiInfo.getLostTxPacketsPerSecond();
166         double txRetriesPps = wifiInfo.getRetriedTxPacketsPerSecond();
167         double probabilityOfSuccessfulTx = txSuccessPps / (txSuccessPps + txBadPps + txRetriesPps);
168         if (probabilityOfSuccessfulTx > 0.2) {
169             // May want this amount to vary with how close to threshold we are
170             mThresholdAdjustment -= 0.5;
171         }
172     }
173 
174     /**
175      * Velocity scorer - predict the rssi a few seconds from now
176      */
177     @Override
generateScore()178     public int generateScore() {
179         if (mFilter.mx == null) return WIFI_TRANSITION_SCORE + 1;
180         double badRssi = getAdjustedRssiThreshold();
181         double horizonSeconds = mScoringParams.getHorizonSeconds();
182         Matrix x = new Matrix(mFilter.mx);
183         double filteredRssi = x.get(0, 0);
184         setDeltaTimeSeconds(horizonSeconds);
185         x = mFilter.mF.dot(x);
186         double forecastRssi = x.get(0, 0);
187         if (forecastRssi > filteredRssi) {
188             forecastRssi = filteredRssi; // Be pessimistic about predicting an actual increase
189         }
190         int score = (int) (Math.round(forecastRssi) - badRssi) + WIFI_TRANSITION_SCORE;
191         return score;
192     }
193 }
194