1 /*
2  * Copyright 2018 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 static com.android.server.wifi.util.InformationElementUtil.BssLoad.CHANNEL_UTILIZATION_SCALE;
20 
21 import android.content.Context;
22 import android.net.wifi.ScanResult;
23 import android.net.wifi.WifiInfo;
24 import android.net.wifi.WifiManager.DeviceMobilityState;
25 import android.os.Handler;
26 import android.os.HandlerExecutor;
27 import android.telephony.PhoneStateListener;
28 import android.telephony.TelephonyManager;
29 import android.util.Log;
30 
31 import com.android.server.wifi.proto.WifiStatsLog;
32 import com.android.server.wifi.proto.nano.WifiMetricsProto.WifiIsUnusableEvent;
33 import com.android.server.wifi.scanner.KnownBandsChannelHelper;
34 import com.android.server.wifi.util.InformationElementUtil.BssLoad;
35 import com.android.wifi.resources.R;
36 
37 /**
38  * Looks for Wifi data stalls
39  */
40 public class WifiDataStall {
41     private static final String TAG = "WifiDataStall";
42     private boolean mVerboseLoggingEnabled = false;
43     public static final int INVALID_THROUGHPUT = -1;
44     // Maximum time gap between two WifiLinkLayerStats to trigger a data stall
45     public static final int MAX_MS_DELTA_FOR_DATA_STALL = 60 * 1000; // 1 minute
46     // Maximum time that a data stall start time stays valid.
47     public static final long VALIDITY_PERIOD_OF_DATA_STALL_START_MS = 30 * 1000; // 0.5 minutes
48     // Default Tx packet error rate when there is no Tx attempt
49     public static final int DEFAULT_TX_PACKET_ERROR_RATE = 5;
50     // Default CCA level when CCA stats are not available
51     public static final int DEFAULT_CCA_LEVEL_2G = CHANNEL_UTILIZATION_SCALE * 16 / 100;
52     public static final int DEFAULT_CCA_LEVEL_ABOVE_2G = CHANNEL_UTILIZATION_SCALE * 6 / 100;
53     // Minimum time interval in ms between two link layer stats cache updates
54     private static final int LLSTATS_CACHE_UPDATE_INTERVAL_MIN_MS = 30_000;
55     // Maximum time margin between two link layer stats for connection duration update
56     public static final int MAX_TIME_MARGIN_LAST_TWO_POLLS_MS = 200;
57 
58     private final FrameworkFacade mFacade;
59     private final DeviceConfigFacade mDeviceConfigFacade;
60     private final WifiMetrics mWifiMetrics;
61     private final Context mContext;
62     private final WifiChannelUtilization mWifiChannelUtilization;
63     private TelephonyManager mTelephonyManager;
64     private final ThroughputPredictor mThroughputPredictor;
65     private WifiNative.ConnectionCapabilities mConnectionCapabilities;
66 
67     private int mLastFrequency = -1;
68     private String mLastBssid;
69     private long mDataStallStartTimeMs = -1;
70     private Clock mClock;
71     private boolean mDataStallTx = false;
72     private boolean mDataStallRx = false;
73     private long mLastTxBytes;
74     private long mLastRxBytes;
75     private boolean mIsThroughputSufficient = true;
76     private boolean mIsCellularDataAvailable = false;
77     private final PhoneStateListener mPhoneStateListener;
78     private boolean mPhoneStateListenerEnabled = false;
79     private int mTxTputKbps = INVALID_THROUGHPUT;
80     private int mRxTputKbps = INVALID_THROUGHPUT;
81 
WifiDataStall(FrameworkFacade facade, WifiMetrics wifiMetrics, Context context, DeviceConfigFacade deviceConfigFacade, WifiChannelUtilization wifiChannelUtilization, Clock clock, Handler handler, ThroughputPredictor throughputPredictor)82     public WifiDataStall(FrameworkFacade facade, WifiMetrics wifiMetrics, Context context,
83             DeviceConfigFacade deviceConfigFacade, WifiChannelUtilization wifiChannelUtilization,
84             Clock clock, Handler handler, ThroughputPredictor throughputPredictor) {
85         mFacade = facade;
86         mDeviceConfigFacade = deviceConfigFacade;
87         mWifiMetrics = wifiMetrics;
88         mContext = context;
89         mClock = clock;
90         mWifiChannelUtilization = wifiChannelUtilization;
91         mWifiChannelUtilization.setCacheUpdateIntervalMs(LLSTATS_CACHE_UPDATE_INTERVAL_MIN_MS);
92         mThroughputPredictor = throughputPredictor;
93         mPhoneStateListener = new PhoneStateListener(new HandlerExecutor(handler)) {
94             @Override
95             public void onDataConnectionStateChanged(int state, int networkType) {
96                 if (state == TelephonyManager.DATA_CONNECTED) {
97                     mIsCellularDataAvailable = true;
98                 } else if (state == TelephonyManager.DATA_DISCONNECTED) {
99                     mIsCellularDataAvailable = false;
100                 } else {
101                     Log.e(TAG, "onDataConnectionStateChanged unexpected State: " + state);
102                     return;
103                 }
104                 logd("Cellular Data: " + mIsCellularDataAvailable);
105             }
106         };
107     }
108 
109     /**
110      * initialization after wifi is enabled
111      */
init()112     public void init() {
113         mWifiChannelUtilization.init(null);
114         reset();
115     }
116 
117     /**
118      * Reset internal variables
119      */
reset()120     public void reset() {
121         mLastTxBytes = 0;
122         mLastRxBytes = 0;
123         mLastFrequency = -1;
124         mLastBssid = null;
125         mDataStallStartTimeMs = -1;
126         mDataStallTx = false;
127         mDataStallRx = false;
128         mIsThroughputSufficient = true;
129         mTxTputKbps = INVALID_THROUGHPUT;
130         mRxTputKbps = INVALID_THROUGHPUT;
131     }
132 
133     /**
134      * Set ConnectionCapabilities after each association and roaming
135      */
setConnectionCapabilities(WifiNative.ConnectionCapabilities capabilities)136     public void setConnectionCapabilities(WifiNative.ConnectionCapabilities capabilities) {
137         mConnectionCapabilities = capabilities;
138     }
139     /**
140      * Enable phone state listener
141      */
enablePhoneStateListener()142     public void enablePhoneStateListener() {
143         if (mTelephonyManager == null) {
144             mTelephonyManager = (TelephonyManager) mContext
145                     .getSystemService(Context.TELEPHONY_SERVICE);
146         }
147         if (mTelephonyManager != null && !mPhoneStateListenerEnabled) {
148             mPhoneStateListenerEnabled = true;
149             mTelephonyManager.listen(mPhoneStateListener,
150                     PhoneStateListener.LISTEN_DATA_CONNECTION_STATE);
151         }
152     }
153 
154     /**
155      * Disable phone state listener
156      */
disablePhoneStateListener()157     public void disablePhoneStateListener() {
158         if (mTelephonyManager != null && mPhoneStateListenerEnabled) {
159             mPhoneStateListenerEnabled = false;
160             mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
161         }
162     }
163 
164     /**
165      * Enable/Disable verbose logging.
166      * @param verbose true to enable and false to disable.
167      */
enableVerboseLogging(boolean verbose)168     public void enableVerboseLogging(boolean verbose) {
169         mVerboseLoggingEnabled = verbose;
170         mWifiChannelUtilization.enableVerboseLogging(verbose);
171     }
172 
173     /**
174      * Update device mobility state
175      * @param newState the new device mobility state
176      */
setDeviceMobilityState(@eviceMobilityState int newState)177     public void setDeviceMobilityState(@DeviceMobilityState int newState) {
178         mWifiChannelUtilization.setDeviceMobilityState(newState);
179     }
180 
181     /**
182      * Check if current link layer throughput is sufficient.
183      * This should be called after checkDataStallAndThroughputSufficiency().
184      * @return true if it is sufficient or false if it is insufficient
185      */
isThroughputSufficient()186     public boolean isThroughputSufficient() {
187         return mIsThroughputSufficient;
188     }
189 
190     /**
191      * Check if cellular data is available
192      * @return true if it is available and false otherwise
193      */
isCellularDataAvailable()194     public boolean isCellularDataAvailable() {
195         return mIsCellularDataAvailable;
196     }
197 
198     /**
199      * Get the latest Tx throughput based on Tx link speed, PER and channel utilization
200      * @return the latest estimated Tx throughput in Kbps if it is available
201      *  or INVALID_THROUGHPUT if it is not available
202      */
getTxThroughputKbps()203     public int getTxThroughputKbps() {
204         logd("tx tput in kbps: " + mTxTputKbps);
205         return mTxTputKbps;
206     }
207 
208     /**
209      * Get the latest Rx throughput based on Rx link speed and channel utilization
210      * @return the latest estimated Rx throughput in Kbps if it is available
211      *  or INVALID_THROUGHPUT if it is not available
212      */
getRxThroughputKbps()213     public int getRxThroughputKbps() {
214         logd("rx tput in kbps: " + mRxTputKbps);
215         return mRxTputKbps;
216     }
217 
218     /**
219      * Update data stall detection, check throughput sufficiency and report wifi health stat
220      * with the latest link layer stats
221      * @param oldStats second most recent WifiLinkLayerStats
222      * @param newStats most recent WifiLinkLayerStats
223      * @param wifiInfo WifiInfo for current connection
224      * @return trigger type of WifiIsUnusableEvent
225      */
checkDataStallAndThroughputSufficiency(WifiLinkLayerStats oldStats, WifiLinkLayerStats newStats, WifiInfo wifiInfo)226     public int checkDataStallAndThroughputSufficiency(WifiLinkLayerStats oldStats,
227             WifiLinkLayerStats newStats, WifiInfo wifiInfo) {
228         int currFrequency = wifiInfo.getFrequency();
229         mWifiChannelUtilization.refreshChannelStatsAndChannelUtilization(newStats, currFrequency);
230         int ccaLevel = mWifiChannelUtilization.getUtilizationRatio(currFrequency);
231         mWifiMetrics.incrementChannelUtilizationCount(ccaLevel, currFrequency);
232 
233         if (oldStats == null || newStats == null) {
234             // First poll after new association
235             // Update throughput with prediction
236             if (wifiInfo.getRssi() != WifiInfo.INVALID_RSSI && mConnectionCapabilities != null) {
237                 mTxTputKbps = mThroughputPredictor.predictTxThroughput(mConnectionCapabilities,
238                         wifiInfo.getRssi(), currFrequency, ccaLevel) * 1000;
239                 mRxTputKbps = mThroughputPredictor.predictRxThroughput(mConnectionCapabilities,
240                         wifiInfo.getRssi(), currFrequency, ccaLevel) * 1000;
241             }
242             mIsThroughputSufficient = true;
243             mWifiMetrics.resetWifiIsUnusableLinkLayerStats();
244             mWifiMetrics.incrementThroughputKbpsCount(mTxTputKbps, mRxTputKbps, currFrequency);
245             return WifiIsUnusableEvent.TYPE_UNKNOWN;
246         }
247 
248         long txSuccessDelta = (newStats.txmpdu_be + newStats.txmpdu_bk
249                 + newStats.txmpdu_vi + newStats.txmpdu_vo)
250                 - (oldStats.txmpdu_be + oldStats.txmpdu_bk
251                 + oldStats.txmpdu_vi + oldStats.txmpdu_vo);
252         long txRetriesDelta = (newStats.retries_be + newStats.retries_bk
253                 + newStats.retries_vi + newStats.retries_vo)
254                 - (oldStats.retries_be + oldStats.retries_bk
255                 + oldStats.retries_vi + oldStats.retries_vo);
256         long txBadDelta = (newStats.lostmpdu_be + newStats.lostmpdu_bk
257                 + newStats.lostmpdu_vi + newStats.lostmpdu_vo)
258                 - (oldStats.lostmpdu_be + oldStats.lostmpdu_bk
259                 + oldStats.lostmpdu_vi + oldStats.lostmpdu_vo);
260         long rxSuccessDelta = (newStats.rxmpdu_be + newStats.rxmpdu_bk
261                 + newStats.rxmpdu_vi + newStats.rxmpdu_vo)
262                 - (oldStats.rxmpdu_be + oldStats.rxmpdu_bk
263                 + oldStats.rxmpdu_vi + oldStats.rxmpdu_vo);
264         int timeDeltaLastTwoPollsMs = (int) (newStats.timeStampInMs - oldStats.timeStampInMs);
265 
266         long totalTxDelta = txSuccessDelta + txRetriesDelta;
267         boolean isTxTrafficHigh = (totalTxDelta * 1000)
268                 > (mDeviceConfigFacade.getTxPktPerSecondThr() * timeDeltaLastTwoPollsMs);
269         boolean isRxTrafficHigh = (rxSuccessDelta * 1000)
270                 > (mDeviceConfigFacade.getRxPktPerSecondThr() * timeDeltaLastTwoPollsMs);
271         if (timeDeltaLastTwoPollsMs < 0
272                 || txSuccessDelta < 0
273                 || txRetriesDelta < 0
274                 || txBadDelta < 0
275                 || rxSuccessDelta < 0) {
276             mIsThroughputSufficient = true;
277             // There was a reset in WifiLinkLayerStats
278             mWifiMetrics.resetWifiIsUnusableLinkLayerStats();
279             return WifiIsUnusableEvent.TYPE_UNKNOWN;
280         }
281 
282         mWifiMetrics.updateWifiIsUnusableLinkLayerStats(txSuccessDelta, txRetriesDelta,
283                 txBadDelta, rxSuccessDelta, timeDeltaLastTwoPollsMs);
284 
285         int txLinkSpeedMbps = wifiInfo.getLinkSpeed();
286         int rxLinkSpeedMbps = wifiInfo.getRxLinkSpeedMbps();
287         boolean isSameBssidAndFreq = mLastBssid == null || mLastFrequency == -1
288                 || (mLastBssid.equals(wifiInfo.getBSSID())
289                 && mLastFrequency == currFrequency);
290         mLastFrequency = currFrequency;
291         mLastBssid = wifiInfo.getBSSID();
292 
293         if (ccaLevel == BssLoad.INVALID) {
294             ccaLevel = wifiInfo.is24GHz() ? DEFAULT_CCA_LEVEL_2G : DEFAULT_CCA_LEVEL_ABOVE_2G;
295             logd(" use default cca Level");
296         }
297         logd(" ccaLevel = " + ccaLevel);
298 
299         int txPer = updateTxPer(txSuccessDelta, txRetriesDelta, isSameBssidAndFreq,
300                 isTxTrafficHigh);
301 
302         boolean isTxTputLow = false;
303         boolean isRxTputLow = false;
304 
305         if (txLinkSpeedMbps > 0) {
306             // Exclude update with low rate management frames
307             if (isTxTrafficHigh
308                     || txLinkSpeedMbps > mDeviceConfigFacade.getTxLinkSpeedLowThresholdMbps()) {
309                 mTxTputKbps = (int) ((long) txLinkSpeedMbps * 1000 * (100 - txPer) / 100
310                         * (CHANNEL_UTILIZATION_SCALE  - ccaLevel) / CHANNEL_UTILIZATION_SCALE);
311             }
312             isTxTputLow =  mTxTputKbps < mDeviceConfigFacade.getDataStallTxTputThrKbps();
313         } else {
314             mTxTputKbps = INVALID_THROUGHPUT;
315         }
316 
317         if (rxLinkSpeedMbps > 0) {
318             // Exclude update with low rate management frames
319             if (isRxTrafficHigh
320                     || rxLinkSpeedMbps > mDeviceConfigFacade.getRxLinkSpeedLowThresholdMbps()) {
321                 mRxTputKbps = (int) ((long) rxLinkSpeedMbps * 1000
322                         * (CHANNEL_UTILIZATION_SCALE  - ccaLevel) / CHANNEL_UTILIZATION_SCALE);
323             }
324             isRxTputLow = mRxTputKbps < mDeviceConfigFacade.getDataStallRxTputThrKbps();
325         } else {
326             mRxTputKbps = INVALID_THROUGHPUT;
327         }
328         mWifiMetrics.incrementThroughputKbpsCount(mTxTputKbps, mRxTputKbps, currFrequency);
329 
330         mIsThroughputSufficient = isThroughputSufficientInternal(mTxTputKbps, mRxTputKbps,
331                 isTxTrafficHigh, isRxTrafficHigh, timeDeltaLastTwoPollsMs);
332 
333         int maxTimeDeltaMs = mContext.getResources().getInteger(
334                 R.integer.config_wifiPollRssiIntervalMilliseconds)
335                 + MAX_TIME_MARGIN_LAST_TWO_POLLS_MS;
336         if (timeDeltaLastTwoPollsMs > 0 && timeDeltaLastTwoPollsMs <= maxTimeDeltaMs) {
337             mWifiMetrics.incrementConnectionDuration(timeDeltaLastTwoPollsMs,
338                     mIsThroughputSufficient, mIsCellularDataAvailable);
339             reportWifiHealthStat(currFrequency, timeDeltaLastTwoPollsMs, mIsThroughputSufficient,
340                     mIsCellularDataAvailable);
341         }
342 
343         boolean possibleDataStallTx = isTxTputLow
344                 || ccaLevel >= mDeviceConfigFacade.getDataStallCcaLevelThr()
345                 || txPer >= mDeviceConfigFacade.getDataStallTxPerThr();
346         boolean possibleDataStallRx = isRxTputLow
347                 || ccaLevel >= mDeviceConfigFacade.getDataStallCcaLevelThr();
348 
349         boolean dataStallTx = isTxTrafficHigh ? possibleDataStallTx : mDataStallTx;
350         boolean dataStallRx = isRxTrafficHigh ? possibleDataStallRx : mDataStallRx;
351 
352         return detectConsecutiveTwoDataStalls(timeDeltaLastTwoPollsMs, dataStallTx, dataStallRx);
353     }
354 
355     // Data stall event is triggered if there are consecutive Tx and/or Rx data stalls
356     // 1st data stall should be preceded by no data stall
357     // Reset mDataStallStartTimeMs to -1 if currently there is no Tx or Rx data stall
detectConsecutiveTwoDataStalls(int timeDeltaLastTwoPollsMs, boolean dataStallTx, boolean dataStallRx)358     private int detectConsecutiveTwoDataStalls(int timeDeltaLastTwoPollsMs,
359             boolean dataStallTx, boolean dataStallRx) {
360         if (timeDeltaLastTwoPollsMs >= MAX_MS_DELTA_FOR_DATA_STALL) {
361             return WifiIsUnusableEvent.TYPE_UNKNOWN;
362         }
363 
364         if (dataStallTx || dataStallRx) {
365             mDataStallTx = mDataStallTx || dataStallTx;
366             mDataStallRx = mDataStallRx || dataStallRx;
367             if (mDataStallStartTimeMs == -1) {
368                 mDataStallStartTimeMs = mClock.getElapsedSinceBootMillis();
369                 if (mDeviceConfigFacade.getDataStallDurationMs() == 0) {
370                     mDataStallStartTimeMs = -1;
371                     int result = calculateUsabilityEventType(mDataStallTx, mDataStallRx);
372                     mDataStallRx = false;
373                     mDataStallTx = false;
374                     return result;
375                 }
376             } else {
377                 long elapsedTime = mClock.getElapsedSinceBootMillis() - mDataStallStartTimeMs;
378                 if (elapsedTime >= mDeviceConfigFacade.getDataStallDurationMs()) {
379                     mDataStallStartTimeMs = -1;
380                     if (elapsedTime <= VALIDITY_PERIOD_OF_DATA_STALL_START_MS) {
381                         int result = calculateUsabilityEventType(mDataStallTx, mDataStallRx);
382                         mDataStallRx = false;
383                         mDataStallTx = false;
384                         return result;
385                     } else {
386                         mDataStallTx = false;
387                         mDataStallRx = false;
388                     }
389                 } else {
390                     // No need to do anything.
391                 }
392             }
393         } else {
394             mDataStallStartTimeMs = -1;
395             mDataStallTx = false;
396             mDataStallRx = false;
397         }
398         return WifiIsUnusableEvent.TYPE_UNKNOWN;
399     }
400 
updateTxPer(long txSuccessDelta, long txRetriesDelta, boolean isSameBssidAndFreq, boolean isTxTrafficHigh)401     private int updateTxPer(long txSuccessDelta, long txRetriesDelta, boolean isSameBssidAndFreq,
402             boolean isTxTrafficHigh) {
403         if (!isSameBssidAndFreq) {
404             return DEFAULT_TX_PACKET_ERROR_RATE;
405         }
406         long txAttempts = txSuccessDelta + txRetriesDelta;
407         if (txAttempts <= 0 || !isTxTrafficHigh) {
408             return DEFAULT_TX_PACKET_ERROR_RATE;
409         }
410         return (int) (txRetriesDelta * 100 / txAttempts);
411     }
calculateUsabilityEventType(boolean dataStallTx, boolean dataStallRx)412     private int calculateUsabilityEventType(boolean dataStallTx, boolean dataStallRx) {
413         int result = WifiIsUnusableEvent.TYPE_UNKNOWN;
414         if (dataStallTx && dataStallRx) {
415             result = WifiIsUnusableEvent.TYPE_DATA_STALL_BOTH;
416         } else if (dataStallTx) {
417             result = WifiIsUnusableEvent.TYPE_DATA_STALL_BAD_TX;
418         } else if (dataStallRx) {
419             result = WifiIsUnusableEvent.TYPE_DATA_STALL_TX_WITHOUT_RX;
420         }
421         mWifiMetrics.logWifiIsUnusableEvent(result);
422         return result;
423     }
424 
isThroughputSufficientInternal(int l2TxTputKbps, int l2RxTputKbps, boolean isTxTrafficHigh, boolean isRxTrafficHigh, int timeDeltaLastTwoPollsMs)425     private boolean isThroughputSufficientInternal(int l2TxTputKbps, int l2RxTputKbps,
426             boolean isTxTrafficHigh, boolean isRxTrafficHigh, int timeDeltaLastTwoPollsMs) {
427         long txBytes = mFacade.getTotalTxBytes() - mFacade.getMobileTxBytes();
428         long rxBytes = mFacade.getTotalRxBytes() - mFacade.getMobileRxBytes();
429         if (timeDeltaLastTwoPollsMs > MAX_MS_DELTA_FOR_DATA_STALL
430                 || mLastTxBytes == 0 || mLastRxBytes == 0) {
431             mLastTxBytes = txBytes;
432             mLastRxBytes = rxBytes;
433             return true;
434         }
435 
436         int l3TxTputKbps = (int) ((txBytes - mLastTxBytes) * 8 / timeDeltaLastTwoPollsMs);
437         int l3RxTputKbps = (int) ((rxBytes - mLastRxBytes) * 8 / timeDeltaLastTwoPollsMs);
438 
439         mLastTxBytes = txBytes;
440         mLastRxBytes = rxBytes;
441 
442         boolean isTxTputSufficient = isL2ThroughputSufficient(l2TxTputKbps, l3TxTputKbps, false);
443         boolean isRxTputSufficient = isL2ThroughputSufficient(l2RxTputKbps, l3RxTputKbps, true);
444         isTxTputSufficient = detectAndOverrideFalseInSufficient(
445                 isTxTputSufficient, isTxTrafficHigh, mIsThroughputSufficient);
446         isRxTputSufficient = detectAndOverrideFalseInSufficient(
447                 isRxTputSufficient, isRxTrafficHigh, mIsThroughputSufficient);
448 
449         boolean isThroughputSufficient = isTxTputSufficient && isRxTputSufficient;
450 
451         StringBuilder sb = new StringBuilder();
452         logd(sb.append("L2 txTputKbps: ").append(l2TxTputKbps)
453                 .append(", rxTputKbps: ").append(l2RxTputKbps)
454                 .append(", L3 txTputKbps: ").append(l3TxTputKbps)
455                 .append(", rxTputKbps: ").append(l3RxTputKbps)
456                 .append(", TxTrafficHigh: ").append(isTxTrafficHigh)
457                 .append(", RxTrafficHigh: ").append(isRxTrafficHigh)
458                 .append(", Throughput Sufficient: ").append(isThroughputSufficient)
459                 .toString());
460         return isThroughputSufficient;
461     }
462 
463     /**
464      * L2 tput is sufficient when one of the following conditions is met
465      * 1) L3 tput is low and L2 tput is above its low threshold
466      * 2) L3 tput is not low and L2 tput over L3 tput ratio is above sufficientRatioThr
467      * 3) L3 tput is not low and L2 tput is above its high threshold
468      * 4) L2 tput is invalid
469      */
isL2ThroughputSufficient(int l2TputKbps, int l3TputKbps, boolean isForRxTput)470     private boolean isL2ThroughputSufficient(int l2TputKbps, int l3TputKbps, boolean isForRxTput) {
471         if (l2TputKbps == INVALID_THROUGHPUT) return true;
472         int tputSufficientLowThrKbps = mDeviceConfigFacade.getTxTputSufficientLowThrKbps();
473         int tputSufficientHighThrKbps = mDeviceConfigFacade.getTxTputSufficientHighThrKbps();
474         if (isForRxTput) {
475             tputSufficientLowThrKbps = mDeviceConfigFacade.getRxTputSufficientLowThrKbps();
476             tputSufficientHighThrKbps = mDeviceConfigFacade.getRxTputSufficientHighThrKbps();
477         }
478         boolean isL3TputLow = (l3TputKbps * mDeviceConfigFacade.getTputSufficientRatioThrDen())
479                 < (tputSufficientLowThrKbps * mDeviceConfigFacade.getTputSufficientRatioThrNum());
480         boolean isL2TputAboveLowThr = l2TputKbps >= tputSufficientLowThrKbps;
481         if (isL3TputLow) return isL2TputAboveLowThr;
482 
483         boolean isL2TputAboveHighThr = l2TputKbps >= tputSufficientHighThrKbps;
484         boolean isL2L3TputRatioAboveThr =
485                 (l2TputKbps * mDeviceConfigFacade.getTputSufficientRatioThrDen())
486                 >= (l3TputKbps * mDeviceConfigFacade.getTputSufficientRatioThrNum());
487         return isL2TputAboveHighThr || isL2L3TputRatioAboveThr;
488     }
489 
detectAndOverrideFalseInSufficient(boolean isTputSufficient, boolean isTrafficHigh, boolean lastIsTputSufficient)490     private boolean detectAndOverrideFalseInSufficient(boolean isTputSufficient,
491             boolean isTrafficHigh, boolean lastIsTputSufficient) {
492         boolean possibleFalseInsufficient = (!isTrafficHigh && !isTputSufficient);
493         return  possibleFalseInsufficient ? lastIsTputSufficient : isTputSufficient;
494     }
495 
496     /**
497      * Report the latest Wifi connection health to statsd
498      */
reportWifiHealthStat(int frequency, int timeDeltaLastTwoPollsMs, boolean isThroughputSufficient, boolean isCellularDataAvailable)499     private void reportWifiHealthStat(int frequency, int timeDeltaLastTwoPollsMs,
500             boolean isThroughputSufficient,
501             boolean isCellularDataAvailable) {
502         int band = getBand(frequency);
503         WifiStatsLog.write(WifiStatsLog.WIFI_HEALTH_STAT_REPORTED, timeDeltaLastTwoPollsMs,
504                 isThroughputSufficient,  isCellularDataAvailable, band);
505     }
506 
getBand(int frequency)507     private int getBand(int frequency) {
508         int band;
509         if (ScanResult.is24GHz(frequency)) {
510             band = WifiStatsLog.WIFI_HEALTH_STAT_REPORTED__BAND__BAND_2G;
511         } else if (ScanResult.is5GHz(frequency)) {
512             if (frequency <= KnownBandsChannelHelper.BAND_5_GHZ_LOW_END_FREQ) {
513                 band = WifiStatsLog.WIFI_HEALTH_STAT_REPORTED__BAND__BAND_5G_LOW;
514             } else if (frequency <= KnownBandsChannelHelper.BAND_5_GHZ_MID_END_FREQ) {
515                 band = WifiStatsLog.WIFI_HEALTH_STAT_REPORTED__BAND__BAND_5G_MIDDLE;
516             } else {
517                 band = WifiStatsLog.WIFI_HEALTH_STAT_REPORTED__BAND__BAND_5G_HIGH;
518             }
519         } else if (ScanResult.is6GHz(frequency)) {
520             if (frequency <= KnownBandsChannelHelper.BAND_6_GHZ_LOW_END_FREQ) {
521                 band = WifiStatsLog.WIFI_HEALTH_STAT_REPORTED__BAND__BAND_6G_LOW;
522             } else if (frequency <= KnownBandsChannelHelper.BAND_6_GHZ_MID_END_FREQ) {
523                 band = WifiStatsLog.WIFI_HEALTH_STAT_REPORTED__BAND__BAND_6G_MIDDLE;
524             } else {
525                 band = WifiStatsLog.WIFI_HEALTH_STAT_REPORTED__BAND__BAND_6G_HIGH;
526             }
527         } else {
528             band = WifiStatsLog.WIFI_HEALTH_STAT_REPORTED__BAND__UNKNOWN;
529         }
530         return band;
531     }
532 
logd(String string)533     private void logd(String string) {
534         if (mVerboseLoggingEnabled) {
535             Log.d(TAG, string);
536         }
537     }
538 }
539