• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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.NetworkAgent;
20 import android.net.wifi.WifiConfiguration;
21 import android.net.wifi.WifiInfo;
22 import android.util.Log;
23 
24 
25 /**
26 * Calculate scores for connected wifi networks.
27 */
28 public class WifiScoreReport {
29     // TODO: switch to WifiScoreReport if it doesn't break any tools
30     private static final String TAG = "WifiStateMachine";
31 
32     // TODO: This score was hardcorded to 56.  Need to understand why after finishing code refactor
33     private static final int STARTING_SCORE = 56;
34 
35     // TODO: Understand why these values are used
36     private static final int MAX_BAD_LINKSPEED_COUNT = 6;
37     private static final int SCAN_CACHE_VISIBILITY_MS = 12000;
38     private static final int HOME_VISIBLE_NETWORK_MAX_COUNT = 6;
39     private static final int SCAN_CACHE_COUNT_PENALTY = 2;
40     private static final int AGGRESSIVE_HANDOVER_PENALTY = 6;
41     private static final int MIN_SUCCESS_COUNT = 5;
42     private static final int MAX_SUCCESS_COUNT_OF_STUCK_LINK = 3;
43     private static final int MAX_STUCK_LINK_COUNT = 5;
44     private static final int MIN_NUM_TICKS_AT_STATE = 1000;
45     private static final int USER_DISCONNECT_PENALTY = 5;
46     private static final int MAX_BAD_RSSI_COUNT = 7;
47     private static final int BAD_RSSI_COUNT_PENALTY = 2;
48     private static final int MAX_LOW_RSSI_COUNT = 1;
49     private static final double MIN_TX_RATE_FOR_WORKING_LINK = 0.3;
50     private static final int MIN_SUSTAINED_LINK_STUCK_COUNT = 1;
51     private static final int LINK_STUCK_PENALTY = 2;
52     private static final int BAD_LINKSPEED_PENALTY = 4;
53     private static final int GOOD_LINKSPEED_BONUS = 4;
54 
55 
56     private String mReport;
57     private int mBadLinkspeedcount;
58 
WifiScoreReport(String report, int badLinkspeedcount)59     WifiScoreReport(String report, int badLinkspeedcount) {
60         mReport = report;
61         mBadLinkspeedcount = badLinkspeedcount;
62     }
63 
64     /**
65      *  Method returning the String representation of the score report.
66      *
67      *  @return String score report
68      */
getReport()69     public String getReport() {
70         return mReport;
71     }
72 
73     /**
74      *  Method returning the bad link speed count at the time of the current score report.
75      *
76      *  @return int bad linkspeed count
77      */
getBadLinkspeedcount()78     public int getBadLinkspeedcount() {
79         return mBadLinkspeedcount;
80     }
81 
82     /**
83      * Calculate wifi network score based on updated link layer stats and return a new
84      * WifiScoreReport object.
85      *
86      * If the score has changed from the previous value, update the WifiNetworkAgent.
87      * @param wifiInfo WifiInfo information about current network connection
88      * @param currentConfiguration WifiConfiguration current wifi config
89      * @param wifiConfigManager WifiConfigManager Object holding current config state
90      * @param networkAgent NetworkAgent to be notified of new score
91      * @param lastReport String most recent score report
92      * @param aggressiveHandover int current aggressiveHandover setting
93      * @return WifiScoreReport Wifi Score report
94      */
calculateScore(WifiInfo wifiInfo, WifiConfiguration currentConfiguration, WifiConfigManager wifiConfigManager, NetworkAgent networkAgent, WifiScoreReport lastReport, int aggressiveHandover)95     public static WifiScoreReport calculateScore(WifiInfo wifiInfo,
96                                                  WifiConfiguration currentConfiguration,
97                                                  WifiConfigManager wifiConfigManager,
98                                                  NetworkAgent networkAgent,
99                                                  WifiScoreReport lastReport,
100                                                  int aggressiveHandover) {
101         boolean debugLogging = false;
102         if (wifiConfigManager.mEnableVerboseLogging.get() > 0) {
103             debugLogging = true;
104         }
105 
106         StringBuilder sb = new StringBuilder();
107 
108         int score = STARTING_SCORE;
109         boolean isBadLinkspeed = (wifiInfo.is24GHz()
110                 && wifiInfo.getLinkSpeed() < wifiConfigManager.mBadLinkSpeed24)
111                 || (wifiInfo.is5GHz() && wifiInfo.getLinkSpeed()
112                 < wifiConfigManager.mBadLinkSpeed5);
113         boolean isGoodLinkspeed = (wifiInfo.is24GHz()
114                 && wifiInfo.getLinkSpeed() >= wifiConfigManager.mGoodLinkSpeed24)
115                 || (wifiInfo.is5GHz() && wifiInfo.getLinkSpeed()
116                 >= wifiConfigManager.mGoodLinkSpeed5);
117 
118         int badLinkspeedcount = 0;
119         if (lastReport != null) {
120             badLinkspeedcount = lastReport.getBadLinkspeedcount();
121         }
122 
123         if (isBadLinkspeed) {
124             if (badLinkspeedcount < MAX_BAD_LINKSPEED_COUNT) {
125                 badLinkspeedcount++;
126             }
127         } else {
128             if (badLinkspeedcount > 0) {
129                 badLinkspeedcount--;
130             }
131         }
132 
133         if (isBadLinkspeed) sb.append(" bl(").append(badLinkspeedcount).append(")");
134         if (isGoodLinkspeed) sb.append(" gl");
135 
136         /**
137          * We want to make sure that we use the 24GHz RSSI thresholds if
138          * there are 2.4GHz scan results
139          * otherwise we end up lowering the score based on 5GHz values
140          * which may cause a switch to LTE before roaming has a chance to try 2.4GHz
141          * We also might unblacklist the configuation based on 2.4GHz
142          * thresholds but joining 5GHz anyhow, and failing over to 2.4GHz because 5GHz is not good
143          */
144         boolean use24Thresholds = false;
145         boolean homeNetworkBoost = false;
146         ScanDetailCache scanDetailCache =
147                 wifiConfigManager.getScanDetailCache(currentConfiguration);
148         if (currentConfiguration != null && scanDetailCache != null) {
149             currentConfiguration.setVisibility(
150                     scanDetailCache.getVisibility(SCAN_CACHE_VISIBILITY_MS));
151             if (currentConfiguration.visibility != null) {
152                 if (currentConfiguration.visibility.rssi24 != WifiConfiguration.INVALID_RSSI
153                         && currentConfiguration.visibility.rssi24
154                         >= (currentConfiguration.visibility.rssi5 - SCAN_CACHE_COUNT_PENALTY)) {
155                     use24Thresholds = true;
156                 }
157             }
158             if (scanDetailCache.size() <= HOME_VISIBLE_NETWORK_MAX_COUNT
159                     && currentConfiguration.allowedKeyManagement.cardinality() == 1
160                     && currentConfiguration.allowedKeyManagement
161                             .get(WifiConfiguration.KeyMgmt.WPA_PSK)) {
162                 // A PSK network with less than 6 known BSSIDs
163                 // This is most likely a home network and thus we want to stick to wifi more
164                 homeNetworkBoost = true;
165             }
166         }
167         if (homeNetworkBoost) sb.append(" hn");
168         if (use24Thresholds) sb.append(" u24");
169 
170         int rssi = wifiInfo.getRssi() - AGGRESSIVE_HANDOVER_PENALTY * aggressiveHandover
171                 + (homeNetworkBoost ? WifiConfiguration.HOME_NETWORK_RSSI_BOOST : 0);
172         sb.append(String.format(" rssi=%d ag=%d", rssi, aggressiveHandover));
173 
174         boolean is24GHz = use24Thresholds || wifiInfo.is24GHz();
175 
176         boolean isBadRSSI = (is24GHz && rssi < wifiConfigManager.mThresholdMinimumRssi24.get())
177                 || (!is24GHz && rssi < wifiConfigManager.mThresholdMinimumRssi5.get());
178         boolean isLowRSSI = (is24GHz && rssi < wifiConfigManager.mThresholdQualifiedRssi24.get())
179                 || (!is24GHz
180                         && wifiInfo.getRssi() < wifiConfigManager.mThresholdMinimumRssi5.get());
181         boolean isHighRSSI = (is24GHz && rssi >= wifiConfigManager.mThresholdSaturatedRssi24.get())
182                 || (!is24GHz
183                         && wifiInfo.getRssi() >= wifiConfigManager.mThresholdSaturatedRssi5.get());
184 
185         if (isBadRSSI) sb.append(" br");
186         if (isLowRSSI) sb.append(" lr");
187         if (isHighRSSI) sb.append(" hr");
188 
189         int penalizedDueToUserTriggeredDisconnect = 0;        // Not a user triggered disconnect
190         if (currentConfiguration != null
191                 && (wifiInfo.txSuccessRate > MIN_SUCCESS_COUNT
192                         || wifiInfo.rxSuccessRate > MIN_SUCCESS_COUNT)) {
193             if (isBadRSSI) {
194                 currentConfiguration.numTicksAtBadRSSI++;
195                 if (currentConfiguration.numTicksAtBadRSSI > MIN_NUM_TICKS_AT_STATE) {
196                     // We remained associated for a compound amount of time while passing
197                     // traffic, hence loose the corresponding user triggered disabled stats
198                     if (currentConfiguration.numUserTriggeredWifiDisableBadRSSI > 0) {
199                         currentConfiguration.numUserTriggeredWifiDisableBadRSSI--;
200                     }
201                     if (currentConfiguration.numUserTriggeredWifiDisableLowRSSI > 0) {
202                         currentConfiguration.numUserTriggeredWifiDisableLowRSSI--;
203                     }
204                     if (currentConfiguration.numUserTriggeredWifiDisableNotHighRSSI > 0) {
205                         currentConfiguration.numUserTriggeredWifiDisableNotHighRSSI--;
206                     }
207                     currentConfiguration.numTicksAtBadRSSI = 0;
208                 }
209                 if (wifiConfigManager.mEnableWifiCellularHandoverUserTriggeredAdjustment
210                         && (currentConfiguration.numUserTriggeredWifiDisableBadRSSI > 0
211                                 || currentConfiguration.numUserTriggeredWifiDisableLowRSSI > 0
212                                 || currentConfiguration
213                                         .numUserTriggeredWifiDisableNotHighRSSI > 0)) {
214                     score = score - USER_DISCONNECT_PENALTY;
215                     penalizedDueToUserTriggeredDisconnect = 1;
216                     sb.append(" p1");
217                 }
218             } else if (isLowRSSI) {
219                 currentConfiguration.numTicksAtLowRSSI++;
220                 if (currentConfiguration.numTicksAtLowRSSI > MIN_NUM_TICKS_AT_STATE) {
221                     // We remained associated for a compound amount of time while passing
222                     // traffic, hence loose the corresponding user triggered disabled stats
223                     if (currentConfiguration.numUserTriggeredWifiDisableLowRSSI > 0) {
224                         currentConfiguration.numUserTriggeredWifiDisableLowRSSI--;
225                     }
226                     if (currentConfiguration.numUserTriggeredWifiDisableNotHighRSSI > 0) {
227                         currentConfiguration.numUserTriggeredWifiDisableNotHighRSSI--;
228                     }
229                     currentConfiguration.numTicksAtLowRSSI = 0;
230                 }
231                 if (wifiConfigManager.mEnableWifiCellularHandoverUserTriggeredAdjustment
232                         && (currentConfiguration.numUserTriggeredWifiDisableLowRSSI > 0
233                                 || currentConfiguration
234                                         .numUserTriggeredWifiDisableNotHighRSSI > 0)) {
235                     score = score - USER_DISCONNECT_PENALTY;
236                     penalizedDueToUserTriggeredDisconnect = 2;
237                     sb.append(" p2");
238                 }
239             } else if (!isHighRSSI) {
240                 currentConfiguration.numTicksAtNotHighRSSI++;
241                 if (currentConfiguration.numTicksAtNotHighRSSI > MIN_NUM_TICKS_AT_STATE) {
242                     // We remained associated for a compound amount of time while passing
243                     // traffic, hence loose the corresponding user triggered disabled stats
244                     if (currentConfiguration.numUserTriggeredWifiDisableNotHighRSSI > 0) {
245                         currentConfiguration.numUserTriggeredWifiDisableNotHighRSSI--;
246                     }
247                     currentConfiguration.numTicksAtNotHighRSSI = 0;
248                 }
249                 if (wifiConfigManager.mEnableWifiCellularHandoverUserTriggeredAdjustment
250                         && currentConfiguration.numUserTriggeredWifiDisableNotHighRSSI > 0) {
251                     score = score - USER_DISCONNECT_PENALTY;
252                     penalizedDueToUserTriggeredDisconnect = 3;
253                     sb.append(" p3");
254                 }
255             }
256             sb.append(String.format(" ticks %d,%d,%d", currentConfiguration.numTicksAtBadRSSI,
257                     currentConfiguration.numTicksAtLowRSSI,
258                     currentConfiguration.numTicksAtNotHighRSSI));
259         }
260 
261         if (debugLogging) {
262             String rssiStatus = "";
263             if (isBadRSSI) {
264                 rssiStatus += " badRSSI ";
265             } else if (isHighRSSI) {
266                 rssiStatus += " highRSSI ";
267             } else if (isLowRSSI) {
268                 rssiStatus += " lowRSSI ";
269             }
270             if (isBadLinkspeed) rssiStatus += " lowSpeed ";
271             Log.d(TAG, "calculateWifiScore freq=" + Integer.toString(wifiInfo.getFrequency())
272                     + " speed=" + Integer.toString(wifiInfo.getLinkSpeed())
273                     + " score=" + Integer.toString(wifiInfo.score)
274                     + rssiStatus
275                     + " -> txbadrate=" + String.format("%.2f", wifiInfo.txBadRate)
276                     + " txgoodrate=" + String.format("%.2f", wifiInfo.txSuccessRate)
277                     + " txretriesrate=" + String.format("%.2f", wifiInfo.txRetriesRate)
278                     + " rxrate=" + String.format("%.2f", wifiInfo.rxSuccessRate)
279                     + " userTriggerdPenalty" + penalizedDueToUserTriggeredDisconnect);
280         }
281 
282         if ((wifiInfo.txBadRate >= 1) && (wifiInfo.txSuccessRate < MAX_SUCCESS_COUNT_OF_STUCK_LINK)
283                 && (isBadRSSI || isLowRSSI)) {
284             // Link is stuck
285             if (wifiInfo.linkStuckCount < MAX_STUCK_LINK_COUNT) {
286                 wifiInfo.linkStuckCount += 1;
287             }
288             sb.append(String.format(" ls+=%d", wifiInfo.linkStuckCount));
289             if (debugLogging) {
290                 Log.d(TAG, " bad link -> stuck count ="
291                         + Integer.toString(wifiInfo.linkStuckCount));
292             }
293         } else if (wifiInfo.txBadRate < MIN_TX_RATE_FOR_WORKING_LINK) {
294             if (wifiInfo.linkStuckCount > 0) {
295                 wifiInfo.linkStuckCount -= 1;
296             }
297             sb.append(String.format(" ls-=%d", wifiInfo.linkStuckCount));
298             if (debugLogging) {
299                 Log.d(TAG, " good link -> stuck count ="
300                         + Integer.toString(wifiInfo.linkStuckCount));
301             }
302         }
303 
304         sb.append(String.format(" [%d", score));
305 
306         if (wifiInfo.linkStuckCount > MIN_SUSTAINED_LINK_STUCK_COUNT) {
307             // Once link gets stuck for more than 3 seconds, start reducing the score
308             score = score - LINK_STUCK_PENALTY * (wifiInfo.linkStuckCount - 1);
309         }
310         sb.append(String.format(",%d", score));
311 
312         if (isBadLinkspeed) {
313             score -= BAD_LINKSPEED_PENALTY;
314             if (debugLogging) {
315                 Log.d(TAG, " isBadLinkspeed   ---> count=" + badLinkspeedcount
316                         + " score=" + Integer.toString(score));
317             }
318         } else if ((isGoodLinkspeed) && (wifiInfo.txSuccessRate > 5)) {
319             score += GOOD_LINKSPEED_BONUS; // So as bad rssi alone dont kill us
320         }
321         sb.append(String.format(",%d", score));
322 
323         if (isBadRSSI) {
324             if (wifiInfo.badRssiCount < MAX_BAD_RSSI_COUNT) {
325                 wifiInfo.badRssiCount += 1;
326             }
327         } else if (isLowRSSI) {
328             wifiInfo.lowRssiCount = MAX_LOW_RSSI_COUNT; // Dont increment the lowRssi count above 1
329             if (wifiInfo.badRssiCount > 0) {
330                 // Decrement bad Rssi count
331                 wifiInfo.badRssiCount -= 1;
332             }
333         } else {
334             wifiInfo.badRssiCount = 0;
335             wifiInfo.lowRssiCount = 0;
336         }
337 
338         score -= wifiInfo.badRssiCount * BAD_RSSI_COUNT_PENALTY + wifiInfo.lowRssiCount;
339         sb.append(String.format(",%d", score));
340 
341         if (debugLogging) {
342             Log.d(TAG, " badRSSI count" + Integer.toString(wifiInfo.badRssiCount)
343                     + " lowRSSI count" + Integer.toString(wifiInfo.lowRssiCount)
344                     + " --> score " + Integer.toString(score));
345         }
346 
347         if (isHighRSSI) {
348             score += 5;
349             if (debugLogging) Log.d(TAG, " isHighRSSI       ---> score=" + Integer.toString(score));
350         }
351         sb.append(String.format(",%d]", score));
352 
353         sb.append(String.format(" brc=%d lrc=%d", wifiInfo.badRssiCount, wifiInfo.lowRssiCount));
354 
355         //sanitize boundaries
356         if (score > NetworkAgent.WIFI_BASE_SCORE) {
357             score = NetworkAgent.WIFI_BASE_SCORE;
358         }
359         if (score < 0) {
360             score = 0;
361         }
362 
363         //report score
364         if (score != wifiInfo.score) {
365             if (debugLogging) {
366                 Log.d(TAG, "calculateWifiScore() report new score " + Integer.toString(score));
367             }
368             wifiInfo.score = score;
369             if (networkAgent != null) {
370                 networkAgent.sendNetworkScore(score);
371             }
372         }
373         return new WifiScoreReport(sb.toString(), badLinkspeedcount);
374     }
375 }
376