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