blob: 746976aef168616b4d7ea77df7cfeb4c70bb94a7 [file] [log] [blame]
/*
* 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;
import static junit.framework.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.eq;
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;
import android.content.Context;
import android.net.wifi.WifiInfo;
import android.net.wifi.nl80211.WifiNl80211Manager;
import android.os.Handler;
import android.os.test.TestLooper;
import androidx.test.filters.SmallTest;
import com.android.wifi.resources.R;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.HashSet;
/**
* Unit tests for LinkProbeManager
*/
@SmallTest
public class LinkProbeManagerTest extends WifiBaseTest {
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 LinkProbeManager mLinkProbeManager;
private WifiInfo mWifiInfo;
private long mTimeMs;
@Mock private Clock mClock;
@Mock private WifiNative mWifiNative;
@Mock private WifiMetrics mWifiMetrics;
@Mock private FrameworkFacade mFrameworkFacade;
@Mock private Context mContext;
private TestLooper mLooper = new TestLooper();
private MockResources mResources;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mResources = new MockResources();
mResources.setBoolean(R.bool.config_wifi_link_probing_supported, true);
when(mContext.getResources()).thenReturn(mResources);
initLinkProbeManager();
mWifiInfo = new WifiInfo();
mWifiInfo.setBSSID(TEST_BSSID);
mTimeMs = 1000;
when(mClock.getElapsedSinceBootMillis()).thenReturn(mTimeMs);
// Simulate new connection to trigger check for isSupported resource flag.
mLinkProbeManager.resetOnNewConnection();
mLinkProbeManager.resetOnScreenTurnedOn();
}
private void initLinkProbeManager() {
mLinkProbeManager = new LinkProbeManager(mClock, mWifiNative, mWifiMetrics,
mFrameworkFacade, new Handler(mLooper.getLooper()), mContext);
}
/**
* Tests that link probing is correctly triggered when the required conditions are met, and an
* ACK was received from the AP.
*/
@Test
public void testLinkProbeTriggeredAndAcked() throws Exception {
// initialize tx success counter
mWifiInfo.txSuccess = 50;
mTimeMs += 3000;
when(mClock.getElapsedSinceBootMillis()).thenReturn(mTimeMs);
mLinkProbeManager.updateConnectionStats(mWifiInfo, TEST_IFACE_NAME);
// should not probe yet
verify(mWifiNative, never()).probeLink(any(), any(), any(), anyInt());
verify(mWifiMetrics, never()).incrementLinkProbeExperimentProbeCount(any());
// tx success counter did not change since last update
mWifiInfo.txSuccess = 50;
// below RSSI threshold
int rssi = LinkProbeManager.RSSI_THRESHOLD - 5;
mWifiInfo.setRssi(rssi);
// above link speed threshold
int linkSpeed = LinkProbeManager.LINK_SPEED_THRESHOLD_MBPS + 10;
mWifiInfo.setLinkSpeed(linkSpeed);
// more than DELAY_AFTER_TX_SUCCESS_MS passed
long timeDelta = LinkProbeManager.DELAY_AFTER_TX_SUCCESS_MS + 20000;
mTimeMs += timeDelta;
when(mClock.getElapsedSinceBootMillis()).thenReturn(mTimeMs);
mLinkProbeManager.updateConnectionStats(mWifiInfo, TEST_IFACE_NAME);
ArgumentCaptor<WifiNl80211Manager.SendMgmtFrameCallback> callbackCaptor =
ArgumentCaptor.forClass(WifiNl80211Manager.SendMgmtFrameCallback.class);
verify(mWifiNative).probeLink(eq(TEST_IFACE_NAME), any(), callbackCaptor.capture(),
anyInt());
ArgumentCaptor<String> experimentIdCaptor = ArgumentCaptor.forClass(String.class);
verify(mWifiMetrics, atLeastOnce()).incrementLinkProbeExperimentProbeCount(
experimentIdCaptor.capture());
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(timeDelta, rssi, linkSpeed, TEST_ELAPSED_TIME_MS);
}
/**
* Tests that link probing is correctly triggered when the required conditions are met, but
* no ACK was received from the AP.
*/
@Test
public void testLinkProbeTriggeredAndFailed() throws Exception {
// initialize tx success counter
mWifiInfo.txSuccess = 50;
mTimeMs += 3000;
when(mClock.getElapsedSinceBootMillis()).thenReturn(mTimeMs);
mLinkProbeManager.updateConnectionStats(mWifiInfo, TEST_IFACE_NAME);
// should not probe yet
verify(mWifiNative, never()).probeLink(any(), any(), any(), anyInt());
// tx success counter did not change since last update
mWifiInfo.txSuccess = 50;
// above RSSI threshold
int rssi = LinkProbeManager.RSSI_THRESHOLD + 5;
mWifiInfo.setRssi(rssi);
// below link speed threshold
int linkSpeed = LinkProbeManager.LINK_SPEED_THRESHOLD_MBPS - 2;
mWifiInfo.setLinkSpeed(linkSpeed);
// more than DELAY_BETWEEN_PROBES_MS passed
long timeDelta = LinkProbeManager.DELAY_BETWEEN_PROBES_MS + 1000;
mTimeMs += timeDelta;
when(mClock.getElapsedSinceBootMillis()).thenReturn(mTimeMs);
mLinkProbeManager.updateConnectionStats(mWifiInfo, TEST_IFACE_NAME);
ArgumentCaptor<WifiNl80211Manager.SendMgmtFrameCallback> callbackCaptor =
ArgumentCaptor.forClass(WifiNl80211Manager.SendMgmtFrameCallback.class);
verify(mWifiNative).probeLink(eq(TEST_IFACE_NAME), any(), callbackCaptor.capture(),
anyInt());
callbackCaptor.getValue().onFailure(WifiNl80211Manager.SEND_MGMT_FRAME_ERROR_NO_ACK);
verify(mWifiMetrics).logLinkProbeFailure(timeDelta, rssi, linkSpeed,
WifiNl80211Manager.SEND_MGMT_FRAME_ERROR_NO_ACK);
}
/**
* Tests that link probing is not triggered more than once every DELAY_BETWEEN_PROBES_MS
*/
@Test
public void testLinkProbeNotTriggeredTooFrequently() throws Exception {
testLinkProbeTriggeredAndAcked();
// tx success counter did not change since last update
mWifiInfo.txSuccess = 50;
// below RSSI threshold
mWifiInfo.setRssi(LinkProbeManager.RSSI_THRESHOLD - 5);
// above link speed threshold
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
verifyNoMoreInteractions(mWifiNative);
}
/**
* Tests that link probing is not triggered when Tx has succeeded within the last
* DELAY_AFTER_TX_SUCCESS_MS.
*/
@Test
public void testLinkProbeNotTriggeredWhenTxSucceeded() throws Exception {
// initialize tx success counter
mWifiInfo.txSuccess = 50;
mTimeMs += 3000;
when(mClock.getElapsedSinceBootMillis()).thenReturn(mTimeMs);
mLinkProbeManager.updateConnectionStats(mWifiInfo, TEST_IFACE_NAME);
// should not probe yet
verify(mWifiNative, never()).probeLink(any(), any(), any(), anyInt());
// tx succeeded
mWifiInfo.txSuccess = 55;
mTimeMs += 3000;
when(mClock.getElapsedSinceBootMillis()).thenReturn(mTimeMs);
mLinkProbeManager.updateConnectionStats(mWifiInfo, TEST_IFACE_NAME);
// should not probe yet
verify(mWifiNative, never()).probeLink(any(), any(), any(), anyInt());
// tx success counter did not change since last update
mWifiInfo.txSuccess = 55;
// below RSSI threshold
mWifiInfo.setRssi(LinkProbeManager.RSSI_THRESHOLD - 5);
// above link speed threshold
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 is not supported by the device, no probes should run.
*/
@Test
public void testLinkProbeFeatureUnsupported() throws Exception {
mResources.setBoolean(R.bool.config_wifi_link_probing_supported, false);
initLinkProbeManager();
// initialize tx success counter
mWifiInfo.txSuccess = 50;
mTimeMs += 3000;
when(mClock.getElapsedSinceBootMillis()).thenReturn(mTimeMs);
mLinkProbeManager.updateConnectionStats(mWifiInfo, TEST_IFACE_NAME);
// should not probe yet
verify(mWifiNative, never()).probeLink(any(), any(), any(), anyInt());
// tx success counter did not change since last update
mWifiInfo.txSuccess = 50;
// below RSSI threshold
int rssi = LinkProbeManager.RSSI_THRESHOLD - 5;
mWifiInfo.setRssi(rssi);
// above link speed threshold
int linkSpeed = LinkProbeManager.LINK_SPEED_THRESHOLD_MBPS + 10;
mWifiInfo.setLinkSpeed(linkSpeed);
// more than DELAY_AFTER_TX_SUCCESS_MS passed
long timeDelta = LinkProbeManager.DELAY_AFTER_TX_SUCCESS_MS + 1000;
mTimeMs += timeDelta;
when(mClock.getElapsedSinceBootMillis()).thenReturn(mTimeMs);
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((int) LinkProbeManager.MAX_PROBE_COUNT_IN_PERIOD))
.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) (2 * LinkProbeManager.MAX_PROBE_COUNT_IN_PERIOD)))
.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());
}
}