/* * Copyright 2017 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 org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.*; import android.net.wifi.WifiConfiguration; import androidx.test.filters.SmallTest; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; /** * Unit tests for {@link WakeupLock}. */ @SmallTest public class WakeupLockTest { private static final String SSID_1 = "ssid1"; private static final String SSID_2 = "ssid2"; @Mock private WifiConfigManager mWifiConfigManager; @Mock private WifiWakeMetrics mWifiWakeMetrics; @Mock private Clock mClock; private ScanResultMatchInfo mNetwork1; private ScanResultMatchInfo mNetwork2; private WakeupLock mWakeupLock; /** * Initialize objects before each test run. */ @Before public void setUp() { MockitoAnnotations.initMocks(this); when(mClock.getElapsedSinceBootMillis()).thenReturn(0L); mNetwork1 = new ScanResultMatchInfo(); mNetwork1.networkSsid = SSID_1; mNetwork1.networkType = WifiConfiguration.SECURITY_TYPE_OPEN; mNetwork2 = new ScanResultMatchInfo(); mNetwork2.networkSsid = SSID_2; mNetwork2.networkType = WifiConfiguration.SECURITY_TYPE_EAP; mWakeupLock = new WakeupLock(mWifiConfigManager, mWifiWakeMetrics, mClock); } /** * Updates the lock enough times to evict any networks not passed in. * *

It calls update {@link WakeupLock#CONSECUTIVE_MISSED_SCANS_REQUIRED_TO_EVICT} times with * the given network list. It asserts that the lock isn't empty prior to each call to update. */ private void updateEnoughTimesToEvictWithAsserts(Collection networks) { for (int i = 0; i < WakeupLock.CONSECUTIVE_MISSED_SCANS_REQUIRED_TO_EVICT; i++) { assertFalse("Lock empty after " + i + " scans", mWakeupLock.isUnlocked()); mWakeupLock.update(networks); } } /** * Updates the lock enough times to evict any networks not passed in. * *

It calls update {@link WakeupLock#CONSECUTIVE_MISSED_SCANS_REQUIRED_TO_EVICT} times with * the given network list. It does not make any assertions about the state of the lock. */ private void updateEnoughTimesToEvictWithoutAsserts(Collection networks) { for (int i = 0; i < WakeupLock.CONSECUTIVE_MISSED_SCANS_REQUIRED_TO_EVICT; i++) { mWakeupLock.update(networks); } } /** * Verify that calling update {@link WakeupLock#CONSECUTIVE_MISSED_SCANS_REQUIRED_TO_EVICT} * times sets the lock to be initialized. */ @Test public void verifyInitializingLockByScans() { List networks = Collections.singletonList(mNetwork1); mWakeupLock.setLock(networks); assertFalse(mWakeupLock.isInitialized()); for (int i = 0; i < WakeupLock.CONSECUTIVE_MISSED_SCANS_REQUIRED_TO_EVICT - 1; i++) { mWakeupLock.update(networks); assertFalse(mWakeupLock.isInitialized()); } mWakeupLock.update(networks); assertTrue(mWakeupLock.isInitialized()); } /** * Verify that calling update after {@link WakeupLock#MAX_LOCK_TIME_MILLIS} milliseconds sets * the lock to be initialized and does not add the scans to the lock. */ @Test public void verifyInitializingLockByTimeout() { when(mClock.getElapsedSinceBootMillis()) .thenReturn(0L, WakeupLock.MAX_LOCK_TIME_MILLIS + 1); mWakeupLock.setLock(Collections.emptyList()); assertFalse(mWakeupLock.isInitialized()); List networks = Collections.singletonList(mNetwork1); mWakeupLock.update(networks); assertTrue(mWakeupLock.isInitialized()); assertTrue(mWakeupLock.isUnlocked()); } /** * Verify that addToLock saves to the store if it changes the contents of the lock. */ @Test public void addToLockSavesToStore() { mWakeupLock.setLock(Collections.emptyList()); List networks = Collections.singletonList(mNetwork1); mWakeupLock.update(networks); // want 2 invocations, once for setLock(), once for addToLock verify(mWifiConfigManager, times(2)).saveToStore(false); } /** * Verify that the WakeupLock is not empty immediately after being initialized with networks. */ @Test public void verifyNotEmptyWhenSetWithNetworkList() { setLockAndInitializeByTimeout(Arrays.asList(mNetwork1, mNetwork2)); assertFalse(mWakeupLock.isUnlocked()); } /** * Verify that the WakeupLock is unlocked when initialized with an empty list. */ @Test public void isEmptyWhenInitializedWithEmptyList() { setLockAndInitializeByTimeout(Collections.emptyList()); assertTrue(mWakeupLock.isUnlocked()); } /** * Verify that setting the lock clears out previous entries. */ @Test public void setLockClearsPreviousNetworks() { setLockAndInitializeByTimeout(Collections.singletonList(mNetwork1)); assertFalse(mWakeupLock.isUnlocked()); setLockAndInitializeByTimeout(Collections.emptyList()); assertTrue(mWakeupLock.isUnlocked()); } /** * Updating the lock should evict scan results that haven't been seen in * {@link WakeupLock#CONSECUTIVE_MISSED_SCANS_REQUIRED_TO_EVICT} scans. */ @Test public void updateShouldRemoveNetworksAfterConsecutiveMissedScans() { setLockAndInitializeByTimeout(Collections.singletonList(mNetwork1)); updateEnoughTimesToEvictWithAsserts(Collections.singletonList(mNetwork2)); assertTrue(mWakeupLock.isUnlocked()); } /** * Ensure that missed scans must be consecutive in order to evict networks from lock. */ @Test public void updateWithLockedNetworkShouldResetRequiredNumberOfScans() { List lockedNetworks = Collections.singletonList(mNetwork1); List updateNetworks = Collections.singletonList(mNetwork2); setLockAndInitializeByTimeout(lockedNetworks); // one update without network mWakeupLock.update(updateNetworks); // one update with network mWakeupLock.update(lockedNetworks); updateEnoughTimesToEvictWithAsserts(updateNetworks); assertTrue(mWakeupLock.isUnlocked()); } /** * Once a network is removed from the lock, it should not be reset even if it's seen again. */ @Test public void updateWithLockedNetworkAfterItIsRemovedDoesNotReset() { List lockedNetworks = Collections.singletonList(mNetwork1); setLockAndInitializeByTimeout(lockedNetworks); updateEnoughTimesToEvictWithAsserts(Collections.emptyList()); assertTrue(mWakeupLock.isUnlocked()); mWakeupLock.update(lockedNetworks); assertTrue(mWakeupLock.isUnlocked()); } /** * Verify that networks can be incrementally removed from the lock. Their counters should be * independent. */ @Test public void networksCanBeRemovedIncrementallyFromLock() { List lockedNetworks = Arrays.asList(mNetwork1, mNetwork2); setLockAndInitializeByTimeout(lockedNetworks); updateEnoughTimesToEvictWithAsserts(Collections.singletonList(mNetwork1)); assertFalse(mWakeupLock.isUnlocked()); updateEnoughTimesToEvictWithAsserts(Collections.singletonList(mNetwork2)); assertTrue(mWakeupLock.isUnlocked()); } /** * Verify that initializing the lock persists the SSID list to the config store. */ @Test public void initializeShouldSaveSsidsToStore() { setLockAndInitializeByTimeout(Collections.singletonList(mNetwork1)); verify(mWifiConfigManager).saveToStore(eq(false)); } /** * Verify that update saves to store if the lock changes. */ @Test public void updateShouldOnlySaveIfLockChanges() { setLockAndInitializeByTimeout(Collections.singletonList(mNetwork1)); updateEnoughTimesToEvictWithoutAsserts(Collections.emptyList()); // need exactly 2 invocations: 1 for initialize, 1 for successful update verify(mWifiConfigManager, times(2)).saveToStore(eq(false)); } /** * Verify that update does not save to store if the lock does not change. */ @Test public void updateShouldNotSaveIfLockDoesNotChange() { List networks = Collections.singletonList(mNetwork1); setLockAndInitializeByTimeout(networks); verify(mWifiConfigManager, times(1)).saveToStore(anyBoolean()); mWakeupLock.update(networks); } /** * Verify that on unlock, records the unlock event with WifiWakeMetrics with the correct number * of scans. */ @Test public void unlockingShouldRecordEventInMetrics() { when(mClock.getElapsedSinceBootMillis()) .thenReturn(0L, WakeupLock.MAX_LOCK_TIME_MILLIS + 1); List networks = Collections.singletonList(mNetwork1); mWakeupLock.setLock(networks); for (int i = 0; i < WakeupLock.CONSECUTIVE_MISSED_SCANS_REQUIRED_TO_EVICT; i++) { mWakeupLock.update(Collections.emptyList()); } verify(mWifiWakeMetrics).recordUnlockEvent( WakeupLock.CONSECUTIVE_MISSED_SCANS_REQUIRED_TO_EVICT /* numScans */); } private void setLockAndInitializeByTimeout(Collection networks) { when(mClock.getElapsedSinceBootMillis()) .thenReturn(0L, WakeupLock.MAX_LOCK_TIME_MILLIS + 1); mWakeupLock.setLock(networks); mWakeupLock.update(networks); assertTrue(mWakeupLock.isInitialized()); } }