diff options
author | David Su <dysu@google.com> | 2019-05-22 09:44:24 -0700 |
---|---|---|
committer | android-build-merger <android-build-merger@google.com> | 2019-05-22 09:44:24 -0700 |
commit | 9cf15a3a841992b94271fa52fb9d23432572a46d (patch) | |
tree | 7938d5dca8b07c3fa021b378b771774415fa4fdf | |
parent | 328f6b90bb7f14559a744814775ca3db6240534e (diff) | |
parent | ea1688cfc1965d9a61682173276cb9446ce101bb (diff) |
Merge "Adjust link probe manager parameters using experiment results" into qt-dev
am: ea1688cfc1
Change-Id: I9341c770add4c77782b39f57997874cd8fff6c2d
7 files changed, 482 insertions, 127 deletions
diff --git a/service/java/com/android/server/wifi/LinkProbeManager.java b/service/java/com/android/server/wifi/LinkProbeManager.java index b91ba67f4..89c84f76f 100644 --- a/service/java/com/android/server/wifi/LinkProbeManager.java +++ b/service/java/com/android/server/wifi/LinkProbeManager.java @@ -27,9 +27,11 @@ import android.util.Log; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; +import com.android.server.wifi.util.TimedQuotaManager; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.time.Duration; import java.util.ArrayList; import java.util.List; @@ -43,15 +45,25 @@ public class LinkProbeManager { private static final int WIFI_LINK_PROBING_ENABLED_DEFAULT = 1; // 1 = enabled // TODO(112029045): Use constants from ScoringParams instead - @VisibleForTesting - static final int LINK_PROBE_RSSI_THRESHOLD = -70; - @VisibleForTesting - static final int LINK_PROBE_LINK_SPEED_THRESHOLD_MBPS = 15; // in megabits per second - @VisibleForTesting - static final long LINK_PROBE_INTERVAL_MS = 15 * 1000; - - @VisibleForTesting - static final int[] EXPERIMENT_DELAYS_MS = {3000, 6000, 9000, 12000, 15000}; + @VisibleForTesting static final int RSSI_THRESHOLD = -70; + @VisibleForTesting static final int LINK_SPEED_THRESHOLD_MBPS = 15; // in megabits per second + /** Minimum delay before probing after the last probe. */ + @VisibleForTesting static final long DELAY_BETWEEN_PROBES_MS = 6000; + /** Minimum delay before probing after screen turned on. */ + @VisibleForTesting static final long SCREEN_ON_DELAY_MS = 6000; + /** + * Minimum delay before probing after last increase of the Tx success counter (which indicates + * that a data frame (i.e. not counting management frame) was successfully transmitted). + */ + @VisibleForTesting static final long DELAY_AFTER_TX_SUCCESS_MS = 6000; + + @VisibleForTesting static final long MAX_PROBE_COUNT_IN_PERIOD = + WifiMetrics.MAX_LINK_PROBE_STA_EVENTS; + @VisibleForTesting static final long PERIOD_MILLIS = Duration.ofDays(1).toMillis(); + + @VisibleForTesting static final int[] EXPERIMENT_DELAYS_MS = {3000, 6000, 9000, 12000, 15000}; + @VisibleForTesting static final int[] EXPERIMENT_RSSIS = {-65, -70, -75}; + @VisibleForTesting static final int[] EXPERIMENT_LINK_SPEEDS = {10, 15, 20}; private List<Experiment> mExperiments = new ArrayList<>(); private final Clock mClock; @@ -65,16 +77,32 @@ public class LinkProbeManager { private boolean mVerboseLoggingEnabled = false; + /** + * Tracks the last timestamp when a link probe was triggered. Link probing only occurs when at + * least {@link #DELAY_BETWEEN_PROBES_MS} has passed since the last link probe. + */ private long mLastLinkProbeTimestampMs; /** - * Tracks the last timestamp when wifiInfo.txSuccess was increased i.e. the last time a Tx was - * successful. Link probing only occurs when at least {@link #LINK_PROBE_INTERVAL_MS} has passed - * since the last Tx success. - * This is also reset to the current time when {@link #reset()} is called, so that a link probe - * only occurs at least {@link #LINK_PROBE_INTERVAL_MS} after a new connection is made. + * Tracks the last timestamp when {@link WifiInfo#txSuccess} was increased i.e. the last time a + * Tx was successful. Link probing only occurs when at least {@link #DELAY_AFTER_TX_SUCCESS_MS} + * has passed since the last Tx success. + * This is also reset to the current time when {@link #resetOnNewConnection()} is called, so + * that a link probe only occurs at least {@link #DELAY_AFTER_TX_SUCCESS_MS} after a new + * connection is made. */ private long mLastTxSuccessIncreaseTimestampMs; + /** + * Stores the last value of {@link WifiInfo#txSuccess}. The current value of + * {@link WifiInfo#txSuccess} is compared against the last value to determine whether there was + * a successful Tx. + */ private long mLastTxSuccessCount; + /** + * Tracks the last timestamp when the screen turned on. Link probing only occurs when at least + * {@link #SCREEN_ON_DELAY_MS} has passed since the last time the screen was turned on. + */ + private long mLastScreenOnTimestampMs; + private final TimedQuotaManager mTimedQuotaManager; public LinkProbeManager(Clock clock, WifiNative wifiNative, WifiMetrics wifiMetrics, FrameworkFacade frameworkFacade, Looper looper, Context context) { @@ -85,6 +113,7 @@ public class LinkProbeManager { mContext = context; mLinkProbingSupported = mContext.getResources() .getBoolean(R.bool.config_wifi_link_probing_supported); + mTimedQuotaManager = new TimedQuotaManager(clock, MAX_PROBE_COUNT_IN_PERIOD, PERIOD_MILLIS); if (mLinkProbingSupported) { mFrameworkFacade.registerContentObserver(mContext, Settings.Global.getUriFor( @@ -97,7 +126,8 @@ public class LinkProbeManager { }); updateLinkProbeSetting(); - reset(); + resetOnNewConnection(); + resetOnScreenTurnedOn(); } initExperiments(); @@ -124,9 +154,15 @@ public class LinkProbeManager { pw.println("LinkProbeManager - mLastTxSuccessIncreaseTimestampMs: " + mLastTxSuccessIncreaseTimestampMs); pw.println("LinkProbeManager - mLastTxSuccessCount: " + mLastTxSuccessCount); + pw.println("LinkProbeManager - mLastScreenOnTimestampMs: " + mLastScreenOnTimestampMs); + pw.println("LinkProbeManager - mTimedQuotaManager: " + mTimedQuotaManager); } - private void reset() { + /** + * When connecting to a new network, reset internal state. + */ + public void resetOnNewConnection() { + mExperiments.forEach(Experiment::resetOnNewConnection); if (!mLinkProbingSupported) return; long now = mClock.getElapsedSinceBootMillis(); @@ -136,20 +172,14 @@ public class LinkProbeManager { } /** - * When connecting to a new network, reset internal state. - */ - public void resetOnNewConnection() { - mExperiments.forEach(Experiment::resetOnNewConnection); - reset(); - } - - /** * When RSSI poll events are stopped and restarted (usually screen turned off then back on), * reset internal state. */ public void resetOnScreenTurnedOn() { mExperiments.forEach(Experiment::resetOnScreenTurnedOn); - reset(); + if (!mLinkProbingSupported) return; + + mLastScreenOnTimestampMs = mClock.getElapsedSinceBootMillis(); } /** @@ -172,23 +202,33 @@ public class LinkProbeManager { } mLastTxSuccessCount = wifiInfo.txSuccess; - // maximum 1 link probe every LINK_PROBE_INTERVAL_MS + // maximum 1 link probe every DELAY_BETWEEN_PROBES_MS long timeSinceLastLinkProbeMs = now - mLastLinkProbeTimestampMs; - if (timeSinceLastLinkProbeMs < LINK_PROBE_INTERVAL_MS) { + if (timeSinceLastLinkProbeMs < DELAY_BETWEEN_PROBES_MS) { return; } - // if tx succeeded at least once in the last LINK_PROBE_INTERVAL_MS, don't need to probe + // if tx succeeded at least once in the last DELAY_AFTER_TX_SUCCESS_MS, don't need to probe long timeSinceLastTxSuccessIncreaseMs = now - mLastTxSuccessIncreaseTimestampMs; - if (timeSinceLastTxSuccessIncreaseMs < LINK_PROBE_INTERVAL_MS) { + if (timeSinceLastTxSuccessIncreaseMs < DELAY_AFTER_TX_SUCCESS_MS) { + return; + } + + // if not enough time has passed since the screen last turned on, don't probe + long timeSinceLastScreenOnMs = now - mLastScreenOnTimestampMs; + if (timeSinceLastScreenOnMs < SCREEN_ON_DELAY_MS) { return; } // can skip probing if RSSI is valid and high and link speed is fast int rssi = wifiInfo.getRssi(); int linkSpeed = wifiInfo.getLinkSpeed(); - if (rssi != WifiInfo.INVALID_RSSI && rssi > LINK_PROBE_RSSI_THRESHOLD - && linkSpeed > LINK_PROBE_LINK_SPEED_THRESHOLD_MBPS) { + if (rssi != WifiInfo.INVALID_RSSI && rssi > RSSI_THRESHOLD + && linkSpeed > LINK_SPEED_THRESHOLD_MBPS) { + return; + } + + if (!mTimedQuotaManager.requestQuota()) { return; } @@ -201,8 +241,6 @@ public class LinkProbeManager { rssi, linkSpeed)); } - long wallClockTimestampMs = mClock.getWallClockMillis(); - // TODO(b/112029045): also report MCS rate to metrics when supported by driver mWifiNative.probeLink( interfaceName, @@ -214,7 +252,7 @@ public class LinkProbeManager { Log.d(TAG, "link probing success, elapsedTimeMs=" + elapsedTimeMs); } - mWifiMetrics.logLinkProbeSuccess(wallClockTimestampMs, + mWifiMetrics.logLinkProbeSuccess( timeSinceLastTxSuccessIncreaseMs, rssi, linkSpeed, elapsedTimeMs); } @@ -224,7 +262,7 @@ public class LinkProbeManager { if (mVerboseLoggingEnabled) { Log.d(TAG, "link probing failure, reason=" + reason); } - mWifiMetrics.logLinkProbeFailure(wallClockTimestampMs, + mWifiMetrics.logLinkProbeFailure( timeSinceLastTxSuccessIncreaseMs, rssi, linkSpeed, reason); } }, @@ -235,11 +273,11 @@ public class LinkProbeManager { } private void initExperiments() { - for (int screenOnDelayMs : EXPERIMENT_DELAYS_MS) { - for (int noTxDelayMs : EXPERIMENT_DELAYS_MS) { - for (int delayBetweenProbesMs : EXPERIMENT_DELAYS_MS) { + for (int delay : EXPERIMENT_DELAYS_MS) { + for (int rssiThreshold : EXPERIMENT_RSSIS) { + for (int linkSpeedThreshold: EXPERIMENT_LINK_SPEEDS) { Experiment experiment = new Experiment(mClock, mWifiMetrics, - screenOnDelayMs, noTxDelayMs, delayBetweenProbesMs); + delay, delay, delay, rssiThreshold, linkSpeedThreshold); mExperiments.add(experiment); } } @@ -254,29 +292,38 @@ public class LinkProbeManager { private final int mScreenOnDelayMs; private final int mNoTxDelayMs; private final int mDelayBetweenProbesMs; + private final int mRssiThreshold; + private final int mLinkSpeedThreshold; private final String mExperimentId; private long mLastLinkProbeTimestampMs; private long mLastTxSuccessIncreaseTimestampMs; private long mLastTxSuccessCount; + private long mLastScreenOnTimestampMs; Experiment(Clock clock, WifiMetrics wifiMetrics, - int screenOnDelayMs, int noTxDelayMs, int delayBetweenProbesMs) { + int screenOnDelayMs, int noTxDelayMs, int delayBetweenProbesMs, + int rssiThreshold, int linkSpeedThreshold) { mClock = clock; mWifiMetrics = wifiMetrics; mScreenOnDelayMs = screenOnDelayMs; mNoTxDelayMs = noTxDelayMs; mDelayBetweenProbesMs = delayBetweenProbesMs; + mRssiThreshold = rssiThreshold; + mLinkSpeedThreshold = linkSpeedThreshold; mExperimentId = getExperimentId(); resetOnNewConnection(); + resetOnScreenTurnedOn(); } private String getExperimentId() { return "[screenOnDelay=" + mScreenOnDelayMs + ',' + "noTxDelay=" + mNoTxDelayMs + ',' - + "delayBetweenProbes=" + mDelayBetweenProbesMs + ']'; + + "delayBetweenProbes=" + mDelayBetweenProbesMs + ',' + + "rssiThreshold=" + mRssiThreshold + ',' + + "linkSpeedThreshold=" + mLinkSpeedThreshold + ']'; } void resetOnNewConnection() { @@ -287,12 +334,7 @@ public class LinkProbeManager { } void resetOnScreenTurnedOn() { - long now = mClock.getElapsedSinceBootMillis(); - long firstPossibleLinkProbeAfterScreenOnTimestampMs = now + mScreenOnDelayMs; - mLastLinkProbeTimestampMs = Math.max(mLastLinkProbeTimestampMs, - firstPossibleLinkProbeAfterScreenOnTimestampMs - mDelayBetweenProbesMs); - // don't reset mLastTxSuccessIncreaseTimestampMs and mLastTxSuccessCount since no new - // connection was established + mLastScreenOnTimestampMs = mClock.getElapsedSinceBootMillis(); } void updateConnectionStats(WifiInfo wifiInfo) { @@ -314,11 +356,16 @@ public class LinkProbeManager { return; } + long timeSinceLastScreenOnMs = now - mLastScreenOnTimestampMs; + if (timeSinceLastScreenOnMs < SCREEN_ON_DELAY_MS) { + return; + } + // can skip probing if RSSI is valid and high and link speed is fast int rssi = wifiInfo.getRssi(); int linkSpeed = wifiInfo.getLinkSpeed(); - if (rssi != WifiInfo.INVALID_RSSI && rssi > LINK_PROBE_RSSI_THRESHOLD - && linkSpeed > LINK_PROBE_LINK_SPEED_THRESHOLD_MBPS) { + if (rssi != WifiInfo.INVALID_RSSI && rssi > mRssiThreshold + && linkSpeed > mLinkSpeedThreshold) { return; } diff --git a/service/java/com/android/server/wifi/WifiMetrics.java b/service/java/com/android/server/wifi/WifiMetrics.java index 539fe3dee..a62ad379c 100644 --- a/service/java/com/android/server/wifi/WifiMetrics.java +++ b/service/java/com/android/server/wifi/WifiMetrics.java @@ -212,6 +212,13 @@ public class WifiMetrics { private int mProbeMcsRateSinceLastUpdate = -1; private long mScoreBreachLowTimeMillis = -1; + public static final int MAX_STA_EVENTS = 768; + private LinkedList<StaEventWithTime> mStaEventList = new LinkedList<>(); + private int mLastPollRssi = -127; + private int mLastPollLinkSpeed = -1; + private int mLastPollFreq = -1; + private int mLastScore = -1; + /** Tracks if we should be logging WifiIsUnusableEvent */ private boolean mUnusableEventLogging = false; /** Tracks if we should be logging LinkSpeedCounts */ @@ -328,6 +335,8 @@ public class WifiMetrics { * experiment. */ private final ObjectCounter<String> mLinkProbeExperimentProbeCounts = new ObjectCounter<>(); + private int mLinkProbeStaEventCount = 0; + @VisibleForTesting static final int MAX_LINK_PROBE_STA_EVENTS = MAX_STA_EVENTS / 4; private final LinkedList<WifiUsabilityStatsEntry> mWifiUsabilityStatsEntriesList = new LinkedList<>(); @@ -2761,6 +2770,7 @@ public class WifiMetrics { pw.println("mNetworkSelectionExperimentPairNumChoicesCounts:" + mNetworkSelectionExperimentPairNumChoicesCounts); + pw.println("mLinkProbeStaEventCount:" + mLinkProbeStaEventCount); pw.println("mWifiNetworkRequestApiLog:\n" + mWifiNetworkRequestApiLog); pw.println("mWifiNetworkRequestApiMatchSizeHistogram:\n" @@ -3500,6 +3510,7 @@ public class WifiMetrics { mLinkProbeSuccessElapsedTimeMsHistogram.clear(); mLinkProbeFailureReasonCounts.clear(); mLinkProbeExperimentProbeCounts.clear(); + mLinkProbeStaEventCount = 0; mNetworkSelectionExperimentPairNumChoicesCounts.clear(); mWifiNetworkSuggestionApiLog.clear(); mWifiNetworkSuggestionApiLog.clear(); @@ -3935,13 +3946,6 @@ public class WifiMetrics { return sb.toString(); } - public static final int MAX_STA_EVENTS = 768; - private LinkedList<StaEventWithTime> mStaEventList = new LinkedList<StaEventWithTime>(); - private int mLastPollRssi = -127; - private int mLastPollLinkSpeed = -1; - private int mLastPollFreq = -1; - private int mLastScore = -1; - /** * Converts the first 31 bits of a BitSet to a little endian int */ @@ -4684,7 +4688,6 @@ public class WifiMetrics { /** * Reports stats for a successful link probe. * - * @param startTimestampMs The wall clock time when the link probe was started, in ms. * @param timeSinceLastTxSuccessMs At {@code startTimestampMs}, the number of milliseconds since * the last Tx success (according to * {@link WifiInfo#txSuccess}). @@ -4695,7 +4698,7 @@ public class WifiMetrics { * probe was ACKed. Note: this number should be correlated with the number * of retries that the driver attempted before the probe was ACKed. */ - public void logLinkProbeSuccess(long startTimestampMs, long timeSinceLastTxSuccessMs, + public void logLinkProbeSuccess(long timeSinceLastTxSuccessMs, int rssi, int linkSpeed, int elapsedTimeMs) { synchronized (mLock) { mProbeStatusSinceLastUpdate = @@ -4708,19 +4711,20 @@ public class WifiMetrics { mLinkProbeSuccessLinkSpeedCounts.increment(linkSpeed); mLinkProbeSuccessElapsedTimeMsHistogram.increment(elapsedTimeMs); - StaEvent event = new StaEvent(); - event.type = StaEvent.TYPE_LINK_PROBE; - event.linkProbeWasSuccess = true; - event.linkProbeSuccessElapsedTimeMs = elapsedTimeMs; - // TODO(129958996): Cap number of link probe StaEvents - addStaEvent(event); + if (mLinkProbeStaEventCount < MAX_LINK_PROBE_STA_EVENTS) { + StaEvent event = new StaEvent(); + event.type = StaEvent.TYPE_LINK_PROBE; + event.linkProbeWasSuccess = true; + event.linkProbeSuccessElapsedTimeMs = elapsedTimeMs; + addStaEvent(event); + } + mLinkProbeStaEventCount++; } } /** * Reports stats for an unsuccessful link probe. * - * @param startTimestampMs The wall clock time when the link probe was started, in ms. * @param timeSinceLastTxSuccessMs At {@code startTimestampMs}, the number of milliseconds since * the last Tx success (according to * {@link WifiInfo#txSuccess}). @@ -4728,7 +4732,7 @@ public class WifiMetrics { * @param linkSpeed The Tx link speed in Mbps at {@code startTimestampMs}. * @param reason The error code for the failure. See {@link WifiNative.SendMgmtFrameError}. */ - public void logLinkProbeFailure(long startTimestampMs, long timeSinceLastTxSuccessMs, + public void logLinkProbeFailure(long timeSinceLastTxSuccessMs, int rssi, int linkSpeed, @WifiNative.SendMgmtFrameError int reason) { synchronized (mLock) { mProbeStatusSinceLastUpdate = @@ -4741,12 +4745,14 @@ public class WifiMetrics { mLinkProbeFailureLinkSpeedCounts.increment(linkSpeed); mLinkProbeFailureReasonCounts.increment(reason); - StaEvent event = new StaEvent(); - event.type = StaEvent.TYPE_LINK_PROBE; - event.linkProbeWasSuccess = false; - event.linkProbeFailureReason = linkProbeFailureReasonToProto(reason); - // TODO(129958996): Cap number of link probe StaEvents - addStaEvent(event); + if (mLinkProbeStaEventCount < MAX_LINK_PROBE_STA_EVENTS) { + StaEvent event = new StaEvent(); + event.type = StaEvent.TYPE_LINK_PROBE; + event.linkProbeWasSuccess = false; + event.linkProbeFailureReason = linkProbeFailureReasonToProto(reason); + addStaEvent(event); + } + mLinkProbeStaEventCount++; } } diff --git a/service/java/com/android/server/wifi/util/TimedQuotaManager.java b/service/java/com/android/server/wifi/util/TimedQuotaManager.java new file mode 100644 index 000000000..bf78e9bc8 --- /dev/null +++ b/service/java/com/android/server/wifi/util/TimedQuotaManager.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2019 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.util; + +import com.android.server.wifi.Clock; + +/** + * Manages a quota that is reset at the beginning of each new time period. + */ +public class TimedQuotaManager { + private final Clock mClock; + + private final long mQuota; + private final long mPeriodMillis; + private final long mStartTimeMillis; + + /** + * The number of elapsed periods between {@link #mStartTimeMillis} and the time of the last call + * to {@link #requestQuota()}. + */ + private long mLastPeriod; + /** How much quota has been consumed in the current period. */ + private long mConsumedQuota; + + /** + * Constructor. + * @param clock Clock instance. + * @param quota the maximum quota for a given period. + * @param periodMillis the quota will be reset at the beginning of each new period. + */ + public TimedQuotaManager(Clock clock, long quota, long periodMillis) { + mClock = clock; + mQuota = quota; + mPeriodMillis = periodMillis; + mStartTimeMillis = clock.getElapsedSinceBootMillis(); + mLastPeriod = 0; + mConsumedQuota = 0; + } + + /** + * Requests one quota. If there is sufficient remaining quota for the current period, + * returns true and consumes one quota. Otherwise, returns false. + */ + public boolean requestQuota() { + long currentPeriod = getCurrentPeriod(); + if (mLastPeriod < currentPeriod) { + mLastPeriod = currentPeriod; + mConsumedQuota = 0; + } + if (mConsumedQuota < mQuota) { + mConsumedQuota++; + return true; + } + return false; + } + + private long getCurrentPeriod() { + return (mClock.getElapsedSinceBootMillis() - mStartTimeMillis) / mPeriodMillis; + } + + @Override + public String toString() { + return "TimedQuotaManager{" + + "mQuota=" + mQuota + + ", mPeriodMillis=" + mPeriodMillis + + ", mStartTimeMillis=" + mStartTimeMillis + + ", mLastPeriod=" + mLastPeriod + + ", mConsumedQuota=" + mConsumedQuota + + '}'; + } +} diff --git a/tests/wifitests/Android.mk b/tests/wifitests/Android.mk index 9b44ecedf..8e48f0c7f 100644 --- a/tests/wifitests/Android.mk +++ b/tests/wifitests/Android.mk @@ -59,6 +59,7 @@ LOCAL_STATIC_JAVA_LIBRARIES := \ frameworks-base-testutils \ services \ wifi-service \ + truth-prebuilt \ LOCAL_JAVA_LIBRARIES := \ android.test.runner \ diff --git a/tests/wifitests/src/com/android/server/wifi/LinkProbeManagerTest.java b/tests/wifitests/src/com/android/server/wifi/LinkProbeManagerTest.java index 35f63149c..6884c03c0 100644 --- a/tests/wifitests/src/com/android/server/wifi/LinkProbeManagerTest.java +++ b/tests/wifitests/src/com/android/server/wifi/LinkProbeManagerTest.java @@ -23,6 +23,7 @@ import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; @@ -54,7 +55,6 @@ public class LinkProbeManagerTest { private static final String TEST_IFACE_NAME = "testIfaceName"; private static final String TEST_BSSID = "6c:f3:7f:ae:8c:f3"; private static final int TEST_ELAPSED_TIME_MS = 100; - private static final long TEST_TIMESTAMP_MS = 1547837434690L; private LinkProbeManager mLinkProbeManager; @@ -107,8 +107,6 @@ public class LinkProbeManagerTest { */ @Test public void testLinkProbeTriggeredAndAcked() throws Exception { - mLinkProbeManager.resetOnNewConnection(); - // initialize tx success counter mWifiInfo.txSuccess = 50; mTimeMs += 3000; @@ -121,16 +119,15 @@ public class LinkProbeManagerTest { // tx success counter did not change since last update mWifiInfo.txSuccess = 50; // below RSSI threshold - int rssi = LinkProbeManager.LINK_PROBE_RSSI_THRESHOLD - 5; + int rssi = LinkProbeManager.RSSI_THRESHOLD - 5; mWifiInfo.setRssi(rssi); // above link speed threshold - int linkSpeed = LinkProbeManager.LINK_PROBE_LINK_SPEED_THRESHOLD_MBPS + 10; + int linkSpeed = LinkProbeManager.LINK_SPEED_THRESHOLD_MBPS + 10; mWifiInfo.setLinkSpeed(linkSpeed); - // more than LINK_PROBE_INTERVAL_MS passed - long timeDelta = LinkProbeManager.LINK_PROBE_INTERVAL_MS + 1000; + // more than DELAY_AFTER_TX_SUCCESS_MS passed + long timeDelta = LinkProbeManager.DELAY_AFTER_TX_SUCCESS_MS + 20000; mTimeMs += timeDelta; when(mClock.getElapsedSinceBootMillis()).thenReturn(mTimeMs); - when(mClock.getWallClockMillis()).thenReturn(TEST_TIMESTAMP_MS); mLinkProbeManager.updateConnectionStats(mWifiInfo, TEST_IFACE_NAME); ArgumentCaptor<WifiNative.SendMgmtFrameCallback> callbackCaptor = ArgumentCaptor.forClass(WifiNative.SendMgmtFrameCallback.class); @@ -139,13 +136,13 @@ public class LinkProbeManagerTest { ArgumentCaptor<String> experimentIdCaptor = ArgumentCaptor.forClass(String.class); verify(mWifiMetrics, atLeastOnce()).incrementLinkProbeExperimentProbeCount( experimentIdCaptor.capture()); - int len = LinkProbeManager.EXPERIMENT_DELAYS_MS.length; - int numExperimentIds = len * len * len; + int numExperimentIds = LinkProbeManager.EXPERIMENT_DELAYS_MS.length + * LinkProbeManager.EXPERIMENT_RSSIS.length + * LinkProbeManager.EXPERIMENT_LINK_SPEEDS.length; assertEquals(numExperimentIds, new HashSet<>(experimentIdCaptor.getAllValues()).size()); callbackCaptor.getValue().onAck(TEST_ELAPSED_TIME_MS); - verify(mWifiMetrics).logLinkProbeSuccess(TEST_TIMESTAMP_MS, timeDelta, rssi, linkSpeed, - TEST_ELAPSED_TIME_MS); + verify(mWifiMetrics).logLinkProbeSuccess(timeDelta, rssi, linkSpeed, TEST_ELAPSED_TIME_MS); } /** @@ -154,8 +151,6 @@ public class LinkProbeManagerTest { */ @Test public void testLinkProbeTriggeredAndFailed() throws Exception { - mLinkProbeManager.resetOnNewConnection(); - // initialize tx success counter mWifiInfo.txSuccess = 50; mTimeMs += 3000; @@ -167,16 +162,15 @@ public class LinkProbeManagerTest { // tx success counter did not change since last update mWifiInfo.txSuccess = 50; // above RSSI threshold - int rssi = LinkProbeManager.LINK_PROBE_RSSI_THRESHOLD + 5; + int rssi = LinkProbeManager.RSSI_THRESHOLD + 5; mWifiInfo.setRssi(rssi); // below link speed threshold - int linkSpeed = LinkProbeManager.LINK_PROBE_LINK_SPEED_THRESHOLD_MBPS - 2; + int linkSpeed = LinkProbeManager.LINK_SPEED_THRESHOLD_MBPS - 2; mWifiInfo.setLinkSpeed(linkSpeed); - // more than LINK_PROBE_INTERVAL_MS passed - long timeDelta = LinkProbeManager.LINK_PROBE_INTERVAL_MS + 1000; + // more than DELAY_BETWEEN_PROBES_MS passed + long timeDelta = LinkProbeManager.DELAY_BETWEEN_PROBES_MS + 1000; mTimeMs += timeDelta; when(mClock.getElapsedSinceBootMillis()).thenReturn(mTimeMs); - when(mClock.getWallClockMillis()).thenReturn(TEST_TIMESTAMP_MS); mLinkProbeManager.updateConnectionStats(mWifiInfo, TEST_IFACE_NAME); ArgumentCaptor<WifiNative.SendMgmtFrameCallback> callbackCaptor = ArgumentCaptor.forClass(WifiNative.SendMgmtFrameCallback.class); @@ -184,12 +178,12 @@ public class LinkProbeManagerTest { anyInt()); callbackCaptor.getValue().onFailure(WifiNative.SEND_MGMT_FRAME_ERROR_NO_ACK); - verify(mWifiMetrics).logLinkProbeFailure(TEST_TIMESTAMP_MS, timeDelta, rssi, linkSpeed, + verify(mWifiMetrics).logLinkProbeFailure(timeDelta, rssi, linkSpeed, WifiNative.SEND_MGMT_FRAME_ERROR_NO_ACK); } /** - * Tests that link probing is not triggered more than once every LINK_PROBE_INTERVAL_MS + * Tests that link probing is not triggered more than once every DELAY_BETWEEN_PROBES_MS */ @Test public void testLinkProbeNotTriggeredTooFrequently() throws Exception { @@ -198,11 +192,11 @@ public class LinkProbeManagerTest { // tx success counter did not change since last update mWifiInfo.txSuccess = 50; // below RSSI threshold - mWifiInfo.setRssi(LinkProbeManager.LINK_PROBE_RSSI_THRESHOLD - 5); + mWifiInfo.setRssi(LinkProbeManager.RSSI_THRESHOLD - 5); // above link speed threshold - mWifiInfo.setLinkSpeed(LinkProbeManager.LINK_PROBE_LINK_SPEED_THRESHOLD_MBPS + 10); - // *** but less than LINK_PROBE_INTERVAL_MS has passed since last probe *** - mTimeMs += LinkProbeManager.LINK_PROBE_INTERVAL_MS - 1000; + mWifiInfo.setLinkSpeed(LinkProbeManager.LINK_SPEED_THRESHOLD_MBPS + 10); + // *** but less than DELAY_BETWEEN_PROBES_MS has passed since last probe *** + mTimeMs += LinkProbeManager.DELAY_BETWEEN_PROBES_MS - 1000; when(mClock.getElapsedSinceBootMillis()).thenReturn(mTimeMs); mLinkProbeManager.updateConnectionStats(mWifiInfo, TEST_IFACE_NAME); // should not probe @@ -211,12 +205,10 @@ public class LinkProbeManagerTest { /** * Tests that link probing is not triggered when Tx has succeeded within the last - * LINK_PROBE_INTERVAL_MS. + * DELAY_AFTER_TX_SUCCESS_MS. */ @Test public void testLinkProbeNotTriggeredWhenTxSucceeded() throws Exception { - mLinkProbeManager.resetOnNewConnection(); - // initialize tx success counter mWifiInfo.txSuccess = 50; mTimeMs += 3000; @@ -236,17 +228,67 @@ public class LinkProbeManagerTest { // tx success counter did not change since last update mWifiInfo.txSuccess = 55; // below RSSI threshold - mWifiInfo.setRssi(LinkProbeManager.LINK_PROBE_RSSI_THRESHOLD - 5); + mWifiInfo.setRssi(LinkProbeManager.RSSI_THRESHOLD - 5); // above link speed threshold - mWifiInfo.setLinkSpeed(LinkProbeManager.LINK_PROBE_LINK_SPEED_THRESHOLD_MBPS + 10); - // *** but less than LINK_PROBE_INTERVAL_MS has passed since last tx success *** - mTimeMs += LinkProbeManager.LINK_PROBE_INTERVAL_MS - 1000; + mWifiInfo.setLinkSpeed(LinkProbeManager.LINK_SPEED_THRESHOLD_MBPS + 10); + // *** but less than DELAY_AFTER_TX_SUCCESS_MS has passed since last tx success *** + mTimeMs += LinkProbeManager.DELAY_AFTER_TX_SUCCESS_MS - 1000; when(mClock.getElapsedSinceBootMillis()).thenReturn(mTimeMs); mLinkProbeManager.updateConnectionStats(mWifiInfo, TEST_IFACE_NAME); verify(mWifiNative, never()).probeLink(any(), any(), any(), anyInt()); } /** + * Tests that link probing is not triggered when screen was turned on within the last + * {@link LinkProbeManager#SCREEN_ON_DELAY_MS}. + */ + @Test + public void testLinkProbeNotTriggeredWhenScreenJustTurnedOn() throws Exception { + mTimeMs += 30 * 1000; + when(mClock.getElapsedSinceBootMillis()).thenReturn(mTimeMs); + mLinkProbeManager.resetOnScreenTurnedOn(); + // should not probe yet + verify(mWifiNative, never()).probeLink(any(), any(), any(), anyInt()); + + // tx success counter did not change since initialization + mWifiInfo.txSuccess = 0; + // below RSSI threshold + mWifiInfo.setRssi(LinkProbeManager.RSSI_THRESHOLD - 5); + // above link speed threshold + mWifiInfo.setLinkSpeed(LinkProbeManager.LINK_SPEED_THRESHOLD_MBPS + 10); + // *** but less than SCREEN_ON_DELAY_MS has passed since last screen on *** + mTimeMs += LinkProbeManager.SCREEN_ON_DELAY_MS - 1000; + when(mClock.getElapsedSinceBootMillis()).thenReturn(mTimeMs); + mLinkProbeManager.updateConnectionStats(mWifiInfo, TEST_IFACE_NAME); + verify(mWifiNative, never()).probeLink(any(), any(), any(), anyInt()); + } + + /** + * Tests that link probing is triggered when screen was turned on more than + * {@link LinkProbeManager#SCREEN_ON_DELAY_MS} ago. + */ + @Test + public void testLinkProbeTriggeredAfterScreenTurnedOn() throws Exception { + mTimeMs += 30 * 1000; + when(mClock.getElapsedSinceBootMillis()).thenReturn(mTimeMs); + mLinkProbeManager.resetOnScreenTurnedOn(); + // should not probe yet + verify(mWifiNative, never()).probeLink(any(), any(), any(), anyInt()); + + // tx success counter did not change since initialization + mWifiInfo.txSuccess = 0; + // below RSSI threshold + mWifiInfo.setRssi(LinkProbeManager.RSSI_THRESHOLD - 5); + // above link speed threshold + mWifiInfo.setLinkSpeed(LinkProbeManager.LINK_SPEED_THRESHOLD_MBPS + 10); + // *** more than SCREEN_ON_DELAY_MS has passed since last screen on *** + mTimeMs += LinkProbeManager.SCREEN_ON_DELAY_MS + 1000; + when(mClock.getElapsedSinceBootMillis()).thenReturn(mTimeMs); + mLinkProbeManager.updateConnectionStats(mWifiInfo, TEST_IFACE_NAME); + verify(mWifiNative).probeLink(eq(TEST_IFACE_NAME), any(), any(), anyInt()); + } + + /** * Tests when link probing feature flag is disabled, no probes should run. */ @Test @@ -255,8 +297,6 @@ public class LinkProbeManagerTest { eq(Settings.Global.WIFI_LINK_PROBING_ENABLED), anyInt())).thenReturn(0); mContentObserver.onChange(false); - mLinkProbeManager.resetOnNewConnection(); - // initialize tx success counter mWifiInfo.txSuccess = 50; mTimeMs += 3000; @@ -268,16 +308,15 @@ public class LinkProbeManagerTest { // tx success counter did not change since last update mWifiInfo.txSuccess = 50; // below RSSI threshold - int rssi = LinkProbeManager.LINK_PROBE_RSSI_THRESHOLD - 5; + int rssi = LinkProbeManager.RSSI_THRESHOLD - 5; mWifiInfo.setRssi(rssi); // above link speed threshold - int linkSpeed = LinkProbeManager.LINK_PROBE_LINK_SPEED_THRESHOLD_MBPS + 10; + int linkSpeed = LinkProbeManager.LINK_SPEED_THRESHOLD_MBPS + 10; mWifiInfo.setLinkSpeed(linkSpeed); - // more than LINK_PROBE_INTERVAL_MS passed - long timeDelta = LinkProbeManager.LINK_PROBE_INTERVAL_MS + 1000; + // more than DELAY_AFTER_TX_SUCCESS_MS passed + long timeDelta = LinkProbeManager.DELAY_AFTER_TX_SUCCESS_MS + 1000; mTimeMs += timeDelta; when(mClock.getElapsedSinceBootMillis()).thenReturn(mTimeMs); - when(mClock.getWallClockMillis()).thenReturn(TEST_TIMESTAMP_MS); mLinkProbeManager.updateConnectionStats(mWifiInfo, TEST_IFACE_NAME); verify(mWifiNative, never()).probeLink(any() , any(), any(), anyInt()); } @@ -290,8 +329,6 @@ public class LinkProbeManagerTest { mResources.setBoolean(R.bool.config_wifi_link_probing_supported, false); initLinkProbeManager(); - mLinkProbeManager.resetOnNewConnection(); - // initialize tx success counter mWifiInfo.txSuccess = 50; mTimeMs += 3000; @@ -303,17 +340,73 @@ public class LinkProbeManagerTest { // tx success counter did not change since last update mWifiInfo.txSuccess = 50; // below RSSI threshold - int rssi = LinkProbeManager.LINK_PROBE_RSSI_THRESHOLD - 5; + int rssi = LinkProbeManager.RSSI_THRESHOLD - 5; mWifiInfo.setRssi(rssi); // above link speed threshold - int linkSpeed = LinkProbeManager.LINK_PROBE_LINK_SPEED_THRESHOLD_MBPS + 10; + int linkSpeed = LinkProbeManager.LINK_SPEED_THRESHOLD_MBPS + 10; mWifiInfo.setLinkSpeed(linkSpeed); - // more than LINK_PROBE_INTERVAL_MS passed - long timeDelta = LinkProbeManager.LINK_PROBE_INTERVAL_MS + 1000; + // more than DELAY_AFTER_TX_SUCCESS_MS passed + long timeDelta = LinkProbeManager.DELAY_AFTER_TX_SUCCESS_MS + 1000; mTimeMs += timeDelta; when(mClock.getElapsedSinceBootMillis()).thenReturn(mTimeMs); - when(mClock.getWallClockMillis()).thenReturn(TEST_TIMESTAMP_MS); mLinkProbeManager.updateConnectionStats(mWifiInfo, TEST_IFACE_NAME); verify(mWifiNative, never()).probeLink(any() , any(), any(), anyInt()); } + + /** + * Tests exhausting the daily link probe quota and verify that no more link probes are made + * after the limit is reached. Tests that the quota is reset upon entering a new day. + */ + @Test + public void testLinkProbeQuotaExceeded() { + mTimeMs += 30 * 1000; + when(mClock.getElapsedSinceBootMillis()).thenReturn(mTimeMs); + // should not probe yet + verify(mWifiNative, never()).probeLink(any(), any(), any(), anyInt()); + + // tx success counter did not change since initialization + mWifiInfo.txSuccess = 0; + // below RSSI threshold + mWifiInfo.setRssi(LinkProbeManager.RSSI_THRESHOLD - 5); + // above link speed threshold + mWifiInfo.setLinkSpeed(LinkProbeManager.LINK_SPEED_THRESHOLD_MBPS + 10); + + // exhaust quota + for (int i = 1; i <= LinkProbeManager.MAX_PROBE_COUNT_IN_PERIOD; i++) { + mTimeMs += LinkProbeManager.DELAY_BETWEEN_PROBES_MS + 1000; + when(mClock.getElapsedSinceBootMillis()).thenReturn(mTimeMs); + mLinkProbeManager.updateConnectionStats(mWifiInfo, TEST_IFACE_NAME); + verify(mWifiNative, times(i)) + .probeLink(eq(TEST_IFACE_NAME), any(), any(), anyInt()); + } + // verify no more quota + for (int i = 0; i < 10; i++) { + mTimeMs += LinkProbeManager.DELAY_BETWEEN_PROBES_MS + 1000; + when(mClock.getElapsedSinceBootMillis()).thenReturn(mTimeMs); + mLinkProbeManager.updateConnectionStats(mWifiInfo, TEST_IFACE_NAME); + verify(mWifiNative, times((int) LinkProbeManager.MAX_PROBE_COUNT_IN_PERIOD)) + .probeLink(eq(TEST_IFACE_NAME), any(), any(), anyInt()); + } + + // start new period + mTimeMs += LinkProbeManager.PERIOD_MILLIS + 1000; + when(mClock.getElapsedSinceBootMillis()).thenReturn(mTimeMs); + + // exhaust quota again + for (int i = 1; i <= LinkProbeManager.MAX_PROBE_COUNT_IN_PERIOD; i++) { + mTimeMs += LinkProbeManager.DELAY_BETWEEN_PROBES_MS + 1000; + when(mClock.getElapsedSinceBootMillis()).thenReturn(mTimeMs); + mLinkProbeManager.updateConnectionStats(mWifiInfo, TEST_IFACE_NAME); + verify(mWifiNative, times((int) (LinkProbeManager.MAX_PROBE_COUNT_IN_PERIOD + i))) + .probeLink(eq(TEST_IFACE_NAME), any(), any(), anyInt()); + } + // verify no more quota again + for (int i = 0; i < 10; i++) { + mTimeMs += LinkProbeManager.DELAY_BETWEEN_PROBES_MS + 1000; + when(mClock.getElapsedSinceBootMillis()).thenReturn(mTimeMs); + mLinkProbeManager.updateConnectionStats(mWifiInfo, TEST_IFACE_NAME); + verify(mWifiNative, times((int) (2 * LinkProbeManager.MAX_PROBE_COUNT_IN_PERIOD))) + .probeLink(eq(TEST_IFACE_NAME), any(), any(), anyInt()); + } + } } diff --git a/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java b/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java index 88ff48838..e45906a85 100644 --- a/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java +++ b/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java @@ -33,6 +33,7 @@ import static com.android.server.wifi.WifiMetricsTestUtil.buildInt32Count; import static com.android.server.wifi.WifiMetricsTestUtil.buildLinkProbeFailureReasonCount; import static com.android.server.wifi.WifiMetricsTestUtil.buildLinkProbeFailureStaEvent; import static com.android.server.wifi.WifiMetricsTestUtil.buildLinkProbeSuccessStaEvent; +import static com.android.server.wifi.nano.WifiMetricsProto.StaEvent.TYPE_LINK_PROBE; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -115,6 +116,7 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import java.io.StringWriter; import java.util.ArrayList; +import java.util.Arrays; import java.util.BitSet; import java.util.HashMap; import java.util.List; @@ -1953,6 +1955,29 @@ public class WifiMetricsTest { } /** + * Tests that link probe StaEvents do not exceed + * {@link WifiMetrics#MAX_LINK_PROBE_STA_EVENTS}. + */ + @Test + public void testLinkProbeStaEventBounding() throws Exception { + for (int i = 0; i < WifiMetrics.MAX_LINK_PROBE_STA_EVENTS; i++) { + mWifiMetrics.logLinkProbeSuccess(0, 0, 0, 0); + mWifiMetrics.logLinkProbeFailure(0, 0, 0, 0); + } + for (int i = 0; i < 10; i++) { + mWifiMetrics.logStaEvent(StaEvent.TYPE_CMD_START_CONNECT); + } + + dumpProtoAndDeserialize(); + + long numLinkProbeStaEvents = Arrays.stream(mDecodedProto.staEventList) + .filter(event -> event.type == TYPE_LINK_PROBE) + .count(); + assertEquals(WifiMetrics.MAX_LINK_PROBE_STA_EVENTS, numLinkProbeStaEvents); + assertEquals(WifiMetrics.MAX_LINK_PROBE_STA_EVENTS + 10, mDecodedProto.staEventList.length); + } + + /** * Ensure WifiMetrics doesn't cause a null pointer exception when called with null args */ @Test @@ -2803,12 +2828,11 @@ public class WifiMetricsTest { WifiLinkLayerStats stats2 = nextRandomStats(stats1); mWifiMetrics.incrementWifiScoreCount(60); mWifiMetrics.incrementWifiUsabilityScoreCount(2, 55, 15); - mWifiMetrics.logLinkProbeSuccess(nextRandInt(), nextRandInt(), nextRandInt(), - nextRandInt(), 12); + mWifiMetrics.logLinkProbeSuccess(nextRandInt(), nextRandInt(), nextRandInt(), 12); mWifiMetrics.updateWifiUsabilityStatsEntries(info, stats1); mWifiMetrics.incrementWifiScoreCount(58); mWifiMetrics.incrementWifiUsabilityScoreCount(3, 56, 15); - mWifiMetrics.logLinkProbeFailure(nextRandInt(), nextRandInt(), nextRandInt(), + mWifiMetrics.logLinkProbeFailure(nextRandInt(), nextRandInt(), nextRandInt(), nextRandInt()); mWifiMetrics.enterDeviceMobilityState(DEVICE_MOBILITY_STATE_HIGH_MVMT); @@ -3375,14 +3399,14 @@ public class WifiMetricsTest { */ @Test public void testLogLinkProbeMetrics() throws Exception { - mWifiMetrics.logLinkProbeSuccess(1000, 10000, -75, 50, 5); - mWifiMetrics.logLinkProbeFailure(2000, 30000, -80, 10, + mWifiMetrics.logLinkProbeSuccess(10000, -75, 50, 5); + mWifiMetrics.logLinkProbeFailure(30000, -80, 10, WifiNative.SEND_MGMT_FRAME_ERROR_NO_ACK); - mWifiMetrics.logLinkProbeSuccess(3000, 3000, -71, 160, 12); - mWifiMetrics.logLinkProbeFailure(4000, 40000, -80, 6, + mWifiMetrics.logLinkProbeSuccess(3000, -71, 160, 12); + mWifiMetrics.logLinkProbeFailure(40000, -80, 6, WifiNative.SEND_MGMT_FRAME_ERROR_NO_ACK); - mWifiMetrics.logLinkProbeSuccess(5000, 5000, -73, 160, 10); - mWifiMetrics.logLinkProbeFailure(6000, 2000, -78, 6, + mWifiMetrics.logLinkProbeSuccess(5000, -73, 160, 10); + mWifiMetrics.logLinkProbeFailure(2000, -78, 6, WifiNative.SEND_MGMT_FRAME_ERROR_TIMEOUT); dumpProtoAndDeserialize(); diff --git a/tests/wifitests/src/com/android/server/wifi/util/TimedQuotaManagerTest.java b/tests/wifitests/src/com/android/server/wifi/util/TimedQuotaManagerTest.java new file mode 100644 index 000000000..3dcad7cd7 --- /dev/null +++ b/tests/wifitests/src/com/android/server/wifi/util/TimedQuotaManagerTest.java @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2019 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.util; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.when; + +import androidx.test.filters.SmallTest; + +import com.android.server.wifi.Clock; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.time.Duration; + +/** + * Unit tests for {@link TimedQuotaManager}. + */ +@SmallTest +public class TimedQuotaManagerTest { + + private static final long DAY_MILLIS = Duration.ofDays(1).toMillis(); + + @Mock private Clock mClock; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + when(mClock.getElapsedSinceBootMillis()).thenReturn(100000000L); + } + + /** + * Tests that {@link TimedQuotaManager#requestQuota()} returns true before the quota is exceeded + * and returns false after it is exceeded. Tests that upon entering a new time period, the quota + * is reset correctly. + */ + @Test + public void exhaustQuota_newDay_exhaustQuotaAgain() { + TimedQuotaManager qm = new TimedQuotaManager(mClock, 10, DAY_MILLIS); + + for (int i = 0; i < 10; i++) { + assertThat(qm.requestQuota()).isTrue(); + } + for (int i = 0; i < 10; i++) { + assertThat(qm.requestQuota()).isFalse(); + } + + long now = mClock.getElapsedSinceBootMillis(); + when(mClock.getElapsedSinceBootMillis()).thenReturn(now + DAY_MILLIS + 1000); + + for (int i = 0; i < 10; i++) { + assertThat(qm.requestQuota()).isTrue(); + } + for (int i = 0; i < 10; i++) { + assertThat(qm.requestQuota()).isFalse(); + } + } + + /** + * Tests that {@link TimedQuotaManager#requestQuota()} returns true before the quota is exceeded + * and returns false after it is exceeded. Tests when advancing time within the same time + * period, the quota is still enforced. + */ + @Test + public void exhaustQuota_sameDay_stillExhausted() { + TimedQuotaManager qm = new TimedQuotaManager(mClock, 10, DAY_MILLIS); + + for (int i = 0; i < 10; i++) { + assertThat(qm.requestQuota()).isTrue(); + } + for (int i = 0; i < 10; i++) { + assertThat(qm.requestQuota()).isFalse(); + } + + long now = mClock.getElapsedSinceBootMillis(); + when(mClock.getElapsedSinceBootMillis()).thenReturn(now + DAY_MILLIS - 1000); + for (int i = 0; i < 10; i++) { + assertThat(qm.requestQuota()).isFalse(); + } + } +} |