blob: edbc4b0fc7d104a5a680e64a404df8bad79300f2 [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.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_SHORT_CONNECTION_NONLOCAL;
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.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.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.util.IntHistogram;
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 =
BssidBlocklistMonitor.REASON_ASSOCIATION_REJECTION;
WifiScoreCard mWifiScoreCard;
@Mock Clock mClock;
@Mock WifiScoreCard.MemoryStore mMemoryStore;
@Mock DeviceConfigFacade mDeviceConfigFacade;
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(Context.class));
mWifiInfo.setSSID(TEST_SSID_1);
mWifiInfo.setBSSID(TEST_BSSID_1.toString());
mWifiInfo.setNetworkId(TEST_NETWORK_CONFIG_ID);
millisecondsPass(0);
mWifiScoreCard = new WifiScoreCard(mClock, "some seed", mDeviceConfigFacade);
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.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);
mWifiScoreCard.enableVerboseLogging(true);
}
/**
* 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);
}
}
}
mWifiScoreCard.resetConnectionState();
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());
}
}
}
/**
* 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(),
BssidBlocklistMonitor.REASON_ASSOCIATION_TIMEOUT);
}
/**
* 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));
}
private void makeAuthFailureAndWrongPassword() {
mWifiScoreCard.noteConnectionAttempt(mWifiInfo, -53, mWifiInfo.getSSID());
millisecondsPass(500);
mWifiScoreCard.noteConnectionFailure(mWifiInfo, -53, mWifiInfo.getSSID(),
BssidBlocklistMonitor.REASON_AUTHENTICATION_FAILURE);
millisecondsPass(1000);
mWifiScoreCard.noteConnectionAttempt(mWifiInfo, -53, mWifiInfo.getSSID());
millisecondsPass(1000);
mWifiScoreCard.noteConnectionFailure(mWifiInfo, -53, mWifiInfo.getSSID(),
BssidBlocklistMonitor.REASON_WRONG_PASSWORD);
}
private void makeAuthFailureExample() {
mWifiScoreCard.noteConnectionAttempt(mWifiInfo, -53, mWifiInfo.getSSID());
millisecondsPass(500);
mWifiScoreCard.noteConnectionFailure(mWifiInfo, -53, mWifiInfo.getSSID(),
BssidBlocklistMonitor.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));
}
/**
* 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(disconnectionReason);
millisecondsPass(100);
mWifiScoreCard.resetConnectionState();
// SSID_2 is connected and then disconnected
millisecondsPass(2000);
mWifiScoreCard.noteIpConfiguration(mWifiInfo);
millisecondsPass(2000);
mWifiScoreCard.resetConnectionState();
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(),
BssidBlocklistMonitor.REASON_ASSOCIATION_REJECTION);
millisecondsPass(3000);
mWifiScoreCard.noteConnectionAttempt(mWifiInfo, -83, mWifiInfo.getSSID());
millisecondsPass(1000);
mWifiScoreCard.noteConnectionFailure(mWifiInfo, -83, mWifiInfo.getSSID(),
BssidBlocklistMonitor.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(disconnectionReason);
if (addFwAlert) {
mWifiScoreCard.noteFirmwareAlert(6);
}
millisecondsPass(1000);
mWifiScoreCard.resetConnectionState();
}
private void checkShortConnectionExample(NetworkConnectionStats stats, int scale) {
assertEquals(1 * scale, stats.getCount(CNT_CONNECTION_ATTEMPT));
assertEquals(0, stats.getCount(CNT_CONNECTION_FAILURE));
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(disconnectionReason);
millisecondsPass(1000);
mWifiScoreCard.resetConnectionState();
}
private void checkShortConnectionOldPollingExample(NetworkConnectionStats stats) {
assertEquals(1, stats.getCount(CNT_CONNECTION_ATTEMPT));
assertEquals(0, stats.getCount(CNT_CONNECTION_FAILURE));
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.resetConnectionState();
}
/**
* Constructs a protobuf form of Network example.
*/
private byte[] makeSerializedNetworkExample() {
makeAuthFailureAndWrongPassword();
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, 0, 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, 1, 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(0, dailyStats.getConnectionDurationSec());
assertEquals(0, dailyStats.getNumDisconnectionNonlocal());
assertEquals(0, dailyStats.getNumShortConnectionNonlocal());
assertEquals(0, dailyStats.getNumAssociationRejection());
assertEquals(0, dailyStats.getNumAssociationTimeout());
assertEquals(1, dailyStats.getNumAuthenticationFailure());
}
/**
* 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(),
BssidBlocklistMonitor.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());
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));
}
}