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