blob: d48d72a5ebf7b910c17fc18d34e9b99b44398c56 [file] [log] [blame]
/*
* Copyright (C) 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 static com.android.server.wifi.DeviceConfigFacade.DEFAULT_HEALTH_MONITOR_MIN_NUM_CONNECTION_ATTEMPT;
import static com.android.server.wifi.WifiHealthMonitor.REASON_ASSOC_REJECTION;
import static com.android.server.wifi.WifiHealthMonitor.REASON_ASSOC_TIMEOUT;
import static com.android.server.wifi.WifiHealthMonitor.REASON_AUTH_FAILURE;
import static com.android.server.wifi.WifiHealthMonitor.REASON_CONNECTION_FAILURE;
import static com.android.server.wifi.WifiHealthMonitor.REASON_DISCONNECTION_NONLOCAL;
import static com.android.server.wifi.WifiHealthMonitor.REASON_SHORT_CONNECTION_NONLOCAL;
import static com.android.server.wifi.WifiScoreCard.BANDWIDTH_STATS_COUNT_THR;
import static com.android.server.wifi.WifiScoreCard.CNT_ASSOCIATION_REJECTION;
import static com.android.server.wifi.WifiScoreCard.CNT_ASSOCIATION_TIMEOUT;
import static com.android.server.wifi.WifiScoreCard.CNT_AUTHENTICATION_FAILURE;
import static com.android.server.wifi.WifiScoreCard.CNT_CONNECTION_ATTEMPT;
import static com.android.server.wifi.WifiScoreCard.CNT_CONNECTION_DURATION_SEC;
import static com.android.server.wifi.WifiScoreCard.CNT_CONNECTION_FAILURE;
import static com.android.server.wifi.WifiScoreCard.CNT_CONSECUTIVE_CONNECTION_FAILURE;
import static com.android.server.wifi.WifiScoreCard.CNT_DISCONNECTION_NONLOCAL;
import static com.android.server.wifi.WifiScoreCard.CNT_DISCONNECTION_NONLOCAL_CONNECTING;
import static com.android.server.wifi.WifiScoreCard.CNT_SHORT_CONNECTION_NONLOCAL;
import static com.android.server.wifi.WifiScoreCard.LINK_BANDWIDTH_INIT_KBPS;
import static com.android.server.wifi.WifiScoreCard.LINK_RX;
import static com.android.server.wifi.WifiScoreCard.LINK_TX;
import static com.android.server.wifi.util.NativeUtil.hexStringFromByteArray;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
import android.content.Context;
import android.content.res.Resources;
import android.net.MacAddress;
import android.net.wifi.SupplicantState;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiSsid;
import android.util.Base64;
import android.util.Pair;
import androidx.test.filters.SmallTest;
import com.android.server.wifi.WifiHealthMonitor.FailureStats;
import com.android.server.wifi.WifiScoreCard.NetworkConnectionStats;
import com.android.server.wifi.WifiScoreCard.PerNetwork;
import com.android.server.wifi.proto.WifiScoreCardProto.AccessPoint;
import com.android.server.wifi.proto.WifiScoreCardProto.BandwidthStatsAll;
import com.android.server.wifi.proto.WifiScoreCardProto.ConnectionStats;
import com.android.server.wifi.proto.WifiScoreCardProto.Event;
import com.android.server.wifi.proto.WifiScoreCardProto.Network;
import com.android.server.wifi.proto.WifiScoreCardProto.NetworkList;
import com.android.server.wifi.proto.WifiScoreCardProto.NetworkStats;
import com.android.server.wifi.proto.WifiScoreCardProto.Signal;
import com.android.server.wifi.proto.nano.WifiMetricsProto.BandwidthEstimatorStats;
import com.android.server.wifi.util.IntHistogram;
import com.android.server.wifi.util.RssiUtil;
import com.android.wifi.resources.R;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* Unit tests for {@link com.android.server.wifi.WifiScoreCard}.
*/
@SmallTest
public class WifiScoreCardTest extends WifiBaseTest {
static final WifiSsid TEST_SSID_1 = WifiSsid.createFromAsciiEncoded("Joe's Place");
static final WifiSsid TEST_SSID_2 = WifiSsid.createFromAsciiEncoded("Poe's Ravn");
static final MacAddress TEST_BSSID_1 = MacAddress.fromString("aa:bb:cc:dd:ee:ff");
static final MacAddress TEST_BSSID_2 = MacAddress.fromString("1:2:3:4:5:6");
static final int TEST_NETWORK_AGENT_ID = 123;
static final int TEST_NETWORK_CONFIG_ID = 1492;
static final double TOL = 1e-6; // for assertEquals(double, double, tolerance)
static final int TEST_BSSID_FAILURE_REASON =
WifiBlocklistMonitor.REASON_ASSOCIATION_REJECTION;
private static final String WIFI_IFACE_NAME = "wlanTest";
WifiScoreCard mWifiScoreCard;
@Mock Clock mClock;
@Mock WifiScoreCard.MemoryStore mMemoryStore;
@Mock DeviceConfigFacade mDeviceConfigFacade;
@Mock FrameworkFacade mFrameworkFacade;
@Mock Context mContext;
@Mock Resources mResources;
private WifiLinkLayerStats mOldLlStats;
private WifiLinkLayerStats mNewLlStats;
private long mTotalTxBytes;
private long mTotalRxBytes;
final ArrayList<String> mKeys = new ArrayList<>();
final ArrayList<WifiScoreCard.BlobListener> mBlobListeners = new ArrayList<>();
final ArrayList<byte[]> mBlobs = new ArrayList<>();
long mMilliSecondsSinceBoot;
ExtendedWifiInfo mWifiInfo;
void millisecondsPass(long ms) {
mMilliSecondsSinceBoot += ms;
when(mClock.getElapsedSinceBootMillis()).thenReturn(mMilliSecondsSinceBoot);
}
void secondsPass(long s) {
millisecondsPass(s * 1000);
}
/**
* Sets up for unit test
*/
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mKeys.clear();
mBlobListeners.clear();
mBlobs.clear();
mMilliSecondsSinceBoot = 0;
mWifiInfo = new ExtendedWifiInfo(mock(WifiGlobals.class), WIFI_IFACE_NAME);
mWifiInfo.setSSID(TEST_SSID_1);
mWifiInfo.setBSSID(TEST_BSSID_1.toString());
mWifiInfo.setNetworkId(TEST_NETWORK_CONFIG_ID);
mWifiInfo.setMaxSupportedTxLinkSpeedMbps(866);
mWifiInfo.setMaxSupportedRxLinkSpeedMbps(866);
millisecondsPass(0);
mWifiScoreCard = new WifiScoreCard(mClock, "some seed", mDeviceConfigFacade,
mFrameworkFacade, mContext);
mWifiScoreCard.mPersistentHistograms = true; // TODO - remove when ready
when(mDeviceConfigFacade.getConnectionFailureHighThrPercent()).thenReturn(
DeviceConfigFacade.DEFAULT_CONNECTION_FAILURE_HIGH_THR_PERCENT);
when(mDeviceConfigFacade.getConnectionFailureCountMin()).thenReturn(
DeviceConfigFacade.DEFAULT_CONNECTION_FAILURE_COUNT_MIN);
when(mDeviceConfigFacade.getConnectionFailureDisconnectionHighThrPercent()).thenReturn(
DeviceConfigFacade.DEFAULT_CONNECTION_FAILURE_DISCONNECTION_HIGH_THR_PERCENT);
when(mDeviceConfigFacade.getConnectionFailureDisconnectionCountMin()).thenReturn(
DeviceConfigFacade.DEFAULT_CONNECTION_FAILURE_DISCONNECTION_COUNT_MIN);
when(mDeviceConfigFacade.getAssocRejectionHighThrPercent()).thenReturn(
DeviceConfigFacade.DEFAULT_ASSOC_REJECTION_HIGH_THR_PERCENT);
when(mDeviceConfigFacade.getAssocRejectionCountMin()).thenReturn(
DeviceConfigFacade.DEFAULT_ASSOC_REJECTION_COUNT_MIN);
when(mDeviceConfigFacade.getAssocTimeoutHighThrPercent()).thenReturn(
DeviceConfigFacade.DEFAULT_ASSOC_TIMEOUT_HIGH_THR_PERCENT);
when(mDeviceConfigFacade.getAssocTimeoutCountMin()).thenReturn(
DeviceConfigFacade.DEFAULT_ASSOC_TIMEOUT_COUNT_MIN);
when(mDeviceConfigFacade.getAuthFailureHighThrPercent()).thenReturn(
DeviceConfigFacade.DEFAULT_AUTH_FAILURE_HIGH_THR_PERCENT);
when(mDeviceConfigFacade.getAuthFailureCountMin()).thenReturn(
DeviceConfigFacade.DEFAULT_AUTH_FAILURE_COUNT_MIN);
when(mDeviceConfigFacade.getShortConnectionNonlocalHighThrPercent()).thenReturn(
DeviceConfigFacade.DEFAULT_SHORT_CONNECTION_NONLOCAL_HIGH_THR_PERCENT);
when(mDeviceConfigFacade.getShortConnectionNonlocalCountMin()).thenReturn(
DeviceConfigFacade.DEFAULT_SHORT_CONNECTION_NONLOCAL_COUNT_MIN);
when(mDeviceConfigFacade.getDisconnectionNonlocalHighThrPercent()).thenReturn(
DeviceConfigFacade.DEFAULT_DISCONNECTION_NONLOCAL_HIGH_THR_PERCENT);
when(mDeviceConfigFacade.getDisconnectionNonlocalCountMin()).thenReturn(
DeviceConfigFacade.DEFAULT_DISCONNECTION_NONLOCAL_COUNT_MIN);
when(mDeviceConfigFacade.getHealthMonitorMinRssiThrDbm()).thenReturn(
DeviceConfigFacade.DEFAULT_HEALTH_MONITOR_MIN_RSSI_THR_DBM);
when(mDeviceConfigFacade.getHealthMonitorRatioThrNumerator()).thenReturn(
DeviceConfigFacade.DEFAULT_HEALTH_MONITOR_RATIO_THR_NUMERATOR);
when(mDeviceConfigFacade.getHealthMonitorMinNumConnectionAttempt()).thenReturn(
DeviceConfigFacade.DEFAULT_HEALTH_MONITOR_MIN_NUM_CONNECTION_ATTEMPT);
when(mDeviceConfigFacade.getHealthMonitorShortConnectionDurationThrMs()).thenReturn(
DeviceConfigFacade.DEFAULT_HEALTH_MONITOR_SHORT_CONNECTION_DURATION_THR_MS);
when(mDeviceConfigFacade.getAbnormalDisconnectionReasonCodeMask()).thenReturn(
DeviceConfigFacade.DEFAULT_ABNORMAL_DISCONNECTION_REASON_CODE_MASK);
when(mDeviceConfigFacade.getHealthMonitorRssiPollValidTimeMs()).thenReturn(3000);
// Disable FW alert time check by default
when(mDeviceConfigFacade.getHealthMonitorFwAlertValidTimeMs()).thenReturn(-1);
when(mDeviceConfigFacade.getBugReportThresholdExtraRatio()).thenReturn(1);
when(mDeviceConfigFacade.getBandwidthEstimatorLargeTimeConstantSec()).thenReturn(6);
when(mDeviceConfigFacade.getTrafficStatsThresholdMaxKbyte()).thenReturn(4000);
mWifiScoreCard.enableVerboseLogging(true);
when(mFrameworkFacade.getMobileRxBytes()).thenReturn(0L);
when(mFrameworkFacade.getMobileTxBytes()).thenReturn(0L);
when(mFrameworkFacade.getTotalRxBytes()).thenReturn(0L);
when(mFrameworkFacade.getTotalTxBytes()).thenReturn(0L);
when(mContext.getResources()).thenReturn(mResources);
when(mResources.getIntArray(R.array.config_wifiRssiLevelThresholds))
.thenReturn(new int[]{-88, -77, -66, -55});
when(mResources.getInteger(R.integer.config_wifiPollRssiIntervalMilliseconds))
.thenReturn(3000);
mOldLlStats = new WifiLinkLayerStats();
mNewLlStats = new WifiLinkLayerStats();
mTotalTxBytes = 0;
mTotalRxBytes = 0;
}
/**
* Test generic update
*/
@Test
public void testUpdate() throws Exception {
mWifiInfo.setSSID(TEST_SSID_1);
mWifiInfo.setBSSID(TEST_BSSID_1.toString());
mWifiScoreCard.noteIpConfiguration(mWifiInfo);
WifiScoreCard.PerBssid perBssid = mWifiScoreCard.fetchByBssid(TEST_BSSID_1);
assertTrue(perBssid.id > 0);
assertNotNull(perBssid.getL2Key());
assertTrue("L2Key length should be more than 16.", perBssid.getL2Key().length() > 16);
mWifiInfo.setBSSID(TEST_BSSID_2.toString());
mWifiScoreCard.noteIpConfiguration(mWifiInfo);
assertEquals(perBssid, mWifiScoreCard.fetchByBssid(TEST_BSSID_1));
assertNotEquals(perBssid.id, mWifiScoreCard.fetchByBssid(TEST_BSSID_2).id);
assertNotEquals(perBssid.getL2Key(), mWifiScoreCard.fetchByBssid(TEST_BSSID_2).getL2Key());
}
/**
* Test the get, increment, and removal of Bssid blocklist streak counts.
*/
@Test
public void testBssidBlocklistStreakOperations() {
mWifiInfo.setSSID(TEST_SSID_1);
mWifiInfo.setBSSID(TEST_BSSID_1.toString());
mWifiScoreCard.noteIpConfiguration(mWifiInfo);
String ssid = mWifiInfo.getSSID();
String bssid = mWifiInfo.getBSSID();
assertEquals(0, mWifiScoreCard.getBssidBlocklistStreak(
ssid, bssid, TEST_BSSID_FAILURE_REASON));
for (int i = 1; i < 3; i++) {
assertEquals(i, mWifiScoreCard.incrementBssidBlocklistStreak(
ssid, bssid, TEST_BSSID_FAILURE_REASON));
assertEquals(i, mWifiScoreCard.getBssidBlocklistStreak(
ssid, bssid, TEST_BSSID_FAILURE_REASON));
}
mWifiScoreCard.resetBssidBlocklistStreak(ssid, bssid, TEST_BSSID_FAILURE_REASON);
assertEquals(0, mWifiScoreCard.getBssidBlocklistStreak(
ssid, bssid, TEST_BSSID_FAILURE_REASON));
}
/**
* Test clearing the blocklist streak for all APs belonging to a SSID.
*/
@Test
public void testClearBssidBlocklistStreakForSsid() {
// Increment and verify the blocklist streak for SSID_1, BSSID_1
mWifiInfo.setSSID(TEST_SSID_1);
mWifiInfo.setBSSID(TEST_BSSID_1.toString());
mWifiScoreCard.noteIpConfiguration(mWifiInfo);
for (int i = 1; i < 3; i++) {
assertEquals(i, mWifiScoreCard.incrementBssidBlocklistStreak(
mWifiInfo.getSSID(), mWifiInfo.getBSSID(), TEST_BSSID_FAILURE_REASON));
assertEquals(i, mWifiScoreCard.getBssidBlocklistStreak(
mWifiInfo.getSSID(), mWifiInfo.getBSSID(), TEST_BSSID_FAILURE_REASON));
}
// Increment and verify the blocklist streak for SSID_2, BSSID_2
mWifiInfo.setSSID(TEST_SSID_2);
mWifiInfo.setBSSID(TEST_BSSID_2.toString());
mWifiScoreCard.noteIpConfiguration(mWifiInfo);
for (int i = 1; i < 3; i++) {
assertEquals(i, mWifiScoreCard.incrementBssidBlocklistStreak(
mWifiInfo.getSSID(), mWifiInfo.getBSSID(), TEST_BSSID_FAILURE_REASON));
assertEquals(i, mWifiScoreCard.getBssidBlocklistStreak(
mWifiInfo.getSSID(), mWifiInfo.getBSSID(), TEST_BSSID_FAILURE_REASON));
}
// Clear the blocklist streak for SSID_2
mWifiScoreCard.resetBssidBlocklistStreakForSsid(mWifiInfo.getSSID());
// Verify that the blocklist streak for SSID_2 is cleared.
assertEquals(0, mWifiScoreCard.getBssidBlocklistStreak(
mWifiInfo.getSSID(), mWifiInfo.getBSSID(), TEST_BSSID_FAILURE_REASON));
// verify that the blocklist streak for SSID_1 is not cleared.
mWifiInfo.setSSID(TEST_SSID_1);
mWifiInfo.setBSSID(TEST_BSSID_1.toString());
assertEquals(2, mWifiScoreCard.getBssidBlocklistStreak(
mWifiInfo.getSSID(), mWifiInfo.getBSSID(), TEST_BSSID_FAILURE_REASON));
}
/**
* Test the update and retrieval of the last connection time to a BSSID.
*/
@Test
public void testSetBssidConnectionTimestampMs() {
mWifiInfo.setSSID(TEST_SSID_1);
mWifiInfo.setBSSID(TEST_BSSID_1.toString());
mWifiScoreCard.noteIpConfiguration(mWifiInfo);
String ssid = mWifiInfo.getSSID();
String bssid = mWifiInfo.getBSSID();
assertEquals(0L, mWifiScoreCard.getBssidConnectionTimestampMs(ssid, bssid));
assertEquals(0L, mWifiScoreCard.setBssidConnectionTimestampMs(ssid, bssid, 100L));
assertEquals(100L, mWifiScoreCard.getBssidConnectionTimestampMs(ssid, bssid));
}
/**
* Test identifiers.
*/
@Test
public void testIdentifiers() throws Exception {
mWifiInfo.setSSID(TEST_SSID_1);
mWifiInfo.setBSSID(TEST_BSSID_1.toString());
Pair<String, String> p1 = mWifiScoreCard.getL2KeyAndGroupHint(mWifiInfo);
assertNotNull(p1.first);
assertNotNull(p1.second);
mWifiInfo.setBSSID(TEST_BSSID_2.toString());
Pair<String, String> p2 = mWifiScoreCard.getL2KeyAndGroupHint(mWifiInfo);
assertNotEquals(p1.first, p2.first);
assertEquals(p1.second, p2.second);
mWifiInfo.setBSSID(null);
Pair<String, String> p3 = mWifiScoreCard.getL2KeyAndGroupHint(mWifiInfo);
assertNull(p3.first);
assertNull(p3.second);
mWifiInfo.setBSSID("02:00:00:00:00:00");
Pair<String, String> p4 = mWifiScoreCard.getL2KeyAndGroupHint(mWifiInfo);
assertNull(p4.first);
assertNull(p4.second);
}
/**
* Test rssi poll updates
*/
@Test
public void testRssiPollUpdates() throws Exception {
// Start out on one frequency
mWifiInfo.setFrequency(5805);
mWifiInfo.setRssi(-77);
mWifiInfo.setLinkSpeed(12);
mWifiScoreCard.noteSignalPoll(mWifiInfo);
// Switch channels for a bit
mWifiInfo.setFrequency(5290);
mWifiInfo.setRssi(-66);
mWifiInfo.setLinkSpeed(666);
mWifiScoreCard.noteSignalPoll(mWifiInfo);
// Back to the first channel
mWifiInfo.setFrequency(5805);
mWifiInfo.setRssi(-55);
mWifiInfo.setLinkSpeed(86);
mWifiScoreCard.noteSignalPoll(mWifiInfo);
double expectSum = -77 + -55;
double expectSumSq = 77 * 77 + 55 * 55;
// Now verify
WifiScoreCard.PerBssid perBssid = mWifiScoreCard.fetchByBssid(TEST_BSSID_1);
// Looking up the same thing twice should yield the same object.
assertTrue(perBssid.lookupSignal(Event.SIGNAL_POLL, 5805)
== perBssid.lookupSignal(Event.SIGNAL_POLL, 5805));
// Check the rssi statistics for the first channel
assertEquals(2, perBssid.lookupSignal(Event.SIGNAL_POLL, 5805).rssi.count);
assertEquals(expectSum, perBssid.lookupSignal(Event.SIGNAL_POLL, 5805)
.rssi.sum, TOL);
assertEquals(expectSumSq, perBssid.lookupSignal(Event.SIGNAL_POLL, 5805)
.rssi.sumOfSquares, TOL);
assertEquals(-77.0, perBssid.lookupSignal(Event.SIGNAL_POLL, 5805)
.rssi.minValue, TOL);
assertEquals(-55.0, perBssid.lookupSignal(Event.SIGNAL_POLL, 5805)
.rssi.maxValue, TOL);
// Check the rssi statistics for the second channel
assertEquals(1, perBssid.lookupSignal(Event.SIGNAL_POLL, 5290).rssi.count);
// Check that the linkspeed was updated
assertEquals(666.0, perBssid.lookupSignal(Event.SIGNAL_POLL, 5290).linkspeed.sum, TOL);
}
/**
* Statistics on time-to-connect, connection duration
*/
@Test
public void testDurationStatistics() throws Exception {
// Start out disconnected; start connecting
mWifiInfo.setBSSID(android.net.wifi.WifiInfo.DEFAULT_MAC_ADDRESS);
mWifiScoreCard.noteConnectionAttempt(mWifiInfo, -53, mWifiInfo.getSSID());
// First poll has a bad RSSI
millisecondsPass(111);
mWifiInfo.setBSSID(TEST_BSSID_1.toString());
mWifiInfo.setFrequency(5805);
mWifiInfo.setRssi(WifiInfo.INVALID_RSSI);
// A bit later, connection is complete (up through DHCP)
millisecondsPass(222);
mWifiInfo.setRssi(-55);
mWifiScoreCard.noteIpConfiguration(mWifiInfo);
millisecondsPass(666);
// Rssi polls for 99 seconds
for (int i = 0; i < 99; i += 3) {
mWifiScoreCard.noteSignalPoll(mWifiInfo);
secondsPass(3);
}
// Make sure our simulated time adds up
assertEquals(mMilliSecondsSinceBoot, 99999);
// Validation success, rather late!
mWifiScoreCard.noteValidationSuccess(mWifiInfo);
// A long while later, wifi is toggled off
secondsPass(9900);
// Second validation success should not matter.
mWifiScoreCard.noteValidationSuccess(mWifiInfo);
mWifiInfo.setRssi(-88);
mWifiScoreCard.noteIpReachabilityLost(mWifiInfo);
mWifiScoreCard.noteWifiDisabled(mWifiInfo);
// Now verify
WifiScoreCard.PerBssid perBssid = mWifiScoreCard.fetchByBssid(TEST_BSSID_1);
assertEquals(1, perBssid.lookupSignal(Event.IP_CONFIGURATION_SUCCESS, 5805)
.elapsedMs.count);
assertEquals(333.0, perBssid.lookupSignal(Event.IP_CONFIGURATION_SUCCESS, 5805)
.elapsedMs.sum, TOL);
assertEquals(9999999.0, perBssid.lookupSignal(Event.WIFI_DISABLED, 5805)
.elapsedMs.maxValue, TOL);
assertEquals(999.0, perBssid.lookupSignal(Event.FIRST_POLL_AFTER_CONNECTION, 5805)
.elapsedMs.minValue, TOL);
assertEquals(99999.0, perBssid.lookupSignal(Event.VALIDATION_SUCCESS, 5805)
.elapsedMs.sum, TOL);
assertEquals(-88.0, perBssid.lookupSignal(Event.IP_REACHABILITY_LOST, 5805)
.rssi.sum, TOL);
assertNull(perBssid.lookupSignal(Event.SIGNAL_POLL, 5805).elapsedMs);
}
/**
* Firmware roam
*/
@Test
public void testFirmwareRoam() throws Exception {
// Start out disconnected; start connecting
mWifiInfo.setBSSID(android.net.wifi.WifiInfo.DEFAULT_MAC_ADDRESS);
mWifiScoreCard.noteConnectionAttempt(mWifiInfo, -53, mWifiInfo.getSSID());
// First poll has a bad RSSI
millisecondsPass(111);
mWifiInfo.setBSSID(TEST_BSSID_1.toString());
mWifiInfo.setSupplicantState(SupplicantState.COMPLETED);
mWifiInfo.setFrequency(5805);
mWifiInfo.setRssi(WifiInfo.INVALID_RSSI);
// A bit later, connection is complete (up through DHCP)
millisecondsPass(222);
mWifiInfo.setRssi(-55);
mWifiScoreCard.noteIpConfiguration(mWifiInfo);
millisecondsPass(666);
mWifiInfo.setRssi(-77);
// Rssi polls for 99 seconds
for (int i = 0; i < 99; i += 9) {
mWifiScoreCard.noteSignalPoll(mWifiInfo);
secondsPass(9);
}
// Make sure our simulated time adds up
assertEquals(mMilliSecondsSinceBoot, 99999);
// Validation success, rather late!
mWifiScoreCard.noteValidationSuccess(mWifiInfo);
// Simulate a successful roam
mWifiScoreCard.noteSupplicantStateChanging(mWifiInfo, SupplicantState.COMPLETED);
millisecondsPass(1);
mWifiInfo.setBSSID(TEST_BSSID_2.toString());
mWifiInfo.setRssi(-66);
mWifiInfo.setFrequency(2412);
mWifiInfo.setSupplicantState(SupplicantState.COMPLETED);
mWifiScoreCard.noteSupplicantStateChanged(mWifiInfo);
secondsPass(9);
assertEquals(mMilliSecondsSinceBoot, 109000);
mWifiScoreCard.noteSignalPoll(mWifiInfo);
// Simulate an unsuccessful roam
secondsPass(1);
mWifiInfo.setRssi(-74);
mWifiScoreCard.noteSignalPoll(mWifiInfo);
secondsPass(1);
mWifiScoreCard.noteSupplicantStateChanging(mWifiInfo, SupplicantState.COMPLETED);
mWifiInfo.setBSSID(TEST_BSSID_1.toString());
mWifiInfo.setFrequency(5805);
mWifiInfo.setSupplicantState(SupplicantState.COMPLETED);
mWifiScoreCard.noteSupplicantStateChanged(mWifiInfo);
secondsPass(3);
mWifiScoreCard.noteIpReachabilityLost(mWifiInfo);
// Now verify
WifiScoreCard.PerBssid perBssid = mWifiScoreCard.fetchByBssid(TEST_BSSID_1);
assertEquals(1, perBssid.lookupSignal(Event.IP_CONFIGURATION_SUCCESS, 5805)
.elapsedMs.count);
assertEquals(-77, perBssid.lookupSignal(Event.LAST_POLL_BEFORE_ROAM, 5805)
.rssi.minValue, TOL);
assertEquals(1, perBssid.lookupSignal(Event.ROAM_FAILURE, 5805)
.rssi.count);
assertEquals(67, perBssid.estimatePercentInternetAvailability());
perBssid = mWifiScoreCard.fetchByBssid(TEST_BSSID_2);
assertEquals(-66.0, perBssid.lookupSignal(Event.ROAM_SUCCESS, 2412)
.rssi.sum, TOL);
assertEquals(50, perBssid.estimatePercentInternetAvailability());
}
/**
* Constructs a protobuf form of AccessPoint example.
*/
private byte[] makeSerializedAccessPointExample() {
mWifiScoreCard.noteConnectionAttempt(mWifiInfo, -53, mWifiInfo.getSSID());
PerNetwork perNetwork = mWifiScoreCard.lookupNetwork(mWifiInfo.getSSID());
millisecondsPass(10);
// Association completes, a NetworkAgent is created
mWifiScoreCard.noteNetworkAgentCreated(mWifiInfo, TEST_NETWORK_AGENT_ID);
millisecondsPass(101);
mWifiInfo.setRssi(-55);
mWifiInfo.setFrequency(5805);
mWifiInfo.setLinkSpeed(384);
mWifiScoreCard.noteIpConfiguration(mWifiInfo);
perNetwork.addFrequency(mWifiInfo.getFrequency());
millisecondsPass(888);
mWifiScoreCard.noteSignalPoll(mWifiInfo);
millisecondsPass(1000);
mWifiInfo.setRssi(-44);
mWifiScoreCard.noteSignalPoll(mWifiInfo);
mWifiInfo.setFrequency(2432);
perNetwork.addFrequency(mWifiInfo.getFrequency());
for (int round = 0; round < 4; round++) {
for (int i = 0; i < HISTOGRAM_COUNT.length; i++) {
if (HISTOGRAM_COUNT[i] > round) {
mWifiInfo.setRssi(HISTOGRAM_RSSI[i]);
mWifiScoreCard.noteSignalPoll(mWifiInfo);
}
}
}
makeUpdateLinkBandwidthExample();
mWifiScoreCard.resetAllConnectionStates();
WifiScoreCard.PerBssid perBssid = mWifiScoreCard.fetchByBssid(TEST_BSSID_1);
perBssid.lookupSignal(Event.SIGNAL_POLL, 2412).rssi.historicalMean = -42.0;
perBssid.lookupSignal(Event.SIGNAL_POLL, 2412).rssi.historicalVariance = 4.0;
checkSerializationBssidExample("before serialization", perBssid);
// Now convert to protobuf form
byte[] serialized = perBssid.toAccessPoint().toByteArray();
return serialized;
}
private static final int[] HISTOGRAM_RSSI = {-80, -79, -78};
private static final int[] HISTOGRAM_COUNT = {3, 1, 4};
private void checkHistogramExample(String diag, IntHistogram rssiHistogram) {
int i = 0;
for (IntHistogram.Bucket bucket : rssiHistogram) {
if (bucket.count != 0) {
assertTrue(diag, i < HISTOGRAM_COUNT.length);
assertEquals(diag, HISTOGRAM_RSSI[i], bucket.start);
assertEquals(diag, HISTOGRAM_COUNT[i], bucket.count);
i++;
}
}
assertEquals(diag, HISTOGRAM_COUNT.length, i);
}
/**
* Checks that the fields of the bssid serialization example are as expected
*/
private void checkSerializationBssidExample(String diag, WifiScoreCard.PerBssid perBssid) {
assertEquals(diag, 2, perBssid.lookupSignal(Event.SIGNAL_POLL, 5805).rssi.count);
assertEquals(diag, -55.0, perBssid.lookupSignal(Event.SIGNAL_POLL, 5805)
.rssi.minValue, TOL);
assertEquals(diag, -44.0, perBssid.lookupSignal(Event.SIGNAL_POLL, 5805)
.rssi.maxValue, TOL);
assertEquals(diag, 384.0, perBssid.lookupSignal(Event.FIRST_POLL_AFTER_CONNECTION, 5805)
.linkspeed.sum, TOL);
assertEquals(diag, 111.0, perBssid.lookupSignal(Event.IP_CONFIGURATION_SUCCESS, 5805)
.elapsedMs.minValue, TOL);
assertEquals(diag, 0, perBssid.lookupSignal(Event.SIGNAL_POLL, 2412).rssi.count);
assertEquals(diag, -42.0, perBssid.lookupSignal(Event.SIGNAL_POLL, 2412)
.rssi.historicalMean, TOL);
assertEquals(diag, 4.0, perBssid.lookupSignal(Event.SIGNAL_POLL, 2412)
.rssi.historicalVariance, TOL);
checkHistogramExample(diag, perBssid.lookupSignal(Event.SIGNAL_POLL,
2432).rssi.intHistogram);
}
/**
* Checks that the fields of the network are as expected with bssid serialization example
*/
private void checkSerializationBssidExample(String diag, PerNetwork perNetwork) {
NetworkConnectionStats dailyStats = perNetwork.getRecentStats();
assertEquals(diag, 1, dailyStats.getCount(CNT_CONNECTION_ATTEMPT));
assertEquals(diag, 0, dailyStats.getCount(CNT_CONNECTION_FAILURE));
assertEquals(diag, 1, dailyStats.getCount(CNT_CONNECTION_DURATION_SEC));
assertEquals(diag, 0, dailyStats.getCount(CNT_SHORT_CONNECTION_NONLOCAL));
assertEquals(diag, 0, dailyStats.getCount(CNT_DISCONNECTION_NONLOCAL));
assertEquals(diag, 0, dailyStats.getCount(CNT_ASSOCIATION_REJECTION));
assertEquals(diag, 0, dailyStats.getCount(CNT_ASSOCIATION_TIMEOUT));
assertEquals(diag, 0, dailyStats.getCount(CNT_AUTHENTICATION_FAILURE));
List<Integer> frequencies = perNetwork.getFrequencies(Long.MAX_VALUE);
assertEquals(diag, 2, frequencies.size());
List<Integer> expectedFrequencies = new ArrayList<>(Arrays.asList(2432, 5805));
assertEquals(diag, expectedFrequencies, frequencies);
}
/**
* AccessPoint serialization
*/
@Test
public void testAccessPointSerialization() throws Exception {
byte[] serialized = makeSerializedAccessPointExample();
// Verify by parsing it and checking that we see the expected results
AccessPoint ap = AccessPoint.parseFrom(serialized);
assertEquals(5, ap.getEventStatsCount());
for (Signal signal: ap.getEventStatsList()) {
if (signal.getFrequency() == 2412) {
assertFalse(signal.getRssi().hasCount());
assertEquals(-42.0, signal.getRssi().getHistoricalMean(), TOL);
assertEquals(4.0, signal.getRssi().getHistoricalVariance(), TOL);
continue;
}
if (signal.getFrequency() == 2432) {
assertEquals(Event.SIGNAL_POLL, signal.getEvent());
assertEquals(HISTOGRAM_RSSI[2], signal.getRssi().getBuckets(2).getLow());
assertEquals(HISTOGRAM_COUNT[2], signal.getRssi().getBuckets(2).getNumber());
continue;
}
assertEquals(5805, signal.getFrequency());
switch (signal.getEvent()) {
case IP_CONFIGURATION_SUCCESS:
assertEquals(384.0, signal.getLinkspeed().getMaxValue(), TOL);
assertEquals(111.0, signal.getElapsedMs().getMinValue(), TOL);
break;
case SIGNAL_POLL:
assertEquals(2, signal.getRssi().getCount());
break;
case FIRST_POLL_AFTER_CONNECTION:
assertEquals(-55.0, signal.getRssi().getSum(), TOL);
break;
default:
fail(signal.getEvent().toString());
}
}
checkSerializationUpdateLinkBandwidthExample(ap.getBandwidthStatsAll());
}
/**
* Serialization should be reproducible
*/
@Test
public void testReproducableSerialization() throws Exception {
byte[] serialized = makeSerializedAccessPointExample();
setUp();
assertArrayEquals(serialized, makeSerializedAccessPointExample());
}
/**
* AccessPoint Deserialization
*/
@Test
public void testAccessPointDeserialization() throws Exception {
byte[] serialized = makeSerializedAccessPointExample();
setUp(); // Get back to the initial state
WifiScoreCard.PerBssid perBssid = mWifiScoreCard.perBssidFromAccessPoint(
mWifiInfo.getSSID(),
AccessPoint.parseFrom(serialized));
// Now verify
String diag = hexStringFromByteArray(serialized);
checkSerializationBssidExample(diag, perBssid);
}
/**
* Serialization of all internally represented networks
*/
@Test
public void testNetworksSerialization() throws Exception {
makeSerializedAccessPointExample();
byte[] serialized = mWifiScoreCard.getNetworkListByteArray(false);
byte[] cleaned = mWifiScoreCard.getNetworkListByteArray(true);
String base64Encoded = mWifiScoreCard.getNetworkListBase64(true);
setUp(); // Get back to the initial state
String diag = hexStringFromByteArray(serialized);
NetworkList networkList = NetworkList.parseFrom(serialized);
assertEquals(diag, 1, networkList.getNetworksCount());
Network network = networkList.getNetworks(0);
assertEquals(diag, 1, network.getAccessPointsCount());
AccessPoint accessPoint = network.getAccessPoints(0);
WifiScoreCard.PerBssid perBssid = mWifiScoreCard.perBssidFromAccessPoint(network.getSsid(),
accessPoint);
NetworkStats networkStats = network.getNetworkStats();
PerNetwork perNetwork = mWifiScoreCard.perNetworkFromNetworkStats(network.getSsid(),
networkStats);
checkSerializationBssidExample(diag, perBssid);
checkSerializationBssidExample(diag, perNetwork);
// Leaving out the bssids should make the cleaned version shorter.
assertTrue(cleaned.length < serialized.length);
// Check the Base64 version
assertTrue(Arrays.equals(cleaned, Base64.decode(base64Encoded, Base64.DEFAULT)));
// Check that the network ids were carried over
assertEquals(TEST_NETWORK_AGENT_ID, network.getNetworkAgentId());
assertEquals(TEST_NETWORK_CONFIG_ID, network.getNetworkConfigId());
}
/**
* Installation of memory store does not crash
*/
@Test
public void testInstallationOfMemoryStoreDoesNotCrash() throws Exception {
mWifiScoreCard.installMemoryStore(mMemoryStore);
makeSerializedAccessPointExample();
mWifiScoreCard.installMemoryStore(mMemoryStore);
}
/**
* Merge of lazy reads
*/
@Test
public void testLazyReads() {
// Install our own MemoryStore object, which records read requests
mWifiScoreCard.installMemoryStore(new WifiScoreCard.MemoryStore() {
@Override
public void read(String key, String name, WifiScoreCard.BlobListener listener) {
mKeys.add(key);
mBlobListeners.add(listener);
}
@Override
public void write(String key, String name, byte[] value) {
// ignore for now
}
@Override
public void setCluster(String key, String cluster) {
// ignore for now
}
@Override
public void removeCluster(String cluster) {
// ignore for now
}
});
// Now make some changes
byte[] serialized = makeSerializedAccessPointExample();
// 1 for perfBssid and 1 for perNetwork
assertEquals(2, mKeys.size());
// Simulate the asynchronous completion of the read request
millisecondsPass(33);
mBlobListeners.get(0).onBlobRetrieved(serialized);
// Check that the historical mean and variance were updated accordingly
WifiScoreCard.PerBssid perBssid = mWifiScoreCard.fetchByBssid(TEST_BSSID_1);
assertEquals(-42.0, perBssid.lookupSignal(Event.SIGNAL_POLL, 2412)
.rssi.historicalMean, TOL);
assertEquals(4.0, perBssid.lookupSignal(Event.SIGNAL_POLL, 2412)
.rssi.historicalVariance, TOL);
}
/**
* Write test
*/
@Test
public void testWrites() throws Exception {
// Install our own MemoryStore object, which records write requests
mWifiScoreCard.installMemoryStore(new WifiScoreCard.MemoryStore() {
@Override
public void read(String key, String name, WifiScoreCard.BlobListener listener) {
// Just record these, never answer
mBlobListeners.add(listener);
}
@Override
public void write(String key, String name, byte[] value) {
mKeys.add(key);
mBlobs.add(value);
}
@Override
public void setCluster(String key, String cluster) {
}
@Override
public void removeCluster(String cluster) {
// ignore for now
}
});
// Make some changes
byte[] serialized = makeSerializedAccessPointExample();
// 1 for perfBssid and 1 for perNetwork
assertEquals(2, mBlobListeners.size());
secondsPass(33);
// There should be one changed bssid now. We may have already done some writes.
mWifiScoreCard.doWrites();
assertTrue(mKeys.size() > 0);
// The written blob should not contain the BSSID, though the full serialized version does
String writtenHex = hexStringFromByteArray(mBlobs.get(mKeys.size() - 1));
String fullHex = hexStringFromByteArray(serialized);
String bssidHex = hexStringFromByteArray(TEST_BSSID_1.toByteArray());
assertFalse(writtenHex, writtenHex.contains(bssidHex));
assertTrue(fullHex, fullHex.contains(bssidHex));
// A second write request should not find anything to write
final int beforeSize = mKeys.size();
assertEquals(0, mWifiScoreCard.doWrites());
assertEquals(beforeSize, mKeys.size());
}
/**
* Calling doWrites before installing a MemoryStore should do nothing.
*/
@Test
public void testNoWritesUntilReady() throws Exception {
makeSerializedAccessPointExample();
assertEquals(0, mWifiScoreCard.doWrites());
}
/**
* Installing a MemoryStore after startup should issue reads.
*/
@Test
public void testReadAfterDelayedMemoryStoreInstallation() throws Exception {
makeSerializedAccessPointExample();
mWifiScoreCard.installMemoryStore(mMemoryStore);
// 1 for requestReadBssid
verify(mMemoryStore, times(1)).read(any(), any(), any());
}
/**
* Calling clear should forget the state.
*/
@Test
public void testClearReallyDoesClearTheState() throws Exception {
byte[] serialized = makeSerializedAccessPointExample();
assertNotEquals(0, serialized.length);
mWifiScoreCard.clear();
byte[] leftovers = mWifiScoreCard.getNetworkListByteArray(false);
assertEquals(0, leftovers.length);
}
/**
* Test that older items are evicted from memory.
*/
@Test
public void testOlderItemsShouldBeEvicted() throws Exception {
mWifiInfo.setRssi(-55);
mWifiInfo.setFrequency(5805);
mWifiInfo.setLinkSpeed(384);
mWifiScoreCard.installMemoryStore(mMemoryStore);
for (int i = 0; i < 256; i++) {
MacAddress bssid = MacAddress.fromBytes(new byte[]{2, 2, 2, 2, 2, (byte) i});
mWifiInfo.setBSSID(bssid.toString());
mWifiScoreCard.noteSignalPoll(mWifiInfo);
}
// 256 for requestReadBssid() and 1 for requestReadNetwork()
verify(mMemoryStore, times(256 + 1)).read(any(), any(), any());
verify(mMemoryStore, atLeastOnce()).write(any(), any(), any()); // Assumes target size < 256
reset(mMemoryStore);
for (int i = 256 - 3; i < 256; i++) {
MacAddress bssid = MacAddress.fromBytes(new byte[]{2, 2, 2, 2, 2, (byte) i});
mWifiInfo.setBSSID(bssid.toString());
mWifiScoreCard.noteSignalPoll(mWifiInfo);
}
verify(mMemoryStore, never()).read(any(), any(), any()); // Assumes target size >= 3
for (int i = 0; i < 3; i++) {
MacAddress bssid = MacAddress.fromBytes(new byte[]{2, 2, 2, 2, 2, (byte) i});
mWifiInfo.setBSSID(bssid.toString());
mWifiScoreCard.noteSignalPoll(mWifiInfo);
}
verify(mMemoryStore, times(3)).read(any(), any(), any()); // Assumes target size < 253
}
private void makeAssocTimeOutExample() {
mWifiScoreCard.noteConnectionAttempt(mWifiInfo, -53, mWifiInfo.getSSID());
millisecondsPass(1000);
mWifiScoreCard.noteConnectionFailure(mWifiInfo, -53, mWifiInfo.getSSID(),
WifiBlocklistMonitor.REASON_ASSOCIATION_TIMEOUT);
}
private void makeAssocRejectionExample() {
mWifiScoreCard.noteConnectionAttempt(mWifiInfo, -53, mWifiInfo.getSSID());
millisecondsPass(1000);
mWifiScoreCard.noteConnectionFailure(mWifiInfo, -53, mWifiInfo.getSSID(),
WifiBlocklistMonitor.REASON_ASSOCIATION_REJECTION);
}
private void makeApUnableToHandleNewStaExample() {
mWifiScoreCard.noteConnectionAttempt(mWifiInfo, -53, mWifiInfo.getSSID());
millisecondsPass(1000);
mWifiScoreCard.noteConnectionFailure(mWifiInfo, -53, mWifiInfo.getSSID(),
WifiBlocklistMonitor.REASON_AP_UNABLE_TO_HANDLE_NEW_STA);
}
/**
* Check network stats after association timeout.
*/
@Test
public void testNetworkAssocTimeOut() throws Exception {
makeAssocTimeOutExample();
PerNetwork perNetwork = mWifiScoreCard.fetchByNetwork(mWifiInfo.getSSID());
NetworkConnectionStats dailyStats = perNetwork.getRecentStats();
assertEquals(1, dailyStats.getCount(CNT_CONNECTION_ATTEMPT));
assertEquals(1, dailyStats.getCount(CNT_CONNECTION_FAILURE));
assertEquals(0, dailyStats.getCount(CNT_CONNECTION_DURATION_SEC));
assertEquals(0, dailyStats.getCount(CNT_ASSOCIATION_REJECTION));
assertEquals(1, dailyStats.getCount(CNT_ASSOCIATION_TIMEOUT));
assertEquals(0, dailyStats.getCount(CNT_AUTHENTICATION_FAILURE));
assertEquals(1, dailyStats.getCount(CNT_CONSECUTIVE_CONNECTION_FAILURE));
}
/**
* Check network stats after association rejection.
*/
@Test
public void testNetworkAssocRejection() throws Exception {
makeAssocRejectionExample();
makeApUnableToHandleNewStaExample();
PerNetwork perNetwork = mWifiScoreCard.fetchByNetwork(mWifiInfo.getSSID());
NetworkConnectionStats dailyStats = perNetwork.getRecentStats();
assertEquals(2, dailyStats.getCount(CNT_CONNECTION_ATTEMPT));
assertEquals(2, dailyStats.getCount(CNT_CONNECTION_FAILURE));
assertEquals(0, dailyStats.getCount(CNT_CONNECTION_DURATION_SEC));
assertEquals(1, dailyStats.getCount(CNT_ASSOCIATION_REJECTION));
assertEquals(0, dailyStats.getCount(CNT_ASSOCIATION_TIMEOUT));
assertEquals(0, dailyStats.getCount(CNT_AUTHENTICATION_FAILURE));
assertEquals(2, dailyStats.getCount(CNT_CONSECUTIVE_CONNECTION_FAILURE));
}
/**
* Check network stats after auth timeout/disconnection and a normal connection
*/
@Test
public void testAuthTimeoutDisconnection() throws Exception {
makeAuthFailureExample();
mWifiScoreCard.resetAllConnectionStates();
PerNetwork perNetwork = mWifiScoreCard.fetchByNetwork(mWifiInfo.getSSID());
NetworkConnectionStats dailyStats = perNetwork.getRecentStats();
assertEquals(1, dailyStats.getCount(CNT_CONNECTION_ATTEMPT));
assertEquals(1, dailyStats.getCount(CNT_CONNECTION_FAILURE));
assertEquals(0, dailyStats.getCount(CNT_CONNECTION_DURATION_SEC));
assertEquals(0, dailyStats.getCount(CNT_ASSOCIATION_REJECTION));
assertEquals(0, dailyStats.getCount(CNT_ASSOCIATION_TIMEOUT));
assertEquals(1, dailyStats.getCount(CNT_AUTHENTICATION_FAILURE));
assertEquals(1, dailyStats.getCount(CNT_CONSECUTIVE_CONNECTION_FAILURE));
makeNormalConnectionExample();
assertEquals(0, dailyStats.getCount(CNT_CONSECUTIVE_CONNECTION_FAILURE));
}
private void makeAuthFailureAndWrongPassword() {
mWifiScoreCard.noteConnectionAttempt(mWifiInfo, -53, mWifiInfo.getSSID());
millisecondsPass(500);
mWifiScoreCard.noteConnectionFailure(mWifiInfo, -53, mWifiInfo.getSSID(),
WifiBlocklistMonitor.REASON_AUTHENTICATION_FAILURE);
millisecondsPass(1000);
mWifiScoreCard.noteConnectionAttempt(mWifiInfo, -53, mWifiInfo.getSSID());
millisecondsPass(1000);
mWifiScoreCard.noteConnectionFailure(mWifiInfo, -53, mWifiInfo.getSSID(),
WifiBlocklistMonitor.REASON_WRONG_PASSWORD);
}
private void makeAuthFailureExample() {
mWifiScoreCard.noteConnectionAttempt(mWifiInfo, -53, mWifiInfo.getSSID());
millisecondsPass(500);
mWifiScoreCard.noteConnectionFailure(mWifiInfo, -53, mWifiInfo.getSSID(),
WifiBlocklistMonitor.REASON_AUTHENTICATION_FAILURE);
}
/**
* Check network stats after authentication failure and wrong password.
*/
@Test
public void testNetworkAuthenticationFailureWrongPassword() throws Exception {
makeAuthFailureAndWrongPassword();
PerNetwork perNetwork = mWifiScoreCard.fetchByNetwork(mWifiInfo.getSSID());
NetworkConnectionStats dailyStats = perNetwork.getRecentStats();
assertEquals(2, dailyStats.getCount(CNT_CONNECTION_ATTEMPT));
assertEquals(1, dailyStats.getCount(CNT_CONNECTION_FAILURE));
assertEquals(0, dailyStats.getCount(CNT_CONNECTION_DURATION_SEC));
assertEquals(0, dailyStats.getCount(CNT_ASSOCIATION_REJECTION));
assertEquals(0, dailyStats.getCount(CNT_ASSOCIATION_TIMEOUT));
assertEquals(1, dailyStats.getCount(CNT_AUTHENTICATION_FAILURE));
assertEquals(1, dailyStats.getCount(CNT_CONSECUTIVE_CONNECTION_FAILURE));
makeNormalConnectionExample();
assertEquals(0, dailyStats.getCount(CNT_CONSECUTIVE_CONNECTION_FAILURE));
}
private void makeDisconnectionConnectingExample(boolean nonlocal) {
mWifiScoreCard.noteConnectionAttempt(mWifiInfo, -53, mWifiInfo.getSSID());
millisecondsPass(500);
int disconnectionReason = 3;
if (nonlocal) {
mWifiScoreCard.noteNonlocalDisconnect(WIFI_IFACE_NAME, disconnectionReason);
}
mWifiScoreCard.noteConnectionFailure(mWifiInfo, -53, mWifiInfo.getSSID(),
WifiBlocklistMonitor.REASON_NONLOCAL_DISCONNECT_CONNECTING);
millisecondsPass(500);
}
/**
* Check network stats after nonlocal disconnection in middle of connection
*/
@Test
public void testDisconnectionConnecting() throws Exception {
makeDisconnectionConnectingExample(true);
PerNetwork perNetwork = mWifiScoreCard.fetchByNetwork(mWifiInfo.getSSID());
NetworkConnectionStats dailyStats = perNetwork.getRecentStats();
assertEquals(1, dailyStats.getCount(CNT_CONNECTION_ATTEMPT));
assertEquals(1, dailyStats.getCount(CNT_CONNECTION_FAILURE));
assertEquals(1, dailyStats.getCount(CNT_DISCONNECTION_NONLOCAL_CONNECTING));
assertEquals(0, dailyStats.getCount(CNT_CONNECTION_DURATION_SEC));
assertEquals(0, dailyStats.getCount(CNT_ASSOCIATION_REJECTION));
assertEquals(0, dailyStats.getCount(CNT_ASSOCIATION_TIMEOUT));
assertEquals(0, dailyStats.getCount(CNT_AUTHENTICATION_FAILURE));
}
/**
* Check network stats after local disconnection in middle of connection
*/
@Test
public void testLocalDisconnectionConnecting() throws Exception {
makeDisconnectionConnectingExample(false);
PerNetwork perNetwork = mWifiScoreCard.fetchByNetwork(mWifiInfo.getSSID());
NetworkConnectionStats dailyStats = perNetwork.getRecentStats();
assertEquals(1, dailyStats.getCount(CNT_CONNECTION_ATTEMPT));
assertEquals(1, dailyStats.getCount(CNT_CONNECTION_FAILURE));
assertEquals(1, dailyStats.getCount(CNT_DISCONNECTION_NONLOCAL_CONNECTING));
}
/**
* Check network stats when a new connection attempt for SSID2 is issued
* before disconnection of SSID1
*/
@Test
public void testNetworkSwitchWithOverlapping() throws Exception {
// Connect to SSID_1
String ssid1 = mWifiInfo.getSSID();
mWifiScoreCard.noteConnectionAttempt(mWifiInfo, -53, ssid1);
millisecondsPass(5000);
// Attempt to connect to SSID_2
mWifiInfo.setSSID(TEST_SSID_2);
mWifiInfo.setBSSID(TEST_BSSID_2.toString());
String ssid2 = mWifiInfo.getSSID();
mWifiScoreCard.noteConnectionAttempt(mWifiInfo, -53, ssid2);
// Disconnect from SSID_1
millisecondsPass(100);
int disconnectionReason = 4;
mWifiScoreCard.noteNonlocalDisconnect(WIFI_IFACE_NAME, disconnectionReason);
millisecondsPass(100);
mWifiScoreCard.resetConnectionState(WIFI_IFACE_NAME);
// SSID_2 is connected and then disconnected
millisecondsPass(2000);
mWifiScoreCard.noteIpConfiguration(mWifiInfo);
millisecondsPass(2000);
mWifiScoreCard.resetConnectionState(WIFI_IFACE_NAME);
PerNetwork perNetwork = mWifiScoreCard.fetchByNetwork(ssid1);
assertEquals(5, perNetwork.getRecentStats().getCount(CNT_CONNECTION_DURATION_SEC));
perNetwork = mWifiScoreCard.fetchByNetwork(ssid2);
assertEquals(4, perNetwork.getRecentStats().getCount(CNT_CONNECTION_DURATION_SEC));
}
/**
* Check network stats after 2 connection failures at low RSSI.
*/
@Test
public void testNetworkConnectionFailureLowRssi() throws Exception {
mWifiScoreCard.noteConnectionAttempt(mWifiInfo, -83, mWifiInfo.getSSID());
millisecondsPass(1000);
mWifiScoreCard.noteConnectionFailure(mWifiInfo, -83, mWifiInfo.getSSID(),
WifiBlocklistMonitor.REASON_ASSOCIATION_REJECTION);
millisecondsPass(3000);
mWifiScoreCard.noteConnectionAttempt(mWifiInfo, -83, mWifiInfo.getSSID());
millisecondsPass(1000);
mWifiScoreCard.noteConnectionFailure(mWifiInfo, -83, mWifiInfo.getSSID(),
WifiBlocklistMonitor.REASON_ASSOCIATION_REJECTION);
PerNetwork perNetwork = mWifiScoreCard.fetchByNetwork(mWifiInfo.getSSID());
NetworkConnectionStats dailyStats = perNetwork.getRecentStats();
checkShortConnectionExample(dailyStats, 0);
}
private void makeShortConnectionExample(boolean addFwAlert) {
mWifiScoreCard.noteConnectionAttempt(mWifiInfo, -53, mWifiInfo.getSSID());
millisecondsPass(5000);
mWifiInfo.setTxLinkSpeedMbps(100);
mWifiInfo.setSuccessfulTxPacketsPerSecond(20.0);
mWifiInfo.setRetriedTxPacketsRate(1.0);
mWifiInfo.setRssi(-80);
millisecondsPass(1000);
mWifiScoreCard.noteSignalPoll(mWifiInfo);
millisecondsPass(2000);
int disconnectionReason = 34;
mWifiScoreCard.noteNonlocalDisconnect(WIFI_IFACE_NAME, disconnectionReason);
if (addFwAlert) {
mWifiScoreCard.noteFirmwareAlert(6);
}
millisecondsPass(1000);
mWifiScoreCard.resetAllConnectionStates();
}
private void checkShortConnectionExample(NetworkConnectionStats stats, int scale) {
assertEquals(1 * scale, stats.getCount(CNT_CONNECTION_ATTEMPT));
assertEquals(0, stats.getCount(CNT_CONNECTION_FAILURE));
assertEquals(0, stats.getCount(CNT_DISCONNECTION_NONLOCAL_CONNECTING));
assertEquals(9 * scale, stats.getCount(CNT_CONNECTION_DURATION_SEC));
assertEquals(0, stats.getCount(CNT_ASSOCIATION_REJECTION));
assertEquals(0, stats.getCount(CNT_ASSOCIATION_TIMEOUT));
assertEquals(0, stats.getCount(CNT_AUTHENTICATION_FAILURE));
assertEquals(1 * scale, stats.getCount(CNT_SHORT_CONNECTION_NONLOCAL));
assertEquals(1 * scale, stats.getCount(CNT_DISCONNECTION_NONLOCAL));
assertEquals(0, stats.getCount(CNT_CONSECUTIVE_CONNECTION_FAILURE));
}
private void makeShortConnectionOldRssiPollingExample() {
mWifiScoreCard.noteConnectionAttempt(mWifiInfo, -53, mWifiInfo.getSSID());
millisecondsPass(2000);
mWifiInfo.setRssi(-55);
millisecondsPass(1000);
mWifiScoreCard.noteSignalPoll(mWifiInfo);
millisecondsPass(29000);
int disconnectionReason = 3;
mWifiScoreCard.noteNonlocalDisconnect(WIFI_IFACE_NAME, disconnectionReason);
millisecondsPass(1000);
mWifiScoreCard.resetAllConnectionStates();
}
private void checkShortConnectionOldPollingExample(NetworkConnectionStats stats) {
assertEquals(1, stats.getCount(CNT_CONNECTION_ATTEMPT));
assertEquals(0, stats.getCount(CNT_CONNECTION_FAILURE));
assertEquals(0, stats.getCount(CNT_DISCONNECTION_NONLOCAL_CONNECTING));
assertEquals(33, stats.getCount(CNT_CONNECTION_DURATION_SEC));
assertEquals(0, stats.getCount(CNT_ASSOCIATION_REJECTION));
assertEquals(0, stats.getCount(CNT_ASSOCIATION_TIMEOUT));
assertEquals(0, stats.getCount(CNT_AUTHENTICATION_FAILURE));
assertEquals(0, stats.getCount(CNT_SHORT_CONNECTION_NONLOCAL));
assertEquals(0, stats.getCount(CNT_DISCONNECTION_NONLOCAL));
}
/**
* Check network stats after RSSI poll and disconnection, pass FW alert check
*/
@Test
public void testNetworkRssiPollShortNonlocalDisconnectionPassFwAlertCheck() throws Exception {
when(mDeviceConfigFacade.getHealthMonitorFwAlertValidTimeMs()).thenReturn(2000);
// 1st connection session
makeShortConnectionExample(/*addFwAlert*/true);
// 2nd connection session
makeNormalConnectionExample();
PerNetwork perNetwork = mWifiScoreCard.fetchByNetwork(mWifiInfo.getSSID());
NetworkConnectionStats dailyStats = perNetwork.getRecentStats();
assertEquals(2, dailyStats.getCount(CNT_CONNECTION_ATTEMPT));
assertEquals(0, dailyStats.getCount(CNT_CONNECTION_FAILURE));
assertEquals(20, dailyStats.getCount(CNT_CONNECTION_DURATION_SEC));
assertEquals(0, dailyStats.getCount(CNT_ASSOCIATION_REJECTION));
assertEquals(0, dailyStats.getCount(CNT_ASSOCIATION_TIMEOUT));
assertEquals(0, dailyStats.getCount(CNT_AUTHENTICATION_FAILURE));
assertEquals(1, dailyStats.getCount(CNT_SHORT_CONNECTION_NONLOCAL));
assertEquals(1, dailyStats.getCount(CNT_DISCONNECTION_NONLOCAL));
}
/**
* Check network stats after RSSI poll and disconnection, fail FW alert check
*/
@Test
public void testNetworkRssiPollShortNonlocalDisconnectionFailFwAlertCheck() throws Exception {
when(mDeviceConfigFacade.getHealthMonitorFwAlertValidTimeMs()).thenReturn(2000);
// 1st connection session
makeShortConnectionExample(/*addFwAlert*/false);
// 2nd connection session
makeNormalConnectionExample();
PerNetwork perNetwork = mWifiScoreCard.fetchByNetwork(mWifiInfo.getSSID());
NetworkConnectionStats dailyStats = perNetwork.getRecentStats();
assertEquals(0, dailyStats.getCount(CNT_SHORT_CONNECTION_NONLOCAL));
assertEquals(0, dailyStats.getCount(CNT_DISCONNECTION_NONLOCAL));
}
/**
* Check network stats after short connection with an old RSSI polling
*/
@Test
public void testShortNonlocalDisconnectionOldRssiPolling() throws Exception {
makeShortConnectionOldRssiPollingExample();
PerNetwork perNetwork = mWifiScoreCard.fetchByNetwork(mWifiInfo.getSSID());
checkShortConnectionOldPollingExample(perNetwork.getRecentStats());
}
private void makeNormalConnectionExample() {
mWifiScoreCard.noteConnectionAttempt(mWifiInfo, -53, mWifiInfo.getSSID());
millisecondsPass(1000);
mWifiInfo.setRssi(-55);
millisecondsPass(7000);
mWifiScoreCard.noteSignalPoll(mWifiInfo);
millisecondsPass(3000);
mWifiScoreCard.noteIpConfiguration(mWifiInfo);
mWifiScoreCard.resetAllConnectionStates();
}
private void makeUpdateLinkBandwidthExample() {
mWifiInfo.setRssi(-79);
mWifiInfo.setFrequency(2437);
mNewLlStats.on_time = 1000;
mNewLlStats.timeStampInMs = 5_000;
long txBytes = 2_000_000L;
long rxBytes = 4_000_000L;
PerNetwork perNetwork = mWifiScoreCard.lookupNetwork(mWifiInfo.getSSID());
for (int i = 0; i < BANDWIDTH_STATS_COUNT_THR; i++) {
addTotalBytes(txBytes, rxBytes);
perNetwork.updateLinkBandwidth(mOldLlStats, mNewLlStats, mWifiInfo);
}
mWifiInfo.setFrequency(5210);
txBytes = 5_000_000L;
rxBytes = 1000L;
for (int i = 0; i < BANDWIDTH_STATS_COUNT_THR + 2; i++) {
addTotalBytes(txBytes, rxBytes);
perNetwork.updateLinkBandwidth(mOldLlStats, mNewLlStats, mWifiInfo);
}
}
private void checkSerializationUpdateLinkBandwidthExample(BandwidthStatsAll stats) {
assertEquals(2_000_000L * 8 / 1000 * BANDWIDTH_STATS_COUNT_THR,
stats.getStats2G().getTx().getLevel(1).getValue());
assertEquals(4_000_000L * 8 / 1000 * BANDWIDTH_STATS_COUNT_THR,
stats.getStats2G().getRx().getLevel(1).getValue());
assertEquals(BANDWIDTH_STATS_COUNT_THR,
stats.getStats2G().getTx().getLevel(1).getCount());
assertEquals(BANDWIDTH_STATS_COUNT_THR,
stats.getStats2G().getRx().getLevel(1).getCount());
assertEquals(5_000_000L * 8 / 1000 * (BANDWIDTH_STATS_COUNT_THR + 2),
stats.getStatsAbove2G().getTx().getLevel(1).getValue());
assertEquals(0, stats.getStatsAbove2G().getRx().getLevel(1).getValue());
assertEquals(BANDWIDTH_STATS_COUNT_THR + 2,
stats.getStatsAbove2G().getTx().getLevel(1).getCount());
assertEquals(0, stats.getStatsAbove2G().getRx().getLevel(1).getCount());
}
/**
* Constructs a protobuf form of Network example.
*/
private byte[] makeSerializedNetworkExample() {
makeDisconnectionConnectingExample(true);
makeShortConnectionExample(true);
makeUpdateLinkBandwidthExample();
PerNetwork perNetwork = mWifiScoreCard.fetchByNetwork(mWifiInfo.getSSID());
checkSerializationNetworkExample("before serialization", perNetwork);
// Now convert to protobuf form
byte[] serialized = perNetwork.toNetworkStats().toByteArray();
return serialized;
}
/**
* Checks that the fields of the network serialization example are as expected.
*/
private void checkSerializationNetworkExample(String diag, PerNetwork perNetwork) {
NetworkConnectionStats dailyStats = perNetwork.getRecentStats();
assertEquals(diag, 2, dailyStats.getCount(CNT_CONNECTION_ATTEMPT));
assertEquals(diag, 1, dailyStats.getCount(CNT_CONNECTION_FAILURE));
assertEquals(diag, 1, dailyStats.getCount(CNT_DISCONNECTION_NONLOCAL_CONNECTING));
assertEquals(diag, 9, dailyStats.getCount(CNT_CONNECTION_DURATION_SEC));
assertEquals(diag, 1, dailyStats.getCount(CNT_SHORT_CONNECTION_NONLOCAL));
assertEquals(diag, 1, dailyStats.getCount(CNT_DISCONNECTION_NONLOCAL));
assertEquals(diag, 0, dailyStats.getCount(CNT_ASSOCIATION_REJECTION));
assertEquals(diag, 0, dailyStats.getCount(CNT_ASSOCIATION_TIMEOUT));
assertEquals(diag, 0, dailyStats.getCount(CNT_AUTHENTICATION_FAILURE));
}
/**
* Test NetworkStats serialization.
*/
@Test
public void testNetworkStatsSerialization() throws Exception {
byte[] serialized = makeSerializedNetworkExample();
// Verify by parsing it and checking that we see the expected results
NetworkStats ns = NetworkStats.parseFrom(serialized);
ConnectionStats dailyStats = ns.getRecentStats();
assertEquals(2, dailyStats.getNumConnectionAttempt());
assertEquals(1, dailyStats.getNumConnectionFailure());
assertEquals(1, dailyStats.getNumDisconnectionNonlocalConnecting());
assertEquals(9, dailyStats.getConnectionDurationSec());
assertEquals(1, dailyStats.getNumDisconnectionNonlocal());
assertEquals(1, dailyStats.getNumShortConnectionNonlocal());
assertEquals(0, dailyStats.getNumAssociationRejection());
assertEquals(0, dailyStats.getNumAssociationTimeout());
assertEquals(0, dailyStats.getNumAuthenticationFailure());
checkSerializationUpdateLinkBandwidthExample(ns.getBandwidthStatsAll());
}
/**
* Test NetworkStats Deserialization.
*/
@Test
public void testNetworkStatsDeserialization() throws Exception {
byte[] serialized = makeSerializedNetworkExample();
setUp(); // Get back to the initial state
PerNetwork perNetwork = mWifiScoreCard.perNetworkFromNetworkStats(mWifiInfo.getSSID(),
NetworkStats.parseFrom(serialized));
// Now verify
String diag = hexStringFromByteArray(serialized);
checkSerializationNetworkExample(diag, perNetwork);
}
/**
* Check network stats after network connection and then removeNetWork().
*/
@Test
public void testRemoveNetwork() throws Exception {
mWifiScoreCard.noteConnectionAttempt(mWifiInfo, -53, mWifiInfo.getSSID());
millisecondsPass(1000);
mWifiScoreCard.noteConnectionFailure(mWifiInfo, -53, mWifiInfo.getSSID(),
WifiBlocklistMonitor.REASON_ASSOCIATION_REJECTION);
mWifiScoreCard.removeNetwork(mWifiInfo.getSSID());
PerNetwork perNetwork = mWifiScoreCard.fetchByNetwork(mWifiInfo.getSSID());
assertNull(perNetwork);
}
@Test
public void testUpdateAfterDailyDetection() throws Exception {
for (int i = 0; i < DEFAULT_HEALTH_MONITOR_MIN_NUM_CONNECTION_ATTEMPT; i++) {
makeShortConnectionExample(/*addFwAlert*/false);
}
PerNetwork perNetwork = mWifiScoreCard.fetchByNetwork(mWifiInfo.getSSID());
perNetwork.updateAfterDailyDetection();
checkShortConnectionExample(perNetwork.getRecentStats(), 0);
checkShortConnectionExample(perNetwork.getStatsCurrBuild(),
DEFAULT_HEALTH_MONITOR_MIN_NUM_CONNECTION_ATTEMPT);
checkShortConnectionExample(perNetwork.getStatsPrevBuild(), 0);
}
@Test
public void testUpdateAfterSwBuildChange() throws Exception {
for (int i = 0; i < DEFAULT_HEALTH_MONITOR_MIN_NUM_CONNECTION_ATTEMPT; i++) {
makeShortConnectionExample(/*addFwAlert*/false);
}
PerNetwork perNetwork = mWifiScoreCard.fetchByNetwork(mWifiInfo.getSSID());
perNetwork.updateAfterDailyDetection();
perNetwork.updateAfterSwBuildChange();
checkShortConnectionExample(perNetwork.getRecentStats(), 0);
checkShortConnectionExample(perNetwork.getStatsCurrBuild(), 0);
checkShortConnectionExample(perNetwork.getStatsPrevBuild(),
DEFAULT_HEALTH_MONITOR_MIN_NUM_CONNECTION_ATTEMPT);
}
private void makeRecentStatsWithGoodConnection() {
for (int i = 0; i < DEFAULT_HEALTH_MONITOR_MIN_NUM_CONNECTION_ATTEMPT; i++) {
makeNormalConnectionExample();
}
}
private void makeRecentStatsWithShortConnection() {
for (int i = 0; i < DEFAULT_HEALTH_MONITOR_MIN_NUM_CONNECTION_ATTEMPT; i++) {
makeShortConnectionExample(/*addFwAlert*/false);
}
}
private void makeRecentStatsWithAssocTimeOut() {
for (int i = 0; i < DEFAULT_HEALTH_MONITOR_MIN_NUM_CONNECTION_ATTEMPT; i++) {
makeAssocTimeOutExample();
}
}
private void makeRecentStatsWithAuthFailure() {
for (int i = 0; i < DEFAULT_HEALTH_MONITOR_MIN_NUM_CONNECTION_ATTEMPT; i++) {
makeAuthFailureExample();
}
}
private void checkStatsDeltaExample(FailureStats stats, int scale) {
assertEquals(0, stats.getCount(REASON_ASSOC_REJECTION));
assertEquals(1 * scale, stats.getCount(REASON_ASSOC_TIMEOUT));
assertEquals(1 * scale, stats.getCount(REASON_AUTH_FAILURE));
assertEquals(1 * scale, stats.getCount(REASON_CONNECTION_FAILURE));
assertEquals(1 * scale, stats.getCount(REASON_DISCONNECTION_NONLOCAL));
assertEquals(1 * scale, stats.getCount(REASON_SHORT_CONNECTION_NONLOCAL));
}
/**
* Check if daily detection is skipped with insufficient daily stats.
*/
@Test
public void testDailyDetectionWithInsufficientRecentStats() throws Exception {
PerNetwork perNetwork = mWifiScoreCard.lookupNetwork(mWifiInfo.getSSID());
makeShortConnectionExample(/*addFwAlert*/false);
FailureStats statsDec = new FailureStats();
FailureStats statsInc = new FailureStats();
FailureStats statsHigh = new FailureStats();
int detectionFlag = perNetwork.dailyDetection(statsDec, statsInc, statsHigh);
assertEquals(WifiScoreCard.INSUFFICIENT_RECENT_STATS, detectionFlag);
checkStatsDeltaExample(statsDec, 0);
checkStatsDeltaExample(statsInc, 0);
checkStatsDeltaExample(statsHigh, 0);
perNetwork.updateAfterDailyDetection();
checkShortConnectionExample(perNetwork.getRecentStats(), 1);
checkShortConnectionExample(perNetwork.getStatsPrevBuild(), 0);
assertEquals(WifiHealthMonitor.REASON_NO_FAILURE,
mWifiScoreCard.detectAbnormalConnectionFailure(mWifiInfo.getSSID()));
}
/**
* Run a few days with mostly good connection and some failures,
* followed by a SW build change which results
* in performance regression. Check if the regression is detected properly.
*/
@Test
public void testRegressionAfterSwBuildChange() throws Exception {
PerNetwork perNetwork = mWifiScoreCard.lookupNetwork(mWifiInfo.getSSID());
int numGoodConnectionDays = 4;
for (int i = 0; i < numGoodConnectionDays; i++) {
makeRecentStatsWithGoodConnection();
perNetwork.updateAfterDailyDetection();
}
// Extra day with mixed failures
makeRecentStatsWithShortConnection();
makeRecentStatsWithAssocTimeOut();
makeRecentStatsWithAuthFailure();
perNetwork.updateAfterDailyDetection();
perNetwork.updateAfterSwBuildChange();
// Add >2x failures after the SW build change
int numBadConnectionDays = 4;
for (int i = 0; i < numBadConnectionDays; i++) {
makeRecentStatsWithAssocTimeOut();
makeRecentStatsWithAuthFailure();
makeRecentStatsWithShortConnection();
}
assertEquals(WifiHealthMonitor.REASON_SHORT_CONNECTION_NONLOCAL,
mWifiScoreCard.detectAbnormalDisconnection(WIFI_IFACE_NAME));
FailureStats statsDec = new FailureStats();
FailureStats statsInc = new FailureStats();
FailureStats statsHigh = new FailureStats();
int detectionFlag = perNetwork.dailyDetection(statsDec, statsInc, statsHigh);
assertEquals(WifiScoreCard.SUFFICIENT_RECENT_PREV_STATS, detectionFlag);
checkStatsDeltaExample(statsDec, 0);
checkStatsDeltaExample(statsInc, 1);
checkStatsDeltaExample(statsHigh, 0);
}
/**
* Check regression detection is missed due to low failure count
*/
@Test
public void testMissRegressionDetectionDuetoLowFailureCnt() throws Exception {
PerNetwork perNetwork = mWifiScoreCard.lookupNetwork(mWifiInfo.getSSID());
int numGoodConnectionDays = 1;
for (int i = 0; i < numGoodConnectionDays; i++) {
makeRecentStatsWithGoodConnection();
perNetwork.updateAfterDailyDetection();
}
perNetwork.updateAfterSwBuildChange();
makeRecentStatsWithGoodConnection();
// Add a small number of failures for each failure type after the SW build change
for (int i = 0; i < mDeviceConfigFacade.getAuthFailureCountMin() - 1; i++) {
makeShortConnectionExample(/*addFwAlert*/false);
makeAssocTimeOutExample();
makeAuthFailureExample();
}
FailureStats statsDec = new FailureStats();
FailureStats statsInc = new FailureStats();
FailureStats statsHigh = new FailureStats();
int detectionFlag = perNetwork.dailyDetection(statsDec, statsInc, statsHigh);
assertEquals(WifiScoreCard.SUFFICIENT_RECENT_PREV_STATS, detectionFlag);
checkStatsDeltaExample(statsDec, 0);
checkStatsDeltaExample(statsInc, 0);
checkStatsDeltaExample(statsHigh, 0);
assertEquals(WifiHealthMonitor.REASON_NO_FAILURE,
mWifiScoreCard.detectAbnormalConnectionFailure(mWifiInfo.getSSID()));
}
/**
* Run a few days with bad connections, followed by a SW build change which results
* in performance improvement. Check if the improvement is detected properly.
*/
@Test
public void testImprovementAfterSwBuildChange() throws Exception {
PerNetwork perNetwork = mWifiScoreCard.lookupNetwork(mWifiInfo.getSSID());
for (int i = 0; i < 2; i++) {
makeRecentStatsWithShortConnection();
makeRecentStatsWithAssocTimeOut();
makeRecentStatsWithAuthFailure();
perNetwork.updateAfterDailyDetection();
}
perNetwork.updateAfterSwBuildChange();
// Add <50% failures after the SW build change
int numGoodConnectionDays = 4;
for (int i = 0; i < numGoodConnectionDays; i++) {
makeRecentStatsWithGoodConnection();
}
makeRecentStatsWithAssocTimeOut();
makeRecentStatsWithAuthFailure();
makeRecentStatsWithShortConnection();
assertEquals(WifiHealthMonitor.REASON_NO_FAILURE,
mWifiScoreCard.detectAbnormalConnectionFailure(mWifiInfo.getSSID()));
FailureStats statsDec = new FailureStats();
FailureStats statsInc = new FailureStats();
FailureStats statsHigh = new FailureStats();
perNetwork.dailyDetection(statsDec, statsInc, statsHigh);
checkStatsDeltaExample(statsDec, 1);
checkStatsDeltaExample(statsInc, 0);
checkStatsDeltaExample(statsHigh, 0);
}
@Test
public void testPoorConnectionWithoutHistory() throws Exception {
PerNetwork perNetwork = mWifiScoreCard.lookupNetwork(mWifiInfo.getSSID());
makeRecentStatsWithShortConnection(); // Day 1
makeRecentStatsWithAuthFailure();
makeRecentStatsWithAssocTimeOut();
assertEquals(WifiHealthMonitor.REASON_ASSOC_TIMEOUT,
mWifiScoreCard.detectAbnormalConnectionFailure(mWifiInfo.getSSID()));
FailureStats statsDec = new FailureStats();
FailureStats statsInc = new FailureStats();
FailureStats statsHigh = new FailureStats();
int detectionFlag = perNetwork.dailyDetection(statsDec, statsInc, statsHigh);
assertEquals(WifiScoreCard.SUFFICIENT_RECENT_STATS_ONLY, detectionFlag);
checkStatsDeltaExample(statsDec, 0);
checkStatsDeltaExample(statsInc, 0);
checkStatsDeltaExample(statsHigh, 1);
}
@Test
public void testHighAuthFailureRate() throws Exception {
makeRecentStatsWithGoodConnection();
makeRecentStatsWithAuthFailure();
assertEquals(WifiHealthMonitor.REASON_AUTH_FAILURE,
mWifiScoreCard.detectAbnormalConnectionFailure(mWifiInfo.getSSID()));
}
@Test
public void testAddGetFrequencies() {
mWifiScoreCard.noteConnectionAttempt(mWifiInfo, -53, mWifiInfo.getSSID());
PerNetwork perNetwork = mWifiScoreCard.lookupNetwork(mWifiInfo.getSSID());
millisecondsPass(100);
perNetwork.addFrequency(5805);
millisecondsPass(1000);
perNetwork.addFrequency(2432);
assertEquals(2, perNetwork.getFrequencies(Long.MAX_VALUE).size());
assertEquals(2432, (int) perNetwork.getFrequencies(Long.MAX_VALUE).get(0));
assertEquals(5805, (int) perNetwork.getFrequencies(Long.MAX_VALUE).get(1));
// Check over aged channel will not return.
assertEquals(1, perNetwork.getFrequencies(900L).size());
assertEquals(2432, (int) perNetwork.getFrequencies(Long.MAX_VALUE).get(0));
}
private void addTotalBytes(long txBytes, long rxBytes) {
mTotalTxBytes += txBytes;
mTotalRxBytes += rxBytes;
when(mFrameworkFacade.getTotalTxBytes()).thenReturn(mTotalTxBytes);
when(mFrameworkFacade.getTotalRxBytes()).thenReturn(mTotalRxBytes);
}
private void subtractTotalBytes(long txBytes, long rxBytes) {
mTotalTxBytes -= txBytes;
mTotalRxBytes -= rxBytes;
when(mFrameworkFacade.getTotalTxBytes()).thenReturn(mTotalTxBytes);
when(mFrameworkFacade.getTotalRxBytes()).thenReturn(mTotalRxBytes);
}
@Test
public void testLinkBandwidthTwoRadioStatsVariousTxTraffic() {
mWifiInfo.setRssi(-70);
mWifiInfo.setFrequency(2437);
mWifiScoreCard.noteConnectionAttempt(mWifiInfo, -53, mWifiInfo.getSSID());
PerNetwork perNetwork = mWifiScoreCard.lookupNetwork(mWifiInfo.getSSID());
mNewLlStats.on_time = 3000;
mOldLlStats.radioStats = new WifiLinkLayerStats.RadioStat[2];
mOldLlStats.radioStats[0] = new WifiLinkLayerStats.RadioStat();
mOldLlStats.radioStats[1] = new WifiLinkLayerStats.RadioStat();
mNewLlStats.radioStats = new WifiLinkLayerStats.RadioStat[2];
mNewLlStats.radioStats[0] = new WifiLinkLayerStats.RadioStat();
mNewLlStats.radioStats[1] = new WifiLinkLayerStats.RadioStat();
mNewLlStats.radioStats[0].on_time = 500;
mNewLlStats.radioStats[1].on_time = 500;
mOldLlStats.timeStampInMs = 7_000;
mNewLlStats.timeStampInMs = 10_000;
long txBytes = 350_000L;
long rxBytes = 4_000_000L;
for (int i = 0; i < BANDWIDTH_STATS_COUNT_THR - 1; i++) {
addTotalBytes(txBytes, rxBytes);
millisecondsPass(3_000);
perNetwork.updateLinkBandwidth(mOldLlStats, mNewLlStats, mWifiInfo);
}
assertEquals(10_000, perNetwork.getTxLinkBandwidthKbps());
assertEquals(32_000, perNetwork.getRxLinkBandwidthKbps());
txBytes = 400_000L;
rxBytes = 200_000L;
for (int i = 0; i < BANDWIDTH_STATS_COUNT_THR - 1; i++) {
addTotalBytes(txBytes, rxBytes);
millisecondsPass(3_000);
perNetwork.updateLinkBandwidth(mOldLlStats, mNewLlStats, mWifiInfo);
}
assertEquals(3_200, perNetwork.getTxLinkBandwidthKbps());
}
@Test
public void testLinkBandwidthTwoBssidThreeSignalLevelOneBand() {
mWifiInfo.setRssi(-70);
mWifiInfo.setFrequency(2437);
mWifiScoreCard.noteConnectionAttempt(mWifiInfo, -53, mWifiInfo.getSSID());
PerNetwork perNetwork = mWifiScoreCard.lookupNetwork(mWifiInfo.getSSID());
mWifiScoreCard.noteIpConfiguration(mWifiInfo);
mNewLlStats.on_time = 1000;
mOldLlStats.timeStampInMs = 7_000;
mNewLlStats.timeStampInMs = 10_000;
long txBytes = 2_000_000L;
long rxBytes = 4_000_000L;
// Add BANDWIDTH_STATS_COUNT_THR - 2 polls at BSSID 1 at 1st level
for (int i = 0; i < BANDWIDTH_STATS_COUNT_THR - 2; i++) {
addTotalBytes(txBytes, rxBytes);
millisecondsPass(3_000);
perNetwork.updateLinkBandwidth(mOldLlStats, mNewLlStats, mWifiInfo);
}
// Add BANDWIDTH_STATS_COUNT_THR - 2 polls at BSSID 2 at 2nd level
mWifiInfo.setBSSID(TEST_BSSID_2.toString());
mNewLlStats.on_time = 2000;
mWifiInfo.setRssi(-54);
txBytes = 6_000_000L;
rxBytes = 100_000L;
for (int i = 0; i < BANDWIDTH_STATS_COUNT_THR - 2; i++) {
addTotalBytes(txBytes, rxBytes);
millisecondsPass(3_000);
perNetwork.updateLinkBandwidth(mOldLlStats, mNewLlStats, mWifiInfo);
}
// Add BANDWIDTH_STATS_COUNT_THR - 2 polls at BSSID 2 at 3rd level
rxBytes = 4_000_000L;
mWifiInfo.setRssi(-65);
for (int i = 0; i < BANDWIDTH_STATS_COUNT_THR - 2; i++) {
addTotalBytes(txBytes, rxBytes);
millisecondsPass(3_000);
perNetwork.updateLinkBandwidth(mOldLlStats, mNewLlStats, mWifiInfo);
}
assertEquals(23_619, perNetwork.getTxLinkBandwidthKbps());
assertEquals(16_677, perNetwork.getRxLinkBandwidthKbps());
}
@Test
public void testLinkBandwidthInvalidTrafficStats() {
mWifiInfo.setRssi(-70);
mWifiScoreCard.noteConnectionAttempt(mWifiInfo, -53, mWifiInfo.getSSID());
PerNetwork perNetwork = mWifiScoreCard.lookupNetwork(mWifiInfo.getSSID());
mWifiInfo.setFrequency(5210);
mWifiScoreCard.noteIpConfiguration(mWifiInfo);
mNewLlStats.on_time = 1000;
mOldLlStats.timeStampInMs = 7_000;
mNewLlStats.timeStampInMs = 10_000;
long txBytes = 2_000_000L;
long rxBytes = 100_000L;
int[] reportedKbps = new int[]{400_000, 300_000};
int[] l2Kbps = new int[]{800_000, 700_000};
// Add BANDWIDTH_STATS_COUNT_THR polls with one of them has invalid traffic stats
for (int i = 0; i < BANDWIDTH_STATS_COUNT_THR; i++) {
if (i == 1) {
subtractTotalBytes(txBytes, rxBytes);
} else {
addTotalBytes(txBytes, rxBytes);
}
millisecondsPass(3_000);
perNetwork.updateLinkBandwidth(mOldLlStats, mNewLlStats, mWifiInfo);
perNetwork.updateBwMetrics(reportedKbps, l2Kbps);
}
assertEquals(16_000, perNetwork.getTxLinkBandwidthKbps());
assertEquals(LINK_BANDWIDTH_INIT_KBPS[1][LINK_RX][2], perNetwork.getRxLinkBandwidthKbps());
BandwidthEstimatorStats stats = mWifiScoreCard.dumpBandwidthEstimatorStats();
assertEquals(0, stats.stats2G.tx.level.length);
assertEquals(0, stats.stats2G.rx.level.length);
}
@Test
public void testLinkBandwidthOneBssidTwoSignalLevelTwoBand() {
mWifiInfo.setRssi(-70);
mWifiScoreCard.noteConnectionAttempt(mWifiInfo, -53, mWifiInfo.getSSID());
PerNetwork perNetwork = mWifiScoreCard.lookupNetwork(mWifiInfo.getSSID());
mWifiInfo.setFrequency(5210);
mWifiScoreCard.noteIpConfiguration(mWifiInfo);
mNewLlStats.on_time = 1000;
mOldLlStats.timeStampInMs = 7_000;
mNewLlStats.timeStampInMs = 10_000;
long txBytes = 2_000_000L;
long rxBytes = 100_000L;
int [] reportedKbps = new int[]{40_000, 30_000};
int [] l2Kbps = new int[]{80_000, 70_000};
// Add BANDWIDTH_STATS_COUNT_THR polls at 1st level and 1st band
for (int i = 0; i < BANDWIDTH_STATS_COUNT_THR; i++) {
addTotalBytes(txBytes, rxBytes);
millisecondsPass(3_000);
perNetwork.updateLinkBandwidth(mOldLlStats, mNewLlStats, mWifiInfo);
perNetwork.updateBwMetrics(reportedKbps, l2Kbps);
}
// Add BANDWIDTH_STATS_COUNT_THR polls at 2nd level and 1st band
mWifiInfo.setRssi(-65);
rxBytes = 7_000_000L;
for (int i = 0; i < BANDWIDTH_STATS_COUNT_THR; i++) {
addTotalBytes(txBytes, rxBytes);
millisecondsPass(3_000);
perNetwork.updateLinkBandwidth(mOldLlStats, mNewLlStats, mWifiInfo);
perNetwork.updateBwMetrics(reportedKbps, l2Kbps);
}
// Add BANDWIDTH_STATS_COUNT_THR * 2 polls at 1st level and 2nd band
mWifiInfo.setRssi(-70);
mWifiInfo.setFrequency(2437);
txBytes = 6_000_000L;
mNewLlStats.on_time = 2000;
for (int i = 0; i < (2 * BANDWIDTH_STATS_COUNT_THR); i++) {
addTotalBytes(txBytes, rxBytes);
millisecondsPass(3_000);
perNetwork.updateLinkBandwidth(mOldLlStats, mNewLlStats, mWifiInfo);
perNetwork.updateBwMetrics(reportedKbps, l2Kbps);
}
// Expect stats of 1st level and 2nd band are used
assertEquals(23_949, perNetwork.getTxLinkBandwidthKbps());
assertEquals(28_173, perNetwork.getRxLinkBandwidthKbps());
BandwidthEstimatorStats stats = mWifiScoreCard.dumpBandwidthEstimatorStats();
assertEquals(1, stats.stats2G.tx.level.length);
assertEquals(1, stats.stats2G.rx.level.length);
assertEquals(2, stats.stats2G.rx.level[0].signalLevel);
assertEquals(BANDWIDTH_STATS_COUNT_THR - 1, stats.stats2G.rx.level[0].count);
assertEquals(28_000, stats.stats2G.rx.level[0].avgBandwidthKbps);
assertEquals(150, stats.stats2G.rx.level[0].l2ErrorPercent);
assertEquals(7, stats.stats2G.rx.level[0].bandwidthEstErrorPercent);
assertEquals(2, stats.stats2G.tx.level[0].signalLevel);
assertEquals(BANDWIDTH_STATS_COUNT_THR - 1, stats.stats2G.tx.level[0].count);
assertEquals(24_000, stats.stats2G.tx.level[0].avgBandwidthKbps);
assertEquals(233, stats.stats2G.tx.level[0].l2ErrorPercent);
assertEquals(66, stats.stats2G.tx.level[0].bandwidthEstErrorPercent);
assertEquals(0, stats.statsAbove2G.tx.level.length);
assertEquals(0, stats.statsAbove2G.rx.level.length);
}
@Test
public void testLinkBandwidthLargeByteCountReturnNonNegativeValue() {
mWifiInfo.setRssi(-70);
mWifiInfo.setMaxSupportedRxLinkSpeedMbps(200_000);
mWifiScoreCard.noteConnectionAttempt(mWifiInfo, -53, mWifiInfo.getSSID());
PerNetwork perNetwork = mWifiScoreCard.lookupNetwork(mWifiInfo.getSSID());
mWifiInfo.setFrequency(5210);
mWifiScoreCard.noteIpConfiguration(mWifiInfo);
mOldLlStats.timeStampInMs = 7_000;
mNewLlStats.timeStampInMs = 10_000;
long txBytes = 8_000_000_000L;
long rxBytes = 16_000_000_000L;
int [] reportedKbps = new int[]{400_000, 300_000};
int [] l2Kbps = new int[]{800_000, 700_000};
// Report a small on_time so that the calculated BW overflows at 5G
mNewLlStats.on_time = 10;
for (int i = 0; i < BANDWIDTH_STATS_COUNT_THR + 2; i++) {
addTotalBytes(txBytes, rxBytes);
millisecondsPass(3_000);
perNetwork.updateLinkBandwidth(mOldLlStats, mNewLlStats, mWifiInfo);
perNetwork.updateBwMetrics(reportedKbps, l2Kbps);
}
// Report a larger on_time so that the calculated BW won't overflows at 2G
mWifiInfo.setFrequency(2412);
mNewLlStats.on_time = 1000;
for (int i = 0; i < BANDWIDTH_STATS_COUNT_THR + 2; i++) {
addTotalBytes(txBytes, rxBytes);
millisecondsPass(3_000);
perNetwork.updateLinkBandwidth(mOldLlStats, mNewLlStats, mWifiInfo);
perNetwork.updateBwMetrics(reportedKbps, l2Kbps);
}
// Report cold start BW for Tx because the calculated value is higher than
// maxSupportedTxLinkSpeedMbps.
assertEquals(10_000, perNetwork.getTxLinkBandwidthKbps());
assertEquals(128_000_000, perNetwork.getRxLinkBandwidthKbps());
BandwidthEstimatorStats stats = mWifiScoreCard.dumpBandwidthEstimatorStats();
assertEquals(0, stats.statsAbove2G.tx.level.length);
assertEquals(0, stats.statsAbove2G.rx.level.length);
assertEquals(0, stats.stats2G.tx.level.length);
assertEquals(1, stats.stats2G.rx.level.length);
assertEquals(128_000_000, stats.stats2G.rx.level[0].avgBandwidthKbps);
assertEquals(1, stats.stats2G.rx.level[0].count);
mNewLlStats.on_time = 2000;
for (int i = 0; i < BANDWIDTH_STATS_COUNT_THR + 2; i++) {
addTotalBytes(txBytes, rxBytes);
millisecondsPass(3_000);
perNetwork.updateLinkBandwidth(mOldLlStats, mNewLlStats, mWifiInfo);
perNetwork.updateBwMetrics(reportedKbps, l2Kbps);
}
stats = mWifiScoreCard.dumpBandwidthEstimatorStats();
assertEquals(64_000_000, stats.stats2G.rx.level[0].avgBandwidthKbps);
assertEquals(BANDWIDTH_STATS_COUNT_THR + 2, stats.stats2G.rx.level[0].count);
}
@Test
public void testLinkBandwidthResetInvalidStats() {
mWifiInfo.setRssi(-70);
mWifiInfo.setMaxSupportedRxLinkSpeedMbps(200_000);
mWifiScoreCard.noteConnectionAttempt(mWifiInfo, -53, mWifiInfo.getSSID());
PerNetwork perNetwork = mWifiScoreCard.lookupNetwork(mWifiInfo.getSSID());
mWifiScoreCard.noteIpConfiguration(mWifiInfo);
mOldLlStats.timeStampInMs = 7_000;
mNewLlStats.timeStampInMs = 10_000;
long txBytes = 8_000_000_000L;
long rxBytes = 16_000_000_000L;
mWifiInfo.setFrequency(2412);
mNewLlStats.on_time = 1000;
for (int i = 0; i < BANDWIDTH_STATS_COUNT_THR + 2; i++) {
addTotalBytes(txBytes, rxBytes);
millisecondsPass(3_000);
perNetwork.updateLinkBandwidth(mOldLlStats, mNewLlStats, mWifiInfo);
}
assertEquals(128_000_000, perNetwork.getRxLinkBandwidthKbps());
// Reduce max supported Rx link speed so that stats in the memory become invalid
// and fall back to cold start values
mWifiInfo.setMaxSupportedRxLinkSpeedMbps(100);
for (int i = 0; i < BANDWIDTH_STATS_COUNT_THR + 2; i++) {
addTotalBytes(txBytes, rxBytes);
millisecondsPass(3_000);
perNetwork.updateLinkBandwidth(mOldLlStats, mNewLlStats, mWifiInfo);
}
assertEquals(10_070, perNetwork.getRxLinkBandwidthKbps());
}
@Test
public void testLinkBandwidthLowOnTimeHighSignalLevel() {
// Add polls with zero on_time and high signal level
mWifiInfo.setRssi(-53);
int signalLevel = RssiUtil.calculateSignalLevel(mContext, mWifiInfo.getRssi());
mWifiInfo.setFrequency(5210);
mWifiScoreCard.noteConnectionAttempt(mWifiInfo, -53, mWifiInfo.getSSID());
PerNetwork perNetwork = mWifiScoreCard.lookupNetwork(mWifiInfo.getSSID());
mWifiInfo.setFrequency(5210);
mWifiScoreCard.noteIpConfiguration(mWifiInfo);
mNewLlStats.on_time = 5;
mOldLlStats.timeStampInMs = 7_000;
mNewLlStats.timeStampInMs = 10_000;
long txBytes = 2_000_000L;
long rxBytes = 100_000L;
for (int i = 0; i < BANDWIDTH_STATS_COUNT_THR; i++) {
addTotalBytes(txBytes, rxBytes);
millisecondsPass(3_000);
perNetwork.updateLinkBandwidth(mOldLlStats, mNewLlStats, mWifiInfo);
}
// Expect cold-start value
assertEquals(LINK_BANDWIDTH_INIT_KBPS[1][LINK_TX][signalLevel],
perNetwork.getTxLinkBandwidthKbps());
assertEquals(LINK_BANDWIDTH_INIT_KBPS[1][LINK_RX][signalLevel],
perNetwork.getRxLinkBandwidthKbps());
}
@Test
public void testGetLinkBandwidthWithoutUpdateReturnLevel0Band0Value() {
PerNetwork perNetwork = mWifiScoreCard.lookupNetwork(mWifiInfo.getSSID());
// Call getLinkBandwidth() without updateLinkBandwidth()
// Expect cold-start value at level 0 and band 0
assertEquals(LINK_BANDWIDTH_INIT_KBPS[0][LINK_TX][0],
perNetwork.getTxLinkBandwidthKbps());
assertEquals(LINK_BANDWIDTH_INIT_KBPS[0][LINK_RX][0],
perNetwork.getRxLinkBandwidthKbps());
}
}