| /* |
| * Copyright 2018 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.server.wifi; |
| |
| import android.content.Context; |
| import android.provider.Settings; |
| |
| import com.android.server.wifi.nano.WifiMetricsProto.WifiIsUnusableEvent; |
| |
| /** |
| * Looks for Wifi data stalls |
| */ |
| public class WifiDataStall { |
| |
| // Default minimum number of txBadDelta to trigger data stall |
| public static final int MIN_TX_BAD_DEFAULT = 1; |
| // Default minimum number of txSuccessDelta to trigger data stall |
| // when rxSuccessDelta is 0 |
| public static final int MIN_TX_SUCCESS_WITHOUT_RX_DEFAULT = 50; |
| // Maximum time gap between two WifiLinkLayerStats to trigger a data stall |
| public static final long MAX_MS_DELTA_FOR_DATA_STALL = 60 * 1000; // 1 minute |
| |
| private final Context mContext; |
| private final FrameworkFacade mFacade; |
| private final WifiMetrics mWifiMetrics; |
| |
| private int mMinTxBad; |
| private int mMinTxSuccessWithoutRx; |
| |
| public WifiDataStall(Context context, FrameworkFacade facade, WifiMetrics wifiMetrics) { |
| mContext = context; |
| mFacade = facade; |
| mWifiMetrics = wifiMetrics; |
| loadSettings(); |
| } |
| |
| /** |
| * Load setting values related to wifi data stall. |
| */ |
| public void loadSettings() { |
| mMinTxBad = mFacade.getIntegerSetting( |
| mContext, Settings.Global.WIFI_DATA_STALL_MIN_TX_BAD, MIN_TX_BAD_DEFAULT); |
| mMinTxSuccessWithoutRx = mFacade.getIntegerSetting( |
| mContext, Settings.Global.WIFI_DATA_STALL_MIN_TX_SUCCESS_WITHOUT_RX, |
| MIN_TX_SUCCESS_WITHOUT_RX_DEFAULT); |
| mWifiMetrics.setWifiDataStallMinTxBad(mMinTxBad); |
| mWifiMetrics.setWifiDataStallMinRxWithoutTx(mMinTxSuccessWithoutRx); |
| } |
| |
| /** |
| * Checks for data stall by looking at tx/rx packet counts |
| * @param oldStats second most recent WifiLinkLayerStats |
| * @param newStats most recent WifiLinkLayerStats |
| * @return trigger type of WifiIsUnusableEvent |
| */ |
| public int checkForDataStall(WifiLinkLayerStats oldStats, WifiLinkLayerStats newStats) { |
| if (oldStats == null || newStats == null) { |
| mWifiMetrics.resetWifiIsUnusableLinkLayerStats(); |
| return WifiIsUnusableEvent.TYPE_UNKNOWN; |
| } |
| |
| long txSuccessDelta = (newStats.txmpdu_be + newStats.txmpdu_bk |
| + newStats.txmpdu_vi + newStats.txmpdu_vo) |
| - (oldStats.txmpdu_be + oldStats.txmpdu_bk |
| + oldStats.txmpdu_vi + oldStats.txmpdu_vo); |
| long txRetriesDelta = (newStats.retries_be + newStats.retries_bk |
| + newStats.retries_vi + newStats.retries_vo) |
| - (oldStats.retries_be + oldStats.retries_bk |
| + oldStats.retries_vi + oldStats.retries_vo); |
| long txBadDelta = (newStats.lostmpdu_be + newStats.lostmpdu_bk |
| + newStats.lostmpdu_vi + newStats.lostmpdu_vo) |
| - (oldStats.lostmpdu_be + oldStats.lostmpdu_bk |
| + oldStats.lostmpdu_vi + oldStats.lostmpdu_vo); |
| long rxSuccessDelta = (newStats.rxmpdu_be + newStats.rxmpdu_bk |
| + newStats.rxmpdu_vi + newStats.rxmpdu_vo) |
| - (oldStats.rxmpdu_be + oldStats.rxmpdu_bk |
| + oldStats.rxmpdu_vi + oldStats.rxmpdu_vo); |
| long timeMsDelta = newStats.timeStampInMs - oldStats.timeStampInMs; |
| |
| if (timeMsDelta < 0 |
| || txSuccessDelta < 0 |
| || txRetriesDelta < 0 |
| || txBadDelta < 0 |
| || rxSuccessDelta < 0) { |
| // There was a reset in WifiLinkLayerStats |
| mWifiMetrics.resetWifiIsUnusableLinkLayerStats(); |
| return WifiIsUnusableEvent.TYPE_UNKNOWN; |
| } |
| |
| mWifiMetrics.updateWifiIsUnusableLinkLayerStats(txSuccessDelta, txRetriesDelta, |
| txBadDelta, rxSuccessDelta, timeMsDelta); |
| if (timeMsDelta < MAX_MS_DELTA_FOR_DATA_STALL) { |
| // There is a data stall if there are too many tx failures |
| // or if we are not receiving any packets despite many tx successes |
| boolean dataStallBadTx = (txBadDelta >= mMinTxBad); |
| boolean dataStallTxSuccessWithoutRx = |
| (rxSuccessDelta == 0 && txSuccessDelta >= mMinTxSuccessWithoutRx); |
| if (dataStallBadTx && dataStallTxSuccessWithoutRx) { |
| mWifiMetrics.logWifiIsUnusableEvent(WifiIsUnusableEvent.TYPE_DATA_STALL_BOTH); |
| return WifiIsUnusableEvent.TYPE_DATA_STALL_BOTH; |
| } else if (dataStallBadTx) { |
| mWifiMetrics.logWifiIsUnusableEvent(WifiIsUnusableEvent.TYPE_DATA_STALL_BAD_TX); |
| return WifiIsUnusableEvent.TYPE_DATA_STALL_BAD_TX; |
| } else if (dataStallTxSuccessWithoutRx) { |
| mWifiMetrics.logWifiIsUnusableEvent( |
| WifiIsUnusableEvent.TYPE_DATA_STALL_TX_WITHOUT_RX); |
| return WifiIsUnusableEvent.TYPE_DATA_STALL_TX_WITHOUT_RX; |
| } |
| } |
| |
| return WifiIsUnusableEvent.TYPE_UNKNOWN; |
| } |
| } |