• 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.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 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 DeviceConfigFacade mDeviceConfigFacade;
65     private final WifiMetrics mWifiMetrics;
66     private final Context mContext;
67     private final WifiChannelUtilization mWifiChannelUtilization;
68     private TelephonyManager mTelephonyManager;
69     private final ThroughputPredictor mThroughputPredictor;
70     private final ActiveModeWarden mActiveModeWarden;
71     private final ClientModeImplMonitor mClientModeImplMonitor;
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)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         mDeviceConfigFacade = deviceConfigFacade;
155         mWifiMetrics = wifiMetrics;
156         mContext = context;
157         mClock = clock;
158         mWifiChannelUtilization = wifiChannelUtilization;
159         mWifiChannelUtilization.setCacheUpdateIntervalMs(LLSTATS_CACHE_UPDATE_INTERVAL_MIN_MS);
160         mThroughputPredictor = throughputPredictor;
161         mActiveModeWarden = activeModeWarden;
162         mClientModeImplMonitor = clientModeImplMonitor;
163         mPhoneStateListener = new PhoneStateListener(new HandlerExecutor(handler)) {
164             @Override
165             public void onDataConnectionStateChanged(int state, int networkType) {
166                 if (state != TelephonyManager.DATA_CONNECTED
167                         && state != TelephonyManager.DATA_DISCONNECTED) {
168                     Log.e(TAG, "onDataConnectionStateChanged unexpected State: " + state);
169                     return;
170                 }
171                 mIsCellularDataAvailable = state == TelephonyManager.DATA_CONNECTED;
172                 mActiveModeWarden.getPrimaryClientModeManager()
173                         .onCellularConnectivityChanged(mIsCellularDataAvailable
174                                 ? CELLULAR_DATA_AVAILABLE : CELLULAR_DATA_NOT_AVAILABLE);
175                 logd("Cellular Data: " + mIsCellularDataAvailable);
176             }
177         };
178         mActiveModeWarden.registerPrimaryClientModeManagerChangedCallback(
179                 new PrimaryModeChangeCallback());
180         mActiveModeWarden.registerModeChangeCallback(new ModeChangeCallback());
181         mClientModeImplMonitor.registerListener(new ClientModeImplListenerInternal());
182     }
183 
184     /**
185      * initialization after wifi is enabled
186      */
init()187     private void init() {
188         mWifiChannelUtilization.init(null);
189         reset();
190     }
191 
192     /**
193      * Reset internal variables
194      */
reset()195     private void reset() {
196         mLastTxBytes = 0;
197         mLastRxBytes = 0;
198         mLastFrequency = -1;
199         mLastBssid = null;
200         mDataStallStartTimeMs = -1;
201         mDataStallTx = false;
202         mDataStallRx = false;
203         mIsThroughputSufficient = true;
204         mTxTputKbps = INVALID_THROUGHPUT;
205         mRxTputKbps = INVALID_THROUGHPUT;
206     }
207 
createTelephonyManagerForDefaultDataSubIfNeeded()208     private void createTelephonyManagerForDefaultDataSubIfNeeded() {
209         if (mTelephonyManager == null) {
210             mTelephonyManager = (TelephonyManager) mContext
211                     .getSystemService(Context.TELEPHONY_SERVICE);
212         }
213         int defaultSubscriptionId = SubscriptionManager.getDefaultDataSubscriptionId();
214         if (defaultSubscriptionId != SubscriptionManager.INVALID_SUBSCRIPTION_ID
215                 && defaultSubscriptionId != mTelephonyManager.getSubscriptionId()) {
216             mTelephonyManager = mTelephonyManager.createForSubscriptionId(
217                     SubscriptionManager.getDefaultDataSubscriptionId());
218         }
219     }
220 
221     /**
222      * Reset the PhoneStateListener to listen on the default data SIM.
223      */
resetPhoneStateListener()224     public void resetPhoneStateListener() {
225         disablePhoneStateListener();
226         mActiveModeWarden.getPrimaryClientModeManager()
227                 .onCellularConnectivityChanged(CELLULAR_DATA_UNKNOWN);
228         enablePhoneStateListener();
229     }
230 
231     /**
232      * Enable phone state listener
233      */
enablePhoneStateListener()234     private void enablePhoneStateListener() {
235         createTelephonyManagerForDefaultDataSubIfNeeded();
236         if (mTelephonyManager != null && !mPhoneStateListenerEnabled) {
237             mPhoneStateListenerEnabled = true;
238             mTelephonyManager.listen(mPhoneStateListener,
239                     PhoneStateListener.LISTEN_DATA_CONNECTION_STATE);
240         }
241     }
242 
243     /**
244      * Disable phone state listener
245      */
disablePhoneStateListener()246     private void disablePhoneStateListener() {
247         if (mTelephonyManager != null && mPhoneStateListenerEnabled) {
248             mPhoneStateListenerEnabled = false;
249             mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
250         }
251     }
252 
253     /**
254      * Enable/Disable verbose logging.
255      * @param verbose true to enable and false to disable.
256      */
enableVerboseLogging(boolean verbose)257     public void enableVerboseLogging(boolean verbose) {
258         mVerboseLoggingEnabled = verbose;
259         mWifiChannelUtilization.enableVerboseLogging(verbose);
260     }
261 
262     /**
263      * Update device mobility state
264      * @param newState the new device mobility state
265      */
setDeviceMobilityState(@eviceMobilityState int newState)266     public void setDeviceMobilityState(@DeviceMobilityState int newState) {
267         mWifiChannelUtilization.setDeviceMobilityState(newState);
268     }
269 
270     /**
271      * Check if current link layer throughput is sufficient.
272      * This should be called after checkDataStallAndThroughputSufficiency().
273      * @return true if it is sufficient or false if it is insufficient
274      */
isThroughputSufficient()275     public boolean isThroughputSufficient() {
276         return mIsThroughputSufficient;
277     }
278 
279     /**
280      * Check if cellular data is available
281      * @return true if it is available and false otherwise
282      */
isCellularDataAvailable()283     public boolean isCellularDataAvailable() {
284         return mIsCellularDataAvailable;
285     }
286 
287     /**
288      * Get the latest Tx throughput based on Tx link speed, PER and channel utilization
289      * @return the latest estimated Tx throughput in Kbps if it is available
290      *  or INVALID_THROUGHPUT if it is not available
291      */
getTxThroughputKbps()292     public int getTxThroughputKbps() {
293         logd("tx tput in kbps: " + mTxTputKbps);
294         return mTxTputKbps;
295     }
296 
297     /**
298      * Get the latest Rx throughput based on Rx link speed and channel utilization
299      * @return the latest estimated Rx throughput in Kbps if it is available
300      *  or INVALID_THROUGHPUT if it is not available
301      */
getRxThroughputKbps()302     public int getRxThroughputKbps() {
303         logd("rx tput in kbps: " + mRxTputKbps);
304         return mRxTputKbps;
305     }
306 
307     /**
308      * Update data stall detection, check throughput sufficiency and report wifi health stat
309      * with the latest link layer stats
310      * @param connectionCapabilities Connection capabilities.
311      * @param oldStats second most recent WifiLinkLayerStats
312      * @param newStats most recent WifiLinkLayerStats
313      * @param wifiInfo WifiInfo for current connection
314      * @return trigger type of WifiIsUnusableEvent
315      *
316      * Note: This is only collected for primary STA currently because RSSI polling is disabled for
317      * non-primary STAs.
318      */
checkDataStallAndThroughputSufficiency( @onNull String ifaceName, @NonNull ConnectionCapabilities connectionCapabilities, @Nullable WifiLinkLayerStats oldStats, @Nullable WifiLinkLayerStats newStats, @NonNull WifiInfo wifiInfo, long txBytes, long rxBytes)319     public int checkDataStallAndThroughputSufficiency(
320             @NonNull String ifaceName,
321             @NonNull ConnectionCapabilities connectionCapabilities,
322             @Nullable WifiLinkLayerStats oldStats,
323             @Nullable WifiLinkLayerStats newStats,
324             @NonNull WifiInfo wifiInfo,
325             long txBytes, long rxBytes) {
326         int currFrequency = wifiInfo.getFrequency();
327         mWifiChannelUtilization.refreshChannelStatsAndChannelUtilization(newStats, currFrequency);
328         int ccaLevel = mWifiChannelUtilization.getUtilizationRatio(currFrequency);
329         mWifiMetrics.incrementChannelUtilizationCount(ccaLevel, currFrequency);
330         if (oldStats == null || newStats == null) {
331             // First poll after new association
332             // Update throughput with prediction
333             if (wifiInfo.getRssi() != WifiInfo.INVALID_RSSI && connectionCapabilities != null) {
334                 mTxTputKbps = mThroughputPredictor.predictTxThroughput(connectionCapabilities,
335                         wifiInfo.getRssi(), currFrequency, ccaLevel) * 1000;
336                 mRxTputKbps = mThroughputPredictor.predictRxThroughput(connectionCapabilities,
337                         wifiInfo.getRssi(), currFrequency, ccaLevel) * 1000;
338             }
339             mIsThroughputSufficient = true;
340             mWifiMetrics.resetWifiIsUnusableLinkLayerStats();
341             mWifiMetrics.incrementThroughputKbpsCount(mTxTputKbps, mRxTputKbps, currFrequency);
342             return WifiIsUnusableEvent.TYPE_UNKNOWN;
343         }
344 
345         long txSuccessDelta = (newStats.txmpdu_be + newStats.txmpdu_bk
346                 + newStats.txmpdu_vi + newStats.txmpdu_vo)
347                 - (oldStats.txmpdu_be + oldStats.txmpdu_bk
348                 + oldStats.txmpdu_vi + oldStats.txmpdu_vo);
349         long txRetriesDelta = (newStats.retries_be + newStats.retries_bk
350                 + newStats.retries_vi + newStats.retries_vo)
351                 - (oldStats.retries_be + oldStats.retries_bk
352                 + oldStats.retries_vi + oldStats.retries_vo);
353         long txBadDelta = (newStats.lostmpdu_be + newStats.lostmpdu_bk
354                 + newStats.lostmpdu_vi + newStats.lostmpdu_vo)
355                 - (oldStats.lostmpdu_be + oldStats.lostmpdu_bk
356                 + oldStats.lostmpdu_vi + oldStats.lostmpdu_vo);
357         long rxSuccessDelta = (newStats.rxmpdu_be + newStats.rxmpdu_bk
358                 + newStats.rxmpdu_vi + newStats.rxmpdu_vo)
359                 - (oldStats.rxmpdu_be + oldStats.rxmpdu_bk
360                 + oldStats.rxmpdu_vi + oldStats.rxmpdu_vo);
361         int timeDeltaLastTwoPollsMs = (int) (newStats.timeStampInMs - oldStats.timeStampInMs);
362 
363         long totalTxDelta = txSuccessDelta + txRetriesDelta;
364         boolean isTxTrafficHigh = (totalTxDelta * 1000)
365                 > (mDeviceConfigFacade.getTxPktPerSecondThr() * timeDeltaLastTwoPollsMs);
366         boolean isRxTrafficHigh = (rxSuccessDelta * 1000)
367                 > (mDeviceConfigFacade.getRxPktPerSecondThr() * timeDeltaLastTwoPollsMs);
368         if (timeDeltaLastTwoPollsMs < 0
369                 || txSuccessDelta < 0
370                 || txRetriesDelta < 0
371                 || txBadDelta < 0
372                 || rxSuccessDelta < 0) {
373             mIsThroughputSufficient = true;
374             // There was a reset in WifiLinkLayerStats
375             mWifiMetrics.resetWifiIsUnusableLinkLayerStats();
376             return WifiIsUnusableEvent.TYPE_UNKNOWN;
377         }
378 
379         mWifiMetrics.updateWifiIsUnusableLinkLayerStats(txSuccessDelta, txRetriesDelta,
380                 txBadDelta, rxSuccessDelta, timeDeltaLastTwoPollsMs);
381 
382         int txLinkSpeedMbps = wifiInfo.getLinkSpeed();
383         int rxLinkSpeedMbps = wifiInfo.getRxLinkSpeedMbps();
384         boolean isSameBssidAndFreq = mLastBssid == null || mLastFrequency == -1
385                 || (mLastBssid.equals(wifiInfo.getBSSID())
386                 && mLastFrequency == currFrequency);
387         mLastFrequency = currFrequency;
388         mLastBssid = wifiInfo.getBSSID();
389 
390         if (ccaLevel == BssLoad.INVALID) {
391             ccaLevel = wifiInfo.is24GHz() ? DEFAULT_CCA_LEVEL_2G : DEFAULT_CCA_LEVEL_ABOVE_2G;
392             logd(" use default cca Level");
393         }
394         logd(" ccaLevel = " + ccaLevel);
395 
396         int txPer = updateTxPer(txSuccessDelta, txRetriesDelta, isSameBssidAndFreq,
397                 isTxTrafficHigh);
398 
399         boolean isTxTputLow = false;
400         boolean isRxTputLow = false;
401 
402         if (txLinkSpeedMbps > 0) {
403             // Exclude update with low rate management frames
404             if (isTxTrafficHigh
405                     || txLinkSpeedMbps > mDeviceConfigFacade.getTxLinkSpeedLowThresholdMbps()) {
406                 mTxTputKbps = (int) ((long) txLinkSpeedMbps * 1000 * (100 - txPer) / 100
407                         * (CHANNEL_UTILIZATION_SCALE  - ccaLevel) / CHANNEL_UTILIZATION_SCALE);
408             }
409             isTxTputLow =  mTxTputKbps < mDeviceConfigFacade.getDataStallTxTputThrKbps();
410         } else {
411             mTxTputKbps = INVALID_THROUGHPUT;
412         }
413 
414         if (rxLinkSpeedMbps > 0) {
415             // Exclude update with low rate management frames
416             if (isRxTrafficHigh
417                     || rxLinkSpeedMbps > mDeviceConfigFacade.getRxLinkSpeedLowThresholdMbps()) {
418                 mRxTputKbps = (int) ((long) rxLinkSpeedMbps * 1000
419                         * (CHANNEL_UTILIZATION_SCALE  - ccaLevel) / CHANNEL_UTILIZATION_SCALE);
420             }
421             isRxTputLow = mRxTputKbps < mDeviceConfigFacade.getDataStallRxTputThrKbps();
422         } else {
423             mRxTputKbps = INVALID_THROUGHPUT;
424         }
425         mWifiMetrics.incrementThroughputKbpsCount(mTxTputKbps, mRxTputKbps, currFrequency);
426 
427         mIsThroughputSufficient = isThroughputSufficientInternal(mTxTputKbps, mRxTputKbps,
428                 isTxTrafficHigh, isRxTrafficHigh, timeDeltaLastTwoPollsMs, txBytes, rxBytes);
429 
430         int maxTimeDeltaMs = mContext.getResources().getInteger(
431                 R.integer.config_wifiPollRssiIntervalMilliseconds)
432                 + MAX_TIME_MARGIN_LAST_TWO_POLLS_MS;
433         if (timeDeltaLastTwoPollsMs > 0 && timeDeltaLastTwoPollsMs <= maxTimeDeltaMs) {
434             mWifiMetrics.incrementConnectionDuration(timeDeltaLastTwoPollsMs,
435                     mIsThroughputSufficient, mIsCellularDataAvailable);
436         }
437 
438         boolean possibleDataStallTx = isTxTputLow
439                 || ccaLevel >= mDeviceConfigFacade.getDataStallCcaLevelThr()
440                 || txPer >= mDeviceConfigFacade.getDataStallTxPerThr();
441         boolean possibleDataStallRx = isRxTputLow
442                 || ccaLevel >= mDeviceConfigFacade.getDataStallCcaLevelThr();
443 
444         boolean dataStallTx = isTxTrafficHigh ? possibleDataStallTx : mDataStallTx;
445         boolean dataStallRx = isRxTrafficHigh ? possibleDataStallRx : mDataStallRx;
446 
447         return detectConsecutiveTwoDataStalls(ifaceName, timeDeltaLastTwoPollsMs, dataStallTx,
448                 dataStallRx);
449     }
450 
451     // Data stall event is triggered if there are consecutive Tx and/or Rx data stalls
452     // 1st data stall should be preceded by no data stall
453     // Reset mDataStallStartTimeMs to -1 if currently there is no Tx or Rx data stall
detectConsecutiveTwoDataStalls(String ifaceName, int timeDeltaLastTwoPollsMs, boolean dataStallTx, boolean dataStallRx)454     private int detectConsecutiveTwoDataStalls(String ifaceName, int timeDeltaLastTwoPollsMs,
455             boolean dataStallTx, boolean dataStallRx) {
456         if (timeDeltaLastTwoPollsMs >= MAX_MS_DELTA_FOR_DATA_STALL) {
457             return WifiIsUnusableEvent.TYPE_UNKNOWN;
458         }
459 
460         if (dataStallTx || dataStallRx) {
461             mDataStallTx = mDataStallTx || dataStallTx;
462             mDataStallRx = mDataStallRx || dataStallRx;
463             if (mDataStallStartTimeMs == -1) {
464                 mDataStallStartTimeMs = mClock.getElapsedSinceBootMillis();
465                 if (mDeviceConfigFacade.getDataStallDurationMs() == 0) {
466                     mDataStallStartTimeMs = -1;
467                     int result = calculateUsabilityEventType(ifaceName, mDataStallTx,
468                             mDataStallRx);
469                     mDataStallRx = false;
470                     mDataStallTx = false;
471                     return result;
472                 }
473             } else {
474                 long elapsedTime = mClock.getElapsedSinceBootMillis() - mDataStallStartTimeMs;
475                 if (elapsedTime >= mDeviceConfigFacade.getDataStallDurationMs()) {
476                     mDataStallStartTimeMs = -1;
477                     if (elapsedTime <= VALIDITY_PERIOD_OF_DATA_STALL_START_MS) {
478                         int result = calculateUsabilityEventType(ifaceName, mDataStallTx,
479                                 mDataStallRx);
480                         mDataStallRx = false;
481                         mDataStallTx = false;
482                         return result;
483                     } else {
484                         mDataStallTx = false;
485                         mDataStallRx = false;
486                     }
487                 } else {
488                     // No need to do anything.
489                 }
490             }
491         } else {
492             mDataStallStartTimeMs = -1;
493             mDataStallTx = false;
494             mDataStallRx = false;
495         }
496         return WifiIsUnusableEvent.TYPE_UNKNOWN;
497     }
498 
updateTxPer(long txSuccessDelta, long txRetriesDelta, boolean isSameBssidAndFreq, boolean isTxTrafficHigh)499     private int updateTxPer(long txSuccessDelta, long txRetriesDelta, boolean isSameBssidAndFreq,
500             boolean isTxTrafficHigh) {
501         if (!isSameBssidAndFreq) {
502             return DEFAULT_TX_PACKET_ERROR_RATE;
503         }
504         long txAttempts = txSuccessDelta + txRetriesDelta;
505         if (txAttempts <= 0 || !isTxTrafficHigh) {
506             return DEFAULT_TX_PACKET_ERROR_RATE;
507         }
508         return (int) (txRetriesDelta * 100 / txAttempts);
509     }
calculateUsabilityEventType(String ifaceName, boolean dataStallTx, boolean dataStallRx)510     private int calculateUsabilityEventType(String ifaceName, boolean dataStallTx,
511             boolean dataStallRx) {
512         int result = WifiIsUnusableEvent.TYPE_UNKNOWN;
513         if (dataStallTx && dataStallRx) {
514             result = WifiIsUnusableEvent.TYPE_DATA_STALL_BOTH;
515         } else if (dataStallTx) {
516             result = WifiIsUnusableEvent.TYPE_DATA_STALL_BAD_TX;
517         } else if (dataStallRx) {
518             result = WifiIsUnusableEvent.TYPE_DATA_STALL_TX_WITHOUT_RX;
519         }
520         mWifiMetrics.logWifiIsUnusableEvent(ifaceName, result);
521         return result;
522     }
523 
isThroughputSufficientInternal(int l2TxTputKbps, int l2RxTputKbps, boolean isTxTrafficHigh, boolean isRxTrafficHigh, int timeDeltaLastTwoPollsMs, long txBytes, long rxBytes)524     private boolean isThroughputSufficientInternal(int l2TxTputKbps, int l2RxTputKbps,
525             boolean isTxTrafficHigh, boolean isRxTrafficHigh, int timeDeltaLastTwoPollsMs,
526             long txBytes, long rxBytes) {
527         if (timeDeltaLastTwoPollsMs > MAX_MS_DELTA_FOR_DATA_STALL
528                 || mLastTxBytes == 0 || mLastRxBytes == 0) {
529             mLastTxBytes = txBytes;
530             mLastRxBytes = rxBytes;
531             return true;
532         }
533 
534         int l3TxTputKbps = (int) ((txBytes - mLastTxBytes) * 8 / timeDeltaLastTwoPollsMs);
535         int l3RxTputKbps = (int) ((rxBytes - mLastRxBytes) * 8 / timeDeltaLastTwoPollsMs);
536 
537         mLastTxBytes = txBytes;
538         mLastRxBytes = rxBytes;
539 
540         boolean isTxTputSufficient = isL2ThroughputSufficient(l2TxTputKbps, l3TxTputKbps, false);
541         boolean isRxTputSufficient = isL2ThroughputSufficient(l2RxTputKbps, l3RxTputKbps, true);
542         isTxTputSufficient = detectAndOverrideFalseInSufficient(
543                 isTxTputSufficient, isTxTrafficHigh, mIsThroughputSufficient);
544         isRxTputSufficient = detectAndOverrideFalseInSufficient(
545                 isRxTputSufficient, isRxTrafficHigh, mIsThroughputSufficient);
546 
547         boolean isThroughputSufficient = isTxTputSufficient && isRxTputSufficient;
548 
549         StringBuilder sb = new StringBuilder();
550         logd(sb.append("L2 txTputKbps: ").append(l2TxTputKbps)
551                 .append(", rxTputKbps: ").append(l2RxTputKbps)
552                 .append(", L3 txTputKbps: ").append(l3TxTputKbps)
553                 .append(", rxTputKbps: ").append(l3RxTputKbps)
554                 .append(", TxTrafficHigh: ").append(isTxTrafficHigh)
555                 .append(", RxTrafficHigh: ").append(isRxTrafficHigh)
556                 .append(", Throughput Sufficient: ").append(isThroughputSufficient)
557                 .toString());
558         return isThroughputSufficient;
559     }
560 
561     /**
562      * L2 tput is sufficient when one of the following conditions is met
563      * 1) L3 tput is low and L2 tput is above its low threshold
564      * 2) L3 tput is not low and L2 tput over L3 tput ratio is above sufficientRatioThr
565      * 3) L3 tput is not low and L2 tput is above its high threshold
566      * 4) L2 tput is invalid
567      */
isL2ThroughputSufficient(int l2TputKbps, int l3TputKbps, boolean isForRxTput)568     private boolean isL2ThroughputSufficient(int l2TputKbps, int l3TputKbps, boolean isForRxTput) {
569         if (l2TputKbps == INVALID_THROUGHPUT) return true;
570         int tputSufficientLowThrKbps = mDeviceConfigFacade.getTxTputSufficientLowThrKbps();
571         int tputSufficientHighThrKbps = mDeviceConfigFacade.getTxTputSufficientHighThrKbps();
572         if (isForRxTput) {
573             tputSufficientLowThrKbps = mDeviceConfigFacade.getRxTputSufficientLowThrKbps();
574             tputSufficientHighThrKbps = mDeviceConfigFacade.getRxTputSufficientHighThrKbps();
575         }
576         boolean isL3TputLow = (l3TputKbps * mDeviceConfigFacade.getTputSufficientRatioThrDen())
577                 < (tputSufficientLowThrKbps * mDeviceConfigFacade.getTputSufficientRatioThrNum());
578         boolean isL2TputAboveLowThr = l2TputKbps >= tputSufficientLowThrKbps;
579         if (isL3TputLow) return isL2TputAboveLowThr;
580 
581         boolean isL2TputAboveHighThr = l2TputKbps >= tputSufficientHighThrKbps;
582         boolean isL2L3TputRatioAboveThr =
583                 (l2TputKbps * mDeviceConfigFacade.getTputSufficientRatioThrDen())
584                 >= (l3TputKbps * mDeviceConfigFacade.getTputSufficientRatioThrNum());
585         return isL2TputAboveHighThr || isL2L3TputRatioAboveThr;
586     }
587 
detectAndOverrideFalseInSufficient(boolean isTputSufficient, boolean isTrafficHigh, boolean lastIsTputSufficient)588     private boolean detectAndOverrideFalseInSufficient(boolean isTputSufficient,
589             boolean isTrafficHigh, boolean lastIsTputSufficient) {
590         boolean possibleFalseInsufficient = (!isTrafficHigh && !isTputSufficient);
591         return  possibleFalseInsufficient ? lastIsTputSufficient : isTputSufficient;
592     }
593 
logd(String string)594     private void logd(String string) {
595         if (mVerboseLoggingEnabled) {
596             Log.d(TAG, string);
597         }
598     }
599 }
600