| /* |
| * Copyright (C) 2015 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.net.wifi.WifiConfiguration.METERED_OVERRIDE_METERED; |
| import static android.net.wifi.WifiConfiguration.METERED_OVERRIDE_NONE; |
| import static android.net.wifi.WifiConfiguration.METERED_OVERRIDE_NOT_METERED; |
| import static android.net.wifi.WifiConfiguration.NetworkSelectionStatus.DISABLED_NONE; |
| import static android.net.wifi.WifiConfiguration.NetworkSelectionStatus.DISABLED_NO_INTERNET_TEMPORARY; |
| |
| import static com.android.server.wifi.ClientModeImpl.CMD_PRE_DHCP_ACTION; |
| |
| import static org.junit.Assert.assertArrayEquals; |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertFalse; |
| import static org.junit.Assert.assertNotEquals; |
| import static org.junit.Assert.assertNotNull; |
| import static org.junit.Assert.assertNull; |
| import static org.junit.Assert.assertTrue; |
| import static org.mockito.Mockito.any; |
| import static org.mockito.Mockito.anyBoolean; |
| import static org.mockito.Mockito.anyByte; |
| import static org.mockito.Mockito.anyInt; |
| import static org.mockito.Mockito.anyLong; |
| import static org.mockito.Mockito.anyObject; |
| import static org.mockito.Mockito.anyString; |
| import static org.mockito.Mockito.argThat; |
| import static org.mockito.Mockito.atLeastOnce; |
| import static org.mockito.Mockito.doAnswer; |
| import static org.mockito.Mockito.doReturn; |
| import static org.mockito.Mockito.eq; |
| import static org.mockito.Mockito.inOrder; |
| import static org.mockito.Mockito.mock; |
| import static org.mockito.Mockito.never; |
| import static org.mockito.Mockito.reset; |
| import static org.mockito.Mockito.spy; |
| import static org.mockito.Mockito.times; |
| import static org.mockito.Mockito.verify; |
| import static org.mockito.Mockito.verifyNoMoreInteractions; |
| import static org.mockito.Mockito.when; |
| import static org.mockito.Mockito.withSettings; |
| |
| import android.app.ActivityManager; |
| import android.app.test.MockAnswerUtil.AnswerWithArguments; |
| import android.app.test.TestAlarmManager; |
| import android.bluetooth.BluetoothAdapter; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.pm.PackageManager; |
| import android.hardware.wifi.supplicant.V1_0.ISupplicantStaIfaceCallback; |
| import android.net.ConnectivityManager; |
| import android.net.DhcpResultsParcelable; |
| import android.net.InetAddresses; |
| import android.net.Layer2InformationParcelable; |
| import android.net.Layer2PacketParcelable; |
| import android.net.LinkAddress; |
| import android.net.LinkProperties; |
| import android.net.MacAddress; |
| import android.net.Network; |
| import android.net.NetworkAgent; |
| import android.net.NetworkAgentConfig; |
| import android.net.NetworkCapabilities; |
| import android.net.NetworkInfo; |
| import android.net.NetworkSpecifier; |
| import android.net.StaticIpConfiguration; |
| import android.net.ip.IIpClient; |
| import android.net.ip.IpClientCallbacks; |
| import android.net.wifi.IActionListener; |
| import android.net.wifi.ScanResult; |
| import android.net.wifi.SupplicantState; |
| import android.net.wifi.WifiConfiguration; |
| import android.net.wifi.WifiConfiguration.KeyMgmt; |
| import android.net.wifi.WifiEnterpriseConfig; |
| import android.net.wifi.WifiInfo; |
| import android.net.wifi.WifiManager; |
| import android.net.wifi.WifiNetworkAgentSpecifier; |
| import android.net.wifi.WifiScanner; |
| import android.net.wifi.WifiSsid; |
| import android.net.wifi.hotspot2.IProvisioningCallback; |
| import android.net.wifi.hotspot2.OsuProvider; |
| import android.net.wifi.nl80211.WifiNl80211Manager; |
| import android.net.wifi.p2p.WifiP2pManager; |
| import android.os.BatteryStatsManager; |
| import android.os.Binder; |
| import android.os.Bundle; |
| import android.os.Handler; |
| import android.os.HandlerThread; |
| import android.os.IBinder; |
| import android.os.IPowerManager; |
| import android.os.IThermalService; |
| import android.os.Looper; |
| import android.os.Message; |
| import android.os.Messenger; |
| import android.os.PowerManager; |
| import android.os.Process; |
| import android.os.UserManager; |
| import android.os.test.TestLooper; |
| import android.provider.Settings; |
| import android.telephony.SubscriptionInfo; |
| import android.telephony.SubscriptionManager; |
| import android.telephony.TelephonyManager; |
| import android.test.mock.MockContentProvider; |
| import android.test.mock.MockContentResolver; |
| import android.util.Log; |
| import android.util.Pair; |
| |
| import androidx.test.filters.SmallTest; |
| |
| import com.android.dx.mockito.inline.extended.ExtendedMockito; |
| import com.android.internal.util.AsyncChannel; |
| import com.android.internal.util.IState; |
| import com.android.internal.util.StateMachine; |
| import com.android.server.wifi.hotspot2.NetworkDetail; |
| import com.android.server.wifi.hotspot2.PasspointManager; |
| import com.android.server.wifi.hotspot2.PasspointProvisioningTestUtil; |
| import com.android.server.wifi.proto.nano.WifiMetricsProto; |
| import com.android.server.wifi.proto.nano.WifiMetricsProto.StaEvent; |
| import com.android.server.wifi.proto.nano.WifiMetricsProto.WifiIsUnusableEvent; |
| import com.android.server.wifi.proto.nano.WifiMetricsProto.WifiUsabilityStats; |
| import com.android.server.wifi.util.RssiUtilTest; |
| import com.android.server.wifi.util.ScanResultUtil; |
| import com.android.server.wifi.util.WifiPermissionsUtil; |
| import com.android.server.wifi.util.WifiPermissionsWrapper; |
| 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.InOrder; |
| import org.mockito.Mock; |
| import org.mockito.MockitoAnnotations; |
| import org.mockito.MockitoSession; |
| import org.mockito.quality.Strictness; |
| |
| import java.io.ByteArrayOutputStream; |
| import java.io.PrintWriter; |
| import java.lang.reflect.Field; |
| import java.lang.reflect.InvocationTargetException; |
| import java.lang.reflect.Method; |
| import java.nio.charset.StandardCharsets; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.BitSet; |
| import java.util.Collections; |
| import java.util.List; |
| import java.util.concurrent.CountDownLatch; |
| import java.util.function.Consumer; |
| |
| /** |
| * Unit tests for {@link com.android.server.wifi.ClientModeImpl}. |
| */ |
| @SmallTest |
| public class ClientModeImplTest extends WifiBaseTest { |
| public static final String TAG = "ClientModeImplTest"; |
| |
| private static final int MANAGED_PROFILE_UID = 1100000; |
| private static final int OTHER_USER_UID = 1200000; |
| private static final int LOG_REC_LIMIT_IN_VERBOSE_MODE = |
| (ActivityManager.isLowRamDeviceStatic() |
| ? ClientModeImpl.NUM_LOG_RECS_VERBOSE_LOW_MEMORY |
| : ClientModeImpl.NUM_LOG_RECS_VERBOSE); |
| private static final int FRAMEWORK_NETWORK_ID = 0; |
| private static final int PASSPOINT_NETWORK_ID = 1; |
| private static final int TEST_RSSI = -54; |
| private static final int TEST_NETWORK_ID = 54; |
| private static final int WPS_SUPPLICANT_NETWORK_ID = 5; |
| private static final int WPS_FRAMEWORK_NETWORK_ID = 10; |
| private static final String DEFAULT_TEST_SSID = "\"GoogleGuest\""; |
| private static final String OP_PACKAGE_NAME = "com.xxx"; |
| private static final int TEST_UID = Process.SYSTEM_UID + 1000; |
| private static final MacAddress TEST_GLOBAL_MAC_ADDRESS = |
| MacAddress.fromString("10:22:34:56:78:92"); |
| private static final MacAddress TEST_LOCAL_MAC_ADDRESS = |
| MacAddress.fromString("2a:53:43:c3:56:21"); |
| private static final MacAddress TEST_DEFAULT_MAC_ADDRESS = |
| MacAddress.fromString(WifiInfo.DEFAULT_MAC_ADDRESS); |
| |
| // NetworkAgent creates threshold ranges with Integers |
| private static final int RSSI_THRESHOLD_MAX = -30; |
| private static final int RSSI_THRESHOLD_MIN = -76; |
| // Threshold breach callbacks are called with bytes |
| private static final byte RSSI_THRESHOLD_BREACH_MIN = -80; |
| private static final byte RSSI_THRESHOLD_BREACH_MAX = -20; |
| |
| private static final int DATA_SUBID = 1; |
| private static final int CARRIER_ID_1 = 100; |
| |
| private long mBinderToken; |
| private MockitoSession mSession; |
| |
| private static <T> T mockWithInterfaces(Class<T> class1, Class<?>... interfaces) { |
| return mock(class1, withSettings().extraInterfaces(interfaces)); |
| } |
| |
| private void enableDebugLogs() { |
| mCmi.enableVerboseLogging(1); |
| } |
| |
| private FrameworkFacade getFrameworkFacade() throws Exception { |
| FrameworkFacade facade = mock(FrameworkFacade.class); |
| |
| doAnswer(new AnswerWithArguments() { |
| public void answer( |
| Context context, String ifname, IpClientCallbacks callback) { |
| mIpClientCallback = callback; |
| callback.onIpClientCreated(mIpClient); |
| } |
| }).when(facade).makeIpClient(any(), anyString(), any()); |
| |
| return facade; |
| } |
| |
| private Context getContext() throws Exception { |
| when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_WIFI_DIRECT)).thenReturn(true); |
| |
| Context context = mock(Context.class); |
| when(context.getPackageManager()).thenReturn(mPackageManager); |
| |
| MockContentResolver mockContentResolver = new MockContentResolver(); |
| mockContentResolver.addProvider(Settings.AUTHORITY, |
| new MockContentProvider(context) { |
| @Override |
| public Bundle call(String method, String arg, Bundle extras) { |
| return new Bundle(); |
| } |
| }); |
| when(context.getContentResolver()).thenReturn(mockContentResolver); |
| |
| when(context.getSystemService(Context.POWER_SERVICE)).thenReturn( |
| new PowerManager(context, mock(IPowerManager.class), mock(IThermalService.class), |
| new Handler())); |
| |
| mAlarmManager = new TestAlarmManager(); |
| when(context.getSystemService(Context.ALARM_SERVICE)).thenReturn( |
| mAlarmManager.getAlarmManager()); |
| |
| when(context.getSystemService(Context.CONNECTIVITY_SERVICE)).thenReturn( |
| mConnectivityManager); |
| |
| when(context.getOpPackageName()).thenReturn(OP_PACKAGE_NAME); |
| |
| when(context.getSystemService(ActivityManager.class)).thenReturn( |
| mock(ActivityManager.class)); |
| |
| WifiP2pManager p2pm = mock(WifiP2pManager.class); |
| when(context.getSystemService(WifiP2pManager.class)).thenReturn(p2pm); |
| final CountDownLatch untilDone = new CountDownLatch(1); |
| mP2pThread = new HandlerThread("WifiP2pMockThread") { |
| @Override |
| protected void onLooperPrepared() { |
| untilDone.countDown(); |
| } |
| }; |
| mP2pThread.start(); |
| untilDone.await(); |
| Handler handler = new Handler(mP2pThread.getLooper()); |
| when(p2pm.getP2pStateMachineMessenger()).thenReturn(new Messenger(handler)); |
| |
| return context; |
| } |
| |
| private MockResources getMockResources() { |
| MockResources resources = new MockResources(); |
| return resources; |
| } |
| |
| private IState getCurrentState() throws |
| NoSuchMethodException, InvocationTargetException, IllegalAccessException { |
| Method method = StateMachine.class.getDeclaredMethod("getCurrentState"); |
| method.setAccessible(true); |
| return (IState) method.invoke(mCmi); |
| } |
| |
| private static HandlerThread getCmiHandlerThread(ClientModeImpl cmi) throws |
| NoSuchFieldException, InvocationTargetException, IllegalAccessException { |
| Field field = StateMachine.class.getDeclaredField("mSmThread"); |
| field.setAccessible(true); |
| return (HandlerThread) field.get(cmi); |
| } |
| |
| private static void stopLooper(final Looper looper) throws Exception { |
| new Handler(looper).post(new Runnable() { |
| @Override |
| public void run() { |
| looper.quitSafely(); |
| } |
| }); |
| } |
| |
| private void dumpState() { |
| ByteArrayOutputStream stream = new ByteArrayOutputStream(); |
| PrintWriter writer = new PrintWriter(stream); |
| mCmi.dump(null, writer, null); |
| writer.flush(); |
| Log.d(TAG, "ClientModeImpl state -" + stream.toString()); |
| } |
| |
| private static ScanDetail getGoogleGuestScanDetail(int rssi, String bssid, int freq) { |
| ScanResult.InformationElement[] ie = new ScanResult.InformationElement[1]; |
| ie[0] = ScanResults.generateSsidIe(sSSID); |
| NetworkDetail nd = new NetworkDetail(sBSSID, ie, new ArrayList<String>(), sFreq); |
| ScanDetail detail = new ScanDetail(nd, sWifiSsid, bssid, "", rssi, freq, |
| Long.MAX_VALUE, /* needed so that scan results aren't rejected because |
| there older than scan start */ |
| ie, new ArrayList<String>(), ScanResults.generateIERawDatafromScanResultIE(ie)); |
| |
| return detail; |
| } |
| |
| private ArrayList<ScanDetail> getMockScanResults() { |
| ScanResults sr = ScanResults.create(0, 2412, 2437, 2462, 5180, 5220, 5745, 5825); |
| ArrayList<ScanDetail> list = sr.getScanDetailArrayList(); |
| |
| list.add(getGoogleGuestScanDetail(TEST_RSSI, sBSSID, sFreq)); |
| return list; |
| } |
| |
| private void injectDhcpSuccess(DhcpResultsParcelable dhcpResults) { |
| mIpClientCallback.onNewDhcpResults(dhcpResults); |
| mIpClientCallback.onProvisioningSuccess(new LinkProperties()); |
| } |
| |
| private void injectDhcpFailure() { |
| mIpClientCallback.onNewDhcpResults((DhcpResultsParcelable) null); |
| mIpClientCallback.onProvisioningFailure(new LinkProperties()); |
| } |
| |
| static final String sSSID = "\"GoogleGuest\""; |
| static final String SSID_NO_QUOTE = sSSID.replace("\"", ""); |
| static final WifiSsid sWifiSsid = WifiSsid.createFromAsciiEncoded(SSID_NO_QUOTE); |
| static final String sBSSID = "01:02:03:04:05:06"; |
| static final String sBSSID1 = "02:01:04:03:06:05"; |
| static final int sFreq = 2437; |
| static final int sFreq1 = 5240; |
| static final String WIFI_IFACE_NAME = "mockWlan"; |
| static final String sFilsSsid = "FILS-AP"; |
| |
| ClientModeImpl mCmi; |
| HandlerThread mWifiCoreThread; |
| HandlerThread mP2pThread; |
| HandlerThread mSyncThread; |
| AsyncChannel mCmiAsyncChannel; |
| AsyncChannel mNetworkAgentAsyncChannel; |
| TestAlarmManager mAlarmManager; |
| MockWifiMonitor mWifiMonitor; |
| TestLooper mLooper; |
| Context mContext; |
| MockResources mResources; |
| FrameworkFacade mFrameworkFacade; |
| IpClientCallbacks mIpClientCallback; |
| OsuProvider mOsuProvider; |
| WifiConfiguration mConnectedNetwork; |
| WifiCarrierInfoManager mWifiCarrierInfoManager; |
| |
| @Mock WifiScanner mWifiScanner; |
| @Mock SupplicantStateTracker mSupplicantStateTracker; |
| @Mock WifiMetrics mWifiMetrics; |
| @Mock UserManager mUserManager; |
| @Mock BackupManagerProxy mBackupManagerProxy; |
| @Mock WifiCountryCode mCountryCode; |
| @Mock WifiInjector mWifiInjector; |
| @Mock WifiLastResortWatchdog mWifiLastResortWatchdog; |
| @Mock BssidBlocklistMonitor mBssidBlocklistMonitor; |
| @Mock PropertyService mPropertyService; |
| @Mock BuildProperties mBuildProperties; |
| @Mock IBinder mPackageManagerBinder; |
| @Mock SarManager mSarManager; |
| @Mock WifiConfigManager mWifiConfigManager; |
| @Mock WifiNative mWifiNative; |
| @Mock WifiScoreCard mWifiScoreCard; |
| @Mock WifiHealthMonitor mWifiHealthMonitor; |
| @Mock WifiTrafficPoller mWifiTrafficPoller; |
| @Mock WifiConnectivityManager mWifiConnectivityManager; |
| @Mock WifiStateTracker mWifiStateTracker; |
| @Mock PasspointManager mPasspointManager; |
| @Mock SelfRecovery mSelfRecovery; |
| @Mock WifiPermissionsUtil mWifiPermissionsUtil; |
| @Mock IIpClient mIpClient; |
| @Mock TelephonyManager mTelephonyManager; |
| @Mock TelephonyManager mDataTelephonyManager; |
| @Mock WrongPasswordNotifier mWrongPasswordNotifier; |
| @Mock Clock mClock; |
| @Mock ScanDetailCache mScanDetailCache; |
| @Mock BaseWifiDiagnostics mWifiDiagnostics; |
| @Mock ConnectivityManager mConnectivityManager; |
| @Mock IProvisioningCallback mProvisioningCallback; |
| @Mock HandlerThread mWifiHandlerThread; |
| @Mock WifiPermissionsWrapper mWifiPermissionsWrapper; |
| @Mock WakeupController mWakeupController; |
| @Mock WifiDataStall mWifiDataStall; |
| @Mock WifiNetworkFactory mWifiNetworkFactory; |
| @Mock UntrustedWifiNetworkFactory mUntrustedWifiNetworkFactory; |
| @Mock WifiNetworkSuggestionsManager mWifiNetworkSuggestionsManager; |
| @Mock LinkProbeManager mLinkProbeManager; |
| @Mock PackageManager mPackageManager; |
| @Mock WifiLockManager mWifiLockManager; |
| @Mock AsyncChannel mNullAsyncChannel; |
| @Mock Handler mNetworkAgentHandler; |
| @Mock BatteryStatsManager mBatteryStatsManager; |
| @Mock MboOceController mMboOceController; |
| @Mock SubscriptionManager mSubscriptionManager; |
| @Mock ConnectionFailureNotifier mConnectionFailureNotifier; |
| @Mock EapFailureNotifier mEapFailureNotifier; |
| @Mock SimRequiredNotifier mSimRequiredNotifier; |
| @Mock ThroughputPredictor mThroughputPredictor; |
| @Mock ScanRequestProxy mScanRequestProxy; |
| @Mock DeviceConfigFacade mDeviceConfigFacade; |
| @Mock Network mNetwork; |
| |
| final ArgumentCaptor<WifiConfigManager.OnNetworkUpdateListener> mConfigUpdateListenerCaptor = |
| ArgumentCaptor.forClass(WifiConfigManager.OnNetworkUpdateListener.class); |
| |
| public ClientModeImplTest() throws Exception { |
| } |
| |
| @Before |
| public void setUp() throws Exception { |
| Log.d(TAG, "Setting up ..."); |
| |
| // Ensure looper exists |
| mLooper = new TestLooper(); |
| |
| MockitoAnnotations.initMocks(this); |
| |
| /** uncomment this to enable logs from ClientModeImpls */ |
| // enableDebugLogs(); |
| mWifiMonitor = new MockWifiMonitor(); |
| when(mWifiInjector.getWifiMetrics()).thenReturn(mWifiMetrics); |
| when(mWifiInjector.getClock()).thenReturn(new Clock()); |
| when(mWifiInjector.getWifiLastResortWatchdog()).thenReturn(mWifiLastResortWatchdog); |
| when(mWifiInjector.getPropertyService()).thenReturn(mPropertyService); |
| when(mWifiInjector.getBuildProperties()).thenReturn(mBuildProperties); |
| when(mWifiInjector.getWifiBackupRestore()).thenReturn(mock(WifiBackupRestore.class)); |
| when(mWifiInjector.getWifiDiagnostics()).thenReturn(mWifiDiagnostics); |
| when(mWifiInjector.getWifiConfigManager()).thenReturn(mWifiConfigManager); |
| when(mWifiInjector.getWifiScanner()).thenReturn(mWifiScanner); |
| when(mWifiInjector.makeWifiConnectivityManager(any())) |
| .thenReturn(mWifiConnectivityManager); |
| when(mWifiInjector.getPasspointManager()).thenReturn(mPasspointManager); |
| when(mWifiInjector.getWifiStateTracker()).thenReturn(mWifiStateTracker); |
| when(mWifiInjector.getWifiMonitor()).thenReturn(mWifiMonitor); |
| when(mWifiInjector.getWifiNative()).thenReturn(mWifiNative); |
| when(mWifiInjector.getWifiTrafficPoller()).thenReturn(mWifiTrafficPoller); |
| when(mWifiInjector.getSelfRecovery()).thenReturn(mSelfRecovery); |
| when(mWifiInjector.getWifiPermissionsUtil()).thenReturn(mWifiPermissionsUtil); |
| when(mWifiInjector.makeTelephonyManager()).thenReturn(mTelephonyManager); |
| when(mTelephonyManager.createForSubscriptionId(anyInt())).thenReturn(mDataTelephonyManager); |
| when(mWifiInjector.getClock()).thenReturn(mClock); |
| when(mWifiHandlerThread.getLooper()).thenReturn(mLooper.getLooper()); |
| when(mWifiInjector.getWifiHandlerThread()).thenReturn(mWifiHandlerThread); |
| when(mWifiInjector.getWifiPermissionsWrapper()).thenReturn(mWifiPermissionsWrapper); |
| when(mWifiInjector.getWakeupController()).thenReturn(mWakeupController); |
| when(mWifiInjector.getScoringParams()).thenReturn(new ScoringParams()); |
| when(mWifiInjector.getWifiDataStall()).thenReturn(mWifiDataStall); |
| when(mWifiInjector.makeWifiNetworkFactory(any(), any())).thenReturn(mWifiNetworkFactory); |
| when(mWifiInjector.makeUntrustedWifiNetworkFactory(any(), any())) |
| .thenReturn(mUntrustedWifiNetworkFactory); |
| when(mWifiInjector.getWifiNetworkSuggestionsManager()) |
| .thenReturn(mWifiNetworkSuggestionsManager); |
| when(mWifiInjector.getWifiScoreCard()).thenReturn(mWifiScoreCard); |
| when(mWifiInjector.getWifiHealthMonitor()).thenReturn(mWifiHealthMonitor); |
| when(mWifiInjector.getWifiLockManager()).thenReturn(mWifiLockManager); |
| when(mWifiInjector.getWifiThreadRunner()) |
| .thenReturn(new WifiThreadRunner(new Handler(mLooper.getLooper()))); |
| when(mWifiInjector.makeConnectionFailureNotifier(any())) |
| .thenReturn(mConnectionFailureNotifier); |
| when(mWifiInjector.getBssidBlocklistMonitor()).thenReturn(mBssidBlocklistMonitor); |
| when(mWifiInjector.getThroughputPredictor()).thenReturn(mThroughputPredictor); |
| when(mWifiInjector.getScanRequestProxy()).thenReturn(mScanRequestProxy); |
| when(mWifiInjector.getDeviceConfigFacade()).thenReturn(mDeviceConfigFacade); |
| when(mWifiNetworkFactory.getSpecificNetworkRequestUidAndPackageName(any())) |
| .thenReturn(Pair.create(Process.INVALID_UID, "")); |
| when(mWifiNative.initialize()).thenReturn(true); |
| when(mWifiNative.getFactoryMacAddress(WIFI_IFACE_NAME)).thenReturn( |
| TEST_GLOBAL_MAC_ADDRESS); |
| when(mWifiNative.getMacAddress(WIFI_IFACE_NAME)) |
| .thenReturn(TEST_GLOBAL_MAC_ADDRESS.toString()); |
| WifiNative.ConnectionCapabilities cap = new WifiNative.ConnectionCapabilities(); |
| cap.wifiStandard = ScanResult.WIFI_STANDARD_11AC; |
| when(mWifiNative.getConnectionCapabilities(WIFI_IFACE_NAME)).thenReturn(cap); |
| when(mWifiNative.setMacAddress(eq(WIFI_IFACE_NAME), anyObject())) |
| .then(new AnswerWithArguments() { |
| public boolean answer(String iface, MacAddress mac) { |
| when(mWifiNative.getMacAddress(iface)).thenReturn(mac.toString()); |
| return true; |
| } |
| }); |
| doAnswer(new AnswerWithArguments() { |
| public MacAddress answer( |
| WifiConfiguration config) { |
| return config.getRandomizedMacAddress(); |
| } |
| }).when(mWifiConfigManager).getRandomizedMacAndUpdateIfNeeded(any()); |
| when(mWifiNative.connectToNetwork(any(), any())).thenReturn(true); |
| |
| when(mWifiNetworkFactory.hasConnectionRequests()).thenReturn(true); |
| when(mUntrustedWifiNetworkFactory.hasConnectionRequests()).thenReturn(true); |
| |
| mFrameworkFacade = getFrameworkFacade(); |
| mContext = getContext(); |
| |
| mResources = getMockResources(); |
| mResources.setBoolean(R.bool.config_wifi_connected_mac_randomization_supported, true); |
| mResources.setIntArray(R.array.config_wifiRssiLevelThresholds, |
| RssiUtilTest.RSSI_THRESHOLDS); |
| mResources.setInteger(R.integer.config_wifiPollRssiIntervalMilliseconds, 3000); |
| when(mContext.getResources()).thenReturn(mResources); |
| when(mContext.createContextAsUser(any(), anyInt())).thenReturn(mContext); |
| |
| when(mFrameworkFacade.getIntegerSetting(mContext, |
| Settings.Global.WIFI_FREQUENCY_BAND, |
| WifiManager.WIFI_FREQUENCY_BAND_AUTO)).thenReturn( |
| WifiManager.WIFI_FREQUENCY_BAND_AUTO); |
| when(mWifiPermissionsUtil.checkNetworkSettingsPermission(anyInt())).thenReturn(true); |
| when(mWifiPermissionsWrapper.getLocalMacAddressPermission(anyInt())) |
| .thenReturn(PackageManager.PERMISSION_DENIED); |
| doAnswer(inv -> { |
| mIpClientCallback.onQuit(); |
| return null; |
| }).when(mIpClient).shutdown(); |
| when(mConnectivityManager.registerNetworkAgent(any(), any(), any(), any(), anyInt(), any(), |
| anyInt())).thenReturn(mNetwork); |
| List<SubscriptionInfo> subList = Arrays.asList(mock(SubscriptionInfo.class)); |
| when(mSubscriptionManager.getActiveSubscriptionInfoList()).thenReturn(subList); |
| when(mSubscriptionManager.getActiveSubscriptionIdList()) |
| .thenReturn(new int[]{DATA_SUBID}); |
| |
| WifiCarrierInfoManager tu = new WifiCarrierInfoManager(mTelephonyManager, |
| mSubscriptionManager, mWifiInjector, mock(FrameworkFacade.class), |
| mock(WifiContext.class), mock(WifiConfigStore.class), mock(Handler.class), |
| mWifiMetrics); |
| mWifiCarrierInfoManager = spy(tu); |
| // static mocking |
| mSession = ExtendedMockito.mockitoSession().strictness(Strictness.LENIENT) |
| .spyStatic(MacAddress.class) |
| .startMocking(); |
| initializeCmi(); |
| |
| mOsuProvider = PasspointProvisioningTestUtil.generateOsuProvider(true); |
| mConnectedNetwork = spy(WifiConfigurationTestUtil.createOpenNetwork()); |
| when(mNullAsyncChannel.sendMessageSynchronously(any())).thenReturn(null); |
| when(mWifiScoreCard.getL2KeyAndGroupHint(any())).thenReturn(new Pair<>(null, null)); |
| when(mDeviceConfigFacade.isAbnormalDisconnectionBugreportEnabled()).thenReturn(true); |
| when(mDeviceConfigFacade.isAbnormalConnectionFailureBugreportEnabled()).thenReturn(true); |
| when(mDeviceConfigFacade.isOverlappingConnectionBugreportEnabled()).thenReturn(true); |
| when(mDeviceConfigFacade.getOverlappingConnectionDurationThresholdMs()).thenReturn( |
| DeviceConfigFacade.DEFAULT_OVERLAPPING_CONNECTION_DURATION_THRESHOLD_MS); |
| when(mWifiScoreCard.detectAbnormalConnectionFailure(anyString())) |
| .thenReturn(WifiHealthMonitor.REASON_NO_FAILURE); |
| when(mWifiScoreCard.detectAbnormalDisconnection()) |
| .thenReturn(WifiHealthMonitor.REASON_NO_FAILURE); |
| when(mThroughputPredictor.predictMaxTxThroughput(any())).thenReturn(90); |
| when(mThroughputPredictor.predictMaxRxThroughput(any())).thenReturn(80); |
| } |
| |
| private void registerAsyncChannel(Consumer<AsyncChannel> consumer, Messenger messenger, |
| Handler wrappedHandler) { |
| final AsyncChannel channel = new AsyncChannel(); |
| Handler handler = new Handler(mLooper.getLooper()) { |
| @Override |
| public void handleMessage(Message msg) { |
| switch (msg.what) { |
| case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: |
| if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) { |
| consumer.accept(channel); |
| } else { |
| Log.d(TAG, "Failed to connect Command channel " + this); |
| } |
| break; |
| case AsyncChannel.CMD_CHANNEL_DISCONNECTED: |
| Log.d(TAG, "Command channel disconnected " + this); |
| break; |
| case AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED: |
| Log.d(TAG, "Command channel fully connected " + this); |
| break; |
| default: |
| if (wrappedHandler != null) { |
| wrappedHandler.handleMessage(msg); |
| } |
| break; |
| } |
| } |
| }; |
| |
| channel.connect(mContext, handler, messenger); |
| mLooper.dispatchAll(); |
| } |
| |
| private void registerAsyncChannel(Consumer<AsyncChannel> consumer, Messenger messenger) { |
| registerAsyncChannel(consumer, messenger, null /* wrappedHandler */); |
| } |
| |
| private void initializeCmi() throws Exception { |
| mCmi = new ClientModeImpl(mContext, mFrameworkFacade, mLooper.getLooper(), |
| mUserManager, mWifiInjector, mBackupManagerProxy, mCountryCode, mWifiNative, |
| mWrongPasswordNotifier, mSarManager, mWifiTrafficPoller, mLinkProbeManager, |
| mBatteryStatsManager, mSupplicantStateTracker, mMboOceController, |
| mWifiCarrierInfoManager, mEapFailureNotifier, mSimRequiredNotifier); |
| mCmi.start(); |
| mWifiCoreThread = getCmiHandlerThread(mCmi); |
| |
| registerAsyncChannel((x) -> { |
| mCmiAsyncChannel = x; |
| }, mCmi.getMessenger()); |
| |
| mBinderToken = Binder.clearCallingIdentity(); |
| |
| /* Send the BOOT_COMPLETED message to setup some CMI state. */ |
| mCmi.handleBootCompleted(); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiNetworkFactory, atLeastOnce()).register(); |
| verify(mUntrustedWifiNetworkFactory, atLeastOnce()).register(); |
| verify(mWifiConfigManager, atLeastOnce()).addOnNetworkUpdateListener( |
| mConfigUpdateListenerCaptor.capture()); |
| assertNotNull(mConfigUpdateListenerCaptor.getValue()); |
| |
| mCmi.initialize(); |
| mLooper.dispatchAll(); |
| } |
| |
| @After |
| public void cleanUp() throws Exception { |
| Binder.restoreCallingIdentity(mBinderToken); |
| |
| if (mSyncThread != null) stopLooper(mSyncThread.getLooper()); |
| if (mWifiCoreThread != null) stopLooper(mWifiCoreThread.getLooper()); |
| if (mP2pThread != null) stopLooper(mP2pThread.getLooper()); |
| |
| mWifiCoreThread = null; |
| mP2pThread = null; |
| mSyncThread = null; |
| mCmiAsyncChannel = null; |
| mNetworkAgentAsyncChannel = null; |
| mNetworkAgentHandler = null; |
| mCmi = null; |
| mSession.finishMocking(); |
| } |
| |
| @Test |
| public void createNew() throws Exception { |
| assertEquals("DefaultState", getCurrentState().getName()); |
| |
| mCmi.handleBootCompleted(); |
| mLooper.dispatchAll(); |
| assertEquals("DefaultState", getCurrentState().getName()); |
| } |
| |
| @Test |
| public void loadComponentsInStaMode() throws Exception { |
| startSupplicantAndDispatchMessages(); |
| assertEquals("DisconnectedState", getCurrentState().getName()); |
| } |
| |
| @Test |
| public void checkInitialStateStickyWhenDisabledMode() throws Exception { |
| mLooper.dispatchAll(); |
| assertEquals("DefaultState", getCurrentState().getName()); |
| assertEquals(ClientModeImpl.DISABLED_MODE, mCmi.getOperationalModeForTest()); |
| |
| mCmi.setOperationalMode(ClientModeImpl.DISABLED_MODE, null); |
| mLooper.dispatchAll(); |
| assertEquals(ClientModeImpl.DISABLED_MODE, mCmi.getOperationalModeForTest()); |
| assertEquals("DefaultState", getCurrentState().getName()); |
| } |
| |
| @Test |
| public void shouldStartSupplicantWhenConnectModeRequested() throws Exception { |
| // The first time we start out in DefaultState, we sit around here. |
| mLooper.dispatchAll(); |
| assertEquals("DefaultState", getCurrentState().getName()); |
| assertEquals(ClientModeImpl.DISABLED_MODE, mCmi.getOperationalModeForTest()); |
| |
| // But if someone tells us to enter connect mode, we start up supplicant |
| mCmi.setOperationalMode(ClientModeImpl.CONNECT_MODE, WIFI_IFACE_NAME); |
| mLooper.dispatchAll(); |
| assertEquals("DisconnectedState", getCurrentState().getName()); |
| } |
| |
| /** |
| * Test that mode changes accurately reflect the value for isWifiEnabled. |
| */ |
| @Test |
| public void checkIsWifiEnabledForModeChanges() throws Exception { |
| // Check initial state |
| mLooper.dispatchAll(); |
| assertEquals("DefaultState", getCurrentState().getName()); |
| assertEquals(WifiManager.WIFI_STATE_DISABLED, mCmi.syncGetWifiState()); |
| |
| // switch to connect mode and verify wifi is reported as enabled |
| startSupplicantAndDispatchMessages(); |
| |
| assertEquals("DisconnectedState", getCurrentState().getName()); |
| assertEquals(ClientModeImpl.CONNECT_MODE, mCmi.getOperationalModeForTest()); |
| assertEquals(WifiManager.WIFI_STATE_ENABLED, mCmi.syncGetWifiState()); |
| assertEquals("enabled", mCmi.syncGetWifiStateByName()); |
| |
| // now disable wifi and verify the reported wifi state |
| mCmi.setWifiStateForApiCalls(WifiManager.WIFI_STATE_DISABLED); |
| mCmi.setOperationalMode(ClientModeImpl.DISABLED_MODE, null); |
| mLooper.dispatchAll(); |
| assertEquals(ClientModeImpl.DISABLED_MODE, mCmi.getOperationalModeForTest()); |
| assertEquals("DefaultState", getCurrentState().getName()); |
| assertEquals(WifiManager.WIFI_STATE_DISABLED, mCmi.syncGetWifiState()); |
| assertEquals("disabled", mCmi.syncGetWifiStateByName()); |
| verify(mContext, never()).sendStickyBroadcastAsUser( |
| (Intent) argThat(new WifiEnablingStateIntentMatcher()), any()); |
| } |
| |
| private class WifiEnablingStateIntentMatcher implements ArgumentMatcher<Intent> { |
| @Override |
| public boolean matches(Intent intent) { |
| if (WifiManager.WIFI_STATE_CHANGED_ACTION != intent.getAction()) { |
| // not the correct type |
| return false; |
| } |
| return WifiManager.WIFI_STATE_ENABLING |
| == intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, |
| WifiManager.WIFI_STATE_DISABLED); |
| } |
| } |
| |
| private void canForgetNetwork() throws Exception { |
| when(mWifiConfigManager.removeNetwork(eq(0), anyInt(), any())).thenReturn(true); |
| IActionListener connectActionListener = mock(IActionListener.class); |
| mCmi.forget(0, mock(Binder.class), connectActionListener, 0, Binder.getCallingUid()); |
| mLooper.dispatchAll(); |
| verify(connectActionListener).onSuccess(); |
| verify(mWifiConfigManager).removeNetwork(anyInt(), anyInt(), any()); |
| } |
| |
| /** |
| * Verifies that configs can be removed when not in client mode. |
| */ |
| @Test |
| public void canForgetNetworkConfigWhenWifiDisabled() throws Exception { |
| canForgetNetwork(); |
| } |
| |
| /** |
| * Verifies that configs can be forgotten when in client mode. |
| */ |
| @Test |
| public void canForgetNetworkConfigInClientMode() throws Exception { |
| initializeAndAddNetworkAndVerifySuccess(); |
| canForgetNetwork(); |
| } |
| |
| private void canSaveNetworkConfig() throws Exception { |
| WifiConfiguration config = WifiConfigurationTestUtil.createOpenNetwork(); |
| |
| int networkId = TEST_NETWORK_ID; |
| when(mWifiConfigManager.addOrUpdateNetwork(any(WifiConfiguration.class), anyInt())) |
| .thenReturn(new NetworkUpdateResult(networkId)); |
| when(mWifiConfigManager.enableNetwork(eq(networkId), eq(false), anyInt(), any())) |
| .thenReturn(true); |
| |
| IActionListener connectActionListener = mock(IActionListener.class); |
| mCmi.save(config, mock(Binder.class), connectActionListener, 0, Binder.getCallingUid()); |
| mLooper.dispatchAll(); |
| verify(connectActionListener).onSuccess(); |
| |
| verify(mWifiConfigManager).addOrUpdateNetwork(any(WifiConfiguration.class), anyInt()); |
| verify(mWifiConfigManager).enableNetwork(eq(networkId), eq(false), anyInt(), any()); |
| } |
| |
| /** |
| * Verifies that configs can be saved when not in client mode. |
| */ |
| @Test |
| public void canSaveNetworkConfigWhenWifiDisabled() throws Exception { |
| canSaveNetworkConfig(); |
| } |
| |
| /** |
| * Verifies that configs can be saved when in client mode. |
| */ |
| @Test |
| public void canSaveNetworkConfigInClientMode() throws Exception { |
| loadComponentsInStaMode(); |
| canSaveNetworkConfig(); |
| } |
| |
| /** |
| * Verifies that null configs are rejected in SAVE_NETWORK message. |
| */ |
| @Test |
| public void saveNetworkConfigFailsWithNullConfig() throws Exception { |
| IActionListener connectActionListener = mock(IActionListener.class); |
| mCmi.save(null, mock(Binder.class), connectActionListener, 0, Binder.getCallingUid()); |
| mLooper.dispatchAll(); |
| verify(connectActionListener).onFailure(WifiManager.ERROR); |
| |
| verify(mWifiConfigManager, never()) |
| .addOrUpdateNetwork(any(WifiConfiguration.class), anyInt()); |
| verify(mWifiConfigManager, never()) |
| .enableNetwork(anyInt(), anyBoolean(), anyInt(), any()); |
| } |
| |
| /** |
| * Verifies that configs save fails when the addition of network fails. |
| */ |
| @Test |
| public void saveNetworkConfigFailsWithConfigAddFailure() throws Exception { |
| WifiConfiguration config = WifiConfigurationTestUtil.createOpenNetwork(); |
| |
| when(mWifiConfigManager.addOrUpdateNetwork(any(WifiConfiguration.class), anyInt())) |
| .thenReturn(new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID)); |
| |
| IActionListener connectActionListener = mock(IActionListener.class); |
| mCmi.save(config, mock(Binder.class), connectActionListener, 0, Binder.getCallingUid()); |
| mLooper.dispatchAll(); |
| verify(connectActionListener).onFailure(WifiManager.ERROR); |
| |
| verify(mWifiConfigManager).addOrUpdateNetwork(any(WifiConfiguration.class), anyInt()); |
| verify(mWifiConfigManager, never()) |
| .enableNetwork(anyInt(), anyBoolean(), anyInt(), any()); |
| } |
| |
| /** |
| * Verifies that configs save fails when the enable of network fails. |
| */ |
| @Test |
| public void saveNetworkConfigFailsWithConfigEnableFailure() throws Exception { |
| WifiConfiguration config = WifiConfigurationTestUtil.createOpenNetwork(); |
| |
| int networkId = 5; |
| when(mWifiConfigManager.addOrUpdateNetwork(any(WifiConfiguration.class), anyInt())) |
| .thenReturn(new NetworkUpdateResult(networkId)); |
| when(mWifiConfigManager.enableNetwork(eq(networkId), eq(false), anyInt(), any())) |
| .thenReturn(false); |
| |
| IActionListener connectActionListener = mock(IActionListener.class); |
| mCmi.save(config, mock(Binder.class), connectActionListener, 0, Binder.getCallingUid()); |
| mLooper.dispatchAll(); |
| verify(connectActionListener).onFailure(WifiManager.ERROR); |
| |
| verify(mWifiConfigManager).addOrUpdateNetwork(any(WifiConfiguration.class), anyInt()); |
| verify(mWifiConfigManager).enableNetwork(eq(networkId), eq(false), anyInt(), any()); |
| } |
| |
| /** |
| * Helper method to move through startup states. |
| */ |
| private void startSupplicantAndDispatchMessages() throws Exception { |
| mCmi.setWifiStateForApiCalls(WifiManager.WIFI_STATE_ENABLED); |
| mCmi.setOperationalMode(ClientModeImpl.CONNECT_MODE, WIFI_IFACE_NAME); |
| |
| mLooper.dispatchAll(); |
| |
| verify(mWifiLastResortWatchdog, atLeastOnce()).clearAllFailureCounts(); |
| |
| assertEquals("DisconnectedState", getCurrentState().getName()); |
| } |
| |
| private void initializeMocksForAddedNetwork(boolean isHidden) throws Exception { |
| WifiConfiguration config = new WifiConfiguration(); |
| config.networkId = FRAMEWORK_NETWORK_ID; |
| config.SSID = sSSID; |
| config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); |
| config.hiddenSSID = isHidden; |
| |
| when(mWifiConfigManager.getSavedNetworks(anyInt())).thenReturn(Arrays.asList(config)); |
| when(mWifiConfigManager.getConfiguredNetwork(0)).thenReturn(config); |
| when(mWifiConfigManager.getConfiguredNetworkWithoutMasking(0)).thenReturn(config); |
| } |
| |
| private void initializeAndAddNetworkAndVerifySuccess() throws Exception { |
| initializeAndAddNetworkAndVerifySuccess(false); |
| } |
| |
| private void initializeAndAddNetworkAndVerifySuccess(boolean isHidden) throws Exception { |
| loadComponentsInStaMode(); |
| initializeMocksForAddedNetwork(isHidden); |
| } |
| |
| private void setupAndStartConnectSequence(WifiConfiguration config) throws Exception { |
| when(mWifiConfigManager.enableNetwork( |
| eq(config.networkId), eq(true), anyInt(), any())) |
| .thenReturn(true); |
| when(mWifiConfigManager.updateLastConnectUid(eq(config.networkId), anyInt())) |
| .thenReturn(true); |
| when(mWifiConfigManager.getConfiguredNetwork(eq(config.networkId))) |
| .thenReturn(config); |
| when(mWifiConfigManager.getConfiguredNetworkWithoutMasking( |
| eq(config.networkId))).thenReturn(config); |
| |
| verify(mWifiNative).removeAllNetworks(WIFI_IFACE_NAME); |
| |
| IActionListener connectActionListener = mock(IActionListener.class); |
| mCmi.connect(null, config.networkId, mock(Binder.class), connectActionListener, 0, |
| Binder.getCallingUid()); |
| mLooper.dispatchAll(); |
| verify(mWifiConfigManager).userEnabledNetwork(config.networkId); |
| verify(connectActionListener).onSuccess(); |
| } |
| |
| private void validateSuccessfulConnectSequence(WifiConfiguration config) { |
| verify(mWifiConfigManager).enableNetwork( |
| eq(config.networkId), eq(true), anyInt(), any()); |
| verify(mWifiConnectivityManager).setUserConnectChoice(eq(config.networkId)); |
| verify(mWifiConnectivityManager).prepareForForcedConnection(eq(config.networkId)); |
| verify(mWifiConfigManager).getConfiguredNetworkWithoutMasking(eq(config.networkId)); |
| verify(mWifiNative).connectToNetwork(eq(WIFI_IFACE_NAME), eq(config)); |
| } |
| |
| private void validateFailureConnectSequence(WifiConfiguration config) { |
| verify(mWifiConfigManager).enableNetwork( |
| eq(config.networkId), eq(true), anyInt(), any()); |
| verify(mWifiConnectivityManager).setUserConnectChoice(eq(config.networkId)); |
| verify(mWifiConnectivityManager).prepareForForcedConnection(eq(config.networkId)); |
| verify(mWifiConfigManager, never()) |
| .getConfiguredNetworkWithoutMasking(eq(config.networkId)); |
| verify(mWifiNative, never()).connectToNetwork(eq(WIFI_IFACE_NAME), eq(config)); |
| } |
| |
| /** |
| * Tests the network connection initiation sequence with the default network request pending |
| * from WifiNetworkFactory. |
| * This simulates the connect sequence using the public |
| * {@link WifiManager#enableNetwork(int, boolean)} and ensures that we invoke |
| * {@link WifiNative#connectToNetwork(WifiConfiguration)}. |
| */ |
| @Test |
| public void triggerConnect() throws Exception { |
| loadComponentsInStaMode(); |
| WifiConfiguration config = mConnectedNetwork; |
| config.networkId = FRAMEWORK_NETWORK_ID; |
| config.setRandomizedMacAddress(TEST_LOCAL_MAC_ADDRESS); |
| config.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_PERSISTENT; |
| setupAndStartConnectSequence(config); |
| validateSuccessfulConnectSequence(config); |
| } |
| |
| /** |
| * Tests the network connection initiation sequence with the default network request pending |
| * from WifiNetworkFactory. |
| * This simulates the connect sequence using the public |
| * {@link WifiManager#enableNetwork(int, boolean)} and ensures that we invoke |
| * {@link WifiNative#connectToNetwork(WifiConfiguration)}. |
| */ |
| @Test |
| public void triggerConnectFromNonSettingsApp() throws Exception { |
| loadComponentsInStaMode(); |
| WifiConfiguration config = WifiConfigurationTestUtil.createOpenNetwork(); |
| config.networkId = FRAMEWORK_NETWORK_ID; |
| when(mWifiPermissionsUtil.checkNetworkSettingsPermission(Process.myUid())) |
| .thenReturn(false); |
| setupAndStartConnectSequence(config); |
| verify(mWifiConfigManager).enableNetwork( |
| eq(config.networkId), eq(true), anyInt(), any()); |
| verify(mWifiConnectivityManager, never()).setUserConnectChoice(eq(config.networkId)); |
| verify(mWifiConnectivityManager).prepareForForcedConnection(eq(config.networkId)); |
| verify(mWifiConfigManager).getConfiguredNetworkWithoutMasking(eq(config.networkId)); |
| verify(mWifiNative).connectToNetwork(eq(WIFI_IFACE_NAME), eq(config)); |
| } |
| |
| /** |
| * Tests the network connection initiation sequence with no network request pending from |
| * from WifiNetworkFactory. |
| * This simulates the connect sequence using the public |
| * {@link WifiManager#enableNetwork(int, boolean)} and ensures that we don't invoke |
| * {@link WifiNative#connectToNetwork(WifiConfiguration)}. |
| */ |
| @Test |
| public void triggerConnectWithNoNetworkRequest() throws Exception { |
| loadComponentsInStaMode(); |
| // Remove the network requests. |
| when(mWifiNetworkFactory.hasConnectionRequests()).thenReturn(false); |
| when(mUntrustedWifiNetworkFactory.hasConnectionRequests()).thenReturn(false); |
| |
| WifiConfiguration config = WifiConfigurationTestUtil.createOpenNetwork(); |
| config.networkId = FRAMEWORK_NETWORK_ID; |
| setupAndStartConnectSequence(config); |
| validateFailureConnectSequence(config); |
| } |
| |
| /** |
| * Tests the entire successful network connection flow. |
| */ |
| @Test |
| public void connect() throws Exception { |
| triggerConnect(); |
| WifiConfiguration config = mWifiConfigManager.getConfiguredNetwork(FRAMEWORK_NETWORK_ID); |
| config.carrierId = CARRIER_ID_1; |
| when(mWifiConfigManager.getScanDetailCacheForNetwork(FRAMEWORK_NETWORK_ID)) |
| .thenReturn(mScanDetailCache); |
| |
| when(mScanDetailCache.getScanDetail(sBSSID)).thenReturn( |
| getGoogleGuestScanDetail(TEST_RSSI, sBSSID, sFreq)); |
| when(mScanDetailCache.getScanResult(sBSSID)).thenReturn( |
| getGoogleGuestScanDetail(TEST_RSSI, sBSSID, sFreq).getScanResult()); |
| |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(0, sWifiSsid, sBSSID, SupplicantState.ASSOCIATED)); |
| mLooper.dispatchAll(); |
| |
| mCmi.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, 0, 0, sBSSID); |
| mLooper.dispatchAll(); |
| |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(0, sWifiSsid, sBSSID, SupplicantState.COMPLETED)); |
| mLooper.dispatchAll(); |
| |
| assertEquals("ObtainingIpState", getCurrentState().getName()); |
| |
| DhcpResultsParcelable dhcpResults = new DhcpResultsParcelable(); |
| dhcpResults.baseConfiguration = new StaticIpConfiguration(); |
| dhcpResults.baseConfiguration.gateway = InetAddresses.parseNumericAddress("1.2.3.4"); |
| dhcpResults.baseConfiguration.ipAddress = |
| new LinkAddress(InetAddresses.parseNumericAddress("192.168.1.100"), 0); |
| dhcpResults.baseConfiguration.dnsServers.add(InetAddresses.parseNumericAddress("8.8.8.8")); |
| dhcpResults.leaseDuration = 3600; |
| |
| injectDhcpSuccess(dhcpResults); |
| mLooper.dispatchAll(); |
| |
| // Verify WifiMetrics logging for metered metrics based on DHCP results |
| verify(mWifiMetrics).addMeteredStat(any(), anyBoolean()); |
| WifiInfo wifiInfo = mCmi.getWifiInfo(); |
| assertNotNull(wifiInfo); |
| assertEquals(sBSSID, wifiInfo.getBSSID()); |
| assertEquals(sFreq, wifiInfo.getFrequency()); |
| assertTrue(sWifiSsid.equals(wifiInfo.getWifiSsid())); |
| assertNull(wifiInfo.getPasspointProviderFriendlyName()); |
| // Ensure the connection stats for the network is updated. |
| verify(mWifiConfigManager).updateNetworkAfterConnect(FRAMEWORK_NETWORK_ID); |
| verify(mWifiConfigManager).updateRandomizedMacExpireTime(any(), anyLong()); |
| |
| // Anonymous Identity is not set. |
| assertEquals("", mConnectedNetwork.enterpriseConfig.getAnonymousIdentity()); |
| verify(mWifiStateTracker).updateState(eq(WifiStateTracker.CONNECTED)); |
| assertEquals("ConnectedState", getCurrentState().getName()); |
| verify(mWifiMetrics).incrementNumOfCarrierWifiConnectionSuccess(); |
| verify(mWifiLockManager).updateWifiClientConnected(true); |
| verify(mWifiNative).getConnectionCapabilities(any()); |
| verify(mThroughputPredictor).predictMaxTxThroughput(any()); |
| verify(mWifiMetrics).setConnectionMaxSupportedLinkSpeedMbps(90, 80); |
| verify(mWifiDataStall).setConnectionCapabilities(any()); |
| assertEquals(90, wifiInfo.getMaxSupportedTxLinkSpeedMbps()); |
| } |
| |
| private void setupEapSimConnection() throws Exception { |
| mConnectedNetwork = spy(WifiConfigurationTestUtil.createEapNetwork( |
| WifiEnterpriseConfig.Eap.SIM, WifiEnterpriseConfig.Phase2.NONE)); |
| mConnectedNetwork.carrierId = CARRIER_ID_1; |
| doReturn(DATA_SUBID).when(mWifiCarrierInfoManager) |
| .getBestMatchSubscriptionId(any(WifiConfiguration.class)); |
| when(mDataTelephonyManager.getSimOperator()).thenReturn("123456"); |
| when(mDataTelephonyManager.getSimState()).thenReturn(TelephonyManager.SIM_STATE_READY); |
| mConnectedNetwork.enterpriseConfig.setAnonymousIdentity(""); |
| |
| triggerConnect(); |
| |
| when(mWifiConfigManager.getScanDetailCacheForNetwork(FRAMEWORK_NETWORK_ID)) |
| .thenReturn(mScanDetailCache); |
| when(mScanDetailCache.getScanDetail(sBSSID)).thenReturn( |
| getGoogleGuestScanDetail(TEST_RSSI, sBSSID, sFreq)); |
| when(mScanDetailCache.getScanResult(sBSSID)).thenReturn( |
| getGoogleGuestScanDetail(TEST_RSSI, sBSSID, sFreq).getScanResult()); |
| |
| mCmi.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, 0, 0, sBSSID); |
| mLooper.dispatchAll(); |
| assertEquals("ObtainingIpState", getCurrentState().getName()); |
| } |
| |
| /** |
| * When the SIM card was removed, if the current wifi connection is not using it, the connection |
| * should be kept. |
| */ |
| @Test |
| public void testResetSimWhenNonConnectedSimRemoved() throws Exception { |
| setupEapSimConnection(); |
| doReturn(true).when(mWifiCarrierInfoManager).isSimPresent(eq(DATA_SUBID)); |
| mCmi.sendMessage(ClientModeImpl.CMD_RESET_SIM_NETWORKS, |
| ClientModeImpl.RESET_SIM_REASON_SIM_REMOVED); |
| mLooper.dispatchAll(); |
| |
| verify(mSimRequiredNotifier, never()).showSimRequiredNotification(any(), any()); |
| |
| assertEquals("ObtainingIpState", getCurrentState().getName()); |
| } |
| |
| /** |
| * When the SIM card was removed, if the current wifi connection is using it, the connection |
| * should be disconnected, otherwise, the connection shouldn't be impacted. |
| */ |
| @Test |
| public void testResetSimWhenConnectedSimRemoved() throws Exception { |
| setupEapSimConnection(); |
| doReturn(false).when(mWifiCarrierInfoManager).isSimPresent(eq(DATA_SUBID)); |
| mCmi.sendMessage(ClientModeImpl.CMD_RESET_SIM_NETWORKS, |
| ClientModeImpl.RESET_SIM_REASON_SIM_REMOVED); |
| mLooper.dispatchAll(); |
| |
| verify(mSimRequiredNotifier).showSimRequiredNotification(any(), any()); |
| assertEquals("DisconnectingState", getCurrentState().getName()); |
| } |
| |
| /** |
| * When the default data SIM is changed, if the current wifi connection is carrier wifi, |
| * the connection should be disconnected. |
| */ |
| @Test |
| public void testResetSimWhenDefaultDataSimChanged() throws Exception { |
| setupEapSimConnection(); |
| mCmi.sendMessage(ClientModeImpl.CMD_RESET_SIM_NETWORKS, |
| ClientModeImpl.RESET_SIM_REASON_DEFAULT_DATA_SIM_CHANGED); |
| mLooper.dispatchAll(); |
| |
| assertEquals("DisconnectingState", getCurrentState().getName()); |
| } |
| |
| /** |
| * Tests anonymous identity is set again whenever a connection is established for the carrier |
| * that supports encrypted IMSI and anonymous identity and no real pseudonym was provided. |
| */ |
| @Test |
| public void testSetAnonymousIdentityWhenConnectionIsEstablishedNoPseudonym() throws Exception { |
| mConnectedNetwork = spy(WifiConfigurationTestUtil.createEapNetwork( |
| WifiEnterpriseConfig.Eap.SIM, WifiEnterpriseConfig.Phase2.NONE)); |
| when(mDataTelephonyManager.getSimOperator()).thenReturn("123456"); |
| when(mDataTelephonyManager.getSimState()).thenReturn(TelephonyManager.SIM_STATE_READY); |
| mConnectedNetwork.enterpriseConfig.setAnonymousIdentity(""); |
| |
| String expectedAnonymousIdentity = "anonymous@wlan.mnc456.mcc123.3gppnetwork.org"; |
| MockitoSession mockSession = ExtendedMockito.mockitoSession() |
| .mockStatic(SubscriptionManager.class) |
| .startMocking(); |
| when(SubscriptionManager.getDefaultDataSubscriptionId()).thenReturn(DATA_SUBID); |
| doReturn(true).when(mWifiCarrierInfoManager).isImsiEncryptionInfoAvailable(anyInt()); |
| |
| // Initial value should be "not set" |
| assertEquals("", mConnectedNetwork.enterpriseConfig.getAnonymousIdentity()); |
| |
| triggerConnect(); |
| |
| // CMD_START_CONNECT should have set anonymousIdentity to anonymous@<realm> |
| assertEquals(expectedAnonymousIdentity, |
| mConnectedNetwork.enterpriseConfig.getAnonymousIdentity()); |
| |
| when(mWifiConfigManager.getScanDetailCacheForNetwork(FRAMEWORK_NETWORK_ID)) |
| .thenReturn(mScanDetailCache); |
| when(mScanDetailCache.getScanDetail(sBSSID)).thenReturn( |
| getGoogleGuestScanDetail(TEST_RSSI, sBSSID, sFreq)); |
| when(mScanDetailCache.getScanResult(sBSSID)).thenReturn( |
| getGoogleGuestScanDetail(TEST_RSSI, sBSSID, sFreq).getScanResult()); |
| when(mWifiNative.getEapAnonymousIdentity(anyString())) |
| .thenReturn(expectedAnonymousIdentity); |
| |
| mCmi.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, 0, 0, sBSSID); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiNative).getEapAnonymousIdentity(any()); |
| |
| // Post connection value should remain "not set" |
| assertEquals("", mConnectedNetwork.enterpriseConfig.getAnonymousIdentity()); |
| // verify that WifiConfigManager#addOrUpdateNetwork() was called to clear any previously |
| // stored pseudonym. i.e. to enable Encrypted IMSI for subsequent connections. |
| // Note: This test will fail if future logic will have additional conditions that would |
| // trigger "add or update network" operation. The test needs to be updated to account for |
| // this change. |
| verify(mWifiConfigManager).addOrUpdateNetwork(any(), anyInt()); |
| mockSession.finishMocking(); |
| } |
| |
| /** |
| * Tests anonymous identity is set again whenever a connection is established for the carrier |
| * that supports encrypted IMSI and anonymous identity but real pseudonym was provided for |
| * subsequent connections. |
| */ |
| @Test |
| public void testSetAnonymousIdentityWhenConnectionIsEstablishedWithPseudonym() |
| throws Exception { |
| mConnectedNetwork = spy(WifiConfigurationTestUtil.createEapNetwork( |
| WifiEnterpriseConfig.Eap.SIM, WifiEnterpriseConfig.Phase2.NONE)); |
| when(mDataTelephonyManager.getSimOperator()).thenReturn("123456"); |
| when(mDataTelephonyManager.getSimState()).thenReturn(TelephonyManager.SIM_STATE_READY); |
| mConnectedNetwork.enterpriseConfig.setAnonymousIdentity(""); |
| |
| String expectedAnonymousIdentity = "anonymous@wlan.mnc456.mcc123.3gppnetwork.org"; |
| String pseudonym = "83bcca9384fca@wlan.mnc456.mcc123.3gppnetwork.org"; |
| MockitoSession mockSession = ExtendedMockito.mockitoSession() |
| .mockStatic(SubscriptionManager.class) |
| .startMocking(); |
| when(SubscriptionManager.getDefaultDataSubscriptionId()).thenReturn(DATA_SUBID); |
| doReturn(true).when(mWifiCarrierInfoManager).isImsiEncryptionInfoAvailable(anyInt()); |
| |
| triggerConnect(); |
| |
| // CMD_START_CONNECT should have set anonymousIdentity to anonymous@<realm> |
| assertEquals(expectedAnonymousIdentity, |
| mConnectedNetwork.enterpriseConfig.getAnonymousIdentity()); |
| |
| when(mWifiConfigManager.getScanDetailCacheForNetwork(FRAMEWORK_NETWORK_ID)) |
| .thenReturn(mScanDetailCache); |
| when(mScanDetailCache.getScanDetail(sBSSID)).thenReturn( |
| getGoogleGuestScanDetail(TEST_RSSI, sBSSID, sFreq)); |
| when(mScanDetailCache.getScanResult(sBSSID)).thenReturn( |
| getGoogleGuestScanDetail(TEST_RSSI, sBSSID, sFreq).getScanResult()); |
| when(mWifiNative.getEapAnonymousIdentity(anyString())) |
| .thenReturn(pseudonym); |
| |
| mCmi.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, 0, 0, sBSSID); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiNative).getEapAnonymousIdentity(any()); |
| assertEquals(pseudonym, |
| mConnectedNetwork.enterpriseConfig.getAnonymousIdentity()); |
| // Verify that WifiConfigManager#addOrUpdateNetwork() was called if there we received a |
| // real pseudonym to be stored. i.e. Encrypted IMSI will be used once, followed by |
| // pseudonym usage in all subsequent connections. |
| // Note: This test will fail if future logic will have additional conditions that would |
| // trigger "add or update network" operation. The test needs to be updated to account for |
| // this change. |
| verify(mWifiConfigManager).addOrUpdateNetwork(any(), anyInt()); |
| mockSession.finishMocking(); |
| } |
| |
| /** |
| * Tests anonymous identity is set again whenever a connection is established for the carrier |
| * that supports encrypted IMSI and anonymous identity but real but not decorated pseudonym was |
| * provided for subsequent connections. |
| */ |
| @Test |
| public void testSetAnonymousIdentityWhenConnectionIsEstablishedWithNonDecoratedPseudonym() |
| throws Exception { |
| mConnectedNetwork = spy(WifiConfigurationTestUtil.createEapNetwork( |
| WifiEnterpriseConfig.Eap.SIM, WifiEnterpriseConfig.Phase2.NONE)); |
| when(mDataTelephonyManager.getSimOperator()).thenReturn("123456"); |
| when(mDataTelephonyManager.getSimState()).thenReturn(TelephonyManager.SIM_STATE_READY); |
| mConnectedNetwork.enterpriseConfig.setAnonymousIdentity(""); |
| |
| String realm = "wlan.mnc456.mcc123.3gppnetwork.org"; |
| String expectedAnonymousIdentity = "anonymous"; |
| String pseudonym = "83bcca9384fca"; |
| MockitoSession mockSession = ExtendedMockito.mockitoSession() |
| .mockStatic(SubscriptionManager.class) |
| .startMocking(); |
| when(SubscriptionManager.getDefaultDataSubscriptionId()).thenReturn(DATA_SUBID); |
| doReturn(true).when(mWifiCarrierInfoManager).isImsiEncryptionInfoAvailable(anyInt()); |
| |
| triggerConnect(); |
| |
| // CMD_START_CONNECT should have set anonymousIdentity to anonymous@<realm> |
| assertEquals(expectedAnonymousIdentity + "@" + realm, |
| mConnectedNetwork.enterpriseConfig.getAnonymousIdentity()); |
| |
| when(mWifiConfigManager.getScanDetailCacheForNetwork(FRAMEWORK_NETWORK_ID)) |
| .thenReturn(mScanDetailCache); |
| when(mScanDetailCache.getScanDetail(sBSSID)).thenReturn( |
| getGoogleGuestScanDetail(TEST_RSSI, sBSSID, sFreq)); |
| when(mScanDetailCache.getScanResult(sBSSID)).thenReturn( |
| getGoogleGuestScanDetail(TEST_RSSI, sBSSID, sFreq).getScanResult()); |
| when(mWifiNative.getEapAnonymousIdentity(anyString())) |
| .thenReturn(pseudonym); |
| |
| mCmi.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, 0, 0, sBSSID); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiNative).getEapAnonymousIdentity(any()); |
| assertEquals(pseudonym + "@" + realm, |
| mConnectedNetwork.enterpriseConfig.getAnonymousIdentity()); |
| // Verify that WifiConfigManager#addOrUpdateNetwork() was called if there we received a |
| // real pseudonym to be stored. i.e. Encrypted IMSI will be used once, followed by |
| // pseudonym usage in all subsequent connections. |
| // Note: This test will fail if future logic will have additional conditions that would |
| // trigger "add or update network" operation. The test needs to be updated to account for |
| // this change. |
| verify(mWifiConfigManager).addOrUpdateNetwork(any(), anyInt()); |
| mockSession.finishMocking(); |
| } |
| /** |
| * Tests the Passpoint information is set in WifiInfo for Passpoint AP connection. |
| */ |
| @Test |
| public void connectPasspointAp() throws Exception { |
| loadComponentsInStaMode(); |
| WifiConfiguration config = spy(WifiConfigurationTestUtil.createPasspointNetwork()); |
| config.SSID = sWifiSsid.toString(); |
| config.BSSID = sBSSID; |
| config.networkId = FRAMEWORK_NETWORK_ID; |
| config.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_PERSISTENT; |
| setupAndStartConnectSequence(config); |
| validateSuccessfulConnectSequence(config); |
| |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(FRAMEWORK_NETWORK_ID, sWifiSsid, sBSSID, |
| SupplicantState.ASSOCIATING)); |
| mLooper.dispatchAll(); |
| |
| WifiInfo wifiInfo = mCmi.getWifiInfo(); |
| assertNotNull(wifiInfo); |
| assertEquals(WifiConfigurationTestUtil.TEST_FQDN, wifiInfo.getPasspointFqdn()); |
| assertEquals(WifiConfigurationTestUtil.TEST_PROVIDER_FRIENDLY_NAME, |
| wifiInfo.getPasspointProviderFriendlyName()); |
| } |
| |
| /** |
| * Tests that Passpoint fields in WifiInfo are reset when connecting to a non-Passpoint network |
| * during DisconnectedState. |
| * @throws Exception |
| */ |
| @Test |
| public void testResetWifiInfoPasspointFields() throws Exception { |
| loadComponentsInStaMode(); |
| WifiConfiguration config = spy(WifiConfigurationTestUtil.createPasspointNetwork()); |
| config.SSID = sWifiSsid.toString(); |
| config.BSSID = sBSSID; |
| config.networkId = PASSPOINT_NETWORK_ID; |
| config.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_PERSISTENT; |
| setupAndStartConnectSequence(config); |
| validateSuccessfulConnectSequence(config); |
| |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(PASSPOINT_NETWORK_ID, sWifiSsid, sBSSID, |
| SupplicantState.ASSOCIATING)); |
| mLooper.dispatchAll(); |
| |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(FRAMEWORK_NETWORK_ID, sWifiSsid, sBSSID, |
| SupplicantState.ASSOCIATING)); |
| mLooper.dispatchAll(); |
| |
| WifiInfo wifiInfo = mCmi.getWifiInfo(); |
| assertNotNull(wifiInfo); |
| assertNull(wifiInfo.getPasspointFqdn()); |
| assertNull(wifiInfo.getPasspointProviderFriendlyName()); |
| } |
| |
| /** |
| * Tests the OSU information is set in WifiInfo for OSU AP connection. |
| */ |
| @Test |
| public void connectOsuAp() throws Exception { |
| loadComponentsInStaMode(); |
| WifiConfiguration osuConfig = spy(WifiConfigurationTestUtil.createEphemeralNetwork()); |
| osuConfig.SSID = sWifiSsid.toString(); |
| osuConfig.BSSID = sBSSID; |
| osuConfig.osu = true; |
| osuConfig.networkId = FRAMEWORK_NETWORK_ID; |
| osuConfig.providerFriendlyName = WifiConfigurationTestUtil.TEST_PROVIDER_FRIENDLY_NAME; |
| osuConfig.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_PERSISTENT; |
| setupAndStartConnectSequence(osuConfig); |
| validateSuccessfulConnectSequence(osuConfig); |
| |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(FRAMEWORK_NETWORK_ID, sWifiSsid, sBSSID, |
| SupplicantState.ASSOCIATING)); |
| mLooper.dispatchAll(); |
| |
| WifiInfo wifiInfo = mCmi.getWifiInfo(); |
| assertNotNull(wifiInfo); |
| assertTrue(wifiInfo.isOsuAp()); |
| assertEquals(WifiConfigurationTestUtil.TEST_PROVIDER_FRIENDLY_NAME, |
| wifiInfo.getPasspointProviderFriendlyName()); |
| } |
| |
| /** |
| * Tests that OSU fields in WifiInfo are reset when connecting to a non-OSU network during |
| * DisconnectedState. |
| * @throws Exception |
| */ |
| @Test |
| public void testResetWifiInfoOsuFields() throws Exception { |
| loadComponentsInStaMode(); |
| WifiConfiguration osuConfig = spy(WifiConfigurationTestUtil.createEphemeralNetwork()); |
| osuConfig.SSID = sWifiSsid.toString(); |
| osuConfig.BSSID = sBSSID; |
| osuConfig.osu = true; |
| osuConfig.networkId = PASSPOINT_NETWORK_ID; |
| osuConfig.providerFriendlyName = WifiConfigurationTestUtil.TEST_PROVIDER_FRIENDLY_NAME; |
| osuConfig.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_PERSISTENT; |
| setupAndStartConnectSequence(osuConfig); |
| validateSuccessfulConnectSequence(osuConfig); |
| |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(PASSPOINT_NETWORK_ID, sWifiSsid, sBSSID, |
| SupplicantState.ASSOCIATING)); |
| mLooper.dispatchAll(); |
| |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(FRAMEWORK_NETWORK_ID, sWifiSsid, sBSSID, |
| SupplicantState.ASSOCIATING)); |
| mLooper.dispatchAll(); |
| |
| WifiInfo wifiInfo = mCmi.getWifiInfo(); |
| assertNotNull(wifiInfo); |
| assertFalse(wifiInfo.isOsuAp()); |
| } |
| |
| /** |
| * Verify that WifiStateTracker is called if wifi is disabled while connected. |
| */ |
| @Test |
| public void verifyWifiStateTrackerUpdatedWhenDisabled() throws Exception { |
| connect(); |
| |
| mCmi.setOperationalMode(ClientModeImpl.DISABLED_MODE, null); |
| mLooper.dispatchAll(); |
| verify(mWifiStateTracker).updateState(eq(WifiStateTracker.DISCONNECTED)); |
| } |
| |
| /** |
| * Tests the network connection initiation sequence with no network request pending from |
| * from WifiNetworkFactory when we're already connected to a different network. |
| * This simulates the connect sequence using the public |
| * {@link WifiManager#enableNetwork(int, boolean)} and ensures that we invoke |
| * {@link WifiNative#connectToNetwork(WifiConfiguration)}. |
| */ |
| @Test |
| public void triggerConnectWithNoNetworkRequestAndAlreadyConnected() throws Exception { |
| // Simulate the first connection. |
| connect(); |
| |
| // Remove the network requests. |
| when(mWifiNetworkFactory.hasConnectionRequests()).thenReturn(false); |
| when(mUntrustedWifiNetworkFactory.hasConnectionRequests()).thenReturn(false); |
| |
| WifiConfiguration config = WifiConfigurationTestUtil.createOpenNetwork(); |
| config.networkId = FRAMEWORK_NETWORK_ID + 1; |
| setupAndStartConnectSequence(config); |
| validateSuccessfulConnectSequence(config); |
| verify(mWifiPermissionsUtil, atLeastOnce()).checkNetworkSettingsPermission(anyInt()); |
| } |
| |
| /** |
| * Tests the network connection initiation sequence from a non-privileged app with no network |
| * request pending from from WifiNetworkFactory when we're already connected to a different |
| * network. |
| * This simulates the connect sequence using the public |
| * {@link WifiManager#enableNetwork(int, boolean)} and ensures that we don't invoke |
| * {@link WifiNative#connectToNetwork(WifiConfiguration)}. |
| */ |
| @Test |
| public void triggerConnectWithNoNetworkRequestAndAlreadyConnectedButNonPrivilegedApp() |
| throws Exception { |
| // Simulate the first connection. |
| connect(); |
| |
| // Remove the network requests. |
| when(mWifiNetworkFactory.hasConnectionRequests()).thenReturn(false); |
| when(mUntrustedWifiNetworkFactory.hasConnectionRequests()).thenReturn(false); |
| |
| when(mWifiPermissionsUtil.checkNetworkSettingsPermission(anyInt())).thenReturn(false); |
| |
| WifiConfiguration config = WifiConfigurationTestUtil.createOpenNetwork(); |
| config.networkId = FRAMEWORK_NETWORK_ID + 1; |
| setupAndStartConnectSequence(config); |
| verify(mWifiConfigManager).enableNetwork( |
| eq(config.networkId), eq(true), anyInt(), any()); |
| verify(mWifiConnectivityManager, never()).setUserConnectChoice(eq(config.networkId)); |
| verify(mWifiConnectivityManager).prepareForForcedConnection(eq(config.networkId)); |
| verify(mWifiConfigManager, never()) |
| .getConfiguredNetworkWithoutMasking(eq(config.networkId)); |
| verify(mWifiNative, never()).connectToNetwork(eq(WIFI_IFACE_NAME), eq(config)); |
| verify(mWifiPermissionsUtil, times(4)).checkNetworkSettingsPermission(anyInt()); |
| } |
| |
| @Test |
| public void enableWithInvalidNetworkId() throws Exception { |
| initializeAndAddNetworkAndVerifySuccess(); |
| when(mWifiConfigManager.getConfiguredNetwork(eq(0))).thenReturn(null); |
| |
| verify(mWifiNative).removeAllNetworks(WIFI_IFACE_NAME); |
| |
| IActionListener connectActionListener = mock(IActionListener.class); |
| mCmi.connect(null, 0, mock(Binder.class), connectActionListener, 0, Binder.getCallingUid()); |
| mLooper.dispatchAll(); |
| verify(connectActionListener).onFailure(anyInt()); |
| |
| verify(mWifiConfigManager, never()).enableNetwork(eq(0), eq(true), anyInt(), any()); |
| verify(mWifiConfigManager, never()).updateLastConnectUid(eq(0), anyInt()); |
| } |
| |
| /** |
| * If caller tries to connect to a network that is already connected, the connection request |
| * should succeed. |
| * |
| * Test: Create and connect to a network, then try to reconnect to the same network. Verify |
| * that connection request returns with CONNECT_NETWORK_SUCCEEDED. |
| */ |
| @Test |
| public void reconnectToConnectedNetworkWithNetworkId() throws Exception { |
| connect(); |
| |
| // try to reconnect |
| IActionListener connectActionListener = mock(IActionListener.class); |
| mCmi.connect(null, FRAMEWORK_NETWORK_ID, mock(Binder.class), connectActionListener, 0, |
| Binder.getCallingUid()); |
| mLooper.dispatchAll(); |
| verify(connectActionListener).onSuccess(); |
| |
| // Verify that we didn't trigger a second connection. |
| verify(mWifiNative, times(1)).connectToNetwork(eq(WIFI_IFACE_NAME), any()); |
| } |
| |
| /** |
| * If caller tries to connect to a network that is already connected, the connection request |
| * should succeed. |
| * |
| * Test: Create and connect to a network, then try to reconnect to the same network. Verify |
| * that connection request returns with CONNECT_NETWORK_SUCCEEDED. |
| */ |
| @Test |
| public void reconnectToConnectedNetworkWithConfig() throws Exception { |
| connect(); |
| |
| // try to reconnect |
| WifiConfiguration config = new WifiConfiguration(); |
| config.networkId = FRAMEWORK_NETWORK_ID; |
| when(mWifiConfigManager.addOrUpdateNetwork(eq(config), anyInt())) |
| .thenReturn(new NetworkUpdateResult(FRAMEWORK_NETWORK_ID)); |
| IActionListener connectActionListener = mock(IActionListener.class); |
| mCmi.connect(config, WifiConfiguration.INVALID_NETWORK_ID, mock(Binder.class), |
| connectActionListener, 0, Binder.getCallingUid()); |
| mLooper.dispatchAll(); |
| verify(connectActionListener).onSuccess(); |
| |
| // Verify that we didn't trigger a second connection. |
| verify(mWifiNative, times(1)).connectToNetwork(eq(WIFI_IFACE_NAME), any()); |
| } |
| |
| /** |
| * If caller tries to connect to a network that is already connecting, the connection request |
| * should succeed. |
| * |
| * Test: Create and trigger connect to a network, then try to reconnect to the same network. |
| * Verify that connection request returns with CONNECT_NETWORK_SUCCEEDED and did not trigger a |
| * new connection. |
| */ |
| @Test |
| public void reconnectToConnectingNetwork() throws Exception { |
| triggerConnect(); |
| |
| // try to reconnect to the same network (before connection is established). |
| IActionListener connectActionListener = mock(IActionListener.class); |
| mCmi.connect(null, FRAMEWORK_NETWORK_ID, mock(Binder.class), connectActionListener, 0, |
| Binder.getCallingUid()); |
| mLooper.dispatchAll(); |
| verify(connectActionListener).onSuccess(); |
| |
| // Verify that we didn't trigger a second connection. |
| verify(mWifiNative, times(1)).connectToNetwork(eq(WIFI_IFACE_NAME), any()); |
| } |
| |
| /** |
| * If caller tries to connect to a network that is already connecting, the connection request |
| * should succeed. |
| * |
| * Test: Create and trigger connect to a network, then try to reconnect to the same network. |
| * Verify that connection request returns with CONNECT_NETWORK_SUCCEEDED and did trigger a new |
| * connection. |
| */ |
| @Test |
| public void reconnectToConnectingNetworkWithCredentialChange() throws Exception { |
| triggerConnect(); |
| |
| // try to reconnect to the same network with a credential changed (before connection is |
| // established). |
| WifiConfiguration config = new WifiConfiguration(); |
| config.networkId = FRAMEWORK_NETWORK_ID; |
| NetworkUpdateResult networkUpdateResult = |
| new NetworkUpdateResult(false /* ip */, false /* proxy */, true /* credential */); |
| networkUpdateResult.setNetworkId(FRAMEWORK_NETWORK_ID); |
| when(mWifiConfigManager.addOrUpdateNetwork(eq(config), anyInt())) |
| .thenReturn(networkUpdateResult); |
| IActionListener connectActionListener = mock(IActionListener.class); |
| mCmi.connect(config, WifiConfiguration.INVALID_NETWORK_ID, mock(Binder.class), |
| connectActionListener, 0, Binder.getCallingUid()); |
| mLooper.dispatchAll(); |
| verify(connectActionListener).onSuccess(); |
| |
| // Verify that we triggered a second connection. |
| verify(mWifiNative, times(2)).connectToNetwork(eq(WIFI_IFACE_NAME), any()); |
| } |
| |
| /** |
| * If caller tries to connect to a network that previously failed connection, the connection |
| * request should succeed. |
| * |
| * Test: Create and trigger connect to a network, then fail the connection. Now try to reconnect |
| * to the same network. Verify that connection request returns with CONNECT_NETWORK_SUCCEEDED |
| * and did trigger a new * connection. |
| */ |
| @Test |
| public void connectAfterAssociationRejection() throws Exception { |
| triggerConnect(); |
| |
| // fail the connection. |
| mCmi.sendMessage(WifiMonitor.ASSOCIATION_REJECTION_EVENT, 0, |
| ISupplicantStaIfaceCallback.StatusCode.AP_UNABLE_TO_HANDLE_NEW_STA, sBSSID); |
| mLooper.dispatchAll(); |
| |
| IActionListener connectActionListener = mock(IActionListener.class); |
| mCmi.connect(null, FRAMEWORK_NETWORK_ID, mock(Binder.class), connectActionListener, 0, |
| Binder.getCallingUid()); |
| mLooper.dispatchAll(); |
| verify(connectActionListener).onSuccess(); |
| |
| // Verify that we triggered a second connection. |
| verify(mWifiNative, times(2)).connectToNetwork(eq(WIFI_IFACE_NAME), any()); |
| } |
| |
| /** |
| * If caller tries to connect to a network that previously failed connection, the connection |
| * request should succeed. |
| * |
| * Test: Create and trigger connect to a network, then fail the connection. Now try to reconnect |
| * to the same network. Verify that connection request returns with CONNECT_NETWORK_SUCCEEDED |
| * and did trigger a new * connection. |
| */ |
| @Test |
| public void connectAfterConnectionFailure() throws Exception { |
| triggerConnect(); |
| |
| // fail the connection. |
| mCmi.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, FRAMEWORK_NETWORK_ID, 0, sBSSID); |
| mLooper.dispatchAll(); |
| |
| IActionListener connectActionListener = mock(IActionListener.class); |
| mCmi.connect(null, FRAMEWORK_NETWORK_ID, mock(Binder.class), connectActionListener, 0, |
| Binder.getCallingUid()); |
| mLooper.dispatchAll(); |
| verify(connectActionListener).onSuccess(); |
| |
| // Verify that we triggered a second connection. |
| verify(mWifiNative, times(2)).connectToNetwork(eq(WIFI_IFACE_NAME), any()); |
| } |
| |
| /** |
| * If caller tries to connect to a new network while still provisioning the current one, |
| * the connection attempt should succeed. |
| */ |
| @Test |
| public void connectWhileObtainingIp() throws Exception { |
| initializeAndAddNetworkAndVerifySuccess(); |
| |
| verify(mWifiNative).removeAllNetworks(WIFI_IFACE_NAME); |
| |
| IActionListener connectActionListener = mock(IActionListener.class); |
| mCmi.connect(null, 0, mock(Binder.class), connectActionListener, 0, Binder.getCallingUid()); |
| mLooper.dispatchAll(); |
| verify(connectActionListener).onSuccess(); |
| |
| verify(mWifiConfigManager).enableNetwork(eq(0), eq(true), anyInt(), any()); |
| |
| mCmi.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, 0, 0, sBSSID); |
| mLooper.dispatchAll(); |
| |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(0, sWifiSsid, sBSSID, SupplicantState.COMPLETED)); |
| mLooper.dispatchAll(); |
| |
| assertEquals("ObtainingIpState", getCurrentState().getName()); |
| reset(mWifiNative); |
| |
| // Connect to a different network |
| WifiConfiguration config = new WifiConfiguration(); |
| config.networkId = TEST_NETWORK_ID; |
| when(mWifiConfigManager.getConfiguredNetwork(TEST_NETWORK_ID)).thenReturn(config); |
| |
| mCmi.connect(null, TEST_NETWORK_ID, mock(Binder.class), null, 0, Binder.getCallingUid()); |
| mLooper.dispatchAll(); |
| |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(0, sWifiSsid, sBSSID, SupplicantState.DISCONNECTED)); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiConnectivityManager).prepareForForcedConnection(eq(config.networkId)); |
| |
| } |
| |
| /** |
| * Tests that manual connection to a network (from settings app) logs the correct nominator ID. |
| */ |
| @Test |
| public void testManualConnectNominator() throws Exception { |
| initializeAndAddNetworkAndVerifySuccess(); |
| |
| WifiConfiguration config = new WifiConfiguration(); |
| config.networkId = TEST_NETWORK_ID; |
| when(mWifiConfigManager.getConfiguredNetwork(TEST_NETWORK_ID)).thenReturn(config); |
| |
| IActionListener connectActionListener = mock(IActionListener.class); |
| mCmi.connect(null, TEST_NETWORK_ID, mock(Binder.class), connectActionListener, 0, |
| Process.SYSTEM_UID); |
| mLooper.dispatchAll(); |
| verify(connectActionListener).onSuccess(); |
| |
| verify(mWifiMetrics).setNominatorForNetwork(TEST_NETWORK_ID, |
| WifiMetricsProto.ConnectionEvent.NOMINATOR_MANUAL); |
| } |
| |
| @Test |
| public void testDhcpFailure() throws Exception { |
| initializeAndAddNetworkAndVerifySuccess(); |
| |
| IActionListener connectActionListener = mock(IActionListener.class); |
| mCmi.connect(null, 0, mock(Binder.class), connectActionListener, 0, Binder.getCallingUid()); |
| mLooper.dispatchAll(); |
| verify(connectActionListener).onSuccess(); |
| |
| verify(mWifiConfigManager).enableNetwork(eq(0), eq(true), anyInt(), any()); |
| |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(0, sWifiSsid, sBSSID, SupplicantState.ASSOCIATED)); |
| mLooper.dispatchAll(); |
| |
| mCmi.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, 0, 0, sBSSID); |
| mLooper.dispatchAll(); |
| verify(mBssidBlocklistMonitor).handleBssidConnectionSuccess(sBSSID, sSSID); |
| |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(0, sWifiSsid, sBSSID, SupplicantState.COMPLETED)); |
| mLooper.dispatchAll(); |
| |
| assertEquals("ObtainingIpState", getCurrentState().getName()); |
| injectDhcpFailure(); |
| mLooper.dispatchAll(); |
| |
| assertEquals("DisconnectingState", getCurrentState().getName()); |
| // Verify this is not counted as a IP renewal failure |
| verify(mWifiMetrics, never()).incrementIpRenewalFailure(); |
| // Verifies that WifiLastResortWatchdog be notified |
| // by DHCP failure |
| verify(mWifiLastResortWatchdog, times(2)).noteConnectionFailureAndTriggerIfNeeded( |
| sSSID, sBSSID, WifiLastResortWatchdog.FAILURE_CODE_DHCP); |
| verify(mBssidBlocklistMonitor, times(2)).handleBssidConnectionFailure(eq(sBSSID), |
| eq(sSSID), eq(BssidBlocklistMonitor.REASON_DHCP_FAILURE), anyInt()); |
| verify(mBssidBlocklistMonitor, times(2)).handleBssidConnectionFailure(eq(sBSSID), eq(sSSID), |
| eq(BssidBlocklistMonitor.REASON_DHCP_FAILURE), anyInt()); |
| verify(mBssidBlocklistMonitor, never()).handleDhcpProvisioningSuccess(sBSSID, sSSID); |
| verify(mBssidBlocklistMonitor, never()).handleNetworkValidationSuccess(sBSSID, sSSID); |
| } |
| |
| /** |
| * Verify that a IP renewal failure is logged when IP provisioning fail in the |
| * ConnectedState. |
| */ |
| @Test |
| public void testDhcpRenewalMetrics() throws Exception { |
| connect(); |
| injectDhcpFailure(); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiMetrics).incrementIpRenewalFailure(); |
| } |
| |
| /** |
| * Verify that the network selection status will be updated with DISABLED_AUTHENTICATION_FAILURE |
| * when wrong password authentication failure is detected and the network had been |
| * connected previously. |
| */ |
| @Test |
| public void testWrongPasswordWithPreviouslyConnected() throws Exception { |
| initializeAndAddNetworkAndVerifySuccess(); |
| |
| IActionListener connectActionListener = mock(IActionListener.class); |
| mCmi.connect(null, 0, mock(Binder.class), connectActionListener, 0, Binder.getCallingUid()); |
| mLooper.dispatchAll(); |
| verify(connectActionListener).onSuccess(); |
| |
| verify(mWifiConfigManager).enableNetwork(eq(0), eq(true), anyInt(), any()); |
| |
| WifiConfiguration config = new WifiConfiguration(); |
| config.getNetworkSelectionStatus().setHasEverConnected(true); |
| when(mWifiConfigManager.getConfiguredNetwork(anyInt())).thenReturn(config); |
| |
| mCmi.sendMessage(WifiMonitor.AUTHENTICATION_FAILURE_EVENT, |
| WifiManager.ERROR_AUTH_FAILURE_WRONG_PSWD); |
| mLooper.dispatchAll(); |
| |
| verify(mWrongPasswordNotifier, never()).onWrongPasswordError(anyString()); |
| verify(mWifiConfigManager).updateNetworkSelectionStatus(anyInt(), |
| eq(WifiConfiguration.NetworkSelectionStatus.DISABLED_AUTHENTICATION_FAILURE)); |
| |
| assertEquals("DisconnectedState", getCurrentState().getName()); |
| } |
| |
| /** |
| * Verify that the network selection status will be updated with DISABLED_BY_WRONG_PASSWORD |
| * when wrong password authentication failure is detected and the network has never been |
| * connected. |
| */ |
| @Test |
| public void testWrongPasswordWithNeverConnected() throws Exception { |
| initializeAndAddNetworkAndVerifySuccess(); |
| |
| IActionListener connectActionListener = mock(IActionListener.class); |
| mCmi.connect(null, 0, mock(Binder.class), connectActionListener, 0, Binder.getCallingUid()); |
| mLooper.dispatchAll(); |
| verify(connectActionListener).onSuccess(); |
| |
| verify(mWifiConfigManager).enableNetwork(eq(0), eq(true), anyInt(), any()); |
| |
| WifiConfiguration config = new WifiConfiguration(); |
| config.SSID = sSSID; |
| config.getNetworkSelectionStatus().setHasEverConnected(false); |
| config.carrierId = CARRIER_ID_1; |
| when(mWifiConfigManager.getConfiguredNetwork(anyInt())).thenReturn(config); |
| |
| mCmi.sendMessage(WifiMonitor.AUTHENTICATION_FAILURE_EVENT, |
| WifiManager.ERROR_AUTH_FAILURE_WRONG_PSWD); |
| mLooper.dispatchAll(); |
| |
| verify(mWrongPasswordNotifier).onWrongPasswordError(eq(sSSID)); |
| verify(mWifiConfigManager).updateNetworkSelectionStatus(anyInt(), |
| eq(WifiConfiguration.NetworkSelectionStatus.DISABLED_BY_WRONG_PASSWORD)); |
| verify(mWifiMetrics).incrementNumOfCarrierWifiConnectionAuthFailure(); |
| assertEquals("DisconnectedState", getCurrentState().getName()); |
| } |
| |
| /** |
| * Verify that the network selection status will be updated with DISABLED_BY_WRONG_PASSWORD |
| * when wrong password authentication failure is detected and the network is unknown. |
| */ |
| @Test |
| public void testWrongPasswordWithNullNetwork() throws Exception { |
| initializeAndAddNetworkAndVerifySuccess(); |
| |
| IActionListener connectActionListener = mock(IActionListener.class); |
| mCmi.connect(null, 0, mock(Binder.class), connectActionListener, 0, Binder.getCallingUid()); |
| mLooper.dispatchAll(); |
| verify(connectActionListener).onSuccess(); |
| |
| verify(mWifiConfigManager).enableNetwork(eq(0), eq(true), anyInt(), any()); |
| |
| when(mWifiConfigManager.getConfiguredNetwork(anyInt())).thenReturn(null); |
| |
| mCmi.sendMessage(WifiMonitor.AUTHENTICATION_FAILURE_EVENT, |
| WifiManager.ERROR_AUTH_FAILURE_WRONG_PSWD); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiConfigManager).updateNetworkSelectionStatus(anyInt(), |
| eq(WifiConfiguration.NetworkSelectionStatus.DISABLED_BY_WRONG_PASSWORD)); |
| assertEquals("DisconnectedState", getCurrentState().getName()); |
| } |
| |
| /** |
| * Verify that the function resetCarrierKeysForImsiEncryption() in TelephonyManager |
| * is called when a Authentication failure is detected with a vendor specific EAP Error |
| * of certification expired while using EAP-SIM |
| * In this test case, it is assumed that the network had been connected previously. |
| */ |
| @Test |
| public void testEapSimErrorVendorSpecific() throws Exception { |
| when(mWifiMetrics.startConnectionEvent(any(), anyString(), anyInt())).thenReturn(80000); |
| initializeAndAddNetworkAndVerifySuccess(); |
| |
| IActionListener connectActionListener = mock(IActionListener.class); |
| mCmi.connect(null, 0, mock(Binder.class), connectActionListener, 0, Binder.getCallingUid()); |
| mLooper.dispatchAll(); |
| verify(connectActionListener).onSuccess(); |
| |
| verify(mWifiConfigManager).enableNetwork(eq(0), eq(true), anyInt(), any()); |
| |
| WifiConfiguration config = new WifiConfiguration(); |
| config.SSID = sSSID; |
| config.getNetworkSelectionStatus().setHasEverConnected(true); |
| config.allowedKeyManagement.set(KeyMgmt.IEEE8021X); |
| config.enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.SIM); |
| when(mWifiConfigManager.getConfiguredNetwork(anyInt())).thenReturn(config); |
| MockitoSession mockSession = ExtendedMockito.mockitoSession() |
| .mockStatic(SubscriptionManager.class) |
| .startMocking(); |
| when(SubscriptionManager.getDefaultDataSubscriptionId()).thenReturn(DATA_SUBID); |
| when(SubscriptionManager.isValidSubscriptionId(anyInt())).thenReturn(true); |
| when(mWifiScoreCard.detectAbnormalConnectionFailure(anyString())) |
| .thenReturn(WifiHealthMonitor.REASON_AUTH_FAILURE); |
| |
| mCmi.sendMessage(WifiMonitor.AUTHENTICATION_FAILURE_EVENT, |
| WifiManager.ERROR_AUTH_FAILURE_EAP_FAILURE, |
| WifiNative.EAP_SIM_VENDOR_SPECIFIC_CERT_EXPIRED); |
| mLooper.dispatchAll(); |
| |
| verify(mEapFailureNotifier).onEapFailure( |
| WifiNative.EAP_SIM_VENDOR_SPECIFIC_CERT_EXPIRED, config); |
| verify(mDataTelephonyManager).resetCarrierKeysForImsiEncryption(); |
| mockSession.finishMocking(); |
| verify(mDeviceConfigFacade).isAbnormalConnectionFailureBugreportEnabled(); |
| verify(mWifiScoreCard).detectAbnormalConnectionFailure(anyString()); |
| verify(mWifiDiagnostics, times(2)).takeBugReport(anyString(), anyString()); |
| } |
| |
| /** |
| * Verify that the function resetCarrierKeysForImsiEncryption() in TelephonyManager |
| * is not called when a Authentication failure is detected with a vendor specific EAP Error |
| * of certification expired while using other methods than EAP-SIM, EAP-AKA, or EAP-AKA'. |
| */ |
| @Test |
| public void testEapTlsErrorVendorSpecific() throws Exception { |
| initializeAndAddNetworkAndVerifySuccess(); |
| |
| IActionListener connectActionListener = mock(IActionListener.class); |
| mCmi.connect(null, 0, mock(Binder.class), connectActionListener, 0, Binder.getCallingUid()); |
| mLooper.dispatchAll(); |
| verify(connectActionListener).onSuccess(); |
| |
| verify(mWifiConfigManager).enableNetwork(eq(0), eq(true), anyInt(), any()); |
| |
| WifiConfiguration config = new WifiConfiguration(); |
| config.getNetworkSelectionStatus().setHasEverConnected(true); |
| config.enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.TLS); |
| config.allowedKeyManagement.set(KeyMgmt.IEEE8021X); |
| when(mWifiConfigManager.getConfiguredNetwork(anyInt())).thenReturn(config); |
| |
| mCmi.sendMessage(WifiMonitor.AUTHENTICATION_FAILURE_EVENT, |
| WifiManager.ERROR_AUTH_FAILURE_EAP_FAILURE, |
| WifiNative.EAP_SIM_VENDOR_SPECIFIC_CERT_EXPIRED); |
| mLooper.dispatchAll(); |
| |
| verify(mDataTelephonyManager, never()).resetCarrierKeysForImsiEncryption(); |
| } |
| |
| /** |
| * Verify that the network selection status will be updated with |
| * DISABLED_AUTHENTICATION_NO_SUBSCRIBED when service is not subscribed. |
| */ |
| @Test |
| public void testEapSimNoSubscribedError() throws Exception { |
| initializeAndAddNetworkAndVerifySuccess(); |
| |
| IActionListener connectActionListener = mock(IActionListener.class); |
| mCmi.connect(null, 0, mock(Binder.class), connectActionListener, 0, Binder.getCallingUid()); |
| mLooper.dispatchAll(); |
| verify(connectActionListener).onSuccess(); |
| |
| verify(mWifiConfigManager).enableNetwork(eq(0), eq(true), anyInt(), any()); |
| |
| when(mWifiConfigManager.getConfiguredNetwork(anyInt())).thenReturn(null); |
| |
| mCmi.sendMessage(WifiMonitor.AUTHENTICATION_FAILURE_EVENT, |
| WifiManager.ERROR_AUTH_FAILURE_EAP_FAILURE, |
| WifiNative.EAP_SIM_NOT_SUBSCRIBED); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiConfigManager).updateNetworkSelectionStatus(anyInt(), |
| eq(WifiConfiguration.NetworkSelectionStatus |
| .DISABLED_AUTHENTICATION_NO_SUBSCRIPTION)); |
| } |
| |
| @Test |
| public void testBadNetworkEvent() throws Exception { |
| initializeAndAddNetworkAndVerifySuccess(); |
| |
| IActionListener connectActionListener = mock(IActionListener.class); |
| mCmi.connect(null, 0, mock(Binder.class), connectActionListener, 0, Binder.getCallingUid()); |
| mLooper.dispatchAll(); |
| verify(connectActionListener).onSuccess(); |
| |
| verify(mWifiConfigManager).enableNetwork(eq(0), eq(true), anyInt(), any()); |
| |
| mCmi.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, 0, 0, sBSSID); |
| mLooper.dispatchAll(); |
| |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(0, sWifiSsid, sBSSID, SupplicantState.COMPLETED)); |
| mLooper.dispatchAll(); |
| |
| assertEquals("DisconnectedState", getCurrentState().getName()); |
| verify(mWifiDiagnostics, never()).takeBugReport(anyString(), anyString()); |
| } |
| |
| |
| @Test |
| public void getWhatToString() throws Exception { |
| assertEquals("CMD_CHANNEL_HALF_CONNECTED", mCmi.getWhatToString( |
| AsyncChannel.CMD_CHANNEL_HALF_CONNECTED)); |
| assertEquals("CMD_PRE_DHCP_ACTION", mCmi.getWhatToString(CMD_PRE_DHCP_ACTION)); |
| assertEquals("CMD_IP_REACHABILITY_LOST", mCmi.getWhatToString( |
| ClientModeImpl.CMD_IP_REACHABILITY_LOST)); |
| } |
| |
| @Test |
| public void disconnect() throws Exception { |
| when(mWifiScoreCard.detectAbnormalDisconnection()) |
| .thenReturn(WifiHealthMonitor.REASON_SHORT_CONNECTION_NONLOCAL); |
| InOrder inOrderWifiLockManager = inOrder(mWifiLockManager); |
| connect(); |
| inOrderWifiLockManager.verify(mWifiLockManager).updateWifiClientConnected(true); |
| |
| mCmi.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, -1, 3, sBSSID); |
| mLooper.dispatchAll(); |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(0, sWifiSsid, sBSSID, SupplicantState.DISCONNECTED)); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiStateTracker).updateState(eq(WifiStateTracker.DISCONNECTED)); |
| verify(mWifiNetworkSuggestionsManager).handleDisconnect(any(), any()); |
| assertEquals("DisconnectedState", getCurrentState().getName()); |
| inOrderWifiLockManager.verify(mWifiLockManager).updateWifiClientConnected(false); |
| verify(mWifiScoreCard).detectAbnormalDisconnection(); |
| verify(mWifiDiagnostics).takeBugReport(anyString(), anyString()); |
| } |
| |
| /** |
| * Successfully connecting to a network will set WifiConfiguration's value of HasEverConnected |
| * to true. |
| * |
| * Test: Successfully create and connect to a network. Check the config and verify |
| * WifiConfiguration.getHasEverConnected() is true. |
| */ |
| @Test |
| public void setHasEverConnectedTrueOnConnect() throws Exception { |
| connect(); |
| verify(mWifiConfigManager, atLeastOnce()).updateNetworkAfterConnect(0); |
| } |
| |
| /** |
| * Fail network connection attempt and verify HasEverConnected remains false. |
| * |
| * Test: Successfully create a network but fail when connecting. Check the config and verify |
| * WifiConfiguration.getHasEverConnected() is false. |
| */ |
| @Test |
| public void connectionFailureDoesNotSetHasEverConnectedTrue() throws Exception { |
| testDhcpFailure(); |
| verify(mWifiConfigManager, never()).updateNetworkAfterConnect(0); |
| } |
| |
| @Test |
| public void iconQueryTest() throws Exception { |
| // TODO(b/31065385): Passpoint config management. |
| } |
| |
| @Test |
| public void verboseLogRecSizeIsGreaterThanNormalSize() { |
| assertTrue(LOG_REC_LIMIT_IN_VERBOSE_MODE > ClientModeImpl.NUM_LOG_RECS_NORMAL); |
| } |
| |
| /** |
| * Verifies that, by default, we allow only the "normal" number of log records. |
| */ |
| @Test |
| public void normalLogRecSizeIsUsedByDefault() { |
| assertEquals(ClientModeImpl.NUM_LOG_RECS_NORMAL, mCmi.getLogRecMaxSize()); |
| } |
| |
| /** |
| * Verifies that, in verbose mode, we allow a larger number of log records. |
| */ |
| @Test |
| public void enablingVerboseLoggingUpdatesLogRecSize() { |
| mCmi.enableVerboseLogging(1); |
| assertEquals(LOG_REC_LIMIT_IN_VERBOSE_MODE, mCmi.getLogRecMaxSize()); |
| } |
| |
| @Test |
| public void disablingVerboseLoggingClearsRecords() { |
| mCmi.sendMessage(ClientModeImpl.CMD_DISCONNECT); |
| mLooper.dispatchAll(); |
| assertTrue(mCmi.getLogRecSize() >= 1); |
| |
| mCmi.enableVerboseLogging(0); |
| assertEquals(0, mCmi.getLogRecSize()); |
| } |
| |
| @Test |
| public void disablingVerboseLoggingUpdatesLogRecSize() { |
| mCmi.enableVerboseLogging(1); |
| mCmi.enableVerboseLogging(0); |
| assertEquals(ClientModeImpl.NUM_LOG_RECS_NORMAL, mCmi.getLogRecMaxSize()); |
| } |
| |
| @Test |
| public void logRecsIncludeDisconnectCommand() { |
| // There's nothing special about the DISCONNECT command. It's just representative of |
| // "normal" commands. |
| mCmi.sendMessage(ClientModeImpl.CMD_DISCONNECT); |
| mLooper.dispatchAll(); |
| assertEquals(1, mCmi.copyLogRecs() |
| .stream() |
| .filter(logRec -> logRec.getWhat() == ClientModeImpl.CMD_DISCONNECT) |
| .count()); |
| } |
| |
| @Test |
| public void logRecsExcludeRssiPollCommandByDefault() { |
| mCmi.sendMessage(ClientModeImpl.CMD_RSSI_POLL); |
| mLooper.dispatchAll(); |
| assertEquals(0, mCmi.copyLogRecs() |
| .stream() |
| .filter(logRec -> logRec.getWhat() == ClientModeImpl.CMD_RSSI_POLL) |
| .count()); |
| } |
| |
| @Test |
| public void logRecsIncludeRssiPollCommandWhenVerboseLoggingIsEnabled() { |
| mCmi.enableVerboseLogging(1); |
| mCmi.sendMessage(ClientModeImpl.CMD_RSSI_POLL); |
| mLooper.dispatchAll(); |
| assertEquals(1, mCmi.copyLogRecs() |
| .stream() |
| .filter(logRec -> logRec.getWhat() == ClientModeImpl.CMD_RSSI_POLL) |
| .count()); |
| } |
| |
| /** |
| * Verify that syncStartSubscriptionProvisioning will redirect calls with right parameters |
| * to {@link PasspointManager} with expected true being returned when in client mode. |
| */ |
| @Test |
| public void syncStartSubscriptionProvisioningInClientMode() throws Exception { |
| loadComponentsInStaMode(); |
| when(mPasspointManager.startSubscriptionProvisioning(anyInt(), |
| any(OsuProvider.class), any(IProvisioningCallback.class))).thenReturn(true); |
| mLooper.startAutoDispatch(); |
| assertTrue(mCmi.syncStartSubscriptionProvisioning( |
| OTHER_USER_UID, mOsuProvider, mProvisioningCallback, mCmiAsyncChannel)); |
| verify(mPasspointManager).startSubscriptionProvisioning(OTHER_USER_UID, mOsuProvider, |
| mProvisioningCallback); |
| mLooper.stopAutoDispatch(); |
| } |
| |
| /** |
| * Verify that syncStartSubscriptionProvisioning will be a no-op and return false before |
| * SUPPLICANT_START command is received by the CMI. |
| */ |
| @Test |
| public void syncStartSubscriptionProvisioningBeforeSupplicantOrAPStart() throws Exception { |
| mLooper.startAutoDispatch(); |
| assertFalse(mCmi.syncStartSubscriptionProvisioning( |
| OTHER_USER_UID, mOsuProvider, mProvisioningCallback, mCmiAsyncChannel)); |
| mLooper.stopAutoDispatch(); |
| verify(mPasspointManager, never()).startSubscriptionProvisioning( |
| anyInt(), any(OsuProvider.class), any(IProvisioningCallback.class)); |
| } |
| |
| /** |
| * Verify that syncStartSubscriptionProvisioning will be a no-op and return false when not in |
| * client mode. |
| */ |
| @Test |
| public void syncStartSubscriptionProvisioningNoOpWifiDisabled() throws Exception { |
| mLooper.startAutoDispatch(); |
| assertFalse(mCmi.syncStartSubscriptionProvisioning( |
| OTHER_USER_UID, mOsuProvider, mProvisioningCallback, mCmiAsyncChannel)); |
| mLooper.stopAutoDispatch(); |
| verify(mPasspointManager, never()).startSubscriptionProvisioning( |
| anyInt(), any(OsuProvider.class), any(IProvisioningCallback.class)); |
| } |
| |
| /** |
| * Test that we disconnect from a network if it was removed while we are in the |
| * ObtainingIpState. |
| */ |
| @Test |
| public void disconnectFromNetworkWhenRemovedWhileObtainingIpAddr() throws Exception { |
| initializeAndAddNetworkAndVerifySuccess(); |
| |
| when(mWifiConfigManager.enableNetwork(eq(0), eq(true), anyInt(), any())) |
| .thenReturn(true); |
| when(mWifiConfigManager.updateLastConnectUid(eq(0), anyInt())).thenReturn(true); |
| |
| verify(mWifiNative).removeAllNetworks(WIFI_IFACE_NAME); |
| |
| IActionListener connectActionListener = mock(IActionListener.class); |
| mCmi.connect(null, 0, mock(Binder.class), connectActionListener, 0, Binder.getCallingUid()); |
| mLooper.dispatchAll(); |
| verify(connectActionListener).onSuccess(); |
| |
| verify(mWifiConfigManager).enableNetwork(eq(0), eq(true), anyInt(), any()); |
| verify(mWifiConnectivityManager).setUserConnectChoice(eq(0)); |
| when(mWifiConfigManager.getScanDetailCacheForNetwork(FRAMEWORK_NETWORK_ID)) |
| .thenReturn(mScanDetailCache); |
| |
| when(mScanDetailCache.getScanDetail(sBSSID)).thenReturn( |
| getGoogleGuestScanDetail(TEST_RSSI, sBSSID, sFreq)); |
| when(mScanDetailCache.getScanResult(sBSSID)).thenReturn( |
| getGoogleGuestScanDetail(TEST_RSSI, sBSSID, sFreq).getScanResult()); |
| |
| mCmi.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, 0, 0, sBSSID); |
| mLooper.dispatchAll(); |
| |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(0, sWifiSsid, sBSSID, SupplicantState.COMPLETED)); |
| mLooper.dispatchAll(); |
| |
| assertEquals("ObtainingIpState", getCurrentState().getName()); |
| |
| // now remove the config |
| reset(connectActionListener); |
| when(mWifiConfigManager.removeNetwork(eq(FRAMEWORK_NETWORK_ID), anyInt(), any())) |
| .thenReturn(true); |
| mCmi.forget(FRAMEWORK_NETWORK_ID, mock(Binder.class), connectActionListener, 0, |
| Binder.getCallingUid()); |
| mLooper.dispatchAll(); |
| verify(connectActionListener).onSuccess(); |
| verify(mWifiConfigManager).removeNetwork(eq(FRAMEWORK_NETWORK_ID), anyInt(), any()); |
| // trigger removal callback to trigger disconnect. |
| WifiConfiguration removedConfig = new WifiConfiguration(); |
| removedConfig.networkId = FRAMEWORK_NETWORK_ID; |
| mConfigUpdateListenerCaptor.getValue().onNetworkRemoved(removedConfig); |
| |
| reset(mWifiConfigManager); |
| |
| when(mWifiConfigManager.getConfiguredNetwork(FRAMEWORK_NETWORK_ID)).thenReturn(null); |
| |
| DhcpResultsParcelable dhcpResults = new DhcpResultsParcelable(); |
| dhcpResults.baseConfiguration = new StaticIpConfiguration(); |
| dhcpResults.baseConfiguration.gateway = InetAddresses.parseNumericAddress("1.2.3.4"); |
| dhcpResults.baseConfiguration.ipAddress = |
| new LinkAddress(InetAddresses.parseNumericAddress("192.168.1.100"), 0); |
| dhcpResults.baseConfiguration.dnsServers.add(InetAddresses.parseNumericAddress("8.8.8.8")); |
| dhcpResults.leaseDuration = 3600; |
| |
| injectDhcpSuccess(dhcpResults); |
| mLooper.dispatchAll(); |
| |
| assertEquals("DisconnectingState", getCurrentState().getName()); |
| } |
| |
| /** |
| * Test verifying that interface Supplicant update for inactive driver does not trigger |
| * SelfRecovery when WifiNative reports the interface is up. |
| */ |
| @Test |
| public void testSupplicantUpdateDriverInactiveIfaceUpClientModeDoesNotTriggerSelfRecovery() |
| throws Exception { |
| // Trigger initialize to capture the death handler registration. |
| loadComponentsInStaMode(); |
| |
| when(mWifiNative.isInterfaceUp(eq(WIFI_IFACE_NAME))).thenReturn(true); |
| |
| // make sure supplicant has been reported as inactive |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(0, WifiSsid.createFromAsciiEncoded(""), null, |
| SupplicantState.INTERFACE_DISABLED)); |
| mLooper.dispatchAll(); |
| |
| // CMI should trigger self recovery, but not disconnect until externally triggered |
| verify(mSelfRecovery, never()).trigger(eq(SelfRecovery.REASON_STA_IFACE_DOWN)); |
| } |
| |
| /** |
| * Verifies that WifiInfo is updated upon SUPPLICANT_STATE_CHANGE_EVENT. |
| */ |
| @Test |
| public void testWifiInfoUpdatedUponSupplicantStateChangedEvent() throws Exception { |
| // Connect to network with |sBSSID|, |sFreq|. |
| connect(); |
| |
| // Set the scan detail cache for roaming target. |
| when(mWifiConfigManager.getScanDetailCacheForNetwork(FRAMEWORK_NETWORK_ID)) |
| .thenReturn(mScanDetailCache); |
| when(mScanDetailCache.getScanDetail(sBSSID1)).thenReturn( |
| getGoogleGuestScanDetail(TEST_RSSI, sBSSID1, sFreq1)); |
| when(mScanDetailCache.getScanResult(sBSSID1)).thenReturn( |
| getGoogleGuestScanDetail(TEST_RSSI, sBSSID1, sFreq1).getScanResult()); |
| |
| // This simulates the behavior of roaming to network with |sBSSID1|, |sFreq1|. |
| // Send a SUPPLICANT_STATE_CHANGE_EVENT, verify WifiInfo is updated. |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(0, sWifiSsid, sBSSID1, SupplicantState.COMPLETED)); |
| mLooper.dispatchAll(); |
| |
| WifiInfo wifiInfo = mCmi.getWifiInfo(); |
| assertEquals(sBSSID1, wifiInfo.getBSSID()); |
| assertEquals(sFreq1, wifiInfo.getFrequency()); |
| assertEquals(SupplicantState.COMPLETED, wifiInfo.getSupplicantState()); |
| |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(0, sWifiSsid, sBSSID1, SupplicantState.DISCONNECTED)); |
| mLooper.dispatchAll(); |
| |
| wifiInfo = mCmi.getWifiInfo(); |
| assertNull(wifiInfo.getBSSID()); |
| assertEquals(WifiManager.UNKNOWN_SSID, wifiInfo.getSSID()); |
| assertEquals(WifiConfiguration.INVALID_NETWORK_ID, wifiInfo.getNetworkId()); |
| assertEquals(SupplicantState.DISCONNECTED, wifiInfo.getSupplicantState()); |
| } |
| |
| /** |
| * Verifies that WifiInfo is updated upon CMD_ASSOCIATED_BSSID event. |
| */ |
| @Test |
| public void testWifiInfoUpdatedUponAssociatedBSSIDEvent() throws Exception { |
| // Connect to network with |sBSSID|, |sFreq|. |
| connect(); |
| |
| // Set the scan detail cache for roaming target. |
| when(mWifiConfigManager.getScanDetailCacheForNetwork(FRAMEWORK_NETWORK_ID)) |
| .thenReturn(mScanDetailCache); |
| when(mScanDetailCache.getScanDetail(sBSSID1)).thenReturn( |
| getGoogleGuestScanDetail(TEST_RSSI, sBSSID1, sFreq1)); |
| when(mScanDetailCache.getScanResult(sBSSID1)).thenReturn( |
| getGoogleGuestScanDetail(TEST_RSSI, sBSSID1, sFreq1).getScanResult()); |
| |
| // This simulates the behavior of roaming to network with |sBSSID1|, |sFreq1|. |
| // Send a CMD_ASSOCIATED_BSSID, verify WifiInfo is updated. |
| mCmi.sendMessage(WifiMonitor.ASSOCIATED_BSSID_EVENT, 0, 0, sBSSID1); |
| mLooper.dispatchAll(); |
| |
| WifiInfo wifiInfo = mCmi.getWifiInfo(); |
| assertEquals(sBSSID1, wifiInfo.getBSSID()); |
| assertEquals(sFreq1, wifiInfo.getFrequency()); |
| assertEquals(SupplicantState.COMPLETED, wifiInfo.getSupplicantState()); |
| } |
| |
| /** |
| * Verifies that WifiInfo is cleared upon exiting and entering WifiInfo, and that it is not |
| * updated by SUPPLICAN_STATE_CHANGE_EVENTs in ScanModeState. |
| * This protects ClientModeImpl from getting into a bad state where WifiInfo says wifi is |
| * already Connected or Connecting, (when it is in-fact Disconnected), so |
| * WifiConnectivityManager does not attempt any new Connections, freezing wifi. |
| */ |
| @Test |
| public void testWifiInfoCleanedUpEnteringExitingConnectModeState() throws Exception { |
| InOrder inOrder = inOrder(mWifiConnectivityManager, mWifiNetworkFactory); |
| InOrder inOrderSarMgr = inOrder(mSarManager); |
| InOrder inOrderMetrics = inOrder(mWifiMetrics); |
| Log.i(TAG, mCmi.getCurrentState().getName()); |
| String initialBSSID = "aa:bb:cc:dd:ee:ff"; |
| WifiInfo wifiInfo = mCmi.getWifiInfo(); |
| wifiInfo.setBSSID(initialBSSID); |
| |
| // Set CMI to CONNECT_MODE and verify state, and wifi enabled in ConnectivityManager |
| startSupplicantAndDispatchMessages(); |
| assertEquals(ClientModeImpl.CONNECT_MODE, mCmi.getOperationalModeForTest()); |
| assertEquals(WifiManager.WIFI_STATE_ENABLED, mCmi.syncGetWifiState()); |
| inOrder.verify(mWifiConnectivityManager).setWifiEnabled(eq(true)); |
| inOrder.verify(mWifiNetworkFactory).setWifiState(eq(true)); |
| inOrderMetrics.verify(mWifiMetrics) |
| .setWifiState(WifiMetricsProto.WifiLog.WIFI_DISCONNECTED); |
| inOrderMetrics.verify(mWifiMetrics).logStaEvent(StaEvent.TYPE_WIFI_ENABLED); |
| assertNull(wifiInfo.getBSSID()); |
| |
| // Send a SUPPLICANT_STATE_CHANGE_EVENT, verify WifiInfo is updated |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(0, sWifiSsid, sBSSID, SupplicantState.COMPLETED)); |
| mLooper.dispatchAll(); |
| assertEquals(sBSSID, wifiInfo.getBSSID()); |
| assertEquals(SupplicantState.COMPLETED, wifiInfo.getSupplicantState()); |
| |
| // Set CMI to DISABLED_MODE, verify state and wifi disabled in ConnectivityManager, and |
| // WifiInfo is reset() and state set to DISCONNECTED |
| mCmi.setWifiStateForApiCalls(WifiManager.WIFI_STATE_DISABLED); |
| mCmi.setOperationalMode(ClientModeImpl.DISABLED_MODE, null); |
| mLooper.dispatchAll(); |
| |
| assertEquals(ClientModeImpl.DISABLED_MODE, mCmi.getOperationalModeForTest()); |
| assertEquals("DefaultState", getCurrentState().getName()); |
| assertEquals(WifiManager.WIFI_STATE_DISABLED, mCmi.syncGetWifiState()); |
| inOrder.verify(mWifiConnectivityManager).setWifiEnabled(eq(false)); |
| inOrder.verify(mWifiNetworkFactory).setWifiState(eq(false)); |
| inOrderMetrics.verify(mWifiMetrics).setWifiState(WifiMetricsProto.WifiLog.WIFI_DISABLED); |
| inOrderMetrics.verify(mWifiMetrics).logStaEvent(StaEvent.TYPE_WIFI_DISABLED); |
| assertNull(wifiInfo.getBSSID()); |
| assertEquals(SupplicantState.DISCONNECTED, wifiInfo.getSupplicantState()); |
| verify(mPasspointManager).clearAnqpRequestsAndFlushCache(); |
| |
| // Send a SUPPLICANT_STATE_CHANGE_EVENT, verify WifiInfo is not updated |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(0, sWifiSsid, sBSSID, SupplicantState.COMPLETED)); |
| mLooper.dispatchAll(); |
| assertNull(wifiInfo.getBSSID()); |
| assertEquals(SupplicantState.DISCONNECTED, wifiInfo.getSupplicantState()); |
| |
| // Set the bssid to something, so we can verify it is cleared (just in case) |
| wifiInfo.setBSSID(initialBSSID); |
| |
| // Set CMI to CONNECT_MODE and verify state, and wifi enabled in ConnectivityManager, |
| // and WifiInfo has been reset |
| startSupplicantAndDispatchMessages(); |
| |
| assertEquals(ClientModeImpl.CONNECT_MODE, mCmi.getOperationalModeForTest()); |
| assertEquals(WifiManager.WIFI_STATE_ENABLED, mCmi.syncGetWifiState()); |
| inOrder.verify(mWifiConnectivityManager).setWifiEnabled(eq(true)); |
| inOrder.verify(mWifiNetworkFactory).setWifiState(eq(true)); |
| inOrderMetrics.verify(mWifiMetrics) |
| .setWifiState(WifiMetricsProto.WifiLog.WIFI_DISCONNECTED); |
| inOrderMetrics.verify(mWifiMetrics).logStaEvent(StaEvent.TYPE_WIFI_ENABLED); |
| assertEquals("DisconnectedState", getCurrentState().getName()); |
| assertEquals(SupplicantState.DISCONNECTED, wifiInfo.getSupplicantState()); |
| assertNull(wifiInfo.getBSSID()); |
| } |
| |
| /** |
| * Test that connected SSID and BSSID are exposed to system server. |
| * Also tests that {@link ClientModeImpl#syncRequestConnectionInfo()} always |
| * returns a copy of WifiInfo. |
| */ |
| @Test |
| public void testConnectedIdsAreVisibleFromSystemServer() throws Exception { |
| WifiInfo wifiInfo = mCmi.getWifiInfo(); |
| // Get into a connected state, with known BSSID and SSID |
| connect(); |
| assertEquals(sBSSID, wifiInfo.getBSSID()); |
| assertEquals(sWifiSsid, wifiInfo.getWifiSsid()); |
| |
| WifiInfo connectionInfo = mCmi.syncRequestConnectionInfo(); |
| |
| assertNotEquals(wifiInfo, connectionInfo); |
| assertEquals(wifiInfo.getSSID(), connectionInfo.getSSID()); |
| assertEquals(wifiInfo.getBSSID(), connectionInfo.getBSSID()); |
| assertEquals(wifiInfo.getMacAddress(), connectionInfo.getMacAddress()); |
| } |
| |
| /** |
| * Test that reconnectCommand() triggers connectivity scan when ClientModeImpl |
| * is in DisconnectedMode. |
| */ |
| @Test |
| public void testReconnectCommandWhenDisconnected() throws Exception { |
| // Connect to network with |sBSSID|, |sFreq|, and then disconnect. |
| disconnect(); |
| |
| mCmi.reconnectCommand(ClientModeImpl.WIFI_WORK_SOURCE); |
| mLooper.dispatchAll(); |
| verify(mWifiConnectivityManager).forceConnectivityScan(ClientModeImpl.WIFI_WORK_SOURCE); |
| } |
| |
| /** |
| * Test that reconnectCommand() doesn't trigger connectivity scan when ClientModeImpl |
| * is in ConnectedMode. |
| */ |
| @Test |
| public void testReconnectCommandWhenConnected() throws Exception { |
| // Connect to network with |sBSSID|, |sFreq|. |
| connect(); |
| |
| mCmi.reconnectCommand(ClientModeImpl.WIFI_WORK_SOURCE); |
| mLooper.dispatchAll(); |
| verify(mWifiConnectivityManager, never()) |
| .forceConnectivityScan(ClientModeImpl.WIFI_WORK_SOURCE); |
| } |
| |
| /** |
| * Adds the network without putting ClientModeImpl into ConnectMode. |
| */ |
| @Test |
| public void addNetworkInDefaultState() throws Exception { |
| // We should not be in initial state now. |
| assertTrue("DefaultState".equals(getCurrentState().getName())); |
| initializeMocksForAddedNetwork(false); |
| verify(mWifiConnectivityManager, never()).setUserConnectChoice(eq(0)); |
| } |
| |
| /** |
| * Verifies that ClientModeImpl sets and unsets appropriate 'RecentFailureReason' values |
| * on a WifiConfiguration when it fails association, authentication, or successfully connects |
| */ |
| @Test |
| public void testExtraFailureReason_ApIsBusy() throws Exception { |
| // Setup CONNECT_MODE & a WifiConfiguration |
| initializeAndAddNetworkAndVerifySuccess(); |
| // Trigger a connection to this (CMD_START_CONNECT will actually fail, but it sets up |
| // targetNetworkId state) |
| mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, 0, 0, sBSSID); |
| mLooper.dispatchAll(); |
| // Simulate an ASSOCIATION_REJECTION_EVENT, due to the AP being busy |
| mCmi.sendMessage(WifiMonitor.ASSOCIATION_REJECTION_EVENT, 0, |
| ISupplicantStaIfaceCallback.StatusCode.AP_UNABLE_TO_HANDLE_NEW_STA, sBSSID); |
| mLooper.dispatchAll(); |
| verify(mWifiConfigManager).setRecentFailureAssociationStatus(eq(0), |
| eq(WifiConfiguration.RECENT_FAILURE_AP_UNABLE_TO_HANDLE_NEW_STA)); |
| assertEquals("DisconnectedState", getCurrentState().getName()); |
| |
| // Simulate an AUTHENTICATION_FAILURE_EVENT, which should clear the ExtraFailureReason |
| mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, 0, 0, sBSSID); |
| mLooper.dispatchAll(); |
| mCmi.sendMessage(WifiMonitor.AUTHENTICATION_FAILURE_EVENT, 0, 0, null); |
| mLooper.dispatchAll(); |
| verify(mWifiConfigManager, times(1)).clearRecentFailureReason(eq(0)); |
| verify(mWifiConfigManager, times(1)).setRecentFailureAssociationStatus(anyInt(), anyInt()); |
| |
| // Simulate a NETWORK_CONNECTION_EVENT which should clear the ExtraFailureReason |
| mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, 0, 0, sBSSID); |
| mLooper.dispatchAll(); |
| mCmi.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, 0, 0, null); |
| mLooper.dispatchAll(); |
| verify(mWifiConfigManager, times(2)).clearRecentFailureReason(eq(0)); |
| verify(mWifiConfigManager, times(1)).setRecentFailureAssociationStatus(anyInt(), anyInt()); |
| } |
| |
| private WifiConfiguration makeLastSelectedWifiConfiguration(int lastSelectedNetworkId, |
| long timeSinceLastSelected) { |
| long lastSelectedTimestamp = 45666743454L; |
| |
| when(mClock.getElapsedSinceBootMillis()).thenReturn( |
| lastSelectedTimestamp + timeSinceLastSelected); |
| when(mWifiConfigManager.getLastSelectedTimeStamp()).thenReturn(lastSelectedTimestamp); |
| when(mWifiConfigManager.getLastSelectedNetwork()).thenReturn(lastSelectedNetworkId); |
| |
| WifiConfiguration currentConfig = new WifiConfiguration(); |
| currentConfig.networkId = lastSelectedNetworkId; |
| return currentConfig; |
| } |
| |
| /** |
| * Test that the helper method |
| * {@link ClientModeImpl#shouldEvaluateWhetherToSendExplicitlySelected(WifiConfiguration)} |
| * returns true when we connect to the last selected network before expiration of |
| * {@link ClientModeImpl#LAST_SELECTED_NETWORK_EXPIRATION_AGE_MILLIS}. |
| */ |
| @Test |
| public void testShouldEvaluateWhetherToSendExplicitlySelected_SameNetworkNotExpired() { |
| WifiConfiguration currentConfig = makeLastSelectedWifiConfiguration(5, |
| ClientModeImpl.LAST_SELECTED_NETWORK_EXPIRATION_AGE_MILLIS - 1); |
| assertTrue(mCmi.shouldEvaluateWhetherToSendExplicitlySelected(currentConfig)); |
| } |
| |
| /** |
| * Test that the helper method |
| * {@link ClientModeImpl#shouldEvaluateWhetherToSendExplicitlySelected(WifiConfiguration)} |
| * returns false when we connect to the last selected network after expiration of |
| * {@link ClientModeImpl#LAST_SELECTED_NETWORK_EXPIRATION_AGE_MILLIS}. |
| */ |
| @Test |
| public void testShouldEvaluateWhetherToSendExplicitlySelected_SameNetworkExpired() { |
| WifiConfiguration currentConfig = makeLastSelectedWifiConfiguration(5, |
| ClientModeImpl.LAST_SELECTED_NETWORK_EXPIRATION_AGE_MILLIS + 1); |
| assertFalse(mCmi.shouldEvaluateWhetherToSendExplicitlySelected(currentConfig)); |
| } |
| |
| /** |
| * Test that the helper method |
| * {@link ClientModeImpl#shouldEvaluateWhetherToSendExplicitlySelected(WifiConfiguration)} |
| * returns false when we connect to a different network to the last selected network. |
| */ |
| @Test |
| public void testShouldEvaluateWhetherToSendExplicitlySelected_DifferentNetwork() { |
| WifiConfiguration currentConfig = makeLastSelectedWifiConfiguration(5, |
| ClientModeImpl.LAST_SELECTED_NETWORK_EXPIRATION_AGE_MILLIS - 1); |
| currentConfig.networkId = 4; |
| assertFalse(mCmi.shouldEvaluateWhetherToSendExplicitlySelected(currentConfig)); |
| } |
| |
| private void expectRegisterNetworkAgent(Consumer<NetworkAgentConfig> configChecker, |
| Consumer<NetworkCapabilities> networkCapabilitiesChecker) { |
| // Expects that the code calls registerNetworkAgent and provides a way for the test to |
| // verify the messages sent through the NetworkAgent to ConnectivityService. |
| // We cannot just use a mock object here because mWifiNetworkAgent is private to CMI. |
| // TODO (b/134538181): consider exposing WifiNetworkAgent and using mocks. |
| ArgumentCaptor<Messenger> messengerCaptor = ArgumentCaptor.forClass(Messenger.class); |
| ArgumentCaptor<NetworkAgentConfig> configCaptor = |
| ArgumentCaptor.forClass(NetworkAgentConfig.class); |
| ArgumentCaptor<NetworkCapabilities> networkCapabilitiesCaptor = |
| ArgumentCaptor.forClass(NetworkCapabilities.class); |
| verify(mConnectivityManager).registerNetworkAgent(messengerCaptor.capture(), |
| any(NetworkInfo.class), any(LinkProperties.class), |
| networkCapabilitiesCaptor.capture(), |
| anyInt(), configCaptor.capture(), anyInt()); |
| |
| registerAsyncChannel((x) -> { |
| mNetworkAgentAsyncChannel = x; |
| }, messengerCaptor.getValue(), mNetworkAgentHandler); |
| configChecker.accept(configCaptor.getValue()); |
| networkCapabilitiesChecker.accept(networkCapabilitiesCaptor.getValue()); |
| |
| mNetworkAgentAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION); |
| mLooper.dispatchAll(); |
| } |
| |
| private void expectUnregisterNetworkAgent() { |
| // We cannot just use a mock object here because mWifiNetworkAgent is private to CMI. |
| // TODO (b/134538181): consider exposing WifiNetworkAgent and using mocks. |
| ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class); |
| mLooper.dispatchAll(); |
| verify(mNetworkAgentHandler).handleMessage(messageCaptor.capture()); |
| Message message = messageCaptor.getValue(); |
| assertNotNull(message); |
| assertEquals(NetworkAgent.EVENT_NETWORK_INFO_CHANGED, message.what); |
| NetworkInfo networkInfo = (NetworkInfo) message.obj; |
| assertEquals(NetworkInfo.DetailedState.DISCONNECTED, networkInfo.getDetailedState()); |
| } |
| |
| private void expectNetworkAgentUpdateCapabilities( |
| Consumer<NetworkCapabilities> networkCapabilitiesChecker) { |
| // We cannot just use a mock object here because mWifiNetworkAgent is private to CMI. |
| // TODO (b/134538181): consider exposing WifiNetworkAgent and using mocks. |
| ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class); |
| mLooper.dispatchAll(); |
| verify(mNetworkAgentHandler).handleMessage(messageCaptor.capture()); |
| Message message = messageCaptor.getValue(); |
| assertNotNull(message); |
| assertEquals(NetworkAgent.EVENT_NETWORK_CAPABILITIES_CHANGED, message.what); |
| networkCapabilitiesChecker.accept((NetworkCapabilities) message.obj); |
| } |
| |
| /** |
| * Verify that when a network is explicitly selected, but noInternetAccessExpected is false, |
| * the {@link NetworkAgentConfig} contains the right values of explicitlySelected, |
| * acceptUnvalidated and acceptPartialConnectivity. |
| */ |
| @Test |
| public void testExplicitlySelected_ExplicitInternetExpected() throws Exception { |
| // Network is explicitly selected. |
| WifiConfiguration config = makeLastSelectedWifiConfiguration(FRAMEWORK_NETWORK_ID, |
| ClientModeImpl.LAST_SELECTED_NETWORK_EXPIRATION_AGE_MILLIS - 1); |
| mConnectedNetwork.noInternetAccessExpected = false; |
| |
| connect(); |
| expectRegisterNetworkAgent((agentConfig) -> { |
| assertTrue(agentConfig.explicitlySelected); |
| assertFalse(agentConfig.acceptUnvalidated); |
| assertFalse(agentConfig.acceptPartialConnectivity); |
| }, (cap) -> { }); |
| } |
| |
| /** |
| * Verify that when a network is not explicitly selected, but noInternetAccessExpected is true, |
| * the {@link NetworkAgentConfig} contains the right values of explicitlySelected, |
| * acceptUnvalidated and acceptPartialConnectivity. |
| */ |
| @Test |
| public void testExplicitlySelected_NotExplicitNoInternetExpected() throws Exception { |
| // Network is no longer explicitly selected. |
| WifiConfiguration config = makeLastSelectedWifiConfiguration(FRAMEWORK_NETWORK_ID, |
| ClientModeImpl.LAST_SELECTED_NETWORK_EXPIRATION_AGE_MILLIS + 1); |
| mConnectedNetwork.noInternetAccessExpected = true; |
| |
| connect(); |
| expectRegisterNetworkAgent((agentConfig) -> { |
| assertFalse(agentConfig.explicitlySelected); |
| assertFalse(agentConfig.acceptUnvalidated); |
| assertTrue(agentConfig.acceptPartialConnectivity); |
| }, (cap) -> { }); |
| } |
| |
| /** |
| * Verify that when a network is explicitly selected, and noInternetAccessExpected is true, |
| * the {@link NetworkAgentConfig} contains the right values of explicitlySelected, |
| * acceptUnvalidated and acceptPartialConnectivity. |
| */ |
| @Test |
| public void testExplicitlySelected_ExplicitNoInternetExpected() throws Exception { |
| // Network is explicitly selected. |
| WifiConfiguration config = makeLastSelectedWifiConfiguration(FRAMEWORK_NETWORK_ID, |
| ClientModeImpl.LAST_SELECTED_NETWORK_EXPIRATION_AGE_MILLIS - 1); |
| mConnectedNetwork.noInternetAccessExpected = true; |
| |
| connect(); |
| expectRegisterNetworkAgent((agentConfig) -> { |
| assertTrue(agentConfig.explicitlySelected); |
| assertTrue(agentConfig.acceptUnvalidated); |
| assertTrue(agentConfig.acceptPartialConnectivity); |
| }, (cap) -> { }); |
| } |
| |
| /** |
| * Verify that CMI dump includes WakeupController. |
| */ |
| @Test |
| public void testDumpShouldDumpWakeupController() { |
| ByteArrayOutputStream stream = new ByteArrayOutputStream(); |
| PrintWriter writer = new PrintWriter(stream); |
| mCmi.dump(null, writer, null); |
| verify(mWakeupController).dump(null, writer, null); |
| } |
| |
| @Test |
| public void takeBugReportCallsWifiDiagnostics() { |
| mCmi.takeBugReport(anyString(), anyString()); |
| verify(mWifiDiagnostics).takeBugReport(anyString(), anyString()); |
| } |
| |
| /** |
| * Verify that Rssi Monitoring is started and the callback registered after connecting. |
| */ |
| @Test |
| public void verifyRssiMonitoringCallbackIsRegistered() throws Exception { |
| // Simulate the first connection. |
| connect(); |
| ArgumentCaptor<Messenger> messengerCaptor = ArgumentCaptor.forClass(Messenger.class); |
| verify(mConnectivityManager).registerNetworkAgent(messengerCaptor.capture(), |
| any(NetworkInfo.class), any(LinkProperties.class), any(NetworkCapabilities.class), |
| anyInt(), any(NetworkAgentConfig.class), anyInt()); |
| |
| ArrayList<Integer> thresholdsArray = new ArrayList<>(); |
| thresholdsArray.add(RSSI_THRESHOLD_MAX); |
| thresholdsArray.add(RSSI_THRESHOLD_MIN); |
| Bundle thresholds = new Bundle(); |
| thresholds.putIntegerArrayList("thresholds", thresholdsArray); |
| Message message = new Message(); |
| message.what = NetworkAgent.CMD_SET_SIGNAL_STRENGTH_THRESHOLDS; |
| message.obj = thresholds; |
| messengerCaptor.getValue().send(message); |
| mLooper.dispatchAll(); |
| |
| ArgumentCaptor<WifiNative.WifiRssiEventHandler> rssiEventHandlerCaptor = |
| ArgumentCaptor.forClass(WifiNative.WifiRssiEventHandler.class); |
| verify(mWifiNative).startRssiMonitoring(anyString(), anyByte(), anyByte(), |
| rssiEventHandlerCaptor.capture()); |
| |
| // breach below min |
| rssiEventHandlerCaptor.getValue().onRssiThresholdBreached(RSSI_THRESHOLD_BREACH_MIN); |
| mLooper.dispatchAll(); |
| WifiInfo wifiInfo = mCmi.getWifiInfo(); |
| assertEquals(RSSI_THRESHOLD_BREACH_MIN, wifiInfo.getRssi()); |
| |
| // breach above max |
| rssiEventHandlerCaptor.getValue().onRssiThresholdBreached(RSSI_THRESHOLD_BREACH_MAX); |
| mLooper.dispatchAll(); |
| assertEquals(RSSI_THRESHOLD_BREACH_MAX, wifiInfo.getRssi()); |
| } |
| |
| /** |
| * Verify that RSSI and link layer stats polling works in connected mode |
| */ |
| @Test |
| public void verifyConnectedModeRssiPolling() throws Exception { |
| final long startMillis = 1_500_000_000_100L; |
| WifiLinkLayerStats llStats = new WifiLinkLayerStats(); |
| llStats.txmpdu_be = 1000; |
| llStats.rxmpdu_bk = 2000; |
| WifiNl80211Manager.SignalPollResult signalPollResult = |
| new WifiNl80211Manager.SignalPollResult(-42, 65, 54, sFreq); |
| when(mWifiNative.getWifiLinkLayerStats(any())).thenReturn(llStats); |
| when(mWifiNative.signalPoll(any())).thenReturn(signalPollResult); |
| when(mClock.getWallClockMillis()).thenReturn(startMillis + 0); |
| mCmi.enableRssiPolling(true); |
| connect(); |
| mLooper.dispatchAll(); |
| when(mClock.getWallClockMillis()).thenReturn(startMillis + 3333); |
| mLooper.dispatchAll(); |
| WifiInfo wifiInfo = mCmi.getWifiInfo(); |
| assertEquals(llStats.txmpdu_be, wifiInfo.txSuccess); |
| assertEquals(llStats.rxmpdu_bk, wifiInfo.rxSuccess); |
| assertEquals(signalPollResult.currentRssiDbm, wifiInfo.getRssi()); |
| assertEquals(signalPollResult.txBitrateMbps, wifiInfo.getLinkSpeed()); |
| assertEquals(signalPollResult.txBitrateMbps, wifiInfo.getTxLinkSpeedMbps()); |
| assertEquals(signalPollResult.rxBitrateMbps, wifiInfo.getRxLinkSpeedMbps()); |
| assertEquals(sFreq, wifiInfo.getFrequency()); |
| verify(mWifiDataStall, atLeastOnce()).getTxThroughputKbps(); |
| verify(mWifiDataStall, atLeastOnce()).getRxThroughputKbps(); |
| verify(mWifiScoreCard).noteSignalPoll(any()); |
| } |
| |
| /** |
| * Verify RSSI polling with verbose logging |
| */ |
| @Test |
| public void verifyConnectedModeRssiPollingWithVerboseLogging() throws Exception { |
| mCmi.enableVerboseLogging(1); |
| verifyConnectedModeRssiPolling(); |
| } |
| |
| /** |
| * Verify that calls to start and stop filtering multicast packets are passed on to the IpClient |
| * instance. |
| */ |
| @Test |
| public void verifyMcastLockManagerFilterControllerCallsUpdateIpClient() throws Exception { |
| loadComponentsInStaMode(); |
| reset(mIpClient); |
| WifiMulticastLockManager.FilterController filterController = |
| mCmi.getMcastLockManagerFilterController(); |
| filterController.startFilteringMulticastPackets(); |
| verify(mIpClient).setMulticastFilter(eq(true)); |
| filterController.stopFilteringMulticastPackets(); |
| verify(mIpClient).setMulticastFilter(eq(false)); |
| } |
| |
| /** |
| * Verifies that when |
| * 1. Global feature support flag is set to false |
| * 2. connected MAC randomization is on and |
| * 3. macRandomizationSetting of the WifiConfiguration is RANDOMIZATION_PERSISTENT and |
| * 4. randomized MAC for the network to connect to is different from the current MAC. |
| * |
| * The factory MAC address is used for the connection, and no attempt is made to change it. |
| */ |
| @Test |
| public void testConnectedMacRandomizationNotSupported() throws Exception { |
| mResources.setBoolean(R.bool.config_wifi_connected_mac_randomization_supported, false); |
| initializeCmi(); |
| initializeAndAddNetworkAndVerifySuccess(); |
| assertEquals(ClientModeImpl.CONNECT_MODE, mCmi.getOperationalModeForTest()); |
| assertEquals(WifiManager.WIFI_STATE_ENABLED, mCmi.syncGetWifiState()); |
| |
| connect(); |
| assertEquals(TEST_GLOBAL_MAC_ADDRESS.toString(), mCmi.getWifiInfo().getMacAddress()); |
| verify(mWifiNative, never()).setMacAddress(any(), any()); |
| verify(mWifiNative, never()).getFactoryMacAddress(any()); |
| } |
| |
| /** |
| * Verifies that when |
| * 1. connected MAC randomization is on and |
| * 2. macRandomizationSetting of the WifiConfiguration is RANDOMIZATION_PERSISTENT and |
| * 3. randomized MAC for the network to connect to is different from the current MAC. |
| * |
| * Then the current MAC gets set to the randomized MAC when CMD_START_CONNECT executes. |
| */ |
| @Test |
| public void testConnectedMacRandomizationRandomizationPersistentDifferentMac() |
| throws Exception { |
| initializeAndAddNetworkAndVerifySuccess(); |
| assertEquals(ClientModeImpl.CONNECT_MODE, mCmi.getOperationalModeForTest()); |
| assertEquals(WifiManager.WIFI_STATE_ENABLED, mCmi.syncGetWifiState()); |
| |
| connect(); |
| verify(mWifiNative).setMacAddress(WIFI_IFACE_NAME, TEST_LOCAL_MAC_ADDRESS); |
| verify(mWifiMetrics) |
| .logStaEvent(eq(StaEvent.TYPE_MAC_CHANGE), any(WifiConfiguration.class)); |
| assertEquals(TEST_LOCAL_MAC_ADDRESS.toString(), mCmi.getWifiInfo().getMacAddress()); |
| } |
| |
| /** |
| * Verifies that when |
| * 1. connected MAC randomization is on and |
| * 2. macRandomizationSetting of the WifiConfiguration is RANDOMIZATION_PERSISTENT and |
| * 3. randomized MAC for the network to connect to is same as the current MAC. |
| * |
| * Then MAC change should not occur when CMD_START_CONNECT executes. |
| */ |
| @Test |
| public void testConnectedMacRandomizationRandomizationPersistentSameMac() throws Exception { |
| initializeAndAddNetworkAndVerifySuccess(); |
| assertEquals(ClientModeImpl.CONNECT_MODE, mCmi.getOperationalModeForTest()); |
| assertEquals(WifiManager.WIFI_STATE_ENABLED, mCmi.syncGetWifiState()); |
| |
| when(mWifiNative.getMacAddress(WIFI_IFACE_NAME)) |
| .thenReturn(TEST_LOCAL_MAC_ADDRESS.toString()); |
| |
| connect(); |
| verify(mWifiNative, never()).setMacAddress(WIFI_IFACE_NAME, TEST_LOCAL_MAC_ADDRESS); |
| verify(mWifiMetrics, never()) |
| .logStaEvent(eq(StaEvent.TYPE_MAC_CHANGE), any(WifiConfiguration.class)); |
| assertEquals(TEST_LOCAL_MAC_ADDRESS.toString(), mCmi.getWifiInfo().getMacAddress()); |
| } |
| |
| /** |
| * Verifies that when |
| * 1. connected MAC randomization is on and |
| * 2. macRandomizationSetting of the WifiConfiguration is RANDOMIZATION_NONE and |
| * 3. current MAC address is not the factory MAC. |
| * |
| * Then the current MAC gets set to the factory MAC when CMD_START_CONNECT executes. |
| * @throws Exception |
| */ |
| @Test |
| public void testConnectedMacRandomizationRandomizationNoneDifferentMac() throws Exception { |
| initializeAndAddNetworkAndVerifySuccess(); |
| assertEquals(ClientModeImpl.CONNECT_MODE, mCmi.getOperationalModeForTest()); |
| assertEquals(WifiManager.WIFI_STATE_ENABLED, mCmi.syncGetWifiState()); |
| |
| when(mWifiNative.getMacAddress(WIFI_IFACE_NAME)) |
| .thenReturn(TEST_LOCAL_MAC_ADDRESS.toString()); |
| |
| WifiConfiguration config = new WifiConfiguration(); |
| config.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_NONE; |
| config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); |
| when(mWifiConfigManager.getConfiguredNetworkWithoutMasking(0)).thenReturn(config); |
| |
| mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, 0, 0, sBSSID); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiNative).setMacAddress(WIFI_IFACE_NAME, TEST_GLOBAL_MAC_ADDRESS); |
| verify(mWifiMetrics) |
| .logStaEvent(eq(StaEvent.TYPE_MAC_CHANGE), any(WifiConfiguration.class)); |
| assertEquals(TEST_GLOBAL_MAC_ADDRESS.toString(), mCmi.getWifiInfo().getMacAddress()); |
| } |
| |
| /** |
| * Verifies that when |
| * 1. connected MAC randomization is on and |
| * 2. macRandomizationSetting of the WifiConfiguration is RANDOMIZATION_NONE and |
| * |
| * Then the factory MAC should be used to connect to the network. |
| * @throws Exception |
| */ |
| @Test |
| public void testConnectedMacRandomizationRandomizationNoneSameMac() throws Exception { |
| initializeAndAddNetworkAndVerifySuccess(); |
| assertEquals(ClientModeImpl.CONNECT_MODE, mCmi.getOperationalModeForTest()); |
| assertEquals(WifiManager.WIFI_STATE_ENABLED, mCmi.syncGetWifiState()); |
| |
| WifiConfiguration config = new WifiConfiguration(); |
| config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); |
| config.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_NONE; |
| when(mWifiConfigManager.getConfiguredNetworkWithoutMasking(0)).thenReturn(config); |
| |
| mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, 0, 0, sBSSID); |
| mLooper.dispatchAll(); |
| |
| assertEquals(TEST_GLOBAL_MAC_ADDRESS.toString(), mCmi.getWifiInfo().getMacAddress()); |
| } |
| |
| /** |
| * Verifies that WifiInfo returns DEFAULT_MAC_ADDRESS as mac address when Connected MAC |
| * Randomization is on and the device is not connected to a wifi network. |
| */ |
| @Test |
| public void testWifiInfoReturnDefaultMacWhenDisconnectedWithRandomization() throws Exception { |
| when(mWifiNative.getMacAddress(WIFI_IFACE_NAME)) |
| .thenReturn(TEST_LOCAL_MAC_ADDRESS.toString()); |
| |
| connect(); |
| assertEquals(TEST_LOCAL_MAC_ADDRESS.toString(), mCmi.getWifiInfo().getMacAddress()); |
| |
| mCmi.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, -1, 3, sBSSID); |
| mLooper.dispatchAll(); |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(0, sWifiSsid, sBSSID, SupplicantState.DISCONNECTED)); |
| mLooper.dispatchAll(); |
| |
| assertEquals("DisconnectedState", getCurrentState().getName()); |
| assertEquals(WifiInfo.DEFAULT_MAC_ADDRESS, mCmi.getWifiInfo().getMacAddress()); |
| assertFalse(mCmi.getWifiInfo().hasRealMacAddress()); |
| } |
| |
| /** |
| * Verifies that we don't set MAC address when config returns an invalid MAC address. |
| */ |
| @Test |
| public void testDoNotSetMacWhenInvalid() throws Exception { |
| initializeAndAddNetworkAndVerifySuccess(); |
| assertEquals(ClientModeImpl.CONNECT_MODE, mCmi.getOperationalModeForTest()); |
| assertEquals(WifiManager.WIFI_STATE_ENABLED, mCmi.syncGetWifiState()); |
| |
| WifiConfiguration config = new WifiConfiguration(); |
| config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); |
| config.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_PERSISTENT; |
| config.setRandomizedMacAddress(MacAddress.fromString(WifiInfo.DEFAULT_MAC_ADDRESS)); |
| when(mWifiConfigManager.getConfiguredNetworkWithoutMasking(0)).thenReturn(config); |
| |
| mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, 0, 0, sBSSID); |
| mLooper.dispatchAll(); |
| |
| // setMacAddress is invoked once when ClientModeImpl starts to prevent leak of factory MAC. |
| verify(mWifiNative).setMacAddress(eq(WIFI_IFACE_NAME), any(MacAddress.class)); |
| } |
| |
| /** |
| * Verify that we don't crash when WifiNative returns null as the current MAC address. |
| * @throws Exception |
| */ |
| @Test |
| public void testMacRandomizationWifiNativeReturningNull() throws Exception { |
| when(mWifiNative.getMacAddress(anyString())).thenReturn(null); |
| initializeAndAddNetworkAndVerifySuccess(); |
| assertEquals(ClientModeImpl.CONNECT_MODE, mCmi.getOperationalModeForTest()); |
| assertEquals(WifiManager.WIFI_STATE_ENABLED, mCmi.syncGetWifiState()); |
| |
| connect(); |
| verify(mWifiNative).setMacAddress(WIFI_IFACE_NAME, TEST_LOCAL_MAC_ADDRESS); |
| } |
| |
| /** |
| * Verifies that a notification is posted when a connection failure happens on a network |
| * in the hotlist. Then verify that tapping on the notification launches an dialog, which |
| * could be used to set the randomization setting for a network to "Trusted". |
| */ |
| @Test |
| public void testConnectionFailureSendRandomizationSettingsNotification() throws Exception { |
| when(mWifiConfigManager.isInFlakyRandomizationSsidHotlist(anyInt())).thenReturn(true); |
| // Setup CONNECT_MODE & a WifiConfiguration |
| initializeAndAddNetworkAndVerifySuccess(); |
| mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, FRAMEWORK_NETWORK_ID, 0, sBSSID); |
| mCmi.sendMessage(WifiMonitor.AUTHENTICATION_FAILURE_EVENT, |
| WifiManager.ERROR_AUTH_FAILURE_TIMEOUT); |
| mLooper.dispatchAll(); |
| |
| WifiConfiguration config = mCmi.getCurrentWifiConfiguration(); |
| verify(mConnectionFailureNotifier) |
| .showFailedToConnectDueToNoRandomizedMacSupportNotification(FRAMEWORK_NETWORK_ID); |
| } |
| |
| /** |
| * Verifies that a notification is not posted when a wrong password failure happens on a |
| * network in the hotlist. |
| */ |
| @Test |
| public void testNotCallingIsInFlakyRandomizationSsidHotlistOnWrongPassword() throws Exception { |
| when(mWifiConfigManager.isInFlakyRandomizationSsidHotlist(anyInt())).thenReturn(true); |
| // Setup CONNECT_MODE & a WifiConfiguration |
| initializeAndAddNetworkAndVerifySuccess(); |
| mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, FRAMEWORK_NETWORK_ID, 0, sBSSID); |
| mCmi.sendMessage(WifiMonitor.AUTHENTICATION_FAILURE_EVENT, |
| WifiManager.ERROR_AUTH_FAILURE_WRONG_PSWD); |
| mLooper.dispatchAll(); |
| |
| verify(mConnectionFailureNotifier, never()) |
| .showFailedToConnectDueToNoRandomizedMacSupportNotification(anyInt()); |
| } |
| |
| /** |
| * Verifies that CMD_START_CONNECT make WifiDiagnostics report |
| * CONNECTION_EVENT_STARTED |
| * @throws Exception |
| */ |
| @Test |
| public void testReportConnectionEventIsCalledAfterCmdStartConnect() throws Exception { |
| // Setup CONNECT_MODE & a WifiConfiguration |
| initializeAndAddNetworkAndVerifySuccess(); |
| mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, 0, 0, sBSSID); |
| verify(mWifiDiagnostics, never()).reportConnectionEvent( |
| eq(WifiDiagnostics.CONNECTION_EVENT_STARTED)); |
| mLooper.dispatchAll(); |
| verify(mWifiDiagnostics).reportConnectionEvent( |
| eq(WifiDiagnostics.CONNECTION_EVENT_STARTED)); |
| } |
| |
| /** |
| * Verifies that CMD_DIAG_CONNECT_TIMEOUT is processed after the timeout threshold if we |
| * start a connection but do not finish it. |
| * @throws Exception |
| */ |
| @Test |
| public void testCmdDiagsConnectTimeoutIsGeneratedAfterCmdStartConnect() throws Exception { |
| // Setup CONNECT_MODE & a WifiConfiguration |
| initializeAndAddNetworkAndVerifySuccess(); |
| mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, 0, 0, sBSSID); |
| mLooper.dispatchAll(); |
| mLooper.moveTimeForward(ClientModeImpl.DIAGS_CONNECT_TIMEOUT_MILLIS); |
| mLooper.dispatchAll(); |
| verify(mWifiDiagnostics).reportConnectionEvent( |
| eq(BaseWifiDiagnostics.CONNECTION_EVENT_TIMEOUT)); |
| } |
| |
| /** |
| * Verifies that CMD_DIAG_CONNECT_TIMEOUT does not get processed before the timeout threshold. |
| * @throws Exception |
| */ |
| @Test |
| public void testCmdDiagsConnectTimeoutIsNotProcessedBeforeTimerExpires() throws Exception { |
| // Setup CONNECT_MODE & a WifiConfiguration |
| initializeAndAddNetworkAndVerifySuccess(); |
| mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, 0, 0, sBSSID); |
| mLooper.dispatchAll(); |
| mLooper.moveTimeForward(ClientModeImpl.DIAGS_CONNECT_TIMEOUT_MILLIS - 1000); |
| mLooper.dispatchAll(); |
| verify(mWifiDiagnostics, never()).reportConnectionEvent( |
| eq(BaseWifiDiagnostics.CONNECTION_EVENT_TIMEOUT)); |
| } |
| |
| private void verifyConnectionEventTimeoutDoesNotOccur() { |
| mLooper.moveTimeForward(ClientModeImpl.DIAGS_CONNECT_TIMEOUT_MILLIS); |
| mLooper.dispatchAll(); |
| verify(mWifiDiagnostics, never()).reportConnectionEvent( |
| eq(BaseWifiDiagnostics.CONNECTION_EVENT_TIMEOUT)); |
| } |
| |
| /** |
| * Verifies that association failures make WifiDiagnostics report CONNECTION_EVENT_FAILED |
| * and then cancel any pending timeouts. |
| * Also, send connection status to {@link WifiNetworkFactory} & {@link WifiConnectivityManager}. |
| * @throws Exception |
| */ |
| @Test |
| public void testReportConnectionEventIsCalledAfterAssociationFailure() throws Exception { |
| mConnectedNetwork.getNetworkSelectionStatus() |
| .setCandidate(getGoogleGuestScanDetail(TEST_RSSI, sBSSID, sFreq).getScanResult()); |
| // Setup CONNECT_MODE & a WifiConfiguration |
| initializeAndAddNetworkAndVerifySuccess(); |
| mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, 0, 0, sBSSID); |
| mCmi.sendMessage(WifiMonitor.ASSOCIATION_REJECTION_EVENT, 0, |
| ISupplicantStaIfaceCallback.StatusCode.AP_UNABLE_TO_HANDLE_NEW_STA, sBSSID); |
| verify(mWifiDiagnostics, never()).reportConnectionEvent( |
| eq(WifiDiagnostics.CONNECTION_EVENT_FAILED)); |
| mLooper.dispatchAll(); |
| verify(mWifiDiagnostics).reportConnectionEvent( |
| eq(WifiDiagnostics.CONNECTION_EVENT_FAILED)); |
| verify(mWifiConnectivityManager).handleConnectionAttemptEnded( |
| WifiMetrics.ConnectionEvent.FAILURE_ASSOCIATION_REJECTION, sBSSID, sSSID); |
| verify(mWifiNetworkFactory).handleConnectionAttemptEnded( |
| eq(WifiMetrics.ConnectionEvent.FAILURE_ASSOCIATION_REJECTION), |
| any(WifiConfiguration.class)); |
| verify(mWifiNetworkSuggestionsManager).handleConnectionAttemptEnded( |
| eq(WifiMetrics.ConnectionEvent.FAILURE_ASSOCIATION_REJECTION), |
| any(WifiConfiguration.class), eq(null)); |
| verify(mWifiMetrics, never()) |
| .incrementNumBssidDifferentSelectionBetweenFrameworkAndFirmware(); |
| verifyConnectionEventTimeoutDoesNotOccur(); |
| } |
| |
| /** |
| * Verifies that authentication failures make WifiDiagnostics report |
| * CONNECTION_EVENT_FAILED and then cancel any pending timeouts. |
| * Also, send connection status to {@link WifiNetworkFactory} & {@link WifiConnectivityManager}. |
| * @throws Exception |
| */ |
| @Test |
| public void testReportConnectionEventIsCalledAfterAuthenticationFailure() throws Exception { |
| mConnectedNetwork.getNetworkSelectionStatus() |
| .setCandidate(getGoogleGuestScanDetail(TEST_RSSI, sBSSID, sFreq).getScanResult()); |
| // Setup CONNECT_MODE & a WifiConfiguration |
| initializeAndAddNetworkAndVerifySuccess(); |
| mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, 0, 0, sBSSID); |
| mCmi.sendMessage(WifiMonitor.AUTHENTICATION_FAILURE_EVENT, |
| WifiManager.ERROR_AUTH_FAILURE_WRONG_PSWD); |
| verify(mWifiDiagnostics, never()).reportConnectionEvent( |
| eq(WifiDiagnostics.CONNECTION_EVENT_FAILED)); |
| mLooper.dispatchAll(); |
| verify(mWifiDiagnostics).reportConnectionEvent( |
| eq(WifiDiagnostics.CONNECTION_EVENT_FAILED)); |
| verify(mWifiConnectivityManager).handleConnectionAttemptEnded( |
| WifiMetrics.ConnectionEvent.FAILURE_AUTHENTICATION_FAILURE, sBSSID, sSSID); |
| verify(mWifiNetworkFactory).handleConnectionAttemptEnded( |
| eq(WifiMetrics.ConnectionEvent.FAILURE_AUTHENTICATION_FAILURE), |
| any(WifiConfiguration.class)); |
| verify(mWifiNetworkSuggestionsManager).handleConnectionAttemptEnded( |
| eq(WifiMetrics.ConnectionEvent.FAILURE_AUTHENTICATION_FAILURE), |
| any(WifiConfiguration.class), eq(null)); |
| verify(mWifiMetrics, never()) |
| .incrementNumBssidDifferentSelectionBetweenFrameworkAndFirmware(); |
| verifyConnectionEventTimeoutDoesNotOccur(); |
| } |
| |
| /** |
| * Verify that if a NETWORK_DISCONNECTION_EVENT is received in ConnectedState, then an |
| * abnormal disconnect is reported to BssidBlocklistMonitor. |
| */ |
| @Test |
| public void testAbnormalDisconnectNotifiesBssidBlocklistMonitor() throws Exception { |
| // trigger RSSI poll to update WifiInfo |
| mCmi.enableRssiPolling(true); |
| WifiLinkLayerStats llStats = new WifiLinkLayerStats(); |
| llStats.txmpdu_be = 1000; |
| llStats.rxmpdu_bk = 2000; |
| WifiNl80211Manager.SignalPollResult signalPollResult = |
| new WifiNl80211Manager.SignalPollResult(TEST_RSSI, 65, 54, sFreq); |
| when(mWifiNative.getWifiLinkLayerStats(any())).thenReturn(llStats); |
| when(mWifiNative.signalPoll(any())).thenReturn(signalPollResult); |
| |
| connect(); |
| mLooper.dispatchAll(); |
| mCmi.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, 0, 0, sBSSID); |
| mLooper.dispatchAll(); |
| |
| verify(mBssidBlocklistMonitor).handleBssidConnectionFailure(eq(sBSSID), eq(sSSID), |
| eq(BssidBlocklistMonitor.REASON_ABNORMAL_DISCONNECT), anyInt()); |
| } |
| |
| /** |
| * Verify that ClientModeImpl notifies BssidBlocklistMonitor correctly when the RSSI is |
| * too low. |
| */ |
| @Test |
| public void testNotifiesBssidBlocklistMonitorLowRssi() throws Exception { |
| int testLowRssi = -80; |
| initializeAndAddNetworkAndVerifySuccess(); |
| mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, FRAMEWORK_NETWORK_ID, 0, |
| ClientModeImpl.SUPPLICANT_BSSID_ANY); |
| mCmi.sendMessage(WifiMonitor.ASSOCIATION_REJECTION_EVENT, 1, 0, sBSSID); |
| when(mWifiConfigManager.findScanRssi(eq(FRAMEWORK_NETWORK_ID), anyInt())).thenReturn(-80); |
| when(mWifiConfigManager.getScanDetailCacheForNetwork(FRAMEWORK_NETWORK_ID)) |
| .thenReturn(mScanDetailCache); |
| when(mScanDetailCache.getScanDetail(sBSSID)).thenReturn( |
| getGoogleGuestScanDetail(testLowRssi, sBSSID, sFreq)); |
| when(mScanDetailCache.getScanResult(sBSSID)).thenReturn( |
| getGoogleGuestScanDetail(testLowRssi, sBSSID, sFreq).getScanResult()); |
| mLooper.dispatchAll(); |
| |
| verify(mBssidBlocklistMonitor).handleBssidConnectionFailure(sBSSID, sSSID, |
| BssidBlocklistMonitor.REASON_ASSOCIATION_TIMEOUT, testLowRssi); |
| } |
| |
| /** |
| * Verifies that the BssidBlocklistMonitor is notified, but the WifiLastResortWatchdog is |
| * not notified of association rejections of type REASON_CODE_AP_UNABLE_TO_HANDLE_NEW_STA. |
| * @throws Exception |
| */ |
| @Test |
| public void testAssociationRejectionWithReasonApUnableToHandleNewStaUpdatesWatchdog() |
| throws Exception { |
| initializeAndAddNetworkAndVerifySuccess(); |
| mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, 0, 0, sBSSID); |
| mCmi.sendMessage(WifiMonitor.ASSOCIATION_REJECTION_EVENT, 0, |
| ClientModeImpl.REASON_CODE_AP_UNABLE_TO_HANDLE_NEW_STA, sBSSID); |
| mLooper.dispatchAll(); |
| verify(mWifiLastResortWatchdog, never()).noteConnectionFailureAndTriggerIfNeeded( |
| anyString(), anyString(), anyInt()); |
| verify(mBssidBlocklistMonitor).handleBssidConnectionFailure(eq(sBSSID), eq(sSSID), |
| eq(BssidBlocklistMonitor.REASON_AP_UNABLE_TO_HANDLE_NEW_STA), anyInt()); |
| } |
| |
| /** |
| * Verifies that WifiLastResortWatchdog and BssidBlocklistMonitor is notified of |
| * general association rejection failures. |
| * @throws Exception |
| */ |
| @Test |
| public void testAssociationRejectionUpdatesWatchdog() throws Exception { |
| initializeAndAddNetworkAndVerifySuccess(); |
| WifiConfiguration config = mWifiConfigManager.getConfiguredNetwork(FRAMEWORK_NETWORK_ID); |
| config.carrierId = CARRIER_ID_1; |
| mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, 0, 0, sBSSID); |
| mCmi.sendMessage(WifiMonitor.ASSOCIATION_REJECTION_EVENT, 0, 0, sBSSID); |
| mLooper.dispatchAll(); |
| verify(mWifiLastResortWatchdog).noteConnectionFailureAndTriggerIfNeeded( |
| anyString(), anyString(), anyInt()); |
| verify(mBssidBlocklistMonitor).handleBssidConnectionFailure(eq(sBSSID), eq(sSSID), |
| eq(BssidBlocklistMonitor.REASON_ASSOCIATION_REJECTION), anyInt()); |
| verify(mWifiMetrics).incrementNumOfCarrierWifiConnectionNonAuthFailure(); |
| } |
| |
| /** |
| * Verifies that WifiLastResortWatchdog is not notified of authentication failures of type |
| * ERROR_AUTH_FAILURE_WRONG_PSWD. |
| * @throws Exception |
| */ |
| @Test |
| public void testFailureWrongPassIsIgnoredByWatchdog() throws Exception { |
| // Setup CONNECT_MODE & a WifiConfiguration |
| initializeAndAddNetworkAndVerifySuccess(); |
| mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, 0, 0, sBSSID); |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(0, sWifiSsid, sBSSID, SupplicantState.COMPLETED)); |
| mCmi.sendMessage(WifiMonitor.AUTHENTICATION_FAILURE_EVENT, |
| WifiManager.ERROR_AUTH_FAILURE_WRONG_PSWD); |
| mLooper.dispatchAll(); |
| verify(mWifiLastResortWatchdog, never()).noteConnectionFailureAndTriggerIfNeeded( |
| anyString(), anyString(), anyInt()); |
| verify(mBssidBlocklistMonitor).handleBssidConnectionFailure(eq(sBSSID), eq(sSSID), |
| eq(BssidBlocklistMonitor.REASON_WRONG_PASSWORD), anyInt()); |
| } |
| |
| /** |
| * Verifies that WifiLastResortWatchdog is not notified of authentication failures of type |
| * ERROR_AUTH_FAILURE_EAP_FAILURE. |
| * @throws Exception |
| */ |
| @Test |
| public void testEapFailureIsIgnoredByWatchdog() throws Exception { |
| // Setup CONNECT_MODE & a WifiConfiguration |
| initializeAndAddNetworkAndVerifySuccess(); |
| mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, 0, 0, sBSSID); |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(0, sWifiSsid, sBSSID, SupplicantState.COMPLETED)); |
| mCmi.sendMessage(WifiMonitor.AUTHENTICATION_FAILURE_EVENT, |
| WifiManager.ERROR_AUTH_FAILURE_EAP_FAILURE); |
| mLooper.dispatchAll(); |
| verify(mWifiLastResortWatchdog, never()).noteConnectionFailureAndTriggerIfNeeded( |
| anyString(), anyString(), anyInt()); |
| verify(mBssidBlocklistMonitor).handleBssidConnectionFailure(eq(sBSSID), eq(sSSID), |
| eq(BssidBlocklistMonitor.REASON_EAP_FAILURE), anyInt()); |
| } |
| |
| /** |
| * Verifies that WifiLastResortWatchdog is notified of other types of authentication failures. |
| * @throws Exception |
| */ |
| @Test |
| public void testAuthenticationFailureUpdatesWatchdog() throws Exception { |
| // Setup CONNECT_MODE & a WifiConfiguration |
| initializeAndAddNetworkAndVerifySuccess(); |
| mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, 0, 0, sBSSID); |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(0, sWifiSsid, sBSSID, SupplicantState.COMPLETED)); |
| mCmi.sendMessage(WifiMonitor.AUTHENTICATION_FAILURE_EVENT, |
| WifiManager.ERROR_AUTH_FAILURE_TIMEOUT); |
| mLooper.dispatchAll(); |
| verify(mWifiLastResortWatchdog).noteConnectionFailureAndTriggerIfNeeded( |
| anyString(), anyString(), anyInt()); |
| verify(mBssidBlocklistMonitor).handleBssidConnectionFailure(eq(sBSSID), eq(sSSID), |
| eq(BssidBlocklistMonitor.REASON_AUTHENTICATION_FAILURE), anyInt()); |
| } |
| |
| /** |
| * Verify that BssidBlocklistMonitor is notified of the SSID pre-connection so that it could |
| * send down to firmware the list of blocked BSSIDs. |
| */ |
| @Test |
| public void testBssidBlocklistSentToFirmwareAfterCmdStartConnect() throws Exception { |
| initializeAndAddNetworkAndVerifySuccess(); |
| mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, 0, 0, sBSSID); |
| verify(mBssidBlocklistMonitor, never()).updateFirmwareRoamingConfiguration(sSSID); |
| mLooper.dispatchAll(); |
| verify(mBssidBlocklistMonitor).updateFirmwareRoamingConfiguration(sSSID); |
| // But don't expect to see connection success yet |
| verify(mWifiScoreCard, never()).noteIpConfiguration(any()); |
| // And certainly not validation success |
| verify(mWifiScoreCard, never()).noteValidationSuccess(any()); |
| } |
| |
| /** |
| * Verifies that dhcp failures make WifiDiagnostics report CONNECTION_EVENT_FAILED and then |
| * cancel any pending timeouts. |
| * Also, send connection status to {@link WifiNetworkFactory} & {@link WifiConnectivityManager}. |
| * @throws Exception |
| */ |
| @Test |
| public void testReportConnectionEventIsCalledAfterDhcpFailure() throws Exception { |
| mConnectedNetwork.getNetworkSelectionStatus() |
| .setCandidate(getGoogleGuestScanDetail(TEST_RSSI, sBSSID, sFreq).getScanResult()); |
| testDhcpFailure(); |
| verify(mWifiDiagnostics, atLeastOnce()).reportConnectionEvent( |
| eq(WifiDiagnostics.CONNECTION_EVENT_FAILED)); |
| verify(mWifiConnectivityManager, atLeastOnce()).handleConnectionAttemptEnded( |
| WifiMetrics.ConnectionEvent.FAILURE_DHCP, sBSSID, sSSID); |
| verify(mWifiNetworkFactory, atLeastOnce()).handleConnectionAttemptEnded( |
| eq(WifiMetrics.ConnectionEvent.FAILURE_DHCP), any(WifiConfiguration.class)); |
| verify(mWifiNetworkSuggestionsManager, atLeastOnce()).handleConnectionAttemptEnded( |
| eq(WifiMetrics.ConnectionEvent.FAILURE_DHCP), any(WifiConfiguration.class), |
| any(String.class)); |
| verify(mWifiMetrics, never()) |
| .incrementNumBssidDifferentSelectionBetweenFrameworkAndFirmware(); |
| verifyConnectionEventTimeoutDoesNotOccur(); |
| } |
| |
| /** |
| * Verifies that a successful validation make WifiDiagnostics report CONNECTION_EVENT_SUCCEEDED |
| * and then cancel any pending timeouts. |
| * Also, send connection status to {@link WifiNetworkFactory} & {@link WifiConnectivityManager}. |
| * @throws Exception |
| */ |
| @Test |
| public void testReportConnectionEventIsCalledAfterSuccessfulConnection() throws Exception { |
| mConnectedNetwork.getNetworkSelectionStatus() |
| .setCandidate(getGoogleGuestScanDetail(TEST_RSSI, sBSSID1, sFreq).getScanResult()); |
| connect(); |
| ArgumentCaptor<Messenger> messengerCaptor = ArgumentCaptor.forClass(Messenger.class); |
| verify(mConnectivityManager).registerNetworkAgent(messengerCaptor.capture(), |
| any(NetworkInfo.class), any(LinkProperties.class), any(NetworkCapabilities.class), |
| anyInt(), any(NetworkAgentConfig.class), anyInt()); |
| |
| Message message = new Message(); |
| message.what = NetworkAgent.CMD_REPORT_NETWORK_STATUS; |
| message.arg1 = NetworkAgent.VALID_NETWORK; |
| message.obj = new Bundle(); |
| messengerCaptor.getValue().send(message); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiDiagnostics).reportConnectionEvent( |
| eq(WifiDiagnostics.CONNECTION_EVENT_SUCCEEDED)); |
| verify(mWifiConnectivityManager).handleConnectionAttemptEnded( |
| WifiMetrics.ConnectionEvent.FAILURE_NONE, sBSSID, sSSID); |
| verify(mWifiNetworkFactory).handleConnectionAttemptEnded( |
| eq(WifiMetrics.ConnectionEvent.FAILURE_NONE), any(WifiConfiguration.class)); |
| verify(mWifiNetworkSuggestionsManager).handleConnectionAttemptEnded( |
| eq(WifiMetrics.ConnectionEvent.FAILURE_NONE), any(WifiConfiguration.class), |
| any(String.class)); |
| // BSSID different, record this connection. |
| verify(mWifiMetrics).incrementNumBssidDifferentSelectionBetweenFrameworkAndFirmware(); |
| verifyConnectionEventTimeoutDoesNotOccur(); |
| } |
| |
| /** |
| * Verify that score card is notified of a connection attempt |
| */ |
| @Test |
| public void testScoreCardNoteConnectionAttemptAfterCmdStartConnect() throws Exception { |
| initializeAndAddNetworkAndVerifySuccess(); |
| mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, 0, 0, sBSSID); |
| verify(mWifiScoreCard, never()).noteConnectionAttempt(any(), anyInt(), anyString()); |
| mLooper.dispatchAll(); |
| verify(mWifiScoreCard).noteConnectionAttempt(any(), anyInt(), anyString()); |
| verify(mWifiConfigManager).findScanRssi(anyInt(), anyInt()); |
| // But don't expect to see connection success yet |
| verify(mWifiScoreCard, never()).noteIpConfiguration(any()); |
| // And certainly not validation success |
| verify(mWifiScoreCard, never()).noteValidationSuccess(any()); |
| |
| } |
| |
| /** |
| * Verify that score card is notified of a successful connection |
| */ |
| @Test |
| public void testScoreCardNoteConnectionComplete() throws Exception { |
| Pair<String, String> l2KeyAndCluster = Pair.create("Wad", "Gab"); |
| when(mWifiScoreCard.getL2KeyAndGroupHint(any())).thenReturn(l2KeyAndCluster); |
| connect(); |
| mLooper.dispatchAll(); |
| verify(mWifiScoreCard).noteIpConfiguration(any()); |
| ArgumentCaptor<Layer2InformationParcelable> captor = |
| ArgumentCaptor.forClass(Layer2InformationParcelable.class); |
| verify(mIpClient, atLeastOnce()).updateLayer2Information(captor.capture()); |
| final Layer2InformationParcelable info = captor.getValue(); |
| assertEquals(info.l2Key, "Wad"); |
| assertEquals(info.cluster, "Gab"); |
| } |
| |
| /** |
| * Verify that score card/health monitor are notified when wifi is disabled while disconnected |
| */ |
| @Test |
| public void testScoreCardNoteWifiDisabledWhileDisconnected() throws Exception { |
| // connecting and disconnecting shouldn't note wifi disabled |
| disconnect(); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiScoreCard, times(1)).resetConnectionState(); |
| verify(mWifiScoreCard, never()).noteWifiDisabled(any()); |
| verify(mWifiHealthMonitor, never()).setWifiEnabled(false); |
| |
| // disabling while disconnected should note wifi disabled |
| mCmi.setWifiStateForApiCalls(WifiManager.WIFI_STATE_DISABLED); |
| mCmi.setOperationalMode(ClientModeImpl.DISABLED_MODE, null); |
| mLooper.dispatchAll(); |
| verify(mWifiScoreCard, times(2)).resetConnectionState(); |
| verify(mWifiHealthMonitor).setWifiEnabled(false); |
| } |
| |
| /** |
| * Verify that score card/health monitor are notified when wifi is disabled while connected |
| */ |
| @Test |
| public void testScoreCardNoteWifiDisabledWhileConnected() throws Exception { |
| // Get into connected state |
| connect(); |
| mLooper.dispatchAll(); |
| verify(mWifiScoreCard, never()).noteWifiDisabled(any()); |
| verify(mWifiHealthMonitor, never()).setWifiEnabled(false); |
| |
| // disabling while connected should note wifi disabled |
| mCmi.setWifiStateForApiCalls(WifiManager.WIFI_STATE_DISABLED); |
| mCmi.setOperationalMode(ClientModeImpl.DISABLED_MODE, null); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiScoreCard).noteWifiDisabled(any()); |
| verify(mWifiScoreCard).resetConnectionState(); |
| verify(mWifiHealthMonitor).setWifiEnabled(false); |
| } |
| |
| /** |
| * Verify that we do not crash on quick toggling wifi on/off |
| */ |
| @Test |
| public void quickTogglesDoNotCrash() throws Exception { |
| mCmi.setOperationalMode(ClientModeImpl.CONNECT_MODE, WIFI_IFACE_NAME); |
| mCmi.setOperationalMode(ClientModeImpl.DISABLED_MODE, null); |
| mLooper.dispatchAll(); |
| |
| mCmi.setOperationalMode(ClientModeImpl.DISABLED_MODE, null); |
| mCmi.setOperationalMode(ClientModeImpl.CONNECT_MODE, WIFI_IFACE_NAME); |
| mLooper.dispatchAll(); |
| |
| mCmi.setOperationalMode(ClientModeImpl.DISABLED_MODE, null); |
| mCmi.setOperationalMode(ClientModeImpl.CONNECT_MODE, WIFI_IFACE_NAME); |
| mCmi.setOperationalMode(ClientModeImpl.DISABLED_MODE, null); |
| mCmi.setOperationalMode(ClientModeImpl.CONNECT_MODE, WIFI_IFACE_NAME); |
| mLooper.dispatchAll(); |
| |
| mCmi.setOperationalMode(ClientModeImpl.CONNECT_MODE, WIFI_IFACE_NAME); |
| mCmi.setOperationalMode(ClientModeImpl.DISABLED_MODE, null); |
| mCmi.setOperationalMode(ClientModeImpl.CONNECT_MODE, WIFI_IFACE_NAME); |
| mCmi.setOperationalMode(ClientModeImpl.DISABLED_MODE, null); |
| mLooper.dispatchAll(); |
| } |
| |
| /** |
| * Verify that valid calls to set the current wifi state are returned when requested. |
| */ |
| @Test |
| public void verifySetAndGetWifiStateCallsWorking() throws Exception { |
| // we start off disabled |
| assertEquals(WifiManager.WIFI_STATE_DISABLED, mCmi.syncGetWifiState()); |
| |
| // now check after updating |
| mCmi.setWifiStateForApiCalls(WifiManager.WIFI_STATE_UNKNOWN); |
| assertEquals(WifiManager.WIFI_STATE_UNKNOWN, mCmi.syncGetWifiState()); |
| |
| // check after two updates |
| mCmi.setWifiStateForApiCalls(WifiManager.WIFI_STATE_ENABLING); |
| mCmi.setWifiStateForApiCalls(WifiManager.WIFI_STATE_ENABLED); |
| assertEquals(WifiManager.WIFI_STATE_ENABLED, mCmi.syncGetWifiState()); |
| } |
| |
| /** |
| * Verify that invalid states do not change the saved wifi state. |
| */ |
| @Test |
| public void verifyInvalidStatesDoNotChangeSavedWifiState() throws Exception { |
| int invalidStateNegative = -1; |
| int invalidStatePositive = 5; |
| |
| // we start off disabled |
| assertEquals(WifiManager.WIFI_STATE_DISABLED, mCmi.syncGetWifiState()); |
| |
| mCmi.setWifiStateForApiCalls(invalidStateNegative); |
| assertEquals(WifiManager.WIFI_STATE_DISABLED, mCmi.syncGetWifiState()); |
| |
| mCmi.setWifiStateForApiCalls(invalidStatePositive); |
| assertEquals(WifiManager.WIFI_STATE_DISABLED, mCmi.syncGetWifiState()); |
| } |
| |
| /** |
| * Verify that IPClient instance is shutdown when wifi is disabled. |
| */ |
| @Test |
| public void verifyIpClientShutdownWhenDisabled() throws Exception { |
| loadComponentsInStaMode(); |
| |
| mCmi.setOperationalMode(ClientModeImpl.DISABLED_MODE, null); |
| mLooper.dispatchAll(); |
| verify(mIpClient).shutdown(); |
| verify(mWifiConfigManager).removeAllEphemeralOrPasspointConfiguredNetworks(); |
| verify(mWifiConfigManager).clearUserTemporarilyDisabledList(); |
| } |
| |
| /** |
| * Verify that WifiInfo's MAC address is updated when the state machine receives |
| * NETWORK_CONNECTION_EVENT while in ConnectedState. |
| */ |
| @Test |
| public void verifyWifiInfoMacUpdatedWithNetworkConnectionWhileConnected() throws Exception { |
| connect(); |
| assertEquals("ConnectedState", getCurrentState().getName()); |
| assertEquals(TEST_LOCAL_MAC_ADDRESS.toString(), mCmi.getWifiInfo().getMacAddress()); |
| |
| // Verify receiving a NETWORK_CONNECTION_EVENT changes the MAC in WifiInfo |
| when(mWifiNative.getMacAddress(WIFI_IFACE_NAME)) |
| .thenReturn(TEST_GLOBAL_MAC_ADDRESS.toString()); |
| mCmi.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, 0, 0, sBSSID); |
| mLooper.dispatchAll(); |
| assertEquals(TEST_GLOBAL_MAC_ADDRESS.toString(), mCmi.getWifiInfo().getMacAddress()); |
| } |
| |
| /** |
| * Verify that WifiInfo's MAC address is updated when the state machine receives |
| * NETWORK_CONNECTION_EVENT while in DisconnectedState. |
| */ |
| @Test |
| public void verifyWifiInfoMacUpdatedWithNetworkConnectionWhileDisconnected() throws Exception { |
| disconnect(); |
| assertEquals("DisconnectedState", getCurrentState().getName()); |
| // Since MAC randomization is enabled, wifiInfo's MAC should be set to default MAC |
| // when disconnect happens. |
| assertEquals(WifiInfo.DEFAULT_MAC_ADDRESS, mCmi.getWifiInfo().getMacAddress()); |
| |
| when(mWifiNative.getMacAddress(WIFI_IFACE_NAME)) |
| .thenReturn(TEST_LOCAL_MAC_ADDRESS.toString()); |
| mCmi.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, 0, 0, sBSSID); |
| mLooper.dispatchAll(); |
| assertEquals(TEST_LOCAL_MAC_ADDRESS.toString(), mCmi.getWifiInfo().getMacAddress()); |
| } |
| |
| /** |
| * Verify that we temporarily disable the network when auto-connected to a network |
| * with no internet access. |
| */ |
| @Test |
| public void verifyAutoConnectedNetworkWithInternetValidationFailure() throws Exception { |
| // Setup RSSI poll to update WifiInfo with low RSSI |
| mCmi.enableRssiPolling(true); |
| WifiLinkLayerStats llStats = new WifiLinkLayerStats(); |
| llStats.txmpdu_be = 1000; |
| llStats.rxmpdu_bk = 2000; |
| WifiNl80211Manager.SignalPollResult signalPollResult = |
| new WifiNl80211Manager.SignalPollResult(RSSI_THRESHOLD_BREACH_MIN, 65, 54, sFreq); |
| when(mWifiNative.getWifiLinkLayerStats(any())).thenReturn(llStats); |
| when(mWifiNative.signalPoll(any())).thenReturn(signalPollResult); |
| |
| // Simulate the first connection. |
| connect(); |
| ArgumentCaptor<Messenger> messengerCaptor = ArgumentCaptor.forClass(Messenger.class); |
| verify(mConnectivityManager).registerNetworkAgent(messengerCaptor.capture(), |
| any(NetworkInfo.class), any(LinkProperties.class), any(NetworkCapabilities.class), |
| anyInt(), any(NetworkAgentConfig.class), anyInt()); |
| |
| WifiConfiguration currentNetwork = new WifiConfiguration(); |
| currentNetwork.networkId = FRAMEWORK_NETWORK_ID; |
| currentNetwork.SSID = DEFAULT_TEST_SSID; |
| currentNetwork.noInternetAccessExpected = false; |
| currentNetwork.numNoInternetAccessReports = 1; |
| when(mWifiConfigManager.getConfiguredNetwork(FRAMEWORK_NETWORK_ID)) |
| .thenReturn(currentNetwork); |
| when(mWifiConfigManager.getLastSelectedNetwork()).thenReturn(FRAMEWORK_NETWORK_ID + 1); |
| |
| Message message = new Message(); |
| message.what = NetworkAgent.CMD_REPORT_NETWORK_STATUS; |
| message.arg1 = NetworkAgent.INVALID_NETWORK; |
| message.obj = new Bundle(); |
| messengerCaptor.getValue().send(message); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiConfigManager) |
| .incrementNetworkNoInternetAccessReports(FRAMEWORK_NETWORK_ID); |
| verify(mWifiConfigManager).updateNetworkSelectionStatus( |
| FRAMEWORK_NETWORK_ID, DISABLED_NO_INTERNET_TEMPORARY); |
| verify(mBssidBlocklistMonitor).handleBssidConnectionFailure(sBSSID, sSSID, |
| BssidBlocklistMonitor.REASON_NETWORK_VALIDATION_FAILURE, RSSI_THRESHOLD_BREACH_MIN); |
| verify(mWifiScoreCard).noteValidationFailure(any()); |
| } |
| |
| /** |
| * Verify that we don't temporarily disable the network when user selected to connect to a |
| * network with no internet access. |
| */ |
| @Test |
| public void verifyLastSelectedNetworkWithInternetValidationFailure() throws Exception { |
| // Simulate the first connection. |
| connect(); |
| ArgumentCaptor<Messenger> messengerCaptor = ArgumentCaptor.forClass(Messenger.class); |
| verify(mConnectivityManager).registerNetworkAgent(messengerCaptor.capture(), |
| any(NetworkInfo.class), any(LinkProperties.class), any(NetworkCapabilities.class), |
| anyInt(), any(NetworkAgentConfig.class), anyInt()); |
| |
| WifiConfiguration currentNetwork = new WifiConfiguration(); |
| currentNetwork.networkId = FRAMEWORK_NETWORK_ID; |
| currentNetwork.noInternetAccessExpected = false; |
| currentNetwork.numNoInternetAccessReports = 1; |
| when(mWifiConfigManager.getConfiguredNetwork(FRAMEWORK_NETWORK_ID)) |
| .thenReturn(currentNetwork); |
| when(mWifiConfigManager.getLastSelectedNetwork()).thenReturn(FRAMEWORK_NETWORK_ID); |
| |
| Message message = new Message(); |
| message.what = NetworkAgent.CMD_REPORT_NETWORK_STATUS; |
| message.arg1 = NetworkAgent.INVALID_NETWORK; |
| message.obj = new Bundle(); |
| messengerCaptor.getValue().send(message); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiConfigManager) |
| .incrementNetworkNoInternetAccessReports(FRAMEWORK_NETWORK_ID); |
| verify(mWifiConfigManager, never()).updateNetworkSelectionStatus( |
| FRAMEWORK_NETWORK_ID, DISABLED_NO_INTERNET_TEMPORARY); |
| } |
| |
| /** |
| * Verify that we temporarily disable the network when auto-connected to a network |
| * with no internet access. |
| */ |
| @Test |
| public void verifyAutoConnectedNoInternetExpectedNetworkWithInternetValidationFailure() |
| throws Exception { |
| // Simulate the first connection. |
| connect(); |
| ArgumentCaptor<Messenger> messengerCaptor = ArgumentCaptor.forClass(Messenger.class); |
| verify(mConnectivityManager).registerNetworkAgent(messengerCaptor.capture(), |
| any(NetworkInfo.class), any(LinkProperties.class), any(NetworkCapabilities.class), |
| anyInt(), any(NetworkAgentConfig.class), anyInt()); |
| |
| WifiConfiguration currentNetwork = new WifiConfiguration(); |
| currentNetwork.networkId = FRAMEWORK_NETWORK_ID; |
| currentNetwork.noInternetAccessExpected = true; |
| currentNetwork.numNoInternetAccessReports = 1; |
| when(mWifiConfigManager.getConfiguredNetwork(FRAMEWORK_NETWORK_ID)) |
| .thenReturn(currentNetwork); |
| when(mWifiConfigManager.getLastSelectedNetwork()).thenReturn(FRAMEWORK_NETWORK_ID + 1); |
| |
| Message message = new Message(); |
| message.what = NetworkAgent.CMD_REPORT_NETWORK_STATUS; |
| message.arg1 = NetworkAgent.INVALID_NETWORK; |
| message.obj = new Bundle(); |
| messengerCaptor.getValue().send(message); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiConfigManager) |
| .incrementNetworkNoInternetAccessReports(FRAMEWORK_NETWORK_ID); |
| verify(mWifiConfigManager, never()).updateNetworkSelectionStatus( |
| FRAMEWORK_NETWORK_ID, DISABLED_NO_INTERNET_TEMPORARY); |
| } |
| |
| /** |
| * Verify that we enable the network when we detect validated internet access. |
| */ |
| @Test |
| public void verifyNetworkSelectionEnableOnInternetValidation() throws Exception { |
| // Simulate the first connection. |
| connect(); |
| verify(mBssidBlocklistMonitor).handleBssidConnectionSuccess(sBSSID, sSSID); |
| verify(mBssidBlocklistMonitor).handleDhcpProvisioningSuccess(sBSSID, sSSID); |
| verify(mBssidBlocklistMonitor, never()).handleNetworkValidationSuccess(sBSSID, sSSID); |
| |
| ArgumentCaptor<Messenger> messengerCaptor = ArgumentCaptor.forClass(Messenger.class); |
| verify(mConnectivityManager).registerNetworkAgent(messengerCaptor.capture(), |
| any(NetworkInfo.class), any(LinkProperties.class), any(NetworkCapabilities.class), |
| anyInt(), any(NetworkAgentConfig.class), anyInt()); |
| |
| when(mWifiConfigManager.getLastSelectedNetwork()).thenReturn(FRAMEWORK_NETWORK_ID + 1); |
| |
| Message message = new Message(); |
| message.what = NetworkAgent.CMD_REPORT_NETWORK_STATUS; |
| message.arg1 = NetworkAgent.VALID_NETWORK; |
| message.obj = new Bundle(); |
| messengerCaptor.getValue().send(message); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiConfigManager) |
| .setNetworkValidatedInternetAccess(FRAMEWORK_NETWORK_ID, true); |
| verify(mWifiConfigManager).updateNetworkSelectionStatus( |
| FRAMEWORK_NETWORK_ID, DISABLED_NONE); |
| verify(mWifiScoreCard).noteValidationSuccess(any()); |
| verify(mBssidBlocklistMonitor).handleNetworkValidationSuccess(sBSSID, sSSID); |
| } |
| |
| private void connectWithValidInitRssi(int initRssiDbm) throws Exception { |
| triggerConnect(); |
| mCmi.getWifiInfo().setRssi(initRssiDbm); |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(0, sWifiSsid, sBSSID, SupplicantState.ASSOCIATED)); |
| mLooper.dispatchAll(); |
| |
| mCmi.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, 0, 0, sBSSID); |
| mLooper.dispatchAll(); |
| |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(0, sWifiSsid, sBSSID, SupplicantState.COMPLETED)); |
| mLooper.dispatchAll(); |
| |
| assertEquals("ObtainingIpState", getCurrentState().getName()); |
| } |
| |
| /** |
| * Verify that we set the INTERNET and bandwidth capability in the network agent when connected |
| * as a result of auto-join/legacy API's. Also verify up/down stream bandwidth values when |
| * Rx link speed is unavailable. |
| */ |
| @Test |
| public void verifyNetworkCapabilities() throws Exception { |
| when(mWifiDataStall.getTxThroughputKbps()).thenReturn(70_000); |
| when(mWifiDataStall.getRxThroughputKbps()).thenReturn(-1); |
| when(mWifiNetworkFactory.getSpecificNetworkRequestUidAndPackageName(any())) |
| .thenReturn(Pair.create(Process.INVALID_UID, "")); |
| // Simulate the first connection. |
| connectWithValidInitRssi(-42); |
| |
| ArgumentCaptor<NetworkCapabilities> networkCapabilitiesCaptor = |
| ArgumentCaptor.forClass(NetworkCapabilities.class); |
| verify(mConnectivityManager).registerNetworkAgent(any(Messenger.class), |
| any(NetworkInfo.class), any(LinkProperties.class), |
| networkCapabilitiesCaptor.capture(), anyInt(), any(NetworkAgentConfig.class), |
| anyInt()); |
| |
| NetworkCapabilities networkCapabilities = networkCapabilitiesCaptor.getValue(); |
| assertNotNull(networkCapabilities); |
| |
| // Should have internet capability. |
| assertTrue(networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)); |
| assertNull(networkCapabilities.getNetworkSpecifier()); |
| |
| assertEquals(mConnectedNetwork.creatorUid, networkCapabilities.getOwnerUid()); |
| assertArrayEquals( |
| new int[] {mConnectedNetwork.creatorUid}, |
| networkCapabilities.getAdministratorUids()); |
| |
| // Should set bandwidth correctly |
| assertEquals(-42, mCmi.getWifiInfo().getRssi()); |
| assertEquals(70_000, networkCapabilities.getLinkUpstreamBandwidthKbps()); |
| assertEquals(70_000, networkCapabilities.getLinkDownstreamBandwidthKbps()); |
| } |
| |
| /** |
| * Verify that we don't set the INTERNET capability in the network agent when connected |
| * as a result of the new network request API. Also verify up/down stream bandwidth values |
| * when both Tx and Rx link speed are unavailable. |
| */ |
| @Test |
| public void verifyNetworkCapabilitiesForSpecificRequest() throws Exception { |
| when(mWifiDataStall.getTxThroughputKbps()).thenReturn(-1); |
| when(mWifiDataStall.getRxThroughputKbps()).thenReturn(-1); |
| when(mWifiNetworkFactory.getSpecificNetworkRequestUidAndPackageName(any())) |
| .thenReturn(Pair.create(TEST_UID, OP_PACKAGE_NAME)); |
| // Simulate the first connection. |
| connectWithValidInitRssi(-42); |
| ArgumentCaptor<NetworkCapabilities> networkCapabilitiesCaptor = |
| ArgumentCaptor.forClass(NetworkCapabilities.class); |
| verify(mConnectivityManager).registerNetworkAgent(any(Messenger.class), |
| any(NetworkInfo.class), any(LinkProperties.class), |
| networkCapabilitiesCaptor.capture(), anyInt(), any(NetworkAgentConfig.class), |
| anyInt()); |
| |
| NetworkCapabilities networkCapabilities = networkCapabilitiesCaptor.getValue(); |
| assertNotNull(networkCapabilities); |
| |
| // should not have internet capability. |
| assertFalse(networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)); |
| |
| NetworkSpecifier networkSpecifier = networkCapabilities.getNetworkSpecifier(); |
| assertTrue(networkSpecifier instanceof WifiNetworkAgentSpecifier); |
| WifiNetworkAgentSpecifier wifiNetworkAgentSpecifier = |
| (WifiNetworkAgentSpecifier) networkSpecifier; |
| WifiNetworkAgentSpecifier expectedWifiNetworkAgentSpecifier = |
| new WifiNetworkAgentSpecifier(mCmi.getCurrentWifiConfiguration()); |
| assertEquals(expectedWifiNetworkAgentSpecifier, wifiNetworkAgentSpecifier); |
| assertEquals(TEST_UID, networkCapabilities.getRequestorUid()); |
| assertEquals(OP_PACKAGE_NAME, networkCapabilities.getRequestorPackageName()); |
| assertEquals(90_000, networkCapabilities.getLinkUpstreamBandwidthKbps()); |
| assertEquals(80_000, networkCapabilities.getLinkDownstreamBandwidthKbps()); |
| } |
| |
| /** |
| * Verify that we check for data stall during rssi poll |
| * and then check that wifi link layer usage data are being updated. |
| */ |
| @Test |
| public void verifyRssiPollChecksDataStall() throws Exception { |
| mCmi.enableRssiPolling(true); |
| connect(); |
| |
| WifiLinkLayerStats oldLLStats = new WifiLinkLayerStats(); |
| when(mWifiNative.getWifiLinkLayerStats(any())).thenReturn(oldLLStats); |
| mCmi.sendMessage(ClientModeImpl.CMD_RSSI_POLL, 1); |
| mLooper.dispatchAll(); |
| WifiLinkLayerStats newLLStats = new WifiLinkLayerStats(); |
| when(mWifiNative.getWifiLinkLayerStats(any())).thenReturn(newLLStats); |
| mCmi.sendMessage(ClientModeImpl.CMD_RSSI_POLL, 1); |
| mLooper.dispatchAll(); |
| verify(mWifiDataStall).checkDataStallAndThroughputSufficiency( |
| oldLLStats, newLLStats, mCmi.getWifiInfo()); |
| verify(mWifiMetrics).incrementWifiLinkLayerUsageStats(newLLStats); |
| } |
| |
| /** |
| * Verify that we update wifi usability stats entries during rssi poll and that when we get |
| * a data stall we label and save the current list of usability stats entries. |
| * @throws Exception |
| */ |
| @Test |
| public void verifyRssiPollUpdatesWifiUsabilityMetrics() throws Exception { |
| mCmi.enableRssiPolling(true); |
| connect(); |
| |
| WifiLinkLayerStats stats = new WifiLinkLayerStats(); |
| when(mWifiNative.getWifiLinkLayerStats(any())).thenReturn(stats); |
| when(mWifiDataStall.checkDataStallAndThroughputSufficiency(any(), any(), any())) |
| .thenReturn(WifiIsUnusableEvent.TYPE_UNKNOWN); |
| mCmi.sendMessage(ClientModeImpl.CMD_RSSI_POLL, 1); |
| mLooper.dispatchAll(); |
| verify(mWifiMetrics).updateWifiUsabilityStatsEntries(any(), eq(stats)); |
| verify(mWifiMetrics, never()).addToWifiUsabilityStatsList(WifiUsabilityStats.LABEL_BAD, |
| eq(anyInt()), eq(-1)); |
| |
| when(mWifiDataStall.checkDataStallAndThroughputSufficiency(any(), any(), any())) |
| .thenReturn(WifiIsUnusableEvent.TYPE_DATA_STALL_BAD_TX); |
| when(mClock.getElapsedSinceBootMillis()).thenReturn(10L); |
| mCmi.sendMessage(ClientModeImpl.CMD_RSSI_POLL, 1); |
| mLooper.dispatchAll(); |
| verify(mWifiMetrics, times(2)).updateWifiUsabilityStatsEntries(any(), eq(stats)); |
| when(mClock.getElapsedSinceBootMillis()) |
| .thenReturn(10L + ClientModeImpl.DURATION_TO_WAIT_ADD_STATS_AFTER_DATA_STALL_MS); |
| mCmi.sendMessage(ClientModeImpl.CMD_RSSI_POLL, 1); |
| mLooper.dispatchAll(); |
| verify(mWifiMetrics).addToWifiUsabilityStatsList(WifiUsabilityStats.LABEL_BAD, |
| WifiIsUnusableEvent.TYPE_DATA_STALL_BAD_TX, -1); |
| } |
| |
| /** |
| * Verify that when ordered to setPowerSave(true) while Interface is created, |
| * WifiNative is called with the right powerSave mode. |
| */ |
| @Test |
| public void verifySetPowerSaveTrueSuccess() throws Exception { |
| mCmi.setOperationalMode(ClientModeImpl.CONNECT_MODE, WIFI_IFACE_NAME); |
| assertTrue(mCmi.setPowerSave(true)); |
| verify(mWifiNative).setPowerSave(WIFI_IFACE_NAME, true); |
| } |
| |
| /** |
| * Verify that when ordered to setPowerSave(false) while Interface is created, |
| * WifiNative is called with the right powerSave mode. |
| */ |
| @Test |
| public void verifySetPowerSaveFalseSuccess() throws Exception { |
| mCmi.setOperationalMode(ClientModeImpl.CONNECT_MODE, WIFI_IFACE_NAME); |
| assertTrue(mCmi.setPowerSave(false)); |
| verify(mWifiNative).setPowerSave(WIFI_IFACE_NAME, false); |
| } |
| |
| /** |
| * Verify that when interface is not created yet (InterfaceName is null), |
| * then setPowerSave() returns with error and no call in WifiNative happens. |
| */ |
| @Test |
| public void verifySetPowerSaveFailure() throws Exception { |
| boolean powerSave = true; |
| mCmi.setOperationalMode(ClientModeImpl.DISABLED_MODE, null); |
| assertFalse(mCmi.setPowerSave(powerSave)); |
| verify(mWifiNative, never()).setPowerSave(anyString(), anyBoolean()); |
| } |
| |
| /** |
| * Verify that we call into WifiTrafficPoller during rssi poll |
| */ |
| @Test |
| public void verifyRssiPollCallsWifiTrafficPoller() throws Exception { |
| mCmi.enableRssiPolling(true); |
| connect(); |
| |
| verify(mWifiTrafficPoller).notifyOnDataActivity(anyLong(), anyLong()); |
| } |
| |
| /** |
| * Verify that LinkProbeManager is updated during RSSI poll |
| */ |
| @Test |
| public void verifyRssiPollCallsLinkProbeManager() throws Exception { |
| mCmi.enableRssiPolling(true); |
| |
| connect(); |
| // reset() should be called when RSSI polling is enabled and entering L2ConnectedState |
| verify(mLinkProbeManager).resetOnNewConnection(); // called first time here |
| verify(mLinkProbeManager, never()).resetOnScreenTurnedOn(); // not called |
| verify(mLinkProbeManager).updateConnectionStats(any(), any()); |
| |
| mCmi.enableRssiPolling(false); |
| mLooper.dispatchAll(); |
| // reset() should be called when in L2ConnectedState (or child states) and RSSI polling |
| // becomes enabled |
| mCmi.enableRssiPolling(true); |
| mLooper.dispatchAll(); |
| verify(mLinkProbeManager, times(1)).resetOnNewConnection(); // verify not called again |
| verify(mLinkProbeManager).resetOnScreenTurnedOn(); // verify called here |
| } |
| |
| /** |
| * Verify that when ordered to setLowLatencyMode(true), |
| * WifiNative is called with the right lowLatency mode. |
| */ |
| @Test |
| public void verifySetLowLatencyTrueSuccess() throws Exception { |
| when(mWifiNative.setLowLatencyMode(anyBoolean())).thenReturn(true); |
| assertTrue(mCmi.setLowLatencyMode(true)); |
| verify(mWifiNative).setLowLatencyMode(true); |
| } |
| |
| /** |
| * Verify that when ordered to setLowLatencyMode(false), |
| * WifiNative is called with the right lowLatency mode. |
| */ |
| @Test |
| public void verifySetLowLatencyFalseSuccess() throws Exception { |
| when(mWifiNative.setLowLatencyMode(anyBoolean())).thenReturn(true); |
| assertTrue(mCmi.setLowLatencyMode(false)); |
| verify(mWifiNative).setLowLatencyMode(false); |
| } |
| |
| /** |
| * Verify that when WifiNative fails to set low latency mode, |
| * then the call setLowLatencyMode() returns with failure, |
| */ |
| @Test |
| public void verifySetLowLatencyModeFailure() throws Exception { |
| final boolean lowLatencyMode = true; |
| when(mWifiNative.setLowLatencyMode(anyBoolean())).thenReturn(false); |
| assertFalse(mCmi.setLowLatencyMode(lowLatencyMode)); |
| verify(mWifiNative).setLowLatencyMode(eq(lowLatencyMode)); |
| } |
| |
| /** |
| * Verify getting the factory MAC address success case. |
| */ |
| @Test |
| public void testGetFactoryMacAddressSuccess() throws Exception { |
| initializeAndAddNetworkAndVerifySuccess(); |
| assertEquals(TEST_GLOBAL_MAC_ADDRESS.toString(), mCmi.getFactoryMacAddress()); |
| verify(mWifiNative).getFactoryMacAddress(WIFI_IFACE_NAME); |
| verify(mWifiNative).getMacAddress(anyString()); // called once when setting up client mode |
| } |
| |
| /** |
| * Verify getting the factory MAC address failure case. |
| */ |
| @Test |
| public void testGetFactoryMacAddressFail() throws Exception { |
| initializeAndAddNetworkAndVerifySuccess(); |
| when(mWifiNative.getFactoryMacAddress(WIFI_IFACE_NAME)).thenReturn(null); |
| assertNull(mCmi.getFactoryMacAddress()); |
| verify(mWifiNative).getFactoryMacAddress(WIFI_IFACE_NAME); |
| verify(mWifiNative).getMacAddress(anyString()); // called once when setting up client mode |
| } |
| |
| /** |
| * Verify that when WifiNative#getFactoryMacAddress fails, if the device does not support |
| * MAC randomization then the currently programmed MAC address gets returned. |
| */ |
| @Test |
| public void testGetFactoryMacAddressFailWithNoMacRandomizationSupport() throws Exception { |
| mResources.setBoolean(R.bool.config_wifi_connected_mac_randomization_supported, false); |
| initializeCmi(); |
| initializeAndAddNetworkAndVerifySuccess(); |
| when(mWifiNative.getFactoryMacAddress(WIFI_IFACE_NAME)).thenReturn(null); |
| mCmi.getFactoryMacAddress(); |
| verify(mWifiNative).getFactoryMacAddress(anyString()); |
| verify(mWifiNative, times(2)).getMacAddress(WIFI_IFACE_NAME); |
| } |
| |
| @Test |
| public void testSetDeviceMobilityState() { |
| mCmi.setDeviceMobilityState(WifiManager.DEVICE_MOBILITY_STATE_STATIONARY); |
| verify(mWifiConnectivityManager).setDeviceMobilityState( |
| WifiManager.DEVICE_MOBILITY_STATE_STATIONARY); |
| verify(mWifiHealthMonitor).setDeviceMobilityState( |
| WifiManager.DEVICE_MOBILITY_STATE_STATIONARY); |
| verify(mWifiDataStall).setDeviceMobilityState( |
| WifiManager.DEVICE_MOBILITY_STATE_STATIONARY); |
| } |
| |
| /** |
| * Verify the MAC address is being randomized at start to prevent leaking the factory MAC. |
| */ |
| @Test |
| public void testRandomizeMacAddressOnStart() throws Exception { |
| ArgumentCaptor<MacAddress> macAddressCaptor = ArgumentCaptor.forClass(MacAddress.class); |
| loadComponentsInStaMode(); |
| verify(mWifiNative).setMacAddress(anyString(), macAddressCaptor.capture()); |
| MacAddress currentMac = macAddressCaptor.getValue(); |
| |
| assertNotEquals("The currently programmed MAC address should be different from the factory " |
| + "MAC address after ClientModeImpl starts", |
| mCmi.getFactoryMacAddress(), currentMac.toString()); |
| } |
| |
| /** |
| * Verify the MAC address is being randomized at start to prevent leaking the factory MAC. |
| */ |
| @Test |
| public void testNoRandomizeMacAddressOnStartIfMacRandomizationNotEnabled() throws Exception { |
| mResources.setBoolean(R.bool.config_wifi_connected_mac_randomization_supported, false); |
| loadComponentsInStaMode(); |
| verify(mWifiNative, never()).setMacAddress(anyString(), any()); |
| } |
| |
| /** |
| * Verify bugreport will be taken when get IP_REACHABILITY_LOST |
| */ |
| @Test |
| public void testTakebugreportbyIpReachabilityLost() throws Exception { |
| connect(); |
| |
| mCmi.sendMessage(ClientModeImpl.CMD_IP_REACHABILITY_LOST); |
| mLooper.dispatchAll(); |
| verify(mWifiDiagnostics).captureBugReportData( |
| eq(WifiDiagnostics.REPORT_REASON_REACHABILITY_LOST)); |
| } |
| |
| /** |
| * Verify removing sim will also remove an ephemeral Passpoint Provider. And reset carrier |
| * privileged suggestor apps. |
| */ |
| @Test |
| public void testResetSimNetworkWhenRemovingSim() throws Exception { |
| // Switch to connect mode and verify wifi is reported as enabled |
| startSupplicantAndDispatchMessages(); |
| |
| // Indicate that sim is removed. |
| mCmi.sendMessage(ClientModeImpl.CMD_RESET_SIM_NETWORKS, |
| ClientModeImpl.RESET_SIM_REASON_SIM_REMOVED); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiConfigManager).resetSimNetworks(); |
| verify(mWifiNetworkSuggestionsManager).resetCarrierPrivilegedApps(); |
| } |
| |
| /** |
| * Verify inserting sim will reset carrier privileged suggestor apps. |
| * and remove any previous notifications due to sim removal |
| */ |
| @Test |
| public void testResetCarrierPrivilegedAppsWhenInsertingSim() throws Exception { |
| // Switch to connect mode and verify wifi is reported as enabled |
| startSupplicantAndDispatchMessages(); |
| |
| // Indicate that sim is inserted. |
| mCmi.sendMessage(ClientModeImpl.CMD_RESET_SIM_NETWORKS, |
| ClientModeImpl.RESET_SIM_REASON_SIM_INSERTED); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiConfigManager, never()).resetSimNetworks(); |
| verify(mWifiNetworkSuggestionsManager).resetCarrierPrivilegedApps(); |
| verify(mSimRequiredNotifier).dismissSimRequiredNotification(); |
| } |
| |
| /** |
| * Verifies that WifiLastResortWatchdog is notified of FOURWAY_HANDSHAKE_TIMEOUT. |
| */ |
| @Test |
| public void testHandshakeTimeoutUpdatesWatchdog() throws Exception { |
| // Setup CONNECT_MODE & a WifiConfiguration |
| initializeAndAddNetworkAndVerifySuccess(); |
| mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, 0, 0, sBSSID); |
| mLooper.dispatchAll(); |
| // Verifies that WifiLastResortWatchdog won't be notified |
| // by other reason code |
| mCmi.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, 0, 2, sBSSID); |
| mLooper.dispatchAll(); |
| |
| assertEquals("DisconnectedState", getCurrentState().getName()); |
| verify(mWifiLastResortWatchdog, never()).noteConnectionFailureAndTriggerIfNeeded( |
| sSSID, sBSSID, WifiLastResortWatchdog.FAILURE_CODE_AUTHENTICATION); |
| |
| // Verifies that WifiLastResortWatchdog be notified |
| // for FOURWAY_HANDSHAKE_TIMEOUT. |
| mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, 0, 0, sBSSID); |
| mLooper.dispatchAll(); |
| mCmi.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, 0, 15, sBSSID); |
| mLooper.dispatchAll(); |
| |
| assertEquals("DisconnectedState", getCurrentState().getName()); |
| verify(mWifiLastResortWatchdog).noteConnectionFailureAndTriggerIfNeeded( |
| sSSID, sBSSID, WifiLastResortWatchdog.FAILURE_CODE_AUTHENTICATION); |
| |
| } |
| |
| /** |
| * Verify that WifiInfo is correctly populated after connection. |
| */ |
| @Test |
| public void verifyWifiInfoGetNetworkSpecifierPackageName() throws Exception { |
| mConnectedNetwork.fromWifiNetworkSpecifier = true; |
| mConnectedNetwork.ephemeral = true; |
| mConnectedNetwork.trusted = true; |
| mConnectedNetwork.creatorName = OP_PACKAGE_NAME; |
| connect(); |
| |
| assertTrue(mCmi.getWifiInfo().isEphemeral()); |
| assertTrue(mCmi.getWifiInfo().isTrusted()); |
| assertEquals(OP_PACKAGE_NAME, |
| mCmi.getWifiInfo().getRequestingPackageName()); |
| } |
| |
| /** |
| * Verify that WifiInfo is correctly populated after connection. |
| */ |
| @Test |
| public void verifyWifiInfoGetNetworkSuggestionPackageName() throws Exception { |
| mConnectedNetwork.fromWifiNetworkSuggestion = true; |
| mConnectedNetwork.ephemeral = true; |
| mConnectedNetwork.trusted = true; |
| mConnectedNetwork.creatorName = OP_PACKAGE_NAME; |
| connect(); |
| |
| assertTrue(mCmi.getWifiInfo().isEphemeral()); |
| assertTrue(mCmi.getWifiInfo().isTrusted()); |
| assertEquals(OP_PACKAGE_NAME, |
| mCmi.getWifiInfo().getRequestingPackageName()); |
| } |
| |
| /** |
| * Verify that a WifiIsUnusableEvent is logged and the current list of usability stats entries |
| * are labeled and saved when receiving an IP reachability lost message. |
| * @throws Exception |
| */ |
| @Test |
| public void verifyIpReachabilityLostMsgUpdatesWifiUsabilityMetrics() throws Exception { |
| connect(); |
| |
| mCmi.sendMessage(ClientModeImpl.CMD_IP_REACHABILITY_LOST); |
| mLooper.dispatchAll(); |
| verify(mWifiMetrics).logWifiIsUnusableEvent( |
| WifiIsUnusableEvent.TYPE_IP_REACHABILITY_LOST); |
| verify(mWifiMetrics).addToWifiUsabilityStatsList(WifiUsabilityStats.LABEL_BAD, |
| WifiUsabilityStats.TYPE_IP_REACHABILITY_LOST, -1); |
| } |
| |
| /** |
| * Tests that when {@link ClientModeImpl} receives a SUP_REQUEST_IDENTITY message, it responds |
| * to the supplicant with the SIM identity. |
| */ |
| @Test |
| public void testSupRequestIdentity_setsIdentityResponse() throws Exception { |
| mConnectedNetwork = spy(WifiConfigurationTestUtil.createEapNetwork( |
| WifiEnterpriseConfig.Eap.SIM, WifiEnterpriseConfig.Phase2.NONE)); |
| mConnectedNetwork.SSID = DEFAULT_TEST_SSID; |
| |
| when(mDataTelephonyManager.getSubscriberId()).thenReturn("3214561234567890"); |
| when(mDataTelephonyManager.getSimState()).thenReturn(TelephonyManager.SIM_STATE_READY); |
| when(mDataTelephonyManager.getSimOperator()).thenReturn("321456"); |
| when(mDataTelephonyManager.getCarrierInfoForImsiEncryption(anyInt())).thenReturn(null); |
| MockitoSession mockSession = ExtendedMockito.mockitoSession() |
| .mockStatic(SubscriptionManager.class) |
| .startMocking(); |
| when(SubscriptionManager.getDefaultDataSubscriptionId()).thenReturn(DATA_SUBID); |
| when(SubscriptionManager.isValidSubscriptionId(anyInt())).thenReturn(true); |
| |
| triggerConnect(); |
| |
| mCmi.sendMessage(WifiMonitor.SUP_REQUEST_IDENTITY, |
| 0, FRAMEWORK_NETWORK_ID, DEFAULT_TEST_SSID); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiNative).simIdentityResponse(WIFI_IFACE_NAME, |
| "13214561234567890@wlan.mnc456.mcc321.3gppnetwork.org", ""); |
| mockSession.finishMocking(); |
| } |
| |
| /** |
| * Verifies that WifiLastResortWatchdog is notified of DHCP failures when recevied |
| * NETWORK_DISCONNECTION_EVENT while in ObtainingIpState. |
| */ |
| @Test |
| public void testDhcpFailureUpdatesWatchdog_WhenDisconnectedWhileObtainingIpAddr() |
| throws Exception { |
| initializeAndAddNetworkAndVerifySuccess(); |
| |
| verify(mWifiNative).removeAllNetworks(WIFI_IFACE_NAME); |
| |
| IActionListener connectActionListener = mock(IActionListener.class); |
| mCmi.connect(null, 0, mock(Binder.class), connectActionListener, 0, Binder.getCallingUid()); |
| mLooper.dispatchAll(); |
| verify(connectActionListener).onSuccess(); |
| |
| verify(mWifiConfigManager).enableNetwork(eq(0), eq(true), anyInt(), any()); |
| |
| mCmi.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, 0, 0, sBSSID); |
| mLooper.dispatchAll(); |
| |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(0, sWifiSsid, sBSSID, SupplicantState.COMPLETED)); |
| mLooper.dispatchAll(); |
| |
| assertEquals("ObtainingIpState", getCurrentState().getName()); |
| |
| // Verifies that WifiLastResortWatchdog be notified. |
| mCmi.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, 0, 0, sBSSID); |
| mLooper.dispatchAll(); |
| |
| assertEquals("DisconnectedState", getCurrentState().getName()); |
| verify(mWifiLastResortWatchdog).noteConnectionFailureAndTriggerIfNeeded( |
| sSSID, sBSSID, WifiLastResortWatchdog.FAILURE_CODE_DHCP); |
| } |
| |
| /** |
| * Verifies that we trigger a disconnect when the {@link WifiConfigManager}. |
| * OnNetworkUpdateListener#onNetworkRemoved(WifiConfiguration)} is invoked. |
| */ |
| @Test |
| public void testOnNetworkRemoved() throws Exception { |
| connect(); |
| |
| WifiConfiguration removedNetwork = new WifiConfiguration(); |
| removedNetwork.networkId = FRAMEWORK_NETWORK_ID; |
| mConfigUpdateListenerCaptor.getValue().onNetworkRemoved(removedNetwork); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiNative).removeNetworkCachedData(FRAMEWORK_NETWORK_ID); |
| assertEquals("DisconnectingState", getCurrentState().getName()); |
| } |
| |
| /** |
| * Verifies that we trigger a disconnect when the {@link WifiConfigManager |
| * .OnNetworkUpdateListener#onNetworkPermanentlyDisabled(WifiConfiguration, int)} is invoked. |
| */ |
| @Test |
| public void testOnNetworkPermanentlyDisabled() throws Exception { |
| connect(); |
| |
| WifiConfiguration disabledNetwork = new WifiConfiguration(); |
| disabledNetwork.networkId = FRAMEWORK_NETWORK_ID; |
| mConfigUpdateListenerCaptor.getValue().onNetworkPermanentlyDisabled(disabledNetwork, |
| WifiConfiguration.NetworkSelectionStatus.DISABLED_BY_WRONG_PASSWORD); |
| mLooper.dispatchAll(); |
| |
| assertEquals("DisconnectingState", getCurrentState().getName()); |
| } |
| |
| /** |
| * Verifies that we don't trigger a disconnect when the {@link WifiConfigManager |
| * .OnNetworkUpdateListener#onNetworkPermanentlyDisabled(WifiConfiguration, int)} is invoked. |
| */ |
| @Test |
| public void testOnNetworkPermanentlyDisabledWithNoInternet() throws Exception { |
| connect(); |
| |
| WifiConfiguration disabledNetwork = new WifiConfiguration(); |
| disabledNetwork.networkId = FRAMEWORK_NETWORK_ID; |
| mConfigUpdateListenerCaptor.getValue().onNetworkPermanentlyDisabled(disabledNetwork, |
| WifiConfiguration.NetworkSelectionStatus.DISABLED_NO_INTERNET_PERMANENT); |
| mLooper.dispatchAll(); |
| |
| assertEquals("ConnectedState", getCurrentState().getName()); |
| } |
| |
| /** |
| * Verifies that we don't trigger a disconnect when the {@link WifiConfigManager |
| * .OnNetworkUpdateListener#onNetworkTemporarilyDisabled(WifiConfiguration, int)} is invoked. |
| */ |
| @Test |
| public void testOnNetworkTemporarilyDisabledWithNoInternet() throws Exception { |
| connect(); |
| |
| WifiConfiguration disabledNetwork = new WifiConfiguration(); |
| disabledNetwork.networkId = FRAMEWORK_NETWORK_ID; |
| mConfigUpdateListenerCaptor.getValue().onNetworkTemporarilyDisabled(disabledNetwork, |
| WifiConfiguration.NetworkSelectionStatus.DISABLED_NO_INTERNET_TEMPORARY); |
| mLooper.dispatchAll(); |
| |
| assertEquals("ConnectedState", getCurrentState().getName()); |
| } |
| |
| /** |
| * Verify that MboOce/WifiDataStall enable/disable methods are called in ClientMode. |
| */ |
| @Test |
| public void verifyMboOceWifiDataStallSetupInClientMode() throws Exception { |
| startSupplicantAndDispatchMessages(); |
| verify(mMboOceController).enable(); |
| verify(mWifiDataStall).enablePhoneStateListener(); |
| mCmi.setOperationalMode(ClientModeImpl.DISABLED_MODE, null); |
| mLooper.dispatchAll(); |
| verify(mMboOceController).disable(); |
| verify(mWifiDataStall).disablePhoneStateListener(); |
| } |
| |
| /** |
| * Verify that Bluetooth active is set correctly with BT state/connection state changes |
| */ |
| @Test |
| public void verifyBluetoothStateAndConnectionStateChanges() throws Exception { |
| startSupplicantAndDispatchMessages(); |
| mCmi.sendBluetoothAdapterStateChange(BluetoothAdapter.STATE_ON); |
| mLooper.dispatchAll(); |
| verify(mWifiConnectivityManager, times(1)).setBluetoothConnected(false); |
| |
| mCmi.sendBluetoothAdapterConnectionStateChange(BluetoothAdapter.STATE_CONNECTED); |
| mLooper.dispatchAll(); |
| verify(mWifiConnectivityManager, times(1)).setBluetoothConnected(true); |
| |
| mCmi.sendBluetoothAdapterStateChange(BluetoothAdapter.STATE_OFF); |
| mLooper.dispatchAll(); |
| verify(mWifiConnectivityManager, times(2)).setBluetoothConnected(false); |
| |
| mCmi.sendBluetoothAdapterStateChange(BluetoothAdapter.STATE_ON); |
| mLooper.dispatchAll(); |
| verify(mWifiConnectivityManager, times(3)).setBluetoothConnected(false); |
| |
| mCmi.sendBluetoothAdapterConnectionStateChange(BluetoothAdapter.STATE_CONNECTING); |
| mLooper.dispatchAll(); |
| verify(mWifiConnectivityManager, times(2)).setBluetoothConnected(true); |
| |
| mCmi.sendBluetoothAdapterConnectionStateChange(BluetoothAdapter.STATE_DISCONNECTED); |
| mLooper.dispatchAll(); |
| verify(mWifiConnectivityManager, times(4)).setBluetoothConnected(false); |
| |
| mCmi.sendBluetoothAdapterConnectionStateChange(BluetoothAdapter.STATE_CONNECTED); |
| mLooper.dispatchAll(); |
| verify(mWifiConnectivityManager, times(3)).setBluetoothConnected(true); |
| } |
| |
| /** |
| * Test that handleBssTransitionRequest() blocklist the BSS upon |
| * receiving BTM request frame that contains MBO-OCE IE with an |
| * association retry delay attribute. |
| */ |
| @Test |
| public void testBtmFrameWithMboAssocretryDelayBlockListTheBssid() throws Exception { |
| // Connect to network with |sBSSID|, |sFreq|. |
| connect(); |
| |
| MboOceController.BtmFrameData btmFrmData = new MboOceController.BtmFrameData(); |
| |
| btmFrmData.mStatus = MboOceConstants.BTM_RESPONSE_STATUS_REJECT_UNSPECIFIED; |
| btmFrmData.mBssTmDataFlagsMask = MboOceConstants.BTM_DATA_FLAG_DISASSOCIATION_IMMINENT |
| | MboOceConstants.BTM_DATA_FLAG_MBO_ASSOC_RETRY_DELAY_INCLUDED; |
| btmFrmData.mBlockListDurationMs = 60000; |
| |
| mCmi.sendMessage(WifiMonitor.MBO_OCE_BSS_TM_HANDLING_DONE, btmFrmData); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiMetrics, times(1)).incrementSteeringRequestCountIncludingMboAssocRetryDelay(); |
| verify(mBssidBlocklistMonitor).blockBssidForDurationMs(eq(sBSSID), eq(sSSID), |
| eq(btmFrmData.mBlockListDurationMs), anyInt(), anyInt()); |
| } |
| |
| /** |
| * Test that handleBssTransitionRequest() blocklist the BSS upon |
| * receiving BTM request frame that contains disassociation imminent bit |
| * set to 1. |
| */ |
| @Test |
| public void testBtmFrameWithDisassocImminentBitBlockListTheBssid() throws Exception { |
| // Connect to network with |sBSSID|, |sFreq|. |
| connect(); |
| |
| MboOceController.BtmFrameData btmFrmData = new MboOceController.BtmFrameData(); |
| |
| btmFrmData.mStatus = MboOceConstants.BTM_RESPONSE_STATUS_ACCEPT; |
| btmFrmData.mBssTmDataFlagsMask = MboOceConstants.BTM_DATA_FLAG_DISASSOCIATION_IMMINENT; |
| |
| mCmi.sendMessage(WifiMonitor.MBO_OCE_BSS_TM_HANDLING_DONE, btmFrmData); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiMetrics, never()).incrementSteeringRequestCountIncludingMboAssocRetryDelay(); |
| verify(mBssidBlocklistMonitor).blockBssidForDurationMs(eq(sBSSID), eq(sSSID), |
| eq(MboOceConstants.DEFAULT_BLOCKLIST_DURATION_MS), anyInt(), anyInt()); |
| } |
| |
| /** |
| * Test that handleBssTransitionRequest() trigger force scan for |
| * network selection when status code is REJECT. |
| */ |
| @Test |
| public void testBTMRequestRejectTriggerNetworkSelction() throws Exception { |
| // Connect to network with |sBSSID|, |sFreq|. |
| connect(); |
| |
| MboOceController.BtmFrameData btmFrmData = new MboOceController.BtmFrameData(); |
| |
| btmFrmData.mStatus = MboOceConstants.BTM_RESPONSE_STATUS_REJECT_UNSPECIFIED; |
| btmFrmData.mBssTmDataFlagsMask = MboOceConstants.BTM_DATA_FLAG_DISASSOCIATION_IMMINENT |
| | MboOceConstants.BTM_DATA_FLAG_BSS_TERMINATION_INCLUDED |
| | MboOceConstants.BTM_DATA_FLAG_MBO_CELL_DATA_CONNECTION_PREFERENCE_INCLUDED; |
| btmFrmData.mBlockListDurationMs = 60000; |
| |
| mCmi.sendMessage(WifiMonitor.MBO_OCE_BSS_TM_HANDLING_DONE, btmFrmData); |
| mLooper.dispatchAll(); |
| |
| verify(mBssidBlocklistMonitor, never()).blockBssidForDurationMs(eq(sBSSID), eq(sSSID), |
| eq(btmFrmData.mBlockListDurationMs), anyInt(), anyInt()); |
| verify(mWifiConnectivityManager).forceConnectivityScan(ClientModeImpl.WIFI_WORK_SOURCE); |
| verify(mWifiMetrics, times(1)).incrementMboCellularSwitchRequestCount(); |
| verify(mWifiMetrics, times(1)) |
| .incrementForceScanCountDueToSteeringRequest(); |
| |
| } |
| |
| /** |
| * Test that handleBssTransitionRequest() does not trigger force |
| * scan when status code is accept. |
| */ |
| @Test |
| public void testBTMRequestAcceptDoNotTriggerNetworkSelction() throws Exception { |
| // Connect to network with |sBSSID|, |sFreq|. |
| connect(); |
| |
| MboOceController.BtmFrameData btmFrmData = new MboOceController.BtmFrameData(); |
| |
| btmFrmData.mStatus = MboOceConstants.BTM_RESPONSE_STATUS_ACCEPT; |
| btmFrmData.mBssTmDataFlagsMask = MboOceConstants.BTM_DATA_FLAG_DISASSOCIATION_IMMINENT; |
| |
| mCmi.sendMessage(WifiMonitor.MBO_OCE_BSS_TM_HANDLING_DONE, btmFrmData); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiConnectivityManager, never()) |
| .forceConnectivityScan(ClientModeImpl.WIFI_WORK_SOURCE); |
| } |
| |
| /** |
| * Test that the interval for poll RSSI is read from config overlay correctly. |
| */ |
| @Test |
| public void testPollRssiIntervalIsSetCorrectly() throws Exception { |
| assertEquals(3000, mCmi.getPollRssiIntervalMsecs()); |
| mResources.setInteger(R.integer.config_wifiPollRssiIntervalMilliseconds, 6000); |
| assertEquals(6000, mCmi.getPollRssiIntervalMsecs()); |
| mResources.setInteger(R.integer.config_wifiPollRssiIntervalMilliseconds, 7000); |
| assertEquals(6000, mCmi.getPollRssiIntervalMsecs()); |
| } |
| |
| /** |
| * Verifies that the logic does not modify PSK key management when WPA3 auto upgrade feature is |
| * disabled. |
| * |
| * @throws Exception |
| */ |
| @Test |
| public void testNoWpa3UpgradeWhenOverlaysAreOff() throws Exception { |
| initializeAndAddNetworkAndVerifySuccess(); |
| assertEquals(ClientModeImpl.CONNECT_MODE, mCmi.getOperationalModeForTest()); |
| assertEquals(WifiManager.WIFI_STATE_ENABLED, mCmi.syncGetWifiState()); |
| |
| WifiConfiguration config = mock(WifiConfiguration.class); |
| BitSet allowedKeyManagement = mock(BitSet.class); |
| config.allowedKeyManagement = allowedKeyManagement; |
| when(config.allowedKeyManagement.get(eq(WifiConfiguration.KeyMgmt.WPA_PSK))).thenReturn( |
| true); |
| when(config.getNetworkSelectionStatus()) |
| .thenReturn(new WifiConfiguration.NetworkSelectionStatus()); |
| when(mWifiConfigManager.getConfiguredNetworkWithoutMasking(0)).thenReturn(config); |
| mResources.setBoolean(R.bool.config_wifiSaeUpgradeEnabled, false); |
| mResources.setBoolean(R.bool.config_wifiSaeUpgradeOffloadEnabled, false); |
| |
| mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, 0, 0, sBSSID); |
| mLooper.dispatchAll(); |
| |
| verify(config, never()).setSecurityParams(eq(WifiConfiguration.SECURITY_TYPE_SAE)); |
| } |
| |
| /** |
| * Verifies that the logic always enables SAE key management when WPA3 auto upgrade offload |
| * feature is enabled. |
| * |
| * @throws Exception |
| */ |
| @Test |
| public void testWpa3UpgradeOffload() throws Exception { |
| initializeAndAddNetworkAndVerifySuccess(); |
| assertEquals(ClientModeImpl.CONNECT_MODE, mCmi.getOperationalModeForTest()); |
| assertEquals(WifiManager.WIFI_STATE_ENABLED, mCmi.syncGetWifiState()); |
| |
| WifiConfiguration config = mock(WifiConfiguration.class); |
| BitSet allowedKeyManagement = mock(BitSet.class); |
| BitSet allowedAuthAlgorithms = mock(BitSet.class); |
| config.allowedKeyManagement = allowedKeyManagement; |
| config.allowedAuthAlgorithms = allowedAuthAlgorithms; |
| when(config.allowedKeyManagement.get(eq(WifiConfiguration.KeyMgmt.WPA_PSK))).thenReturn( |
| true); |
| when(config.getNetworkSelectionStatus()) |
| .thenReturn(new WifiConfiguration.NetworkSelectionStatus()); |
| when(mWifiConfigManager.getConfiguredNetworkWithoutMasking(0)).thenReturn(config); |
| mResources.setBoolean(R.bool.config_wifiSaeUpgradeEnabled, true); |
| mResources.setBoolean(R.bool.config_wifiSaeUpgradeOffloadEnabled, true); |
| |
| mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, 0, 0, sBSSID); |
| mLooper.dispatchAll(); |
| |
| verify(allowedKeyManagement).set(eq(WifiConfiguration.KeyMgmt.SAE)); |
| verify(allowedAuthAlgorithms).clear(); |
| } |
| |
| /** |
| * Verifies that the logic does not enable SAE key management when WPA3 auto upgrade feature is |
| * enabled but no SAE candidate is available. |
| * |
| * @throws Exception |
| */ |
| @Test |
| public void testNoWpa3UpgradeWithPskCandidate() throws Exception { |
| initializeAndAddNetworkAndVerifySuccess(); |
| assertEquals(ClientModeImpl.CONNECT_MODE, mCmi.getOperationalModeForTest()); |
| assertEquals(WifiManager.WIFI_STATE_ENABLED, mCmi.syncGetWifiState()); |
| |
| WifiConfiguration config = mock(WifiConfiguration.class); |
| BitSet allowedKeyManagement = mock(BitSet.class); |
| config.allowedKeyManagement = allowedKeyManagement; |
| when(config.allowedKeyManagement.get(eq(WifiConfiguration.KeyMgmt.WPA_PSK))) |
| .thenReturn(true); |
| WifiConfiguration.NetworkSelectionStatus networkSelectionStatus = |
| mock(WifiConfiguration.NetworkSelectionStatus.class); |
| when(config.getNetworkSelectionStatus()).thenReturn(networkSelectionStatus); |
| when(mWifiConfigManager.getConfiguredNetworkWithoutMasking(0)).thenReturn(config); |
| mResources.setBoolean(R.bool.config_wifiSaeUpgradeEnabled, true); |
| mResources.setBoolean(R.bool.config_wifiSaeUpgradeOffloadEnabled, false); |
| |
| String ssid = "WPA2-Network"; |
| String caps = "[WPA2-FT/PSK+PSK][ESS][WPS]"; |
| ScanResult scanResult = new ScanResult(WifiSsid.createFromAsciiEncoded(ssid), ssid, |
| "ab:cd:01:ef:45:89", 1245, 0, caps, -78, 2450, 1025, 22, 33, 20, 0, |
| 0, true); |
| scanResult.informationElements = new ScanResult.InformationElement[]{ |
| createIE(ScanResult.InformationElement.EID_SSID, |
| ssid.getBytes(StandardCharsets.UTF_8)) |
| }; |
| when(networkSelectionStatus.getCandidate()).thenReturn(scanResult); |
| |
| mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, 0, 0, sBSSID); |
| mLooper.dispatchAll(); |
| |
| verify(config, never()).setSecurityParams(eq(WifiConfiguration.SECURITY_TYPE_SAE)); |
| } |
| |
| /** |
| * Verifies that the logic enables SAE key management when WPA3 auto upgrade feature is |
| * enabled and an SAE candidate is available. |
| * |
| * @throws Exception |
| */ |
| @Test |
| public void testWpa3UpgradeWithSaeCandidate() throws Exception { |
| initializeAndAddNetworkAndVerifySuccess(); |
| assertEquals(ClientModeImpl.CONNECT_MODE, mCmi.getOperationalModeForTest()); |
| assertEquals(WifiManager.WIFI_STATE_ENABLED, mCmi.syncGetWifiState()); |
| |
| WifiConfiguration config = mock(WifiConfiguration.class); |
| BitSet allowedKeyManagement = mock(BitSet.class); |
| BitSet allowedAuthAlgorithms = mock(BitSet.class); |
| BitSet allowedProtocols = mock(BitSet.class); |
| BitSet allowedPairwiseCiphers = mock(BitSet.class); |
| BitSet allowedGroupCiphers = mock(BitSet.class); |
| config.allowedKeyManagement = allowedKeyManagement; |
| config.allowedAuthAlgorithms = allowedAuthAlgorithms; |
| config.allowedProtocols = allowedProtocols; |
| config.allowedPairwiseCiphers = allowedPairwiseCiphers; |
| config.allowedGroupCiphers = allowedGroupCiphers; |
| when(config.allowedKeyManagement.get(eq(WifiConfiguration.KeyMgmt.WPA_PSK))) |
| .thenReturn(true); |
| WifiConfiguration.NetworkSelectionStatus networkSelectionStatus = |
| mock(WifiConfiguration.NetworkSelectionStatus.class); |
| when(config.getNetworkSelectionStatus()).thenReturn(networkSelectionStatus); |
| when(mWifiConfigManager.getConfiguredNetworkWithoutMasking(anyInt())).thenReturn(config); |
| when(mWifiConfigManager.getScanDetailCacheForNetwork(anyInt())).thenReturn(null); |
| mResources.setBoolean(R.bool.config_wifiSaeUpgradeEnabled, true); |
| mResources.setBoolean(R.bool.config_wifiSaeUpgradeOffloadEnabled, false); |
| |
| final String ssid = "WPA3-Network"; |
| String caps = "[WPA2-FT/SAE+SAE][ESS][WPS]"; |
| ScanResult scanResult = new ScanResult(WifiSsid.createFromAsciiEncoded(ssid), ssid, |
| "ab:cd:01:ef:45:89", 1245, 0, caps, -78, 2450, 1025, 22, 33, 20, 0, |
| 0, true); |
| scanResult.informationElements = new ScanResult.InformationElement[]{ |
| createIE(ScanResult.InformationElement.EID_SSID, |
| ssid.getBytes(StandardCharsets.UTF_8)) |
| }; |
| when(networkSelectionStatus.getCandidate()).thenReturn(scanResult); |
| |
| mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, 0, 0, sBSSID); |
| mLooper.dispatchAll(); |
| |
| verify(config).setSecurityParams(eq(WifiConfiguration.SECURITY_TYPE_SAE)); |
| } |
| |
| /** |
| * Verifies that the logic does not enable SAE key management when WPA3 auto upgrade feature is |
| * enabled and an SAE candidate is available, while another WPA2 AP is in range. |
| * |
| * @throws Exception |
| */ |
| @Test |
| public void testWpa3UpgradeWithSaeCandidateAndPskApInRange() throws Exception { |
| initializeAndAddNetworkAndVerifySuccess(); |
| assertEquals(ClientModeImpl.CONNECT_MODE, mCmi.getOperationalModeForTest()); |
| assertEquals(WifiManager.WIFI_STATE_ENABLED, mCmi.syncGetWifiState()); |
| |
| WifiConfiguration config = mock(WifiConfiguration.class); |
| BitSet allowedKeyManagement = mock(BitSet.class); |
| BitSet allowedAuthAlgorithms = mock(BitSet.class); |
| BitSet allowedProtocols = mock(BitSet.class); |
| BitSet allowedPairwiseCiphers = mock(BitSet.class); |
| BitSet allowedGroupCiphers = mock(BitSet.class); |
| config.allowedKeyManagement = allowedKeyManagement; |
| config.allowedAuthAlgorithms = allowedAuthAlgorithms; |
| config.allowedProtocols = allowedProtocols; |
| config.allowedPairwiseCiphers = allowedPairwiseCiphers; |
| config.allowedGroupCiphers = allowedGroupCiphers; |
| when(config.allowedKeyManagement.get(eq(WifiConfiguration.KeyMgmt.WPA_PSK))) |
| .thenReturn(true); |
| WifiConfiguration.NetworkSelectionStatus networkSelectionStatus = |
| mock(WifiConfiguration.NetworkSelectionStatus.class); |
| when(config.getNetworkSelectionStatus()).thenReturn(networkSelectionStatus); |
| when(mWifiConfigManager.getConfiguredNetworkWithoutMasking(anyInt())).thenReturn(config); |
| when(mWifiConfigManager.getScanDetailCacheForNetwork(anyInt())).thenReturn(null); |
| mResources.setBoolean(R.bool.config_wifiSaeUpgradeEnabled, true); |
| mResources.setBoolean(R.bool.config_wifiSaeUpgradeOffloadEnabled, false); |
| |
| final String saeBssid = "ab:cd:01:ef:45:89"; |
| final String pskBssid = "ab:cd:01:ef:45:9a"; |
| |
| final String ssidSae = "Mixed-Network"; |
| config.SSID = ScanResultUtil.createQuotedSSID(ssidSae); |
| config.networkId = 1; |
| |
| String capsSae = "[WPA2-FT/SAE+SAE][ESS][WPS]"; |
| ScanResult scanResultSae = new ScanResult(WifiSsid.createFromAsciiEncoded(ssidSae), ssidSae, |
| saeBssid, 1245, 0, capsSae, -78, 2412, 1025, 22, 33, 20, 0, |
| 0, true); |
| ScanResult.InformationElement ieSae = createIE(ScanResult.InformationElement.EID_SSID, |
| ssidSae.getBytes(StandardCharsets.UTF_8)); |
| scanResultSae.informationElements = new ScanResult.InformationElement[]{ieSae}; |
| when(networkSelectionStatus.getCandidate()).thenReturn(scanResultSae); |
| ScanResult.InformationElement[] ieArr = new ScanResult.InformationElement[1]; |
| ieArr[0] = ieSae; |
| |
| final String ssidPsk = "Mixed-Network"; |
| String capsPsk = "[WPA2-FT/PSK+PSK][ESS][WPS]"; |
| ScanResult scanResultPsk = new ScanResult(WifiSsid.createFromAsciiEncoded(ssidPsk), ssidPsk, |
| pskBssid, 1245, 0, capsPsk, -48, 2462, 1025, 22, 33, 20, 0, |
| 0, true); |
| ScanResult.InformationElement iePsk = createIE(ScanResult.InformationElement.EID_SSID, |
| ssidPsk.getBytes(StandardCharsets.UTF_8)); |
| scanResultPsk.informationElements = new ScanResult.InformationElement[]{iePsk}; |
| ScanResult.InformationElement[] ieArrPsk = new ScanResult.InformationElement[1]; |
| ieArrPsk[0] = iePsk; |
| List<ScanResult> scanResults = new ArrayList<>(); |
| scanResults.add(scanResultPsk); |
| scanResults.add(scanResultSae); |
| |
| when(mScanRequestProxy.getScanResults()).thenReturn(scanResults); |
| |
| mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, 0, 0, sBSSID); |
| mLooper.dispatchAll(); |
| |
| verify(config, never()).setSecurityParams(eq(WifiConfiguration.SECURITY_TYPE_SAE)); |
| } |
| |
| /** |
| * Verifies that the logic enables SAE key management when WPA3 auto upgrade feature is |
| * enabled and no candidate is available (i.e. user selected a WPA2 saved network and the |
| * network is actually WPA3. |
| * |
| * @throws Exception |
| */ |
| @Test |
| public void testWpa3UpgradeWithNoCandidate() throws Exception { |
| initializeAndAddNetworkAndVerifySuccess(); |
| assertEquals(ClientModeImpl.CONNECT_MODE, mCmi.getOperationalModeForTest()); |
| assertEquals(WifiManager.WIFI_STATE_ENABLED, mCmi.syncGetWifiState()); |
| |
| WifiConfiguration config = mock(WifiConfiguration.class); |
| BitSet allowedKeyManagement = mock(BitSet.class); |
| BitSet allowedAuthAlgorithms = mock(BitSet.class); |
| BitSet allowedProtocols = mock(BitSet.class); |
| BitSet allowedPairwiseCiphers = mock(BitSet.class); |
| BitSet allowedGroupCiphers = mock(BitSet.class); |
| config.allowedKeyManagement = allowedKeyManagement; |
| config.allowedAuthAlgorithms = allowedAuthAlgorithms; |
| config.allowedProtocols = allowedProtocols; |
| config.allowedPairwiseCiphers = allowedPairwiseCiphers; |
| config.allowedGroupCiphers = allowedGroupCiphers; |
| when(config.allowedKeyManagement.get(eq(WifiConfiguration.KeyMgmt.WPA_PSK))) |
| .thenReturn(true); |
| WifiConfiguration.NetworkSelectionStatus networkSelectionStatus = |
| mock(WifiConfiguration.NetworkSelectionStatus.class); |
| when(config.getNetworkSelectionStatus()).thenReturn(networkSelectionStatus); |
| when(mWifiConfigManager.getConfiguredNetworkWithoutMasking(anyInt())).thenReturn(config); |
| |
| mResources.setBoolean(R.bool.config_wifiSaeUpgradeEnabled, true); |
| mResources.setBoolean(R.bool.config_wifiSaeUpgradeOffloadEnabled, false); |
| |
| final String saeBssid = "ab:cd:01:ef:45:89"; |
| final String ssidSae = "WPA3-Network"; |
| config.SSID = ScanResultUtil.createQuotedSSID(ssidSae); |
| config.networkId = 1; |
| |
| String capsSae = "[WPA2-FT/SAE+SAE][ESS][WPS]"; |
| ScanResult scanResultSae = new ScanResult(WifiSsid.createFromAsciiEncoded(ssidSae), ssidSae, |
| saeBssid, 1245, 0, capsSae, -78, 2412, 1025, 22, 33, 20, 0, |
| 0, true); |
| ScanResult.InformationElement ieSae = createIE(ScanResult.InformationElement.EID_SSID, |
| ssidSae.getBytes(StandardCharsets.UTF_8)); |
| scanResultSae.informationElements = new ScanResult.InformationElement[]{ieSae}; |
| when(networkSelectionStatus.getCandidate()).thenReturn(null); |
| List<ScanResult> scanResults = new ArrayList<>(); |
| scanResults.add(scanResultSae); |
| |
| when(mScanRequestProxy.getScanResults()).thenReturn(scanResults); |
| |
| mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, 0, 0, sBSSID); |
| mLooper.dispatchAll(); |
| |
| verify(config).setSecurityParams(eq(WifiConfiguration.SECURITY_TYPE_SAE)); |
| } |
| |
| private static ScanResult.InformationElement createIE(int id, byte[] bytes) { |
| ScanResult.InformationElement ie = new ScanResult.InformationElement(); |
| ie.id = id; |
| ie.bytes = bytes; |
| return ie; |
| } |
| |
| /* |
| * Verify isWifiBandSupported for 5GHz with an overlay override config |
| */ |
| @Test |
| public void testIsWifiBandSupported5gWithOverride() throws Exception { |
| mResources.setBoolean(R.bool.config_wifi5ghzSupport, true); |
| assertTrue(mCmi.isWifiBandSupported(WifiScanner.WIFI_BAND_5_GHZ)); |
| verify(mWifiNative, never()).getChannelsForBand(anyInt()); |
| } |
| |
| /** |
| * Verify isWifiBandSupported for 6GHz with an overlay override config |
| */ |
| @Test |
| public void testIsWifiBandSupported6gWithOverride() throws Exception { |
| mResources.setBoolean(R.bool.config_wifi6ghzSupport, true); |
| assertTrue(mCmi.isWifiBandSupported(WifiScanner.WIFI_BAND_6_GHZ)); |
| verify(mWifiNative, never()).getChannelsForBand(anyInt()); |
| } |
| |
| /** |
| * Verify isWifiBandSupported for 5GHz with no overlay override config no channels |
| */ |
| @Test |
| public void testIsWifiBandSupported5gNoOverrideNoChannels() throws Exception { |
| final int[] emptyArray = {}; |
| mResources.setBoolean(R.bool.config_wifi5ghzSupport, false); |
| when(mWifiNative.getChannelsForBand(anyInt())).thenReturn(emptyArray); |
| assertFalse(mCmi.isWifiBandSupported(WifiScanner.WIFI_BAND_5_GHZ)); |
| verify(mWifiNative).getChannelsForBand(WifiScanner.WIFI_BAND_5_GHZ); |
| } |
| |
| /** |
| * Verify isWifiBandSupported for 5GHz with no overlay override config with channels |
| */ |
| @Test |
| public void testIsWifiBandSupported5gNoOverrideWithChannels() throws Exception { |
| final int[] channelArray = {5170}; |
| mResources.setBoolean(R.bool.config_wifi5ghzSupport, false); |
| when(mWifiNative.getChannelsForBand(anyInt())).thenReturn(channelArray); |
| assertTrue(mCmi.isWifiBandSupported(WifiScanner.WIFI_BAND_5_GHZ)); |
| verify(mWifiNative).getChannelsForBand(WifiScanner.WIFI_BAND_5_GHZ); |
| } |
| |
| /** |
| * Verify isWifiBandSupported for 6GHz with no overlay override config no channels |
| */ |
| @Test |
| public void testIsWifiBandSupported6gNoOverrideNoChannels() throws Exception { |
| final int[] emptyArray = {}; |
| mResources.setBoolean(R.bool.config_wifi6ghzSupport, false); |
| when(mWifiNative.getChannelsForBand(anyInt())).thenReturn(emptyArray); |
| assertFalse(mCmi.isWifiBandSupported(WifiScanner.WIFI_BAND_6_GHZ)); |
| verify(mWifiNative).getChannelsForBand(WifiScanner.WIFI_BAND_6_GHZ); |
| } |
| |
| /** |
| * Verify isWifiBandSupported for 6GHz with no overlay override config with channels |
| */ |
| @Test |
| public void testIsWifiBandSupported6gNoOverrideWithChannels() throws Exception { |
| final int[] channelArray = {6420}; |
| mResources.setBoolean(R.bool.config_wifi6ghzSupport, false); |
| when(mWifiNative.getChannelsForBand(anyInt())).thenReturn(channelArray); |
| assertTrue(mCmi.isWifiBandSupported(WifiScanner.WIFI_BAND_6_GHZ)); |
| verify(mWifiNative).getChannelsForBand(WifiScanner.WIFI_BAND_6_GHZ); |
| } |
| |
| /** |
| * Helper function for setting up fils test. |
| * |
| * @param isDriverSupportFils true if driver support fils. |
| * @return wifi configuration. |
| */ |
| private WifiConfiguration setupFilsTest(boolean isDriverSupportFils) { |
| assertEquals(ClientModeImpl.CONNECT_MODE, mCmi.getOperationalModeForTest()); |
| assertEquals(WifiManager.WIFI_STATE_ENABLED, mCmi.syncGetWifiState()); |
| |
| WifiConfiguration config = new WifiConfiguration(); |
| config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP); |
| config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP); |
| config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.IEEE8021X); |
| config.SSID = ScanResultUtil.createQuotedSSID(sFilsSsid); |
| config.networkId = 1; |
| config.setRandomizedMacAddress(TEST_LOCAL_MAC_ADDRESS); |
| config.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_PERSISTENT; |
| |
| when(mWifiConfigManager.getConfiguredNetworkWithoutMasking(anyInt())).thenReturn(config); |
| if (isDriverSupportFils) { |
| when(mWifiNative.getSupportedFeatureSet(WIFI_IFACE_NAME)).thenReturn( |
| WifiManager.WIFI_FEATURE_FILS_SHA256 | WifiManager.WIFI_FEATURE_FILS_SHA384); |
| } else { |
| when(mWifiNative.getSupportedFeatureSet(WIFI_IFACE_NAME)).thenReturn((long) 0); |
| } |
| |
| return config; |
| } |
| |
| /** |
| * Helper function for setting up a scan result with FILS supported AP. |
| * |
| */ |
| private void setupFilsEnabledApInScanResult() { |
| String caps = "[WPA2-EAP+EAP-SHA256+EAP-FILS-SHA256-CCMP]" |
| + "[RSN-EAP+EAP-SHA256+EAP-FILS-SHA256-CCMP][ESS]"; |
| ScanResult scanResult = new ScanResult(WifiSsid.createFromAsciiEncoded(sFilsSsid), |
| sFilsSsid, sBSSID, 1245, 0, caps, -78, 2412, 1025, 22, 33, 20, 0, 0, true); |
| ScanResult.InformationElement ie = createIE(ScanResult.InformationElement.EID_SSID, |
| sFilsSsid.getBytes(StandardCharsets.UTF_8)); |
| scanResult.informationElements = new ScanResult.InformationElement[]{ie}; |
| List<ScanResult> scanResults = new ArrayList<>(); |
| scanResults.add(scanResult); |
| |
| when(mScanRequestProxy.getScanResults()).thenReturn(scanResults); |
| } |
| |
| |
| /** |
| * Helper function to send CMD_START_FILS_CONNECTION along with HLP IEs. |
| * |
| */ |
| private void prepareFilsHlpPktAndSendStartConnect() { |
| Layer2PacketParcelable l2Packet = new Layer2PacketParcelable(); |
| l2Packet.dstMacAddress = TEST_GLOBAL_MAC_ADDRESS; |
| l2Packet.payload = new byte[] {0x00, 0x12, 0x13, 0x00, 0x12, 0x13, 0x00, 0x12, 0x13, |
| 0x12, 0x13, 0x00, 0x12, 0x13, 0x00, 0x12, 0x13, 0x00, 0x12, 0x13, 0x55, 0x66}; |
| mCmi.sendMessage(ClientModeImpl.CMD_START_FILS_CONNECTION, 0, 0, |
| Collections.singletonList(l2Packet)); |
| mLooper.dispatchAll(); |
| } |
| |
| /** |
| * Verifies that while connecting to AP, the logic looks into the scan result and |
| * looks for AP matching the network type and ssid and update the wificonfig with FILS |
| * AKM if supported. |
| * |
| * @throws Exception |
| */ |
| @Test |
| public void testFilsAKMUpdateBeforeConnect() throws Exception { |
| initializeAndAddNetworkAndVerifySuccess(); |
| WifiConfiguration config = setupFilsTest(true); |
| setupFilsEnabledApInScanResult(); |
| |
| mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, 0, 0, sBSSID); |
| mLooper.dispatchAll(); |
| |
| assertTrue(config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.FILS_SHA256)); |
| verify(mWifiNative, never()).connectToNetwork(eq(WIFI_IFACE_NAME), eq(config)); |
| } |
| |
| /** |
| * Verifies that while connecting to AP, framework updates the wifi config with |
| * FILS AKM only if underlying driver support FILS feature. |
| * |
| * @throws Exception |
| */ |
| @Test |
| public void testFilsAkmIsNotAddedinWifiConfigIfDriverDoesNotSupportFils() throws Exception { |
| initializeAndAddNetworkAndVerifySuccess(); |
| WifiConfiguration config = setupFilsTest(false); |
| setupFilsEnabledApInScanResult(); |
| |
| mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, 0, 0, sBSSID); |
| mLooper.dispatchAll(); |
| |
| assertFalse(config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.FILS_SHA256)); |
| verify(mWifiNative).connectToNetwork(eq(WIFI_IFACE_NAME), eq(config)); |
| } |
| |
| |
| /** |
| * Verifies that the HLP (DHCP) packets are send to wpa_supplicant |
| * prior to Fils connection. |
| * |
| * @throws Exception |
| */ |
| @Test |
| public void testFilsHlpUpdateBeforeFilsConnection() throws Exception { |
| initializeAndAddNetworkAndVerifySuccess(); |
| WifiConfiguration config = setupFilsTest(true); |
| setupFilsEnabledApInScanResult(); |
| |
| mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, 0, 0, sBSSID); |
| mLooper.dispatchAll(); |
| |
| prepareFilsHlpPktAndSendStartConnect(); |
| |
| verify(mWifiNative).flushAllHlp(eq(WIFI_IFACE_NAME)); |
| verify(mWifiNative).addHlpReq(eq(WIFI_IFACE_NAME), any(), any()); |
| verify(mWifiNative).connectToNetwork(eq(WIFI_IFACE_NAME), eq(config)); |
| } |
| |
| /** |
| * Verifies that an association rejection in first FILS connect attempt doesn't block |
| * the second connection attempt. |
| * |
| * @throws Exception |
| */ |
| @Test |
| public void testFilsSecondConnectAttemptIsNotBLockedAfterAssocReject() throws Exception { |
| initializeAndAddNetworkAndVerifySuccess(); |
| WifiConfiguration config = setupFilsTest(true); |
| setupFilsEnabledApInScanResult(); |
| |
| mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, 0, 0, sBSSID); |
| mLooper.dispatchAll(); |
| |
| prepareFilsHlpPktAndSendStartConnect(); |
| |
| verify(mWifiNative, times(1)).connectToNetwork(eq(WIFI_IFACE_NAME), eq(config)); |
| |
| mCmi.sendMessage(WifiMonitor.ASSOCIATION_REJECTION_EVENT, 0, 2, sBSSID); |
| mLooper.dispatchAll(); |
| |
| mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, 0, 0, sBSSID); |
| mLooper.dispatchAll(); |
| prepareFilsHlpPktAndSendStartConnect(); |
| |
| verify(mWifiNative, times(2)).connectToNetwork(eq(WIFI_IFACE_NAME), eq(config)); |
| } |
| |
| /** |
| * Verifies Fils connection. |
| * |
| * @throws Exception |
| */ |
| @Test |
| public void testFilsConnection() throws Exception { |
| initializeAndAddNetworkAndVerifySuccess(); |
| WifiConfiguration config = setupFilsTest(true); |
| setupFilsEnabledApInScanResult(); |
| |
| mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, 0, 0, sBSSID); |
| mLooper.dispatchAll(); |
| |
| prepareFilsHlpPktAndSendStartConnect(); |
| |
| verify(mWifiMetrics, times(1)).incrementConnectRequestWithFilsAkmCount(); |
| |
| mCmi.sendMessage(WifiMonitor.FILS_NETWORK_CONNECTION_EVENT, 0, 0, sBSSID); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiMetrics, times(1)).incrementL2ConnectionThroughFilsAuthCount(); |
| |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(0, WifiSsid.createFromAsciiEncoded(sFilsSsid), |
| sBSSID, SupplicantState.COMPLETED)); |
| mLooper.dispatchAll(); |
| |
| assertEquals("ObtainingIpState", getCurrentState().getName()); |
| |
| DhcpResultsParcelable dhcpResults = new DhcpResultsParcelable(); |
| dhcpResults.baseConfiguration = new StaticIpConfiguration(); |
| dhcpResults.baseConfiguration.gateway = InetAddresses.parseNumericAddress("1.2.3.4"); |
| dhcpResults.baseConfiguration.ipAddress = |
| new LinkAddress(InetAddresses.parseNumericAddress("192.168.1.100"), 0); |
| dhcpResults.baseConfiguration.dnsServers.add(InetAddresses.parseNumericAddress("8.8.8.8")); |
| dhcpResults.leaseDuration = 3600; |
| |
| injectDhcpSuccess(dhcpResults); |
| mLooper.dispatchAll(); |
| |
| WifiInfo wifiInfo = mCmi.getWifiInfo(); |
| assertNotNull(wifiInfo); |
| assertEquals(sBSSID, wifiInfo.getBSSID()); |
| assertTrue(WifiSsid.createFromAsciiEncoded(sFilsSsid).equals(wifiInfo.getWifiSsid())); |
| assertEquals("ConnectedState", getCurrentState().getName()); |
| } |
| |
| /** |
| * Tests the wifi info is updated correctly for connecting network. |
| */ |
| @Test |
| public void testWifiInfoOnConnectingNextNetwork() throws Exception { |
| |
| mConnectedNetwork.ephemeral = true; |
| mConnectedNetwork.trusted = true; |
| mConnectedNetwork.osu = true; |
| |
| triggerConnect(); |
| when(mWifiConfigManager.getScanDetailCacheForNetwork(FRAMEWORK_NETWORK_ID)) |
| .thenReturn(mScanDetailCache); |
| |
| when(mScanDetailCache.getScanDetail(sBSSID)).thenReturn( |
| getGoogleGuestScanDetail(TEST_RSSI, sBSSID, sFreq)); |
| when(mScanDetailCache.getScanResult(sBSSID)).thenReturn( |
| getGoogleGuestScanDetail(TEST_RSSI, sBSSID, sFreq).getScanResult()); |
| |
| // before the fist success connection, there is no valid wifi info. |
| assertEquals(WifiConfiguration.INVALID_NETWORK_ID, mCmi.getWifiInfo().getNetworkId()); |
| |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(FRAMEWORK_NETWORK_ID, |
| sWifiSsid, sBSSID, SupplicantState.ASSOCIATED)); |
| mLooper.dispatchAll(); |
| |
| // retrieve correct wifi info on receiving the supplicant state change event. |
| assertEquals(FRAMEWORK_NETWORK_ID, mCmi.getWifiInfo().getNetworkId()); |
| assertEquals(mConnectedNetwork.ephemeral, mCmi.getWifiInfo().isEphemeral()); |
| assertEquals(mConnectedNetwork.trusted, mCmi.getWifiInfo().isTrusted()); |
| assertEquals(mConnectedNetwork.osu, mCmi.getWifiInfo().isOsuAp()); |
| } |
| |
| /** |
| * Verify that we disconnect when we mark a previous unmetered network metered. |
| */ |
| @Test |
| public void verifyDisconnectOnMarkingNetworkMetered() throws Exception { |
| connect(); |
| expectRegisterNetworkAgent((config) -> { }, (cap) -> { |
| assertTrue(cap.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED)); |
| }); |
| |
| WifiConfiguration oldConfig = new WifiConfiguration(mConnectedNetwork); |
| mConnectedNetwork.meteredOverride = METERED_OVERRIDE_METERED; |
| |
| mConfigUpdateListenerCaptor.getValue().onNetworkUpdated(mConnectedNetwork, oldConfig); |
| mLooper.dispatchAll(); |
| assertEquals("DisconnectingState", getCurrentState().getName()); |
| } |
| |
| /** |
| * Verify that we only update capabilites when we mark a previous unmetered network metered. |
| */ |
| @Test |
| public void verifyUpdateCapabilitiesOnMarkingNetworkUnmetered() throws Exception { |
| mConnectedNetwork.meteredOverride = METERED_OVERRIDE_METERED; |
| connect(); |
| expectRegisterNetworkAgent((config) -> { }, (cap) -> { |
| assertFalse(cap.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED)); |
| }); |
| reset(mNetworkAgentHandler); |
| |
| WifiConfiguration oldConfig = new WifiConfiguration(mConnectedNetwork); |
| mConnectedNetwork.meteredOverride = METERED_OVERRIDE_NOT_METERED; |
| |
| mConfigUpdateListenerCaptor.getValue().onNetworkUpdated(mConnectedNetwork, oldConfig); |
| mLooper.dispatchAll(); |
| assertEquals("ConnectedState", getCurrentState().getName()); |
| |
| expectNetworkAgentUpdateCapabilities((cap) -> { |
| assertTrue(cap.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED)); |
| }); |
| } |
| |
| |
| /** |
| * Verify that we disconnect when we mark a previous unmetered network metered. |
| */ |
| @Test |
| public void verifyDisconnectOnMarkingNetworkAutoMeteredWithMeteredHint() throws Exception { |
| mConnectedNetwork.meteredOverride = METERED_OVERRIDE_NOT_METERED; |
| connect(); |
| expectRegisterNetworkAgent((config) -> { }, (cap) -> { |
| assertTrue(cap.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED)); |
| }); |
| reset(mNetworkAgentHandler); |
| |
| // Mark network metered none. |
| WifiConfiguration oldConfig = new WifiConfiguration(mConnectedNetwork); |
| mConnectedNetwork.meteredOverride = METERED_OVERRIDE_NONE; |
| |
| // Set metered hint in WifiInfo (either via DHCP or ScanResult IE). |
| WifiInfo wifiInfo = mCmi.getWifiInfo(); |
| wifiInfo.setMeteredHint(true); |
| |
| mConfigUpdateListenerCaptor.getValue().onNetworkUpdated(mConnectedNetwork, oldConfig); |
| mLooper.dispatchAll(); |
| assertEquals("DisconnectingState", getCurrentState().getName()); |
| } |
| |
| /** |
| * Verify that we only update capabilites when we mark a previous unmetered network metered. |
| */ |
| @Test |
| public void verifyUpdateCapabilitiesOnMarkingNetworkAutoMeteredWithoutMeteredHint() |
| throws Exception { |
| mConnectedNetwork.meteredOverride = METERED_OVERRIDE_METERED; |
| connect(); |
| expectRegisterNetworkAgent((config) -> { }, (cap) -> { |
| assertFalse(cap.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED)); |
| }); |
| reset(mNetworkAgentHandler); |
| |
| WifiConfiguration oldConfig = new WifiConfiguration(mConnectedNetwork); |
| mConnectedNetwork.meteredOverride = METERED_OVERRIDE_NONE; |
| |
| // Reset metered hint in WifiInfo. |
| WifiInfo wifiInfo = mCmi.getWifiInfo(); |
| wifiInfo.setMeteredHint(false); |
| |
| mConfigUpdateListenerCaptor.getValue().onNetworkUpdated(mConnectedNetwork, oldConfig); |
| mLooper.dispatchAll(); |
| assertEquals("ConnectedState", getCurrentState().getName()); |
| |
| expectNetworkAgentUpdateCapabilities((cap) -> { |
| assertTrue(cap.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED)); |
| }); |
| } |
| |
| /** |
| * Verify that we do nothing on no metered change. |
| */ |
| @Test |
| public void verifyDoNothingMarkingNetworkAutoMeteredWithMeteredHint() throws Exception { |
| mConnectedNetwork.meteredOverride = METERED_OVERRIDE_METERED; |
| connect(); |
| expectRegisterNetworkAgent((config) -> { }, (cap) -> { |
| assertFalse(cap.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED)); |
| }); |
| reset(mNetworkAgentHandler); |
| |
| // Mark network metered none. |
| WifiConfiguration oldConfig = new WifiConfiguration(mConnectedNetwork); |
| mConnectedNetwork.meteredOverride = METERED_OVERRIDE_NONE; |
| |
| // Set metered hint in WifiInfo (either via DHCP or ScanResult IE). |
| WifiInfo wifiInfo = mCmi.getWifiInfo(); |
| wifiInfo.setMeteredHint(true); |
| |
| mConfigUpdateListenerCaptor.getValue().onNetworkUpdated(mConnectedNetwork, oldConfig); |
| mLooper.dispatchAll(); |
| assertEquals("ConnectedState", getCurrentState().getName()); |
| |
| verifyNoMoreInteractions(mNetworkAgentHandler); |
| } |
| |
| /** |
| * Verify that we do nothing on no metered change. |
| */ |
| @Test |
| public void verifyDoNothingMarkingNetworkAutoMeteredWithoutMeteredHint() throws Exception { |
| mConnectedNetwork.meteredOverride = METERED_OVERRIDE_NOT_METERED; |
| connect(); |
| expectRegisterNetworkAgent((config) -> { }, (cap) -> { |
| assertTrue(cap.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED)); |
| }); |
| reset(mNetworkAgentHandler); |
| |
| // Mark network metered none. |
| WifiConfiguration oldConfig = new WifiConfiguration(mConnectedNetwork); |
| mConnectedNetwork.meteredOverride = METERED_OVERRIDE_NONE; |
| |
| // Reset metered hint in WifiInfo. |
| WifiInfo wifiInfo = mCmi.getWifiInfo(); |
| wifiInfo.setMeteredHint(false); |
| |
| mConfigUpdateListenerCaptor.getValue().onNetworkUpdated(mConnectedNetwork, oldConfig); |
| mLooper.dispatchAll(); |
| assertEquals("ConnectedState", getCurrentState().getName()); |
| |
| verifyNoMoreInteractions(mNetworkAgentHandler); |
| } |
| |
| /* |
| * Verify that network cached data is cleared correctly in |
| * disconnected state. |
| */ |
| @Test |
| public void testNetworkCachedDataIsClearedCorrectlyInDisconnectedState() throws Exception { |
| // Setup CONNECT_MODE & a WifiConfiguration |
| initializeAndAddNetworkAndVerifySuccess(); |
| mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, 0, 0, sBSSID); |
| mLooper.dispatchAll(); |
| |
| // got UNSPECIFIED during this connection attempt |
| mCmi.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, 0, 1, sBSSID); |
| mLooper.dispatchAll(); |
| |
| assertEquals("DisconnectedState", getCurrentState().getName()); |
| verify(mWifiNative, never()).removeNetworkCachedData(anyInt()); |
| |
| // got 4WAY_HANDSHAKE_TIMEOUT during this connection attempt |
| mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, 0, 0, sBSSID); |
| mLooper.dispatchAll(); |
| mCmi.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, 0, 15, sBSSID); |
| mLooper.dispatchAll(); |
| |
| assertEquals("DisconnectedState", getCurrentState().getName()); |
| verify(mWifiNative).removeNetworkCachedData(FRAMEWORK_NETWORK_ID); |
| } |
| |
| /* |
| * Verify that network cached data is cleared correctly in |
| * disconnected state. |
| */ |
| @Test |
| public void testNetworkCachedDataIsClearedCorrectlyInObtainingIpState() throws Exception { |
| initializeAndAddNetworkAndVerifySuccess(); |
| |
| verify(mWifiNative).removeAllNetworks(WIFI_IFACE_NAME); |
| |
| IActionListener connectActionListener = mock(IActionListener.class); |
| mCmi.connect(null, 0, mock(Binder.class), connectActionListener, 0, Binder.getCallingUid()); |
| mLooper.dispatchAll(); |
| verify(connectActionListener).onSuccess(); |
| |
| verify(mWifiConfigManager).enableNetwork(eq(0), eq(true), anyInt(), any()); |
| |
| mCmi.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, 0, 0, sBSSID); |
| mLooper.dispatchAll(); |
| |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(0, sWifiSsid, sBSSID, SupplicantState.COMPLETED)); |
| mLooper.dispatchAll(); |
| |
| assertEquals("ObtainingIpState", getCurrentState().getName()); |
| |
| // got 4WAY_HANDSHAKE_TIMEOUT during this connection attempt |
| mCmi.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, 0, 15, sBSSID); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiNative).removeNetworkCachedData(FRAMEWORK_NETWORK_ID); |
| } |
| |
| /* |
| * Verify that network cached data is NOT cleared in ConnectedState. |
| */ |
| @Test |
| public void testNetworkCachedDataIsClearedIf4WayHandshakeFailure() throws Exception { |
| when(mWifiScoreCard.detectAbnormalDisconnection()) |
| .thenReturn(WifiHealthMonitor.REASON_SHORT_CONNECTION_NONLOCAL); |
| InOrder inOrderWifiLockManager = inOrder(mWifiLockManager); |
| connect(); |
| inOrderWifiLockManager.verify(mWifiLockManager).updateWifiClientConnected(true); |
| |
| // got 4WAY_HANDSHAKE_TIMEOUT |
| mCmi.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, 0, 15, sBSSID); |
| mLooper.dispatchAll(); |
| verify(mWifiNative, never()).removeNetworkCachedData(anyInt()); |
| } |
| |
| /** |
| * Verify that network cached data is cleared on updating a network. |
| */ |
| @Test |
| public void testNetworkCachedDataIsClearedOnUpdatingNetwork() throws Exception { |
| WifiConfiguration oldConfig = new WifiConfiguration(mConnectedNetwork); |
| mConnectedNetwork.meteredOverride = METERED_OVERRIDE_METERED; |
| |
| mConfigUpdateListenerCaptor.getValue().onNetworkUpdated(mConnectedNetwork, oldConfig); |
| mLooper.dispatchAll(); |
| verify(mWifiNative).removeNetworkCachedData(eq(oldConfig.networkId)); |
| } |
| |
| |
| @Test |
| public void testIpReachabilityLostAndRoamEventsRace() throws Exception { |
| connect(); |
| expectRegisterNetworkAgent((agentConfig) -> { }, (cap) -> { }); |
| reset(mNetworkAgentHandler); |
| |
| // Trigger ip reachibility loss and ensure we trigger a disconnect & we're in |
| // "DisconnectingState" |
| mCmi.sendMessage(ClientModeImpl.CMD_IP_REACHABILITY_LOST); |
| mLooper.dispatchAll(); |
| verify(mWifiNative).disconnect(any()); |
| assertEquals("DisconnectingState", getCurrentState().getName()); |
| |
| // Now send a network connection (indicating a roam) event before we get the disconnect |
| // event. |
| mCmi.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, 0, 0, sBSSID); |
| mLooper.dispatchAll(); |
| // ensure that we ignored the transient roam while we're disconnecting. |
| assertEquals("DisconnectingState", getCurrentState().getName()); |
| verifyNoMoreInteractions(mNetworkAgentHandler); |
| |
| // Now send the disconnect event and ensure that we transition to "DisconnectedState". |
| mCmi.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, 0, 0, sBSSID); |
| mLooper.dispatchAll(); |
| assertEquals("DisconnectedState", getCurrentState().getName()); |
| expectUnregisterNetworkAgent(); |
| |
| verifyNoMoreInteractions(mNetworkAgentHandler); |
| } |
| |
| @Test |
| public void testSyncGetCurrentNetwork() throws Exception { |
| // syncGetCurrentNetwork() returns null when disconnected |
| mLooper.startAutoDispatch(); |
| assertNull(mCmi.syncGetCurrentNetwork(mCmiAsyncChannel)); |
| mLooper.stopAutoDispatch(); |
| |
| connect(); |
| |
| // syncGetCurrentNetwork() returns non-null Network when connected |
| mLooper.startAutoDispatch(); |
| assertEquals(mNetwork, mCmi.syncGetCurrentNetwork(mCmiAsyncChannel)); |
| mLooper.stopAutoDispatch(); |
| } |
| |
| @Test |
| public void clearRequestingPackageNameInWifiInfoOnConnectionFailure() throws Exception { |
| mConnectedNetwork.fromWifiNetworkSpecifier = true; |
| mConnectedNetwork.ephemeral = true; |
| mConnectedNetwork.creatorName = OP_PACKAGE_NAME; |
| |
| triggerConnect(); |
| |
| // association completed |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(0, sWifiSsid, sBSSID, SupplicantState.ASSOCIATED)); |
| mLooper.dispatchAll(); |
| |
| assertTrue(mCmi.getWifiInfo().isEphemeral()); |
| assertEquals(OP_PACKAGE_NAME, mCmi.getWifiInfo().getRequestingPackageName()); |
| |
| // fail the connection. |
| mCmi.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, FRAMEWORK_NETWORK_ID, 0, sBSSID); |
| mLooper.dispatchAll(); |
| |
| assertFalse(mCmi.getWifiInfo().isEphemeral()); |
| assertNull(mCmi.getWifiInfo().getRequestingPackageName()); |
| } |
| } |