• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.os.HandlerExecutor;
29 import android.telephony.PhoneStateListener;
30 import android.telephony.SubscriptionManager;
31 import android.telephony.TelephonyManager;
32 import android.util.Log;
33 
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 import com.android.wifi.resources.R;
39 
40 import java.lang.annotation.Retention;
41 import java.lang.annotation.RetentionPolicy;
42 
43 /**
44  * Looks for Wifi data stalls
45  */
46 public class WifiDataStall {
47     private static final String TAG = "WifiDataStall";
48     private boolean mVerboseLoggingEnabled = false;
49     public static final int INVALID_THROUGHPUT = -1;
50     // Maximum time gap between two WifiLinkLayerStats to trigger a data stall
51     public static final int MAX_MS_DELTA_FOR_DATA_STALL = 60 * 1000; // 1 minute
52     // Maximum time that a data stall start time stays valid.
53     public static final long VALIDITY_PERIOD_OF_DATA_STALL_START_MS = 30 * 1000; // 0.5 minutes
54     // Default Tx packet error rate when there is no Tx attempt
55     public static final int DEFAULT_TX_PACKET_ERROR_RATE = 5;
56     // Default CCA level when CCA stats are not available
57     public static final int DEFAULT_CCA_LEVEL_2G = CHANNEL_UTILIZATION_SCALE * 16 / 100;
58     public static final int DEFAULT_CCA_LEVEL_ABOVE_2G = CHANNEL_UTILIZATION_SCALE * 6 / 100;
59     // Minimum time interval in ms between two link layer stats cache updates
60     private static final int LLSTATS_CACHE_UPDATE_INTERVAL_MIN_MS = 30_000;
61     // Maximum time margin between two link layer stats for connection duration update
62     public static final int MAX_TIME_MARGIN_LAST_TWO_POLLS_MS = 200;
63 
64     private final FrameworkFacade mFacade;
65     private final DeviceConfigFacade mDeviceConfigFacade;
66     private final WifiMetrics mWifiMetrics;
67     private final Context mContext;
68     private final WifiChannelUtilization mWifiChannelUtilization;
69     private TelephonyManager mTelephonyManager;
70     private final ThroughputPredictor mThroughputPredictor;
71     private final ActiveModeWarden mActiveModeWarden;
72     private final ClientModeImplMonitor mClientModeImplMonitor;
73 
74     private int mLastFrequency = -1;
75     private String mLastBssid;
76     private long mDataStallStartTimeMs = -1;
77     private Clock mClock;
78     private boolean mDataStallTx = false;
79     private boolean mDataStallRx = false;
80     private long mLastTxBytes;
81     private long mLastRxBytes;
82     private boolean mIsThroughputSufficient = true;
83     private boolean mIsCellularDataAvailable = false;
84     private final PhoneStateListener mPhoneStateListener;
85     private boolean mPhoneStateListenerEnabled = false;
86     private int mTxTputKbps = INVALID_THROUGHPUT;
87     private int mRxTputKbps = INVALID_THROUGHPUT;
88 
89     /** @hide */
90     @IntDef(prefix = { "CELLULAR_DATA_" }, value = {
91             CELLULAR_DATA_UNKNOWN,
92             CELLULAR_DATA_AVAILABLE,
93             CELLULAR_DATA_NOT_AVAILABLE,
94     })
95     @Retention(RetentionPolicy.SOURCE)
96     public @interface CellularDataStatusCode {}
97     public static final int CELLULAR_DATA_UNKNOWN = 0;
98     public static final int CELLULAR_DATA_AVAILABLE = 1;
99     public static final int CELLULAR_DATA_NOT_AVAILABLE = 2;
100 
101     private class ModeChangeCallback implements ActiveModeWarden.ModeChangeCallback {
102         @Override
onActiveModeManagerAdded(@onNull ActiveModeManager activeModeManager)103         public void onActiveModeManagerAdded(@NonNull ActiveModeManager activeModeManager) {
104             update();
105         }
106 
107         @Override
onActiveModeManagerRemoved(@onNull ActiveModeManager activeModeManager)108         public void onActiveModeManagerRemoved(@NonNull ActiveModeManager activeModeManager) {
109             update();
110         }
111 
112         @Override
onActiveModeManagerRoleChanged(@onNull ActiveModeManager activeModeManager)113         public void onActiveModeManagerRoleChanged(@NonNull ActiveModeManager activeModeManager) {
114             update();
115         }
116 
update()117         private void update() {
118             // Register/Unregister phone listener on wifi on/off.
119             if (mActiveModeWarden.getPrimaryClientModeManagerNullable() != null) {
120                 enablePhoneStateListener();
121             } else {
122                 disablePhoneStateListener();
123             }
124         }
125     }
126 
127     private class PrimaryModeChangeCallback implements PrimaryClientModeManagerChangedCallback {
128         @Override
onChange( @ullable ConcreteClientModeManager prevPrimaryClientModeManager, @Nullable ConcreteClientModeManager newPrimaryClientModeManager)129         public void onChange(
130                 @Nullable ConcreteClientModeManager prevPrimaryClientModeManager,
131                 @Nullable ConcreteClientModeManager newPrimaryClientModeManager) {
132             // This is needed to reset state on an MBB switch or wifi toggle.
133             if (prevPrimaryClientModeManager != null) {
134                 reset();
135             }
136             if (newPrimaryClientModeManager != null) {
137                 init();
138             }
139         }
140     }
141 
142     private class ClientModeImplListenerInternal implements ClientModeImplListener {
143         @Override
onConnectionEnd(@onNull ConcreteClientModeManager clientModeManager)144         public void onConnectionEnd(@NonNull ConcreteClientModeManager clientModeManager) {
145             if (clientModeManager.getRole() == ActiveModeManager.ROLE_CLIENT_PRIMARY) {
146                 reset();
147             }
148         }
149     }
150 
WifiDataStall(FrameworkFacade facade, WifiMetrics wifiMetrics, Context context, DeviceConfigFacade deviceConfigFacade, WifiChannelUtilization wifiChannelUtilization, Clock clock, Handler handler, ThroughputPredictor throughputPredictor, ActiveModeWarden activeModeWarden, ClientModeImplMonitor clientModeImplMonitor)151     public WifiDataStall(FrameworkFacade facade, WifiMetrics wifiMetrics, Context context,
152             DeviceConfigFacade deviceConfigFacade, WifiChannelUtilization wifiChannelUtilization,
153             Clock clock, Handler handler, ThroughputPredictor throughputPredictor,
154             ActiveModeWarden activeModeWarden, ClientModeImplMonitor clientModeImplMonitor) {
155         mFacade = facade;
156         mDeviceConfigFacade = deviceConfigFacade;
157         mWifiMetrics = wifiMetrics;
158         mContext = context;
159         mClock = clock;
160         mWifiChannelUtilization = wifiChannelUtilization;
161         mWifiChannelUtilization.setCacheUpdateIntervalMs(LLSTATS_CACHE_UPDATE_INTERVAL_MIN_MS);
162         mThroughputPredictor = throughputPredictor;
163         mActiveModeWarden = activeModeWarden;
164         mClientModeImplMonitor = clientModeImplMonitor;
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)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         int currFrequency = wifiInfo.getFrequency();
328         mWifiChannelUtilization.refreshChannelStatsAndChannelUtilization(newStats, currFrequency);
329         int ccaLevel = mWifiChannelUtilization.getUtilizationRatio(currFrequency);
330         mWifiMetrics.incrementChannelUtilizationCount(ccaLevel, currFrequency);
331         if (oldStats == null || newStats == null) {
332             // First poll after new association
333             // Update throughput with prediction
334             if (wifiInfo.getRssi() != WifiInfo.INVALID_RSSI && connectionCapabilities != null) {
335                 mTxTputKbps = mThroughputPredictor.predictTxThroughput(connectionCapabilities,
336                         wifiInfo.getRssi(), currFrequency, ccaLevel) * 1000;
337                 mRxTputKbps = mThroughputPredictor.predictRxThroughput(connectionCapabilities,
338                         wifiInfo.getRssi(), currFrequency, ccaLevel) * 1000;
339             }
340             mIsThroughputSufficient = true;
341             mWifiMetrics.resetWifiIsUnusableLinkLayerStats();
342             mWifiMetrics.incrementThroughputKbpsCount(mTxTputKbps, mRxTputKbps, currFrequency);
343             return WifiIsUnusableEvent.TYPE_UNKNOWN;
344         }
345 
346         long txSuccessDelta = (newStats.txmpdu_be + newStats.txmpdu_bk
347                 + newStats.txmpdu_vi + newStats.txmpdu_vo)
348                 - (oldStats.txmpdu_be + oldStats.txmpdu_bk
349                 + oldStats.txmpdu_vi + oldStats.txmpdu_vo);
350         long txRetriesDelta = (newStats.retries_be + newStats.retries_bk
351                 + newStats.retries_vi + newStats.retries_vo)
352                 - (oldStats.retries_be + oldStats.retries_bk
353                 + oldStats.retries_vi + oldStats.retries_vo);
354         long txBadDelta = (newStats.lostmpdu_be + newStats.lostmpdu_bk
355                 + newStats.lostmpdu_vi + newStats.lostmpdu_vo)
356                 - (oldStats.lostmpdu_be + oldStats.lostmpdu_bk
357                 + oldStats.lostmpdu_vi + oldStats.lostmpdu_vo);
358         long rxSuccessDelta = (newStats.rxmpdu_be + newStats.rxmpdu_bk
359                 + newStats.rxmpdu_vi + newStats.rxmpdu_vo)
360                 - (oldStats.rxmpdu_be + oldStats.rxmpdu_bk
361                 + oldStats.rxmpdu_vi + oldStats.rxmpdu_vo);
362         int timeDeltaLastTwoPollsMs = (int) (newStats.timeStampInMs - oldStats.timeStampInMs);
363 
364         long totalTxDelta = txSuccessDelta + txRetriesDelta;
365         boolean isTxTrafficHigh = (totalTxDelta * 1000)
366                 > (mDeviceConfigFacade.getTxPktPerSecondThr() * timeDeltaLastTwoPollsMs);
367         boolean isRxTrafficHigh = (rxSuccessDelta * 1000)
368                 > (mDeviceConfigFacade.getRxPktPerSecondThr() * timeDeltaLastTwoPollsMs);
369         if (timeDeltaLastTwoPollsMs < 0
370                 || txSuccessDelta < 0
371                 || txRetriesDelta < 0
372                 || txBadDelta < 0
373                 || rxSuccessDelta < 0) {
374             mIsThroughputSufficient = true;
375             // There was a reset in WifiLinkLayerStats
376             mWifiMetrics.resetWifiIsUnusableLinkLayerStats();
377             return WifiIsUnusableEvent.TYPE_UNKNOWN;
378         }
379 
380         mWifiMetrics.updateWifiIsUnusableLinkLayerStats(txSuccessDelta, txRetriesDelta,
381                 txBadDelta, rxSuccessDelta, timeDeltaLastTwoPollsMs);
382 
383         int txLinkSpeedMbps = wifiInfo.getLinkSpeed();
384         int rxLinkSpeedMbps = wifiInfo.getRxLinkSpeedMbps();
385         boolean isSameBssidAndFreq = mLastBssid == null || mLastFrequency == -1
386                 || (mLastBssid.equals(wifiInfo.getBSSID())
387                 && mLastFrequency == currFrequency);
388         mLastFrequency = currFrequency;
389         mLastBssid = wifiInfo.getBSSID();
390 
391         if (ccaLevel == BssLoad.INVALID) {
392             ccaLevel = wifiInfo.is24GHz() ? DEFAULT_CCA_LEVEL_2G : DEFAULT_CCA_LEVEL_ABOVE_2G;
393             logd(" use default cca Level");
394         }
395         logd(" ccaLevel = " + ccaLevel);
396 
397         int txPer = updateTxPer(txSuccessDelta, txRetriesDelta, isSameBssidAndFreq,
398                 isTxTrafficHigh);
399 
400         boolean isTxTputLow = false;
401         boolean isRxTputLow = false;
402 
403         if (txLinkSpeedMbps > 0) {
404             // Exclude update with low rate management frames
405             if (isTxTrafficHigh
406                     || txLinkSpeedMbps > mDeviceConfigFacade.getTxLinkSpeedLowThresholdMbps()) {
407                 mTxTputKbps = (int) ((long) txLinkSpeedMbps * 1000 * (100 - txPer) / 100
408                         * (CHANNEL_UTILIZATION_SCALE  - ccaLevel) / CHANNEL_UTILIZATION_SCALE);
409             }
410             isTxTputLow =  mTxTputKbps < mDeviceConfigFacade.getDataStallTxTputThrKbps();
411         } else {
412             mTxTputKbps = INVALID_THROUGHPUT;
413         }
414 
415         if (rxLinkSpeedMbps > 0) {
416             // Exclude update with low rate management frames
417             if (isRxTrafficHigh
418                     || rxLinkSpeedMbps > mDeviceConfigFacade.getRxLinkSpeedLowThresholdMbps()) {
419                 mRxTputKbps = (int) ((long) rxLinkSpeedMbps * 1000
420                         * (CHANNEL_UTILIZATION_SCALE  - ccaLevel) / CHANNEL_UTILIZATION_SCALE);
421             }
422             isRxTputLow = mRxTputKbps < mDeviceConfigFacade.getDataStallRxTputThrKbps();
423         } else {
424             mRxTputKbps = INVALID_THROUGHPUT;
425         }
426         mWifiMetrics.incrementThroughputKbpsCount(mTxTputKbps, mRxTputKbps, currFrequency);
427 
428         mIsThroughputSufficient = isThroughputSufficientInternal(mTxTputKbps, mRxTputKbps,
429                 isTxTrafficHigh, isRxTrafficHigh, timeDeltaLastTwoPollsMs);
430 
431         int maxTimeDeltaMs = mContext.getResources().getInteger(
432                 R.integer.config_wifiPollRssiIntervalMilliseconds)
433                 + MAX_TIME_MARGIN_LAST_TWO_POLLS_MS;
434         if (timeDeltaLastTwoPollsMs > 0 && timeDeltaLastTwoPollsMs <= maxTimeDeltaMs) {
435             mWifiMetrics.incrementConnectionDuration(timeDeltaLastTwoPollsMs,
436                     mIsThroughputSufficient, mIsCellularDataAvailable);
437         }
438 
439         boolean possibleDataStallTx = isTxTputLow
440                 || ccaLevel >= mDeviceConfigFacade.getDataStallCcaLevelThr()
441                 || txPer >= mDeviceConfigFacade.getDataStallTxPerThr();
442         boolean possibleDataStallRx = isRxTputLow
443                 || ccaLevel >= mDeviceConfigFacade.getDataStallCcaLevelThr();
444 
445         boolean dataStallTx = isTxTrafficHigh ? possibleDataStallTx : mDataStallTx;
446         boolean dataStallRx = isRxTrafficHigh ? possibleDataStallRx : mDataStallRx;
447 
448         return detectConsecutiveTwoDataStalls(ifaceName, timeDeltaLastTwoPollsMs, dataStallTx,
449                 dataStallRx);
450     }
451 
452     // Data stall event is triggered if there are consecutive Tx and/or Rx data stalls
453     // 1st data stall should be preceded by no data stall
454     // Reset mDataStallStartTimeMs to -1 if currently there is no Tx or Rx data stall
detectConsecutiveTwoDataStalls(String ifaceName, int timeDeltaLastTwoPollsMs, boolean dataStallTx, boolean dataStallRx)455     private int detectConsecutiveTwoDataStalls(String ifaceName, int timeDeltaLastTwoPollsMs,
456             boolean dataStallTx, boolean dataStallRx) {
457         if (timeDeltaLastTwoPollsMs >= MAX_MS_DELTA_FOR_DATA_STALL) {
458             return WifiIsUnusableEvent.TYPE_UNKNOWN;
459         }
460 
461         if (dataStallTx || dataStallRx) {
462             mDataStallTx = mDataStallTx || dataStallTx;
463             mDataStallRx = mDataStallRx || dataStallRx;
464             if (mDataStallStartTimeMs == -1) {
465                 mDataStallStartTimeMs = mClock.getElapsedSinceBootMillis();
466                 if (mDeviceConfigFacade.getDataStallDurationMs() == 0) {
467                     mDataStallStartTimeMs = -1;
468                     int result = calculateUsabilityEventType(ifaceName, mDataStallTx,
469                             mDataStallRx);
470                     mDataStallRx = false;
471                     mDataStallTx = false;
472                     return result;
473                 }
474             } else {
475                 long elapsedTime = mClock.getElapsedSinceBootMillis() - mDataStallStartTimeMs;
476                 if (elapsedTime >= mDeviceConfigFacade.getDataStallDurationMs()) {
477                     mDataStallStartTimeMs = -1;
478                     if (elapsedTime <= VALIDITY_PERIOD_OF_DATA_STALL_START_MS) {
479                         int result = calculateUsabilityEventType(ifaceName, mDataStallTx,
480                                 mDataStallRx);
481                         mDataStallRx = false;
482                         mDataStallTx = false;
483                         return result;
484                     } else {
485                         mDataStallTx = false;
486                         mDataStallRx = false;
487                     }
488                 } else {
489                     // No need to do anything.
490                 }
491             }
492         } else {
493             mDataStallStartTimeMs = -1;
494             mDataStallTx = false;
495             mDataStallRx = false;
496         }
497         return WifiIsUnusableEvent.TYPE_UNKNOWN;
498     }
499 
updateTxPer(long txSuccessDelta, long txRetriesDelta, boolean isSameBssidAndFreq, boolean isTxTrafficHigh)500     private int updateTxPer(long txSuccessDelta, long txRetriesDelta, boolean isSameBssidAndFreq,
501             boolean isTxTrafficHigh) {
502         if (!isSameBssidAndFreq) {
503             return DEFAULT_TX_PACKET_ERROR_RATE;
504         }
505         long txAttempts = txSuccessDelta + txRetriesDelta;
506         if (txAttempts <= 0 || !isTxTrafficHigh) {
507             return DEFAULT_TX_PACKET_ERROR_RATE;
508         }
509         return (int) (txRetriesDelta * 100 / txAttempts);
510     }
calculateUsabilityEventType(String ifaceName, boolean dataStallTx, boolean dataStallRx)511     private int calculateUsabilityEventType(String ifaceName, boolean dataStallTx,
512             boolean dataStallRx) {
513         int result = WifiIsUnusableEvent.TYPE_UNKNOWN;
514         if (dataStallTx && dataStallRx) {
515             result = WifiIsUnusableEvent.TYPE_DATA_STALL_BOTH;
516         } else if (dataStallTx) {
517             result = WifiIsUnusableEvent.TYPE_DATA_STALL_BAD_TX;
518         } else if (dataStallRx) {
519             result = WifiIsUnusableEvent.TYPE_DATA_STALL_TX_WITHOUT_RX;
520         }
521         mWifiMetrics.logWifiIsUnusableEvent(ifaceName, result);
522         return result;
523     }
524 
isThroughputSufficientInternal(int l2TxTputKbps, int l2RxTputKbps, boolean isTxTrafficHigh, boolean isRxTrafficHigh, int timeDeltaLastTwoPollsMs)525     private boolean isThroughputSufficientInternal(int l2TxTputKbps, int l2RxTputKbps,
526             boolean isTxTrafficHigh, boolean isRxTrafficHigh, int timeDeltaLastTwoPollsMs) {
527         long txBytes = mFacade.getTotalTxBytes() - mFacade.getMobileTxBytes();
528         long rxBytes = mFacade.getTotalRxBytes() - mFacade.getMobileRxBytes();
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.getTputSufficientRatioThrDen())
579                 < (tputSufficientLowThrKbps * mDeviceConfigFacade.getTputSufficientRatioThrNum());
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);
599         }
600     }
601 }
602