| /* |
| * Copyright (C) 2016 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 android.content.Intent.ACTION_SCREEN_OFF; |
| import static android.content.Intent.ACTION_SCREEN_ON; |
| |
| import static com.android.server.wifi.ActiveModeManager.ROLE_CLIENT_PRIMARY; |
| import static com.android.server.wifi.ActiveModeManager.ROLE_CLIENT_SCAN_ONLY; |
| import static com.android.server.wifi.ActiveModeManager.ROLE_CLIENT_SECONDARY_LONG_LIVED; |
| import static com.android.server.wifi.ActiveModeManager.ROLE_CLIENT_SECONDARY_TRANSIENT; |
| import static com.android.server.wifi.ClientModeImpl.WIFI_WORK_SOURCE; |
| import static com.android.server.wifi.WifiConfigurationTestUtil.generateWifiConfig; |
| |
| import static org.junit.Assert.assertArrayEquals; |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertFalse; |
| import static org.junit.Assert.assertNotNull; |
| import static org.junit.Assert.assertNull; |
| import static org.junit.Assert.assertTrue; |
| import static org.junit.Assume.assumeTrue; |
| import static org.mockito.Mockito.any; |
| import static org.mockito.Mockito.anyBoolean; |
| import static org.mockito.Mockito.anyInt; |
| import static org.mockito.Mockito.anyList; |
| import static org.mockito.Mockito.anyLong; |
| import static org.mockito.Mockito.anyObject; |
| import static org.mockito.Mockito.anySet; |
| import static org.mockito.Mockito.anyString; |
| import static org.mockito.Mockito.argThat; |
| import static org.mockito.Mockito.atLeast; |
| import static org.mockito.Mockito.atLeastOnce; |
| import static org.mockito.Mockito.clearInvocations; |
| import static org.mockito.Mockito.doAnswer; |
| import static org.mockito.Mockito.doNothing; |
| import static org.mockito.Mockito.eq; |
| import static org.mockito.Mockito.inOrder; |
| import static org.mockito.Mockito.lenient; |
| import static org.mockito.Mockito.mock; |
| import static org.mockito.Mockito.never; |
| import static org.mockito.Mockito.reset; |
| import static org.mockito.Mockito.times; |
| import static org.mockito.Mockito.validateMockitoUsage; |
| import static org.mockito.Mockito.verify; |
| import static org.mockito.Mockito.verifyNoMoreInteractions; |
| import static org.mockito.Mockito.when; |
| import static org.mockito.Mockito.withSettings; |
| |
| import android.app.AlarmManager; |
| import android.app.test.MockAnswerUtil.AnswerWithArguments; |
| import android.app.test.TestAlarmManager; |
| import android.content.BroadcastReceiver; |
| import android.content.Intent; |
| import android.content.pm.PackageManager; |
| import android.net.IpConfiguration; |
| import android.net.MacAddress; |
| import android.net.wifi.IPnoScanResultsCallback; |
| import android.net.wifi.ScanResult; |
| import android.net.wifi.ScanResult.InformationElement; |
| import android.net.wifi.SupplicantState; |
| import android.net.wifi.WifiConfiguration; |
| import android.net.wifi.WifiContext; |
| import android.net.wifi.WifiInfo; |
| import android.net.wifi.WifiManager; |
| import android.net.wifi.WifiNetworkSuggestion; |
| import android.net.wifi.WifiScanner; |
| import android.net.wifi.WifiScanner.PnoSettings; |
| import android.net.wifi.WifiScanner.ScanData; |
| import android.net.wifi.WifiScanner.ScanSettings; |
| import android.net.wifi.WifiSsid; |
| import android.net.wifi.hotspot2.PasspointConfiguration; |
| import android.net.wifi.util.ScanResultUtil; |
| import android.os.Handler; |
| import android.os.IBinder; |
| import android.os.IPowerManager; |
| import android.os.IThermalService; |
| import android.os.Looper; |
| import android.os.Message; |
| import android.os.PowerManager; |
| import android.os.Process; |
| import android.os.SystemClock; |
| import android.os.WorkSource; |
| import android.os.test.TestLooper; |
| import android.util.ArraySet; |
| import android.util.LocalLog; |
| |
| import androidx.test.filters.SmallTest; |
| |
| import com.android.dx.mockito.inline.extended.ExtendedMockito; |
| import com.android.modules.utils.build.SdkLevel; |
| import com.android.server.wifi.ActiveModeWarden.ExternalClientModeManagerRequestListener; |
| import com.android.server.wifi.hotspot2.PasspointManager; |
| import com.android.server.wifi.proto.nano.WifiMetricsProto; |
| import com.android.server.wifi.scanner.WifiScannerInternal; |
| import com.android.server.wifi.util.LruConnectionTracker; |
| import com.android.server.wifi.util.WifiPermissionsUtil; |
| import com.android.wifi.resources.R; |
| |
| import org.junit.After; |
| import org.junit.Before; |
| import org.junit.Test; |
| import org.mockito.ArgumentCaptor; |
| import org.mockito.ArgumentMatcher; |
| import org.mockito.Captor; |
| import org.mockito.InOrder; |
| import org.mockito.Mock; |
| import org.mockito.MockitoAnnotations; |
| import org.mockito.MockitoSession; |
| import org.mockito.quality.Strictness; |
| import org.mockito.stubbing.Answer; |
| |
| import java.io.FileDescriptor; |
| import java.io.PrintWriter; |
| import java.io.StringWriter; |
| import java.nio.charset.StandardCharsets; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.Comparator; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.stream.Collectors; |
| |
| /** |
| * Unit tests for {@link com.android.server.wifi.WifiConnectivityManager}. |
| */ |
| @SmallTest |
| public class WifiConnectivityManagerTest extends WifiBaseTest { |
| /** |
| * Called before each test |
| */ |
| @Before |
| public void setUp() throws Exception { |
| MockitoAnnotations.initMocks(this); |
| mResources = new MockResources(); |
| setUpResources(mResources); |
| mAlarmManager = new TestAlarmManager(); |
| mContext = mockContext(); |
| mLocalLog = new LocalLog(512); |
| setupMockForClientModeManager(mPrimaryClientModeManager); |
| mWifiConfigManager = mockWifiConfigManager(); |
| mWifiInfo = getWifiInfo(); |
| mScanData = mockScanData(); |
| mockWifiScanner(); |
| mWifiConnectivityHelper = mockWifiConnectivityHelper(); |
| mWifiNS = mockWifiNetworkSelector(); |
| mLooper = new TestLooper(mClock::getElapsedSinceBootMillis); |
| mTestHandler = new TestHandler(mLooper.getLooper()); |
| WifiLocalServices.removeServiceForTest(WifiScannerInternal.class); |
| WifiLocalServices.addService(WifiScannerInternal.class, mWifiScanner); |
| when(mWifiNetworkSuggestionsManager.retrieveHiddenNetworkList(anyBoolean())) |
| .thenReturn(new ArrayList<>()); |
| when(mWifiNetworkSuggestionsManager.getAllApprovedNetworkSuggestions()) |
| .thenReturn(new HashSet<>()); |
| when(mPasspointManager.getProviderConfigs(anyInt(), anyBoolean())) |
| .thenReturn(new ArrayList<>()); |
| mPowerManagerService = mock(IPowerManager.class); |
| PowerManager powerManager = |
| new PowerManager(mContext, mPowerManagerService, mock(IThermalService.class), |
| new Handler()); |
| when(mContext.getSystemService(PowerManager.class)).thenReturn(powerManager); |
| when(powerManager.isInteractive()).thenReturn(false); |
| when(mPrimaryClientModeManager.getRole()).thenReturn(ActiveModeManager.ROLE_CLIENT_PRIMARY); |
| when(mPrimaryClientModeManager.getConnectionInfo()).thenReturn(mWifiInfo); |
| when(mActiveModeWarden.getPrimaryClientModeManager()).thenReturn(mPrimaryClientModeManager); |
| doAnswer(new AnswerWithArguments() { |
| public void answer(ExternalClientModeManagerRequestListener listener, |
| WorkSource requestorWs, String ssid, String bssid) { |
| listener.onAnswer(mPrimaryClientModeManager); |
| } |
| }).when(mActiveModeWarden).requestSecondaryTransientClientModeManager( |
| any(), eq(ActiveModeWarden.INTERNAL_REQUESTOR_WS), any(), any()); |
| doAnswer(new AnswerWithArguments() { |
| public void answer(ExternalClientModeManagerRequestListener listener, |
| WorkSource requestorWs, String ssid, String bssid) { |
| listener.onAnswer(mPrimaryClientModeManager); |
| } |
| }).when(mActiveModeWarden).requestSecondaryLongLivedClientModeManager( |
| any(), any(), any(), any()); |
| |
| mWifiConnectivityManager = createConnectivityManager(); |
| mWifiConnectivityManager.setTrustedConnectionAllowed(true); |
| mWifiConnectivityManager.enableVerboseLogging(true); |
| setWifiEnabled(true); |
| when(mClock.getElapsedSinceBootMillis()).thenReturn(CURRENT_SYSTEM_TIME_MS); |
| when(mWifiLastResortWatchdog.shouldIgnoreBssidUpdate(anyString())).thenReturn(false); |
| mLruConnectionTracker = new LruConnectionTracker(100, mContext); |
| Comparator<WifiConfiguration> comparator = |
| Comparator.comparingInt(mLruConnectionTracker::getAgeIndexOfNetwork); |
| when(mWifiConfigManager.getScanListComparator()).thenReturn(comparator); |
| |
| // Need to mock WifiInjector since some code used in WifiConnectivityManager calls |
| // WifiInjector.getInstance(). |
| mSession = ExtendedMockito.mockitoSession() |
| .strictness(Strictness.LENIENT) |
| .mockStatic(WifiInjector.class, withSettings().lenient()) |
| .startMocking(); |
| WifiInjector wifiInjector = mock(WifiInjector.class); |
| when(wifiInjector.getActiveModeWarden()).thenReturn(mActiveModeWarden); |
| when(wifiInjector.getWifiGlobals()).thenReturn(mWifiGlobals); |
| lenient().when(WifiInjector.getInstance()).thenReturn(wifiInjector); |
| when(mSsidTranslator.getAllPossibleOriginalSsids(any())).thenAnswer( |
| (Answer<List<WifiSsid>>) invocation -> Arrays.asList(invocation.getArgument(0), |
| WifiSsid.fromString(UNTRANSLATED_HEX_SSID)) |
| ); |
| } |
| |
| private void setUpResources(MockResources resources) { |
| resources.setBoolean( |
| R.bool.config_wifi_framework_enable_associated_network_selection, true); |
| resources.setInteger( |
| R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_24GHz, -60); |
| resources.setInteger( |
| R.integer.config_wifiFrameworkMinPacketPerSecondActiveTraffic, 16); |
| resources.setIntArray( |
| R.array.config_wifiConnectedScanIntervalScheduleSec, |
| VALID_CONNECTED_SINGLE_SCAN_SCHEDULE_SEC); |
| resources.setIntArray( |
| R.array.config_wifiDisconnectedScanIntervalScheduleSec, |
| VALID_DISCONNECTED_SINGLE_SCAN_SCHEDULE_SEC); |
| resources.setIntArray(R.array.config_wifiConnectedScanType, |
| VALID_CONNECTED_SINGLE_SCAN_TYPE); |
| resources.setIntArray(R.array.config_wifiDisconnectedScanType, |
| VALID_DISCONNECTED_SINGLE_SCAN_TYPE); |
| resources.setIntArray( |
| R.array.config_wifiSingleSavedNetworkConnectedScanIntervalScheduleSec, |
| SCHEDULE_EMPTY_SEC); |
| resources.setInteger( |
| R.integer.config_wifiHighMovementNetworkSelectionOptimizationScanDelayMs, |
| HIGH_MVMT_SCAN_DELAY_MS); |
| resources.setInteger( |
| R.integer.config_wifiHighMovementNetworkSelectionOptimizationRssiDelta, |
| HIGH_MVMT_RSSI_DELTA); |
| resources.setInteger(R.integer.config_wifiInitialPartialScanChannelCacheAgeMins, |
| CHANNEL_CACHE_AGE_MINS); |
| resources.setInteger(R.integer.config_wifiMovingPnoScanIntervalMillis, |
| MOVING_PNO_SCAN_INTERVAL_MILLIS); |
| resources.setInteger(R.integer.config_wifiStationaryPnoScanIntervalMillis, |
| STATIONARY_PNO_SCAN_INTERVAL_MILLIS); |
| resources.setInteger(R.integer.config_wifiPnoScanLowRssiNetworkRetryStartDelaySec, |
| LOW_RSSI_NETWORK_RETRY_START_DELAY_SEC); |
| resources.setInteger(R.integer.config_wifiPnoScanLowRssiNetworkRetryMaxDelaySec, |
| LOW_RSSI_NETWORK_RETRY_MAX_DELAY_SEC); |
| resources.setBoolean(R.bool.config_wifiEnable6ghzPscScanning, true); |
| resources.setBoolean(R.bool.config_wifiUseHalApiToDisableFwRoaming, true); |
| } |
| |
| /** |
| * Called after each test |
| */ |
| @After |
| public void cleanup() { |
| verify(mScoringParams, atLeast(0)).getEntryRssi(anyInt()); |
| verifyNoMoreInteractions(mScoringParams); |
| validateMockitoUsage(); |
| if (mSession != null) { |
| mSession.finishMocking(); |
| } |
| } |
| |
| private WifiContext mContext; |
| private TestAlarmManager mAlarmManager; |
| private TestLooper mLooper; |
| private TestHandler mTestHandler; |
| private WifiConnectivityManager mWifiConnectivityManager; |
| private WifiNetworkSelector mWifiNS; |
| private WifiConnectivityHelper mWifiConnectivityHelper; |
| private ScanData mScanData; |
| private WifiConfigManager mWifiConfigManager; |
| private WifiInfo mWifiInfo; |
| private LocalLog mLocalLog; |
| private LruConnectionTracker mLruConnectionTracker; |
| @Mock private WifiScannerInternal mWifiScanner; |
| @Mock private Clock mClock; |
| @Mock private WifiLastResortWatchdog mWifiLastResortWatchdog; |
| @Mock private OpenNetworkNotifier mOpenNetworkNotifier; |
| @Mock private WifiMetrics mWifiMetrics; |
| @Mock private WifiNetworkSuggestionsManager mWifiNetworkSuggestionsManager; |
| @Mock private WifiBlocklistMonitor mWifiBlocklistMonitor; |
| @Mock private WifiChannelUtilization mWifiChannelUtilization; |
| @Mock private ScoringParams mScoringParams; |
| @Mock private WifiScoreCard mWifiScoreCard; |
| @Mock private PasspointManager mPasspointManager; |
| @Mock private FrameworkFacade mFacade; |
| @Mock private MultiInternetManager mMultiInternetManager; |
| @Mock private WifiScoreCard.PerNetwork mPerNetwork; |
| @Mock private WifiScoreCard.PerNetwork mPerNetwork1; |
| @Mock private PasspointConfiguration mPasspointConfiguration; |
| @Mock private WifiConfiguration mSuggestionConfig; |
| @Mock private WifiNetworkSuggestion mWifiNetworkSuggestion; |
| @Mock private IPowerManager mPowerManagerService; |
| @Mock private DeviceConfigFacade mDeviceConfigFacade; |
| @Mock private ActiveModeWarden mActiveModeWarden; |
| @Mock private ConcreteClientModeManager mPrimaryClientModeManager; |
| @Mock private ConcreteClientModeManager mSecondaryClientModeManager; |
| @Mock private WifiGlobals mWifiGlobals; |
| @Mock private ExternalPnoScanRequestManager mExternalPnoScanRequestManager; |
| @Mock private SsidTranslator mSsidTranslator; |
| @Mock private WifiPermissionsUtil mWifiPermissionsUtil; |
| @Mock WifiCandidates.Candidate mCandidate1; |
| @Mock WifiCandidates.Candidate mCandidate2; |
| @Mock WifiCandidates.Candidate mCandidate3; |
| private WifiConfiguration mCandidateWifiConfig1; |
| private WifiConfiguration mCandidateWifiConfig2; |
| private List<WifiCandidates.Candidate> mCandidateList; |
| @Captor ArgumentCaptor<String> mCandidateBssidCaptor; |
| @Captor ArgumentCaptor<WifiConfigManager.OnNetworkUpdateListener> |
| mNetworkUpdateListenerCaptor; |
| @Captor ArgumentCaptor<WifiNetworkSuggestionsManager.OnSuggestionUpdateListener> |
| mSuggestionUpdateListenerCaptor; |
| @Captor ArgumentCaptor<ActiveModeWarden.ModeChangeCallback> mModeChangeCallbackCaptor; |
| @Captor ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverCaptor; |
| @Captor ArgumentCaptor<MultiInternetManager.ConnectionStatusListener> |
| mMultiInternetConnectionStatusListenerCaptor; |
| private MockitoSession mSession; |
| private MockResources mResources; |
| |
| private static final int CANDIDATE_NETWORK_ID = 0; |
| private static final int CANDIDATE_NETWORK_ID_2 = 2; |
| private static final String CANDIDATE_SSID = "\"AnSsid\""; |
| private static final String CANDIDATE_SSID_2 = "\"AnSsid2\""; |
| private static final String CANDIDATE_BSSID = "6c:f3:7f:ae:8c:f3"; |
| private static final String CANDIDATE_BSSID_2 = "6c:f3:7f:ae:8d:f3"; |
| private static final String CANDIDATE_BSSID_3 = "6c:f3:7f:ae:8c:f4"; |
| private static final String INVALID_SCAN_RESULT_BSSID = "6c:f3:7f:ae:8c:f4"; |
| private static final int TEST_FREQUENCY = 2420; |
| private static final long CURRENT_SYSTEM_TIME_MS = 1000; |
| private static final int MAX_BSSID_BLOCKLIST_SIZE = 16; |
| |
| // Scan schedule and corresponding scan types |
| private static final int[] VALID_CONNECTED_SINGLE_SCAN_SCHEDULE_SEC = {10, 30, 50}; |
| private static final int[] VALID_CONNECTED_SINGLE_SAVED_NETWORK_SCHEDULE_SEC = {15, 35, 55}; |
| private static final int[] VALID_DISCONNECTED_SINGLE_SCAN_SCHEDULE_SEC = {25, 40, 60}; |
| private static final int[] VALID_CONNECTED_SINGLE_SCAN_TYPE = {1, 0, 0}; |
| private static final int[] VALID_CONNECTED_SINGLE_SAVED_NETWORK_TYPE = {2, 0, 1}; |
| private static final int[] VALID_DISCONNECTED_SINGLE_SCAN_TYPE = {2, 1, 1}; |
| private static final int[] VALID_EXTERNAL_SINGLE_SCAN_SCHEDULE_SEC = {40, 80}; |
| private static final int[] VALID_EXTERNAL_SINGLE_SCAN_TYPE = {1, 0}; |
| |
| private static final int[] SCHEDULE_EMPTY_SEC = {}; |
| private static final int[] INVALID_SCHEDULE_NEGATIVE_VALUES_SEC = {10, -10, 20}; |
| private static final int[] INVALID_SCHEDULE_ZERO_VALUES_SEC = {10, 0, 20}; |
| private static final int MAX_SCAN_INTERVAL_IN_SCHEDULE_SEC = 60; |
| private static final int[] DEFAULT_SINGLE_SCAN_SCHEDULE_SEC = {20, 40, 80, 160}; |
| private static final int[] DEFAULT_SINGLE_SCAN_TYPE = {2, 2, 2, 2}; |
| private static final int MAX_SCAN_INTERVAL_IN_DEFAULT_SCHEDULE_SEC = 160; |
| private static final int TEST_FREQUENCY_1 = 2412; |
| private static final int TEST_FREQUENCY_2 = 5180; |
| private static final int TEST_FREQUENCY_3 = 5240; |
| private static final int TEST_CURRENT_CONNECTED_FREQUENCY = 2427; |
| private static final int HIGH_MVMT_SCAN_DELAY_MS = 10000; |
| private static final int HIGH_MVMT_RSSI_DELTA = 10; |
| private static final String TEST_FQDN = "FQDN"; |
| private static final String TEST_SSID = "SSID"; |
| private static final String UNTRANSLATED_HEX_SSID = "abcdef"; |
| private static final int TEMP_BSSID_BLOCK_DURATION_MS = 10 * 1000; // 10 seconds |
| private static final int TEST_CONNECTED_NETWORK_ID = 55; |
| private static final String TEST_CONNECTED_BSSID = "6c:f3:7f:ae:8c:f1"; |
| private static final int CHANNEL_CACHE_AGE_MINS = 14400; |
| private static final int MOVING_PNO_SCAN_INTERVAL_MILLIS = 20_000; |
| private static final int STATIONARY_PNO_SCAN_INTERVAL_MILLIS = 60_000; |
| private static final int POWER_SAVE_SCAN_INTERVAL_MULTIPLIER = 2; |
| private static final int LOW_RSSI_NETWORK_RETRY_START_DELAY_SEC = 20; |
| private static final int LOW_RSSI_NETWORK_RETRY_MAX_DELAY_SEC = 80; |
| private static final int SCAN_TRIGGER_TIMES = 7; |
| private static final long NETWORK_CHANGE_TRIGGER_PNO_THROTTLE_MS = 3000; // 3 seconds |
| private static final int TEST_FREQUENCY_2G = 2412; |
| private static final int TEST_FREQUENCY_5G = 5262; |
| |
| /** |
| * A test Handler that stores one single incoming Message with delayed time internally, to be |
| * able to manually triggered by calling {@link #timeAdvance}. Only one delayed message can be |
| * scheduled at a time. The scheduled delayed message intervals are recorded and returned by |
| * {@link #getIntervals}. The intervals are cleared by calling {@link #reset}. |
| */ |
| private class TestHandler extends RunnerHandler { |
| private ArrayList<Long> mIntervals = new ArrayList<>(); |
| private Message mMessage; |
| |
| TestHandler(Looper looper) { |
| super(looper, 100, new LocalLog(128)); |
| } |
| |
| public List<Long> getIntervals() { |
| return mIntervals; |
| } |
| |
| public void reset() { |
| mIntervals.clear(); |
| } |
| |
| public void timeAdvance() { |
| if (mMessage != null) { |
| // Dispatch the message without waiting. |
| super.dispatchMessage(mMessage); |
| } |
| } |
| |
| @Override |
| public boolean sendMessageAtTime(Message msg, long uptimeMillis) { |
| // uptimeMillis is an absolute time obtained as SystemClock.uptimeMillis() + delay |
| // in Handler and can't be replaced with customized clock. |
| // if custom clock is given, recalculate the time with regards to it |
| long delayMs = uptimeMillis - SystemClock.uptimeMillis(); |
| if (delayMs > 0) { |
| mIntervals.add(delayMs); |
| mMessage = msg; |
| } |
| uptimeMillis = delayMs + mClock.getElapsedSinceBootMillis(); |
| // Message is still queued to super, so it doesn't get filtered out and rely on the |
| // timeAdvance() to dispatch. timeAdvance() can force time to advance and send the |
| // message immediately. If it is not called not the message can still be dispatched |
| // at the time the message is scheduled. |
| return super.sendMessageAtTime(msg, uptimeMillis); |
| } |
| } |
| |
| WifiContext mockContext() { |
| WifiContext context = mock(WifiContext.class); |
| |
| when(context.getResources()).thenReturn(mResources); |
| when(context.getSystemService(AlarmManager.class)).thenReturn( |
| mAlarmManager.getAlarmManager()); |
| when(context.getPackageManager()).thenReturn(mock(PackageManager.class)); |
| |
| return context; |
| } |
| |
| ScanData mockScanData() { |
| ScanData scanData = mock(ScanData.class); |
| |
| when(scanData.getScannedBandsInternal()).thenReturn(WifiScanner.WIFI_BAND_ALL); |
| |
| return scanData; |
| } |
| |
| void mockWifiScanner() { |
| ArgumentCaptor<WifiScannerInternal.ScanListener> allSingleScanListenerCaptor = |
| ArgumentCaptor.forClass(WifiScannerInternal.ScanListener.class); |
| |
| doNothing().when(mWifiScanner).registerScanListener(allSingleScanListenerCaptor.capture()); |
| |
| ScanData[] scanDatas = new ScanData[1]; |
| scanDatas[0] = mScanData; |
| |
| doAnswer(new AnswerWithArguments() { |
| public void answer(ScanSettings settings, WifiScannerInternal.ScanListener listener) |
| throws Exception { |
| listener.getWifiScannerListener().onResults(scanDatas); |
| // WCM processes scan results received via onFullResult (even though they're the |
| // same as onResult for single scans). |
| if (mScanData != null && mScanData.getResults() != null) { |
| for (int i = 0; i < mScanData.getResults().length; i++) { |
| allSingleScanListenerCaptor.getValue().getWifiScannerListener() |
| .onFullResult(mScanData.getResults()[i]); |
| } |
| } |
| allSingleScanListenerCaptor.getValue().getWifiScannerListener().onResults( |
| scanDatas); |
| }}).when(mWifiScanner).startScan(anyObject(), anyObject()); |
| |
| // This unfortunately needs to be a somewhat valid scan result, otherwise |
| // |ScanDetailUtil.toScanDetail| raises exceptions. |
| final ScanResult[] scanResults = new ScanResult[1]; |
| scanResults[0] = new ScanResult(WifiSsid.fromUtf8Text(CANDIDATE_SSID), |
| CANDIDATE_SSID, CANDIDATE_BSSID, 1245, 0, "some caps", |
| -78, 2450, 1025, 22, 33, 20, 0, 0, true); |
| scanResults[0].informationElements = new InformationElement[1]; |
| scanResults[0].informationElements[0] = new InformationElement(); |
| scanResults[0].informationElements[0].id = InformationElement.EID_SSID; |
| scanResults[0].informationElements[0].bytes = |
| CANDIDATE_SSID.getBytes(StandardCharsets.UTF_8); |
| |
| doAnswer(new AnswerWithArguments() { |
| public void answer(ScanSettings settings, PnoSettings pnoSettings, |
| WifiScannerInternal.ScanListener listener) throws Exception { |
| WifiScanner.PnoScanListener l = |
| (WifiScanner.PnoScanListener) listener.getWifiScannerListener(); |
| l.onPnoNetworkFound(scanResults); |
| }}).when(mWifiScanner).startPnoScan( |
| anyObject(), anyObject(), anyObject()); |
| |
| } |
| |
| WifiConnectivityHelper mockWifiConnectivityHelper() { |
| WifiConnectivityHelper connectivityHelper = mock(WifiConnectivityHelper.class); |
| |
| when(connectivityHelper.isFirmwareRoamingSupported()).thenReturn(false); |
| when(connectivityHelper.getMaxNumBlocklistBssid()).thenReturn(MAX_BSSID_BLOCKLIST_SIZE); |
| |
| return connectivityHelper; |
| } |
| |
| private void setupMockForClientModeManager(ConcreteClientModeManager cmm) { |
| when(cmm.getRole()).thenReturn(ActiveModeManager.ROLE_CLIENT_PRIMARY); |
| when(cmm.isConnected()).thenReturn(false); |
| when(cmm.isDisconnected()).thenReturn(true); |
| when(cmm.isSupplicantTransientState()).thenReturn(false); |
| when(cmm.enableRoaming(anyBoolean())).thenReturn(true); |
| } |
| |
| WifiNetworkSelector mockWifiNetworkSelector() { |
| WifiNetworkSelector ns = mock(WifiNetworkSelector.class); |
| |
| WifiConfiguration candidate = generateWifiConfig( |
| 0, CANDIDATE_NETWORK_ID, CANDIDATE_SSID, false, true, null, null, |
| WifiConfigurationTestUtil.SECURITY_NONE); |
| candidate.BSSID = ClientModeImpl.SUPPLICANT_BSSID_ANY; |
| ScanResult candidateScanResult = new ScanResult(); |
| candidateScanResult.SSID = CANDIDATE_SSID; |
| candidateScanResult.BSSID = CANDIDATE_BSSID; |
| candidate.getNetworkSelectionStatus().setCandidate(candidateScanResult); |
| mCandidateWifiConfig1 = candidate; |
| mCandidateWifiConfig2 = new WifiConfiguration(candidate); |
| mCandidateWifiConfig2.networkId = CANDIDATE_NETWORK_ID_2; |
| |
| when(mWifiConfigManager.getConfiguredNetwork(CANDIDATE_NETWORK_ID)).thenReturn(candidate); |
| MacAddress macAddress = MacAddress.fromString(CANDIDATE_BSSID); |
| ScanResultMatchInfo matchInfo = mock(ScanResultMatchInfo.class); |
| // Assume that this test use the default security params. |
| when(matchInfo.getDefaultSecurityParams()).thenReturn(candidate.getDefaultSecurityParams()); |
| WifiCandidates.Key key = new WifiCandidates.Key(matchInfo, |
| macAddress, 0); |
| when(mCandidate1.getKey()).thenReturn(key); |
| when(mCandidate1.getScanRssi()).thenReturn(-40); |
| when(mCandidate1.getFrequency()).thenReturn(TEST_FREQUENCY); |
| when(mCandidate2.getKey()).thenReturn(key); |
| when(mCandidate2.getScanRssi()).thenReturn(-60); |
| mCandidateList = new ArrayList<WifiCandidates.Candidate>(); |
| mCandidateList.add(mCandidate1); |
| when(ns.getCandidatesFromScan(any(), any(), any(), anyBoolean(), anyBoolean(), |
| anyBoolean(), any(), anyBoolean())).thenReturn(mCandidateList); |
| when(ns.selectNetwork(any())) |
| .then(new AnswerWithArguments() { |
| public WifiConfiguration answer(List<WifiCandidates.Candidate> candidateList) { |
| if (candidateList == null || candidateList.size() == 0) { |
| return null; |
| } |
| return candidate; |
| } |
| }); |
| when(ns.isSufficiencyCheckEnabled()).thenReturn(true); |
| when(ns.isAssociatedNetworkSelectionEnabled()).thenReturn(true); |
| return ns; |
| } |
| |
| WifiInfo getWifiInfo() { |
| WifiInfo wifiInfo = new WifiInfo(); |
| |
| wifiInfo.setNetworkId(WifiConfiguration.INVALID_NETWORK_ID); |
| wifiInfo.setBSSID(null); |
| wifiInfo.setSupplicantState(SupplicantState.DISCONNECTED); |
| |
| return wifiInfo; |
| } |
| |
| WifiConfigManager mockWifiConfigManager() { |
| WifiConfigManager wifiConfigManager = mock(WifiConfigManager.class); |
| WifiConfiguration config = WifiConfigurationTestUtil.createOpenNetwork(); |
| config.getNetworkSelectionStatus().setHasEverConnected(true); |
| List<WifiConfiguration> networkList = new ArrayList<>(); |
| networkList.add(config); |
| when(wifiConfigManager.getConfiguredNetwork(anyInt())).thenReturn(null); |
| when(wifiConfigManager.getSavedNetworks(anyInt())).thenReturn(networkList); |
| |
| return wifiConfigManager; |
| } |
| |
| WifiConnectivityManager createConnectivityManager() { |
| WifiConnectivityManager wCm = new WifiConnectivityManager(mContext, mScoringParams, |
| mWifiConfigManager, mWifiNetworkSuggestionsManager, |
| mWifiNS, mWifiConnectivityHelper, |
| mWifiLastResortWatchdog, mOpenNetworkNotifier, |
| mWifiMetrics, mTestHandler, mClock, |
| mLocalLog, mWifiScoreCard, mWifiBlocklistMonitor, mWifiChannelUtilization, |
| mPasspointManager, mMultiInternetManager, mDeviceConfigFacade, mActiveModeWarden, |
| mFacade, mWifiGlobals, mExternalPnoScanRequestManager, mSsidTranslator, |
| mWifiPermissionsUtil); |
| mLooper.dispatchAll(); |
| verify(mActiveModeWarden, atLeastOnce()).registerModeChangeCallback( |
| mModeChangeCallbackCaptor.capture()); |
| verify(mContext, atLeastOnce()).registerReceiver( |
| mBroadcastReceiverCaptor.capture(), any(), any(), any()); |
| verify(mWifiConfigManager, atLeastOnce()).addOnNetworkUpdateListener( |
| mNetworkUpdateListenerCaptor.capture()); |
| verify(mWifiNetworkSuggestionsManager, atLeastOnce()).addOnSuggestionUpdateListener( |
| mSuggestionUpdateListenerCaptor.capture()); |
| verify(mMultiInternetManager, atLeastOnce()).setConnectionStatusListener( |
| mMultiInternetConnectionStatusListenerCaptor.capture()); |
| return wCm; |
| } |
| |
| void setWifiStateConnected() { |
| setWifiStateConnected(TEST_CONNECTED_NETWORK_ID, TEST_CONNECTED_BSSID); |
| } |
| |
| void setWifiStateConnected(int networkId, String bssid) { |
| // Prep for setting WiFi to connected state |
| WifiConfiguration connectedWifiConfiguration = new WifiConfiguration(); |
| connectedWifiConfiguration.networkId = networkId; |
| when(mPrimaryClientModeManager.getConnectedWifiConfiguration()) |
| .thenReturn(connectedWifiConfiguration); |
| when(mPrimaryClientModeManager.getConnectedBssid()) |
| .thenReturn(bssid); |
| |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_CONNECTED); |
| mLooper.dispatchAll(); |
| } |
| |
| /** |
| * Verify that a primary CMM changing role to secondary transient (MBB) will not trigger cleanup |
| * that's meant to be done when wifi is disabled. |
| */ |
| @Test |
| public void testPrimaryToSecondaryTransientDoesNotDisableWifi() { |
| ConcreteClientModeManager cmm = mock(ConcreteClientModeManager.class); |
| when(cmm.getPreviousRole()).thenReturn(ROLE_CLIENT_PRIMARY); |
| when(cmm.getRole()).thenReturn(ROLE_CLIENT_SECONDARY_TRANSIENT); |
| when(mActiveModeWarden.getInternetConnectivityClientModeManagers()).thenReturn( |
| Collections.EMPTY_LIST); |
| mModeChangeCallbackCaptor.getValue().onActiveModeManagerRoleChanged(cmm); |
| verify(mWifiConfigManager, never()).removeAllEphemeralOrPasspointConfiguredNetworks(); |
| } |
| |
| /** |
| * Verify that the primary CMM switching to scan only mode will trigger cleanup code. |
| */ |
| @Test |
| public void testPrimaryToScanOnlyWillDisableWifi() { |
| ConcreteClientModeManager cmm = mock(ConcreteClientModeManager.class); |
| when(cmm.getPreviousRole()).thenReturn(ROLE_CLIENT_PRIMARY); |
| when(cmm.getRole()).thenReturn(ROLE_CLIENT_SCAN_ONLY); |
| when(mActiveModeWarden.getInternetConnectivityClientModeManagers()).thenReturn( |
| Collections.EMPTY_LIST); |
| mModeChangeCallbackCaptor.getValue().onActiveModeManagerRoleChanged(cmm); |
| verify(mWifiConfigManager).removeAllEphemeralOrPasspointConfiguredNetworks(); |
| } |
| |
| /** |
| * Don't connect to the candidate network if we're already connected to that network on the |
| * primary ClientModeManager. |
| */ |
| @Test |
| public void alreadyConnectedOnPrimaryCmm_dontConnectAgain() { |
| when(mWifiConnectivityHelper.isFirmwareRoamingSupported()).thenReturn(true); |
| // Set screen to on |
| setScreenState(true); |
| |
| WifiConfiguration config = new WifiConfiguration(); |
| config.networkId = CANDIDATE_NETWORK_ID; |
| when(mPrimaryClientModeManager.getConnectingWifiConfiguration()).thenReturn(config); |
| |
| // Set WiFi to disconnected state |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_DISCONNECTED); |
| mLooper.dispatchAll(); |
| verify(mActiveModeWarden, never()).requestSecondaryTransientClientModeManager( |
| any(), any(), any(), any()); |
| verify(mPrimaryClientModeManager, never()).startConnectToNetwork( |
| anyInt(), anyInt(), any()); |
| } |
| |
| /** Connect using the primary ClientModeManager if it's not connected to anything */ |
| @Test |
| public void disconnectedOnPrimaryCmm_connectUsingPrimaryCmm() { |
| when(mWifiConnectivityHelper.isFirmwareRoamingSupported()).thenReturn(true); |
| // Set screen to on |
| setScreenState(true); |
| |
| when(mPrimaryClientModeManager.getConnectedWifiConfiguration()).thenReturn(null); |
| when(mPrimaryClientModeManager.getConnectingWifiConfiguration()).thenReturn(null); |
| |
| // Set WiFi to disconnected state |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_DISCONNECTED); |
| mLooper.dispatchAll(); |
| verify(mPrimaryClientModeManager).startConnectToNetwork( |
| CANDIDATE_NETWORK_ID, Process.WIFI_UID, "any"); |
| verify(mPrimaryClientModeManager).enableRoaming(true); |
| verify(mActiveModeWarden).stopAllClientModeManagersInRole(ROLE_CLIENT_SECONDARY_TRANSIENT); |
| verify(mActiveModeWarden, never()).requestSecondaryTransientClientModeManager( |
| any(), any(), any(), any()); |
| } |
| |
| /** Don't crash if allocated a null ClientModeManager. */ |
| @Test |
| public void requestSecondaryTransientCmm_gotNullCmm() { |
| doAnswer(new AnswerWithArguments() { |
| public void answer(ExternalClientModeManagerRequestListener listener, |
| WorkSource requestorWs, String ssid, String bssid) { |
| listener.onAnswer(null); |
| } |
| }).when(mActiveModeWarden).requestSecondaryTransientClientModeManager( |
| any(), eq(ActiveModeWarden.INTERNAL_REQUESTOR_WS), any(), any()); |
| |
| // primary CMM already connected |
| WifiConfiguration config2 = new WifiConfiguration(); |
| config2.networkId = CANDIDATE_NETWORK_ID_2; |
| when(mPrimaryClientModeManager.getConnectedWifiConfiguration()) |
| .thenReturn(config2); |
| |
| // Set screen to on |
| setScreenState(true); |
| |
| // Set WiFi to disconnected state |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_DISCONNECTED); |
| mLooper.dispatchAll(); |
| verify(mActiveModeWarden).requestSecondaryTransientClientModeManager( |
| any(), |
| eq(ActiveModeWarden.INTERNAL_REQUESTOR_WS), |
| eq(CANDIDATE_SSID), |
| eq(CANDIDATE_BSSID)); |
| verify(mPrimaryClientModeManager, never()).startConnectToNetwork( |
| anyInt(), anyInt(), any()); |
| } |
| |
| /** |
| * Don't attempt to connect again if the allocated ClientModeManager is already connected to |
| * the desired network. |
| */ |
| @Test |
| public void requestSecondaryTransientCmm_gotAlreadyConnectedCmm() { |
| when(mWifiConnectivityHelper.isFirmwareRoamingSupported()).thenReturn(true); |
| |
| WifiConfiguration config = new WifiConfiguration(); |
| config.networkId = CANDIDATE_NETWORK_ID; |
| ClientModeManager alreadyConnectedCmm = mock(ClientModeManager.class); |
| when(alreadyConnectedCmm.getConnectingWifiConfiguration()).thenReturn(config); |
| |
| doAnswer(new AnswerWithArguments() { |
| public void answer(ExternalClientModeManagerRequestListener listener, |
| WorkSource requestorWs, String ssid, String bssid) { |
| listener.onAnswer(alreadyConnectedCmm); |
| } |
| }).when(mActiveModeWarden).requestSecondaryTransientClientModeManager( |
| any(), eq(ActiveModeWarden.INTERNAL_REQUESTOR_WS), any(), any()); |
| |
| // primary CMM already connected |
| WifiConfiguration config2 = new WifiConfiguration(); |
| config2.networkId = CANDIDATE_NETWORK_ID_2; |
| when(mPrimaryClientModeManager.getConnectedWifiConfiguration()) |
| .thenReturn(config2); |
| |
| // Set screen to on |
| setScreenState(true); |
| |
| // Set WiFi to disconnected state |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_DISCONNECTED); |
| mLooper.dispatchAll(); |
| verify(mActiveModeWarden).requestSecondaryTransientClientModeManager( |
| any(), |
| eq(ActiveModeWarden.INTERNAL_REQUESTOR_WS), |
| eq(CANDIDATE_SSID), |
| eq(null)); |
| |
| // already connected, don't connect again |
| verify(alreadyConnectedCmm, never()).startConnectToNetwork( |
| anyInt(), anyInt(), any()); |
| } |
| |
| /** |
| * Verify MBB full flow. |
| */ |
| @Test |
| public void connectWhenConnected_UsingMbb() { |
| when(mWifiConnectivityHelper.isFirmwareRoamingSupported()).thenReturn(true); |
| |
| ClientModeManager mbbCmm = mock(ClientModeManager.class); |
| doAnswer(new AnswerWithArguments() { |
| public void answer(ExternalClientModeManagerRequestListener listener, |
| WorkSource requestorWs, String ssid, String bssid) { |
| listener.onAnswer(mbbCmm); |
| } |
| }).when(mActiveModeWarden).requestSecondaryTransientClientModeManager( |
| any(), eq(ActiveModeWarden.INTERNAL_REQUESTOR_WS), any(), any()); |
| |
| // primary CMM already connected |
| when(mPrimaryClientModeManager.getConnectedWifiConfiguration()) |
| .thenReturn(mCandidateWifiConfig2); |
| |
| // Set screen to on |
| setScreenState(true); |
| |
| // Set WiFi to connected state |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_CONNECTED); |
| mLooper.dispatchAll(); |
| // Request secondary STA and connect using it. |
| verify(mActiveModeWarden).requestSecondaryTransientClientModeManager( |
| any(), |
| eq(ActiveModeWarden.INTERNAL_REQUESTOR_WS), |
| eq(CANDIDATE_SSID), |
| eq(null)); |
| verify(mbbCmm).startConnectToNetwork(eq(CANDIDATE_NETWORK_ID), anyInt(), any()); |
| } |
| |
| /** |
| * Fallback to single STA behavior when both networks have MAC randomization disabled. |
| */ |
| @Test |
| public void connectWhenConnected_UsingBbmIfBothNetworksHaveMacRandomizationDisabled() { |
| when(mWifiConnectivityHelper.isFirmwareRoamingSupported()).thenReturn(true); |
| |
| ClientModeManager mbbCmm = mock(ClientModeManager.class); |
| doAnswer(new AnswerWithArguments() { |
| public void answer(ExternalClientModeManagerRequestListener listener, |
| WorkSource requestorWs, String ssid, String bssid) { |
| listener.onAnswer(mbbCmm); |
| } |
| }).when(mActiveModeWarden).requestSecondaryTransientClientModeManager( |
| any(), eq(ActiveModeWarden.INTERNAL_REQUESTOR_WS), any(), any()); |
| |
| // Turn off MAC randomization on both networks. |
| mCandidateWifiConfig1.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_NONE; |
| mCandidateWifiConfig2.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_NONE; |
| |
| // primary CMM already connected |
| when(mPrimaryClientModeManager.getConnectedWifiConfiguration()) |
| .thenReturn(mCandidateWifiConfig2); |
| |
| // Set screen to on |
| setScreenState(true); |
| |
| // Set WiFi to connected state |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_CONNECTED); |
| mLooper.dispatchAll(); |
| // Don't request secondary STA, fallback to primary STA. |
| verify(mActiveModeWarden, never()).requestSecondaryTransientClientModeManager( |
| any(), any(), any(), any()); |
| verify(mbbCmm, never()).startConnectToNetwork(anyInt(), anyInt(), any()); |
| verify(mPrimaryClientModeManager).startConnectToNetwork( |
| eq(CANDIDATE_NETWORK_ID), anyInt(), any()); |
| verify(mPrimaryClientModeManager).enableRoaming(true); |
| } |
| |
| /** |
| * Setup all the mocks for the positive case, individual negative test cases below override |
| * specific params. |
| */ |
| private void setupMocksForSecondaryLongLivedTests() { |
| when(mWifiConnectivityHelper.isFirmwareRoamingSupported()).thenReturn(true); |
| when(mCandidate1.isOemPaid()).thenReturn(true); |
| when(mCandidate1.isOemPrivate()).thenReturn(true); |
| mCandidateWifiConfig1.oemPaid = true; |
| mCandidateWifiConfig1.oemPrivate = true; |
| when(mWifiNS.selectNetwork(argThat( |
| candidates -> (candidates != null && candidates.size() == 1 |
| && (candidates.get(0).isOemPaid() || candidates.get(0).isOemPrivate())) |
| ))).thenReturn(mCandidateWifiConfig1); |
| when(mActiveModeWarden.isStaStaConcurrencySupportedForRestrictedConnections()) |
| .thenReturn(true); |
| when(mActiveModeWarden.canRequestMoreClientModeManagersInRole( |
| any(), eq(ROLE_CLIENT_SECONDARY_LONG_LIVED), eq(false))).thenReturn(true); |
| doAnswer(new AnswerWithArguments() { |
| public void answer(ExternalClientModeManagerRequestListener listener, |
| WorkSource requestorWs, String ssid, String bssid) { |
| listener.onAnswer(mSecondaryClientModeManager); |
| } |
| }).when(mActiveModeWarden).requestSecondaryLongLivedClientModeManager( |
| any(), any(), any(), any()); |
| when(mSecondaryClientModeManager.getRole()).thenReturn(ROLE_CLIENT_SECONDARY_LONG_LIVED); |
| } |
| |
| @Test |
| public void secondaryLongLived_noOemPaidOrOemPrivateConnectionAllowed() { |
| setupMocksForSecondaryLongLivedTests(); |
| |
| // Set screen to on |
| setScreenState(true); |
| |
| // OEM paid/OEM private connection disallowed. |
| mWifiConnectivityManager.setOemPaidConnectionAllowed(false, null); |
| mWifiConnectivityManager.setOemPrivateConnectionAllowed(false, null); |
| |
| // Set WiFi to disconnected state |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_DISCONNECTED); |
| mLooper.dispatchAll(); |
| verify(mPrimaryClientModeManager).startConnectToNetwork( |
| CANDIDATE_NETWORK_ID, Process.WIFI_UID, "any"); |
| verify(mActiveModeWarden, never()).requestSecondaryLongLivedClientModeManager( |
| any(), any(), any(), any()); |
| } |
| |
| @Test |
| public void secondaryLongLived_oemPaidConnectionAllowedWithOemPrivateCandidate() { |
| setupMocksForSecondaryLongLivedTests(); |
| |
| // Set screen to on |
| setScreenState(true); |
| |
| // OEM paid connection allowed. |
| mWifiConnectivityManager.setOemPaidConnectionAllowed(true, new WorkSource()); |
| |
| // Mark the candidate oem private only |
| when(mCandidate1.isOemPaid()).thenReturn(false); |
| when(mCandidate1.isOemPrivate()).thenReturn(true); |
| mCandidateWifiConfig1.oemPaid = false; |
| mCandidateWifiConfig1.oemPrivate = true; |
| |
| // Set WiFi to disconnected state |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_DISCONNECTED); |
| mLooper.dispatchAll(); |
| verify(mPrimaryClientModeManager).startConnectToNetwork( |
| CANDIDATE_NETWORK_ID, Process.WIFI_UID, "any"); |
| verify(mActiveModeWarden, never()).requestSecondaryLongLivedClientModeManager( |
| any(), any(), any(), any()); |
| } |
| |
| @Test |
| public void secondaryLongLived_oemPrivateConnectionAllowedWithOemPaidCandidate() { |
| setupMocksForSecondaryLongLivedTests(); |
| |
| // Set screen to on |
| setScreenState(true); |
| |
| // OEM private connection allowed. |
| mWifiConnectivityManager.setOemPrivateConnectionAllowed(true, new WorkSource()); |
| |
| // Mark the candidate oem paid only |
| when(mCandidate1.isOemPaid()).thenReturn(true); |
| when(mCandidate1.isOemPrivate()).thenReturn(false); |
| mCandidateWifiConfig1.oemPaid = true; |
| mCandidateWifiConfig1.oemPrivate = false; |
| |
| // Set WiFi to disconnected state |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_DISCONNECTED); |
| mLooper.dispatchAll(); |
| verify(mPrimaryClientModeManager).startConnectToNetwork( |
| CANDIDATE_NETWORK_ID, Process.WIFI_UID, "any"); |
| verify(mActiveModeWarden, never()).requestSecondaryLongLivedClientModeManager( |
| any(), any(), any(), any()); |
| } |
| |
| @Test |
| public void secondaryLongLived_noSecondaryStaSupport() { |
| setupMocksForSecondaryLongLivedTests(); |
| |
| // Set screen to on |
| setScreenState(true); |
| |
| // OEM paid connection allowed. |
| mWifiConnectivityManager.setOemPaidConnectionAllowed(true, new WorkSource()); |
| |
| // STA + STA is not supported. |
| when(mActiveModeWarden.isStaStaConcurrencySupportedForRestrictedConnections()) |
| .thenReturn(false); |
| when(mActiveModeWarden.isStaStaConcurrencySupportedForMultiInternet()) |
| .thenReturn(false); |
| |
| // Set WiFi to disconnected state |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_DISCONNECTED); |
| mLooper.dispatchAll(); |
| verify(mPrimaryClientModeManager).startConnectToNetwork( |
| CANDIDATE_NETWORK_ID, Process.WIFI_UID, "any"); |
| verify(mActiveModeWarden, never()).requestSecondaryLongLivedClientModeManager( |
| any(), any(), any(), any()); |
| } |
| |
| @Test |
| public void secondaryLongLived_noSecondaryCandidateSelected() { |
| setupMocksForSecondaryLongLivedTests(); |
| |
| // Set screen to on |
| setScreenState(true); |
| |
| // OEM paid connection allowed. |
| mWifiConnectivityManager.setOemPaidConnectionAllowed(true, new WorkSource()); |
| |
| // Network selection does not select a secondary candidate. |
| when(mWifiNS.selectNetwork(argThat( |
| candidates -> (candidates != null && candidates.size() == 1 |
| && (candidates.get(0).isOemPaid() || candidates.get(0).isOemPrivate())) |
| ))).thenReturn(null) // first for secondary returns null. |
| .thenReturn(mCandidateWifiConfig1); // second for primary returns something. |
| |
| // Set WiFi to disconnected state |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_DISCONNECTED); |
| mLooper.dispatchAll(); |
| verify(mPrimaryClientModeManager).startConnectToNetwork( |
| CANDIDATE_NETWORK_ID, Process.WIFI_UID, "any"); |
| verify(mActiveModeWarden, never()).requestSecondaryLongLivedClientModeManager( |
| any(), any(), any(), any()); |
| } |
| |
| @Test |
| public void secondaryLongLived_secondaryStaRequestReturnsNull() { |
| setupMocksForSecondaryLongLivedTests(); |
| |
| // Set screen to on |
| setScreenState(true); |
| |
| // OEM paid connection allowed. |
| mWifiConnectivityManager.setOemPaidConnectionAllowed(true, new WorkSource()); |
| |
| // STA + STA is supported, but secondary STA request returns null |
| doAnswer(new AnswerWithArguments() { |
| public void answer(ExternalClientModeManagerRequestListener listener, |
| WorkSource requestorWs, String ssid, String bssid) { |
| listener.onAnswer(null); |
| } |
| }).when(mActiveModeWarden).requestSecondaryLongLivedClientModeManager( |
| any(), any(), any(), any()); |
| |
| // Set WiFi to disconnected state |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_DISCONNECTED); |
| mLooper.dispatchAll(); |
| // No connection triggered (even on primary since wifi is off). |
| verify(mPrimaryClientModeManager, never()).startConnectToNetwork( |
| CANDIDATE_NETWORK_ID, Process.WIFI_UID, "any"); |
| verify(mActiveModeWarden).requestSecondaryLongLivedClientModeManager( |
| any(), any(), any(), any()); |
| } |
| |
| @Test |
| public void secondaryLongLived_secondaryStaRequestReturnsPrimary() { |
| setupMocksForSecondaryLongLivedTests(); |
| |
| // Set screen to on |
| setScreenState(true); |
| |
| // OEM paid connection allowed. |
| mWifiConnectivityManager.setOemPaidConnectionAllowed(true, new WorkSource()); |
| |
| // STA + STA is supported, but secondary STA request returns the primary |
| doAnswer(new AnswerWithArguments() { |
| public void answer(ExternalClientModeManagerRequestListener listener, |
| WorkSource requestorWs, String ssid, String bssid) { |
| listener.onAnswer(mPrimaryClientModeManager); |
| } |
| }).when(mActiveModeWarden).requestSecondaryLongLivedClientModeManager( |
| any(), any(), any(), any()); |
| |
| // Set WiFi to disconnected state |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_DISCONNECTED); |
| mLooper.dispatchAll(); |
| // connection triggered on primary |
| verify(mPrimaryClientModeManager).startConnectToNetwork( |
| CANDIDATE_NETWORK_ID, Process.WIFI_UID, "any"); |
| verify(mActiveModeWarden).requestSecondaryLongLivedClientModeManager( |
| any(), any(), any(), any()); |
| } |
| |
| @Test |
| public void secondaryLongLived_secondaryStaRequestSucceedsWithOemPaidConnectionAllowed() { |
| setupMocksForSecondaryLongLivedTests(); |
| |
| // Set screen to on |
| setScreenState(true); |
| |
| // OEM paid connection allowed. |
| mWifiConnectivityManager.setOemPaidConnectionAllowed(true, new WorkSource()); |
| |
| // Mark the candidate oem paid only |
| when(mCandidate1.isOemPaid()).thenReturn(true); |
| when(mCandidate1.isOemPrivate()).thenReturn(false); |
| mCandidateWifiConfig1.oemPaid = true; |
| mCandidateWifiConfig1.oemPrivate = false; |
| |
| // Set WiFi to disconnected state |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_DISCONNECTED); |
| mLooper.dispatchAll(); |
| // connection triggered on secondary |
| verify(mPrimaryClientModeManager, never()).startConnectToNetwork( |
| CANDIDATE_NETWORK_ID, Process.WIFI_UID, "any"); |
| verify(mSecondaryClientModeManager).startConnectToNetwork( |
| CANDIDATE_NETWORK_ID, Process.WIFI_UID, "any"); |
| verify(mActiveModeWarden).requestSecondaryLongLivedClientModeManager( |
| any(), any(), any(), any()); |
| |
| // Simulate connection failing on the secondary |
| clearInvocations(mSecondaryClientModeManager, mPrimaryClientModeManager, mWifiNS); |
| WifiConfiguration config = WifiConfigurationTestUtil.createPskNetwork(CANDIDATE_SSID); |
| mWifiConnectivityManager.handleConnectionAttemptEnded( |
| mSecondaryClientModeManager, |
| WifiMetrics.ConnectionEvent.FAILURE_ASSOCIATION_REJECTION, |
| WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, CANDIDATE_BSSID, |
| config); |
| // verify connection is never restarted when a connection on the secondary STA fails. |
| verify(mWifiNS, never()).selectNetwork(any()); |
| verify(mSecondaryClientModeManager, never()).startConnectToNetwork( |
| anyInt(), anyInt(), any()); |
| verify(mPrimaryClientModeManager, never()).startConnectToNetwork( |
| anyInt(), anyInt(), any()); |
| } |
| |
| @Test |
| public void secondaryLongLived_secondaryStaRequestSucceedsWithOemPrivateConnectionAllowed() { |
| setupMocksForSecondaryLongLivedTests(); |
| |
| // Set screen to on |
| setScreenState(true); |
| |
| // OEM paid connection allowed. |
| mWifiConnectivityManager.setOemPrivateConnectionAllowed(true, new WorkSource()); |
| |
| // Mark the candidate oem private only |
| when(mCandidate1.isOemPaid()).thenReturn(false); |
| when(mCandidate1.isOemPrivate()).thenReturn(true); |
| mCandidateWifiConfig1.oemPaid = false; |
| mCandidateWifiConfig1.oemPrivate = true; |
| |
| // Set WiFi to disconnected state |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_DISCONNECTED); |
| mLooper.dispatchAll(); |
| // connection triggered on secondary |
| verify(mPrimaryClientModeManager, never()).startConnectToNetwork( |
| CANDIDATE_NETWORK_ID, Process.WIFI_UID, "any"); |
| verify(mSecondaryClientModeManager).startConnectToNetwork( |
| CANDIDATE_NETWORK_ID, Process.WIFI_UID, "any"); |
| verify(mActiveModeWarden).requestSecondaryLongLivedClientModeManager( |
| any(), any(), any(), any()); |
| } |
| |
| @Test |
| public void secondaryLongLived_secondaryStaRequestSucceedsAlongWithPrimary() { |
| setupMocksForSecondaryLongLivedTests(); |
| |
| // 2 candidates - 1 oem paid, other regular. |
| // Mark the first candidate oem private only |
| when(mCandidate1.isOemPaid()).thenReturn(false); |
| when(mCandidate1.isOemPrivate()).thenReturn(true); |
| mCandidateWifiConfig1.oemPaid = false; |
| mCandidateWifiConfig1.oemPrivate = true; |
| |
| // Add the second regular candidate. |
| mCandidateList.add(mCandidate2); |
| |
| // Set screen to on |
| setScreenState(true); |
| |
| // OEM paid connection allowed. |
| mWifiConnectivityManager.setOemPrivateConnectionAllowed(true, new WorkSource()); |
| |
| // Network selection setup for primary. |
| when(mWifiNS.selectNetwork(argThat( |
| candidates -> (candidates != null && candidates.size() == 1 |
| // not oem paid or oem private. |
| && !(candidates.get(0).isOemPaid() || candidates.get(0).isOemPrivate())) |
| ))).thenReturn(mCandidateWifiConfig2); |
| |
| // Set WiFi to disconnected state |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_DISCONNECTED); |
| mLooper.dispatchAll(); |
| // connection triggered on primary & secondary |
| verify(mPrimaryClientModeManager).startConnectToNetwork( |
| CANDIDATE_NETWORK_ID_2, Process.WIFI_UID, "any"); |
| verify(mSecondaryClientModeManager).startConnectToNetwork( |
| CANDIDATE_NETWORK_ID, Process.WIFI_UID, "any"); |
| verify(mActiveModeWarden).requestSecondaryLongLivedClientModeManager( |
| any(), any(), any(), any()); |
| } |
| |
| /** |
| * Verify that when the secondary is already connecting to the selected secondary network, |
| * we only connect the primary STA. |
| */ |
| @Test |
| public void secondaryLongLived_secondaryStaRequestSucceedsWhenSecondaryAlreadyConnecting() { |
| setupMocksForSecondaryLongLivedTests(); |
| |
| // 2 candidates - 1 oem paid, other regular. |
| // Mark the first candidate oem private only |
| when(mCandidate1.isOemPaid()).thenReturn(false); |
| when(mCandidate1.isOemPrivate()).thenReturn(true); |
| mCandidateWifiConfig1.oemPaid = false; |
| mCandidateWifiConfig1.oemPrivate = true; |
| |
| // mock secondary STA to already connecting to the target OEM private network |
| when(mSecondaryClientModeManager.getConnectingWifiConfiguration()).thenReturn( |
| mCandidateWifiConfig1); |
| |
| // Add the second regular candidate. |
| mCandidateList.add(mCandidate2); |
| |
| // Set screen to on |
| setScreenState(true); |
| |
| // OEM paid connection allowed. |
| mWifiConnectivityManager.setOemPrivateConnectionAllowed(true, new WorkSource()); |
| |
| // Network selection setup for primary. |
| when(mWifiNS.selectNetwork(argThat( |
| candidates -> (candidates != null && candidates.size() == 1 |
| // not oem paid or oem private. |
| && !(candidates.get(0).isOemPaid() || candidates.get(0).isOemPrivate())) |
| ))).thenReturn(mCandidateWifiConfig2); |
| |
| // Set WiFi to disconnected state |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_DISCONNECTED); |
| mLooper.dispatchAll(); |
| // connection triggered on only on primary to CANDIDATE_NETWORK_ID_2. |
| verify(mPrimaryClientModeManager).startConnectToNetwork( |
| CANDIDATE_NETWORK_ID_2, Process.WIFI_UID, "any"); |
| verify(mSecondaryClientModeManager, never()).startConnectToNetwork( |
| CANDIDATE_NETWORK_ID, Process.WIFI_UID, "any"); |
| verify(mActiveModeWarden).requestSecondaryLongLivedClientModeManager( |
| any(), any(), eq(CANDIDATE_SSID), any()); |
| } |
| |
| /** |
| * Create scan data with different bands of 2G and 5G. |
| */ |
| private ScanData createScanDataWithDifferentBands() { |
| // Create 4 scan results. |
| ScanData[] scanDatas = |
| ScanTestUtil.createScanDatas(new int[][]{{5150, 5175, 2412, 2400}}, new int[]{0}); |
| // WCM barfs if the scan result does not have an IE. |
| return scanDatas[0]; |
| } |
| |
| private WifiConfiguration getTestWifiConfig(int networkId, String ssid) { |
| WifiConfiguration config = generateWifiConfig( |
| networkId, 0, ssid, false, true, null, null, |
| WifiConfigurationTestUtil.SECURITY_PSK); |
| config.BSSID = ClientModeImpl.SUPPLICANT_BSSID_ANY; |
| config.oemPaid = false; |
| config.oemPrivate = false; |
| config.ephemeral = true; |
| when(mWifiConfigManager.getConfiguredNetwork(networkId)).thenReturn(config); |
| return config; |
| } |
| |
| private WifiCandidates.Candidate getTestWifiCandidate(int networkId, String ssid, String bssid, |
| int rssi, int frequency) { |
| WifiCandidates.Candidate candidate = mock(WifiCandidates.Candidate.class); |
| when(candidate.isOemPaid()).thenReturn(false); |
| when(candidate.isOemPrivate()).thenReturn(false); |
| |
| // Set up the scan candidates |
| ScanResult result = new ScanResult(WifiSsid.fromString(ssid), |
| ssid, bssid, 1245, 0, "some caps", rssi, frequency, |
| 1025, 22, 33, 20, 0, 0, true); |
| ScanResultMatchInfo matchInfo = ScanResultMatchInfo.fromScanResult(result); |
| WifiCandidates.Key key = new WifiCandidates.Key(matchInfo, MacAddress.fromString(bssid), |
| networkId, WifiConfiguration.SECURITY_TYPE_PSK); |
| when(candidate.getKey()).thenReturn(key); |
| when(candidate.getScanRssi()).thenReturn(rssi); |
| when(candidate.getFrequency()).thenReturn(frequency); |
| return candidate; |
| } |
| |
| /** |
| * Set up the mocks for the multi internet use case unit tests. |
| */ |
| private void setupMocksForMultiInternetTests(boolean isDbs) { |
| mCandidateWifiConfig1 = getTestWifiConfig(CANDIDATE_NETWORK_ID, CANDIDATE_SSID); |
| mCandidateWifiConfig2 = getTestWifiConfig(CANDIDATE_NETWORK_ID_2, CANDIDATE_SSID_2); |
| |
| mScanData = createScanDataWithDifferentBands(); |
| when(mWifiConnectivityHelper.isFirmwareRoamingSupported()).thenReturn(true); |
| when(mActiveModeWarden.isStaStaConcurrencySupportedForMultiInternet()).thenReturn(true); |
| when(mActiveModeWarden.getPrimaryClientModeManagerNullable()) |
| .thenReturn(mPrimaryClientModeManager); |
| when(mActiveModeWarden.isStaStaConcurrencySupportedForRestrictedConnections()) |
| .thenReturn(true); |
| when(mActiveModeWarden.canRequestMoreClientModeManagersInRole( |
| any(), eq(ROLE_CLIENT_SECONDARY_LONG_LIVED), eq(false))).thenReturn(true); |
| mCandidate1 = getTestWifiCandidate(CANDIDATE_NETWORK_ID, CANDIDATE_SSID, CANDIDATE_BSSID, |
| -40, |
| TEST_FREQUENCY); |
| mCandidate2 = getTestWifiCandidate(CANDIDATE_NETWORK_ID_2, CANDIDATE_SSID_2, |
| CANDIDATE_BSSID_2, -60, |
| TEST_FREQUENCY_2); |
| // A DBS candidate with same SSID as mCandidate1 |
| mCandidate3 = getTestWifiCandidate(CANDIDATE_NETWORK_ID, CANDIDATE_SSID, CANDIDATE_BSSID_3, |
| -40, |
| TEST_FREQUENCY_3); |
| mCandidateList = new ArrayList<WifiCandidates.Candidate>( |
| Arrays.asList(mCandidate1, mCandidate2)); |
| if (isDbs) mCandidateList.add(mCandidate3); |
| when(mWifiNS.getCandidatesFromScan(any(), any(), any(), anyBoolean(), anyBoolean(), |
| anyBoolean(), any(), anyBoolean())).thenReturn(mCandidateList); |
| |
| doAnswer(new AnswerWithArguments() { |
| public void answer(ExternalClientModeManagerRequestListener listener, |
| WorkSource requestorWs, String ssid, String bssid) { |
| listener.onAnswer(mSecondaryClientModeManager); |
| } |
| }).when(mActiveModeWarden).requestSecondaryLongLivedClientModeManager( |
| any(), any(), any(), any()); |
| when(mSecondaryClientModeManager.getRole()).thenReturn(ROLE_CLIENT_SECONDARY_LONG_LIVED); |
| } |
| |
| /** |
| * Set up the primary network selection mocks for the multi internet use case unit tests. |
| */ |
| private void setupMockPrimaryNetworkSelect(int networkId, String bssid, int rssi, |
| int frequency) { |
| WifiConfiguration config = mWifiConfigManager.getConfiguredNetwork(networkId); |
| config.getNetworkSelectionStatus().setCandidate( |
| new ScanResult(WifiSsid.fromUtf8Text(config.SSID), |
| config.SSID, bssid, 1245, 0, "some caps", rssi, frequency, |
| 1025, 22, 33, 20, 0, 0, true)); |
| // Selection for primary |
| when(mWifiNS.selectNetwork(any())) |
| .then(new AnswerWithArguments() { |
| public WifiConfiguration answer(List<WifiCandidates.Candidate> candidateList) { |
| if (candidateList != null) { |
| for (WifiCandidates.Candidate candidate : candidateList) { |
| if (networkId == candidate.getKey().networkId) { |
| return config; |
| } |
| } |
| } |
| return null; |
| } |
| }); |
| } |
| |
| /** |
| * Set up the secondary network selection mocks for the multi internet use case unit tests. |
| */ |
| private void setupMockSecondaryNetworkSelect(int networkId, String bssid, int rssi, |
| int frequency) { |
| WifiConfiguration config = mWifiConfigManager.getConfiguredNetwork(networkId); |
| config.getNetworkSelectionStatus().setCandidate( |
| new ScanResult(WifiSsid.fromUtf8Text(config.SSID), |
| config.SSID, bssid, 1245, 0, "some caps", rssi, frequency, |
| 1025, 22, 33, 20, 0, 0, true)); |
| // Selection for secondary |
| when(mWifiNS.selectNetwork(any(), anyBoolean())) |
| .then(new AnswerWithArguments() { |
| public WifiConfiguration answer(List<WifiCandidates.Candidate> candidateList, |
| boolean override) { |
| if (candidateList != null) { |
| for (WifiCandidates.Candidate candidate : candidateList) { |
| if (networkId == candidate.getKey().networkId) { |
| return config; |
| } |
| } |
| } |
| return null; |
| } |
| }); |
| } |
| |
| /** |
| * Set up the client manager wifi info mocks for the multi internet use case unit tests. |
| */ |
| private void mockClientManagerInfo(ConcreteClientModeManager clientManager, |
| WifiConfiguration configuration) { |
| WifiInfo wifiInfo = getWifiInfo(); |
| wifiInfo.setNetworkId(configuration.networkId); |
| wifiInfo.setSSID(WifiSsid.fromString(configuration.SSID)); |
| wifiInfo.setBSSID(configuration.BSSID); |
| wifiInfo.setFrequency( |
| configuration.getNetworkSelectionStatus().getCandidate().frequency); |
| wifiInfo.setCurrentSecurityType(WifiConfiguration.SECURITY_TYPE_PSK); |
| when(clientManager.isConnected()).thenReturn(true); |
| when(clientManager.isDisconnected()).thenReturn(false); |
| when(clientManager.getConnectionInfo()).thenReturn(wifiInfo); |
| } |
| |
| private void testMultiInternetSecondaryConnectionRequest(boolean isDbsOnly, boolean isDhcp, |
| boolean success) { |
| when(mMultiInternetManager.isStaConcurrencyForMultiInternetMultiApAllowed()) |
| .thenReturn(!isDbsOnly); |
| setupMockPrimaryNetworkSelect(CANDIDATE_NETWORK_ID, CANDIDATE_BSSID, -50, TEST_FREQUENCY); |
| WifiConfiguration targetConfig = isDbsOnly ? mCandidateWifiConfig1 : mCandidateWifiConfig2; |
| String targetBssid = isDbsOnly ? CANDIDATE_BSSID_3 : CANDIDATE_BSSID_2; |
| targetConfig.setIpAssignment( |
| isDhcp ? IpConfiguration.IpAssignment.DHCP : IpConfiguration.IpAssignment.STATIC); |
| setupMockSecondaryNetworkSelect(targetConfig.networkId, targetBssid, -50, TEST_FREQUENCY_3); |
| // Set screen to on |
| setScreenState(true); |
| |
| // Set WiFi to disconnected state to trigger scan |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_DISCONNECTED); |
| mLooper.dispatchAll(); |
| // Verify a connection starting |
| verify(mWifiNS).selectNetwork((List<WifiCandidates.Candidate>) |
| argThat(new WifiCandidatesListSizeMatcher(mCandidateList.size()))); |
| verify(mPrimaryClientModeManager).startConnectToNetwork(anyInt(), anyInt(), any()); |
| |
| // mCandidateWifiConfig1 is connected as primary |
| mockClientManagerInfo(mPrimaryClientModeManager, mCandidateWifiConfig1); |
| |
| when(mMultiInternetManager.hasPendingConnectionRequests()).thenReturn(true); |
| // Set the connection pending status |
| mMultiInternetConnectionStatusListenerCaptor.getValue().onStatusChange( |
| MultiInternetManager.MULTI_INTERNET_STATE_CONNECTION_REQUESTED, |
| new WorkSource()); |
| mMultiInternetConnectionStatusListenerCaptor.getValue().onStartScan(new WorkSource()); |
| mLooper.dispatchAll(); |
| if (!success) { |
| verify(mSecondaryClientModeManager, never()).startConnectToNetwork( |
| targetConfig.networkId, Process.WIFI_UID, targetBssid); |
| verify(mSecondaryClientModeManager, never()).enableRoaming(false); |
| verify(mActiveModeWarden, never()).requestSecondaryLongLivedClientModeManager( |
| any(), any(), any(), any()); |
| return; |
| } |
| |
| verify(mSecondaryClientModeManager, times(2)).startConnectToNetwork( |
| targetConfig.networkId, Process.WIFI_UID, targetBssid); |
| verify(mSecondaryClientModeManager, times(2)).enableRoaming(false); |
| verify(mActiveModeWarden, times(2)).requestSecondaryLongLivedClientModeManager( |
| any(), any(), any(), any()); |
| |
| // Simulate connection failing on the secondary |
| clearInvocations(mSecondaryClientModeManager, mPrimaryClientModeManager, mWifiNS); |
| mWifiConnectivityManager.handleConnectionAttemptEnded( |
| mSecondaryClientModeManager, |
| WifiMetrics.ConnectionEvent.FAILURE_ASSOCIATION_REJECTION, |
| WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, CANDIDATE_BSSID, |
| WifiConfigurationTestUtil.createPskNetwork(CANDIDATE_SSID)); |
| // verify connection is never restarted when a connection on the secondary STA fails. |
| verify(mWifiNS, never()).selectNetwork(any()); |
| verify(mSecondaryClientModeManager, never()).startConnectToNetwork( |
| anyInt(), anyInt(), any()); |
| verify(mPrimaryClientModeManager, never()).startConnectToNetwork( |
| anyInt(), anyInt(), any()); |
| } |
| |
| @Test |
| public void multiInternetSecondaryConnectionRequestSucceedsWithDbsApOnly() { |
| setupMocksForMultiInternetTests(true); |
| testMultiInternetSecondaryConnectionRequest(true, true, true); |
| } |
| |
| @Test |
| public void multiInternetSecondaryConnectionRequestSucceedsWithDbsApOnlyFailOnStaticIp() { |
| setupMocksForMultiInternetTests(true); |
| testMultiInternetSecondaryConnectionRequest(true, false, false); |
| } |
| |
| @Test |
| public void multiInternetSecondaryConnectionRequestSucceedsWithMultiApAllowed() { |
| setupMocksForMultiInternetTests(false); |
| testMultiInternetSecondaryConnectionRequest(false, true, true); |
| } |
| |
| /** |
| * Wifi enters disconnected state while screen is on. |
| * |
| * Expected behavior: WifiConnectivityManager calls |
| * ClientModeManager.startConnectToNetwork() with the |
| * expected candidate network ID and BSSID. |
| */ |
| @Test |
| public void enterWifiDisconnectedStateWhenScreenOn() { |
| // Set screen to on |
| setScreenState(true); |
| |
| // Set WiFi to disconnected state |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_DISCONNECTED); |
| mLooper.dispatchAll(); |
| verify(mPrimaryClientModeManager).startConnectToNetwork( |
| CANDIDATE_NETWORK_ID, Process.WIFI_UID, CANDIDATE_BSSID); |
| } |
| |
| /** |
| * Wifi enters connected state while screen is on. |
| * |
| * Expected behavior: WifiConnectivityManager calls |
| * ClientModeManager.startConnectToNetwork() with the |
| * expected candidate network ID and BSSID. |
| */ |
| @Test |
| public void enterWifiConnectedStateWhenScreenOn() { |
| // Set screen to on |
| setScreenState(true); |
| |
| // Set WiFi to connected state |
| setWifiStateConnected(); |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_CONNECTED); |
| mLooper.dispatchAll(); |
| verify(mPrimaryClientModeManager).startConnectToNetwork( |
| CANDIDATE_NETWORK_ID, Process.WIFI_UID, CANDIDATE_BSSID); |
| } |
| |
| /** |
| * Screen turned on while WiFi in disconnected state. |
| * |
| * Expected behavior: WifiConnectivityManager calls |
| * ClientModeManager.startConnectToNetwork() with the |
| * expected candidate network ID and BSSID. |
| */ |
| @Test |
| public void turnScreenOnWhenWifiInDisconnectedState() { |
| // Set WiFi to disconnected state |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_DISCONNECTED); |
| mLooper.dispatchAll(); |
| // Set screen to on |
| setScreenState(true); |
| |
| verify(mPrimaryClientModeManager, atLeastOnce()).startConnectToNetwork( |
| CANDIDATE_NETWORK_ID, Process.WIFI_UID, CANDIDATE_BSSID); |
| } |
| |
| /** |
| * Screen turned on while WiFi in connected state. |
| * |
| * Expected behavior: WifiConnectivityManager calls |
| * ClientModeManager.startConnectToNetwork() with the |
| * expected candidate network ID and BSSID. |
| */ |
| @Test |
| public void turnScreenOnWhenWifiInConnectedState() { |
| // Set WiFi to connected state |
| setWifiStateConnected(); |
| |
| // Set screen to on |
| setScreenState(true); |
| mLooper.dispatchAll(); |
| verify(mPrimaryClientModeManager, atLeastOnce()).startConnectToNetwork( |
| CANDIDATE_NETWORK_ID, Process.WIFI_UID, CANDIDATE_BSSID); |
| } |
| |
| /** |
| * Screen turned on while WiFi in connected state but |
| * auto roaming is disabled. |
| * |
| * Expected behavior: WifiConnectivityManager doesn't invoke |
| * ClientModeManager.startConnectToNetwork() because roaming |
| * is turned off. |
| */ |
| @Test |
| public void turnScreenOnWhenWifiInConnectedStateRoamingDisabled() { |
| // Turn off auto roaming |
| mResources.setBoolean( |
| R.bool.config_wifi_framework_enable_associated_network_selection, false); |
| mWifiConnectivityManager = createConnectivityManager(); |
| mWifiConnectivityManager.setTrustedConnectionAllowed(true); |
| |
| // Set WiFi to connected state |
| setWifiStateConnected(); |
| |
| // Set screen to on |
| setScreenState(true); |
| |
| verify(mPrimaryClientModeManager, times(0)).startConnectToNetwork( |
| CANDIDATE_NETWORK_ID, Process.WIFI_UID, CANDIDATE_BSSID); |
| } |
| |
| /** |
| * Multiple back to back connection attempts within the rate interval should be rate limited. |
| * |
| * Expected behavior: WifiConnectivityManager calls ClientModeManager.startConnectToNetwork() |
| * with the expected candidate network ID and BSSID for only the expected number of times within |
| * the given interval. |
| */ |
| @Test |
| public void connectionAttemptRateLimitedWhenScreenOff() { |
| int maxAttemptRate = WifiConnectivityManager.MAX_CONNECTION_ATTEMPTS_RATE; |
| int timeInterval = WifiConnectivityManager.MAX_CONNECTION_ATTEMPTS_TIME_INTERVAL_MS; |
| int numAttempts = 0; |
| int connectionAttemptIntervals = timeInterval / maxAttemptRate; |
| |
| setScreenState(false); |
| |
| // First attempt the max rate number of connections within the rate interval. |
| long currentTimeStamp = 0; |
| for (int attempt = 0; attempt < maxAttemptRate; attempt++) { |
| currentTimeStamp += connectionAttemptIntervals; |
| when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp); |
| // Set WiFi to disconnected state to trigger PNO scan |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_DISCONNECTED); |
| mLooper.dispatchAll(); |
| numAttempts++; |
| } |
| // Now trigger another connection attempt before the rate interval, this should be |
| // skipped because we've crossed rate limit. |
| when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp); |
| // Set WiFi to disconnected state to trigger PNO scan |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_DISCONNECTED); |
| mLooper.dispatchAll(); |
| // Verify that we attempt to connect upto the rate. |
| verify(mPrimaryClientModeManager, times(numAttempts)).startConnectToNetwork( |
| CANDIDATE_NETWORK_ID, Process.WIFI_UID, CANDIDATE_BSSID); |
| } |
| |
| /** |
| * Multiple back to back connection attempts outside the rate interval should not be rate |
| * limited. |
| * |
| * Expected behavior: WifiConnectivityManager calls ClientModeManager.startConnectToNetwork() |
| * with the expected candidate network ID and BSSID for only the expected number of times within |
| * the given interval. |
| */ |
| @Test |
| public void connectionAttemptNotRateLimitedWhenScreenOff() { |
| int maxAttemptRate = WifiConnectivityManager.MAX_CONNECTION_ATTEMPTS_RATE; |
| int timeInterval = WifiConnectivityManager.MAX_CONNECTION_ATTEMPTS_TIME_INTERVAL_MS; |
| int numAttempts = 0; |
| int connectionAttemptIntervals = timeInterval / maxAttemptRate; |
| |
| setScreenState(false); |
| |
| // First attempt the max rate number of connections within the rate interval. |
| long currentTimeStamp = 0; |
| for (int attempt = 0; attempt < maxAttemptRate; attempt++) { |
| currentTimeStamp += connectionAttemptIntervals; |
| when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp); |
| // Set WiFi to disconnected state to trigger PNO scan |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_DISCONNECTED); |
| mLooper.dispatchAll(); |
| numAttempts++; |
| } |
| // Now trigger another connection attempt after the rate interval, this should not be |
| // skipped because we should've evicted the older attempt. |
| when(mClock.getElapsedSinceBootMillis()).thenReturn( |
| currentTimeStamp + connectionAttemptIntervals * 2); |
| // Set WiFi to disconnected state to trigger PNO scan |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_DISCONNECTED); |
| mLooper.dispatchAll(); |
| numAttempts++; |
| |
| // Verify that all the connection attempts went through |
| verify(mPrimaryClientModeManager, times(numAttempts)).startConnectToNetwork( |
| CANDIDATE_NETWORK_ID, Process.WIFI_UID, CANDIDATE_BSSID); |
| } |
| |
| /** |
| * Multiple back to back connection attempts after a force connectivity scan should not be rate |
| * limited. |
| * |
| * Expected behavior: WifiConnectivityManager calls ClientModeManager.startConnectToNetwork() |
| * with the expected candidate network ID and BSSID for only the expected number of times within |
| * the given interval. |
| */ |
| @Test |
| public void connectionAttemptNotRateLimitedWhenScreenOffForceConnectivityScan() { |
| int maxAttemptRate = WifiConnectivityManager.MAX_CONNECTION_ATTEMPTS_RATE; |
| int timeInterval = WifiConnectivityManager.MAX_CONNECTION_ATTEMPTS_TIME_INTERVAL_MS; |
| int numAttempts = 0; |
| int connectionAttemptIntervals = timeInterval / maxAttemptRate; |
| |
| setScreenState(false); |
| |
| // First attempt the max rate number of connections within the rate interval. |
| long currentTimeStamp = 0; |
| for (int attempt = 0; attempt < maxAttemptRate; attempt++) { |
| currentTimeStamp += connectionAttemptIntervals; |
| when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp); |
| // Set WiFi to disconnected state to trigger PNO scan |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_DISCONNECTED); |
| mLooper.dispatchAll(); |
| numAttempts++; |
| } |
| |
| mWifiConnectivityManager.forceConnectivityScan(new WorkSource()); |
| |
| for (int attempt = 0; attempt < maxAttemptRate; attempt++) { |
| currentTimeStamp += connectionAttemptIntervals; |
| when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp); |
| // Set WiFi to disconnected state to trigger PNO scan |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_DISCONNECTED); |
| mLooper.dispatchAll(); |
| numAttempts++; |
| } |
| |
| // Verify that all the connection attempts went through |
| verify(mPrimaryClientModeManager, times(numAttempts)).startConnectToNetwork( |
| CANDIDATE_NETWORK_ID, Process.WIFI_UID, CANDIDATE_BSSID); |
| } |
| |
| /** |
| * Multiple back to back connection attempts after a user selection should not be rate limited. |
| * |
| * Expected behavior: WifiConnectivityManager calls ClientModeManager.startConnectToNetwork() |
| * with the expected candidate network ID and BSSID for only the expected number of times within |
| * the given interval. |
| */ |
| @Test |
| public void connectionAttemptNotRateLimitedWhenScreenOffAfterUserSelection() { |
| int maxAttemptRate = WifiConnectivityManager.MAX_CONNECTION_ATTEMPTS_RATE; |
| int timeInterval = WifiConnectivityManager.MAX_CONNECTION_ATTEMPTS_TIME_INTERVAL_MS; |
| int numAttempts = 0; |
| int connectionAttemptIntervals = timeInterval / maxAttemptRate; |
| |
| setScreenState(false); |
| |
| // First attempt the max rate number of connections within the rate interval. |
| long currentTimeStamp = 0; |
| for (int attempt = 0; attempt < maxAttemptRate; attempt++) { |
| currentTimeStamp += connectionAttemptIntervals; |
| when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp); |
| // Set WiFi to disconnected state to trigger PNO scan |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_DISCONNECTED); |
| mLooper.dispatchAll(); |
| numAttempts++; |
| } |
| |
| mWifiConnectivityManager.prepareForForcedConnection(CANDIDATE_NETWORK_ID); |
| |
| for (int attempt = 0; attempt < maxAttemptRate; attempt++) { |
| currentTimeStamp += connectionAttemptIntervals; |
| when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp); |
| // Set WiFi to disconnected state to trigger PNO scan |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_DISCONNECTED); |
| mLooper.dispatchAll(); |
| numAttempts++; |
| } |
| |
| // Verify that all the connection attempts went through |
| verify(mPrimaryClientModeManager, times(numAttempts)).startConnectToNetwork( |
| CANDIDATE_NETWORK_ID, Process.WIFI_UID, CANDIDATE_BSSID); |
| } |
| |
| /** |
| * Multiple back to back connection attempts after a wifi toggle should not be rate limited. |
| * |
| * Expected behavior: WifiConnectivityManager calls ClientModeManager.startConnectToNetwork() |
| * with the expected candidate network ID and BSSID for only the expected number of times within |
| * the given interval. |
| */ |
| @Test |
| public void connectionAttemptNotRateLimitedWhenScreenOffAfterWifiToggle() { |
| int maxAttemptRate = WifiConnectivityManager.MAX_CONNECTION_ATTEMPTS_RATE; |
| int timeInterval = WifiConnectivityManager.MAX_CONNECTION_ATTEMPTS_TIME_INTERVAL_MS; |
| int numAttempts = 0; |
| int connectionAttemptIntervals = timeInterval / maxAttemptRate; |
| |
| setScreenState(false); |
| |
| // First attempt the max rate number of connections within the rate interval. |
| long currentTimeStamp = 0; |
| for (int attempt = 0; attempt < maxAttemptRate; attempt++) { |
| currentTimeStamp += connectionAttemptIntervals; |
| when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp); |
| // Set WiFi to disconnected state to trigger PNO scan |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_DISCONNECTED); |
| mLooper.dispatchAll(); |
| numAttempts++; |
| } |
| |
| setWifiEnabled(false); |
| setWifiEnabled(true); |
| |
| for (int attempt = 0; attempt < maxAttemptRate; attempt++) { |
| currentTimeStamp += connectionAttemptIntervals; |
| when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp); |
| // Set WiFi to disconnected state to trigger PNO scan |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_DISCONNECTED); |
| mLooper.dispatchAll(); |
| numAttempts++; |
| } |
| |
| // Verify that all the connection attempts went through |
| verify(mPrimaryClientModeManager, times(numAttempts)).startConnectToNetwork( |
| CANDIDATE_NETWORK_ID, Process.WIFI_UID, CANDIDATE_BSSID); |
| } |
| |
| /** |
| * PNO retry for low RSSI networks. |
| * |
| * Expected behavior: WifiConnectivityManager doubles the low RSSI |
| * network retry delay value after QNS skips the PNO scan results |
| * because of their low RSSI values and reaches max after three scans |
| */ |
| @Test |
| public void pnoRetryForLowRssiNetwork() { |
| when(mWifiNS.selectNetwork(any())).thenReturn(null); |
| |
| // Set screen to off |
| setScreenState(false); |
| |
| // Set WiFi to disconnected state to trigger PNO scan |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_DISCONNECTED); |
| mLooper.dispatchAll(); |
| verify(mWifiMetrics).noteFirstNetworkSelectionAfterBoot(false); |
| |
| // Get the retry delay value after QNS didn't select a |
| // network candidate from the PNO scan results. |
| int lowRssiNetworkRetryDelayAfterOnePnoMs = mWifiConnectivityManager |
| .getLowRssiNetworkRetryDelay(); |
| |
| assertEquals(LOW_RSSI_NETWORK_RETRY_START_DELAY_SEC * 2000, |
| lowRssiNetworkRetryDelayAfterOnePnoMs); |
| |
| // Set WiFi to disconnected state to trigger two more PNO scans |
| for (int i = 0; i < 2; i++) { |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_DISCONNECTED); |
| mLooper.dispatchAll(); |
| } |
| int lowRssiNetworkRetryDelayAfterThreePnoMs = mWifiConnectivityManager |
| .getLowRssiNetworkRetryDelay(); |
| assertEquals(LOW_RSSI_NETWORK_RETRY_MAX_DELAY_SEC * 1000, |
| lowRssiNetworkRetryDelayAfterThreePnoMs); |
| } |
| |
| /** |
| * Ensure that the watchdog bite increments the "Pno bad" metric. |
| * |
| * Expected behavior: WifiConnectivityManager detects that the PNO scan failed to find |
| * a candidate while watchdog single scan did. |
| */ |
| @Test |
| public void watchdogBitePnoBadIncrementsMetrics() { |
| // Set screen to off |
| setScreenState(false); |
| |
| // Set WiFi to disconnected state to trigger PNO scan |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_DISCONNECTED); |
| mLooper.dispatchAll(); |
| // Now fire the watchdog alarm and verify the metrics were incremented. |
| mAlarmManager.dispatch(WifiConnectivityManager.WATCHDOG_TIMER_TAG); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiMetrics).incrementNumConnectivityWatchdogPnoBad(); |
| verify(mWifiMetrics, never()).incrementNumConnectivityWatchdogPnoGood(); |
| } |
| |
| /** |
| * Ensure that the watchdog bite increments the "Pno good" metric. |
| * |
| * Expected behavior: WifiConnectivityManager detects that the PNO scan failed to find |
| * a candidate which was the same with watchdog single scan. |
| */ |
| @Test |
| public void watchdogBitePnoGoodIncrementsMetrics() { |
| // Qns returns no candidate after watchdog single scan. |
| when(mWifiNS.selectNetwork(any())).thenReturn(null); |
| |
| // Set screen to off |
| setScreenState(false); |
| |
| // Set WiFi to disconnected state to trigger PNO scan |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_DISCONNECTED); |
| mLooper.dispatchAll(); |
| // Now fire the watchdog alarm and verify the metrics were incremented. |
| mAlarmManager.dispatch(WifiConnectivityManager.WATCHDOG_TIMER_TAG); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiMetrics).incrementNumConnectivityWatchdogPnoGood(); |
| verify(mWifiMetrics, never()).incrementNumConnectivityWatchdogPnoBad(); |
| } |
| |
| @Test |
| public void testNetworkConnectionCancelWatchdogTimer() { |
| // Set screen to off |
| setScreenState(false); |
| |
| // Set WiFi to disconnected state to trigger PNO scan |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_DISCONNECTED); |
| mLooper.dispatchAll(); |
| // Verify the watchdog alarm has been set |
| assertTrue(mAlarmManager.isPending(WifiConnectivityManager.WATCHDOG_TIMER_TAG)); |
| |
| // Set WiFi to connected |
| setWifiStateConnected(); |
| |
| // Verify the watchdog alarm has been canceled |
| assertFalse(mAlarmManager.isPending(WifiConnectivityManager.WATCHDOG_TIMER_TAG)); |
| } |
| |
| /** |
| * Verify that 2 scans that are sufficiently far apart are required to initiate a connection |
| * when the high mobility scanning optimization is enabled. |
| */ |
| @Test |
| public void testHighMovementNetworkSelection() { |
| when(mClock.getElapsedSinceBootMillis()).thenReturn(0L); |
| // Enable high movement optimization |
| mResources.setBoolean(R.bool.config_wifiHighMovementNetworkSelectionOptimizationEnabled, |
| true); |
| mWifiConnectivityManager.setDeviceMobilityState( |
| WifiManager.DEVICE_MOBILITY_STATE_HIGH_MVMT); |
| |
| // Set WiFi to disconnected state to trigger scan |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_DISCONNECTED); |
| mLooper.dispatchAll(); |
| |
| // Verify there is no connection due to currently having no cached candidates. |
| verify(mPrimaryClientModeManager, never()).startConnectToNetwork( |
| CANDIDATE_NETWORK_ID, Process.WIFI_UID, CANDIDATE_BSSID); |
| |
| // Move time forward but do not cross HIGH_MVMT_SCAN_DELAY_MS yet. |
| when(mClock.getElapsedSinceBootMillis()).thenReturn(HIGH_MVMT_SCAN_DELAY_MS - 1L); |
| // Set WiFi to disconnected state to trigger scan |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_DISCONNECTED); |
| mLooper.dispatchAll(); |
| |
| // Verify we still don't connect because not enough time have passed since the candidates |
| // were cached. |
| verify(mPrimaryClientModeManager, never()).startConnectToNetwork( |
| CANDIDATE_NETWORK_ID, Process.WIFI_UID, CANDIDATE_BSSID); |
| |
| // Move time past HIGH_MVMT_SCAN_DELAY_MS. |
| when(mClock.getElapsedSinceBootMillis()).thenReturn((long) HIGH_MVMT_SCAN_DELAY_MS); |
| // Set WiFi to disconnected state to trigger scan |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_DISCONNECTED); |
| mLooper.dispatchAll(); |
| |
| // Verify a candidate if found this time. |
| verify(mPrimaryClientModeManager).startConnectToNetwork( |
| CANDIDATE_NETWORK_ID, Process.WIFI_UID, CANDIDATE_BSSID); |
| verify(mWifiMetrics, times(2)).incrementNumHighMovementConnectionSkipped(); |
| verify(mWifiMetrics).incrementNumHighMovementConnectionStarted(); |
| } |
| |
| /** |
| * Verify that the device is initiating partial scans to verify AP stability in the high |
| * movement mobility state. |
| */ |
| @Test |
| public void testHighMovementTriggerPartialScan() { |
| when(mClock.getElapsedSinceBootMillis()).thenReturn(0L); |
| // Enable high movement optimization |
| mResources.setBoolean(R.bool.config_wifiHighMovementNetworkSelectionOptimizationEnabled, |
| true); |
| mWifiConnectivityManager.setDeviceMobilityState( |
| WifiManager.DEVICE_MOBILITY_STATE_HIGH_MVMT); |
| |
| // Set WiFi to disconnected state to trigger scan |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_DISCONNECTED); |
| mLooper.dispatchAll(); |
| // Verify there is no connection due to currently having no cached candidates. |
| verify(mPrimaryClientModeManager, never()).startConnectToNetwork( |
| CANDIDATE_NETWORK_ID, Process.WIFI_UID, CANDIDATE_BSSID); |
| |
| // Move time forward and verify that a delayed partial scan is scheduled. |
| when(mClock.getElapsedSinceBootMillis()).thenReturn(HIGH_MVMT_SCAN_DELAY_MS + 1L); |
| mAlarmManager.dispatch(WifiConnectivityManager.DELAYED_PARTIAL_SCAN_TIMER_TAG); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiScanner).startScan((ScanSettings) argThat(new WifiPartialScanSettingMatcher()), |
| any()); |
| } |
| |
| private class WifiPartialScanSettingMatcher implements ArgumentMatcher<ScanSettings> { |
| @Override |
| public boolean matches(ScanSettings scanSettings) { |
| return scanSettings.band == WifiScanner.WIFI_BAND_UNSPECIFIED |
| && scanSettings.channels[0].frequency == TEST_FREQUENCY; |
| } |
| } |
| |
| /** |
| * Verify that when there are we obtain more than one valid candidates from scan results and |
| * network connection fails, connection is immediately retried on the remaining candidates. |
| */ |
| @Test |
| public void testRetryConnectionOnFailure() { |
| // Setup WifiNetworkSelector to return 2 valid candidates from scan results |
| MacAddress macAddress = MacAddress.fromString(CANDIDATE_BSSID_2); |
| WifiCandidates.Key key = new WifiCandidates.Key(mock(ScanResultMatchInfo.class), |
| macAddress, 0, WifiConfiguration.SECURITY_TYPE_OPEN); |
| WifiCandidates.Candidate otherCandidate = mock(WifiCandidates.Candidate.class); |
| when(otherCandidate.getKey()).thenReturn(key); |
| List<WifiCandidates.Candidate> candidateList = new ArrayList<>(); |
| candidateList.add(mCandidate1); |
| candidateList.add(otherCandidate); |
| when(mWifiNS.getCandidatesFromScan(any(), any(), any(), anyBoolean(), anyBoolean(), |
| anyBoolean(), any(), anyBoolean())).thenReturn(candidateList); |
| |
| // Set WiFi to disconnected state to trigger scan |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_DISCONNECTED); |
| mLooper.dispatchAll(); |
| // Verify a connection starting |
| verify(mWifiNS).selectNetwork((List<WifiCandidates.Candidate>) |
| argThat(new WifiCandidatesListSizeMatcher(2))); |
| verify(mPrimaryClientModeManager).startConnectToNetwork(anyInt(), anyInt(), any()); |
| |
| // Simulate the connection failing |
| WifiConfiguration config = WifiConfigurationTestUtil.createPskNetwork(CANDIDATE_SSID); |
| mWifiConnectivityManager.handleConnectionAttemptEnded( |
| mPrimaryClientModeManager, |
| WifiMetrics.ConnectionEvent.FAILURE_ASSOCIATION_REJECTION, |
| WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, CANDIDATE_BSSID, |
| config); |
| // Verify the failed BSSID is added to blocklist |
| verify(mWifiBlocklistMonitor).blockBssidForDurationMs(eq(CANDIDATE_BSSID), |
| eq(config), anyLong(), anyInt(), anyInt()); |
| // Verify another connection starting |
| verify(mWifiNS).selectNetwork((List<WifiCandidates.Candidate>) |
| argThat(new WifiCandidatesListSizeMatcher(1))); |
| verify(mPrimaryClientModeManager, times(2)).startConnectToNetwork( |
| anyInt(), anyInt(), any()); |
| |
| // Simulate the second connection also failing |
| mWifiConnectivityManager.handleConnectionAttemptEnded( |
| mPrimaryClientModeManager, |
| WifiMetrics.ConnectionEvent.FAILURE_ASSOCIATION_REJECTION, |
| WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, CANDIDATE_BSSID_2, |
| config); |
| // Verify there are no more connections |
| verify(mWifiNS).selectNetwork((List<WifiCandidates.Candidate>) |
| argThat(new WifiCandidatesListSizeMatcher(0))); |
| verify(mPrimaryClientModeManager, times(2)).startConnectToNetwork( |
| anyInt(), anyInt(), any()); |
| } |
| |
| @Test |
| public void testRetryConnectionEapFailureIgnoreSameNetwork() { |
| // Setup WifiNetworkSelector to return 2 valid candidates with the same |
| // ScanResultMatchInfo so they are the same network, but different BSSID. |
| ScanResultMatchInfo matchInfo = ScanResultMatchInfo.fromWifiConfiguration( |
| mCandidateWifiConfig1); |
| WifiCandidates.Key key = new WifiCandidates.Key(matchInfo, |
| MacAddress.fromString(CANDIDATE_BSSID), 0); |
| WifiCandidates.Key key2 = new WifiCandidates.Key(matchInfo, |
| MacAddress.fromString(CANDIDATE_BSSID_2), 0); |
| WifiCandidates.Candidate candidate1 = mock(WifiCandidates.Candidate.class); |
| when(candidate1.getKey()).thenReturn(key); |
| WifiCandidates.Candidate candidate2 = mock(WifiCandidates.Candidate.class); |
| when(candidate2.getKey()).thenReturn(key2); |
| List<WifiCandidates.Candidate> candidateList = new ArrayList<>(); |
| candidateList.add(candidate1); |
| candidateList.add(candidate2); |
| when(mWifiNS.getCandidatesFromScan(any(), any(), any(), anyBoolean(), anyBoolean(), |
| anyBoolean(), any(), anyBoolean())).thenReturn(candidateList); |
| |
| // Set WiFi to disconnected state to trigger scan |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_DISCONNECTED); |
| mLooper.dispatchAll(); |
| // Verify a connection starting |
| verify(mWifiNS).selectNetwork((List<WifiCandidates.Candidate>) |
| argThat(new WifiCandidatesListSizeMatcher(2))); |
| verify(mPrimaryClientModeManager).startConnectToNetwork(anyInt(), anyInt(), any()); |
| |
| // Simulate the connection failing |
| mWifiConnectivityManager.handleConnectionAttemptEnded( |
| mPrimaryClientModeManager, |
| WifiMetrics.ConnectionEvent.FAILURE_AUTHENTICATION_FAILURE, |
| WifiMetricsProto.ConnectionEvent.AUTH_FAILURE_EAP_FAILURE, CANDIDATE_BSSID, |
| mCandidateWifiConfig1); |
| mLooper.dispatchAll(); |
| // verify no there is no retry. |
| verify(mPrimaryClientModeManager).startConnectToNetwork(anyInt(), anyInt(), any()); |
| } |
| |
| @Test |
| public void testRetryConnectionIgnoreNetworkWithAutojoinDisabled() { |
| // Setup WifiNetworkSelector to return 2 valid candidates from scan results |
| MacAddress macAddress = MacAddress.fromString(CANDIDATE_BSSID_2); |
| WifiCandidates.Key key = new WifiCandidates.Key(mock(ScanResultMatchInfo.class), |
| macAddress, 0, WifiConfiguration.SECURITY_TYPE_OPEN); |
| WifiCandidates.Candidate otherCandidate = mock(WifiCandidates.Candidate.class); |
| when(otherCandidate.getKey()).thenReturn(key); |
| List<WifiCandidates.Candidate> candidateList = new ArrayList<>(); |
| candidateList.add(mCandidate1); |
| candidateList.add(otherCandidate); |
| when(mWifiNS.getCandidatesFromScan(any(), any(), any(), anyBoolean(), anyBoolean(), |
| anyBoolean(), any(), anyBoolean())).thenReturn(candidateList); |
| |
| // Set WiFi to disconnected state to trigger scan |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_DISCONNECTED); |
| mLooper.dispatchAll(); |
| // Verify a connection starting |
| verify(mWifiNS).selectNetwork((List<WifiCandidates.Candidate>) |
| argThat(new WifiCandidatesListSizeMatcher(2))); |
| verify(mPrimaryClientModeManager).startConnectToNetwork(anyInt(), anyInt(), any()); |
| |
| // mock the WifiConfiguration to have allowAutoJoin = false |
| mCandidateWifiConfig1.allowAutojoin = false; |
| |
| // Simulate the connection failing |
| WifiConfiguration config = WifiConfigurationTestUtil.createPskNetwork(CANDIDATE_SSID); |
| mWifiConnectivityManager.handleConnectionAttemptEnded( |
| mPrimaryClientModeManager, |
| WifiMetrics.ConnectionEvent.FAILURE_ASSOCIATION_REJECTION, |
| WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, CANDIDATE_BSSID, |
| config); |
| |
| // Verify another connection do not start. |
| verify(mPrimaryClientModeManager).startConnectToNetwork(anyInt(), anyInt(), any()); |
| } |
| |
| /** |
| * Verify that we do not try to reconnect to an admin restricted network |
| */ |
| @Test |
| public void testRetryConnectionIgnoreNetworkWithAdminRestriction() { |
| // Setup WifiNetworkSelector to return 2 valid candidates from scan results |
| MacAddress macAddress = MacAddress.fromString(CANDIDATE_BSSID_2); |
| WifiCandidates.Key key = new WifiCandidates.Key(mock(ScanResultMatchInfo.class), |
| macAddress, 0, WifiConfiguration.SECURITY_TYPE_OPEN); |
| WifiCandidates.Candidate otherCandidate = mock(WifiCandidates.Candidate.class); |
| when(otherCandidate.getKey()).thenReturn(key); |
| List<WifiCandidates.Candidate> candidateList = new ArrayList<>(); |
| candidateList.add(mCandidate1); |
| candidateList.add(otherCandidate); |
| when(mWifiNS.getCandidatesFromScan(any(), any(), any(), anyBoolean(), anyBoolean(), |
| anyBoolean(), any(), anyBoolean())).thenReturn(candidateList); |
| |
| // Set WiFi to disconnected state to trigger scan |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_DISCONNECTED); |
| mLooper.dispatchAll(); |
| // Verify a connection starting |
| verify(mWifiNS).selectNetwork((List<WifiCandidates.Candidate>) |
| argThat(new WifiCandidatesListSizeMatcher(2))); |
| verify(mPrimaryClientModeManager).startConnectToNetwork(anyInt(), anyInt(), any()); |
| |
| // mock the WifiConfiguration to have an admin restriction |
| WifiConfiguration config = WifiConfigurationTestUtil.createPskNetwork(CANDIDATE_SSID); |
| when(mWifiPermissionsUtil.isAdminRestrictedNetwork(config)).thenReturn(true); |
| |
| // Simulate the connection failing |
| mWifiConnectivityManager.handleConnectionAttemptEnded( |
| mPrimaryClientModeManager, |
| WifiMetrics.ConnectionEvent.FAILURE_ASSOCIATION_REJECTION, |
| WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, CANDIDATE_BSSID, |
| config); |
| |
| // Verify another connection do not start. |
| verify(mPrimaryClientModeManager).startConnectToNetwork(anyInt(), anyInt(), any()); |
| } |
| |
| private class WifiCandidatesListSizeMatcher implements |
| ArgumentMatcher<List<WifiCandidates.Candidate>> { |
| int mSize; |
| WifiCandidatesListSizeMatcher(int size) { |
| mSize = size; |
| } |
| @Override |
| public boolean matches(List<WifiCandidates.Candidate> candidateList) { |
| return candidateList.size() == mSize; |
| } |
| } |
| |
| /** |
| * Verify that the cached candidates become cleared after a period of time. |
| */ |
| @Test |
| public void testRetryConnectionOnFailureCacheTimeout() { |
| // Setup WifiNetworkSelector to return 2 valid candidates from scan results |
| when(mClock.getElapsedSinceBootMillis()).thenReturn(0L); |
| MacAddress macAddress = MacAddress.fromString(CANDIDATE_BSSID_2); |
| WifiCandidates.Key key = new WifiCandidates.Key(mock(ScanResultMatchInfo.class), |
| macAddress, 0, WifiConfiguration.SECURITY_TYPE_OPEN); |
| WifiCandidates.Candidate otherCandidate = mock(WifiCandidates.Candidate.class); |
| when(otherCandidate.getKey()).thenReturn(key); |
| List<WifiCandidates.Candidate> candidateList = new ArrayList<>(); |
| candidateList.add(mCandidate1); |
| candidateList.add(otherCandidate); |
| when(mWifiNS.getCandidatesFromScan(any(), any(), any(), anyBoolean(), anyBoolean(), |
| anyBoolean(), any(), anyBoolean())).thenReturn(candidateList); |
| |
| // Set WiFi to disconnected state to trigger scan |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_DISCONNECTED); |
| mLooper.dispatchAll(); |
| // Verify a connection starting |
| verify(mWifiNS).selectNetwork((List<WifiCandidates.Candidate>) |
| argThat(new WifiCandidatesListSizeMatcher(2))); |
| verify(mPrimaryClientModeManager).startConnectToNetwork(anyInt(), anyInt(), any()); |
| |
| // Simulate the connection failing after the cache timeout period. |
| when(mClock.getElapsedSinceBootMillis()).thenReturn(TEMP_BSSID_BLOCK_DURATION_MS + 1L); |
| WifiConfiguration config = WifiConfigurationTestUtil.createPskNetwork(CANDIDATE_SSID); |
| mWifiConnectivityManager.handleConnectionAttemptEnded( |
| mPrimaryClientModeManager, |
| WifiMetrics.ConnectionEvent.FAILURE_ASSOCIATION_REJECTION, |
| WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, CANDIDATE_BSSID, |
| config); |
| // verify there are no additional connections. |
| verify(mPrimaryClientModeManager).startConnectToNetwork(anyInt(), anyInt(), any()); |
| } |
| |
| /** |
| * Verify that when cached candidates get cleared there will no longer be retries after a |
| * connection failure. |
| */ |
| @Test |
| public void testNoRetryConnectionOnFailureAfterCacheCleared() { |
| // Setup WifiNetworkSelector to return 2 valid candidates from scan results |
| MacAddress macAddress = MacAddress.fromString(CANDIDATE_BSSID_2); |
| WifiCandidates.Key key = new WifiCandidates.Key(mock(ScanResultMatchInfo.class), |
| macAddress, 0, WifiConfiguration.SECURITY_TYPE_OPEN); |
| WifiCandidates.Candidate otherCandidate = mock(WifiCandidates.Candidate.class); |
| when(otherCandidate.getKey()).thenReturn(key); |
| List<WifiCandidates.Candidate> candidateList = new ArrayList<>(); |
| candidateList.add(mCandidate1); |
| candidateList.add(otherCandidate); |
| when(mWifiNS.getCandidatesFromScan(any(), any(), any(), anyBoolean(), anyBoolean(), |
| anyBoolean(), any(), anyBoolean())).thenReturn(candidateList); |
| |
| // Set WiFi to disconnected state to trigger scan |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_DISCONNECTED); |
| mLooper.dispatchAll(); |
| // Verify a connection starting |
| verify(mWifiNS).selectNetwork((List<WifiCandidates.Candidate>) |
| argThat(new WifiCandidatesListSizeMatcher(2))); |
| verify(mPrimaryClientModeManager).startConnectToNetwork(anyInt(), anyInt(), any()); |
| |
| // now clear the cached candidates |
| mWifiConnectivityManager.clearCachedCandidates(); |
| |
| // Simulate the connection failing |
| WifiConfiguration config = WifiConfigurationTestUtil.createPskNetwork(CANDIDATE_SSID); |
| mWifiConnectivityManager.handleConnectionAttemptEnded( |
| mPrimaryClientModeManager, |
| WifiMetrics.ConnectionEvent.FAILURE_ASSOCIATION_REJECTION, |
| WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, CANDIDATE_BSSID, |
| config); |
| |
| // Verify there no re-attempt to connect |
| verify(mPrimaryClientModeManager).startConnectToNetwork(anyInt(), anyInt(), any()); |
| } |
| |
| /** |
| * Verify that the cached candidates that become disabled are not selected for connection. |
| */ |
| @Test |
| public void testRetryConnectionIgnoresDisabledNetworks() { |
| // Setup WifiNetworkSelector to return 2 valid candidates from scan results |
| int testOtherNetworkNetworkId = 123; |
| MacAddress macAddress = MacAddress.fromString(CANDIDATE_BSSID_2); |
| WifiCandidates.Key key = new WifiCandidates.Key(mock(ScanResultMatchInfo.class), |
| macAddress, testOtherNetworkNetworkId, WifiConfiguration.SECURITY_TYPE_OPEN); |
| WifiCandidates.Candidate otherCandidate = mock(WifiCandidates.Candidate.class); |
| when(otherCandidate.getKey()).thenReturn(key); |
| List<WifiCandidates.Candidate> candidateList = new ArrayList<>(); |
| candidateList.add(mCandidate1); |
| candidateList.add(otherCandidate); |
| when(mWifiNS.getCandidatesFromScan(any(), any(), any(), anyBoolean(), anyBoolean(), |
| anyBoolean(), any(), anyBoolean())).thenReturn(candidateList); |
| |
| // Set WiFi to disconnected state to trigger scan |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_DISCONNECTED); |
| mLooper.dispatchAll(); |
| // Verify a connection starting |
| verify(mWifiNS).selectNetwork((List<WifiCandidates.Candidate>) |
| argThat(new WifiCandidatesListSizeMatcher(2))); |
| verify(mPrimaryClientModeManager).startConnectToNetwork(anyInt(), anyInt(), any()); |
| |
| // make sure the configuration for otherCandidate is disabled, and verify there is no |
| // connection attempt after the disconnect happens. |
| when(otherCandidate.getNetworkConfigId()).thenReturn(testOtherNetworkNetworkId); |
| WifiConfiguration candidateOtherConfig = WifiConfigurationTestUtil.createOpenNetwork(); |
| candidateOtherConfig.getNetworkSelectionStatus().setNetworkSelectionStatus( |
| WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_PERMANENTLY_DISABLED); |
| when(mWifiConfigManager.getConfiguredNetwork(testOtherNetworkNetworkId)) |
| .thenReturn(candidateOtherConfig); |
| |
| // Simulate the connection failing |
| WifiConfiguration config = WifiConfigurationTestUtil.createPskNetwork(CANDIDATE_SSID); |
| mWifiConnectivityManager.handleConnectionAttemptEnded( |
| mPrimaryClientModeManager, |
| WifiMetrics.ConnectionEvent.FAILURE_ASSOCIATION_REJECTION, |
| WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, CANDIDATE_BSSID, |
| config); |
| |
| // Verify no more connections since there are 0 valid candidates remaining. |
| verify(mWifiNS).selectNetwork((List<WifiCandidates.Candidate>) |
| argThat(new WifiCandidatesListSizeMatcher(0))); |
| verify(mPrimaryClientModeManager).startConnectToNetwork(anyInt(), anyInt(), any()); |
| } |
| |
| /** |
| * Verify that in the high movement mobility state, when the RSSI delta of a BSSID from |
| * 2 consecutive scans becomes greater than a threshold, the candidate get ignored from |
| * network selection. |
| */ |
| @Test |
| public void testHighMovementRssiFilter() { |
| when(mClock.getElapsedSinceBootMillis()).thenReturn(0L); |
| // Enable high movement optimization |
| mResources.setBoolean(R.bool.config_wifiHighMovementNetworkSelectionOptimizationEnabled, |
| true); |
| mWifiConnectivityManager.setDeviceMobilityState( |
| WifiManager.DEVICE_MOBILITY_STATE_HIGH_MVMT); |
| |
| // Set WiFi to disconnected state to trigger scan |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_DISCONNECTED); |
| mLooper.dispatchAll(); |
| |
| // Verify there is no connection due to currently having no cached candidates. |
| verify(mPrimaryClientModeManager, never()).startConnectToNetwork( |
| CANDIDATE_NETWORK_ID, Process.WIFI_UID, CANDIDATE_BSSID); |
| |
| // Move time past HIGH_MVMT_SCAN_DELAY_MS. |
| when(mClock.getElapsedSinceBootMillis()).thenReturn((long) HIGH_MVMT_SCAN_DELAY_MS); |
| |
| // Mock the current Candidate to have RSSI over the filter threshold |
| mCandidateList.clear(); |
| mCandidateList.add(mCandidate2); |
| |
| // Set WiFi to disconnected state to trigger scan |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_DISCONNECTED); |
| mLooper.dispatchAll(); |
| |
| // Verify connect is not started. |
| verify(mPrimaryClientModeManager, never()).startConnectToNetwork( |
| CANDIDATE_NETWORK_ID, Process.WIFI_UID, CANDIDATE_BSSID); |
| verify(mWifiMetrics, times(2)).incrementNumHighMovementConnectionSkipped(); |
| } |
| |
| /** |
| * {@link OpenNetworkNotifier} handles scan results on network selection. |
| * |
| * Expected behavior: ONA handles scan results |
| */ |
| @Test |
| public void wifiDisconnected_noCandidateInSelect_openNetworkNotifierScanResultsHandled() { |
| // no connection candidate selected |
| when(mWifiNS.selectNetwork(any())).thenReturn(null); |
| |
| List<ScanDetail> expectedOpenNetworks = new ArrayList<>(); |
| expectedOpenNetworks.add( |
| new ScanDetail( |
| new ScanResult(WifiSsid.fromUtf8Text(CANDIDATE_SSID), |
| CANDIDATE_SSID, CANDIDATE_BSSID, 1245, 0, "some caps", -78, 2450, |
| 1025, 22, 33, 20, 0, 0, true))); |
| |
| when(mWifiNS.getFilteredScanDetailsForOpenUnsavedNetworks()) |
| .thenReturn(expectedOpenNetworks); |
| |
| // Set WiFi to disconnected state to trigger PNO scan |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_DISCONNECTED); |
| |
| verify(mOpenNetworkNotifier).handleScanResults(expectedOpenNetworks); |
| } |
| |
| /** |
| * {@link OpenNetworkNotifier} handles scan results on network selection. |
| * |
| * Expected behavior: ONA handles scan results |
| */ |
| @Test |
| public void wifiDisconnected_noCandidatesInScan_openNetworkNotifierScanResultsHandled() { |
| // no connection candidates from scan. |
| when(mWifiNS.getCandidatesFromScan(any(), any(), any(), anyBoolean(), anyBoolean(), |
| anyBoolean(), any(), anyBoolean())).thenReturn(null); |
| |
| List<ScanDetail> expectedOpenNetworks = new ArrayList<>(); |
| expectedOpenNetworks.add( |
| new ScanDetail( |
| new ScanResult(WifiSsid.fromUtf8Text(CANDIDATE_SSID), |
| CANDIDATE_SSID, CANDIDATE_BSSID, 1245, 0, "some caps", -78, 2450, |
| 1025, 22, 33, 20, 0, 0, true))); |
| |
| when(mWifiNS.getFilteredScanDetailsForOpenUnsavedNetworks()) |
| .thenReturn(expectedOpenNetworks); |
| |
| // Set WiFi to disconnected state to trigger PNO scan |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_DISCONNECTED); |
| |
| verify(mOpenNetworkNotifier).handleScanResults(expectedOpenNetworks); |
| } |
| |
| /** |
| * When wifi is connected, {@link OpenNetworkNotifier} handles the Wi-Fi connected behavior. |
| * |
| * Expected behavior: ONA handles connected behavior |
| */ |
| @Test |
| public void wifiConnected_openNetworkNotifierHandlesConnection() { |
| // Set WiFi to connected state |
| mWifiInfo.setSSID(WifiSsid.fromUtf8Text(CANDIDATE_SSID)); |
| WifiConfiguration config = WifiConfigurationTestUtil.createPskNetwork(CANDIDATE_SSID); |
| mWifiConnectivityManager.handleConnectionAttemptEnded( |
| mPrimaryClientModeManager, |
| WifiMetrics.ConnectionEvent.FAILURE_NONE, |
| WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, CANDIDATE_BSSID, config); |
| verify(mOpenNetworkNotifier).handleWifiConnected(CANDIDATE_SSID); |
| } |
| |
| /** |
| * When wifi is connected, {@link OpenNetworkNotifier} handles connection state |
| * change. |
| * |
| * Expected behavior: ONA does not clear pending notification. |
| */ |
| @Test |
| public void wifiDisconnected_openNetworkNotifierDoesNotClearPendingNotification() { |
| // Set WiFi to disconnected state |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_DISCONNECTED); |
| |
| verify(mOpenNetworkNotifier, never()).clearPendingNotification(anyBoolean()); |
| } |
| |
| /** |
| * When a Wi-Fi connection attempt ends, {@link OpenNetworkNotifier} handles the connection |
| * failure. A failure code that is not {@link WifiMetrics.ConnectionEvent#FAILURE_NONE} |
| * represents a connection failure. |
| * |
| * Expected behavior: ONA handles connection failure. |
| */ |
| @Test |
| public void wifiConnectionEndsWithFailure_openNetworkNotifierHandlesConnectionFailure() { |
| WifiConfiguration config = WifiConfigurationTestUtil.createPskNetwork(CANDIDATE_SSID); |
| mWifiConnectivityManager.handleConnectionAttemptEnded( |
| mPrimaryClientModeManager, |
| WifiMetrics.ConnectionEvent.FAILURE_CONNECT_NETWORK_FAILED, |
| WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, CANDIDATE_BSSID, |
| config); |
| |
| verify(mOpenNetworkNotifier).handleConnectionFailure(); |
| } |
| |
| /** |
| * When a Wi-Fi connection attempt ends, {@link OpenNetworkNotifier} does not handle connection |
| * failure after a successful connection. {@link WifiMetrics.ConnectionEvent#FAILURE_NONE} |
| * represents a successful connection. |
| * |
| * Expected behavior: ONA does nothing. |
| */ |
| @Test |
| public void wifiConnectionEndsWithSuccess_openNetworkNotifierDoesNotHandleConnectionFailure() { |
| WifiConfiguration config = WifiConfigurationTestUtil.createPskNetwork(CANDIDATE_SSID); |
| mWifiConnectivityManager.handleConnectionAttemptEnded( |
| mPrimaryClientModeManager, |
| WifiMetrics.ConnectionEvent.FAILURE_NONE, |
| WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, CANDIDATE_BSSID, config); |
| |
| verify(mOpenNetworkNotifier, never()).handleConnectionFailure(); |
| } |
| |
| /** |
| * When Wi-Fi is disabled, clear the pending notification and reset notification repeat delay. |
| * |
| * Expected behavior: clear pending notification and reset notification repeat delay |
| * */ |
| @Test |
| public void openNetworkNotifierClearsPendingNotificationOnWifiDisabled() { |
| setWifiEnabled(false); |
| |
| verify(mOpenNetworkNotifier).clearPendingNotification(true /* resetRepeatDelay */); |
| } |
| |
| /** |
| * Verify that the ONA controller tracks screen state changes. |
| */ |
| @Test |
| public void openNetworkNotifierTracksScreenStateChanges() { |
| // Screen state change at bootup. |
| verify(mOpenNetworkNotifier).handleScreenStateChanged(false); |
| |
| setScreenState(false); |
| |
| verify(mOpenNetworkNotifier, times(2)).handleScreenStateChanged(false); |
| |
| setScreenState(true); |
| |
| verify(mOpenNetworkNotifier).handleScreenStateChanged(true); |
| } |
| |
| /** |
| * Verify that the initial fast scan schedules the scan timer just like regular scans. |
| */ |
| @Test |
| public void testInitialFastScanSchedulesMoreScans() { |
| // Enable the fast initial scan feature |
| mResources.setBoolean(R.bool.config_wifiEnablePartialInitialScan, true); |
| // return 2 available frequencies |
| when(mWifiScoreCard.lookupNetwork(anyString())).thenReturn(mPerNetwork); |
| when(mPerNetwork.getFrequencies(anyLong())).thenReturn(new ArrayList<>( |
| Arrays.asList(TEST_FREQUENCY_1, TEST_FREQUENCY_2))); |
| |
| long currentTimeStamp = CURRENT_SYSTEM_TIME_MS; |
| when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp); |
| mWifiConnectivityManager.setTrustedConnectionAllowed(true); |
| |
| // set screen off and wifi disconnected |
| setScreenState(false); |
| mWifiConnectivityManager.handleConnectionStateChanged(mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_DISCONNECTED); |
| |
| List<Long> intervals = triggerPeriodicScansAndGetIntervals(0 /* triggerTimes */, |
| () -> { |
| // Set screen to ON to start a fast initial scan |
| setScreenState(true); |
| }, currentTimeStamp); |
| verifyScanTimesAndIntervals(1 /* scanTimes */, intervals, |
| VALID_DISCONNECTED_SINGLE_SCAN_SCHEDULE_SEC, |
| VALID_DISCONNECTED_SINGLE_SCAN_TYPE); |
| |
| // Verify the initial scan state is awaiting for response |
| assertEquals(WifiConnectivityManager.INITIAL_SCAN_STATE_AWAITING_RESPONSE, |
| mWifiConnectivityManager.getInitialScanState()); |
| verify(mWifiMetrics).incrementInitialPartialScanCount(); |
| } |
| |
| /** |
| * Verify that if configuration for single scan schedule is empty, default |
| * schedule is being used. |
| */ |
| @Test |
| public void checkPeriodicScanIntervalWhenDisconnectedWithEmptySchedule() throws Exception { |
| mResources.setIntArray(R.array.config_wifiDisconnectedScanIntervalScheduleSec, |
| SCHEDULE_EMPTY_SEC); |
| mResources.setIntArray(R.array.config_wifiDisconnectedScanType, SCHEDULE_EMPTY_SEC); |
| |
| checkWorkingWithDefaultSchedule(); |
| } |
| |
| /** |
| * Verify that if configuration for single scan schedule has zero values, default |
| * schedule is being used. |
| */ |
| @Test |
| public void checkPeriodicScanIntervalWhenDisconnectedWithZeroValuesSchedule() { |
| mResources.setIntArray( |
| R.array.config_wifiDisconnectedScanIntervalScheduleSec, |
| INVALID_SCHEDULE_ZERO_VALUES_SEC); |
| mResources.setIntArray(R.array.config_wifiDisconnectedScanType, |
| INVALID_SCHEDULE_NEGATIVE_VALUES_SEC); |
| |
| checkWorkingWithDefaultSchedule(); |
| } |
| |
| /** |
| * Verify that if configuration for single scan schedule has negative values, default |
| * schedule is being used. |
| */ |
| @Test |
| public void checkPeriodicScanIntervalWhenDisconnectedWithNegativeValuesSchedule() { |
| mResources.setIntArray( |
| R.array.config_wifiDisconnectedScanIntervalScheduleSec, |
| INVALID_SCHEDULE_NEGATIVE_VALUES_SEC); |
| mResources.setIntArray(R.array.config_wifiDisconnectedScanType, |
| INVALID_SCHEDULE_NEGATIVE_VALUES_SEC); |
| |
| checkWorkingWithDefaultSchedule(); |
| } |
| |
| /** |
| * Verify that when power save mode in on, the periodic scan interval is increased. |
| */ |
| @Test |
| public void checkPeriodicScanIntervalWhenDisconnectAndPowerSaveModeOn() throws Exception { |
| mResources.setIntArray( |
| R.array.config_wifiDisconnectedScanIntervalScheduleSec, |
| INVALID_SCHEDULE_ZERO_VALUES_SEC); |
| mResources.setIntArray(R.array.config_wifiDisconnectedScanType, SCHEDULE_EMPTY_SEC); |
| |
| when(mDeviceConfigFacade.isWifiBatterySaverEnabled()).thenReturn(true); |
| when(mPowerManagerService.isPowerSaveMode()).thenReturn(true); |
| checkWorkingWithDefaultScheduleWithMultiplier(POWER_SAVE_SCAN_INTERVAL_MULTIPLIER); |
| } |
| |
| private void checkWorkingWithDefaultSchedule() { |
| checkWorkingWithDefaultScheduleWithMultiplier(1 /* multiplier */); |
| } |
| |
| /** |
| * Get the value at index, or the last value if index is out of bound. |
| * @param schedule Int array of schedule. |
| * @param index Array index. |
| */ |
| private int getByIndexOrLast(int[] schedule, int index) { |
| return index < schedule.length ? schedule[index] : schedule[schedule.length - 1]; |
| } |
| |
| private void verifyScanTimesAndIntervals(int scanTimes, List<Long> intervals, |
| int[] intervalSchedule, int[] scheduleScanType) { |
| // Verify the scans actually happened for expected times, one scan for state change and |
| // each for scan timer triggered. |
| verify(mWifiScanner, times(scanTimes)).startScan(anyObject(), anyObject()); |
| |
| // Verify scans are happening using the expected scan type. |
| Map<Integer, Integer> scanTypeToTimesMap = new HashMap<>(); |
| for (int i = 0; i < scanTimes; i++) { |
| int expected = getByIndexOrLast(scheduleScanType, i); |
| scanTypeToTimesMap.put(expected, 1 + scanTypeToTimesMap.getOrDefault(expected, 0)); |
| } |
| for (Map.Entry<Integer, Integer> entry : scanTypeToTimesMap.entrySet()) { |
| verify(mWifiScanner, times(entry.getValue())).startScan( |
| argThat(new ArgumentMatcher<ScanSettings>() { |
| @Override |
| public boolean matches(ScanSettings scanSettings) { |
| return scanSettings.type == entry.getKey(); |
| } |
| }), any()); |
| } |
| |
| // Verify the scan intervals are same as expected interval schedule. |
| for (int i = 0; i < intervals.size(); i++) { |
| long expected = (long) (getByIndexOrLast(intervalSchedule, i) * 1000); |
| // TestHandler#sendMessageAtTime is not perfectly mocked and uses |
| // SystemClock.uptimeMillis() to generate |intervals|. This sometimes results in error |
| // margins of ~1ms and cause flaky test failures. |
| final long delta = Math.abs(expected - intervals.get(i).longValue()); |
| assertTrue("Interval " + i + " (" + delta + ") not in 1ms error margin", |
| delta < 2); |
| } |
| } |
| |
| private void verifyScanTimesAndFirstInterval(int scanTimes, List<Long> intervals, |
| int expectedInterval) { |
| // Verify the scans actually happened for expected times, one scan for state change and |
| // each for scan timer triggered. |
| verify(mWifiScanner, times(scanTimes)).startScan(anyObject(), anyObject()); |
| |
| // The actual interval should be same as scheduled. |
| assertEquals(expectedInterval * 1000, intervals.get(0).longValue()); |
| } |
| |
| /** |
| * Trigger the Wifi periodic scan and get scan intervals, after setting the Wifi state. |
| * @param triggerTimes The times to trigger the scheduled periodic scan. If it's 0 then don't |
| * trigger the scheduled periodic scan just return the interval. |
| * @param setStateCallback The callback function to be called to set the Wifi state. |
| * @param startTime The simulated scan start time. |
| */ |
| private List<Long> triggerPeriodicScansAndGetIntervals(int triggerTimes, |
| Runnable setStateCallback, long startTime) { |
| when(mClock.getElapsedSinceBootMillis()).thenReturn(startTime); |
| // Call the Wifi state callback to set the specified Wifi State to test the scan intervals. |
| setStateCallback.run(); |
| |
| for (int i = 0; i < triggerTimes; i++) { |
| // Mock the advanced time as when the scan timer supposed to fire |
| when(mClock.getElapsedSinceBootMillis()).thenReturn(startTime |
| + mTestHandler.getIntervals().stream().mapToLong(Long::longValue).sum()); |
| // Now advance the test handler and fire the periodic scan timer |
| mTestHandler.timeAdvance(); |
| } |
| |
| /* Verify the number of intervals recorded for periodic scans is (times + 1): |
| * One initial interval by scan scheduled in setStateCallback. |
| * One interval by each scan triggered. |
| */ |
| assertEquals(triggerTimes + 1, mTestHandler.getIntervals().size()); |
| |
| return mTestHandler.getIntervals(); |
| } |
| |
| private void checkWorkingWithDefaultScheduleWithMultiplier(float multiplier) { |
| long currentTimeStamp = CURRENT_SYSTEM_TIME_MS; |
| when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp); |
| |
| mWifiConnectivityManager = createConnectivityManager(); |
| mWifiConnectivityManager.setTrustedConnectionAllowed(true); |
| setWifiEnabled(true); |
| |
| // Set screen to ON |
| setScreenState(true); |
| |
| List<Long> intervals = triggerPeriodicScansAndGetIntervals(SCAN_TRIGGER_TIMES, |
| () -> { |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_DISCONNECTED); |
| }, currentTimeStamp); |
| |
| verifyScanTimesAndIntervals(SCAN_TRIGGER_TIMES + 1, intervals, |
| Arrays.stream(DEFAULT_SINGLE_SCAN_SCHEDULE_SEC).map(i -> (int) (i * multiplier)) |
| .toArray(), DEFAULT_SINGLE_SCAN_TYPE); |
| } |
| |
| /** |
| * Verify that scan interval for screen on and wifi disconnected scenario |
| * is in the exponential backoff fashion. |
| * |
| * Expected behavior: WifiConnectivityManager doubles periodic |
| * scan interval. |
| */ |
| @Test |
| public void checkPeriodicScanIntervalWhenDisconnected() { |
| long currentTimeStamp = CURRENT_SYSTEM_TIME_MS; |
| when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp); |
| |
| // Set screen to ON |
| setScreenState(true); |
| |
| // Wait for max periodic scan interval so that any impact triggered |
| // by screen state change can settle |
| currentTimeStamp += MAX_SCAN_INTERVAL_IN_SCHEDULE_SEC * 1000; |
| when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp); |
| |
| List<Long> intervals = triggerPeriodicScansAndGetIntervals(SCAN_TRIGGER_TIMES, |
| () -> { |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_DISCONNECTED); |
| }, currentTimeStamp); |
| |
| verifyScanTimesAndIntervals(SCAN_TRIGGER_TIMES + 1, intervals, |
| VALID_DISCONNECTED_SINGLE_SCAN_SCHEDULE_SEC, VALID_DISCONNECTED_SINGLE_SCAN_TYPE); |
| } |
| |
| @Test |
| public void checkSetExternalPeriodicScanInterval() { |
| assumeTrue(SdkLevel.isAtLeastT()); |
| long currentTimeStamp = CURRENT_SYSTEM_TIME_MS; |
| when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp); |
| |
| mWifiConnectivityManager.setExternalScreenOnScanSchedule( |
| VALID_EXTERNAL_SINGLE_SCAN_SCHEDULE_SEC, VALID_EXTERNAL_SINGLE_SCAN_TYPE); |
| // Set screen to ON |
| setScreenState(true); |
| |
| // Wait for max periodic scan interval so that any impact triggered |
| // by screen state change can settle |
| currentTimeStamp += MAX_SCAN_INTERVAL_IN_SCHEDULE_SEC * 1000; |
| when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp); |
| |
| List<Long> intervals = triggerPeriodicScansAndGetIntervals(SCAN_TRIGGER_TIMES, |
| () -> { |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_DISCONNECTED); |
| }, currentTimeStamp); |
| |
| verifyScanTimesAndIntervals(SCAN_TRIGGER_TIMES + 1, intervals, |
| VALID_EXTERNAL_SINGLE_SCAN_SCHEDULE_SEC, VALID_EXTERNAL_SINGLE_SCAN_TYPE); |
| } |
| |
| @Test |
| public void testSetOneShotScreenOnConnectivityScanDelayMillis() { |
| assumeTrue(SdkLevel.isAtLeastT()); |
| int scanDelayMs = 12345; |
| mWifiConnectivityManager.setOneShotScreenOnConnectivityScanDelayMillis(scanDelayMs); |
| |
| // Toggle screen to ON |
| assertEquals(0, mTestHandler.getIntervals().size()); |
| setScreenState(false); |
| setScreenState(true); |
| assertEquals(1, mTestHandler.getIntervals().size()); |
| assertTrue("Delay is not in 1ms error margin", |
| Math.abs(scanDelayMs - mTestHandler.getIntervals().get(0).longValue()) < 2); |
| |
| // Toggle again and there should be no more delayed scan |
| setScreenState(false); |
| setScreenState(true); |
| assertEquals(1, mTestHandler.getIntervals().size()); |
| |
| // set the scan delay and verify again |
| scanDelayMs = 23455; |
| mWifiConnectivityManager.setOneShotScreenOnConnectivityScanDelayMillis(scanDelayMs); |
| setScreenState(false); |
| setScreenState(true); |
| assertEquals(2, mTestHandler.getIntervals().size()); |
| assertTrue("Delay is not in 1ms error margin", |
| Math.abs(scanDelayMs - mTestHandler.getIntervals().get(1).longValue()) < 2); |
| } |
| |
| /** |
| * Verify that scan interval for screen on and wifi connected scenario |
| * is in the exponential backoff fashion. |
| * |
| * Expected behavior: WifiConnectivityManager doubles periodic |
| * scan interval. |
| */ |
| @Test |
| public void checkPeriodicScanIntervalWhenConnected() { |
| long currentTimeStamp = CURRENT_SYSTEM_TIME_MS; |
| when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp); |
| |
| if (SdkLevel.isAtLeastT()) { |
| // verify that setting the external scan schedule and then setting to null again should |
| // result in no-op, and not affect the scan schedule at all. |
| mWifiConnectivityManager.setExternalScreenOnScanSchedule( |
| VALID_EXTERNAL_SINGLE_SCAN_SCHEDULE_SEC, VALID_EXTERNAL_SINGLE_SCAN_TYPE); |
| mWifiConnectivityManager.setExternalScreenOnScanSchedule( |
| null, null); |
| } |
| |
| // Set screen to ON |
| setScreenState(true); |
| |
| // Wait for max scanning interval so that any impact triggered |
| // by screen state change can settle |
| currentTimeStamp += MAX_SCAN_INTERVAL_IN_SCHEDULE_SEC * 1000; |
| List<Long> intervals = triggerPeriodicScansAndGetIntervals(SCAN_TRIGGER_TIMES, |
| () -> { |
| // Set WiFi to connected state to trigger periodic scan |
| setWifiStateConnected(); |
| }, currentTimeStamp); |
| verifyScanTimesAndIntervals(SCAN_TRIGGER_TIMES + 1, intervals, |
| VALID_CONNECTED_SINGLE_SCAN_SCHEDULE_SEC, VALID_CONNECTED_SINGLE_SCAN_TYPE); |
| } |
| |
| /** |
| * Verify that scan interval for screen on and wifi is connected to the only network known to |
| * the device. |
| */ |
| @Test |
| public void checkPeriodicScanIntervalWhenConnectedAndOnlySingleNetwork() { |
| long currentTimeStamp = CURRENT_SYSTEM_TIME_MS; |
| when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp); |
| mResources.setIntArray( |
| R.array.config_wifiSingleSavedNetworkConnectedScanIntervalScheduleSec, |
| VALID_CONNECTED_SINGLE_SAVED_NETWORK_SCHEDULE_SEC); |
| mResources.setIntArray( |
| R.array.config_wifiSingleSavedNetworkConnectedScanType, |
| VALID_CONNECTED_SINGLE_SAVED_NETWORK_TYPE); |
| WifiConfiguration wifiConfiguration = new WifiConfiguration(); |
| wifiConfiguration.networkId = TEST_CONNECTED_NETWORK_ID; |
| List<WifiConfiguration> wifiConfigurationList = new ArrayList<WifiConfiguration>(); |
| wifiConfigurationList.add(wifiConfiguration); |
| when(mWifiConfigManager.getSavedNetworks(anyInt())).thenReturn(wifiConfigurationList); |
| |
| // Set screen to ON |
| setScreenState(true); |
| // Wait for max scanning interval so that any impact triggered |
| // by screen state change can settle |
| currentTimeStamp += MAX_SCAN_INTERVAL_IN_SCHEDULE_SEC * 1000; |
| List<Long> intervals = triggerPeriodicScansAndGetIntervals(SCAN_TRIGGER_TIMES, |
| () -> { |
| // Set WiFi to connected state to trigger periodic scan |
| setWifiStateConnected(); |
| }, currentTimeStamp); |
| verifyScanTimesAndIntervals(SCAN_TRIGGER_TIMES + 1, intervals, |
| VALID_CONNECTED_SINGLE_SAVED_NETWORK_SCHEDULE_SEC, |
| VALID_CONNECTED_SINGLE_SAVED_NETWORK_TYPE); |
| } |
| |
| /** |
| * When screen on and single saved network schedule is set |
| * If we have multiple saved networks, the regular connected state scan schedule is used |
| */ |
| @Test |
| public void checkScanScheduleForMultipleSavedNetwork() { |
| long currentTimeStamp = CURRENT_SYSTEM_TIME_MS; |
| when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp); |
| |
| // Set screen to ON |
| setScreenState(true); |
| |
| // Wait for max scanning interval so that any impact triggered |
| // by screen state change can settle |
| currentTimeStamp += MAX_SCAN_INTERVAL_IN_SCHEDULE_SEC * 1000; |
| when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp); |
| |
| mResources.setIntArray( |
| R.array.config_wifiSingleSavedNetworkConnectedScanIntervalScheduleSec, |
| VALID_CONNECTED_SINGLE_SAVED_NETWORK_SCHEDULE_SEC); |
| |
| WifiConfiguration wifiConfiguration1 = new WifiConfiguration(); |
| WifiConfiguration wifiConfiguration2 = new WifiConfiguration(); |
| wifiConfiguration1.status = WifiConfiguration.Status.CURRENT; |
| List<WifiConfiguration> wifiConfigurationList = new ArrayList<WifiConfiguration>(); |
| wifiConfigurationList.add(wifiConfiguration1); |
| wifiConfigurationList.add(wifiConfiguration2); |
| when(mWifiConfigManager.getSavedNetworks(anyInt())).thenReturn(wifiConfigurationList); |
| |
| // Set firmware roaming to enabled |
| when(mWifiConnectivityHelper.isFirmwareRoamingSupported()).thenReturn(true); |
| |
| List<Long> intervals = triggerPeriodicScansAndGetIntervals(0 /* triggerTimes */, |
| () -> { |
| // Set WiFi to connected state to trigger periodic scan |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_CONNECTED); |
| }, currentTimeStamp); |
| verifyScanTimesAndFirstInterval(1 /* scanTimes */, intervals, |
| VALID_CONNECTED_SINGLE_SCAN_SCHEDULE_SEC[0]); |
| } |
| |
| /** |
| * When screen on and single saved network schedule is set |
| * If we have a single saved network (connected network), |
| * no passpoint or suggestion networks. |
| * the single-saved-network connected state scan schedule is used |
| */ |
| @Test |
| public void checkScanScheduleForSingleSavedNetworkConnected() { |
| long currentTimeStamp = CURRENT_SYSTEM_TIME_MS; |
| when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp); |
| |
| // Set screen to ON |
| setScreenState(true); |
| |
| // Wait for max scanning interval so that any impact triggered |
| // by screen state change can settle |
| currentTimeStamp += MAX_SCAN_INTERVAL_IN_SCHEDULE_SEC * 1000; |
| when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp); |
| |
| mResources.setIntArray( |
| R.array.config_wifiSingleSavedNetworkConnectedScanIntervalScheduleSec, |
| VALID_CONNECTED_SINGLE_SAVED_NETWORK_SCHEDULE_SEC); |
| |
| WifiConfiguration wifiConfiguration = new WifiConfiguration(); |
| wifiConfiguration.networkId = TEST_CONNECTED_NETWORK_ID; |
| List<WifiConfiguration> wifiConfigurationList = new ArrayList<WifiConfiguration>(); |
| wifiConfigurationList.add(wifiConfiguration); |
| when(mWifiConfigManager.getSavedNetworks(anyInt())).thenReturn(wifiConfigurationList); |
| |
| // Set firmware roaming to enabled |
| when(mWifiConnectivityHelper.isFirmwareRoamingSupported()).thenReturn(true); |
| |
| List<Long> intervals = triggerPeriodicScansAndGetIntervals(1 /* triggerTimes */, |
| () -> { |
| // Set WiFi to connected state to trigger periodic scan |
| setWifiStateConnected(); |
| }, currentTimeStamp); |
| verifyScanTimesAndFirstInterval(2 /* scanTimes */, intervals, |
| VALID_CONNECTED_SINGLE_SAVED_NETWORK_SCHEDULE_SEC[0]); |
| } |
| |
| /** |
| * When screen on and single saved network schedule is set |
| * If we have a single saved network (not connected network), |
| * no passpoint or suggestion networks. |
| * the regular connected state scan schedule is used |
| */ |
| @Test |
| public void checkScanScheduleForSingleSavedNetwork() { |
| int testSavedNetworkId = TEST_CONNECTED_NETWORK_ID + 1; |
| long currentTimeStamp = CURRENT_SYSTEM_TIME_MS; |
| when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp); |
| |
| // Set screen to ON |
| setScreenState(true); |
| |
| // Wait for max scanning interval so that any impact triggered |
| // by screen state change can settle |
| currentTimeStamp += MAX_SCAN_INTERVAL_IN_SCHEDULE_SEC * 1000; |
| when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp); |
| |
| mResources.setIntArray( |
| R.array.config_wifiSingleSavedNetworkConnectedScanIntervalScheduleSec, |
| VALID_CONNECTED_SINGLE_SAVED_NETWORK_SCHEDULE_SEC); |
| |
| // Set firmware roaming to enabled |
| when(mWifiConnectivityHelper.isFirmwareRoamingSupported()).thenReturn(true); |
| |
| WifiConfiguration wifiConfiguration = new WifiConfiguration(); |
| wifiConfiguration.status = WifiConfiguration.Status.ENABLED; |
| wifiConfiguration.networkId = testSavedNetworkId; |
| List<WifiConfiguration> wifiConfigurationList = new ArrayList<WifiConfiguration>(); |
| wifiConfigurationList.add(wifiConfiguration); |
| when(mWifiConfigManager.getSavedNetworks(anyInt())).thenReturn(wifiConfigurationList); |
| |
| List<Long> intervals = triggerPeriodicScansAndGetIntervals(1 /* triggerTimes */, |
| () -> { |
| // Set WiFi to connected state to trigger periodic scan |
| setWifiStateConnected(); |
| }, currentTimeStamp); |
| verifyScanTimesAndFirstInterval(2 /* scanTimes */, intervals, |
| VALID_CONNECTED_SINGLE_SCAN_SCHEDULE_SEC[0]); |
| } |
| |
| /** |
| * When screen on and single saved network schedule is set |
| * If we have a single passpoint network (connected network), |
| * and no saved or suggestion networks the single-saved-network |
| * connected state scan schedule is used. |
| */ |
| @Test |
| public void checkScanScheduleForSinglePasspointNetworkConnected() { |
| long currentTimeStamp = CURRENT_SYSTEM_TIME_MS; |
| when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp); |
| |
| // Set screen to ON |
| setScreenState(true); |
| |
| // Wait for max scanning interval so that any impact triggered |
| // by screen state change can settle |
| currentTimeStamp += MAX_SCAN_INTERVAL_IN_SCHEDULE_SEC * 1000; |
| when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp); |
| |
| mResources.setIntArray( |
| R.array.config_wifiSingleSavedNetworkConnectedScanIntervalScheduleSec, |
| VALID_CONNECTED_SINGLE_SAVED_NETWORK_SCHEDULE_SEC); |
| |
| // Prepare for a single passpoint network |
| WifiConfiguration config = new WifiConfiguration(); |
| config.networkId = TEST_CONNECTED_NETWORK_ID; |
| String passpointKey = "PASSPOINT_KEY"; |
| when(mWifiConfigManager.getConfiguredNetwork(passpointKey)).thenReturn(config); |
| List<PasspointConfiguration> passpointNetworks = new ArrayList<PasspointConfiguration>(); |
| passpointNetworks.add(mPasspointConfiguration); |
| when(mPasspointConfiguration.getUniqueId()).thenReturn(passpointKey); |
| when(mPasspointManager.getProviderConfigs(anyInt(), anyBoolean())) |
| .thenReturn(passpointNetworks); |
| |
| // Prepare for no saved networks |
| when(mWifiConfigManager.getSavedNetworks(anyInt())).thenReturn(new ArrayList<>()); |
| |
| // Set firmware roaming to enabled |
| when(mWifiConnectivityHelper.isFirmwareRoamingSupported()).thenReturn(true); |
| |
| List<Long> intervals = triggerPeriodicScansAndGetIntervals(1 /* triggerTimes */, |
| () -> { |
| // Set WiFi to connected state to trigger periodic scan |
| setWifiStateConnected(); |
| }, currentTimeStamp); |
| verifyScanTimesAndFirstInterval(2 /* scanTimes */, intervals, |
| VALID_CONNECTED_SINGLE_SAVED_NETWORK_SCHEDULE_SEC[0]); |
| } |
| |
| /** |
| * When screen on and single saved network schedule is set |
| * If we have a single suggestion network (connected network), |
| * and no saved network or passpoint networks the single-saved-network |
| * connected state scan schedule is used |
| */ |
| @Test |
| public void checkScanScheduleForSingleSuggestionsNetworkConnected() { |
| long currentTimeStamp = CURRENT_SYSTEM_TIME_MS; |
| when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp); |
| |
| // Set screen to ON |
| setScreenState(true); |
| |
| // Wait for max scanning interval so that any impact triggered |
| // by screen state change can settle |
| currentTimeStamp += MAX_SCAN_INTERVAL_IN_SCHEDULE_SEC * 1000; |
| when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp); |
| |
| mResources.setIntArray( |
| R.array.config_wifiSingleSavedNetworkConnectedScanIntervalScheduleSec, |
| VALID_CONNECTED_SINGLE_SAVED_NETWORK_SCHEDULE_SEC); |
| |
| // Prepare for a single suggestions network |
| WifiConfiguration config = new WifiConfiguration(); |
| config.networkId = TEST_CONNECTED_NETWORK_ID; |
| String networkKey = "NETWORK_KEY"; |
| when(mWifiConfigManager.getConfiguredNetwork(networkKey)).thenReturn(config); |
| when(mSuggestionConfig.getProfileKey()).thenReturn(networkKey); |
| when(mWifiNetworkSuggestion.getWifiConfiguration()).thenReturn(mSuggestionConfig); |
| Set<WifiNetworkSuggestion> suggestionNetworks = new HashSet<WifiNetworkSuggestion>(); |
| suggestionNetworks.add(mWifiNetworkSuggestion); |
| when(mWifiNetworkSuggestionsManager.getAllApprovedNetworkSuggestions()) |
| .thenReturn(suggestionNetworks); |
| |
| // Prepare for no saved networks |
| when(mWifiConfigManager.getSavedNetworks(anyInt())).thenReturn(new ArrayList<>()); |
| |
| // Set firmware roaming to enabled |
| when(mWifiConnectivityHelper.isFirmwareRoamingSupported()).thenReturn(true); |
| |
| List<Long> intervals = triggerPeriodicScansAndGetIntervals(1 /* triggerTimes */, |
| () -> { |
| // Set WiFi to connected state to trigger periodic scan |
| setWifiStateConnected(); |
| }, currentTimeStamp); |
| verifyScanTimesAndFirstInterval(2 /* scanTimes */, intervals, |
| VALID_CONNECTED_SINGLE_SAVED_NETWORK_SCHEDULE_SEC[0]); |
| } |
| |
| /** |
| * When screen on and single saved network schedule is set |
| * If we have a single suggestion network (connected network), |
| * and saved network/passpoint networks the regular |
| * connected state scan schedule is used |
| */ |
| @Test |
| public void checkScanScheduleForSavedPasspointSuggestionNetworkConnected() { |
| long currentTimeStamp = CURRENT_SYSTEM_TIME_MS; |
| when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp); |
| |
| // Set screen to ON |
| setScreenState(true); |
| |
| // Wait for max scanning interval so that any impact triggered |
| // by screen state change can settle |
| currentTimeStamp += MAX_SCAN_INTERVAL_IN_SCHEDULE_SEC * 1000; |
| when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp); |
| |
| mResources.setIntArray( |
| R.array.config_wifiSingleSavedNetworkConnectedScanIntervalScheduleSec, |
| VALID_CONNECTED_SINGLE_SAVED_NETWORK_SCHEDULE_SEC); |
| |
| // Prepare for a single suggestions network |
| WifiConfiguration config = new WifiConfiguration(); |
| config.networkId = TEST_CONNECTED_NETWORK_ID; |
| String networkKey = "NETWORK_KEY"; |
| when(mWifiConfigManager.getConfiguredNetwork(networkKey)).thenReturn(config); |
| when(mSuggestionConfig.getProfileKey()).thenReturn(networkKey); |
| when(mWifiNetworkSuggestion.getWifiConfiguration()).thenReturn(mSuggestionConfig); |
| Set<WifiNetworkSuggestion> suggestionNetworks = new HashSet<WifiNetworkSuggestion>(); |
| suggestionNetworks.add(mWifiNetworkSuggestion); |
| when(mWifiNetworkSuggestionsManager.getAllApprovedNetworkSuggestions()) |
| .thenReturn(suggestionNetworks); |
| |
| // Prepare for a single passpoint network |
| WifiConfiguration passpointConfig = new WifiConfiguration(); |
| String passpointKey = "PASSPOINT_KEY"; |
| when(mWifiConfigManager.getConfiguredNetwork(passpointKey)).thenReturn(passpointConfig); |
| List<PasspointConfiguration> passpointNetworks = new ArrayList<PasspointConfiguration>(); |
| passpointNetworks.add(mPasspointConfiguration); |
| when(mPasspointConfiguration.getUniqueId()).thenReturn(passpointKey); |
| when(mPasspointManager.getProviderConfigs(anyInt(), anyBoolean())) |
| .thenReturn(passpointNetworks); |
| |
| // Set firmware roaming to enabled |
| when(mWifiConnectivityHelper.isFirmwareRoamingSupported()).thenReturn(true); |
| |
| List<Long> intervals = triggerPeriodicScansAndGetIntervals(1 /* triggerTimes */, |
| () -> { |
| // Set WiFi to connected state to trigger periodic scan |
| setWifiStateConnected(); |
| }, currentTimeStamp); |
| verifyScanTimesAndFirstInterval(2 /* scanTimes */, intervals, |
| VALID_CONNECTED_SINGLE_SCAN_SCHEDULE_SEC[0]); |
| } |
| |
| /** |
| * Remove network will trigger update scan and meet single network requirement. |
| * Verify before disconnect finished, will not trigger single network scan schedule. |
| */ |
| @Test |
| public void checkScanScheduleForCurrentConnectedNetworkIsNull() { |
| long currentTimeStamp = CURRENT_SYSTEM_TIME_MS; |
| when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp); |
| |
| // Set screen to ON |
| setScreenState(true); |
| |
| // Wait for max scanning interval so that any impact triggered |
| // by screen state change can settle |
| currentTimeStamp += MAX_SCAN_INTERVAL_IN_SCHEDULE_SEC * 1000; |
| when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp); |
| |
| mResources.setIntArray( |
| R.array.config_wifiSingleSavedNetworkConnectedScanIntervalScheduleSec, |
| VALID_CONNECTED_SINGLE_SAVED_NETWORK_SCHEDULE_SEC); |
| |
| // Set firmware roaming to enabled |
| when(mWifiConnectivityHelper.isFirmwareRoamingSupported()).thenReturn(true); |
| |
| // Set up single saved network |
| WifiConfiguration wifiConfiguration = new WifiConfiguration(); |
| wifiConfiguration.networkId = TEST_CONNECTED_NETWORK_ID; |
| List<WifiConfiguration> wifiConfigurationList = new ArrayList<WifiConfiguration>(); |
| wifiConfigurationList.add(wifiConfiguration); |
| when(mWifiConfigManager.getSavedNetworks(anyInt())).thenReturn(wifiConfigurationList); |
| |
| // Set WiFi to connected state to trigger periodic scan |
| setWifiStateConnected(); |
| mTestHandler.reset(); |
| |
| List<Long> intervals = triggerPeriodicScansAndGetIntervals(1 /* triggerTimes */, |
| () -> { |
| // Simulate remove network, disconnect not finished. |
| when(mPrimaryClientModeManager.getConnectedWifiConfiguration()) |
| .thenReturn(null); |
| mNetworkUpdateListenerCaptor.getValue().onNetworkRemoved(null); |
| }, currentTimeStamp); |
| verifyScanTimesAndFirstInterval(2 /* scanTimes */, intervals, |
| VALID_CONNECTED_SINGLE_SCAN_SCHEDULE_SEC[0]); |
| } |
| |
| /** |
| * When screen on trigger a disconnected state change event then a connected state |
| * change event back to back to verify that the minium scan interval is enforced. |
| * |
| * Expected behavior: WifiConnectivityManager start the second periodic single |
| * scan after the first one by first interval in connected scanning schedule. |
| */ |
| @Test |
| public void checkMinimumPeriodicScanIntervalWhenScreenOnAndConnected() { |
| long currentTimeStamp = CURRENT_SYSTEM_TIME_MS; |
| when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp); |
| |
| // Set screen to ON |
| setScreenState(true); |
| |
| // Wait for max scanning interval in schedule so that any impact triggered |
| // by screen state change can settle |
| currentTimeStamp += MAX_SCAN_INTERVAL_IN_SCHEDULE_SEC * 1000; |
| long scanForDisconnectedTimeStamp = currentTimeStamp; |
| when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp); |
| |
| // Set WiFi to disconnected state which triggers a scan immediately |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_DISCONNECTED); |
| verify(mWifiScanner, times(1)).startScan(anyObject(), anyObject()); |
| |
| // Set up time stamp for when entering CONNECTED state |
| currentTimeStamp += 2000; |
| when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp); |
| mTestHandler.reset(); |
| |
| List<Long> intervals = triggerPeriodicScansAndGetIntervals(1 /* triggerTimes */, |
| () -> { |
| // Set WiFi to connected state to trigger periodic scan |
| setWifiStateConnected(); |
| }, currentTimeStamp); |
| intervals.set(0, intervals.get(0) + 2000); |
| verifyScanTimesAndFirstInterval(2 /* scanTimes */, intervals, |
| VALID_CONNECTED_SINGLE_SCAN_SCHEDULE_SEC[0]); |
| } |
| |
| /** |
| * Check that the device does not trigger any periodic scans when it doesn't have any |
| * saved, passpoint, or suggestion network and open network notifier is disabled |
| */ |
| @Test |
| public void checkNoScanWhenNoPotentialNetwork() { |
| // Disable open network notifier |
| when(mOpenNetworkNotifier.isSettingEnabled()).thenReturn(false); |
| // Return no saved networks |
| when(mWifiConfigManager.getSavedNetworks(anyInt())) |
| .thenReturn(new ArrayList<WifiConfiguration>()); |
| // Return no suggestion networks |
| when(mWifiNetworkSuggestionsManager.getAllApprovedNetworkSuggestions()) |
| .thenReturn(new HashSet<>()); |
| // Return no passpoint networks |
| when(mPasspointManager.getProviderConfigs(anyInt(), anyBoolean())) |
| .thenReturn(new ArrayList<>()); |
| |
| // Set screen to ON |
| setScreenState(true); |
| |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_DISCONNECTED); |
| |
| verify(mWifiScanner, never()).startScan(anyObject(), anyObject()); |
| } |
| |
| /** |
| * When screen on trigger a connected state change event then a disconnected state |
| * change event back to back to verify that a scan is fired immediately for the |
| * disconnected state change event. |
| * |
| * Expected behavior: WifiConnectivityManager directly starts the periodic immediately |
| * for the disconnected state change event. The second scan for disconnected state is |
| * via alarm timer. |
| */ |
| @Test |
| public void scanImmediatelyWhenScreenOnAndDisconnected() { |
| long currentTimeStamp = CURRENT_SYSTEM_TIME_MS; |
| when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp); |
| |
| // Set screen to ON |
| setScreenState(true); |
| |
| // Wait for maximum scanning interval in schedule so that any impact triggered |
| // by screen state change can settle |
| currentTimeStamp += MAX_SCAN_INTERVAL_IN_SCHEDULE_SEC * 1000; |
| long scanForConnectedTimeStamp = currentTimeStamp; |
| when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp); |
| |
| // Set WiFi to connected state to trigger the periodic scan |
| setWifiStateConnected(); |
| |
| verify(mWifiScanner, times(1)).startScan(anyObject(), anyObject()); |
| |
| // Set up the time stamp for when entering DISCONNECTED state |
| currentTimeStamp += 2000; |
| long enteringDisconnectedStateTimeStamp = currentTimeStamp; |
| when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp); |
| mTestHandler.reset(); |
| |
| List<Long> intervals = triggerPeriodicScansAndGetIntervals(0 /* triggerTimes */, |
| () -> { |
| // Set WiFi to disconnected state to trigger its periodic scan |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_DISCONNECTED); |
| }, currentTimeStamp); |
| verifyScanTimesAndFirstInterval(2 /* scanTimes */, intervals, |
| VALID_DISCONNECTED_SINGLE_SCAN_SCHEDULE_SEC[0]); |
| } |
| |
| /** |
| * When screen on trigger a connection state change event and a forced connectivity |
| * scan event back to back to verify that the minimum scan interval is not applied |
| * in this scenario. |
| * |
| * Expected behavior: WifiConnectivityManager starts the second periodic single |
| * scan immediately. |
| */ |
| @Test |
| public void checkMinimumPeriodicScanIntervalNotEnforced() { |
| long currentTimeStamp = CURRENT_SYSTEM_TIME_MS; |
| when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp); |
| |
| // Set screen to ON |
| setScreenState(true); |
| |
| // Wait for maximum interval in scanning schedule so that any impact triggered |
| // by screen state change can settle |
| currentTimeStamp += MAX_SCAN_INTERVAL_IN_SCHEDULE_SEC * 1000; |
| long firstScanTimeStamp = currentTimeStamp; |
| when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp); |
| |
| // Set WiFi to connected state to trigger the periodic scan |
| setWifiStateConnected(); |
| |
| // Set the second scan attempt time stamp |
| currentTimeStamp += 2000; |
| when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp); |
| |
| // Allow untrusted networks so WifiConnectivityManager starts a periodic scan |
| // immediately. |
| mWifiConnectivityManager.setUntrustedConnectionAllowed(true); |
| |
| // Get the second periodic scan actual time stamp. Note, this scan is not |
| // started from the AlarmManager. |
| long secondScanTimeStamp = mWifiConnectivityManager.getLastPeriodicSingleScanTimeStamp(); |
| |
| // Verify that the second scan is fired immediately |
| assertEquals(secondScanTimeStamp, currentTimeStamp); |
| } |
| |
| /** |
| * Verify that we perform full band scan in the following two cases |
| * 1) Current RSSI is low, no active stream, network is insufficient |
| * 2) Current RSSI is high, no active stream, and a long time since last network selection |
| * 3) Current RSSI is high, no active stream, and a short time since last network selection, |
| * internet status is not acceptable |
| * |
| * Expected behavior: WifiConnectivityManager does full band scan in both cases |
| */ |
| @Test |
| public void verifyFullBandScanWhenConnected() { |
| mResources.setInteger( |
| R.integer.config_wifiConnectedHighRssiScanMinimumWindowSizeSec, 600); |
| |
| // Verify case 1 |
| when(mWifiNS.isNetworkSufficient(eq(mWifiInfo))).thenReturn(false); |
| when(mWifiNS.hasActiveStream(eq(mWifiInfo))).thenReturn(false); |
| when(mWifiNS.hasSufficientLinkQuality(eq(mWifiInfo))).thenReturn(false); |
| when(mWifiNS.hasInternetOrExpectNoInternet(eq(mWifiInfo))).thenReturn(true); |
| |
| final List<Integer> channelList = new ArrayList<>(); |
| channelList.add(TEST_FREQUENCY_1); |
| channelList.add(TEST_FREQUENCY_2); |
| channelList.add(TEST_FREQUENCY_3); |
| WifiConfiguration configuration = WifiConfigurationTestUtil.createOpenNetwork(); |
| configuration.networkId = TEST_CONNECTED_NETWORK_ID; |
| when(mWifiConfigManager.getConfiguredNetwork(TEST_CONNECTED_NETWORK_ID)) |
| .thenReturn(configuration); |
| when(mPrimaryClientModeManager.getConnectedWifiConfiguration()) |
| .thenReturn(configuration); |
| when(mWifiScoreCard.lookupNetwork(configuration.SSID)).thenReturn(mPerNetwork); |
| when(mPerNetwork.getFrequencies(anyLong())).thenReturn(new ArrayList<>()); |
| |
| doAnswer(new AnswerWithArguments() { |
| public void answer(ScanSettings settings, WifiScannerInternal.ScanListener listener) |
| throws Exception { |
| if (SdkLevel.isAtLeastS()) { |
| assertEquals(WifiScanner.WIFI_BAND_24_5_WITH_DFS_6_GHZ, settings.band); |
| assertEquals("RNR should be enabled for full scans", |
| WifiScanner.WIFI_RNR_ENABLED, settings.getRnrSetting()); |
| assertTrue("PSC should be enabled for full scans", |
| settings.is6GhzPscOnlyEnabled()); |
| } else { |
| assertEquals(WifiScanner.WIFI_BAND_ALL, settings.band); |
| } |
| assertNull(settings.channels); |
| }}).when(mWifiScanner).startScan(anyObject(), anyObject()); |
| |
| when(mClock.getElapsedSinceBootMillis()).thenReturn(0L); |
| // Set screen to ON |
| setScreenState(true); |
| |
| // Set WiFi to connected state to trigger periodic scan |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_CONNECTED); |
| mLooper.dispatchAll(); |
| verify(mWifiScanner).startScan(anyObject(), anyObject()); |
| |
| // Verify case 2 |
| when(mWifiNS.isNetworkSufficient(eq(mWifiInfo))).thenReturn(true); |
| when(mWifiNS.hasActiveStream(eq(mWifiInfo))).thenReturn(false); |
| when(mWifiNS.hasSufficientLinkQuality(eq(mWifiInfo))).thenReturn(true); |
| when(mWifiNS.hasInternetOrExpectNoInternet(eq(mWifiInfo))).thenReturn(true); |
| when(mClock.getElapsedSinceBootMillis()).thenReturn(600_000L + 1L); |
| setScreenState(true); |
| // Set WiFi to connected state to trigger periodic scan |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_CONNECTED); |
| verify(mWifiScanner, times(2)).startScan(anyObject(), anyObject()); |
| |
| // Verify case 3 |
| when(mWifiNS.isNetworkSufficient(eq(mWifiInfo))).thenReturn(false); |
| when(mWifiNS.hasActiveStream(eq(mWifiInfo))).thenReturn(false); |
| when(mWifiNS.hasSufficientLinkQuality(eq(mWifiInfo))).thenReturn(true); |
| when(mWifiNS.hasInternetOrExpectNoInternet(eq(mWifiInfo))).thenReturn(false); |
| when(mClock.getElapsedSinceBootMillis()).thenReturn(0L); |
| setScreenState(true); |
| // Set WiFi to connected state to trigger periodic scan |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_CONNECTED); |
| verify(mWifiScanner, times(2)).startScan(anyObject(), anyObject()); |
| } |
| |
| /** |
| * Verify that we perform partial scan when the current RSSI is low, |
| * Tx/Rx success rates are high, and when the currently connected network is present |
| * in scan cache in WifiConfigManager. |
| * WifiConnectivityManager does partial scan only when firmware roaming is not supported. |
| * |
| * Expected behavior: WifiConnectivityManager does partial scan. |
| */ |
| @Test |
| public void checkPartialScanRequestedWithLowRssiAndActiveStreamWithoutFwRoaming() { |
| when(mWifiNS.isNetworkSufficient(eq(mWifiInfo))).thenReturn(false); |
| when(mWifiNS.hasActiveStream(eq(mWifiInfo))).thenReturn(true); |
| when(mWifiNS.hasSufficientLinkQuality(eq(mWifiInfo))).thenReturn(false); |
| when(mWifiNS.hasInternetOrExpectNoInternet(eq(mWifiInfo))).thenReturn(true); |
| |
| mResources.setInteger( |
| R.integer.config_wifi_framework_associated_partial_scan_max_num_active_channels, |
| 10); |
| |
| WifiConfiguration configuration = WifiConfigurationTestUtil.createOpenNetwork(); |
| configuration.networkId = TEST_CONNECTED_NETWORK_ID; |
| when(mWifiConfigManager.getConfiguredNetwork(TEST_CONNECTED_NETWORK_ID)) |
| .thenReturn(configuration); |
| List<Integer> channelList = linkScoreCardFreqsToNetwork(configuration).get(0); |
| when(mPrimaryClientModeManager.getConnectedWifiConfiguration()) |
| .thenReturn(configuration); |
| |
| when(mWifiConnectivityHelper.isFirmwareRoamingSupported()).thenReturn(false); |
| |
| doAnswer(new AnswerWithArguments() { |
| public void answer(ScanSettings settings, WifiScannerInternal.ScanListener listener) |
| throws Exception { |
| assertEquals(settings.band, WifiScanner.WIFI_BAND_UNSPECIFIED); |
| assertEquals(settings.channels.length, channelList.size()); |
| if (SdkLevel.isAtLeastS()) { |
| assertEquals("Should never force enable RNR for partial scans", |
| WifiScanner.WIFI_RNR_NOT_NEEDED, settings.getRnrSetting()); |
| assertFalse("PSC should be disabled for partial scans", |
| settings.is6GhzPscOnlyEnabled()); |
| } |
| for (int chanIdx = 0; chanIdx < settings.channels.length; chanIdx++) { |
| assertTrue(channelList.contains(settings.channels[chanIdx].frequency)); |
| } |
| mLooper.dispatchAll(); |
| }}).when(mWifiScanner).startScan(anyObject(), anyObject()); |
| |
| // Set screen to ON |
| setScreenState(true); |
| |
| // Set WiFi to connected state to trigger periodic scan |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_CONNECTED); |
| |
| verify(mWifiScanner).startScan(anyObject(), anyObject()); |
| } |
| |
| /** |
| * Verify that we perform partial scan when the current RSSI is high, |
| * Tx/Rx success rates are low, and when the currently connected network is present |
| * in scan cache in WifiConfigManager. |
| * WifiConnectivityManager does partial scan only when firmware roaming is not supported. |
| * |
| * Expected behavior: WifiConnectivityManager does partial scan. |
| */ |
| @Test |
| public void checkPartialSCanRequestedWithHighRssiNoActiveStreamWithoutFwRoaming() { |
| when(mWifiNS.isNetworkSufficient(eq(mWifiInfo))).thenReturn(false); |
| when(mWifiNS.hasActiveStream(eq(mWifiInfo))).thenReturn(false); |
| when(mWifiNS.hasSufficientLinkQuality(eq(mWifiInfo))).thenReturn(true); |
| when(mWifiNS.hasInternetOrExpectNoInternet(eq(mWifiInfo))).thenReturn(true); |
| |
| mResources.setInteger( |
| R.integer.config_wifi_framework_associated_partial_scan_max_num_active_channels, |
| 10); |
| |
| WifiConfiguration configuration = WifiConfigurationTestUtil.createOpenNetwork(); |
| configuration.networkId = TEST_CONNECTED_NETWORK_ID; |
| when(mWifiConfigManager.getConfiguredNetwork(TEST_CONNECTED_NETWORK_ID)) |
| .thenReturn(configuration); |
| List<Integer> channelList = linkScoreCardFreqsToNetwork(configuration).get(0); |
| |
| when(mPrimaryClientModeManager.getConnectedWifiConfiguration()) |
| .thenReturn(configuration); |
| when(mWifiConnectivityHelper.isFirmwareRoamingSupported()).thenReturn(false); |
| |
| doAnswer(new AnswerWithArguments() { |
| public void answer(ScanSettings settings, WifiScannerInternal.ScanListener listener) |
| throws Exception { |
| assertEquals(settings.band, WifiScanner.WIFI_BAND_UNSPECIFIED); |
| assertEquals(settings.channels.length, channelList.size()); |
| for (int chanIdx = 0; chanIdx < settings.channels.length; chanIdx++) { |
| assertTrue(channelList.contains(settings.channels[chanIdx].frequency)); |
| } |
| mLooper.dispatchAll(); |
| }}).when(mWifiScanner).startScan(anyObject(), anyObject()); |
| |
| // Set screen to ON |
| setScreenState(true); |
| |
| // Set WiFi to connected state to trigger periodic scan |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_CONNECTED); |
| |
| verify(mWifiScanner).startScan(anyObject(), anyObject()); |
| } |
| |
| |
| /** |
| * Verify that we fall back to full band scan when the currently connected network's tx/rx |
| * success rate is high, RSSI is also high but the currently connected network |
| * is not present in scan cache in WifiConfigManager. |
| * This is simulated by returning an empty hashset in |makeChannelList|. |
| * |
| * Expected behavior: WifiConnectivityManager does full band scan. |
| */ |
| @Test |
| public void checkSingleScanSettingsWhenConnectedWithHighDataRateNotInCache() { |
| when(mWifiNS.isNetworkSufficient(eq(mWifiInfo))).thenReturn(true); |
| when(mWifiNS.hasActiveStream(eq(mWifiInfo))).thenReturn(true); |
| when(mWifiNS.hasSufficientLinkQuality(eq(mWifiInfo))).thenReturn(true); |
| when(mWifiNS.hasInternetOrExpectNoInternet(eq(mWifiInfo))).thenReturn(true); |
| |
| WifiConfiguration configuration = WifiConfigurationTestUtil.createOpenNetwork(); |
| configuration.networkId = TEST_CONNECTED_NETWORK_ID; |
| when(mWifiConfigManager.getConfiguredNetwork(TEST_CONNECTED_NETWORK_ID)) |
| .thenReturn(configuration); |
| List<Integer> channelList = linkScoreCardFreqsToNetwork(configuration).get(0); |
| |
| when(mPrimaryClientModeManager.getConnectedWifiConfiguration()) |
| .thenReturn(new WifiConfiguration()); |
| |
| doAnswer(new AnswerWithArguments() { |
| public void answer(ScanSettings settings, WifiScannerInternal.ScanListener listener) |
| throws Exception { |
| assertNull(settings.channels); |
| if (SdkLevel.isAtLeastS()) { |
| assertEquals(WifiScanner.WIFI_BAND_24_5_WITH_DFS_6_GHZ, settings.band); |
| assertEquals("RNR should be enabled for full scans", |
| WifiScanner.WIFI_RNR_ENABLED, settings.getRnrSetting()); |
| assertTrue("PSC should be enabled for full scans", |
| settings.is6GhzPscOnlyEnabled()); |
| } else { |
| assertEquals(WifiScanner.WIFI_BAND_ALL, settings.band); |
| } |
| mLooper.dispatchAll(); |
| }}).when(mWifiScanner).startScan(anyObject(), anyObject()); |
| |
| // Set screen to ON |
| setScreenState(true); |
| |
| // Set WiFi to connected state to trigger periodic scan |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_CONNECTED); |
| |
| verify(mWifiScanner).startScan(anyObject(), anyObject()); |
| } |
| |
| /** |
| * Verify that we retry connectivity scan up to MAX_SCAN_RESTART_ALLOWED times |
| * when Wifi somehow gets into a bad state and fails to scan. |
| * |
| * Expected behavior: WifiConnectivityManager schedules connectivity scan |
| * MAX_SCAN_RESTART_ALLOWED times. |
| */ |
| @Test |
| public void checkMaximumScanRetry() { |
| // Set screen to ON |
| setScreenState(true); |
| |
| doAnswer(new AnswerWithArguments() { |
| public void answer(ScanSettings settings, WifiScannerInternal.ScanListener listener) |
| throws Exception { |
| listener.onFailure(-1, "ScanFailure"); |
| mLooper.dispatchAll(); |
| }}).when(mWifiScanner).startScan(anyObject(), anyObject()); |
| |
| // Set WiFi to disconnected state to trigger the single scan based periodic scan |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_DISCONNECTED); |
| |
| // Fire the alarm timer 2x timers |
| for (int i = 0; i < (WifiConnectivityManager.MAX_SCAN_RESTART_ALLOWED * 2); i++) { |
| mAlarmManager.dispatch(WifiConnectivityManager.RESTART_SINGLE_SCAN_TIMER_TAG); |
| mLooper.dispatchAll(); |
| } |
| |
| // Verify that the connectivity scan has been retried for MAX_SCAN_RESTART_ALLOWED |
| // times. Note, WifiScanner.startScan() is invoked MAX_SCAN_RESTART_ALLOWED + 1 times. |
| // The very first scan is the initial one, and the other MAX_SCAN_RESTART_ALLOWED |
| // are the retrial ones. |
| verify(mWifiScanner, times(WifiConnectivityManager.MAX_SCAN_RESTART_ALLOWED + 1)).startScan( |
| anyObject(), anyObject()); |
| } |
| |
| @Test |
| public void testNoRetryScanWhenScreenOff() { |
| // Set screen to ON |
| setScreenState(true); |
| |
| doAnswer(new AnswerWithArguments() { |
| public void answer(ScanSettings settings, WifiScannerInternal.ScanListener listener) |
| throws Exception { |
| listener.onFailure(-1, "ScanFailure"); |
| mLooper.dispatchAll(); |
| }}).when(mWifiScanner).startScan(anyObject(), anyObject()); |
| |
| // Set WiFi to disconnected state to trigger the single scan based periodic scan |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_DISCONNECTED); |
| |
| |
| // turn the screen off |
| setScreenState(false); |
| // Fire the alarm timer 2x timers |
| for (int i = 0; i < (WifiConnectivityManager.MAX_SCAN_RESTART_ALLOWED * 2); i++) { |
| mAlarmManager.dispatch(WifiConnectivityManager.RESTART_SINGLE_SCAN_TIMER_TAG); |
| mLooper.dispatchAll(); |
| } |
| |
| // Verify that the connectivity scan has happened 2 times. Note, the first scan is due |
| // to the initial request, and the second scan is the first retry after failure. |
| // There are no more retries afterwards because the screen is off. |
| verify(mWifiScanner, times(2)).startScan(anyObject(), anyObject()); |
| } |
| |
| /** |
| * Verify that a successful scan result resets scan retry counter |
| * |
| * Steps |
| * 1. Trigger a scan that fails |
| * 2. Let the retry succeed |
| * 3. Trigger a scan again and have it and all subsequent retries fail |
| * 4. Verify that there are MAX_SCAN_RESTART_ALLOWED + 3 startScan calls. (2 are from the |
| * original scans, and MAX_SCAN_RESTART_ALLOWED + 1 from retries) |
| */ |
| @Test |
| public void verifyScanFailureCountIsResetAfterOnResult() { |
| setScreenState(true); |
| // Setup WifiScanner to fail |
| doAnswer(new AnswerWithArguments() { |
| public void answer(ScanSettings settings, WifiScannerInternal.ScanListener listener) |
| throws Exception { |
| listener.onFailure(-1, "ScanFailure"); |
| mLooper.dispatchAll(); |
| }}).when(mWifiScanner).startScan(anyObject(), anyObject()); |
| |
| mWifiConnectivityManager.forceConnectivityScan(null); |
| // make the retry succeed |
| doAnswer(new AnswerWithArguments() { |
| public void answer(ScanSettings settings, WifiScannerInternal.ScanListener listener) |
| throws Exception { |
| listener.onResults(null); |
| mLooper.dispatchAll(); |
| }}).when(mWifiScanner).startScan(anyObject(), anyObject()); |
| mAlarmManager.dispatch(WifiConnectivityManager.RESTART_SINGLE_SCAN_TIMER_TAG); |
| mLooper.dispatchAll(); |
| |
| // Verify that startScan is called once for the original scan, plus once for the retry. |
| // The successful retry should have now cleared the restart count |
| verify(mWifiScanner, times(2)).startScan( |
| anyObject(), anyObject()); |
| |
| // Now force a new scan and verify we retry MAX_SCAN_RESTART_ALLOWED times |
| doAnswer(new AnswerWithArguments() { |
| public void answer(ScanSettings settings, WifiScannerInternal.ScanListener listener) |
| throws Exception { |
| listener.onFailure(-1, "ScanFailure"); |
| mLooper.dispatchAll(); |
| }}).when(mWifiScanner).startScan(anyObject(), anyObject()); |
| mWifiConnectivityManager.forceConnectivityScan(null); |
| // Fire the alarm timer 2x timers |
| for (int i = 0; i < (WifiConnectivityManager.MAX_SCAN_RESTART_ALLOWED * 2); i++) { |
| mAlarmManager.dispatch(WifiConnectivityManager.RESTART_SINGLE_SCAN_TIMER_TAG); |
| mLooper.dispatchAll(); |
| } |
| |
| // Verify that the connectivity scan has been retried for MAX_SCAN_RESTART_ALLOWED + 3 |
| // times. Note, WifiScanner.startScan() is invoked 2 times by the first part of this test, |
| // and additionally MAX_SCAN_RESTART_ALLOWED + 1 times from forceConnectivityScan and |
| // subsequent retries. |
| verify(mWifiScanner, times(WifiConnectivityManager.MAX_SCAN_RESTART_ALLOWED + 3)).startScan( |
| anyObject(), anyObject()); |
| } |
| |
| /** |
| * Listen to scan results not requested by WifiConnectivityManager and |
| * act on them. |
| * |
| * Expected behavior: WifiConnectivityManager calls |
| * ClientModeManager.startConnectToNetwork() with the |
| * expected candidate network ID and BSSID. |
| */ |
| @Test |
| public void listenToAllSingleScanResults() { |
| ScanSettings settings = new ScanSettings(); |
| WifiScannerInternal.ScanListener scanListener = new WifiScannerInternal.ScanListener(mock( |
| WifiScanner.ScanListener.class), mTestHandler); |
| |
| // Request a single scan outside of WifiConnectivityManager. |
| mWifiScanner.startScan(settings, scanListener); |
| mLooper.dispatchAll(); |
| // Verify that WCM receives the scan results and initiates a connection |
| // to the network. |
| verify(mPrimaryClientModeManager).startConnectToNetwork( |
| CANDIDATE_NETWORK_ID, Process.WIFI_UID, CANDIDATE_BSSID); |
| } |
| |
| /** |
| * Verify that a forced connectivity scan waits for full band scan |
| * results. |
| * |
| * Expected behavior: WifiConnectivityManager doesn't invoke |
| * ClientModeManager.startConnectToNetwork() when full band scan |
| * results are not available. |
| */ |
| @Test |
| public void waitForFullBandScanResults() { |
| // Set WiFi to connected state. |
| setWifiStateConnected(); |
| |
| // Set up as partial scan results. |
| when(mScanData.getScannedBandsInternal()).thenReturn(WifiScanner.WIFI_BAND_5_GHZ); |
| |
| // Force a connectivity scan which enables WifiConnectivityManager |
| // to wait for full band scan results. |
| mWifiConnectivityManager.forceConnectivityScan(WIFI_WORK_SOURCE); |
| mLooper.dispatchAll(); |
| // No roaming because no full band scan results. |
| verify(mPrimaryClientModeManager, times(0)).startConnectToNetwork( |
| CANDIDATE_NETWORK_ID, Process.WIFI_UID, CANDIDATE_BSSID); |
| |
| // Set up as full band scan results. |
| when(mScanData.getScannedBandsInternal()).thenReturn(WifiScanner.WIFI_BAND_ALL); |
| |
| // Force a connectivity scan which enables WifiConnectivityManager |
| // to wait for full band scan results. |
| mWifiConnectivityManager.forceConnectivityScan(WIFI_WORK_SOURCE); |
| mLooper.dispatchAll(); |
| // Roaming attempt because full band scan results are available. |
| verify(mPrimaryClientModeManager).startConnectToNetwork( |
| CANDIDATE_NETWORK_ID, Process.WIFI_UID, CANDIDATE_BSSID); |
| } |
| |
| /** |
| * Verify when new scanResults are available, UserDisabledList will be updated. |
| */ |
| @Test |
| public void verifyUserDisabledListUpdated() { |
| mResources.setBoolean( |
| R.bool.config_wifi_framework_use_single_radio_chain_scan_results_network_selection, |
| true); |
| verify(mWifiConfigManager, never()).updateUserDisabledList(anyList()); |
| Set<String> updateNetworks = new HashSet<>(); |
| mScanData = createScanDataWithDifferentRadioChainInfos(); |
| int i = 0; |
| for (ScanResult scanResult : mScanData.getResults()) { |
| scanResult.SSID = TEST_SSID + i; |
| updateNetworks.add(ScanResultUtil.createQuotedSsid(scanResult.SSID)); |
| i++; |
| } |
| updateNetworks.add(TEST_FQDN); |
| mScanData.getResults()[0].setFlag(ScanResult.FLAG_PASSPOINT_NETWORK); |
| HashMap<String, Map<Integer, List<ScanResult>>> passpointNetworks = new HashMap<>(); |
| passpointNetworks.put(TEST_FQDN, new HashMap<>()); |
| when(mPasspointManager.getAllMatchingPasspointProfilesForScanResults(any())) |
| .thenReturn(passpointNetworks); |
| |
| mWifiConnectivityManager.forceConnectivityScan(WIFI_WORK_SOURCE); |
| mLooper.dispatchAll(); |
| ArgumentCaptor<ArrayList<String>> listArgumentCaptor = |
| ArgumentCaptor.forClass(ArrayList.class); |
| verify(mWifiConfigManager).updateUserDisabledList(listArgumentCaptor.capture()); |
| assertEquals(updateNetworks, new HashSet<>(listArgumentCaptor.getValue())); |
| } |
| |
| /** |
| * Verify that after receiving scan results, we attempt to clear expired recent failure reasons. |
| */ |
| @Test |
| public void verifyClearExpiredRecentFailureStatusAfterScan() { |
| // mWifiScanner is mocked to directly return scan results when a scan is triggered. |
| mWifiConnectivityManager.forceConnectivityScan(WIFI_WORK_SOURCE); |
| mLooper.dispatchAll(); |
| verify(mWifiConfigManager).cleanupExpiredRecentFailureReasons(); |
| } |
| |
| /** |
| * Verify that a blocklisted BSSID becomes available only after |
| * BSSID_BLOCKLIST_EXPIRE_TIME_MS. |
| */ |
| @Test |
| public void verifyBlocklistRefreshedAfterScanResults() { |
| WifiConfiguration disabledConfig = WifiConfigurationTestUtil.createPskNetwork(); |
| List<ScanDetail> mockScanDetails = new ArrayList<>(); |
| mockScanDetails.add(mock(ScanDetail.class)); |
| when(mWifiBlocklistMonitor.tryEnablingBlockedBssids(any())).thenReturn(mockScanDetails); |
| when(mWifiConfigManager.getSavedNetworkForScanDetail(any())).thenReturn( |
| disabledConfig); |
| |
| when(mWifiConnectivityHelper.isFirmwareRoamingSupported()).thenReturn(true); |
| |
| InOrder inOrder = inOrder(mWifiBlocklistMonitor, mWifiConfigManager); |
| // Force a connectivity scan |
| inOrder.verify(mWifiBlocklistMonitor, never()) |
| .updateAndGetBssidBlocklistForSsids(anySet()); |
| mWifiConnectivityManager.forceConnectivityScan(WIFI_WORK_SOURCE); |
| mLooper.dispatchAll(); |
| inOrder.verify(mWifiBlocklistMonitor).tryEnablingBlockedBssids(any()); |
| inOrder.verify(mWifiConfigManager).updateNetworkSelectionStatus(disabledConfig.networkId, |
| WifiConfiguration.NetworkSelectionStatus.DISABLED_NONE); |
| inOrder.verify(mWifiBlocklistMonitor).updateAndGetBssidBlocklistForSsids(anySet()); |
| } |
| |
| /** |
| * Verify blocklists and ephemeral networks are cleared from WifiConfigManager when exiting |
| * Wifi client mode. And if requires, ANQP cache is also flushed. |
| */ |
| @Test |
| public void clearEnableTemporarilyDisabledNetworksWhenExitingWifiClientMode() { |
| when(mWifiConnectivityHelper.isFirmwareRoamingSupported()).thenReturn(true); |
| when(mWifiGlobals.flushAnqpCacheOnWifiToggleOffEvent()).thenReturn(true); |
| // Exit Wifi client mode. |
| setWifiEnabled(false); |
| |
| // Verify the blocklists is cleared again. |
| verify(mWifiConfigManager).enableTemporaryDisabledNetworks(); |
| verify(mWifiConfigManager).stopRestrictingAutoJoinToSubscriptionId(); |
| verify(mWifiConfigManager).removeAllEphemeralOrPasspointConfiguredNetworks(); |
| verify(mWifiConfigManager).clearUserTemporarilyDisabledList(); |
| |
| // Verify ANQP cache is flushed. |
| verify(mPasspointManager).clearAnqpRequestsAndFlushCache(); |
| // Verify WifiNetworkSelector is informed of the disable. |
| verify(mWifiNS).resetOnDisable(); |
| } |
| |
| /** |
| * Verifies that the ANQP cache is not flushed when the configuration does not permit it. |
| */ |
| @Test |
| public void testAnqpFlushCacheSkippedIfNotConfigured() { |
| when(mWifiConnectivityHelper.isFirmwareRoamingSupported()).thenReturn(true); |
| when(mWifiGlobals.flushAnqpCacheOnWifiToggleOffEvent()).thenReturn(false); |
| // Exit Wifi client mode. |
| setWifiEnabled(false); |
| |
| // Verify ANQP cache is not flushed. |
| verify(mPasspointManager, never()).clearAnqpRequestsAndFlushCache(); |
| } |
| |
| /** |
| * Verify that BSSID blocklist gets cleared when preparing for a forced connection |
| * initiated by user/app. |
| */ |
| @Test |
| public void clearBssidBlocklistWhenPreparingForForcedConnection() { |
| when(mWifiConnectivityHelper.isFirmwareRoamingSupported()).thenReturn(true); |
| // Prepare for a forced connection attempt. |
| WifiConfiguration currentNetwork = generateWifiConfig( |
| 0, CANDIDATE_NETWORK_ID, CANDIDATE_SSID, false, true, null, null, |
| WifiConfigurationTestUtil.SECURITY_NONE); |
| when(mWifiConfigManager.getConfiguredNetwork(anyInt())).thenReturn(currentNetwork); |
| mWifiConnectivityManager.prepareForForcedConnection(1); |
| verify(mWifiBlocklistMonitor).clearBssidBlocklistForSsid(CANDIDATE_SSID); |
| } |
| |
| /** |
| * When WifiConnectivityManager is on and Wifi client mode is enabled, framework |
| * queries firmware via WifiConnectivityHelper to check if firmware roaming is |
| * supported and its capability. |
| * |
| * Expected behavior: WifiConnectivityManager#setWifiEnabled calls into |
| * WifiConnectivityHelper#getFirmwareRoamingInfo |
| */ |
| @Test |
| public void verifyGetFirmwareRoamingInfoIsCalledWhenEnableWiFiAndWcmOn() { |
| // WifiConnectivityManager is on by default |
| setWifiEnabled(true); |
| verify(mWifiConnectivityHelper).getFirmwareRoamingInfo(); |
| } |
| |
| /** |
| * When WifiConnectivityManager is off, verify that framework does not |
| * query firmware via WifiConnectivityHelper to check if firmware roaming is |
| * supported and its capability when enabling Wifi client mode. |
| * |
| * Expected behavior: WifiConnectivityManager#setWifiEnabled does not call into |
| * WifiConnectivityHelper#getFirmwareRoamingInfo |
| */ |
| @Test |
| public void verifyGetFirmwareRoamingInfoIsNotCalledWhenEnableWiFiAndWcmOff() { |
| reset(mWifiConnectivityHelper); |
| mWifiConnectivityManager.setAutoJoinEnabledExternal(false, false); |
| setWifiEnabled(true); |
| verify(mWifiConnectivityHelper, times(0)).getFirmwareRoamingInfo(); |
| } |
| |
| /** |
| * Verify if setAutoJoinEnabledExternal is disabled by a device admin, it cannot be re-enabled |
| * by a non device admin caller. |
| */ |
| @Test |
| public void testSetAutoJoinEnabledExternalDeviceOwnerPrivileged() { |
| assertTrue(mWifiConnectivityManager.getAutoJoinEnabledExternal()); |
| |
| // test disable/enable by non device admin |
| mWifiConnectivityManager.setAutoJoinEnabledExternal(false, false); |
| assertFalse(mWifiConnectivityManager.getAutoJoinEnabledExternal()); |
| mWifiConnectivityManager.setAutoJoinEnabledExternal(true, false); |
| assertTrue(mWifiConnectivityManager.getAutoJoinEnabledExternal()); |
| |
| // test disable by device admin |
| mWifiConnectivityManager.setAutoJoinEnabledExternal(false, true); |
| assertFalse(mWifiConnectivityManager.getAutoJoinEnabledExternal()); |
| |
| // verify that a non device admin cannot re-enable autoJoin |
| mWifiConnectivityManager.setAutoJoinEnabledExternal(true, false); |
| assertFalse(mWifiConnectivityManager.getAutoJoinEnabledExternal()); |
| |
| // verify device admin setting autojoin back to true |
| mWifiConnectivityManager.setAutoJoinEnabledExternal(true, true); |
| assertTrue(mWifiConnectivityManager.getAutoJoinEnabledExternal()); |
| |
| // verify that a non device admin can now modify autoJoin |
| mWifiConnectivityManager.setAutoJoinEnabledExternal(false, false); |
| assertFalse(mWifiConnectivityManager.getAutoJoinEnabledExternal()); |
| } |
| |
| /* |
| * Firmware supports controlled roaming. |
| * Connect to a network which doesn't have a config specified BSSID. |
| * |
| * Expected behavior: WifiConnectivityManager calls |
| * ClientModeManager.startConnectToNetwork() with the |
| * expected candidate network ID, and the BSSID value should be |
| * 'any' since firmware controls the roaming. |
| */ |
| @Test |
| public void useAnyBssidToConnectWhenFirmwareRoamingOnAndConfigHasNoBssidSpecified() { |
| // Firmware controls roaming |
| when(mWifiConnectivityHelper.isFirmwareRoamingSupported()).thenReturn(true); |
| |
| // Set screen to on |
| setScreenState(true); |
| |
| // Set WiFi to disconnected state |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_DISCONNECTED); |
| mLooper.dispatchAll(); |
| verify(mPrimaryClientModeManager).startConnectToNetwork( |
| CANDIDATE_NETWORK_ID, Process.WIFI_UID, ClientModeImpl.SUPPLICANT_BSSID_ANY); |
| } |
| |
| /* |
| * Firmware supports controlled roaming. |
| * Connect to a network which has a config specified BSSID. |
| * |
| * Expected behavior: WifiConnectivityManager calls |
| * ClientModeManager.startConnectToNetwork() with the |
| * expected candidate network ID, and the BSSID value should be |
| * the config specified one. |
| */ |
| @Test |
| public void useConfigSpecifiedBssidToConnectWhenFirmwareRoamingOn() { |
| // Firmware controls roaming |
| when(mWifiConnectivityHelper.isFirmwareRoamingSupported()).thenReturn(true); |
| |
| // Set up the candidate configuration such that it has a BSSID specified. |
| WifiConfiguration candidate = generateWifiConfig( |
| 0, CANDIDATE_NETWORK_ID, CANDIDATE_SSID, false, true, null, null, |
| WifiConfigurationTestUtil.SECURITY_NONE); |
| candidate.BSSID = CANDIDATE_BSSID; // config specified |
| ScanResult candidateScanResult = new ScanResult(); |
| candidateScanResult.SSID = CANDIDATE_SSID; |
| candidateScanResult.BSSID = CANDIDATE_BSSID; |
| candidate.getNetworkSelectionStatus().setCandidate(candidateScanResult); |
| when(mWifiNS.selectNetwork(any())).thenReturn(candidate); |
| |
| // Set screen to on |
| setScreenState(true); |
| |
| // Set WiFi to disconnected state |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_DISCONNECTED); |
| mLooper.dispatchAll(); |
| verify(mPrimaryClientModeManager).startConnectToNetwork( |
| CANDIDATE_NETWORK_ID, Process.WIFI_UID, CANDIDATE_BSSID); |
| verify(mPrimaryClientModeManager).enableRoaming(false); |
| |
| verify(mWifiMetrics).noteFirstNetworkSelectionAfterBoot(true); |
| } |
| |
| /* |
| * Firmware does not support controlled roaming. |
| * Connect to a network which doesn't have a config specified BSSID. |
| * |
| * Expected behavior: WifiConnectivityManager calls |
| * ClientModeManager.startConnectToNetwork() with the expected candidate network ID, |
| * and the BSSID value should be the candidate scan result specified. |
| */ |
| @Test |
| public void useScanResultBssidToConnectWhenFirmwareRoamingOffAndConfigHasNoBssidSpecified() { |
| // Set screen to on |
| setScreenState(true); |
| |
| // Set WiFi to disconnected state |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_DISCONNECTED); |
| mLooper.dispatchAll(); |
| verify(mPrimaryClientModeManager).startConnectToNetwork( |
| CANDIDATE_NETWORK_ID, Process.WIFI_UID, CANDIDATE_BSSID); |
| } |
| |
| /* |
| * Firmware does not support controlled roaming. |
| * Connect to a network which has a config specified BSSID. |
| * |
| * Expected behavior: WifiConnectivityManager calls |
| * ClientModeManager.startConnectToNetwork() with the expected candidate network ID, |
| * and the BSSID value should be the config specified one. |
| */ |
| @Test |
| public void useConfigSpecifiedBssidToConnectionWhenFirmwareRoamingOff() { |
| // Set up the candidate configuration such that it has a BSSID specified. |
| WifiConfiguration candidate = generateWifiConfig( |
| 0, CANDIDATE_NETWORK_ID, CANDIDATE_SSID, false, true, null, null, |
| WifiConfigurationTestUtil.SECURITY_NONE); |
| candidate.BSSID = CANDIDATE_BSSID; // config specified |
| ScanResult candidateScanResult = new ScanResult(); |
| candidateScanResult.SSID = CANDIDATE_SSID; |
| candidateScanResult.BSSID = CANDIDATE_BSSID; |
| candidate.getNetworkSelectionStatus().setCandidate(candidateScanResult); |
| when(mWifiNS.selectNetwork(any())).thenReturn(candidate); |
| |
| // Set screen to on |
| setScreenState(true); |
| mLooper.dispatchAll(); |
| // Set WiFi to disconnected state |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_DISCONNECTED); |
| mLooper.dispatchAll(); |
| verify(mPrimaryClientModeManager).startConnectToNetwork( |
| CANDIDATE_NETWORK_ID, Process.WIFI_UID, CANDIDATE_BSSID); |
| } |
| |
| /** |
| * Firmware does not support controlled roaming. |
| * WiFi in connected state, framework triggers roaming. |
| * |
| * Expected behavior: WifiConnectivityManager invokes |
| * ClientModeManager.startRoamToNetwork(). |
| */ |
| @Test |
| public void frameworkInitiatedRoaming() { |
| // Set WiFi to connected state |
| setWifiStateConnected(CANDIDATE_NETWORK_ID, CANDIDATE_BSSID_2); |
| |
| // Set screen to on |
| setScreenState(true); |
| mLooper.dispatchAll(); |
| verify(mPrimaryClientModeManager).startRoamToNetwork(eq(CANDIDATE_NETWORK_ID), |
| mCandidateBssidCaptor.capture()); |
| assertEquals(mCandidateBssidCaptor.getValue(), CANDIDATE_BSSID); |
| verify(mPrimaryClientModeManager, never()).startConnectToNetwork( |
| anyInt(), anyInt(), anyObject()); |
| } |
| |
| /** |
| * Firmware supports controlled roaming. |
| * WiFi in connected state, framework does not trigger roaming |
| * as it's handed off to the firmware. |
| * |
| * Expected behavior: WifiConnectivityManager doesn't invoke |
| * ClientModeManager.startRoamToNetwork(). |
| */ |
| @Test |
| public void noFrameworkRoamingIfConnectedAndFirmwareRoamingSupported() { |
| // Set WiFi to connected state |
| setWifiStateConnected(CANDIDATE_NETWORK_ID, CANDIDATE_BSSID_2); |
| |
| // Firmware controls roaming |
| when(mWifiConnectivityHelper.isFirmwareRoamingSupported()).thenReturn(true); |
| |
| // Set screen to on |
| setScreenState(true); |
| mLooper.dispatchAll(); |
| verify(mPrimaryClientModeManager, never()).startRoamToNetwork(anyInt(), anyObject()); |
| verify(mPrimaryClientModeManager, never()).startConnectToNetwork( |
| anyInt(), anyInt(), anyObject()); |
| } |
| |
| /* |
| * Wifi in disconnected state. Drop the connection attempt if the recommended |
| * network configuration has a BSSID specified but the scan result BSSID doesn't |
| * match it. |
| * |
| * Expected behavior: WifiConnectivityManager doesn't invoke |
| * ClientModeManager.startConnectToNetwork(). |
| */ |
| @Test |
| public void dropConnectAttemptIfConfigSpecifiedBssidDifferentFromScanResultBssid() { |
| // Set up the candidate configuration such that it has a BSSID specified. |
| WifiConfiguration candidate = generateWifiConfig( |
| 0, CANDIDATE_NETWORK_ID, CANDIDATE_SSID, false, true, null, null, |
| WifiConfigurationTestUtil.SECURITY_NONE); |
| candidate.BSSID = CANDIDATE_BSSID; // config specified |
| ScanResult candidateScanResult = new ScanResult(); |
| candidateScanResult.SSID = CANDIDATE_SSID; |
| // Set up the scan result BSSID to be different from the config specified one. |
| candidateScanResult.BSSID = INVALID_SCAN_RESULT_BSSID; |
| candidate.getNetworkSelectionStatus().setCandidate(candidateScanResult); |
| when(mWifiNS.selectNetwork(any())).thenReturn(candidate); |
| |
| // Set screen to on |
| setScreenState(true); |
| |
| // Set WiFi to disconnected state |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_DISCONNECTED); |
| mLooper.dispatchAll(); |
| verify(mPrimaryClientModeManager, times(0)).startConnectToNetwork( |
| CANDIDATE_NETWORK_ID, Process.WIFI_UID, CANDIDATE_BSSID); |
| } |
| |
| /* |
| * Wifi in connected state. Drop the roaming attempt if the recommended |
| * network configuration has a BSSID specified but the scan result BSSID doesn't |
| * match it. |
| * |
| * Expected behavior: WifiConnectivityManager doesn't invoke |
| * ClientModeManager.startRoamToNetwork(). |
| */ |
| @Test |
| public void dropRoamingAttemptIfConfigSpecifiedBssidDifferentFromScanResultBssid() { |
| // Mock the currently connected network which has the same networkID and |
| // SSID as the one to be selected. |
| WifiConfiguration currentNetwork = generateWifiConfig( |
| TEST_CONNECTED_NETWORK_ID, 0, CANDIDATE_SSID, false, true, null, null, |
| WifiConfigurationTestUtil.SECURITY_NONE); |
| when(mWifiConfigManager.getConfiguredNetwork(anyInt())).thenReturn(currentNetwork); |
| |
| // Set up the candidate configuration such that it has a BSSID specified. |
| WifiConfiguration candidate = generateWifiConfig( |
| TEST_CONNECTED_NETWORK_ID, 0, CANDIDATE_SSID, false, true, null, null, |
| WifiConfigurationTestUtil.SECURITY_NONE); |
| candidate.BSSID = CANDIDATE_BSSID; // config specified |
| ScanResult candidateScanResult = new ScanResult(); |
| candidateScanResult.SSID = CANDIDATE_SSID; |
| // Set up the scan result BSSID to be different from the config specified one. |
| candidateScanResult.BSSID = INVALID_SCAN_RESULT_BSSID; |
| candidate.getNetworkSelectionStatus().setCandidate(candidateScanResult); |
| when(mWifiNS.selectNetwork(any())).thenReturn(candidate); |
| |
| // Set WiFi to connected state |
| setWifiStateConnected(); |
| |
| // Set screen to on |
| setScreenState(true); |
| |
| verify(mPrimaryClientModeManager, times(0)).startRoamToNetwork(anyInt(), anyObject()); |
| } |
| |
| /** |
| * Dump local log buffer. |
| * |
| * Expected behavior: Logs dumped from WifiConnectivityManager.dump() |
| * contain the message we put in mLocalLog. |
| */ |
| @Test |
| public void dumpLocalLog() { |
| final String localLogMessage = "This is a message from the test"; |
| mLocalLog.log(localLogMessage); |
| |
| StringWriter sw = new StringWriter(); |
| PrintWriter pw = new PrintWriter(sw); |
| mWifiConnectivityManager.dump(new FileDescriptor(), pw, new String[]{}); |
| assertTrue(sw.toString().contains(localLogMessage)); |
| } |
| |
| /** |
| * Dump ONA controller. |
| * |
| * Expected behavior: {@link OpenNetworkNotifier#dump(FileDescriptor, PrintWriter, |
| * String[])} is invoked. |
| */ |
| @Test |
| public void dumpNotificationController() { |
| StringWriter sw = new StringWriter(); |
| PrintWriter pw = new PrintWriter(sw); |
| mWifiConnectivityManager.dump(new FileDescriptor(), pw, new String[]{}); |
| |
| verify(mOpenNetworkNotifier).dump(any(), any(), any()); |
| } |
| |
| /** |
| * Create scan data with different radio chain infos: |
| * First scan result has null radio chain info (No DBS support). |
| * Second scan result has empty radio chain info (No DBS support). |
| * Third scan result has 1 radio chain info (DBS scan). |
| * Fourth scan result has 2 radio chain info (non-DBS scan). |
| */ |
| private ScanData createScanDataWithDifferentRadioChainInfos() { |
| // Create 4 scan results. |
| ScanData[] scanDatas = |
| ScanTestUtil.createScanDatas(new int[][]{{5150, 5175, 2412, 2400}}, new int[]{0}); |
| // WCM barfs if the scan result does not have an IE. |
| scanDatas[0].getResults()[0].informationElements = new InformationElement[0]; |
| scanDatas[0].getResults()[1].informationElements = new InformationElement[0]; |
| scanDatas[0].getResults()[2].informationElements = new InformationElement[0]; |
| scanDatas[0].getResults()[3].informationElements = new InformationElement[0]; |
| scanDatas[0].getResults()[0].radioChainInfos = null; |
| scanDatas[0].getResults()[1].radioChainInfos = new ScanResult.RadioChainInfo[0]; |
| scanDatas[0].getResults()[2].radioChainInfos = new ScanResult.RadioChainInfo[1]; |
| scanDatas[0].getResults()[3].radioChainInfos = new ScanResult.RadioChainInfo[2]; |
| |
| return scanDatas[0]; |
| } |
| |
| /** |
| * If |config_wifi_framework_use_single_radio_chain_scan_results_network_selection| flag is |
| * false, WifiConnectivityManager should filter scan results which contain scans from a single |
| * radio chain (i.e DBS scan). |
| * Note: |
| * a) ScanResult with no radio chain indicates a lack of DBS support on the device. |
| * b) ScanResult with 2 radio chain info indicates a scan done using both the radio chains |
| * on a DBS supported device. |
| * |
| * Expected behavior: WifiConnectivityManager invokes |
| * {@link WifiNetworkSelector#getCandidatesFromScan(List, Set, List, boolean, boolean, Set, boolean)} |
| * boolean, boolean, boolean)} after filtering out the scan results obtained via DBS scan. |
| */ |
| @Test |
| public void filterScanResultsWithOneRadioChainInfoForNetworkSelectionIfConfigDisabled() { |
| mResources.setBoolean( |
| R.bool.config_wifi_framework_use_single_radio_chain_scan_results_network_selection, |
| false); |
| when(mWifiNS.selectNetwork(any())).thenReturn(null); |
| mWifiConnectivityManager = createConnectivityManager(); |
| |
| mScanData = createScanDataWithDifferentRadioChainInfos(); |
| |
| // Capture scan details which were sent to network selector. |
| final List<ScanDetail> capturedScanDetails = new ArrayList<>(); |
| doAnswer(new AnswerWithArguments() { |
| public List<WifiCandidates.Candidate> answer( |
| List<ScanDetail> scanDetails, Set<String> bssidBlocklist, |
| List<WifiNetworkSelector.ClientModeManagerState> cmmStates, |
| boolean untrustedNetworkAllowed, |
| boolean oemPaidNetworkAllowed, boolean oemPrivateNetworkAllowed, |
| Set<Integer> restrictedNetworkAllowedUids, boolean multiInternetNetworkAllowed) |
| throws Exception { |
| capturedScanDetails.addAll(scanDetails); |
| return null; |
| }}).when(mWifiNS).getCandidatesFromScan( |
| any(), any(), any(), anyBoolean(), eq(true), eq(false), any(), eq(false)); |
| |
| mWifiConnectivityManager.setTrustedConnectionAllowed(true); |
| mWifiConnectivityManager.setOemPaidConnectionAllowed(true, new WorkSource()); |
| // Set WiFi to disconnected state with screen on which triggers a scan immediately. |
| setWifiEnabled(true); |
| setScreenState(true); |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_DISCONNECTED); |
| // We should have filtered out the 3rd scan result. |
| assertEquals(3, capturedScanDetails.size()); |
| List<ScanResult> capturedScanResults = |
| capturedScanDetails.stream().map(ScanDetail::getScanResult) |
| .collect(Collectors.toList()); |
| |
| assertEquals(3, capturedScanResults.size()); |
| assertTrue(capturedScanResults.contains(mScanData.getResults()[0])); |
| assertTrue(capturedScanResults.contains(mScanData.getResults()[1])); |
| assertFalse(capturedScanResults.contains(mScanData.getResults()[2])); |
| assertTrue(capturedScanResults.contains(mScanData.getResults()[3])); |
| } |
| |
| /** |
| * If |config_wifi_framework_use_single_radio_chain_scan_results_network_selection| flag is |
| * true, WifiConnectivityManager should not filter scan results which contain scans from a |
| * single radio chain (i.e DBS scan). |
| * Note: |
| * a) ScanResult with no radio chain indicates a lack of DBS support on the device. |
| * b) ScanResult with 2 radio chain info indicates a scan done using both the radio chains |
| * on a DBS supported device. |
| * |
| * Expected behavior: WifiConnectivityManager invokes |
| * {@link WifiNetworkSelector#selectNetwork(List)} |
| * after filtering out the scan results obtained via DBS scan. |
| */ |
| @Test |
| public void dontFilterScanResultsWithOneRadioChainInfoForNetworkSelectionIfConfigEnabled() { |
| mResources.setBoolean( |
| R.bool.config_wifi_framework_use_single_radio_chain_scan_results_network_selection, |
| true); |
| when(mWifiNS.selectNetwork(any())).thenReturn(null); |
| mWifiConnectivityManager = createConnectivityManager(); |
| |
| mScanData = createScanDataWithDifferentRadioChainInfos(); |
| |
| // Capture scan details which were sent to network selector. |
| final List<ScanDetail> capturedScanDetails = new ArrayList<>(); |
| doAnswer(new AnswerWithArguments() { |
| public List<WifiCandidates.Candidate> answer( |
| List<ScanDetail> scanDetails, Set<String> bssidBlocklist, |
| List<WifiNetworkSelector.ClientModeManagerState> cmmStates, |
| boolean untrustedNetworkAllowed, |
| boolean oemPaidNetworkAllowed, boolean oemPrivateNetworkAllowed, |
| Set<Integer> restrictedNetworkAllowedUids, boolean multiInternetNetworkAllowed) |
| throws Exception { |
| capturedScanDetails.addAll(scanDetails); |
| return null; |
| }}).when(mWifiNS).getCandidatesFromScan( |
| any(), any(), any(), anyBoolean(), eq(false), eq(true), any(), eq(false)); |
| |
| mWifiConnectivityManager.setTrustedConnectionAllowed(true); |
| mWifiConnectivityManager.setOemPrivateConnectionAllowed(true, new WorkSource()); |
| // Set WiFi to disconnected state with screen on which triggers a scan immediately. |
| setWifiEnabled(true); |
| setScreenState(true); |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_DISCONNECTED); |
| mLooper.dispatchAll(); |
| // We should not filter any of the scan results. |
| assertEquals(4, capturedScanDetails.size()); |
| List<ScanResult> capturedScanResults = |
| capturedScanDetails.stream().map(ScanDetail::getScanResult) |
| .collect(Collectors.toList()); |
| |
| assertEquals(4, capturedScanResults.size()); |
| assertTrue(capturedScanResults.contains(mScanData.getResults()[0])); |
| assertTrue(capturedScanResults.contains(mScanData.getResults()[1])); |
| assertTrue(capturedScanResults.contains(mScanData.getResults()[2])); |
| assertTrue(capturedScanResults.contains(mScanData.getResults()[3])); |
| } |
| |
| /** |
| * Verify the various auto join enable/disable sequences when auto join is disabled externally. |
| * |
| * Expected behavior: Autojoin is turned on as a long as there is |
| * - Auto join is enabled externally |
| * And |
| * - No specific network request being processed. |
| * And |
| * - Pending generic Network request for trusted wifi connection. |
| * OR |
| * - Pending generic Network request for untrused wifi connection. |
| */ |
| @Test |
| public void verifyEnableAndDisableAutoJoinWhenExternalAutoJoinIsDisabled() { |
| mWifiConnectivityManager = createConnectivityManager(); |
| |
| // set wifi on & disconnected to trigger pno scans when auto-join is enabled. |
| setWifiEnabled(true); |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_DISCONNECTED); |
| |
| // Disable externally. |
| mWifiConnectivityManager.setAutoJoinEnabledExternal(false, false); |
| |
| // Enable trusted connection. This should NOT trigger a pno scan for auto-join. |
| mWifiConnectivityManager.setTrustedConnectionAllowed(true); |
| verify(mWifiScanner, never()).startPnoScan(any(), any(), any()); |
| |
| // End of processing a specific request. This should NOT trigger a new pno scan for |
| // auto-join. |
| mWifiConnectivityManager.setSpecificNetworkRequestInProgress(false); |
| verify(mWifiScanner, never()).startPnoScan(any(), any(), any()); |
| |
| // Enable untrusted connection. This should NOT trigger a pno scan for auto-join. |
| mWifiConnectivityManager.setUntrustedConnectionAllowed(true); |
| verify(mWifiScanner, never()).startPnoScan(any(), any(), any()); |
| } |
| |
| /** |
| * Verify the various auto join enable/disable sequences when auto join is enabled externally. |
| * |
| * Expected behavior: Autojoin is turned on as a long as there is |
| * - Auto join is enabled externally |
| * And |
| * - No specific network request being processed. |
| * And |
| * - Pending generic Network request for trusted wifi connection. |
| * OR |
| * - Pending generic Network request for untrused wifi connection. |
| */ |
| @Test |
| public void verifyEnableAndDisableAutoJoin() { |
| mWifiConnectivityManager = createConnectivityManager(); |
| |
| // set wifi on & disconnected to trigger pno scans when auto-join is enabled. |
| setWifiEnabled(true); |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_DISCONNECTED); |
| |
| // Enable trusted connection. This should trigger a pno scan for auto-join. |
| mWifiConnectivityManager.setTrustedConnectionAllowed(true); |
| verify(mWifiScanner).startPnoScan(any(), any(), any()); |
| |
| // Start of processing a specific request. This should stop any pno scan for auto-join. |
| mWifiConnectivityManager.setSpecificNetworkRequestInProgress(true); |
| verify(mWifiScanner).stopPnoScan(any()); |
| |
| // End of processing a specific request. This should now trigger a new pno scan for |
| // auto-join. |
| mWifiConnectivityManager.setSpecificNetworkRequestInProgress(false); |
| verify(mWifiScanner, times(2)).startPnoScan(any(), any(), any()); |
| |
| // Disable trusted connection. This should stop any pno scan for auto-join. |
| mWifiConnectivityManager.setTrustedConnectionAllowed(false); |
| verify(mWifiScanner, times(2)).stopPnoScan(any()); |
| |
| // Enable untrusted connection. This should trigger a pno scan for auto-join. |
| mWifiConnectivityManager.setUntrustedConnectionAllowed(true); |
| verify(mWifiScanner, times(3)).startPnoScan(any(), any(), any()); |
| } |
| |
| /** |
| * Verify that the increased PNO interval is used when power save is on. |
| */ |
| @Test |
| public void testPnoIntervalPowerSaveEnabled() throws Exception { |
| when(mDeviceConfigFacade.isWifiBatterySaverEnabled()).thenReturn(true); |
| when(mPowerManagerService.isPowerSaveMode()).thenReturn(true); |
| verifyPnoScanWithInterval( |
| MOVING_PNO_SCAN_INTERVAL_MILLIS * POWER_SAVE_SCAN_INTERVAL_MULTIPLIER); |
| } |
| |
| /** |
| * Verify that the normal PNO interval is used when power save is off. |
| */ |
| @Test |
| public void testPnoIntervalPowerSaveDisabled() throws Exception { |
| when(mDeviceConfigFacade.isWifiBatterySaverEnabled()).thenReturn(true); |
| when(mPowerManagerService.isPowerSaveMode()).thenReturn(false); |
| verifyPnoScanWithInterval(MOVING_PNO_SCAN_INTERVAL_MILLIS); |
| } |
| |
| /** |
| * Verify that the normal PNO interval is used when the power save feature is disabled. |
| */ |
| @Test |
| public void testPnoIntervalPowerSaveEnabled_FeatureDisabled() throws Exception { |
| when(mDeviceConfigFacade.isWifiBatterySaverEnabled()).thenReturn(false); |
| when(mPowerManagerService.isPowerSaveMode()).thenReturn(true); |
| verifyPnoScanWithInterval(MOVING_PNO_SCAN_INTERVAL_MILLIS); |
| } |
| |
| |
| /** |
| * Verify PNO scan is started with the given scan interval. |
| */ |
| private void verifyPnoScanWithInterval(int interval) throws Exception { |
| setWifiEnabled(true); |
| // starts a PNO scan |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_DISCONNECTED); |
| mWifiConnectivityManager.setTrustedConnectionAllowed(true); |
| |
| ArgumentCaptor<ScanSettings> scanSettingsCaptor = ArgumentCaptor.forClass( |
| ScanSettings.class); |
| InOrder inOrder = inOrder(mWifiScanner); |
| |
| inOrder.verify(mWifiScanner).startPnoScan(scanSettingsCaptor.capture(), any(), any()); |
| assertEquals(interval, scanSettingsCaptor.getValue().periodInMs); |
| } |
| |
| /** |
| * Change device mobility state in the middle of a PNO scan. PNO scan should stop, then restart |
| * with the updated scan period. |
| */ |
| @Test |
| public void changeDeviceMobilityStateDuringScan() { |
| setWifiEnabled(true); |
| |
| // starts a PNO scan |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_DISCONNECTED); |
| mWifiConnectivityManager.setTrustedConnectionAllowed(true); |
| |
| ArgumentCaptor<ScanSettings> scanSettingsCaptor = ArgumentCaptor.forClass( |
| ScanSettings.class); |
| InOrder inOrder = inOrder(mWifiScanner); |
| |
| inOrder.verify(mWifiScanner).startPnoScan(scanSettingsCaptor.capture(), any(), any()); |
| assertEquals(scanSettingsCaptor.getValue().periodInMs, MOVING_PNO_SCAN_INTERVAL_MILLIS); |
| |
| // initial connectivity state uses moving PNO scan interval, now set it to stationary |
| mWifiConnectivityManager.setDeviceMobilityState( |
| WifiManager.DEVICE_MOBILITY_STATE_STATIONARY); |
| |
| inOrder.verify(mWifiScanner).stopPnoScan(any()); |
| inOrder.verify(mWifiScanner).startPnoScan(scanSettingsCaptor.capture(), any(), any()); |
| assertEquals(scanSettingsCaptor.getValue().periodInMs, STATIONARY_PNO_SCAN_INTERVAL_MILLIS); |
| verify(mScoringParams, times(2)).getEntryRssi(ScanResult.BAND_6_GHZ_START_FREQ_MHZ); |
| verify(mScoringParams, times(2)).getEntryRssi(ScanResult.BAND_5_GHZ_START_FREQ_MHZ); |
| verify(mScoringParams, times(2)).getEntryRssi(ScanResult.BAND_24_GHZ_START_FREQ_MHZ); |
| } |
| |
| /** |
| * Change device mobility state in the middle of a PNO scan, but it is changed to another |
| * mobility state with the same scan period. Original PNO scan should continue. |
| */ |
| @Test |
| public void changeDeviceMobilityStateDuringScanWithSameScanPeriod() { |
| setWifiEnabled(true); |
| |
| // starts a PNO scan |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_DISCONNECTED); |
| mWifiConnectivityManager.setTrustedConnectionAllowed(true); |
| |
| ArgumentCaptor<ScanSettings> scanSettingsCaptor = ArgumentCaptor.forClass( |
| ScanSettings.class); |
| InOrder inOrder = inOrder(mWifiScanner); |
| inOrder.verify(mWifiScanner, never()).stopPnoScan(any()); |
| inOrder.verify(mWifiScanner).startPnoScan(scanSettingsCaptor.capture(), any(), any()); |
| assertEquals(scanSettingsCaptor.getValue().periodInMs, MOVING_PNO_SCAN_INTERVAL_MILLIS); |
| |
| mWifiConnectivityManager.setDeviceMobilityState( |
| WifiManager.DEVICE_MOBILITY_STATE_LOW_MVMT); |
| |
| inOrder.verifyNoMoreInteractions(); |
| } |
| |
| /** |
| * Device is already connected, setting device mobility state should do nothing since no PNO |
| * scans are running. Then, when PNO scan is started afterwards, should use the new scan period. |
| */ |
| @Test |
| public void setDeviceMobilityStateBeforePnoScan() { |
| // ensure no PNO scan running |
| setWifiEnabled(true); |
| setWifiStateConnected(); |
| |
| // initial connectivity state uses moving PNO scan interval, now set it to stationary |
| mWifiConnectivityManager.setDeviceMobilityState( |
| WifiManager.DEVICE_MOBILITY_STATE_STATIONARY); |
| |
| // no scans should start or stop because no PNO scan is running |
| verify(mWifiScanner, never()).startPnoScan(any(), any(), any()); |
| verify(mWifiScanner, never()).stopPnoScan(any()); |
| |
| // starts a PNO scan |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_DISCONNECTED); |
| mWifiConnectivityManager.setTrustedConnectionAllowed(true); |
| |
| ArgumentCaptor<ScanSettings> scanSettingsCaptor = ArgumentCaptor.forClass( |
| ScanSettings.class); |
| |
| verify(mWifiScanner).startPnoScan(scanSettingsCaptor.capture(), any(), any()); |
| // check that now the PNO scan uses the stationary interval, even though it was set before |
| // the PNO scan started |
| assertEquals(scanSettingsCaptor.getValue().periodInMs, STATIONARY_PNO_SCAN_INTERVAL_MILLIS); |
| } |
| |
| /** |
| * Tests the metrics collection of PNO scans through changes to device mobility state and |
| * starting and stopping of PNO scans. |
| */ |
| @Test |
| public void deviceMobilityStateMetricsChangeStateAndStopStart() { |
| InOrder inOrder = inOrder(mWifiMetrics); |
| |
| mWifiConnectivityManager = createConnectivityManager(); |
| setWifiEnabled(true); |
| |
| // change mobility state while no PNO scans running |
| mWifiConnectivityManager.setDeviceMobilityState( |
| WifiManager.DEVICE_MOBILITY_STATE_LOW_MVMT); |
| inOrder.verify(mWifiMetrics).enterDeviceMobilityState( |
| WifiManager.DEVICE_MOBILITY_STATE_LOW_MVMT); |
| |
| // starts a PNO scan |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_DISCONNECTED); |
| mWifiConnectivityManager.setTrustedConnectionAllowed(true); |
| inOrder.verify(mWifiMetrics).logPnoScanStart(); |
| |
| // change to High Movement, which has the same scan interval as Low Movement |
| mWifiConnectivityManager.setDeviceMobilityState( |
| WifiManager.DEVICE_MOBILITY_STATE_HIGH_MVMT); |
| inOrder.verify(mWifiMetrics).logPnoScanStop(); |
| inOrder.verify(mWifiMetrics).enterDeviceMobilityState( |
| WifiManager.DEVICE_MOBILITY_STATE_HIGH_MVMT); |
| inOrder.verify(mWifiMetrics).logPnoScanStart(); |
| |
| // change to Stationary, which has a different scan interval from High Movement |
| mWifiConnectivityManager.setDeviceMobilityState( |
| WifiManager.DEVICE_MOBILITY_STATE_STATIONARY); |
| inOrder.verify(mWifiMetrics).logPnoScanStop(); |
| inOrder.verify(mWifiMetrics).enterDeviceMobilityState( |
| WifiManager.DEVICE_MOBILITY_STATE_STATIONARY); |
| inOrder.verify(mWifiMetrics).logPnoScanStart(); |
| |
| // stops PNO scan |
| mWifiConnectivityManager.setTrustedConnectionAllowed(false); |
| inOrder.verify(mWifiMetrics).logPnoScanStop(); |
| |
| // change mobility state while no PNO scans running |
| mWifiConnectivityManager.setDeviceMobilityState( |
| WifiManager.DEVICE_MOBILITY_STATE_HIGH_MVMT); |
| inOrder.verify(mWifiMetrics).enterDeviceMobilityState( |
| WifiManager.DEVICE_MOBILITY_STATE_HIGH_MVMT); |
| |
| inOrder.verifyNoMoreInteractions(); |
| } |
| |
| /** |
| * Verify that WifiChannelUtilization is updated |
| */ |
| @Test |
| public void verifyWifiChannelUtilizationRefreshedAfterScanResults() { |
| WifiLinkLayerStats llstats = new WifiLinkLayerStats(); |
| when(mPrimaryClientModeManager.getWifiLinkLayerStats()).thenReturn(llstats); |
| |
| // Force a connectivity scan |
| mWifiConnectivityManager.forceConnectivityScan(WIFI_WORK_SOURCE); |
| mLooper.dispatchAll(); |
| verify(mWifiChannelUtilization).refreshChannelStatsAndChannelUtilization( |
| llstats, WifiChannelUtilization.UNKNOWN_FREQ); |
| } |
| |
| /** |
| * Verify that WifiChannelUtilization is initialized properly |
| */ |
| @Test |
| public void verifyWifiChannelUtilizationInitAfterWifiToggle() { |
| verify(mWifiChannelUtilization, times(1)).init(null); |
| WifiLinkLayerStats llstats = new WifiLinkLayerStats(); |
| when(mPrimaryClientModeManager.getWifiLinkLayerStats()).thenReturn(llstats); |
| |
| setWifiEnabled(false); |
| setWifiEnabled(true); |
| verify(mWifiChannelUtilization, times(1)).init(llstats); |
| } |
| |
| /** |
| * Verify that WifiChannelUtilization sets mobility state correctly |
| */ |
| @Test |
| public void verifyWifiChannelUtilizationSetMobilityState() { |
| WifiLinkLayerStats llstats = new WifiLinkLayerStats(); |
| when(mPrimaryClientModeManager.getWifiLinkLayerStats()).thenReturn(llstats); |
| |
| mWifiConnectivityManager.setDeviceMobilityState( |
| WifiManager.DEVICE_MOBILITY_STATE_HIGH_MVMT); |
| verify(mWifiChannelUtilization).setDeviceMobilityState( |
| WifiManager.DEVICE_MOBILITY_STATE_HIGH_MVMT); |
| mWifiConnectivityManager.setDeviceMobilityState( |
| WifiManager.DEVICE_MOBILITY_STATE_STATIONARY); |
| verify(mWifiChannelUtilization).setDeviceMobilityState( |
| WifiManager.DEVICE_MOBILITY_STATE_STATIONARY); |
| } |
| |
| /** |
| * Verify that WifiChannelUtilization is updated |
| */ |
| @Test |
| public void verifyForceConnectivityScan() { |
| // Auto-join enabled |
| mWifiConnectivityManager.setAutoJoinEnabledExternal(true, false); |
| mWifiConnectivityManager.forceConnectivityScan(WIFI_WORK_SOURCE); |
| verify(mWifiScanner).startScan(any(), any()); |
| |
| // Auto-join disabled, no new scans |
| mWifiConnectivityManager.setAutoJoinEnabledExternal(false, false); |
| mWifiConnectivityManager.forceConnectivityScan(WIFI_WORK_SOURCE); |
| verify(mWifiScanner, times(1)).startScan(any(), any()); |
| |
| // Wifi disabled, no new scans |
| setWifiEnabled(false); |
| mWifiConnectivityManager.forceConnectivityScan(WIFI_WORK_SOURCE); |
| verify(mWifiScanner, times(1)).startScan(any(), any()); |
| } |
| |
| @Test |
| public void testSetAndClearExternalPnoScanRequest() { |
| int testUid = 123; |
| String testPackage = "TestPackage"; |
| IBinder binder = mock(IBinder.class); |
| IPnoScanResultsCallback callback = mock(IPnoScanResultsCallback.class); |
| List<WifiSsid> requestedSsids = Arrays.asList( |
| WifiSsid.fromString("\"TEST_SSID_1\""), |
| WifiSsid.fromString("\"TEST_SSID_2\"")); |
| int[] frequencies = new int[] {TEST_FREQUENCY}; |
| mWifiConnectivityManager.setExternalPnoScanRequest(testUid, testPackage, binder, callback, |
| requestedSsids, frequencies); |
| verify(mExternalPnoScanRequestManager).setRequest(testUid, testPackage, binder, callback, |
| requestedSsids, frequencies); |
| mWifiConnectivityManager.clearExternalPnoScanRequest(testUid); |
| verify(mExternalPnoScanRequestManager).removeRequest(testUid); |
| } |
| |
| /** |
| * When location is disabled external PNO SSIDs should not get scanned. |
| */ |
| @Test |
| public void testExternalPnoScanRequest_gatedBylocationMode() { |
| when(mWifiScoreCard.lookupNetwork(any())).thenReturn(mock(WifiScoreCard.PerNetwork.class)); |
| mResources.setBoolean(R.bool.config_wifiPnoFrequencyCullingEnabled, true); |
| mWifiConnectivityManager.setLocationModeEnabled(false); |
| // mock saved networks list to be empty |
| when(mWifiConfigManager.getSavedNetworks(anyInt())).thenReturn(Collections.EMPTY_LIST); |
| |
| |
| // Mock a couple external requested PNO SSIDs |
| Set<String> requestedSsids = new ArraySet<>(); |
| requestedSsids.add("\"Test_SSID_1\""); |
| requestedSsids.add("\"Test_SSID_2\""); |
| when(mExternalPnoScanRequestManager.getExternalPnoScanSsids()).thenReturn(requestedSsids); |
| Set<Integer> frequencies = new ArraySet<>(); |
| frequencies.add(TEST_FREQUENCY); |
| when(mExternalPnoScanRequestManager.getExternalPnoScanFrequencies()) |
| .thenReturn(frequencies); |
| |
| assertEquals(Collections.EMPTY_LIST, mWifiConnectivityManager.retrievePnoNetworkList()); |
| |
| // turn location mode on and now PNO scan should include the requested SSIDs |
| mWifiConnectivityManager.setLocationModeEnabled(true); |
| List<WifiScanner.PnoSettings.PnoNetwork> pnoNetworks = |
| mWifiConnectivityManager.retrievePnoNetworkList(); |
| assertEquals(2, pnoNetworks.size()); |
| assertEquals("\"Test_SSID_1\"", pnoNetworks.get(0).ssid); |
| assertEquals("\"Test_SSID_2\"", pnoNetworks.get(1).ssid); |
| assertArrayEquals(new int[] {TEST_FREQUENCY}, pnoNetworks.get(0).frequencies); |
| assertArrayEquals(new int[] {TEST_FREQUENCY}, pnoNetworks.get(1).frequencies); |
| } |
| |
| /** |
| * Test external requested PNO SSIDs get handled properly when there are existing saved networks |
| * with same SSID. |
| */ |
| @Test |
| public void testExternalPnoScanRequest_withSavedNetworks() { |
| mWifiConnectivityManager.setLocationModeEnabled(true); |
| // Create and add 3 networks. |
| WifiConfiguration network1 = WifiConfigurationTestUtil.createPasspointNetwork(); |
| network1.ephemeral = true; |
| network1.getNetworkSelectionStatus().setHasEverConnected(false); |
| WifiConfiguration network2 = WifiConfigurationTestUtil.createPskNetwork(); |
| network2.getNetworkSelectionStatus().setHasEverConnected(true); |
| WifiConfiguration network3 = WifiConfigurationTestUtil.createPskNetwork(); |
| List<WifiConfiguration> networkList = new ArrayList<>(); |
| networkList.add(network1); |
| networkList.add(network2); |
| networkList.add(network3); |
| mLruConnectionTracker.addNetwork(network3); |
| mLruConnectionTracker.addNetwork(network2); |
| mLruConnectionTracker.addNetwork(network1); |
| when(mWifiConfigManager.getSavedNetworks(anyInt())).thenReturn(networkList); |
| |
| // Mock a couple external requested PNO SSIDs. network3.SSID is in both saved networks |
| // and external requested networks. |
| Set<String> requestedSsids = new ArraySet<>(); |
| requestedSsids.add("\"Test_SSID_1\""); |
| requestedSsids.add(network2.SSID); |
| when(mExternalPnoScanRequestManager.getExternalPnoScanSsids()).thenReturn(requestedSsids); |
| |
| List<WifiScanner.PnoSettings.PnoNetwork> pnoNetworks = |
| mWifiConnectivityManager.retrievePnoNetworkList(); |
| // There should be 4 SSIDs in total: network1, an extra original (untranslated) SSID of |
| // network1, network2, and Test_SSID_1. |
| // network1 should be included in PNO even if it's never connected because it's ephemeral. |
| // network3 should not get included because it's saved and never connected before. |
| assertEquals(4, pnoNetworks.size()); |
| // Verify the order. Test_SSID_1 and network2 should be in the front because they are |
| // requested by an external app. Verify network2.SSID only appears once. |
| assertEquals("\"Test_SSID_1\"", pnoNetworks.get(0).ssid); |
| assertEquals(network2.SSID, pnoNetworks.get(1).ssid); |
| assertEquals(network1.SSID, pnoNetworks.get(2).ssid); |
| assertEquals(UNTRANSLATED_HEX_SSID, pnoNetworks.get(3).ssid); // Possible untranslated SSID |
| } |
| |
| @Test |
| public void testExternalPnoScanRequest_reportResults() { |
| setWifiEnabled(true); |
| mWifiConnectivityManager.setLocationModeEnabled(true); |
| |
| when(mClock.getElapsedSinceBootMillis()).thenReturn(0L); |
| // starts a PNO scan |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_DISCONNECTED); |
| mWifiConnectivityManager.setTrustedConnectionAllowed(true); |
| |
| InOrder inOrder = inOrder(mWifiScanner, mExternalPnoScanRequestManager); |
| |
| inOrder.verify(mWifiScanner).startPnoScan(any(), any(), any()); |
| inOrder.verify(mExternalPnoScanRequestManager).onScanResultsAvailable(any()); |
| |
| // mock connectivity scan |
| mWifiConnectivityManager.forceConnectivityScan(WIFI_WORK_SOURCE); |
| mLooper.dispatchAll(); |
| // verify mExternalPnoScanRequestManager is notified again |
| inOrder.verify(mExternalPnoScanRequestManager).onScanResultsAvailable(any()); |
| } |
| |
| /** |
| * Verify no network is network selection disabled, auto-join disabled using. |
| * {@link WifiConnectivityManager#retrievePnoNetworkList()}. |
| */ |
| @Test |
| public void testRetrievePnoList() { |
| // Create and add 3 networks. |
| WifiConfiguration network1 = WifiConfigurationTestUtil.createEapNetwork(); |
| WifiConfiguration network2 = WifiConfigurationTestUtil.createPskNetwork(); |
| WifiConfiguration network3 = WifiConfigurationTestUtil.createOpenHiddenNetwork(); |
| network1.getNetworkSelectionStatus().setHasEverConnected(true); |
| network2.getNetworkSelectionStatus().setHasEverConnected(true); |
| network3.getNetworkSelectionStatus().setHasEverConnected(true); |
| |
| List<WifiConfiguration> networkList = new ArrayList<>(); |
| networkList.add(network1); |
| networkList.add(network2); |
| networkList.add(network3); |
| mLruConnectionTracker.addNetwork(network3); |
| mLruConnectionTracker.addNetwork(network2); |
| mLruConnectionTracker.addNetwork(network1); |
| when(mWifiConfigManager.getSavedNetworks(anyInt())).thenReturn(networkList); |
| // Retrieve the Pno network list & verify. |
| List<WifiScanner.PnoSettings.PnoNetwork> pnoNetworks = |
| mWifiConnectivityManager.retrievePnoNetworkList(); |
| verify(mWifiNetworkSuggestionsManager).getAllScanOptimizationSuggestionNetworks(); |
| assertEquals(4, pnoNetworks.size()); |
| assertEquals(network1.SSID, pnoNetworks.get(0).ssid); |
| assertEquals(UNTRANSLATED_HEX_SSID, pnoNetworks.get(1).ssid); // Possible untranslated SSID |
| assertEquals(network2.SSID, pnoNetworks.get(2).ssid); |
| assertEquals(network3.SSID, pnoNetworks.get(3).ssid); |
| |
| // Now permanently disable |network3|. This should remove network 3 from the list. |
| network3.getNetworkSelectionStatus().setNetworkSelectionStatus( |
| WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_TEMPORARY_DISABLED); |
| |
| // Retrieve the Pno network list & verify. |
| pnoNetworks = mWifiConnectivityManager.retrievePnoNetworkList(); |
| assertEquals(3, pnoNetworks.size()); |
| assertEquals(network1.SSID, pnoNetworks.get(0).ssid); |
| assertEquals(UNTRANSLATED_HEX_SSID, pnoNetworks.get(1).ssid); // Possible untranslated SSID |
| assertEquals(network2.SSID, pnoNetworks.get(2).ssid); |
| |
| // Now set network1 autojoin disabled. This should remove network 1 from the list. |
| network1.allowAutojoin = false; |
| // Retrieve the Pno network list & verify. |
| pnoNetworks = mWifiConnectivityManager.retrievePnoNetworkList(); |
| assertEquals(2, pnoNetworks.size()); |
| assertEquals(network2.SSID, pnoNetworks.get(0).ssid); |
| assertEquals(UNTRANSLATED_HEX_SSID, pnoNetworks.get(1).ssid); // Possible untranslated SSID |
| |
| // Now set network2 to be temporarily disabled by the user. This should remove network 2 |
| // from the list. |
| when(mWifiConfigManager.isNetworkTemporarilyDisabledByUser(network2.SSID)).thenReturn(true); |
| pnoNetworks = mWifiConnectivityManager.retrievePnoNetworkList(); |
| assertEquals(0, pnoNetworks.size()); |
| } |
| |
| /** |
| * Verifies frequencies are populated correctly for pno networks. |
| * {@link WifiConnectivityManager#retrievePnoNetworkList()}. |
| */ |
| @Test |
| public void testRetrievePnoListFrequencies() { |
| // Create 2 networks. |
| WifiConfiguration network1 = WifiConfigurationTestUtil.createEapNetwork(); |
| WifiConfiguration network2 = WifiConfigurationTestUtil.createPskNetwork(); |
| network1.getNetworkSelectionStatus().setHasEverConnected(true); |
| network2.getNetworkSelectionStatus().setHasEverConnected(true); |
| List<WifiConfiguration> networkList = new ArrayList<>(); |
| networkList.add(network1); |
| networkList.add(network2); |
| mLruConnectionTracker.addNetwork(network2); |
| mLruConnectionTracker.addNetwork(network1); |
| when(mWifiConfigManager.getSavedNetworks(anyInt())).thenReturn(networkList); |
| // Retrieve the Pno network list and verify. |
| // Frequencies should be empty since no scan results have been received yet. |
| List<WifiScanner.PnoSettings.PnoNetwork> pnoNetworks = |
| mWifiConnectivityManager.retrievePnoNetworkList(); |
| assertEquals(3, pnoNetworks.size()); |
| assertEquals(network1.SSID, pnoNetworks.get(0).ssid); |
| assertEquals(UNTRANSLATED_HEX_SSID, pnoNetworks.get(1).ssid); // Possible untranslated SSID |
| assertEquals(network2.SSID, pnoNetworks.get(2).ssid); |
| assertEquals("frequencies should be empty", 0, pnoNetworks.get(0).frequencies.length); |
| assertEquals("frequencies should be empty", 0, pnoNetworks.get(1).frequencies.length); |
| assertEquals("frequencies should be empty", 0, pnoNetworks.get(2).frequencies.length); |
| |
| //Set up wifiScoreCard to get frequency. |
| List<Integer> channelList = Arrays |
| .asList(TEST_FREQUENCY_1, TEST_FREQUENCY_2, TEST_FREQUENCY_3); |
| when(mWifiScoreCard.lookupNetwork(network1.SSID)).thenReturn(mPerNetwork); |
| when(mWifiScoreCard.lookupNetwork(network2.SSID)).thenReturn(mPerNetwork1); |
| when(mPerNetwork.getFrequencies(anyLong())).thenReturn(channelList); |
| when(mPerNetwork1.getFrequencies(anyLong())).thenReturn(new ArrayList<>()); |
| |
| //Set config_wifiPnoFrequencyCullingEnabled false, should ignore get frequency. |
| mResources.setBoolean(R.bool.config_wifiPnoFrequencyCullingEnabled, false); |
| pnoNetworks = mWifiConnectivityManager.retrievePnoNetworkList(); |
| assertEquals(3, pnoNetworks.size()); |
| assertEquals(network1.SSID, pnoNetworks.get(0).ssid); |
| assertEquals(UNTRANSLATED_HEX_SSID, pnoNetworks.get(1).ssid); // Possible untranslated SSID |
| assertEquals(network2.SSID, pnoNetworks.get(2).ssid); |
| assertEquals("frequencies should be empty", 0, pnoNetworks.get(0).frequencies.length); |
| assertEquals("frequencies should be empty", 0, pnoNetworks.get(1).frequencies.length); |
| assertEquals("frequencies should be empty", 0, pnoNetworks.get(2).frequencies.length); |
| |
| // Set config_wifiPnoFrequencyCullingEnabled false, should get the right frequency. |
| mResources.setBoolean(R.bool.config_wifiPnoFrequencyCullingEnabled, true); |
| pnoNetworks = mWifiConnectivityManager.retrievePnoNetworkList(); |
| assertEquals(3, pnoNetworks.size()); |
| assertEquals(network1.SSID, pnoNetworks.get(0).ssid); |
| assertEquals(UNTRANSLATED_HEX_SSID, pnoNetworks.get(1).ssid); // Possible untranslated SSID |
| assertEquals(network2.SSID, pnoNetworks.get(2).ssid); |
| assertEquals(3, pnoNetworks.get(0).frequencies.length); |
| Arrays.sort(pnoNetworks.get(0).frequencies); |
| assertEquals(TEST_FREQUENCY_1, pnoNetworks.get(0).frequencies[0]); |
| assertEquals(TEST_FREQUENCY_2, pnoNetworks.get(0).frequencies[1]); |
| assertEquals(TEST_FREQUENCY_3, pnoNetworks.get(0).frequencies[2]); |
| assertEquals("frequencies should be empty", 0, pnoNetworks.get(2).frequencies.length); |
| } |
| |
| |
| /** |
| * Verifies the ordering of network list generated using |
| * {@link WifiConnectivityManager#retrievePnoNetworkList()}. |
| */ |
| @Test |
| public void testRetrievePnoListOrder() { |
| //Create 4 networks. |
| WifiConfiguration network1 = WifiConfigurationTestUtil.createEapNetwork(); |
| WifiConfiguration network2 = WifiConfigurationTestUtil.createPskNetwork(); |
| WifiConfiguration network3 = WifiConfigurationTestUtil.createOpenHiddenNetwork(); |
| WifiConfiguration network4 = WifiConfigurationTestUtil.createPskNetwork(); |
| |
| // mark all networks except network4 as connected before |
| network1.getNetworkSelectionStatus().setHasEverConnected(true); |
| network2.getNetworkSelectionStatus().setHasEverConnected(true); |
| network3.getNetworkSelectionStatus().setHasEverConnected(true); |
| |
| mLruConnectionTracker.addNetwork(network1); |
| mLruConnectionTracker.addNetwork(network2); |
| mLruConnectionTracker.addNetwork(network3); |
| mLruConnectionTracker.addNetwork(network4); |
| List<WifiConfiguration> networkList = new ArrayList<>(); |
| networkList.add(network1); |
| networkList.add(network2); |
| networkList.add(network3); |
| when(mWifiConfigManager.getSavedNetworks(anyInt())).thenReturn(networkList); |
| List<WifiScanner.PnoSettings.PnoNetwork> pnoNetworks = |
| mWifiConnectivityManager.retrievePnoNetworkList(); |
| |
| // Verify correct order of networks. Note that network4 should not appear for PNO scan |
| // since it had not been connected before. |
| assertEquals(4, pnoNetworks.size()); |
| assertEquals(network3.SSID, pnoNetworks.get(0).ssid); |
| assertEquals(UNTRANSLATED_HEX_SSID, pnoNetworks.get(1).ssid); // Possible untranslated SSID |
| assertEquals(network2.SSID, pnoNetworks.get(2).ssid); |
| assertEquals(network1.SSID, pnoNetworks.get(3).ssid); |
| } |
| |
| private List<List<Integer>> linkScoreCardFreqsToNetwork(WifiConfiguration... configs) { |
| List<List<Integer>> results = new ArrayList<>(); |
| int i = 0; |
| for (WifiConfiguration config : configs) { |
| List<Integer> channelList = Arrays.asList(TEST_FREQUENCY_1 + i, TEST_FREQUENCY_2 + i, |
| TEST_FREQUENCY_3 + i); |
| WifiScoreCard.PerNetwork perNetwork = mock(WifiScoreCard.PerNetwork.class); |
| when(mWifiScoreCard.lookupNetwork(config.SSID)).thenReturn(perNetwork); |
| when(perNetwork.getFrequencies(anyLong())).thenReturn(channelList); |
| results.add(channelList); |
| i++; |
| } |
| return results; |
| } |
| |
| /** |
| * Verify that the length of frequency set will not exceed the provided max value |
| */ |
| @Test |
| public void testFetchChannelSetForPartialScanMaxCount() { |
| WifiConfiguration configuration1 = WifiConfigurationTestUtil.createOpenNetwork(); |
| WifiConfiguration configuration2 = WifiConfigurationTestUtil.createOpenNetwork(); |
| configuration1.getNetworkSelectionStatus().setHasEverConnected(true); |
| configuration2.getNetworkSelectionStatus().setHasEverConnected(true); |
| when(mWifiConfigManager.getSavedNetworks(anyInt())) |
| .thenReturn(Arrays.asList(configuration1, configuration2)); |
| |
| List<List<Integer>> freqs = linkScoreCardFreqsToNetwork(configuration1, configuration2); |
| |
| mLruConnectionTracker.addNetwork(configuration2); |
| mLruConnectionTracker.addNetwork(configuration1); |
| |
| assertEquals(new HashSet<>(freqs.get(0)), mWifiConnectivityManager |
| .fetchChannelSetForPartialScan(3, CHANNEL_CACHE_AGE_MINS)); |
| } |
| |
| /** |
| * Verifies the creation of channel list using |
| * {@link WifiConnectivityManager#fetchChannelSetForNetworkForPartialScan(int)}. |
| */ |
| @Test |
| public void testFetchChannelSetForNetwork() { |
| WifiConfiguration configuration = WifiConfigurationTestUtil.createOpenNetwork(); |
| configuration.networkId = TEST_CONNECTED_NETWORK_ID; |
| when(mWifiConfigManager.getConfiguredNetwork(TEST_CONNECTED_NETWORK_ID)) |
| .thenReturn(configuration); |
| List<List<Integer>> freqs = linkScoreCardFreqsToNetwork(configuration); |
| |
| assertEquals(new HashSet<>(freqs.get(0)), mWifiConnectivityManager |
| .fetchChannelSetForNetworkForPartialScan(configuration.networkId)); |
| } |
| |
| /** |
| * Verifies the creation of channel list using |
| * {@link WifiConnectivityManager#fetchChannelSetForNetworkForPartialScan(int)} and |
| * ensures that the frequenecy of the currently connected network is in the returned |
| * channel set. |
| */ |
| @Test |
| public void testFetchChannelSetForNetworkIncludeCurrentNetwork() { |
| WifiConfiguration configuration = WifiConfigurationTestUtil.createOpenNetwork(); |
| configuration.networkId = TEST_CONNECTED_NETWORK_ID; |
| when(mWifiConfigManager.getConfiguredNetwork(TEST_CONNECTED_NETWORK_ID)) |
| .thenReturn(configuration); |
| linkScoreCardFreqsToNetwork(configuration); |
| |
| mWifiInfo.setFrequency(TEST_CURRENT_CONNECTED_FREQUENCY); |
| |
| // Currently connected network frequency 2427 is not in the TEST_FREQ_LIST |
| Set<Integer> freqs = mWifiConnectivityManager.fetchChannelSetForNetworkForPartialScan( |
| configuration.networkId); |
| |
| assertTrue(freqs.contains(2427)); |
| } |
| |
| /** |
| * Verifies the creation of channel list using |
| * {@link WifiConnectivityManager#fetchChannelSetForNetworkForPartialScan(int)} and |
| * ensures that the list size does not exceed the max configured for the device. |
| */ |
| @Test |
| public void testFetchChannelSetForNetworkIsLimitedToConfiguredSize() { |
| // Need to recreate the WifiConfigManager instance for this test to modify the config |
| // value which is read only in the constructor. |
| int maxListSize = 2; |
| mResources.setInteger( |
| R.integer.config_wifi_framework_associated_partial_scan_max_num_active_channels, |
| maxListSize); |
| |
| WifiConfiguration configuration = WifiConfigurationTestUtil.createOpenNetwork(); |
| configuration.networkId = TEST_CONNECTED_NETWORK_ID; |
| when(mWifiConfigManager.getConfiguredNetwork(TEST_CONNECTED_NETWORK_ID)) |
| .thenReturn(configuration); |
| List<List<Integer>> freqs = linkScoreCardFreqsToNetwork(configuration); |
| // Ensure that the fetched list size is limited. |
| Set<Integer> results = mWifiConnectivityManager.fetchChannelSetForNetworkForPartialScan( |
| configuration.networkId); |
| assertEquals(maxListSize, results.size()); |
| assertFalse(results.contains(freqs.get(0).get(2))); |
| } |
| |
| @Test |
| public void restartPnoScanForNetworkChanges() { |
| setWifiEnabled(true); |
| |
| when(mClock.getElapsedSinceBootMillis()).thenReturn(0L); |
| // starts a PNO scan |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| mPrimaryClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_DISCONNECTED); |
| mWifiConnectivityManager.setTrustedConnectionAllowed(true); |
| |
| InOrder inOrder = inOrder(mWifiScanner); |
| |
| inOrder.verify(mWifiScanner).startPnoScan(any(), any(), any()); |
| |
| // Add or update suggestions. |
| mSuggestionUpdateListenerCaptor.getValue().onSuggestionsAddedOrUpdated( |
| Arrays.asList(mWifiNetworkSuggestion)); |
| // Add saved network |
| mNetworkUpdateListenerCaptor.getValue().onNetworkAdded(new WifiConfiguration()); |
| // Ensure that we don't immediately restarted PNO. |
| inOrder.verify(mWifiScanner, never()).stopPnoScan(any()); |
| inOrder.verify(mWifiScanner, never()).startPnoScan(any(), any(), any()); |
| |
| // Verify there is only 1 delayed scan scheduled |
| assertEquals(1, mTestHandler.getIntervals().size()); |
| assertEquals(NETWORK_CHANGE_TRIGGER_PNO_THROTTLE_MS, |
| (long) mTestHandler.getIntervals().get(0)); |
| when(mClock.getElapsedSinceBootMillis()).thenReturn(mTestHandler.getIntervals().get(0)); |
| // Now advance the test handler and fire the periodic scan timer |
| mTestHandler.timeAdvance(); |
| |
| // Ensure that we restarted PNO. |
| inOrder.verify(mWifiScanner).stopPnoScan(any()); |
| inOrder.verify(mWifiScanner).startPnoScan(any(), any(), any()); |
| } |
| |
| @Test |
| public void includeSecondaryStaWhenPresentInGetCandidatesFromScan() { |
| // Set screen to on |
| setScreenState(true); |
| |
| ConcreteClientModeManager primaryCmm = mock(ConcreteClientModeManager.class); |
| WifiInfo wifiInfo1 = mock(WifiInfo.class); |
| when(primaryCmm.getInterfaceName()).thenReturn("wlan0"); |
| when(primaryCmm.getRole()).thenReturn(ROLE_CLIENT_PRIMARY); |
| when(primaryCmm.isConnected()).thenReturn(false); |
| when(primaryCmm.isDisconnected()).thenReturn(true); |
| when(primaryCmm.getConnectionInfo()).thenReturn(wifiInfo1); |
| |
| ConcreteClientModeManager secondaryCmm = mock(ConcreteClientModeManager.class); |
| WifiInfo wifiInfo2 = mock(WifiInfo.class); |
| when(secondaryCmm.getInterfaceName()).thenReturn("wlan1"); |
| when(secondaryCmm.getRole()).thenReturn(ROLE_CLIENT_SECONDARY_LONG_LIVED); |
| when(secondaryCmm.isConnected()).thenReturn(false); |
| when(secondaryCmm.isDisconnected()).thenReturn(true); |
| when(secondaryCmm.getConnectionInfo()).thenReturn(wifiInfo2); |
| |
| when(mActiveModeWarden.getInternetConnectivityClientModeManagers()) |
| .thenReturn(Arrays.asList(primaryCmm, secondaryCmm)); |
| |
| // Set WiFi to disconnected state to trigger scan |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| primaryCmm, |
| WifiConnectivityManager.WIFI_STATE_DISCONNECTED); |
| mLooper.dispatchAll(); |
| List<WifiNetworkSelector.ClientModeManagerState> expectedCmmStates = |
| Arrays.asList(new WifiNetworkSelector.ClientModeManagerState( |
| "wlan0", false, true, wifiInfo1), |
| new WifiNetworkSelector.ClientModeManagerState( |
| "wlan1", false, true, wifiInfo2)); |
| verify(mWifiNS).getCandidatesFromScan(any(), any(), |
| eq(expectedCmmStates), anyBoolean(), anyBoolean(), anyBoolean(), any(), |
| anyBoolean()); |
| } |
| |
| @Test |
| public void includeSecondaryStaWhenNotPresentButAvailableInGetCandidatesFromScan() { |
| // Set screen to on |
| setScreenState(true); |
| // set OEM paid connection allowed. |
| WorkSource oemPaidWs = new WorkSource(); |
| mWifiConnectivityManager.setOemPaidConnectionAllowed(true, oemPaidWs); |
| |
| ConcreteClientModeManager primaryCmm = mock(ConcreteClientModeManager.class); |
| WifiInfo wifiInfo1 = mock(WifiInfo.class); |
| when(primaryCmm.getInterfaceName()).thenReturn("wlan0"); |
| when(primaryCmm.getRole()).thenReturn(ROLE_CLIENT_PRIMARY); |
| when(primaryCmm.isConnected()).thenReturn(false); |
| when(primaryCmm.isDisconnected()).thenReturn(true); |
| when(primaryCmm.getConnectionInfo()).thenReturn(wifiInfo1); |
| |
| when(mActiveModeWarden.getInternetConnectivityClientModeManagers()) |
| .thenReturn(Arrays.asList(primaryCmm)); |
| // Second STA creation is allowed. |
| when(mActiveModeWarden.canRequestMoreClientModeManagersInRole( |
| eq(oemPaidWs), eq(ROLE_CLIENT_SECONDARY_LONG_LIVED), eq(false))).thenReturn(true); |
| |
| // Set WiFi to disconnected state to trigger scan |
| mWifiConnectivityManager.handleConnectionStateChanged( |
| primaryCmm, |
| WifiConnectivityManager.WIFI_STATE_DISCONNECTED); |
| mLooper.dispatchAll(); |
| List<WifiNetworkSelector.ClientModeManagerState> expectedCmmStates = |
| Arrays.asList(new WifiNetworkSelector.ClientModeManagerState( |
| "wlan0", false, true, wifiInfo1), |
| new WifiNetworkSelector.ClientModeManagerState( |
| "unknown", false, true, new WifiInfo())); |
| verify(mWifiNS).getCandidatesFromScan(any(), any(), |
| eq(expectedCmmStates), anyBoolean(), anyBoolean(), anyBoolean(), any(), |
| anyBoolean()); |
| } |
| |
| private void setWifiEnabled(boolean enable) { |
| ActiveModeWarden.ModeChangeCallback modeChangeCallback = |
| mModeChangeCallbackCaptor.getValue(); |
| assertNotNull(modeChangeCallback); |
| if (enable) { |
| when(mActiveModeWarden.getInternetConnectivityClientModeManagers()) |
| .thenReturn(Arrays.asList(mPrimaryClientModeManager)); |
| modeChangeCallback.onActiveModeManagerAdded(mPrimaryClientModeManager); |
| } else { |
| when(mActiveModeWarden.getInternetConnectivityClientModeManagers()) |
| .thenReturn(Arrays.asList()); |
| modeChangeCallback.onActiveModeManagerRemoved(mPrimaryClientModeManager); |
| } |
| } |
| |
| private void setScreenState(boolean screenOn) { |
| InOrder inOrder = inOrder(mWifiNS); |
| BroadcastReceiver broadcastReceiver = mBroadcastReceiverCaptor.getValue(); |
| assertNotNull(broadcastReceiver); |
| Intent intent = new Intent(screenOn ? ACTION_SCREEN_ON : ACTION_SCREEN_OFF); |
| broadcastReceiver.onReceive(mContext, intent); |
| inOrder.verify(mWifiNS).setScreenState(screenOn); |
| } |
| } |