| /* |
| * 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.NetworkInfo.DetailedState.AUTHENTICATING; |
| import static android.net.NetworkInfo.DetailedState.CONNECTED; |
| import static android.net.NetworkInfo.DetailedState.CONNECTING; |
| import static android.net.NetworkInfo.DetailedState.DISCONNECTED; |
| import static android.net.NetworkInfo.DetailedState.OBTAINING_IPADDR; |
| 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_PERMANENT; |
| import static android.net.wifi.WifiConfiguration.NetworkSelectionStatus.DISABLED_NO_INTERNET_TEMPORARY; |
| import static android.net.wifi.WifiConfiguration.NetworkSelectionStatus.DISABLED_UNWANTED_LOW_RSSI; |
| import static android.net.wifi.WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_PERMANENTLY_DISABLED; |
| import static android.net.wifi.WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE; |
| import static android.net.wifi.WifiManager.AddNetworkResult.STATUS_SUCCESS; |
| |
| import static com.android.server.wifi.ActiveModeManager.ROLE_CLIENT_PRIMARY; |
| import static com.android.server.wifi.ActiveModeManager.ROLE_CLIENT_SCAN_ONLY; |
| import static com.android.server.wifi.ActiveModeManager.ROLE_CLIENT_SECONDARY_LONG_LIVED; |
| import static com.android.server.wifi.ActiveModeManager.ROLE_CLIENT_SECONDARY_TRANSIENT; |
| import static com.android.server.wifi.ClientModeImpl.ARP_TABLE_PATH; |
| import static com.android.server.wifi.ClientModeImpl.CMD_PRE_DHCP_ACTION; |
| import static com.android.server.wifi.ClientModeImpl.CMD_PRE_DHCP_ACTION_COMPLETE; |
| import static com.android.server.wifi.ClientModeImpl.CMD_UNWANTED_NETWORK; |
| import static com.android.server.wifi.ClientModeImpl.WIFI_WORK_SOURCE; |
| import static com.android.server.wifi.WifiSettingsConfigStore.SECONDARY_WIFI_STA_FACTORY_MAC_ADDRESS; |
| import static com.android.server.wifi.WifiSettingsConfigStore.WIFI_STA_FACTORY_MAC_ADDRESS; |
| |
| 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.junit.Assert.fail; |
| import static org.junit.Assume.assumeFalse; |
| import static org.junit.Assume.assumeTrue; |
| 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.atLeast; |
| import static org.mockito.Mockito.atLeastOnce; |
| import static org.mockito.Mockito.clearInvocations; |
| 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.Manifest; |
| import android.annotation.NonNull; |
| import android.annotation.Nullable; |
| import android.app.ActivityManager; |
| import android.app.test.MockAnswerUtil.AnswerWithArguments; |
| import android.app.test.TestAlarmManager; |
| import android.content.BroadcastReceiver; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.pm.PackageManager; |
| import android.hardware.wifi.supplicant.V1_0.ISupplicantStaIfaceCallback; |
| import android.hardware.wifi.supplicant.V1_4.ISupplicantStaIfaceCallback.AssociationRejectionData; |
| import android.hardware.wifi.supplicant.V1_4.ISupplicantStaIfaceCallback.MboAssocDisallowedReasonCode; |
| import android.net.CaptivePortalData; |
| import android.net.DhcpResultsParcelable; |
| import android.net.InetAddresses; |
| import android.net.IpConfiguration; |
| import android.net.IpPrefix; |
| 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.NetworkProvider; |
| import android.net.NetworkSpecifier; |
| import android.net.ProvisioningConfigurationParcelable; |
| import android.net.RouteInfo; |
| import android.net.StaticIpConfiguration; |
| import android.net.Uri; |
| import android.net.apf.ApfCapabilities; |
| import android.net.ip.IIpClient; |
| import android.net.ip.IpClientCallbacks; |
| import android.net.networkstack.aidl.ip.ReachabilityLossInfoParcelable; |
| import android.net.networkstack.aidl.ip.ReachabilityLossReason; |
| import android.net.vcn.VcnManager; |
| import android.net.vcn.VcnNetworkPolicyResult; |
| import android.net.wifi.IActionListener; |
| import android.net.wifi.MloLink; |
| import android.net.wifi.ScanResult; |
| import android.net.wifi.SecurityParams; |
| import android.net.wifi.SupplicantState; |
| import android.net.wifi.WifiConfiguration; |
| import android.net.wifi.WifiConfiguration.KeyMgmt; |
| import android.net.wifi.WifiContext; |
| import android.net.wifi.WifiEnterpriseConfig; |
| import android.net.wifi.WifiInfo; |
| import android.net.wifi.WifiManager; |
| import android.net.wifi.WifiNetworkAgentSpecifier; |
| import android.net.wifi.WifiNetworkSpecifier; |
| 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.p2p.WifiP2pManager; |
| import android.net.wifi.util.ScanResultUtil; |
| import android.os.BatteryStatsManager; |
| import android.os.Binder; |
| import android.os.Bundle; |
| import android.os.Handler; |
| import android.os.HandlerThread; |
| import android.os.Looper; |
| import android.os.Messenger; |
| import android.os.PowerManager; |
| import android.os.Process; |
| import android.os.UserHandle; |
| import android.os.test.TestLooper; |
| import android.provider.Settings; |
| import android.telephony.TelephonyManager; |
| import android.test.mock.MockContentProvider; |
| import android.test.mock.MockContentResolver; |
| import android.util.ArraySet; |
| import android.util.LocalLog; |
| import android.util.Log; |
| import android.util.Pair; |
| import android.util.Range; |
| |
| 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.modules.utils.build.SdkLevel; |
| import com.android.server.wifi.ClientMode.LinkProbeCallback; |
| import com.android.server.wifi.ClientModeManagerBroadcastQueue.QueuedBroadcast; |
| import com.android.server.wifi.WifiNative.ConnectionCapabilities; |
| import com.android.server.wifi.WifiScoreCard.PerBssid; |
| import com.android.server.wifi.WifiScoreCard.PerNetwork; |
| 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.hotspot2.WnmData; |
| import com.android.server.wifi.p2p.WifiP2pServiceImpl; |
| 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.ActionListenerWrapper; |
| import com.android.server.wifi.util.NativeUtil; |
| import com.android.server.wifi.util.RssiUtilTest; |
| import com.android.server.wifi.util.WifiPermissionsUtil; |
| import com.android.wifi.resources.R; |
| |
| import org.junit.After; |
| import org.junit.Before; |
| import org.junit.Test; |
| import org.mockito.ArgumentCaptor; |
| import org.mockito.ArgumentMatcher; |
| import org.mockito.Captor; |
| import org.mockito.InOrder; |
| import org.mockito.Mock; |
| import org.mockito.MockitoAnnotations; |
| import org.mockito.MockitoSession; |
| import org.mockito.quality.Strictness; |
| import org.mockito.verification.VerificationMode; |
| |
| import java.io.BufferedReader; |
| import java.io.ByteArrayOutputStream; |
| import java.io.FileDescriptor; |
| import java.io.PrintWriter; |
| import java.io.StringWriter; |
| import java.lang.reflect.Field; |
| import java.lang.reflect.InvocationTargetException; |
| import java.lang.reflect.Method; |
| import java.net.Inet4Address; |
| import java.net.InetAddress; |
| import java.net.URL; |
| import java.nio.charset.StandardCharsets; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.BitSet; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Random; |
| import java.util.Set; |
| import java.util.StringJoiner; |
| 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 = ClientModeImpl.NUM_LOG_RECS_VERBOSE; |
| private static final int LOG_REC_LIMIT_IN_VERBOSE_MODE_LOW_RAM = |
| ClientModeImpl.NUM_LOG_RECS_VERBOSE_LOW_MEMORY; |
| private static final int FRAMEWORK_NETWORK_ID = 0; |
| private static final int PASSPOINT_NETWORK_ID = 1; |
| private static final int OTHER_NETWORK_ID = 47; |
| 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_LOCAL_MAC_ADDRESS_SECONDARY_DBS = |
| MacAddress.fromString("2a:53:43:c3:56:22"); |
| 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 static final long TEST_BSSID = 0x112233445566L; |
| private static final int TEST_DELAY_IN_SECONDS = 300; |
| |
| private static final int DEFINED_ERROR_CODE = 32764; |
| private static final String TEST_TERMS_AND_CONDITIONS_URL = |
| "https://policies.google.com/terms?hl=en-US"; |
| private static final String VENUE_URL = |
| "https://www.android.com/android-11/"; |
| private static final long[] TEST_RCOI_ARRAY = {0xcafeL, 0xbabaL}; |
| private static final long TEST_MATCHED_RCOI = TEST_RCOI_ARRAY[0]; |
| |
| private static final String TEST_AP_MLD_MAC_ADDRESS_STR = "02:03:04:05:06:07"; |
| private static final MacAddress TEST_AP_MLD_MAC_ADDRESS = |
| MacAddress.fromString(TEST_AP_MLD_MAC_ADDRESS_STR); |
| |
| private static final String TEST_MLO_LINK_ADDR_STR = "02:03:04:05:06:0A"; |
| private static final MacAddress TEST_MLO_LINK_ADDR = |
| MacAddress.fromString(TEST_MLO_LINK_ADDR_STR); |
| |
| private static final String TEST_MLO_LINK_ADDR_1_STR = "02:03:04:05:06:0B"; |
| private static final MacAddress TEST_MLO_LINK_ADDR_1 = |
| MacAddress.fromString(TEST_MLO_LINK_ADDR_1_STR); |
| |
| private static final int TEST_MLO_LINK_ID = 1; |
| private static final int TEST_MLO_LINK_ID_1 = 2; |
| |
| private static final int TEST_MLO_LINK_FREQ = 5160; |
| private static final int TEST_MLO_LINK_FREQ_1 = 2437; |
| |
| private static final String TEST_TDLS_PEER_ADDR_STR = "02:55:11:02:36:4C"; |
| |
| private long mBinderToken; |
| private MockitoSession mSession; |
| private TestNetworkParams mTestNetworkParams = new TestNetworkParams(); |
| |
| /** |
| * Helper class for setting the default parameters of the WifiConfiguration that gets used |
| * in connect(). |
| */ |
| class TestNetworkParams { |
| public boolean hasEverConnected = false; |
| } |
| |
| private static <T> T mockWithInterfaces(Class<T> class1, Class<?>... interfaces) { |
| return mock(class1, withSettings().extraInterfaces(interfaces)); |
| } |
| |
| private void enableDebugLogs() { |
| mCmi.enableVerboseLogging(true); |
| } |
| |
| 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 WifiContext getContext() throws Exception { |
| when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_WIFI_DIRECT)).thenReturn(true); |
| |
| WifiContext context = mock(WifiContext.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(mPowerManager); |
| when(context.getSystemService(PowerManager.class)).thenReturn(mPowerManager); |
| when(mPowerManager.isInteractive()).thenReturn(true); |
| when(mPowerManager.newWakeLock(anyInt(), anyString())).thenReturn( |
| mock(PowerManager.WakeLock.class)); |
| |
| mAlarmManager = new TestAlarmManager(); |
| when(context.getSystemService(Context.ALARM_SERVICE)).thenReturn( |
| mAlarmManager.getAlarmManager()); |
| |
| when(context.getOpPackageName()).thenReturn(OP_PACKAGE_NAME); |
| |
| when(context.getSystemService(ActivityManager.class)).thenReturn(mActivityManager); |
| |
| 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)); |
| |
| mUserAllContext = mock(Context.class); |
| when(context.createContextAsUser(UserHandle.ALL, 0)).thenReturn(mUserAllContext); |
| |
| 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(TEST_SSID); |
| NetworkDetail nd = new NetworkDetail(TEST_BSSID_STR, ie, new ArrayList<String>(), sFreq); |
| ScanDetail detail = new ScanDetail(nd, TEST_WIFI_SSID, 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 static ScanDetail getHiddenScanDetail(int rssi, String bssid, int freq) { |
| ScanResult.InformationElement ie = new ScanResult.InformationElement(); |
| WifiSsid ssid = WifiSsid.fromBytes(new byte[]{0x00, 0x00, 0x00, 0x00, 0x00, 0x00}); |
| ie.id = ScanResult.InformationElement.EID_SSID; |
| ie.bytes = ssid.getBytes(); |
| ScanResult.InformationElement[] ies = new ScanResult.InformationElement[1]; |
| ies[0] = ie; |
| NetworkDetail nd = new NetworkDetail(TEST_BSSID_STR, ies, new ArrayList<String>(), sFreq); |
| ScanDetail detail = new ScanDetail(nd, ssid, bssid, "", rssi, freq, |
| Long.MAX_VALUE, /* needed so that scan results aren't rejected because |
| there older than scan start */ |
| ies, new ArrayList<String>(), ScanResults.generateIERawDatafromScanResultIE(ies)); |
| |
| 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, TEST_BSSID_STR, sFreq)); |
| return list; |
| } |
| |
| private void injectDhcpSuccess(DhcpResultsParcelable dhcpResults) { |
| mIpClientCallback.onNewDhcpResults(dhcpResults); |
| mIpClientCallback.onProvisioningSuccess(new LinkProperties()); |
| } |
| |
| private void injectDhcpSuccess() { |
| 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); |
| } |
| |
| private void injectDhcpFailure() { |
| mIpClientCallback.onNewDhcpResults((DhcpResultsParcelable) null); |
| mIpClientCallback.onProvisioningFailure(new LinkProperties()); |
| } |
| |
| static final String TEST_SSID = "\"GoogleGuest\""; |
| static final String SSID_NO_QUOTE = TEST_SSID.replace("\"", ""); |
| static final WifiSsid TEST_WIFI_SSID = WifiSsid.fromUtf8Text(SSID_NO_QUOTE); |
| static final String TEST_SSID1 = "\"RandomSsid1\""; |
| static final String SSID_NO_QUOTE1 = TEST_SSID1.replace("\"", ""); |
| static final WifiSsid TEST_WIFI_SSID1 = WifiSsid.fromUtf8Text(SSID_NO_QUOTE1); |
| static final String TEST_BSSID_STR = "01:02:03:04:05:06"; |
| static final String TEST_BSSID_STR1 = "02:01:04:03:06:05"; |
| static final String TEST_BSSID_STR2 = "02:01:04:03:06:04"; |
| static final int sFreq = 2437; |
| static final int sFreq1 = 5240; |
| static final String WIFI_IFACE_NAME = "mockWlan"; |
| static final String sFilsSsid = "FILS-AP"; |
| static final ApfCapabilities APF_CAP = new ApfCapabilities(1, 2, 3); |
| static final long TEST_TX_BYTES = 6666; |
| static final long TEST_RX_BYTES = 8888; |
| static final int TEST_MULTICAST_LOCK_MAX_DTIM_MULTIPLIER = 1; |
| static final int TEST_IPV6_ONLY_NETWORK_MAX_DTIM_MULTIPLIER = 2; |
| static final int TEST_IPV4_ONLY_NETWORK_MAX_DTIM_MULTIPLIER = 9; |
| static final int TEST_DUAL_STACK_NETWORK_MAX_DTIM_MULTIPLIER = 2; |
| static final int TEST_CHANNEL = 6; |
| static final int TEST_CHANNEL_1 = 44; |
| |
| ClientModeImpl mCmi; |
| HandlerThread mWifiCoreThread; |
| HandlerThread mP2pThread; |
| HandlerThread mSyncThread; |
| TestAlarmManager mAlarmManager; |
| MockWifiMonitor mWifiMonitor; |
| TestLooper mLooper; |
| WifiContext mContext; |
| Context mUserAllContext; |
| MockResources mResources; |
| FrameworkFacade mFrameworkFacade; |
| IpClientCallbacks mIpClientCallback; |
| OsuProvider mOsuProvider; |
| WifiConfiguration mConnectedNetwork; |
| WifiConfiguration mTestConfig; |
| ExtendedWifiInfo mWifiInfo; |
| ConnectionCapabilities mConnectionCapabilities = new ConnectionCapabilities(); |
| |
| @Mock ActivityManager mActivityManager; |
| @Mock WifiNetworkAgent mWifiNetworkAgent; |
| @Mock SupplicantStateTracker mSupplicantStateTracker; |
| @Mock WifiMetrics mWifiMetrics; |
| @Mock WifiInjector mWifiInjector; |
| @Mock WifiLastResortWatchdog mWifiLastResortWatchdog; |
| @Mock WifiBlocklistMonitor mWifiBlocklistMonitor; |
| @Mock WifiConfigManager mWifiConfigManager; |
| @Mock WifiNative mWifiNative; |
| @Mock WifiScoreCard mWifiScoreCard; |
| @Mock PerNetwork mPerNetwork; |
| @Mock PerBssid mPerBssid; |
| @Mock WifiScoreCard.NetworkConnectionStats mPerNetworkRecentStats; |
| @Mock WifiHealthMonitor mWifiHealthMonitor; |
| @Mock WifiTrafficPoller mWifiTrafficPoller; |
| @Mock WifiConnectivityManager mWifiConnectivityManager; |
| @Mock WifiStateTracker mWifiStateTracker; |
| @Mock PasspointManager mPasspointManager; |
| @Mock WifiPermissionsUtil mWifiPermissionsUtil; |
| @Mock IIpClient mIpClient; |
| @Mock TelephonyManager mTelephonyManager; |
| @Mock TelephonyManager mDataTelephonyManager; |
| @Mock WrongPasswordNotifier mWrongPasswordNotifier; |
| @Mock Clock mClock; |
| @Mock ScanDetailCache mScanDetailCache; |
| @Mock WifiDiagnostics mWifiDiagnostics; |
| @Mock IProvisioningCallback mProvisioningCallback; |
| @Mock WakeupController mWakeupController; |
| @Mock WifiDataStall mWifiDataStall; |
| @Mock WifiNetworkFactory mWifiNetworkFactory; |
| @Mock UntrustedWifiNetworkFactory mUntrustedWifiNetworkFactory; |
| @Mock OemWifiNetworkFactory mOemWifiNetworkFactory; |
| @Mock RestrictedWifiNetworkFactory mRestrictedWifiNetworkFactory; |
| @Mock MultiInternetManager mMultiInternetManager; |
| @Mock WifiNetworkSuggestionsManager mWifiNetworkSuggestionsManager; |
| @Mock LinkProbeManager mLinkProbeManager; |
| @Mock PackageManager mPackageManager; |
| @Mock WifiLockManager mWifiLockManager; |
| @Mock AsyncChannel mNullAsyncChannel; |
| @Mock BatteryStatsManager mBatteryStatsManager; |
| @Mock MboOceController mMboOceController; |
| @Mock ConnectionFailureNotifier mConnectionFailureNotifier; |
| @Mock EapFailureNotifier mEapFailureNotifier; |
| @Mock SimRequiredNotifier mSimRequiredNotifier; |
| @Mock ThroughputPredictor mThroughputPredictor; |
| @Mock ScanRequestProxy mScanRequestProxy; |
| @Mock DeviceConfigFacade mDeviceConfigFacade; |
| @Mock Network mNetwork; |
| @Mock ConcreteClientModeManager mClientModeManager; |
| @Mock WifiScoreReport mWifiScoreReport; |
| @Mock PowerManager mPowerManager; |
| @Mock WifiP2pConnection mWifiP2pConnection; |
| @Mock WifiGlobals mWifiGlobals; |
| @Mock LinkProbeCallback mLinkProbeCallback; |
| @Mock ClientModeImplMonitor mCmiMonitor; |
| @Mock ClientModeManagerBroadcastQueue mBroadcastQueue; |
| @Mock WifiNetworkSelector mWifiNetworkSelector; |
| @Mock ActiveModeWarden mActiveModeWarden; |
| @Mock ClientModeManager mPrimaryClientModeManager; |
| @Mock WifiSettingsConfigStore mSettingsConfigStore; |
| @Mock Uri mMockUri; |
| @Mock WifiCarrierInfoManager mWifiCarrierInfoManager; |
| @Mock WifiPseudonymManager mWifiPseudonymManager; |
| @Mock WifiNotificationManager mWifiNotificationManager; |
| |
| @Mock WifiConnectivityHelper mWifiConnectivityHelper; |
| @Mock InsecureEapNetworkHandler mInsecureEapNetworkHandler; |
| @Mock ScanResult mScanResult; |
| @Mock HandlerThread mWifiHandlerThread; |
| @Mock SsidTranslator mSsidTranslator; |
| @Mock ApplicationQosPolicyRequestHandler mApplicationQosPolicyRequestHandler; |
| @Mock LocalLog mLocalLog; |
| @Mock WifiDeviceStateChangeManager mWifiDeviceStateChangeManager; |
| @Mock WifiCountryCode mWifiCountryCode; |
| |
| @Captor ArgumentCaptor<WifiConfigManager.OnNetworkUpdateListener> mConfigUpdateListenerCaptor; |
| @Captor ArgumentCaptor<WifiNetworkAgent.Callback> mWifiNetworkAgentCallbackCaptor; |
| @Captor ArgumentCaptor<WifiCarrierInfoManager.OnCarrierOffloadDisabledListener> |
| mOffloadDisabledListenerArgumentCaptor = ArgumentCaptor.forClass( |
| WifiCarrierInfoManager.OnCarrierOffloadDisabledListener.class); |
| @Captor ArgumentCaptor<BroadcastReceiver> mScreenStateBroadcastReceiverCaptor; |
| |
| @Captor |
| ArgumentCaptor<WifiDeviceStateChangeManager.StateChangeCallback> |
| mStateChangeCallbackArgumentCaptor; |
| |
| @Captor ArgumentCaptor<ProvisioningConfigurationParcelable> mProvisioningConfigurationCaptor; |
| WifiInfo mPrimaryWifiInfo; |
| |
| private void setUpWifiNative() throws Exception { |
| when(mWifiNative.getStaFactoryMacAddress(WIFI_IFACE_NAME)).thenReturn( |
| TEST_GLOBAL_MAC_ADDRESS); |
| doAnswer(new AnswerWithArguments() { |
| public void answer(WifiSettingsConfigStore.Key<String> key, Object macAddress) { |
| when(mSettingsConfigStore.get(WIFI_STA_FACTORY_MAC_ADDRESS)) |
| .thenReturn((String) macAddress); |
| } |
| }).when(mSettingsConfigStore).put(eq(WIFI_STA_FACTORY_MAC_ADDRESS), any(String.class)); |
| when(mWifiNative.getMacAddress(WIFI_IFACE_NAME)) |
| .thenReturn(TEST_GLOBAL_MAC_ADDRESS.toString()); |
| ConnectionCapabilities cap = new ConnectionCapabilities(); |
| cap.wifiStandard = ScanResult.WIFI_STANDARD_11AC; |
| when(mWifiNative.getConnectionCapabilities(WIFI_IFACE_NAME)) |
| .thenReturn(mConnectionCapabilities); |
| when(mWifiNative.setStaMacAddress(eq(WIFI_IFACE_NAME), anyObject())) |
| .then(new AnswerWithArguments() { |
| public boolean answer(String iface, MacAddress mac) { |
| when(mWifiNative.getMacAddress(iface)).thenReturn(mac.toString()); |
| return true; |
| } |
| }); |
| when(mWifiNative.connectToNetwork(any(), any())).thenReturn(true); |
| when(mWifiNative.getApfCapabilities(anyString())).thenReturn(APF_CAP); |
| when(mWifiNative.isQosPolicyFeatureEnabled()).thenReturn(true); |
| } |
| |
| /** Reset verify() counters on WifiNative, and restore when() mocks on mWifiNative */ |
| private void resetWifiNative() throws Exception { |
| reset(mWifiNative); |
| setUpWifiNative(); |
| } |
| |
| @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 = spy(new MockWifiMonitor()); |
| when(mTelephonyManager.createForSubscriptionId(anyInt())).thenReturn(mDataTelephonyManager); |
| when(mWifiNetworkFactory.getSpecificNetworkRequestUids(any(), any())) |
| .thenReturn(Collections.emptySet()); |
| when(mWifiNetworkFactory.getSpecificNetworkRequestUidAndPackageName(any(), any())) |
| .thenReturn(Pair.create(Process.INVALID_UID, "")); |
| setUpWifiNative(); |
| doAnswer(new AnswerWithArguments() { |
| public MacAddress answer( |
| WifiConfiguration config, boolean isForSecondaryDbs) { |
| MacAddress mac = config.getRandomizedMacAddress(); |
| if (isForSecondaryDbs) { |
| mac = MacAddressUtil.nextMacAddress(mac); |
| } |
| return mac; |
| } |
| }).when(mWifiConfigManager).getRandomizedMacAndUpdateIfNeeded(any(), anyBoolean()); |
| |
| mTestNetworkParams = new TestNetworkParams(); |
| when(mWifiNetworkFactory.hasConnectionRequests()).thenReturn(true); |
| when(mUntrustedWifiNetworkFactory.hasConnectionRequests()).thenReturn(true); |
| when(mOemWifiNetworkFactory.hasConnectionRequests()).thenReturn(true); |
| when(mMultiInternetManager.hasPendingConnectionRequests()).thenReturn(true); |
| when(mApplicationQosPolicyRequestHandler.isFeatureEnabled()).thenReturn(true); |
| when(mWifiInjector.getWifiHandlerLocalLog()).thenReturn(mLocalLog); |
| when(mWifiInjector.getWifiCountryCode()).thenReturn(mWifiCountryCode); |
| when(mWifiInjector.getApplicationQosPolicyRequestHandler()) |
| .thenReturn(mApplicationQosPolicyRequestHandler); |
| |
| mFrameworkFacade = getFrameworkFacade(); |
| mContext = getContext(); |
| mWifiInfo = new ExtendedWifiInfo(mWifiGlobals, WIFI_IFACE_NAME); |
| |
| when(mWifiGlobals.isConnectedMacRandomizationEnabled()).thenReturn(true); |
| mResources = getMockResources(); |
| mResources.setIntArray(R.array.config_wifiRssiLevelThresholds, |
| RssiUtilTest.RSSI_THRESHOLDS); |
| mResources.setInteger(R.integer.config_wifiLinkBandwidthUpdateThresholdPercent, 25); |
| mResources.setInteger(R.integer.config_wifiClientModeImplNumLogRecs, 100); |
| mResources.setBoolean(R.bool.config_wifiEnableLinkedNetworkRoaming, true); |
| when(mContext.getResources()).thenReturn(mResources); |
| |
| when(mWifiGlobals.getPollRssiIntervalMillis()).thenReturn(3000); |
| when(mWifiGlobals.getIpReachabilityDisconnectEnabled()).thenReturn(true); |
| |
| when(mFrameworkFacade.getIntegerSetting(mContext, |
| Settings.Global.WIFI_FREQUENCY_BAND, |
| WifiManager.WIFI_FREQUENCY_BAND_AUTO)).thenReturn( |
| WifiManager.WIFI_FREQUENCY_BAND_AUTO); |
| when(mFrameworkFacade.getTxBytes(eq(WIFI_IFACE_NAME))).thenReturn(TEST_TX_BYTES); |
| when(mFrameworkFacade.getRxBytes(eq(WIFI_IFACE_NAME))).thenReturn(TEST_RX_BYTES); |
| when(mFrameworkFacade.getTotalTxBytes()).thenReturn(TEST_TX_BYTES); |
| when(mFrameworkFacade.getTotalRxBytes()).thenReturn(TEST_RX_BYTES); |
| when(mWifiPermissionsUtil.checkNetworkSettingsPermission(anyInt())).thenReturn(true); |
| doAnswer(inv -> { |
| mIpClientCallback.onQuit(); |
| return null; |
| }).when(mIpClient).shutdown(); |
| when(mWifiNetworkAgent.getNetwork()).thenReturn(mNetwork); |
| |
| // static mocking |
| mSession = ExtendedMockito.mockitoSession().strictness(Strictness.LENIENT) |
| .mockStatic(WifiInjector.class, withSettings().lenient()) |
| .spyStatic(MacAddress.class) |
| .startMocking(); |
| when(WifiInjector.getInstance()).thenReturn(mWifiInjector); |
| |
| when(mWifiInjector.getActiveModeWarden()).thenReturn(mActiveModeWarden); |
| when(mActiveModeWarden.getPrimaryClientModeManager()).thenReturn(mPrimaryClientModeManager); |
| when(mPrimaryClientModeManager.getSupportedFeatures()).thenReturn( |
| WifiManager.WIFI_FEATURE_WPA3_SAE | WifiManager.WIFI_FEATURE_OWE); |
| when(mWifiInjector.getWifiGlobals()).thenReturn(mWifiGlobals); |
| when(mWifiInjector.getWifiHandlerThread()).thenReturn(mWifiHandlerThread); |
| when(mWifiInjector.getSsidTranslator()).thenReturn(mSsidTranslator); |
| when(mWifiInjector.getWifiDeviceStateChangeManager()) |
| .thenReturn(mWifiDeviceStateChangeManager); |
| when(mWifiHandlerThread.getLooper()).thenReturn(mLooper.getLooper()); |
| when(mWifiGlobals.isWpa3SaeUpgradeEnabled()).thenReturn(true); |
| when(mWifiGlobals.isOweUpgradeEnabled()).thenReturn(true); |
| when(mWifiGlobals.getClientModeImplNumLogRecs()).thenReturn(100); |
| when(mWifiGlobals.isSaveFactoryMacToConfigStoreEnabled()).thenReturn(true); |
| when(mWifiGlobals.getNetworkNotFoundEventThreshold()).thenReturn(3); |
| when(mWifiGlobals.getMacRandomizationUnsupportedSsidPrefixes()).thenReturn( |
| Collections.EMPTY_SET); |
| when(mWifiInjector.makeWifiNetworkAgent(any(), any(), any(), any(), any())) |
| .thenAnswer(new AnswerWithArguments() { |
| public WifiNetworkAgent answer( |
| @NonNull NetworkCapabilities nc, |
| @NonNull LinkProperties linkProperties, |
| @NonNull NetworkAgentConfig naConfig, |
| @Nullable NetworkProvider provider, |
| @NonNull WifiNetworkAgent.Callback callback) { |
| when(mWifiNetworkAgent.getCallback()).thenReturn(callback); |
| return mWifiNetworkAgent; |
| } |
| }); |
| when(mClientModeManager.getRole()).thenReturn(ROLE_CLIENT_PRIMARY); |
| // Update the WifiInfo in WifiActiveModeWarden |
| doAnswer(inv -> { |
| if (mCmi != null) { |
| mPrimaryWifiInfo = mCmi.getConnectionInfo(); |
| } |
| return null; |
| }).when(mActiveModeWarden).updateCurrentConnectionInfo(); |
| initializeCmi(); |
| // Retrieve factory MAC address on first bootup. |
| verify(mWifiNative).getStaFactoryMacAddress(WIFI_IFACE_NAME); |
| |
| 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(any())) |
| .thenReturn(WifiHealthMonitor.REASON_NO_FAILURE); |
| when(mPerNetwork.getRecentStats()).thenReturn(mPerNetworkRecentStats); |
| when(mWifiScoreCard.lookupNetwork(any())).thenReturn(mPerNetwork); |
| when(mWifiScoreCard.lookupBssid(any(), any())).thenReturn(mPerBssid); |
| when(mThroughputPredictor.predictMaxTxThroughput(any())).thenReturn(90); |
| when(mThroughputPredictor.predictMaxRxThroughput(any())).thenReturn(80); |
| |
| doAnswer(new AnswerWithArguments() { |
| public void answer(boolean shouldReduceNetworkScore) { |
| mCmi.setShouldReduceNetworkScore(shouldReduceNetworkScore); |
| } |
| }).when(mClientModeManager).setShouldReduceNetworkScore(anyBoolean()); |
| doAnswer(new AnswerWithArguments() { |
| public void answer(ClientModeManager manager, QueuedBroadcast broadcast) { |
| broadcast.send(); |
| } |
| }).when(mBroadcastQueue).queueOrSendBroadcast(any(), any()); |
| } |
| |
| private void validateConnectionInfo() { |
| // The WifiInfo#equals returns false for pre-S, don't enforce the check. |
| if (!SdkLevel.isAtLeastS()) return; |
| if (mClientModeManager.getRole() == ROLE_CLIENT_PRIMARY) { |
| assertEquals(mCmi.getConnectionInfo(), mPrimaryWifiInfo); |
| } |
| } |
| |
| private void initializeCmi() throws Exception { |
| mCmi = new ClientModeImpl(mContext, mWifiMetrics, mClock, mWifiScoreCard, mWifiStateTracker, |
| mWifiPermissionsUtil, mWifiConfigManager, mPasspointManager, |
| mWifiMonitor, mWifiDiagnostics, mWifiDataStall, |
| new ScoringParams(), new WifiThreadRunner(new Handler(mLooper.getLooper())), |
| mWifiNetworkSuggestionsManager, mWifiHealthMonitor, mThroughputPredictor, |
| mDeviceConfigFacade, mScanRequestProxy, mWifiInfo, mWifiConnectivityManager, |
| mWifiBlocklistMonitor, mConnectionFailureNotifier, |
| WifiInjector.REGULAR_NETWORK_CAPABILITIES_FILTER, mWifiNetworkFactory, |
| mUntrustedWifiNetworkFactory, mOemWifiNetworkFactory, mRestrictedWifiNetworkFactory, |
| mMultiInternetManager, mWifiLastResortWatchdog, mWakeupController, |
| mWifiLockManager, mFrameworkFacade, mLooper.getLooper(), |
| mWifiNative, mWrongPasswordNotifier, mWifiTrafficPoller, mLinkProbeManager, |
| 1, mBatteryStatsManager, mSupplicantStateTracker, mMboOceController, |
| mWifiCarrierInfoManager, mWifiPseudonymManager, mEapFailureNotifier, |
| mSimRequiredNotifier, mWifiScoreReport, mWifiP2pConnection, mWifiGlobals, |
| WIFI_IFACE_NAME, mClientModeManager, mCmiMonitor, |
| mBroadcastQueue, mWifiNetworkSelector, mTelephonyManager, mWifiInjector, |
| mSettingsConfigStore, false, mWifiNotificationManager, |
| mWifiConnectivityHelper); |
| mCmi.mInsecureEapNetworkHandler = mInsecureEapNetworkHandler; |
| |
| mWifiCoreThread = getCmiHandlerThread(mCmi); |
| |
| mBinderToken = Binder.clearCallingIdentity(); |
| |
| verify(mWifiConfigManager, atLeastOnce()).addOnNetworkUpdateListener( |
| mConfigUpdateListenerCaptor.capture()); |
| assertNotNull(mConfigUpdateListenerCaptor.getValue()); |
| |
| verify(mWifiCarrierInfoManager, atLeastOnce()).addOnCarrierOffloadDisabledListener( |
| mOffloadDisabledListenerArgumentCaptor.capture()); |
| assertNotNull(mOffloadDisabledListenerArgumentCaptor.getValue()); |
| |
| mCmi.enableVerboseLogging(true); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiLastResortWatchdog, atLeastOnce()).clearAllFailureCounts(); |
| assertEquals("DisconnectedState", getCurrentState().getName()); |
| validateConnectionInfo(); |
| |
| verify(mWifiDeviceStateChangeManager, atLeastOnce()) |
| .registerStateChangeCallback(mStateChangeCallbackArgumentCaptor.capture()); |
| } |
| |
| @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; |
| mCmi = null; |
| if (mSession != null) { |
| mSession.finishMocking(); |
| } |
| } |
| |
| /** |
| * Test that mode changes accurately reflect the value for isWifiEnabled. |
| */ |
| @Test |
| public void checkIsWifiEnabledForModeChanges() throws Exception { |
| // now disable client mode and verify the reported wifi state |
| mCmi.stop(); |
| mLooper.dispatchAll(); |
| verify(mContext, never()) |
| .sendStickyBroadcastAsUser(argThat(new WifiEnablingStateIntentMatcher()), any()); |
| } |
| |
| private static class WifiEnablingStateIntentMatcher implements ArgumentMatcher<Intent> { |
| @Override |
| public boolean matches(Intent intent) { |
| return WifiManager.WIFI_STATE_CHANGED_ACTION.equals(intent.getAction()) |
| && WifiManager.WIFI_STATE_ENABLING |
| == intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, |
| WifiManager.WIFI_STATE_DISABLED); |
| } |
| } |
| |
| private class NetworkStateChangedIntentMatcher implements ArgumentMatcher<Intent> { |
| private final NetworkInfo.DetailedState mState; |
| NetworkStateChangedIntentMatcher(NetworkInfo.DetailedState state) { |
| mState = state; |
| } |
| @Override |
| public boolean matches(Intent intent) { |
| if (WifiManager.NETWORK_STATE_CHANGED_ACTION != intent.getAction()) { |
| // not the correct type |
| return false; |
| } |
| NetworkInfo networkInfo = intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO); |
| return networkInfo.getDetailedState() == mState; |
| } |
| } |
| |
| private void canSaveNetworkConfig() throws Exception { |
| IActionListener connectActionListener = mock(IActionListener.class); |
| mCmi.saveNetwork( |
| new NetworkUpdateResult(TEST_NETWORK_ID), |
| new ActionListenerWrapper(connectActionListener), |
| Binder.getCallingUid(), OP_PACKAGE_NAME); |
| mLooper.dispatchAll(); |
| verify(connectActionListener).onSuccess(); |
| } |
| |
| /** |
| * Verifies that configs can be saved when in client mode. |
| */ |
| @Test |
| public void canSaveNetworkConfigInClientMode() throws Exception { |
| canSaveNetworkConfig(); |
| } |
| |
| /** |
| * Verifies that admin restricted configs can be saved without triggering a connection. |
| */ |
| @Test |
| public void canSaveAdminRestrictedNetworkWithoutConnecting() throws Exception { |
| assumeTrue(SdkLevel.isAtLeastT()); |
| |
| mWifiInfo.setNetworkId(WifiConfiguration.INVALID_NETWORK_ID); |
| IActionListener connectActionListener = mock(IActionListener.class); |
| when(mWifiPermissionsUtil.isAdminRestrictedNetwork(any())).thenReturn(true); |
| mCmi.saveNetwork( |
| new NetworkUpdateResult(TEST_NETWORK_ID, STATUS_SUCCESS, false, false, true, false), |
| new ActionListenerWrapper(connectActionListener), |
| Binder.getCallingUid(), OP_PACKAGE_NAME); |
| mLooper.dispatchAll(); |
| |
| verify(connectActionListener).onSuccess(); |
| verify(mWifiPermissionsUtil).isAdminRestrictedNetwork(any()); |
| verify(mClientModeManager, never()) |
| .setShouldReduceNetworkScore(false); |
| } |
| |
| /** |
| * Verifies that deprecated security type configs can be saved without triggering a connection. |
| */ |
| @Test |
| public void canSaveDeprecatedSecurityTypeNetworkWithoutConnecting() throws Exception { |
| mWifiInfo.setNetworkId(WifiConfiguration.INVALID_NETWORK_ID); |
| IActionListener connectActionListener = mock(IActionListener.class); |
| when(mWifiGlobals.isDeprecatedSecurityTypeNetwork(any())).thenReturn(true); |
| mCmi.saveNetwork( |
| new NetworkUpdateResult(TEST_NETWORK_ID, STATUS_SUCCESS, false, false, true, false), |
| new ActionListenerWrapper(connectActionListener), |
| Binder.getCallingUid(), OP_PACKAGE_NAME); |
| mLooper.dispatchAll(); |
| |
| verify(connectActionListener).onSuccess(); |
| verify(mClientModeManager, never()) |
| .setShouldReduceNetworkScore(false); |
| } |
| |
| private WifiConfiguration createTestNetwork(boolean isHidden) { |
| WifiConfiguration config = new WifiConfiguration(); |
| config.networkId = FRAMEWORK_NETWORK_ID; |
| config.SSID = TEST_SSID; |
| config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); |
| config.hiddenSSID = isHidden; |
| return config; |
| } |
| |
| private void initializeMocksForAddedNetwork(WifiConfiguration config) throws Exception { |
| when(mWifiConfigManager.getSavedNetworks(anyInt())).thenReturn(Arrays.asList(config)); |
| when(mWifiConfigManager.getConfiguredNetwork(0)).thenReturn(config); |
| when(mWifiConfigManager.getConfiguredNetworkWithoutMasking(0)).thenReturn(config); |
| } |
| |
| private void initializeMocksForAddedNetwork(boolean isHidden) throws Exception { |
| mTestConfig = createTestNetwork(isHidden); |
| initializeMocksForAddedNetwork(mTestConfig); |
| } |
| |
| private void initializeAndAddNetworkAndVerifySuccess(WifiConfiguration config) |
| throws Exception { |
| initializeMocksForAddedNetwork(config); |
| } |
| |
| private void initializeAndAddNetworkAndVerifySuccess() throws Exception { |
| initializeAndAddNetworkAndVerifySuccess(false); |
| } |
| |
| private void initializeAndAddNetworkAndVerifySuccess(boolean isHidden) throws Exception { |
| initializeMocksForAddedNetwork(isHidden); |
| } |
| |
| private void setupAndStartConnectSequence(WifiConfiguration config) throws Exception { |
| 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.connectNetwork( |
| new NetworkUpdateResult(config.networkId), |
| new ActionListenerWrapper(connectActionListener), |
| Binder.getCallingUid(), OP_PACKAGE_NAME); |
| mLooper.dispatchAll(); |
| verify(connectActionListener).onSuccess(); |
| } |
| |
| private void validateSuccessfulConnectSequence(WifiConfiguration config) { |
| verify(mWifiConnectivityManager).prepareForForcedConnection(eq(config.networkId)); |
| verify(mWifiConfigManager).getConfiguredNetworkWithoutMasking(eq(config.networkId)); |
| verify(mWifiNative).connectToNetwork(eq(WIFI_IFACE_NAME), eq(config)); |
| verify(mCmiMonitor).onConnectionStart(mClientModeManager); |
| assertEquals("L2ConnectingState", mCmi.getCurrentState().getName()); |
| } |
| |
| private void validateFailureConnectSequence(WifiConfiguration config) { |
| 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 { |
| WifiConfiguration config = mConnectedNetwork; |
| config.networkId = FRAMEWORK_NETWORK_ID; |
| config.setRandomizedMacAddress(TEST_LOCAL_MAC_ADDRESS); |
| config.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_AUTO; |
| config.getNetworkSelectionStatus().setHasEverConnected(mTestNetworkParams.hasEverConnected); |
| assertEquals(null, config.getNetworkSelectionStatus().getCandidateSecurityParams()); |
| setupAndStartConnectSequence(config); |
| validateSuccessfulConnectSequence(config); |
| assertEquals(config.getSecurityParamsList().stream() |
| .filter(WifiConfigurationUtil::isSecurityParamsValid) |
| .findFirst().orElse(null), |
| config.getNetworkSelectionStatus().getCandidateSecurityParams()); |
| mConnectedNetwork.getNetworkSelectionStatus().setLastUsedSecurityParams( |
| config.getNetworkSelectionStatus().getCandidateSecurityParams()); |
| } |
| |
| /** |
| * Tests the manual connection request will run network selection to find |
| * a proper security params, but not use the default one. |
| */ |
| @Test |
| public void triggerConnectWithUpgradeType() throws Exception { |
| String ssid = "TestOpenOweSsid"; |
| WifiConfiguration config = spy(WifiConfigurationTestUtil.createOpenOweNetwork( |
| ScanResultUtil.createQuotedSsid(ssid))); |
| doAnswer(new AnswerWithArguments() { |
| public WifiConfiguration answer(List<WifiCandidates.Candidate> candidates) { |
| config.getNetworkSelectionStatus().setCandidateSecurityParams( |
| SecurityParams.createSecurityParamsBySecurityType( |
| WifiConfiguration.SECURITY_TYPE_OWE)); |
| return config; |
| } |
| }).when(mWifiNetworkSelector).selectNetwork(any()); |
| String caps = "[RSN-OWE_TRANSITION]"; |
| ScanResult scanResult = new ScanResult(WifiSsid.fromUtf8Text(ssid), |
| ssid, TEST_BSSID_STR, 1245, 0, caps, -78, 2412, 1025, 22, 33, 20, 0, 0, true); |
| ScanResult.InformationElement ie = createIE(ScanResult.InformationElement.EID_SSID, |
| ssid.getBytes(StandardCharsets.UTF_8)); |
| scanResult.informationElements = new ScanResult.InformationElement[]{ie}; |
| when(mScanRequestProxy.getScanResults()).thenReturn(Arrays.asList(scanResult)); |
| |
| config.networkId = FRAMEWORK_NETWORK_ID; |
| config.setRandomizedMacAddress(TEST_LOCAL_MAC_ADDRESS); |
| config.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_AUTO; |
| config.getNetworkSelectionStatus().setHasEverConnected(mTestNetworkParams.hasEverConnected); |
| assertEquals(null, config.getNetworkSelectionStatus().getCandidateSecurityParams()); |
| |
| setupAndStartConnectSequence(config); |
| validateSuccessfulConnectSequence(config); |
| assertEquals(WifiConfiguration.SECURITY_TYPE_OWE, |
| config.getNetworkSelectionStatus().getCandidateSecurityParams().getSecurityType()); |
| } |
| |
| /** |
| * Tests the manual connection request will use the last used security params when there |
| * is no scan result. |
| */ |
| @Test |
| public void triggerConnectWithLastUsedSecurityParams() throws Exception { |
| String ssid = "TestOpenOweSsid"; |
| WifiConfiguration config = spy(WifiConfigurationTestUtil.createOpenOweNetwork( |
| ScanResultUtil.createQuotedSsid(ssid))); |
| when(mScanRequestProxy.getScanResults()).thenReturn(new ArrayList<>()); |
| |
| config.networkId = FRAMEWORK_NETWORK_ID; |
| config.setRandomizedMacAddress(TEST_LOCAL_MAC_ADDRESS); |
| config.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_AUTO; |
| config.getNetworkSelectionStatus().setHasEverConnected(mTestNetworkParams.hasEverConnected); |
| config.getNetworkSelectionStatus().setLastUsedSecurityParams( |
| SecurityParams.createSecurityParamsBySecurityType( |
| WifiConfiguration.SECURITY_TYPE_OWE)); |
| assertEquals(null, config.getNetworkSelectionStatus().getCandidateSecurityParams()); |
| |
| setupAndStartConnectSequence(config); |
| validateSuccessfulConnectSequence(config); |
| assertEquals(WifiConfiguration.SECURITY_TYPE_OWE, |
| config.getNetworkSelectionStatus().getCandidateSecurityParams().getSecurityType()); |
| } |
| |
| /** |
| * 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 { |
| WifiConfiguration config = WifiConfigurationTestUtil.createOpenNetwork(); |
| config.networkId = FRAMEWORK_NETWORK_ID; |
| when(mWifiPermissionsUtil.checkNetworkSettingsPermission(Process.myUid())) |
| .thenReturn(false); |
| setupAndStartConnectSequence(config); |
| 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 { |
| // Remove the network requests. |
| when(mWifiNetworkFactory.hasConnectionRequests()).thenReturn(false); |
| when(mUntrustedWifiNetworkFactory.hasConnectionRequests()).thenReturn(false); |
| when(mOemWifiNetworkFactory.hasConnectionRequests()).thenReturn(false); |
| when(mMultiInternetManager.hasPendingConnectionRequests()).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 testConnect() throws Exception { |
| connect(null); |
| } |
| |
| private void connect() throws Exception { |
| connect(null); |
| } |
| |
| /** |
| * Simulate a connect |
| * |
| * @param wnmDataForTermsAndConditions Use null unless it is required to simulate a terms and |
| * conditions acceptance notification from Passpoint |
| * @throws Exception |
| */ |
| private void connect(WnmData wnmDataForTermsAndConditions) throws Exception { |
| assertNull(mCmi.getConnectingWifiConfiguration()); |
| assertNull(mCmi.getConnectedWifiConfiguration()); |
| |
| triggerConnect(); |
| validateConnectionInfo(); |
| |
| assertNotNull(mCmi.getConnectingWifiConfiguration()); |
| assertNull(mCmi.getConnectedWifiConfiguration()); |
| |
| WifiConfiguration config = mWifiConfigManager.getConfiguredNetwork(FRAMEWORK_NETWORK_ID); |
| config.carrierId = CARRIER_ID_1; |
| when(mWifiConfigManager.getScanDetailCacheForNetwork(FRAMEWORK_NETWORK_ID)) |
| .thenReturn(mScanDetailCache); |
| |
| when(mScanDetailCache.getScanDetail(TEST_BSSID_STR)).thenReturn( |
| getGoogleGuestScanDetail(TEST_RSSI, TEST_BSSID_STR, sFreq)); |
| when(mScanDetailCache.getScanResult(TEST_BSSID_STR)).thenReturn( |
| getGoogleGuestScanDetail(TEST_RSSI, TEST_BSSID_STR, sFreq).getScanResult()); |
| ScanResult scanResult = new ScanResult(WifiSsid.fromUtf8Text(sFilsSsid), |
| sFilsSsid, TEST_BSSID_STR, 1245, 0, "", -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}; |
| when(mScanRequestProxy.getScanResult(eq(TEST_BSSID_STR))).thenReturn(scanResult); |
| |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(0, TEST_WIFI_SSID, TEST_BSSID_STR, sFreq, |
| SupplicantState.ASSOCIATED)); |
| mLooper.dispatchAll(); |
| validateConnectionInfo(); |
| |
| WifiSsid wifiSsid = WifiSsid.fromBytes( |
| NativeUtil.byteArrayFromArrayList(NativeUtil.decodeSsid(mConnectedNetwork.SSID))); |
| mCmi.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, |
| new NetworkConnectionEventInfo(0, wifiSsid, TEST_BSSID_STR, false, null)); |
| mLooper.dispatchAll(); |
| // WifiInfo should be updated if it is primary |
| validateConnectionInfo(); |
| |
| verify(mWifiMetrics).noteFirstL2ConnectionAfterBoot(true); |
| |
| // L2 connected, but not L3 connected yet. So, still "Connecting"... |
| assertNotNull(mCmi.getConnectingWifiConfiguration()); |
| assertNull(mCmi.getConnectedWifiConfiguration()); |
| |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(0, TEST_WIFI_SSID, TEST_BSSID_STR, sFreq, |
| SupplicantState.COMPLETED)); |
| mLooper.dispatchAll(); |
| validateConnectionInfo(); |
| |
| assertEquals("L3ProvisioningState", getCurrentState().getName()); |
| verifyNetworkStateChangedBroadcast(times(1), |
| new NetworkStateChangedIntentMatcher(CONNECTING)); |
| verifyNetworkStateChangedBroadcast(times(1), |
| new NetworkStateChangedIntentMatcher(OBTAINING_IPADDR)); |
| |
| if (wnmDataForTermsAndConditions != null) { |
| mCmi.sendMessage(WifiMonitor.HS20_TERMS_AND_CONDITIONS_ACCEPTANCE_REQUIRED_EVENT, |
| 0, 0, wnmDataForTermsAndConditions); |
| mLooper.dispatchAll(); |
| } |
| |
| 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(); |
| |
| assertNull(mCmi.getConnectingWifiConfiguration()); |
| assertNotNull(mCmi.getConnectedWifiConfiguration()); |
| |
| // Verify WifiMetrics logging for metered metrics based on DHCP results |
| verify(mWifiMetrics).addMeteredStat(any(), anyBoolean()); |
| WifiInfo wifiInfo = mWifiInfo; |
| assertNotNull(wifiInfo); |
| assertEquals(TEST_BSSID_STR, wifiInfo.getBSSID()); |
| assertEquals(sFreq, wifiInfo.getFrequency()); |
| assertEquals(TEST_WIFI_SSID, wifiInfo.getWifiSsid()); |
| assertNotEquals(WifiInfo.DEFAULT_MAC_ADDRESS, wifiInfo.getMacAddress()); |
| assertEquals(mConnectedNetwork.getDefaultSecurityParams().getSecurityType(), |
| mWifiInfo.getCurrentSecurityType()); |
| if (wifiInfo.isPasspointAp()) { |
| assertEquals(wifiInfo.getPasspointProviderFriendlyName(), |
| WifiConfigurationTestUtil.TEST_PROVIDER_FRIENDLY_NAME); |
| } else { |
| assertNull(wifiInfo.getPasspointProviderFriendlyName()); |
| } |
| assertEquals(Arrays.asList(scanResult.informationElements), |
| wifiInfo.getInformationElements()); |
| assertNotNull(wifiInfo.getNetworkKey()); |
| expectRegisterNetworkAgent((na) -> { |
| if (!mConnectedNetwork.carrierMerged) { |
| assertNull(na.subscriberId); |
| } |
| }, (nc) -> { |
| if (SdkLevel.isAtLeastS()) { |
| WifiInfo wifiInfoFromTi = (WifiInfo) nc.getTransportInfo(); |
| assertEquals(TEST_BSSID_STR, wifiInfoFromTi.getBSSID()); |
| assertEquals(sFreq, wifiInfoFromTi.getFrequency()); |
| assertEquals(TEST_WIFI_SSID, wifiInfoFromTi.getWifiSsid()); |
| if (wifiInfo.isPasspointAp()) { |
| assertEquals(wifiInfoFromTi.getPasspointProviderFriendlyName(), |
| WifiConfigurationTestUtil.TEST_PROVIDER_FRIENDLY_NAME); |
| } else { |
| assertNull(wifiInfoFromTi.getPasspointProviderFriendlyName()); |
| } |
| } |
| }); |
| |
| assertFalse(mCmi.isIpProvisioningTimedOut()); |
| // Ensure the connection stats for the network is updated. |
| verify(mWifiConfigManager).updateNetworkAfterConnect(eq(FRAMEWORK_NETWORK_ID), |
| anyBoolean(), anyBoolean(), anyInt()); |
| verify(mWifiConfigManager).updateRandomizedMacExpireTime(any(), anyLong()); |
| verifyNetworkStateChangedBroadcast(times(1), |
| new NetworkStateChangedIntentMatcher(CONNECTED)); |
| |
| // Anonymous Identity is not set. |
| assertEquals("", mConnectedNetwork.enterpriseConfig.getAnonymousIdentity()); |
| verify(mWifiStateTracker).updateState(WIFI_IFACE_NAME, WifiStateTracker.CONNECTED); |
| assertEquals("L3ConnectedState", getCurrentState().getName()); |
| verify(mWifiMetrics).incrementNumOfCarrierWifiConnectionSuccess(); |
| verify(mWifiLockManager).updateWifiClientConnected(mClientModeManager, true); |
| verify(mWifiNative).getConnectionCapabilities(any()); |
| verify(mThroughputPredictor).predictMaxTxThroughput(any()); |
| verify(mWifiMetrics).setConnectionMaxSupportedLinkSpeedMbps(WIFI_IFACE_NAME, 90, 80); |
| assertEquals(90, wifiInfo.getMaxSupportedTxLinkSpeedMbps()); |
| verify(mWifiMetrics).noteFirstL3ConnectionAfterBoot(true); |
| validateConnectionInfo(); |
| } |
| |
| private void connectWithIpProvisionTimeout(boolean lateDhcpResponse) throws Exception { |
| mResources.setBoolean(R.bool.config_wifiRemainConnectedAfterIpProvisionTimeout, true); |
| assertNull(mCmi.getConnectingWifiConfiguration()); |
| assertNull(mCmi.getConnectedWifiConfiguration()); |
| |
| triggerConnect(); |
| validateConnectionInfo(); |
| |
| assertNotNull(mCmi.getConnectingWifiConfiguration()); |
| assertNull(mCmi.getConnectedWifiConfiguration()); |
| |
| WifiConfiguration config = mWifiConfigManager.getConfiguredNetwork(FRAMEWORK_NETWORK_ID); |
| config.carrierId = CARRIER_ID_1; |
| when(mWifiConfigManager.getScanDetailCacheForNetwork(FRAMEWORK_NETWORK_ID)) |
| .thenReturn(mScanDetailCache); |
| |
| when(mScanDetailCache.getScanDetail(TEST_BSSID_STR)).thenReturn( |
| getGoogleGuestScanDetail(TEST_RSSI, TEST_BSSID_STR, sFreq)); |
| when(mScanDetailCache.getScanResult(TEST_BSSID_STR)).thenReturn( |
| getGoogleGuestScanDetail(TEST_RSSI, TEST_BSSID_STR, sFreq).getScanResult()); |
| ScanResult scanResult = new ScanResult(WifiSsid.fromUtf8Text(sFilsSsid), |
| sFilsSsid, TEST_BSSID_STR, 1245, 0, "", -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}; |
| when(mScanRequestProxy.getScanResult(eq(TEST_BSSID_STR))).thenReturn(scanResult); |
| |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(0, TEST_WIFI_SSID, TEST_BSSID_STR, sFreq, |
| SupplicantState.ASSOCIATED)); |
| mLooper.dispatchAll(); |
| validateConnectionInfo(); |
| |
| WifiSsid wifiSsid = WifiSsid.fromBytes( |
| NativeUtil.byteArrayFromArrayList(NativeUtil.decodeSsid(mConnectedNetwork.SSID))); |
| mCmi.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, |
| new NetworkConnectionEventInfo(0, wifiSsid, TEST_BSSID_STR, false, null)); |
| mLooper.dispatchAll(); |
| // WifiInfo should be updated if it is primary |
| validateConnectionInfo(); |
| |
| verify(mWifiMetrics).noteFirstL2ConnectionAfterBoot(true); |
| |
| // L2 connected, but not L3 connected yet. So, still "Connecting"... |
| assertNotNull(mCmi.getConnectingWifiConfiguration()); |
| assertNull(mCmi.getConnectedWifiConfiguration()); |
| |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(0, TEST_WIFI_SSID, TEST_BSSID_STR, sFreq, |
| SupplicantState.COMPLETED)); |
| mLooper.dispatchAll(); |
| validateConnectionInfo(); |
| |
| assertEquals("L3ProvisioningState", getCurrentState().getName()); |
| verifyNetworkStateChangedBroadcast(times(1), |
| new NetworkStateChangedIntentMatcher(CONNECTING)); |
| verifyNetworkStateChangedBroadcast(times(1), |
| new NetworkStateChangedIntentMatcher(OBTAINING_IPADDR)); |
| mLooper.dispatchAll(); |
| |
| mLooper.moveTimeForward(mCmi.WAIT_FOR_L3_PROVISIONING_TIMEOUT_MS); |
| mLooper.dispatchAll(); |
| verify(mWifiConnectivityManager).handleConnectionStateChanged(mClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_CONNECTED); |
| assertTrue(mCmi.isIpProvisioningTimedOut()); |
| verify(mWifiConfigManager).setIpProvisioningTimedOut(eq(FRAMEWORK_NETWORK_ID), eq(true)); |
| |
| if (!lateDhcpResponse) { |
| return; |
| } |
| |
| // Simulate the scenario of a late response of the dhcp after the network is labeled a local |
| // only due to ip provisioning timeout. |
| |
| 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(); |
| assertNull(mCmi.getConnectingWifiConfiguration()); |
| assertNotNull(mCmi.getConnectedWifiConfiguration()); |
| |
| // Verify WifiMetrics logging for metered metrics based on DHCP results |
| verify(mWifiMetrics).addMeteredStat(any(), anyBoolean()); |
| WifiInfo wifiInfo = mWifiInfo; |
| assertNotNull(wifiInfo); |
| assertEquals(TEST_BSSID_STR, wifiInfo.getBSSID()); |
| assertEquals(sFreq, wifiInfo.getFrequency()); |
| assertEquals(TEST_WIFI_SSID, wifiInfo.getWifiSsid()); |
| assertNotEquals(WifiInfo.DEFAULT_MAC_ADDRESS, wifiInfo.getMacAddress()); |
| assertEquals(mConnectedNetwork.getDefaultSecurityParams().getSecurityType(), |
| mWifiInfo.getCurrentSecurityType()); |
| |
| if (wifiInfo.isPasspointAp()) { |
| assertEquals(wifiInfo.getPasspointProviderFriendlyName(), |
| WifiConfigurationTestUtil.TEST_PROVIDER_FRIENDLY_NAME); |
| } else { |
| assertNull(wifiInfo.getPasspointProviderFriendlyName()); |
| } |
| assertEquals(Arrays.asList(scanResult.informationElements), |
| wifiInfo.getInformationElements()); |
| assertNotNull(wifiInfo.getNetworkKey()); |
| expectRegisterNetworkAgent((na) -> { |
| if (!mConnectedNetwork.carrierMerged) { |
| assertNull(na.subscriberId); |
| } |
| }, (nc) -> { |
| if (SdkLevel.isAtLeastS()) { |
| WifiInfo wifiInfoFromTi = (WifiInfo) nc.getTransportInfo(); |
| assertEquals(TEST_BSSID_STR, wifiInfoFromTi.getBSSID()); |
| assertEquals(sFreq, wifiInfoFromTi.getFrequency()); |
| assertEquals(TEST_WIFI_SSID, wifiInfoFromTi.getWifiSsid()); |
| if (wifiInfo.isPasspointAp()) { |
| assertEquals(wifiInfoFromTi.getPasspointProviderFriendlyName(), |
| WifiConfigurationTestUtil.TEST_PROVIDER_FRIENDLY_NAME); |
| } else { |
| assertNull(wifiInfoFromTi.getPasspointProviderFriendlyName()); |
| } |
| } |
| }); |
| |
| assertFalse(mCmi.isIpProvisioningTimedOut()); |
| verify(mWifiConfigManager).setIpProvisioningTimedOut(eq(FRAMEWORK_NETWORK_ID), eq(false)); |
| |
| // Ensure the connection stats for the network is updated. |
| verify(mWifiConfigManager).updateRandomizedMacExpireTime(any(), anyLong()); |
| verifyNetworkStateChangedBroadcast(atLeast(1), |
| new NetworkStateChangedIntentMatcher(CONNECTED)); |
| |
| //Ensure that the network is registered |
| verify(mWifiConfigManager).updateNetworkAfterConnect( |
| eq(FRAMEWORK_NETWORK_ID), anyBoolean(), anyBoolean(), anyInt()); |
| verify(mWifiConnectivityManager, times(2)).handleConnectionStateChanged(mClientModeManager, |
| WifiConnectivityManager.WIFI_STATE_CONNECTED); |
| |
| |
| // Anonymous Identity is not set. |
| assertEquals("", mConnectedNetwork.enterpriseConfig.getAnonymousIdentity()); |
| verify(mWifiStateTracker).updateState(WIFI_IFACE_NAME, WifiStateTracker.CONNECTED); |
| assertEquals("L3ConnectedState", getCurrentState().getName()); |
| verify(mWifiMetrics).incrementNumOfCarrierWifiConnectionSuccess(); |
| verify(mWifiLockManager).updateWifiClientConnected(mClientModeManager, true); |
| verify(mWifiNative).getConnectionCapabilities(any()); |
| verify(mThroughputPredictor).predictMaxTxThroughput(any()); |
| verify(mWifiMetrics).setConnectionMaxSupportedLinkSpeedMbps(WIFI_IFACE_NAME, 90, 80); |
| assertEquals(90, wifiInfo.getMaxSupportedTxLinkSpeedMbps()); |
| verify(mWifiMetrics).noteFirstL3ConnectionAfterBoot(true); |
| validateConnectionInfo(); |
| |
| } |
| |
| @Test |
| public void testConnectWithIpProvisioningTimeout() throws Exception { |
| connectWithIpProvisionTimeout(false); |
| } |
| |
| @Test |
| public void testConnectWithIpProvisioningTimeoutLateDhcpResponse() throws Exception { |
| connectWithIpProvisionTimeout(true); |
| } |
| |
| private void verifyNetworkStateChangedBroadcast(VerificationMode mode, |
| ArgumentCaptor<Intent> intentCaptor) { |
| if (SdkLevel.isAtLeastU()) { |
| verify(mUserAllContext, mode).sendStickyBroadcast(intentCaptor.capture(), any()); |
| } else { |
| verify(mContext, mode).sendStickyBroadcastAsUser(intentCaptor.capture(), any()); |
| } |
| } |
| |
| private void verifyNetworkStateChangedBroadcast(VerificationMode mode, |
| ArgumentMatcher<Intent> intentMatcher) { |
| if (SdkLevel.isAtLeastU()) { |
| verify(mUserAllContext, mode).sendStickyBroadcast(argThat(intentMatcher), any()); |
| } else { |
| verify(mContext, mode).sendStickyBroadcastAsUser(argThat(intentMatcher), any()); |
| } |
| } |
| |
| private void inOrderVerifyNetworkStateChangedBroadcasts( |
| ArgumentMatcher<Intent>... intentMatchers) { |
| final InOrder inOrder = SdkLevel.isAtLeastU() |
| ? inOrder(mUserAllContext) : inOrder(mContext); |
| for (ArgumentMatcher<Intent> intentMatcher : intentMatchers) { |
| if (SdkLevel.isAtLeastU()) { |
| inOrder.verify(mUserAllContext).sendStickyBroadcast(argThat(intentMatcher), any()); |
| } else { |
| inOrder.verify(mContext).sendStickyBroadcastAsUser(argThat(intentMatcher), any()); |
| } |
| } |
| } |
| |
| private void setupEapSimConnection() throws Exception { |
| mConnectedNetwork = spy(WifiConfigurationTestUtil.createEapNetwork( |
| WifiEnterpriseConfig.Eap.SIM, WifiEnterpriseConfig.Phase2.NONE)); |
| mConnectedNetwork.carrierId = CARRIER_ID_1; |
| when(mWifiCarrierInfoManager.getBestMatchSubscriptionId(any(WifiConfiguration.class))) |
| .thenReturn(DATA_SUBID); |
| when(mWifiCarrierInfoManager.isSimReady(DATA_SUBID)).thenReturn(true); |
| mConnectedNetwork.enterpriseConfig.setAnonymousIdentity(""); |
| |
| triggerConnect(); |
| |
| when(mWifiConfigManager.getScanDetailCacheForNetwork(FRAMEWORK_NETWORK_ID)) |
| .thenReturn(mScanDetailCache); |
| when(mScanDetailCache.getScanDetail(TEST_BSSID_STR)).thenReturn( |
| getGoogleGuestScanDetail(TEST_RSSI, TEST_BSSID_STR, sFreq)); |
| when(mScanDetailCache.getScanResult(TEST_BSSID_STR)).thenReturn( |
| getGoogleGuestScanDetail(TEST_RSSI, TEST_BSSID_STR, sFreq).getScanResult()); |
| |
| WifiSsid wifiSsid = WifiSsid.fromBytes( |
| NativeUtil.byteArrayFromArrayList(NativeUtil.decodeSsid(mConnectedNetwork.SSID))); |
| mCmi.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, |
| new NetworkConnectionEventInfo(0, wifiSsid, TEST_BSSID_STR, false, null)); |
| mLooper.dispatchAll(); |
| assertEquals("L3ProvisioningState", getCurrentState().getName()); |
| } |
| |
| @Test |
| public void testUpdatingOobPseudonymToSupplicant() throws Exception { |
| when(mWifiCarrierInfoManager.isOobPseudonymFeatureEnabled(anyInt())).thenReturn(true); |
| when(mDeviceConfigFacade.isOobPseudonymEnabled()).thenReturn(true); |
| |
| ArgumentCaptor<WifiPseudonymManager.PseudonymUpdatingListener> listenerCaptor = |
| ArgumentCaptor.forClass(WifiPseudonymManager.PseudonymUpdatingListener.class); |
| setupEapSimConnection(); |
| verify(mWifiPseudonymManager).registerPseudonymUpdatingListener(listenerCaptor.capture()); |
| String expectedPseudonym = "abc-123"; |
| String expectedDecoratedPseudonym = "abc-123@wlan.mnc456.mcc123.3gppnetwork.org"; |
| when(mWifiCarrierInfoManager.decoratePseudonymWith3GppRealm(any(), eq(expectedPseudonym))) |
| .thenReturn(expectedDecoratedPseudonym); |
| when(mWifiNative.isSupplicantAidlServiceVersionAtLeast(1)).thenReturn(true); |
| listenerCaptor.getValue().onUpdated(CARRIER_ID_1, expectedPseudonym); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiNative).setEapAnonymousIdentity( |
| anyString(), eq(expectedDecoratedPseudonym), eq(true)); |
| |
| mCmi.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, |
| new DisconnectEventInfo(mConnectedNetwork.SSID, TEST_BSSID_STR, 0, false)); |
| mLooper.dispatchAll(); |
| |
| assertEquals("DisconnectedState", getCurrentState().getName()); |
| verify(mWifiPseudonymManager) |
| .unregisterPseudonymUpdatingListener(eq(listenerCaptor.getValue())); |
| } |
| |
| @Test |
| public void testNotUpdatingOobPseudonymToSupplicant() throws Exception { |
| when(mWifiCarrierInfoManager.isOobPseudonymFeatureEnabled(anyInt())).thenReturn(true); |
| when(mDeviceConfigFacade.isOobPseudonymEnabled()).thenReturn(true); |
| |
| ArgumentCaptor<WifiPseudonymManager.PseudonymUpdatingListener> listenerCaptor = |
| ArgumentCaptor.forClass(WifiPseudonymManager.PseudonymUpdatingListener.class); |
| setupEapSimConnection(); |
| verify(mWifiPseudonymManager).registerPseudonymUpdatingListener(listenerCaptor.capture()); |
| String expectedPseudonym = "abc-123"; |
| String expectedDecoratedPseudonym = "abc-123@wlan.mnc456.mcc123.3gppnetwork.org"; |
| when(mWifiCarrierInfoManager.decoratePseudonymWith3GppRealm(any(), eq(expectedPseudonym))) |
| .thenReturn(expectedDecoratedPseudonym); |
| when(mWifiNative.isSupplicantAidlServiceVersionAtLeast(1)).thenReturn(false); |
| listenerCaptor.getValue().onUpdated(CARRIER_ID_1, expectedPseudonym); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiNative, never()).setEapAnonymousIdentity( |
| anyString(), anyString(), anyBoolean()); |
| |
| mCmi.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, |
| new DisconnectEventInfo(mConnectedNetwork.SSID, TEST_BSSID_STR, 0, false)); |
| mLooper.dispatchAll(); |
| |
| assertEquals("DisconnectedState", getCurrentState().getName()); |
| verify(mWifiPseudonymManager) |
| .unregisterPseudonymUpdatingListener(eq(listenerCaptor.getValue())); |
| } |
| |
| /** |
| * Test when a roam occurs simultaneously with another connection attempt. |
| * The roam's NETWORK_CONNECTION_EVENT should be ignored, only the new network's |
| * NETWORK_CONNECTION_EVENT should be acted upon. |
| */ |
| @Test |
| public void roamRaceWithConnect() throws Exception { |
| connect(); |
| |
| initializeAndAddNetworkAndVerifySuccess(); |
| |
| // connect to new network |
| WifiConfiguration config = WifiConfigurationTestUtil.createOpenNetwork(); |
| config.networkId = OTHER_NETWORK_ID; |
| setupAndStartConnectSequence(config); |
| |
| // in L2ConnectingState |
| assertEquals("L2ConnectingState", getCurrentState().getName()); |
| |
| // send NETWORK_CONNECTION_EVENT for previous network ID |
| mCmi.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, |
| new NetworkConnectionEventInfo( |
| FRAMEWORK_NETWORK_ID, TEST_WIFI_SSID, TEST_BSSID_STR, false, null)); |
| mLooper.dispatchAll(); |
| |
| // should ignore it, stay in L2ConnectingState |
| assertEquals("L2ConnectingState", getCurrentState().getName()); |
| |
| // send expected new network SSID |
| WifiSsid wifiSsid = WifiSsid.fromBytes( |
| NativeUtil.byteArrayFromArrayList(NativeUtil.decodeSsid(config.SSID))); |
| mCmi.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, |
| new NetworkConnectionEventInfo( |
| OTHER_NETWORK_ID, wifiSsid, TEST_BSSID_STR1, false, null)); |
| mLooper.dispatchAll(); |
| |
| // then move to next state |
| assertEquals("L3ProvisioningState", getCurrentState().getName()); |
| } |
| |
| @Test |
| public void testSimAuthRequestIsHandledWhileAlreadyConnectedSuccess() throws Exception { |
| connect(); |
| |
| WifiCarrierInfoManager.SimAuthRequestData requestData = |
| new WifiCarrierInfoManager.SimAuthRequestData(); |
| requestData.protocol = WifiEnterpriseConfig.Eap.SIM; |
| requestData.networkId = FRAMEWORK_NETWORK_ID; |
| String testSimAuthResponse = "TEST_SIM_AUTH_RESPONSE"; |
| when(mWifiCarrierInfoManager.getGsmSimAuthResponse(any(), any())) |
| .thenReturn(testSimAuthResponse); |
| mCmi.sendMessage(WifiMonitor.SUP_REQUEST_SIM_AUTH, requestData); |
| mLooper.dispatchAll(); |
| |
| // Expect success |
| verify(mWifiNative).simAuthResponse(WIFI_IFACE_NAME, WifiNative.SIM_AUTH_RESP_TYPE_GSM_AUTH, |
| testSimAuthResponse); |
| } |
| |
| @Test |
| public void testSimAuthRequestIsHandledWhileAlreadyConnectedFail() throws Exception { |
| connect(); |
| |
| WifiCarrierInfoManager.SimAuthRequestData requestData = |
| new WifiCarrierInfoManager.SimAuthRequestData(); |
| requestData.protocol = WifiEnterpriseConfig.Eap.SIM; |
| requestData.networkId = FRAMEWORK_NETWORK_ID; |
| // Mock WifiCarrierInfoManager to return null so that sim auth fails. |
| when(mWifiCarrierInfoManager.getGsmSimAuthResponse(any(), any())).thenReturn(null); |
| mCmi.sendMessage(WifiMonitor.SUP_REQUEST_SIM_AUTH, requestData); |
| mLooper.dispatchAll(); |
| |
| // Expect failure |
| verify(mWifiNative).simAuthFailedResponse(WIFI_IFACE_NAME); |
| } |
| |
| /** |
| * 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).isSimReady(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("L3ProvisioningState", 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 { |
| when(mWifiCarrierInfoManager.isOobPseudonymFeatureEnabled(anyInt())).thenReturn(true); |
| setupEapSimConnection(); |
| doReturn(false).when(mWifiCarrierInfoManager).isSimReady(eq(DATA_SUBID)); |
| mCmi.sendMessage(ClientModeImpl.CMD_RESET_SIM_NETWORKS, |
| ClientModeImpl.RESET_SIM_REASON_SIM_REMOVED); |
| mLooper.dispatchAll(); |
| |
| verify(mSimRequiredNotifier).showSimRequiredNotification(any(), any()); |
| verify(mWifiNative, times(2)).removeAllNetworks(WIFI_IFACE_NAME); |
| verify(mWifiMetrics).startConnectionEvent( |
| anyString(), any(), anyString(), anyInt(), eq(true), anyInt()); |
| } |
| |
| /** |
| * 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 testResetSimWhenConnectedSimRemovedAfterNetworkRemoval() throws Exception { |
| setupEapSimConnection(); |
| doReturn(false).when(mWifiCarrierInfoManager).isSimReady(eq(DATA_SUBID)); |
| when(mWifiConfigManager.getConfiguredNetwork(anyInt())).thenReturn(null); |
| mCmi.sendMessage(ClientModeImpl.CMD_RESET_SIM_NETWORKS, |
| ClientModeImpl.RESET_SIM_REASON_SIM_REMOVED); |
| mLooper.dispatchAll(); |
| |
| verify(mSimRequiredNotifier, never()).showSimRequiredNotification(any(), any()); |
| assertEquals("L3ProvisioningState", 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(); |
| |
| verify(mWifiNative, times(2)).removeAllNetworks(WIFI_IFACE_NAME); |
| verify(mWifiMetrics).logStaEvent(anyString(), eq(StaEvent.TYPE_FRAMEWORK_DISCONNECT), |
| eq(StaEvent.DISCONNECT_RESET_SIM_NETWORKS)); |
| verify(mSimRequiredNotifier, never()).showSimRequiredNotification(any(), anyString()); |
| } |
| |
| /** |
| * 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)); |
| mConnectedNetwork.enterpriseConfig.setAnonymousIdentity(""); |
| String expectedAnonymousIdentity = "anonymous@wlan.mnc456.mcc123.3gppnetwork.org"; |
| |
| when(mWifiCarrierInfoManager.getBestMatchSubscriptionId(any(WifiConfiguration.class))) |
| .thenReturn(DATA_SUBID); |
| when(mWifiCarrierInfoManager.isSimReady(DATA_SUBID)).thenReturn(true); |
| when(mWifiCarrierInfoManager.isImsiEncryptionInfoAvailable(anyInt())).thenReturn(true); |
| when(mWifiCarrierInfoManager.getAnonymousIdentityWith3GppRealm(any())) |
| .thenReturn(expectedAnonymousIdentity); |
| |
| // 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(TEST_BSSID_STR)).thenReturn( |
| getGoogleGuestScanDetail(TEST_RSSI, TEST_BSSID_STR, sFreq)); |
| when(mScanDetailCache.getScanResult(TEST_BSSID_STR)).thenReturn( |
| getGoogleGuestScanDetail(TEST_RSSI, TEST_BSSID_STR, sFreq).getScanResult()); |
| when(mWifiNative.getEapAnonymousIdentity(anyString())) |
| .thenReturn(expectedAnonymousIdentity); |
| |
| WifiSsid wifiSsid = WifiSsid.fromBytes( |
| NativeUtil.byteArrayFromArrayList(NativeUtil.decodeSsid(mConnectedNetwork.SSID))); |
| mCmi.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, |
| new NetworkConnectionEventInfo(0, wifiSsid, TEST_BSSID_STR, false, null)); |
| 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()); |
| } |
| |
| /** |
| * 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)); |
| mConnectedNetwork.enterpriseConfig.setAnonymousIdentity(""); |
| String expectedAnonymousIdentity = "anonymous@wlan.mnc456.mcc123.3gppnetwork.org"; |
| String pseudonym = "83bcca9384fca@wlan.mnc456.mcc123.3gppnetwork.org"; |
| |
| when(mWifiCarrierInfoManager.getBestMatchSubscriptionId(any(WifiConfiguration.class))) |
| .thenReturn(DATA_SUBID); |
| when(mWifiCarrierInfoManager.isSimReady(DATA_SUBID)).thenReturn(true); |
| when(mWifiCarrierInfoManager.isImsiEncryptionInfoAvailable(anyInt())).thenReturn(true); |
| when(mWifiCarrierInfoManager.getAnonymousIdentityWith3GppRealm(any())) |
| .thenReturn(expectedAnonymousIdentity); |
| |
| 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(TEST_BSSID_STR)).thenReturn( |
| getGoogleGuestScanDetail(TEST_RSSI, TEST_BSSID_STR, sFreq)); |
| when(mScanDetailCache.getScanResult(TEST_BSSID_STR)).thenReturn( |
| getGoogleGuestScanDetail(TEST_RSSI, TEST_BSSID_STR, sFreq).getScanResult()); |
| when(mWifiNative.getEapAnonymousIdentity(anyString())) |
| .thenReturn(pseudonym); |
| when(mWifiNative.isSupplicantAidlServiceVersionAtLeast(1)).thenReturn(true); |
| |
| WifiSsid wifiSsid = WifiSsid.fromBytes( |
| NativeUtil.byteArrayFromArrayList(NativeUtil.decodeSsid(mConnectedNetwork.SSID))); |
| mCmi.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, |
| new NetworkConnectionEventInfo(0, wifiSsid, TEST_BSSID_STR, false, null)); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiNative).getEapAnonymousIdentity(any()); |
| // No decorated pseudonum, only update the cached data. |
| verify(mWifiNative).setEapAnonymousIdentity(any(), eq(pseudonym), eq(false)); |
| 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()); |
| } |
| |
| /** |
| * 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)); |
| mConnectedNetwork.enterpriseConfig.setAnonymousIdentity(""); |
| String pseudonym = "83bcca9384fca"; |
| String realm = "wlan.mnc456.mcc123.3gppnetwork.org"; |
| String expectedAnonymousIdentity = "anonymous@wlan.mnc456.mcc123.3gppnetwork.org"; |
| |
| when(mWifiCarrierInfoManager.getBestMatchSubscriptionId(any(WifiConfiguration.class))) |
| .thenReturn(DATA_SUBID); |
| when(mWifiCarrierInfoManager.isSimReady(DATA_SUBID)).thenReturn(true); |
| when(mWifiCarrierInfoManager.isImsiEncryptionInfoAvailable(anyInt())).thenReturn(true); |
| when(mWifiCarrierInfoManager.getAnonymousIdentityWith3GppRealm(any())) |
| .thenReturn(expectedAnonymousIdentity); |
| doAnswer(invocation -> { return invocation.getArgument(1) + "@" + realm; }) |
| .when(mWifiCarrierInfoManager).decoratePseudonymWith3GppRealm(any(), anyString()); |
| 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(TEST_BSSID_STR)).thenReturn( |
| getGoogleGuestScanDetail(TEST_RSSI, TEST_BSSID_STR, sFreq)); |
| when(mScanDetailCache.getScanResult(TEST_BSSID_STR)).thenReturn( |
| getGoogleGuestScanDetail(TEST_RSSI, TEST_BSSID_STR, sFreq).getScanResult()); |
| when(mWifiNative.getEapAnonymousIdentity(anyString())) |
| .thenReturn(pseudonym); |
| when(mWifiNative.isSupplicantAidlServiceVersionAtLeast(1)).thenReturn(true); |
| |
| WifiSsid wifiSsid = WifiSsid.fromBytes( |
| NativeUtil.byteArrayFromArrayList(NativeUtil.decodeSsid(mConnectedNetwork.SSID))); |
| mCmi.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, |
| new NetworkConnectionEventInfo(0, wifiSsid, TEST_BSSID_STR, false, null)); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiNative).getEapAnonymousIdentity(any()); |
| verify(mWifiNative).setEapAnonymousIdentity(any(), eq(pseudonym + "@" + realm), |
| eq(true)); |
| 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()); |
| } |
| |
| /** |
| * Tests anonymous identity will be set to suggestion network. |
| */ |
| @Test |
| public void testSetAnonymousIdentityWhenConnectionIsEstablishedWithPseudonymForSuggestion() |
| throws Exception { |
| mConnectedNetwork = spy(WifiConfigurationTestUtil.createEapNetwork( |
| WifiEnterpriseConfig.Eap.SIM, WifiEnterpriseConfig.Phase2.NONE)); |
| mConnectedNetwork.enterpriseConfig.setAnonymousIdentity(""); |
| mConnectedNetwork.fromWifiNetworkSuggestion = true; |
| String expectedAnonymousIdentity = "anonymous@wlan.mnc456.mcc123.3gppnetwork.org"; |
| String pseudonym = "83bcca9384fca@wlan.mnc456.mcc123.3gppnetwork.org"; |
| |
| when(mWifiCarrierInfoManager.getBestMatchSubscriptionId(any(WifiConfiguration.class))) |
| .thenReturn(DATA_SUBID); |
| when(mWifiCarrierInfoManager.isSimReady(DATA_SUBID)).thenReturn(true); |
| when(mWifiCarrierInfoManager.isImsiEncryptionInfoAvailable(anyInt())).thenReturn(true); |
| when(mWifiCarrierInfoManager.getAnonymousIdentityWith3GppRealm(any())) |
| .thenReturn(expectedAnonymousIdentity); |
| |
| 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(TEST_BSSID_STR)).thenReturn( |
| getGoogleGuestScanDetail(TEST_RSSI, TEST_BSSID_STR, sFreq)); |
| when(mScanDetailCache.getScanResult(TEST_BSSID_STR)).thenReturn( |
| getGoogleGuestScanDetail(TEST_RSSI, TEST_BSSID_STR, sFreq).getScanResult()); |
| when(mWifiNative.getEapAnonymousIdentity(anyString())) |
| .thenReturn(pseudonym); |
| when(mWifiNative.isSupplicantAidlServiceVersionAtLeast(1)).thenReturn(true); |
| |
| WifiSsid wifiSsid = WifiSsid.fromBytes( |
| NativeUtil.byteArrayFromArrayList(NativeUtil.decodeSsid(mConnectedNetwork.SSID))); |
| mCmi.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, |
| new NetworkConnectionEventInfo(0, wifiSsid, TEST_BSSID_STR, false, null)); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiNative).getEapAnonymousIdentity(any()); |
| // No decorated pseudonum, only update the cached data. |
| verify(mWifiNative).setEapAnonymousIdentity(any(), eq(pseudonym), eq(false)); |
| 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()); |
| verify(mWifiNetworkSuggestionsManager).setAnonymousIdentity(any()); |
| } |
| |
| /** |
| * For EAP SIM network, the In-band pseudonym from supplicant should override the OOB |
| * pseudonym if the OOB Pseudonym feature is enabled. |
| */ |
| @Test |
| public void testUpdatePseudonymAsInBandPseudonymWhenConnectionIsEstablished() |
| throws Exception { |
| mConnectedNetwork = spy(WifiConfigurationTestUtil.createEapNetwork( |
| WifiEnterpriseConfig.Eap.SIM, WifiEnterpriseConfig.Phase2.NONE)); |
| String initialOobPseudonym = "1234abcg@wlan.mnc456.mcc123.3gppnetwork.org"; |
| mConnectedNetwork.enterpriseConfig.setAnonymousIdentity(initialOobPseudonym); |
| mConnectedNetwork.fromWifiNetworkSuggestion = true; |
| String pseudonymFromSupplicant = "83bcca9384fca@wlan.mnc456.mcc123.3gppnetwork.org"; |
| |
| when(mWifiCarrierInfoManager.isOobPseudonymFeatureEnabled(anyInt())).thenReturn(true); |
| when(mWifiCarrierInfoManager.getBestMatchSubscriptionId(any(WifiConfiguration.class))) |
| .thenReturn(DATA_SUBID); |
| when(mWifiCarrierInfoManager.isSimReady(DATA_SUBID)).thenReturn(true); |
| |
| triggerConnect(); |
| |
| // CMD_START_CONNECT doesn't change the anonymous identity. |
| assertEquals(initialOobPseudonym, |
| mConnectedNetwork.enterpriseConfig.getAnonymousIdentity()); |
| |
| when(mWifiConfigManager.getScanDetailCacheForNetwork(FRAMEWORK_NETWORK_ID)) |
| .thenReturn(mScanDetailCache); |
| when(mScanDetailCache.getScanDetail(TEST_BSSID_STR)).thenReturn( |
| getGoogleGuestScanDetail(TEST_RSSI, TEST_BSSID_STR, sFreq)); |
| when(mScanDetailCache.getScanResult(TEST_BSSID_STR)).thenReturn( |
| getGoogleGuestScanDetail(TEST_RSSI, TEST_BSSID_STR, sFreq).getScanResult()); |
| when(mWifiNative.getEapAnonymousIdentity(anyString())) |
| .thenReturn(pseudonymFromSupplicant); |
| when(mWifiNative.isSupplicantAidlServiceVersionAtLeast(1)).thenReturn(true); |
| |
| WifiSsid wifiSsid = WifiSsid.fromBytes( |
| NativeUtil.byteArrayFromArrayList(NativeUtil.decodeSsid(mConnectedNetwork.SSID))); |
| mCmi.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, |
| new NetworkConnectionEventInfo(0, wifiSsid, TEST_BSSID_STR, false, null)); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiNative).getEapAnonymousIdentity(any()); |
| // No decorated pseudonum, only update the cached data. |
| verify(mWifiNative).setEapAnonymousIdentity(any(), eq(pseudonymFromSupplicant), |
| eq(false)); |
| verify(mWifiPseudonymManager).setInBandPseudonym(anyInt(), eq(pseudonymFromSupplicant)); |
| assertEquals(pseudonymFromSupplicant, |
| mConnectedNetwork.enterpriseConfig.getAnonymousIdentity()); |
| } |
| |
| /** |
| * Tests anonymous identity will be set to passpoint network. |
| */ |
| @Test |
| public void testSetAnonymousIdentityWhenConnectionIsEstablishedWithPseudonymForPasspoint() |
| throws Exception { |
| mConnectedNetwork = spy(WifiConfigurationTestUtil.createEapNetwork( |
| WifiEnterpriseConfig.Eap.SIM, WifiEnterpriseConfig.Phase2.NONE)); |
| mConnectedNetwork.enterpriseConfig.setAnonymousIdentity(""); |
| mConnectedNetwork.FQDN = WifiConfigurationTestUtil.TEST_FQDN; |
| mConnectedNetwork.providerFriendlyName = |
| WifiConfigurationTestUtil.TEST_PROVIDER_FRIENDLY_NAME; |
| mConnectedNetwork.setPasspointUniqueId(WifiConfigurationTestUtil.TEST_FQDN + "_" |
| + WifiConfigurationTestUtil.TEST_FQDN.hashCode()); |
| String expectedAnonymousIdentity = "anonymous@wlan.mnc456.mcc123.3gppnetwork.org"; |
| String pseudonym = "83bcca9384fca@wlan.mnc456.mcc123.3gppnetwork.org"; |
| |
| when(mWifiCarrierInfoManager.getBestMatchSubscriptionId(any(WifiConfiguration.class))) |
| .thenReturn(DATA_SUBID); |
| when(mWifiCarrierInfoManager.isSimReady(DATA_SUBID)).thenReturn(true); |
| when(mWifiCarrierInfoManager.isImsiEncryptionInfoAvailable(anyInt())).thenReturn(true); |
| when(mWifiCarrierInfoManager.getAnonymousIdentityWith3GppRealm(any())) |
| .thenReturn(expectedAnonymousIdentity); |
| |
| 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(TEST_BSSID_STR)).thenReturn( |
| getGoogleGuestScanDetail(TEST_RSSI, TEST_BSSID_STR, sFreq)); |
| when(mScanDetailCache.getScanResult(TEST_BSSID_STR)).thenReturn( |
| getGoogleGuestScanDetail(TEST_RSSI, TEST_BSSID_STR, sFreq).getScanResult()); |
| when(mWifiNative.getEapAnonymousIdentity(anyString())) |
| .thenReturn(pseudonym); |
| when(mWifiNative.isSupplicantAidlServiceVersionAtLeast(1)).thenReturn(true); |
| |
| WifiSsid wifiSsid = WifiSsid.fromBytes( |
| NativeUtil.byteArrayFromArrayList(NativeUtil.decodeSsid(mConnectedNetwork.SSID))); |
| mCmi.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, |
| new NetworkConnectionEventInfo(0, wifiSsid, TEST_BSSID_STR, false, null)); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiNative).getEapAnonymousIdentity(any()); |
| // No decorated pseudonum, only update the cached data. |
| verify(mWifiNative).setEapAnonymousIdentity(any(), eq(pseudonym), eq(false)); |
| 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()); |
| verify(mPasspointManager).setAnonymousIdentity(any()); |
| } |
| /** |
| * Tests the Passpoint information is set in WifiInfo for Passpoint AP connection. |
| */ |
| @Test |
| public void connectPasspointAp() throws Exception { |
| WifiConfiguration config = spy(WifiConfigurationTestUtil.createPasspointNetwork()); |
| config.SSID = TEST_WIFI_SSID.toString(); |
| config.BSSID = TEST_BSSID_STR; |
| config.networkId = FRAMEWORK_NETWORK_ID; |
| config.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_AUTO; |
| config.roamingConsortiumIds = TEST_RCOI_ARRAY; |
| when(mPasspointManager.getSelectedRcoiForNetwork(eq(config.getPasspointUniqueId()), |
| eq(config.SSID))).thenReturn(TEST_MATCHED_RCOI); |
| setupAndStartConnectSequence(config); |
| validateSuccessfulConnectSequence(config); |
| assertEquals(TEST_MATCHED_RCOI, config.enterpriseConfig.getSelectedRcoi()); |
| |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(FRAMEWORK_NETWORK_ID, TEST_WIFI_SSID, TEST_BSSID_STR, 0, |
| SupplicantState.ASSOCIATING)); |
| mLooper.dispatchAll(); |
| |
| WifiInfo wifiInfo = mWifiInfo; |
| 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 { |
| WifiConfiguration config = spy(WifiConfigurationTestUtil.createPasspointNetwork()); |
| config.SSID = TEST_WIFI_SSID.toString(); |
| config.BSSID = TEST_BSSID_STR; |
| config.networkId = PASSPOINT_NETWORK_ID; |
| config.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_AUTO; |
| setupAndStartConnectSequence(config); |
| validateSuccessfulConnectSequence(config); |
| |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(PASSPOINT_NETWORK_ID, TEST_WIFI_SSID, TEST_BSSID_STR, 0, |
| SupplicantState.ASSOCIATING)); |
| mLooper.dispatchAll(); |
| |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(FRAMEWORK_NETWORK_ID, TEST_WIFI_SSID, TEST_BSSID_STR, 0, |
| SupplicantState.ASSOCIATING)); |
| mLooper.dispatchAll(); |
| |
| WifiInfo wifiInfo = mWifiInfo; |
| 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 { |
| WifiConfiguration osuConfig = spy(WifiConfigurationTestUtil.createEphemeralNetwork()); |
| osuConfig.SSID = TEST_WIFI_SSID.toString(); |
| osuConfig.BSSID = TEST_BSSID_STR; |
| osuConfig.osu = true; |
| osuConfig.networkId = FRAMEWORK_NETWORK_ID; |
| osuConfig.providerFriendlyName = WifiConfigurationTestUtil.TEST_PROVIDER_FRIENDLY_NAME; |
| osuConfig.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_AUTO; |
| setupAndStartConnectSequence(osuConfig); |
| validateSuccessfulConnectSequence(osuConfig); |
| |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(FRAMEWORK_NETWORK_ID, TEST_WIFI_SSID, TEST_BSSID_STR, 0, |
| SupplicantState.ASSOCIATING)); |
| mLooper.dispatchAll(); |
| |
| WifiInfo wifiInfo = mWifiInfo; |
| 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 { |
| WifiConfiguration osuConfig = spy(WifiConfigurationTestUtil.createEphemeralNetwork()); |
| osuConfig.SSID = TEST_WIFI_SSID.toString(); |
| osuConfig.BSSID = TEST_BSSID_STR; |
| osuConfig.osu = true; |
| osuConfig.networkId = PASSPOINT_NETWORK_ID; |
| osuConfig.providerFriendlyName = WifiConfigurationTestUtil.TEST_PROVIDER_FRIENDLY_NAME; |
| osuConfig.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_AUTO; |
| setupAndStartConnectSequence(osuConfig); |
| validateSuccessfulConnectSequence(osuConfig); |
| |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(PASSPOINT_NETWORK_ID, TEST_WIFI_SSID, TEST_BSSID_STR, 0, |
| SupplicantState.ASSOCIATING)); |
| mLooper.dispatchAll(); |
| |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(FRAMEWORK_NETWORK_ID, TEST_WIFI_SSID, TEST_BSSID_STR, 0, |
| SupplicantState.ASSOCIATING)); |
| mLooper.dispatchAll(); |
| |
| WifiInfo wifiInfo = mWifiInfo; |
| assertNotNull(wifiInfo); |
| assertFalse(wifiInfo.isOsuAp()); |
| } |
| |
| /** |
| * Tests that {@link WifiInfo#getHiddenSsid()} returns {@code true} if we've connected to a |
| * hidden SSID network. |
| * @throws Exception |
| */ |
| @Test |
| public void testConnectHiddenSsid() throws Exception { |
| connect(); |
| |
| // Set the scan detail cache for hidden SSID. |
| when(mWifiConfigManager.getScanDetailCacheForNetwork(FRAMEWORK_NETWORK_ID)) |
| .thenReturn(mScanDetailCache); |
| ScanDetail hiddenScanDetail = getHiddenScanDetail(TEST_RSSI, TEST_BSSID_STR1, sFreq1); |
| when(mScanDetailCache.getScanDetail(TEST_BSSID_STR1)).thenReturn(hiddenScanDetail); |
| when(mScanDetailCache.getScanResult(TEST_BSSID_STR1)).thenReturn( |
| hiddenScanDetail.getScanResult()); |
| |
| mCmi.sendMessage(WifiMonitor.ASSOCIATED_BSSID_EVENT, 0, 0, TEST_BSSID_STR1); |
| mLooper.dispatchAll(); |
| |
| // Hidden SSID scan result should set WifiInfo.getHiddenSsid() to true. |
| assertTrue(mWifiInfo.getHiddenSSID()); |
| |
| // Set the scan detail cache for non-hidden SSID. |
| ScanDetail googleGuestScanDetail = |
| getGoogleGuestScanDetail(TEST_RSSI, TEST_BSSID_STR, sFreq1); |
| when(mScanDetailCache.getScanDetail(TEST_BSSID_STR)).thenReturn(googleGuestScanDetail); |
| when(mScanDetailCache.getScanResult(TEST_BSSID_STR)).thenReturn( |
| googleGuestScanDetail.getScanResult()); |
| |
| mCmi.sendMessage(WifiMonitor.ASSOCIATED_BSSID_EVENT, 0, 0, TEST_BSSID_STR); |
| mLooper.dispatchAll(); |
| |
| // Non-hidden SSID scan result should set WifiInfo.getHiddenSsid() to false. |
| assertFalse(mWifiInfo.getHiddenSSID()); |
| } |
| |
| /** |
| * Verify that WifiStateTracker is called if wifi is disabled while connected. |
| */ |
| @Test |
| public void verifyWifiStateTrackerUpdatedWhenDisabled() throws Exception { |
| connect(); |
| |
| mCmi.stop(); |
| mLooper.dispatchAll(); |
| verify(mWifiStateTracker).updateState(WIFI_IFACE_NAME, WifiStateTracker.DISCONNECTED); |
| } |
| |
| @Test |
| public void testIdleModeChanged_firmwareRoaming() throws Exception { |
| // verify no-op when either the feature flag is disabled or firmware roaming is not |
| // supported |
| when(mWifiGlobals.isDisableFirmwareRoamingInIdleMode()).thenReturn(false); |
| when(mWifiConnectivityHelper.isFirmwareRoamingSupported()).thenReturn(true); |
| mCmi.onIdleModeChanged(true); |
| verify(mWifiNative, never()).enableFirmwareRoaming(anyString(), anyInt()); |
| when(mWifiGlobals.isDisableFirmwareRoamingInIdleMode()).thenReturn(true); |
| when(mWifiConnectivityHelper.isFirmwareRoamingSupported()).thenReturn(false); |
| mCmi.onIdleModeChanged(true); |
| verify(mWifiNative, never()).enableFirmwareRoaming(anyString(), anyInt()); |
| |
| // Enable both, then verify firmware roaming is disabled when idle mode is entered |
| when(mWifiGlobals.isDisableFirmwareRoamingInIdleMode()).thenReturn(true); |
| when(mWifiConnectivityHelper.isFirmwareRoamingSupported()).thenReturn(true); |
| mCmi.onIdleModeChanged(true); |
| verify(mWifiNative).enableFirmwareRoaming(anyString(), |
| eq(WifiNative.DISABLE_FIRMWARE_ROAMING)); |
| |
| // Verify firmware roaming is enabled when idle mode exited |
| mCmi.onIdleModeChanged(false); |
| verify(mWifiNative).enableFirmwareRoaming(anyString(), |
| eq(WifiNative.ENABLE_FIRMWARE_ROAMING)); |
| } |
| |
| @Test |
| public void testIdleModeChanged_firmwareRoamingLocalOnlyCase() throws Exception { |
| // mock connected network to be local only |
| connect(); |
| mConnectedNetwork.fromWifiNetworkSpecifier = true; |
| |
| // Enable feature, then verify firmware roaming is disabled when idle mode is entered |
| when(mWifiGlobals.isDisableFirmwareRoamingInIdleMode()).thenReturn(true); |
| when(mWifiConnectivityHelper.isFirmwareRoamingSupported()).thenReturn(true); |
| mCmi.onIdleModeChanged(true); |
| verify(mWifiNative).enableFirmwareRoaming(anyString(), |
| eq(WifiNative.DISABLE_FIRMWARE_ROAMING)); |
| |
| // Verify firmware roaming is not enabled when idle mode exited |
| mCmi.onIdleModeChanged(false); |
| verify(mWifiNative, never()).enableFirmwareRoaming(anyString(), |
| eq(WifiNative.ENABLE_FIRMWARE_ROAMING)); |
| } |
| |
| /** |
| * 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); |
| when(mOemWifiNetworkFactory.hasConnectionRequests()).thenReturn(false); |
| when(mMultiInternetManager.hasPendingConnectionRequests()).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(mOemWifiNetworkFactory.hasConnectionRequests()).thenReturn(false); |
| when(mMultiInternetManager.hasPendingConnectionRequests()).thenReturn(false); |
| |
| when(mWifiPermissionsUtil.checkNetworkSettingsPermission(anyInt())).thenReturn(false); |
| |
| WifiConfiguration config = WifiConfigurationTestUtil.createOpenNetwork(); |
| config.networkId = FRAMEWORK_NETWORK_ID + 1; |
| setupAndStartConnectSequence(config); |
| verify(mWifiConnectivityManager).prepareForForcedConnection(eq(config.networkId)); |
| verify(mWifiConfigManager, never()) |
| .getConfiguredNetworkWithoutMasking(eq(config.networkId)); |
| verify(mWifiNative, never()).connectToNetwork(eq(WIFI_IFACE_NAME), eq(config)); |
| // These are called from connectToUserSelectNetwork() and L2ConnectedState::enter() |
| verify(mWifiPermissionsUtil, times(4)).checkNetworkSettingsPermission(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.connectNetwork( |
| new NetworkUpdateResult(FRAMEWORK_NETWORK_ID), |
| new ActionListenerWrapper(connectActionListener), |
| Binder.getCallingUid(), OP_PACKAGE_NAME); |
| 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 |
| IActionListener connectActionListener = mock(IActionListener.class); |
| int callingUid = Binder.getCallingUid(); |
| mCmi.connectNetwork( |
| new NetworkUpdateResult(FRAMEWORK_NETWORK_ID), |
| new ActionListenerWrapper(connectActionListener), |
| callingUid, OP_PACKAGE_NAME); |
| 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.connectNetwork( |
| new NetworkUpdateResult(FRAMEWORK_NETWORK_ID), |
| new ActionListenerWrapper(connectActionListener), |
| Binder.getCallingUid(), OP_PACKAGE_NAME); |
| 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). |
| NetworkUpdateResult networkUpdateResult = new NetworkUpdateResult( |
| FRAMEWORK_NETWORK_ID, |
| STATUS_SUCCESS, |
| false /* ip */, |
| false /* proxy */, |
| true /* credential */, |
| false /* isNewNetwork */); |
| IActionListener connectActionListener = mock(IActionListener.class); |
| mCmi.connectNetwork( |
| networkUpdateResult, |
| new ActionListenerWrapper(connectActionListener), |
| Binder.getCallingUid(), OP_PACKAGE_NAME); |
| 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, |
| new AssocRejectEventInfo(mConnectedNetwork.SSID, TEST_BSSID_STR, |
| ISupplicantStaIfaceCallback.StatusCode.AP_UNABLE_TO_HANDLE_NEW_STA, false)); |
| mLooper.dispatchAll(); |
| |
| IActionListener connectActionListener = mock(IActionListener.class); |
| mCmi.connectNetwork( |
| new NetworkUpdateResult(FRAMEWORK_NETWORK_ID), |
| new ActionListenerWrapper(connectActionListener), |
| Binder.getCallingUid(), OP_PACKAGE_NAME); |
| 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. |
| DisconnectEventInfo disconnectEventInfo = |
| new DisconnectEventInfo(mConnectedNetwork.SSID, TEST_BSSID_STR, 0, false); |
| mCmi.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, disconnectEventInfo); |
| mLooper.dispatchAll(); |
| |
| IActionListener connectActionListener = mock(IActionListener.class); |
| mCmi.connectNetwork( |
| new NetworkUpdateResult(FRAMEWORK_NETWORK_ID), |
| new ActionListenerWrapper(connectActionListener), |
| Binder.getCallingUid(), OP_PACKAGE_NAME); |
| mLooper.dispatchAll(); |
| verify(connectActionListener).onSuccess(); |
| |
| // Verify that we triggered a second connection. |
| verify(mWifiNative, times(2)).connectToNetwork(eq(WIFI_IFACE_NAME), any()); |
| } |
| |
| /** |
| * If the interface has been switched to scan, the network disconnection event should clear the |
| * current network. |
| * @throws Exception |
| */ |
| @Test |
| public void testNetworkDisconnectAfterInterfaceSwitchedToScan() throws Exception { |
| triggerConnect(); |
| IActionListener connectActionListener = mock(IActionListener.class); |
| mCmi.connectNetwork( |
| new NetworkUpdateResult(FRAMEWORK_NETWORK_ID), |
| new ActionListenerWrapper(connectActionListener), |
| Binder.getCallingUid(), OP_PACKAGE_NAME); |
| mLooper.dispatchAll(); |
| verify(connectActionListener).onSuccess(); |
| |
| when(mClientModeManager.getRole()).thenReturn(ROLE_CLIENT_SCAN_ONLY); |
| // Disconnection from previous network. |
| DisconnectEventInfo disconnectEventInfo = |
| new DisconnectEventInfo(TEST_SSID, TEST_BSSID_STR, 0, false); |
| mCmi.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, disconnectEventInfo); |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(0, TEST_WIFI_SSID, TEST_BSSID_STR, sFreq, |
| SupplicantState.DISCONNECTED)); |
| mLooper.dispatchAll(); |
| verify(mActiveModeWarden).setCurrentNetwork(null); |
| } |
| |
| /** |
| * 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); |
| |
| startConnectSuccess(); |
| |
| mCmi.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, |
| new NetworkConnectionEventInfo(0, TEST_WIFI_SSID, TEST_BSSID_STR, false, null)); |
| mLooper.dispatchAll(); |
| |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(0, TEST_WIFI_SSID, TEST_BSSID_STR, sFreq, |
| SupplicantState.COMPLETED)); |
| mLooper.dispatchAll(); |
| |
| assertEquals("L3ProvisioningState", getCurrentState().getName()); |
| |
| // Connect to a different network |
| WifiConfiguration config = WifiConfigurationTestUtil.createOpenNetwork(); |
| config.networkId = FRAMEWORK_NETWORK_ID + 1; |
| setupAndStartConnectSequence(config); |
| validateSuccessfulConnectSequence(config); |
| |
| // Disconnection from previous network. |
| DisconnectEventInfo disconnectEventInfo = |
| new DisconnectEventInfo(TEST_SSID, TEST_BSSID_STR, 0, false); |
| mCmi.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, disconnectEventInfo); |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(0, TEST_WIFI_SSID, TEST_BSSID_STR, sFreq, |
| SupplicantState.DISCONNECTED)); |
| mLooper.dispatchAll(); |
| |
| // Ensure we don't end the new connection event. |
| verify(mWifiMetrics, never()).endConnectionEvent( |
| any(), eq(WifiMetrics.ConnectionEvent.FAILURE_NETWORK_DISCONNECTION), |
| anyInt(), anyInt(), anyInt(), anyInt()); |
| verify(mWifiConnectivityManager).prepareForForcedConnection(FRAMEWORK_NETWORK_ID + 1); |
| } |
| |
| /** |
| * If there is a network removal while still connecting to it, the connection |
| * should be aborted. |
| */ |
| @Test |
| public void networkRemovalWhileObtainingIp() throws Exception { |
| initializeAndAddNetworkAndVerifySuccess(); |
| |
| verify(mWifiNative).removeAllNetworks(WIFI_IFACE_NAME); |
| |
| startConnectSuccess(); |
| |
| mCmi.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, |
| new NetworkConnectionEventInfo(0, TEST_WIFI_SSID, TEST_BSSID_STR, false, null)); |
| mLooper.dispatchAll(); |
| |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(0, TEST_WIFI_SSID, TEST_BSSID_STR, sFreq, |
| SupplicantState.COMPLETED)); |
| mLooper.dispatchAll(); |
| |
| assertEquals("L3ProvisioningState", getCurrentState().getName()); |
| reset(mWifiNative); |
| |
| // Simulate the target network removal & the disconnect trigger. |
| WifiConfiguration removedNetwork = new WifiConfiguration(); |
| removedNetwork.networkId = FRAMEWORK_NETWORK_ID; |
| for (WifiConfigManager.OnNetworkUpdateListener listener : mConfigUpdateListenerCaptor |
| .getAllValues()) { |
| listener.onNetworkRemoved(removedNetwork); |
| } |
| mLooper.dispatchAll(); |
| |
| verify(mWifiNative).removeNetworkCachedData(FRAMEWORK_NETWORK_ID); |
| verify(mWifiNative).disconnect(WIFI_IFACE_NAME); |
| verify(mWifiMetrics).logStaEvent(anyString(), eq(StaEvent.TYPE_FRAMEWORK_DISCONNECT), |
| eq(StaEvent.DISCONNECT_NETWORK_REMOVED)); |
| |
| when(mWifiConfigManager.getConfiguredNetwork(FRAMEWORK_NETWORK_ID)).thenReturn(null); |
| DisconnectEventInfo disconnectEventInfo = |
| new DisconnectEventInfo(TEST_SSID, TEST_BSSID_STR, 0, false); |
| mCmi.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, disconnectEventInfo); |
| mLooper.dispatchAll(); |
| |
| assertEquals("DisconnectedState", getCurrentState().getName()); |
| } |
| |
| /** |
| * 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.connectNetwork( |
| new NetworkUpdateResult(TEST_NETWORK_ID), |
| new ActionListenerWrapper(connectActionListener), |
| Process.SYSTEM_UID, OP_PACKAGE_NAME); |
| mLooper.dispatchAll(); |
| verify(connectActionListener).onSuccess(); |
| |
| verify(mWifiMetrics).setNominatorForNetwork(TEST_NETWORK_ID, |
| WifiMetricsProto.ConnectionEvent.NOMINATOR_MANUAL); |
| } |
| |
| private void startConnectSuccess() throws Exception { |
| startConnectSuccess(FRAMEWORK_NETWORK_ID); |
| } |
| |
| private void startConnectSuccess(int networkId) throws Exception { |
| IActionListener connectActionListener = mock(IActionListener.class); |
| mCmi.connectNetwork( |
| new NetworkUpdateResult(networkId), |
| new ActionListenerWrapper(connectActionListener), |
| Binder.getCallingUid(), OP_PACKAGE_NAME); |
| mLooper.dispatchAll(); |
| verify(connectActionListener).onSuccess(); |
| } |
| |
| @Test |
| public void testDhcpFailure() throws Exception { |
| initializeAndAddNetworkAndVerifySuccess(); |
| |
| startConnectSuccess(); |
| |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(0, TEST_WIFI_SSID, TEST_BSSID_STR, sFreq, |
| SupplicantState.ASSOCIATED)); |
| mLooper.dispatchAll(); |
| |
| mCmi.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, |
| new NetworkConnectionEventInfo(0, TEST_WIFI_SSID, TEST_BSSID_STR, false, null)); |
| mLooper.dispatchAll(); |
| verify(mWifiBlocklistMonitor).handleBssidConnectionSuccess(TEST_BSSID_STR, TEST_SSID); |
| |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(0, TEST_WIFI_SSID, TEST_BSSID_STR, sFreq, |
| SupplicantState.COMPLETED)); |
| mLooper.dispatchAll(); |
| |
| assertEquals("L3ProvisioningState", getCurrentState().getName()); |
| injectDhcpFailure(); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiNative).disconnect(WIFI_IFACE_NAME); |
| // 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( |
| eq(TEST_SSID), eq(TEST_BSSID_STR), |
| eq(WifiLastResortWatchdog.FAILURE_CODE_DHCP), anyBoolean()); |
| verify(mWifiBlocklistMonitor).handleBssidConnectionFailure(eq(TEST_BSSID_STR), |
| eq(mTestConfig), eq(WifiBlocklistMonitor.REASON_DHCP_FAILURE), anyInt()); |
| verify(mWifiBlocklistMonitor, never()).handleDhcpProvisioningSuccess( |
| TEST_BSSID_STR, TEST_SSID); |
| verify(mWifiBlocklistMonitor, never()).handleNetworkValidationSuccess( |
| TEST_BSSID_STR, TEST_SSID); |
| DisconnectEventInfo disconnectEventInfo = |
| new DisconnectEventInfo(mConnectedNetwork.SSID, TEST_BSSID_STR, 3, true); |
| mCmi.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, disconnectEventInfo); |
| mLooper.dispatchAll(); |
| } |
| |
| /** |
| * Verify that a IP renewal failure is logged when IP provisioning fail in the |
| * L3ConnectedState. |
| */ |
| @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(); |
| |
| startConnectSuccess(); |
| |
| WifiConfiguration config = createTestNetwork(false); |
| config.getNetworkSelectionStatus().setHasEverConnected(true); |
| when(mWifiConfigManager.getConfiguredNetwork(anyInt())).thenReturn(config); |
| |
| mCmi.sendMessage(WifiMonitor.AUTHENTICATION_FAILURE_EVENT, |
| new AuthenticationFailureEventInfo(TEST_SSID, MacAddress.fromString(TEST_BSSID_STR), |
| WifiManager.ERROR_AUTH_FAILURE_WRONG_PSWD, -1)); |
| DisconnectEventInfo disconnectEventInfo = |
| new DisconnectEventInfo(TEST_SSID, TEST_BSSID_STR, 0, false); |
| mCmi.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, disconnectEventInfo); |
| mLooper.dispatchAll(); |
| |
| verify(mWrongPasswordNotifier, never()).onWrongPasswordError(any()); |
| verify(mWifiConfigManager).updateNetworkSelectionStatus(anyInt(), |
| eq(WifiConfiguration.NetworkSelectionStatus.DISABLED_AUTHENTICATION_FAILURE)); |
| |
| assertEquals("DisconnectedState", getCurrentState().getName()); |
| } |
| |
| /** |
| * Verify that when wrong password authentication failure notification is not sent when |
| * the network was local only. |
| */ |
| @Test |
| public void testWrongPasswordWithLocalOnlyConnection() throws Exception { |
| initializeAndAddNetworkAndVerifySuccess(); |
| |
| startConnectSuccess(); |
| |
| WifiConfiguration config = createTestNetwork(false); |
| when(mWifiConfigManager.getConfiguredNetwork(anyInt())).thenReturn(config); |
| |
| when(mWifiNetworkFactory.getSpecificNetworkRequestUids(any(), any())) |
| .thenReturn(Set.of(TEST_UID)); |
| when(mWifiNetworkFactory.getSpecificNetworkRequestUidAndPackageName(any(), any())) |
| .thenReturn(Pair.create(TEST_UID, OP_PACKAGE_NAME)); |
| mCmi.sendMessage(WifiMonitor.AUTHENTICATION_FAILURE_EVENT, |
| new AuthenticationFailureEventInfo(TEST_SSID, MacAddress.fromString(TEST_BSSID_STR), |
| WifiManager.ERROR_AUTH_FAILURE_WRONG_PSWD, -1)); |
| DisconnectEventInfo disconnectEventInfo = |
| new DisconnectEventInfo(TEST_SSID, TEST_BSSID_STR, 0, false); |
| mCmi.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, disconnectEventInfo); |
| mLooper.dispatchAll(); |
| |
| verify(mWrongPasswordNotifier, never()).onWrongPasswordError(any()); |
| assertEquals("DisconnectedState", getCurrentState().getName()); |
| } |
| |
| /** |
| * Verify that when wrong password authentication failure notification is sent when |
| * the network was NOT local only. |
| */ |
| @Test |
| public void testWrongPasswordWithNonLocalOnlyConnection() throws Exception { |
| initializeAndAddNetworkAndVerifySuccess(); |
| |
| startConnectSuccess(); |
| |
| WifiConfiguration config = createTestNetwork(false); |
| when(mWifiConfigManager.getConfiguredNetwork(anyInt())).thenReturn(config); |
| |
| when(mWifiNetworkFactory.getSpecificNetworkRequestUids(any(), any())) |
| .thenReturn(Collections.emptySet()); |
| when(mWifiNetworkFactory.getSpecificNetworkRequestUidAndPackageName(any(), any())) |
| .thenReturn(Pair.create(Process.INVALID_UID, "")); |
| mCmi.sendMessage(WifiMonitor.AUTHENTICATION_FAILURE_EVENT, |
| new AuthenticationFailureEventInfo(TEST_SSID, MacAddress.fromString(TEST_BSSID_STR), |
| WifiManager.ERROR_AUTH_FAILURE_WRONG_PSWD, -1)); |
| DisconnectEventInfo disconnectEventInfo = |
| new DisconnectEventInfo(TEST_SSID, TEST_BSSID_STR, 0, false); |
| mCmi.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, disconnectEventInfo); |
| mLooper.dispatchAll(); |
| |
| verify(mWrongPasswordNotifier).onWrongPasswordError(any()); |
| assertEquals("DisconnectedState", getCurrentState().getName()); |
| } |
| |
| /** |
| * It is observed sometimes the WifiMonitor.NETWORK_DISCONNECTION_EVENT is observed before the |
| * actual connection failure messages while making a connection. |
| * The test make sure that make sure that the connection event is ended properly in the above |
| * case. |
| */ |
| @Test |
| public void testDisconnectionEventInL2ConnectingStateEndsConnectionEvent() throws Exception { |
| initializeAndAddNetworkAndVerifySuccess(); |
| |
| startConnectSuccess(); |
| |
| WifiConfiguration config = createTestNetwork(false); |
| config.getNetworkSelectionStatus().setHasEverConnected(true); |
| when(mWifiConfigManager.getConfiguredNetwork(anyInt())).thenReturn(config); |
| |
| DisconnectEventInfo disconnectEventInfo = |
| new DisconnectEventInfo(TEST_SSID, TEST_BSSID_STR, 0, false); |
| mCmi.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, disconnectEventInfo); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiMetrics).endConnectionEvent( |
| any(), eq(WifiMetrics.ConnectionEvent.FAILURE_NETWORK_DISCONNECTION), |
| anyInt(), anyInt(), anyInt(), anyInt()); |
| verify(mWifiConnectivityManager).handleConnectionAttemptEnded( |
| any(), anyInt(), anyInt(), any(), any()); |
| assertEquals(WifiInfo.SECURITY_TYPE_UNKNOWN, mWifiInfo.getCurrentSecurityType()); |
| 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(); |
| |
| startConnectSuccess(); |
| |
| WifiConfiguration config = new WifiConfiguration(); |
| config.SSID = TEST_SSID; |
| config.getNetworkSelectionStatus().setHasEverConnected(false); |
| config.carrierId = CARRIER_ID_1; |
| when(mWifiConfigManager.getConfiguredNetwork(anyInt())).thenReturn(config); |
| |
| // Need to add supplicant state changed event to simulate broadcasts correctly |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(0, TEST_WIFI_SSID, TEST_BSSID_STR, sFreq, |
| SupplicantState.ASSOCIATED)); |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(0, TEST_WIFI_SSID, TEST_BSSID_STR, sFreq, |
| SupplicantState.FOUR_WAY_HANDSHAKE)); |
| |
| mCmi.sendMessage(WifiMonitor.AUTHENTICATION_FAILURE_EVENT, |
| new AuthenticationFailureEventInfo(TEST_SSID, MacAddress.fromString(TEST_BSSID_STR), |
| WifiManager.ERROR_AUTH_FAILURE_WRONG_PSWD, -1)); |
| DisconnectEventInfo disconnectEventInfo = |
| new DisconnectEventInfo(TEST_SSID, TEST_BSSID_STR, 0, false); |
| mCmi.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, disconnectEventInfo); |
| mLooper.dispatchAll(); |
| |
| verify(mWrongPasswordNotifier).onWrongPasswordError(config); |
| verify(mWifiConfigManager).updateNetworkSelectionStatus(anyInt(), |
| eq(WifiConfiguration.NetworkSelectionStatus.DISABLED_BY_WRONG_PASSWORD)); |
| verify(mWifiMetrics).incrementNumOfCarrierWifiConnectionAuthFailure(); |
| // Verify broadcasts corresponding to supplicant ASSOCIATED, FOUR_WAY_HANDSHAKE, then |
| // finally wrong password causing disconnect. |
| inOrderVerifyNetworkStateChangedBroadcasts( |
| new NetworkStateChangedIntentMatcher(CONNECTING), |
| new NetworkStateChangedIntentMatcher(AUTHENTICATING), |
| new NetworkStateChangedIntentMatcher(DISCONNECTED)); |
| 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(), any(), anyString(), anyInt(), anyBoolean(), |
| anyInt())).thenReturn(80000); |
| initializeAndAddNetworkAndVerifySuccess(); |
| |
| startConnectSuccess(); |
| |
| WifiConfiguration config = new WifiConfiguration(); |
| config.SSID = TEST_SSID; |
| config.getNetworkSelectionStatus().setHasEverConnected(true); |
| config.allowedKeyManagement.set(KeyMgmt.IEEE8021X); |
| config.enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.SIM); |
| when(mWifiConfigManager.getConfiguredNetwork(anyInt())).thenReturn(config); |
| when(mWifiScoreCard.detectAbnormalConnectionFailure(anyString())) |
| .thenReturn(WifiHealthMonitor.REASON_AUTH_FAILURE); |
| |
| mCmi.sendMessage(WifiMonitor.AUTHENTICATION_FAILURE_EVENT, |
| new AuthenticationFailureEventInfo(TEST_SSID, MacAddress.fromString(TEST_BSSID_STR), |
| 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, true); |
| verify(mWifiCarrierInfoManager).resetCarrierKeysForImsiEncryption(any()); |
| verify(mDeviceConfigFacade).isAbnormalConnectionFailureBugreportEnabled(); |
| verify(mWifiScoreCard).detectAbnormalConnectionFailure(anyString()); |
| verify(mWifiDiagnostics, times(2)).takeBugReport(anyString(), anyString()); |
| } |
| |
| @Test |
| public void testEapAkaRetrieveOobPseudonymTriggeredByAuthenticationFailure() throws Exception { |
| when(mWifiMetrics.startConnectionEvent(any(), any(), anyString(), anyInt(), anyBoolean(), |
| anyInt())).thenReturn(80000); |
| when(mWifiCarrierInfoManager.isOobPseudonymFeatureEnabled(anyInt())).thenReturn(true); |
| initializeAndAddNetworkAndVerifySuccess(); |
| |
| startConnectSuccess(); |
| |
| WifiConfiguration config = new WifiConfiguration(); |
| config.SSID = TEST_SSID; |
| config.getNetworkSelectionStatus().setHasEverConnected(true); |
| config.allowedKeyManagement.set(KeyMgmt.IEEE8021X); |
| config.enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.AKA); |
| when(mWifiConfigManager.getConfiguredNetwork(anyInt())).thenReturn(config); |
| when(mWifiScoreCard.detectAbnormalConnectionFailure(anyString())) |
| .thenReturn(WifiHealthMonitor.REASON_AUTH_FAILURE); |
| |
| mCmi.sendMessage(WifiMonitor.AUTHENTICATION_FAILURE_EVENT, |
| new AuthenticationFailureEventInfo(TEST_SSID, MacAddress.fromString(TEST_BSSID_STR), |
| WifiManager.ERROR_AUTH_FAILURE_EAP_FAILURE, |
| -1)); |
| mLooper.dispatchAll(); |
| |
| verify(mEapFailureNotifier).onEapFailure( |
| -1, config, true); |
| verify(mWifiPseudonymManager).retrieveOobPseudonymWithRateLimit(anyInt()); |
| 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(); |
| |
| startConnectSuccess(); |
| |
| 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, |
| new AuthenticationFailureEventInfo(TEST_SSID, MacAddress.fromString(TEST_BSSID_STR), |
| WifiManager.ERROR_AUTH_FAILURE_EAP_FAILURE, |
| WifiNative.EAP_SIM_VENDOR_SPECIFIC_CERT_EXPIRED)); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiCarrierInfoManager, never()).resetCarrierKeysForImsiEncryption(any()); |
| } |
| |
| @Test |
| public void testAuthFailureOnDifferentSsidIsIgnored() throws Exception { |
| initializeAndAddNetworkAndVerifySuccess(); |
| |
| startConnectSuccess(); |
| mCmi.sendMessage(WifiMonitor.AUTHENTICATION_FAILURE_EVENT, |
| new AuthenticationFailureEventInfo("WRONG_SSID", |
| MacAddress.fromString(TEST_BSSID_STR), |
| WifiManager.ERROR_AUTH_FAILURE_EAP_FAILURE, |
| WifiNative.EAP_SIM_NOT_SUBSCRIBED)); |
| mLooper.dispatchAll(); |
| verify(mWifiConfigManager, never()).updateNetworkSelectionStatus(anyInt(), |
| eq(WifiConfiguration.NetworkSelectionStatus |
| .DISABLED_AUTHENTICATION_NO_SUBSCRIPTION)); |
| } |
| |
| /** |
| * 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(); |
| |
| startConnectSuccess(); |
| |
| mCmi.sendMessage(WifiMonitor.AUTHENTICATION_FAILURE_EVENT, |
| new AuthenticationFailureEventInfo(TEST_SSID, MacAddress.fromString(TEST_BSSID_STR), |
| 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(); |
| |
| startConnectSuccess(); |
| |
| DisconnectEventInfo disconnectEventInfo = |
| new DisconnectEventInfo(TEST_SSID, TEST_BSSID_STR, 0, false); |
| mCmi.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, disconnectEventInfo); |
| mLooper.dispatchAll(); |
| |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(0, TEST_WIFI_SSID, TEST_BSSID_STR, sFreq, |
| SupplicantState.COMPLETED)); |
| mLooper.dispatchAll(); |
| |
| assertEquals("DisconnectedState", getCurrentState().getName()); |
| verify(mWifiDiagnostics, never()).takeBugReport(anyString(), anyString()); |
| } |
| |
| |
| @Test |
| public void getWhatToString() throws Exception { |
| assertEquals("CMD_PRE_DHCP_ACTION", mCmi.getWhatToString(CMD_PRE_DHCP_ACTION)); |
| assertEquals("CMD_IP_REACHABILITY_LOST", mCmi.getWhatToString( |
| ClientModeImpl.CMD_IP_REACHABILITY_LOST)); |
| assertEquals("CMD_IP_REACHABILITY_FAILURE", mCmi.getWhatToString( |
| ClientModeImpl.CMD_IP_REACHABILITY_FAILURE)); |
| } |
| |
| @Test |
| public void disconnect() throws Exception { |
| when(mWifiScoreCard.detectAbnormalDisconnection(any())) |
| .thenReturn(WifiHealthMonitor.REASON_SHORT_CONNECTION_NONLOCAL); |
| InOrder inOrderWifiLockManager = inOrder(mWifiLockManager); |
| connect(); |
| inOrderWifiLockManager.verify(mWifiLockManager) |
| .updateWifiClientConnected(mClientModeManager, true); |
| |
| DisconnectEventInfo disconnectEventInfo = |
| new DisconnectEventInfo(mConnectedNetwork.SSID, TEST_BSSID_STR, 0, false); |
| mCmi.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, disconnectEventInfo); |
| mLooper.dispatchAll(); |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(0, WifiSsid.fromUtf8Text(mConnectedNetwork.SSID), |
| TEST_BSSID_STR, sFreq, SupplicantState.DISCONNECTED)); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiStateTracker).updateState(WIFI_IFACE_NAME, WifiStateTracker.DISCONNECTED); |
| assertEquals("DisconnectedState", getCurrentState().getName()); |
| verify(mCmiMonitor).onConnectionEnd(mClientModeManager); |
| inOrderWifiLockManager.verify(mWifiLockManager) |
| .updateWifiClientConnected(mClientModeManager, false); |
| verify(mWifiScoreCard).detectAbnormalDisconnection(WIFI_IFACE_NAME); |
| verify(mWifiDiagnostics).takeBugReport(anyString(), anyString()); |
| verify(mWifiNative).disableNetwork(WIFI_IFACE_NAME); |
| // Set MAC address thrice - once at bootup, once for new connection, once for disconnect. |
| verify(mWifiNative, times(3)).setStaMacAddress(eq(WIFI_IFACE_NAME), any()); |
| // ClientModeManager should only be stopped when in lingering mode |
| verify(mClientModeManager, never()).stop(); |
| } |
| |
| @Test |
| public void secondaryRoleCmmDisconnected_stopsClientModeManager() throws Exception { |
| // Owning ClientModeManager has role SECONDARY_TRANSIENT |
| when(mClientModeManager.getRole()).thenReturn(ROLE_CLIENT_SECONDARY_TRANSIENT); |
| |
| connect(); |
| |
| // ClientModeManager never stopped |
| verify(mClientModeManager, never()).stop(); |
| |
| // Disconnected from network |
| DisconnectEventInfo disconnectEventInfo = |
| new DisconnectEventInfo(mConnectedNetwork.SSID, TEST_BSSID_STR, 0, false); |
| mCmi.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, disconnectEventInfo); |
| mLooper.dispatchAll(); |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(0, WifiSsid.fromUtf8Text(mConnectedNetwork.SSID), |
| TEST_BSSID_STR, sFreq, SupplicantState.DISCONNECTED)); |
| mLooper.dispatchAll(); |
| |
| assertEquals("DisconnectedState", getCurrentState().getName()); |
| |
| // Since in lingering mode, disconnect => stop ClientModeManager |
| verify(mClientModeManager).stop(); |
| } |
| |
| @Test |
| public void primaryCmmDisconnected_doesntStopsClientModeManager() throws Exception { |
| // Owning ClientModeManager is primary |
| when(mClientModeManager.getRole()).thenReturn(ROLE_CLIENT_PRIMARY); |
| |
| connect(); |
| |
| // ClientModeManager never stopped |
| verify(mClientModeManager, never()).stop(); |
| |
| // Disconnected from network |
| DisconnectEventInfo disconnectEventInfo = |
| new DisconnectEventInfo(mConnectedNetwork.SSID, TEST_BSSID_STR, 0, false); |
| mCmi.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, disconnectEventInfo); |
| mLooper.dispatchAll(); |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(0, WifiSsid.fromUtf8Text(mConnectedNetwork.SSID), |
| TEST_BSSID_STR, sFreq, SupplicantState.DISCONNECTED)); |
| mLooper.dispatchAll(); |
| |
| assertEquals("DisconnectedState", getCurrentState().getName()); |
| |
| // Since primary => don't stop ClientModeManager |
| verify(mClientModeManager, never()).stop(); |
| } |
| |
| /** |
| * 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(eq(0), eq(true), |
| eq(false), |
| anyInt()); |
| } |
| |
| /** |
| * 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(eq(0), eq(true), eq(false), anyInt()); |
| } |
| |
| @Test |
| public void iconQueryTest() throws Exception { |
| // TODO(b/31065385): Passpoint config management. |
| } |
| |
| @Test |
| public void verboseLogRecSizeIsGreaterThanNormalSize() { |
| assertTrue(LOG_REC_LIMIT_IN_VERBOSE_MODE > mWifiGlobals.getClientModeImplNumLogRecs()); |
| } |
| |
| /** |
| * Verifies that, by default, we allow only the "normal" number of log records. |
| */ |
| @Test |
| public void normalLogRecSizeIsUsedByDefault() { |
| mCmi.enableVerboseLogging(false); |
| assertEquals(mWifiGlobals.getClientModeImplNumLogRecs(), mCmi.getLogRecMaxSize()); |
| } |
| |
| /** |
| * Verifies that, in verbose mode, we allow a larger number of log records. |
| */ |
| @Test |
| public void enablingVerboseLoggingUpdatesLogRecSize() { |
| when(mActivityManager.isLowRamDevice()).thenReturn(false); |
| mCmi.enableVerboseLogging(true); |
| assertEquals(LOG_REC_LIMIT_IN_VERBOSE_MODE, mCmi.getLogRecMaxSize()); |
| } |
| |
| /** |
| * Verifies that, in verbose mode, we allow a larger number of log records on a low ram device. |
| */ |
| @Test |
| public void enablingVerboseLoggingUpdatesLogRecSizeLowRamDevice() { |
| when(mActivityManager.isLowRamDevice()).thenReturn(true); |
| mCmi.enableVerboseLogging(true); |
| assertEquals(LOG_REC_LIMIT_IN_VERBOSE_MODE_LOW_RAM, mCmi.getLogRecMaxSize()); |
| } |
| |
| @Test |
| public void disablingVerboseLoggingClearsRecords() { |
| mCmi.sendMessage(ClientModeImpl.CMD_DISCONNECT); |
| mLooper.dispatchAll(); |
| assertTrue(mCmi.getLogRecSize() >= 1); |
| |
| mCmi.enableVerboseLogging(false); |
| assertEquals(0, mCmi.getLogRecSize()); |
| } |
| |
| @Test |
| public void disablingVerboseLoggingUpdatesLogRecSize() { |
| mCmi.enableVerboseLogging(true); |
| mCmi.enableVerboseLogging(false); |
| assertEquals(mWifiGlobals.getClientModeImplNumLogRecs(), 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.enableVerboseLogging(false); |
| 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(true); |
| 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 { |
| when(mPasspointManager.startSubscriptionProvisioning(anyInt(), |
| any(OsuProvider.class), any(IProvisioningCallback.class))).thenReturn(true); |
| mLooper.startAutoDispatch(); |
| assertTrue(mCmi.syncStartSubscriptionProvisioning( |
| OTHER_USER_UID, mOsuProvider, mProvisioningCallback)); |
| verify(mPasspointManager).startSubscriptionProvisioning(OTHER_USER_UID, mOsuProvider, |
| mProvisioningCallback); |
| mLooper.stopAutoDispatch(); |
| } |
| |
| @Test |
| public void testGetCurrentNetwork() throws Exception { |
| // getCurrentNetwork() returns null when disconnected |
| assertNull(mCmi.getCurrentNetwork()); |
| connect(); |
| |
| assertEquals("L3ConnectedState", getCurrentState().getName()); |
| // getCurrentNetwork() returns non-null Network when connected |
| assertEquals(mNetwork, mCmi.getCurrentNetwork()); |
| // Now trigger disconnect |
| mCmi.disconnect(); |
| DisconnectEventInfo disconnectEventInfo = |
| new DisconnectEventInfo(mConnectedNetwork.SSID, TEST_BSSID_STR, 0, false); |
| mCmi.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, disconnectEventInfo); |
| mLooper.dispatchAll(); |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(0, WifiSsid.fromUtf8Text(mConnectedNetwork.SSID), |
| TEST_BSSID_STR, sFreq, SupplicantState.DISCONNECTED)); |
| mLooper.dispatchAll(); |
| assertEquals("DisconnectedState", getCurrentState().getName()); |
| assertNull(mCmi.getCurrentNetwork()); |
| } |
| |
| /** |
| * Test that we disconnect from a network if it was removed while we are in the |
| * L3ProvisioningState. |
| */ |
| @Test |
| public void disconnectFromNetworkWhenRemovedWhileObtainingIpAddr() throws Exception { |
| initializeAndAddNetworkAndVerifySuccess(); |
| |
| verify(mWifiNative).removeAllNetworks(WIFI_IFACE_NAME); |
| |
| startConnectSuccess(); |
| |
| when(mWifiConfigManager.getScanDetailCacheForNetwork(FRAMEWORK_NETWORK_ID)) |
| .thenReturn(mScanDetailCache); |
| |
| when(mScanDetailCache.getScanDetail(TEST_BSSID_STR)).thenReturn( |
| getGoogleGuestScanDetail(TEST_RSSI, TEST_BSSID_STR, sFreq)); |
| when(mScanDetailCache.getScanResult(TEST_BSSID_STR)).thenReturn( |
| getGoogleGuestScanDetail(TEST_RSSI, TEST_BSSID_STR, sFreq).getScanResult()); |
| |
| mCmi.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, |
| new NetworkConnectionEventInfo(0, TEST_WIFI_SSID, TEST_BSSID_STR, false, null)); |
| mLooper.dispatchAll(); |
| |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(0, TEST_WIFI_SSID, TEST_BSSID_STR, sFreq, |
| SupplicantState.COMPLETED)); |
| mLooper.dispatchAll(); |
| |
| assertEquals("L3ProvisioningState", getCurrentState().getName()); |
| |
| // trigger removal callback to trigger disconnect. |
| WifiConfiguration removedConfig = new WifiConfiguration(); |
| removedConfig.networkId = FRAMEWORK_NETWORK_ID; |
| for (WifiConfigManager.OnNetworkUpdateListener listener : mConfigUpdateListenerCaptor |
| .getAllValues()) { |
| listener.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(); |
| |
| verify(mWifiNative, times(2)).disconnect(WIFI_IFACE_NAME); |
| } |
| |
| /** |
| * Verifies that WifiInfo is updated upon SUPPLICANT_STATE_CHANGE_EVENT. |
| */ |
| @Test |
| public void testWifiInfoUpdatedUponSupplicantStateChangedEvent() throws Exception { |
| // Connect to network with |TEST_BSSID_STR|, |sFreq|. |
| connect(); |
| |
| // Set the scan detail cache for roaming target. |
| when(mWifiConfigManager.getScanDetailCacheForNetwork(FRAMEWORK_NETWORK_ID)) |
| .thenReturn(mScanDetailCache); |
| when(mScanDetailCache.getScanDetail(TEST_BSSID_STR1)).thenReturn( |
| getGoogleGuestScanDetail(TEST_RSSI, TEST_BSSID_STR1, sFreq1)); |
| when(mScanDetailCache.getScanResult(TEST_BSSID_STR1)).thenReturn( |
| getGoogleGuestScanDetail(TEST_RSSI, TEST_BSSID_STR1, sFreq1).getScanResult()); |
| |
| // This simulates the behavior of roaming to network with |TEST_BSSID_STR1|, |sFreq1|. |
| // Send a SUPPLICANT_STATE_CHANGE_EVENT, verify WifiInfo is updated. |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(0, TEST_WIFI_SSID, TEST_BSSID_STR1, sFreq1, |
| SupplicantState.COMPLETED)); |
| mLooper.dispatchAll(); |
| |
| WifiInfo wifiInfo = mWifiInfo; |
| assertEquals(TEST_BSSID_STR1, wifiInfo.getBSSID()); |
| assertEquals(sFreq1, wifiInfo.getFrequency()); |
| assertEquals(SupplicantState.COMPLETED, wifiInfo.getSupplicantState()); |
| |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(0, TEST_WIFI_SSID, TEST_BSSID_STR1, sFreq1, |
| SupplicantState.DISCONNECTED)); |
| mLooper.dispatchAll(); |
| |
| wifiInfo = mWifiInfo; |
| assertNull(wifiInfo.getBSSID()); |
| assertEquals(WifiManager.UNKNOWN_SSID, wifiInfo.getSSID()); |
| assertEquals(WifiConfiguration.INVALID_NETWORK_ID, wifiInfo.getNetworkId()); |
| assertEquals(SupplicantState.DISCONNECTED, wifiInfo.getSupplicantState()); |
| assertEquals("DisconnectedState", getCurrentState().getName()); |
| } |
| |
| |
| /** |
| * Verifies that WifiInfo is updated upon SUPPLICANT_STATE_CHANGE_EVENT. |
| */ |
| @Test |
| public void testWifiInfoUpdatedUponSupplicantStateChangedEventWithWrongSsid() throws Exception { |
| // Connect to network with |TEST_BSSID_STR|, |sFreq|. |
| connect(); |
| |
| // Set the scan detail cache for roaming target. |
| when(mWifiConfigManager.getScanDetailCacheForNetwork(FRAMEWORK_NETWORK_ID)) |
| .thenReturn(mScanDetailCache); |
| when(mScanDetailCache.getScanDetail(TEST_BSSID_STR1)).thenReturn( |
| getGoogleGuestScanDetail(TEST_RSSI, TEST_BSSID_STR1, sFreq1)); |
| when(mScanDetailCache.getScanResult(TEST_BSSID_STR1)).thenReturn( |
| getGoogleGuestScanDetail(TEST_RSSI, TEST_BSSID_STR1, sFreq1).getScanResult()); |
| |
| // This simulates the behavior of roaming to network with |TEST_BSSID_STR1|, |sFreq1|. |
| // Send a SUPPLICANT_STATE_CHANGE_EVENT, verify WifiInfo is updated. |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(0, TEST_WIFI_SSID, TEST_BSSID_STR1, sFreq1, |
| SupplicantState.COMPLETED)); |
| mLooper.dispatchAll(); |
| |
| WifiInfo wifiInfo = mWifiInfo; |
| assertEquals(TEST_BSSID_STR1, wifiInfo.getBSSID()); |
| assertEquals(sFreq1, wifiInfo.getFrequency()); |
| assertEquals(SupplicantState.COMPLETED, wifiInfo.getSupplicantState()); |
| |
| // Send state change event with wrong ssid. |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(0, TEST_WIFI_SSID1, TEST_BSSID_STR, sFreq, |
| SupplicantState.DISCONNECTED)); |
| mLooper.dispatchAll(); |
| |
| wifiInfo = mWifiInfo; |
| assertNull(wifiInfo.getBSSID()); |
| assertEquals(WifiManager.UNKNOWN_SSID, wifiInfo.getSSID()); |
| assertEquals(WifiConfiguration.INVALID_NETWORK_ID, wifiInfo.getNetworkId()); |
| assertEquals(SupplicantState.DISCONNECTED, wifiInfo.getSupplicantState()); |
| assertEquals("DisconnectedState", getCurrentState().getName()); |
| } |
| |
| @Test |
| public void testTriggerWifiNetworkStateChangedListener() throws Exception { |
| InOrder inOrder = inOrder(mActiveModeWarden); |
| connect(); |
| inOrder.verify(mActiveModeWarden).onNetworkStateChanged( |
| WifiManager.WifiNetworkStateChangedListener.WIFI_ROLE_CLIENT_PRIMARY, |
| WifiManager.WifiNetworkStateChangedListener.WIFI_NETWORK_STATUS_CONNECTING); |
| inOrder.verify(mActiveModeWarden).onNetworkStateChanged( |
| WifiManager.WifiNetworkStateChangedListener.WIFI_ROLE_CLIENT_PRIMARY, |
| WifiManager.WifiNetworkStateChangedListener |
| .WIFI_NETWORK_STATUS_OBTAINING_IPADDR); |
| inOrder.verify(mActiveModeWarden).onNetworkStateChanged( |
| WifiManager.WifiNetworkStateChangedListener.WIFI_ROLE_CLIENT_PRIMARY, |
| WifiManager.WifiNetworkStateChangedListener.WIFI_NETWORK_STATUS_CONNECTED); |
| inOrder.verify(mActiveModeWarden, never()).onNetworkStateChanged( |
| WifiManager.WifiNetworkStateChangedListener.WIFI_ROLE_CLIENT_PRIMARY, |
| WifiManager.WifiNetworkStateChangedListener.WIFI_NETWORK_STATUS_DISCONNECTED); |
| } |
| |
| /** |
| * Verifies that WifiInfo is updated upon CMD_ASSOCIATED_BSSID event. |
| */ |
| @Test |
| public void testWifiInfoUpdatedUponAssociatedBSSIDEvent() throws Exception { |
| // Connect to network with |TEST_BSSID_STR|, |sFreq|. |
| connect(); |
| |
| // Set the scan detail cache for roaming target. |
| when(mWifiConfigManager.getScanDetailCacheForNetwork(FRAMEWORK_NETWORK_ID)) |
| .thenReturn(mScanDetailCache); |
| when(mScanDetailCache.getScanDetail(TEST_BSSID_STR1)).thenReturn( |
| getGoogleGuestScanDetail(TEST_RSSI, TEST_BSSID_STR1, sFreq1)); |
| when(mScanDetailCache.getScanResult(TEST_BSSID_STR1)).thenReturn( |
| getGoogleGuestScanDetail(TEST_RSSI, TEST_BSSID_STR1, sFreq1).getScanResult()); |
| |
| // This simulates the behavior of roaming to network with |TEST_BSSID_STR1|, |sFreq1|. |
| // Send a CMD_ASSOCIATED_BSSID, verify WifiInfo is updated. |
| mCmi.sendMessage(WifiMonitor.ASSOCIATED_BSSID_EVENT, 0, 0, TEST_BSSID_STR1); |
| mLooper.dispatchAll(); |
| |
| WifiInfo wifiInfo = mWifiInfo; |
| assertEquals(TEST_BSSID_STR1, wifiInfo.getBSSID()); |
| assertEquals(sFreq1, wifiInfo.getFrequency()); |
| assertEquals(SupplicantState.COMPLETED, wifiInfo.getSupplicantState()); |
| verifyNetworkStateChangedBroadcast(times(2), |
| new NetworkStateChangedIntentMatcher(CONNECTED)); |
| } |
| |
| private void callmethod(VerificationMode mode, ArgumentMatcher<Intent> intentMatcher) { |
| if (SdkLevel.isAtLeastU()) { |
| verify(mUserAllContext, mode).sendStickyBroadcast( |
| argThat(intentMatcher), any()); |
| } else { |
| verify(mContext, mode).sendStickyBroadcastAsUser( |
| argThat(intentMatcher), any()); |
| } |
| } |
| |
| /** |
| * 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 testWifiInfoCleanedUpEnteringExitingConnectableState() throws Exception { |
| InOrder inOrderMetrics = inOrder(mWifiMetrics); |
| Log.i(TAG, mCmi.getCurrentState().getName()); |
| String initialBSSID = "aa:bb:cc:dd:ee:ff"; |
| WifiInfo wifiInfo = mWifiInfo; |
| wifiInfo.setBSSID(initialBSSID); |
| |
| // reset mWifiNative since initializeCmi() was called in setup() |
| resetWifiNative(); |
| |
| // Set CMI to CONNECT_MODE and verify state, and wifi enabled in ConnectivityManager |
| initializeCmi(); |
| inOrderMetrics.verify(mWifiMetrics) |
| .setWifiState(WIFI_IFACE_NAME, WifiMetricsProto.WifiLog.WIFI_DISCONNECTED); |
| inOrderMetrics.verify(mWifiMetrics) |
| .logStaEvent(WIFI_IFACE_NAME, StaEvent.TYPE_WIFI_ENABLED); |
| assertNull(wifiInfo.getBSSID()); |
| |
| // Send a SUPPLICANT_STATE_CHANGE_EVENT, verify WifiInfo is updated |
| connect(); |
| assertEquals(TEST_BSSID_STR, 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.stop(); |
| mLooper.dispatchAll(); |
| |
| inOrderMetrics.verify(mWifiMetrics).setWifiState(WIFI_IFACE_NAME, |
| WifiMetricsProto.WifiLog.WIFI_DISABLED); |
| inOrderMetrics.verify(mWifiMetrics) |
| .logStaEvent(WIFI_IFACE_NAME, StaEvent.TYPE_WIFI_DISABLED); |
| assertNull(wifiInfo.getBSSID()); |
| assertEquals(SupplicantState.DISCONNECTED, wifiInfo.getSupplicantState()); |
| } |
| |
| @Test |
| public void testWifiInfoCleanedUpEnteringExitingConnectableState2() throws Exception { |
| // Send a SUPPLICANT_STATE_CHANGE_EVENT, verify WifiInfo is not updated |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(0, TEST_WIFI_SSID, TEST_BSSID_STR, sFreq, |
| SupplicantState.COMPLETED)); |
| mLooper.dispatchAll(); |
| assertNull(mWifiInfo.getBSSID()); |
| assertEquals(SupplicantState.DISCONNECTED, mWifiInfo.getSupplicantState()); |
| } |
| |
| @Test |
| public void testWifiInfoCleanedUpEnteringExitingConnectableState3() throws Exception { |
| String initialBSSID = "aa:bb:cc:dd:ee:ff"; |
| InOrder inOrderMetrics = inOrder(mWifiMetrics); |
| |
| // Set the bssid to something, so we can verify it is cleared (just in case) |
| mWifiInfo.setBSSID(initialBSSID); |
| |
| initializeCmi(); |
| |
| inOrderMetrics.verify(mWifiMetrics) |
| .setWifiState(WIFI_IFACE_NAME, WifiMetricsProto.WifiLog.WIFI_DISCONNECTED); |
| inOrderMetrics.verify(mWifiMetrics) |
| .logStaEvent(WIFI_IFACE_NAME, StaEvent.TYPE_WIFI_ENABLED); |
| assertEquals("DisconnectedState", getCurrentState().getName()); |
| assertEquals(SupplicantState.DISCONNECTED, mWifiInfo.getSupplicantState()); |
| assertNull(mWifiInfo.getBSSID()); |
| } |
| |
| /** |
| * Test that connected SSID and BSSID are exposed to system server. |
| * Also tests that {@link ClientModeImpl#getConnectionInfo()} always |
| * returns a copy of WifiInfo. |
| */ |
| @Test |
| public void testConnectedIdsAreVisibleFromSystemServer() throws Exception { |
| WifiInfo wifiInfo = mWifiInfo; |
| // Get into a connected state, with known BSSID and SSID |
| connect(); |
| assertEquals(TEST_BSSID_STR, wifiInfo.getBSSID()); |
| assertEquals(TEST_WIFI_SSID, wifiInfo.getWifiSsid()); |
| |
| WifiInfo connectionInfo = mCmi.getConnectionInfo(); |
| 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 |TEST_BSSID_STR|, |sFreq|, and then disconnect. |
| disconnect(); |
| verify(mActiveModeWarden).onNetworkStateChanged( |
| WifiManager.WifiNetworkStateChangedListener.WIFI_ROLE_CLIENT_PRIMARY, |
| WifiManager.WifiNetworkStateChangedListener.WIFI_NETWORK_STATUS_DISCONNECTED); |
| |
| mCmi.reconnect(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 |TEST_BSSID_STR|, |sFreq|. |
| connect(); |
| |
| mCmi.reconnect(ClientModeImpl.WIFI_WORK_SOURCE); |
| mLooper.dispatchAll(); |
| verify(mWifiConnectivityManager, never()) |
| .forceConnectivityScan(ClientModeImpl.WIFI_WORK_SOURCE); |
| } |
| |
| /** |
| * 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, TEST_BSSID_STR); |
| mLooper.dispatchAll(); |
| // Simulate an ASSOCIATION_REJECTION_EVENT, due to the AP being busy |
| mCmi.sendMessage(WifiMonitor.ASSOCIATION_REJECTION_EVENT, |
| new AssocRejectEventInfo(TEST_SSID, TEST_BSSID_STR, |
| ISupplicantStaIfaceCallback.StatusCode.AP_UNABLE_TO_HANDLE_NEW_STA, |
| false)); |
| 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 |
| reset(mWifiConfigManager); |
| 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, TEST_BSSID_STR); |
| mLooper.dispatchAll(); |
| mCmi.sendMessage(WifiMonitor.AUTHENTICATION_FAILURE_EVENT, |
| new AuthenticationFailureEventInfo(TEST_SSID, MacAddress.fromString(TEST_BSSID_STR), |
| WifiManager.ERROR_AUTH_FAILURE_TIMEOUT, -1)); |
| DisconnectEventInfo disconnectEventInfo = |
| new DisconnectEventInfo(TEST_SSID, TEST_BSSID_STR, 0, false); |
| mCmi.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, disconnectEventInfo); |
| mLooper.dispatchAll(); |
| verify(mWifiConfigManager).clearRecentFailureReason(eq(0)); |
| verify(mWifiConfigManager, never()).setRecentFailureAssociationStatus(anyInt(), anyInt()); |
| |
| // Simulate a NETWORK_CONNECTION_EVENT which should clear the ExtraFailureReason |
| reset(mWifiConfigManager); |
| 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, TEST_BSSID_STR); |
| mLooper.dispatchAll(); |
| mCmi.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, |
| new NetworkConnectionEventInfo(0, TEST_WIFI_SSID, null, false, null)); |
| mLooper.dispatchAll(); |
| verify(mWifiConfigManager).clearRecentFailureReason(eq(0)); |
| verify(mWifiConfigManager, never()).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#isRecentlySelectedByTheUser(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 testIsRecentlySelectedByTheUser_SameNetworkNotExpired() { |
| WifiConfiguration currentConfig = makeLastSelectedWifiConfiguration(5, |
| ClientModeImpl.LAST_SELECTED_NETWORK_EXPIRATION_AGE_MILLIS - 1); |
| assertTrue(mCmi.isRecentlySelectedByTheUser(currentConfig)); |
| } |
| |
| /** |
| * Test that the helper method |
| * {@link ClientModeImpl#isRecentlySelectedByTheUser(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 testIsRecentlySelectedByTheUser_SameNetworkExpired() { |
| WifiConfiguration currentConfig = makeLastSelectedWifiConfiguration(5, |
| ClientModeImpl.LAST_SELECTED_NETWORK_EXPIRATION_AGE_MILLIS + 1); |
| assertFalse(mCmi.isRecentlySelectedByTheUser(currentConfig)); |
| } |
| |
| /** |
| * Test that the helper method |
| * {@link ClientModeImpl#isRecentlySelectedByTheUser(WifiConfiguration)} |
| * returns false when we connect to a different network to the last selected network. |
| */ |
| @Test |
| public void testIsRecentlySelectedByTheUser_DifferentNetwork() { |
| WifiConfiguration currentConfig = makeLastSelectedWifiConfiguration(5, |
| ClientModeImpl.LAST_SELECTED_NETWORK_EXPIRATION_AGE_MILLIS - 1); |
| currentConfig.networkId = 4; |
| assertFalse(mCmi.isRecentlySelectedByTheUser(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. |
| ArgumentCaptor<NetworkAgentConfig> configCaptor = |
| ArgumentCaptor.forClass(NetworkAgentConfig.class); |
| ArgumentCaptor<NetworkCapabilities> networkCapabilitiesCaptor = |
| ArgumentCaptor.forClass(NetworkCapabilities.class); |
| |
| verify(mWifiInjector).makeWifiNetworkAgent( |
| networkCapabilitiesCaptor.capture(), |
| any(), |
| configCaptor.capture(), |
| any(), |
| mWifiNetworkAgentCallbackCaptor.capture()); |
| |
| configChecker.accept(configCaptor.getValue()); |
| networkCapabilitiesChecker.accept(networkCapabilitiesCaptor.getValue()); |
| } |
| |
| private void expectNetworkAgentUpdateCapabilities( |
| Consumer<NetworkCapabilities> networkCapabilitiesChecker) throws Exception { |
| ArgumentCaptor<NetworkCapabilities> captor = ArgumentCaptor.forClass( |
| NetworkCapabilities.class); |
| mLooper.dispatchAll(); |
| verify(mWifiNetworkAgent).sendNetworkCapabilitiesAndCache(captor.capture()); |
| networkCapabilitiesChecker.accept(captor.getValue()); |
| } |
| |
| /** |
| * 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 explicitly selected, has role SECONDARY_TRANSIENT, but |
| * noInternetAccessExpected is false, the {@link NetworkAgentConfig} contains the right values |
| * of explicitlySelected, acceptUnvalidated and acceptPartialConnectivity. |
| */ |
| @Test |
| public void testExplicitlySelected_secondaryTransient_expectNotExplicitlySelected() |
| throws Exception { |
| // Network is explicitly selected. |
| WifiConfiguration config = makeLastSelectedWifiConfiguration(FRAMEWORK_NETWORK_ID, |
| ClientModeImpl.LAST_SELECTED_NETWORK_EXPIRATION_AGE_MILLIS - 1); |
| mConnectedNetwork.noInternetAccessExpected = false; |
| |
| when(mClientModeManager.getRole()).thenReturn(ROLE_CLIENT_SECONDARY_TRANSIENT); |
| |
| connect(); |
| expectRegisterNetworkAgent((agentConfig) -> { |
| assertFalse(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 Rssi Monitoring is started and the callback registered after connecting. |
| */ |
| @Test |
| public void verifyRssiMonitoringCallbackIsRegistered() throws Exception { |
| // Simulate the first connection. |
| connect(); |
| |
| verify(mWifiInjector).makeWifiNetworkAgent(any(), any(), any(), any(), |
| mWifiNetworkAgentCallbackCaptor.capture()); |
| |
| ArrayList<Integer> thresholdsArray = new ArrayList<>(); |
| thresholdsArray.add(RSSI_THRESHOLD_MAX); |
| thresholdsArray.add(RSSI_THRESHOLD_MIN); |
| mWifiNetworkAgentCallbackCaptor.getValue().onSignalStrengthThresholdsUpdated( |
| thresholdsArray.stream().mapToInt(Integer::intValue).toArray()); |
| 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 = mWifiInfo; |
| 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 monitoring is not enabled on secondary STA. |
| */ |
| @Test |
| public void testRssiMonitoringOnSecondaryIsNotEnabled() throws Exception { |
| when(mClientModeManager.getRole()).thenReturn(ROLE_CLIENT_SECONDARY_TRANSIENT); |
| connect(); |
| |
| verify(mWifiInjector).makeWifiNetworkAgent(any(), any(), any(), any(), |
| mWifiNetworkAgentCallbackCaptor.capture()); |
| |
| int[] thresholds = {RSSI_THRESHOLD_BREACH_MAX}; |
| mWifiNetworkAgentCallbackCaptor.getValue().onSignalStrengthThresholdsUpdated(thresholds); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiNative, never()).startRssiMonitoring(anyString(), anyByte(), anyByte(), |
| any()); |
| } |
| |
| /** |
| * 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; |
| WifiSignalPollResults signalPollResults = new WifiSignalPollResults(); |
| signalPollResults.addEntry(0, -42, 65, 54, sFreq); |
| when(mWifiNative.getSupportedFeatureSet(WIFI_IFACE_NAME)).thenReturn( |
| WifiManager.WIFI_FEATURE_LINK_LAYER_STATS); |
| when(mWifiNative.getWifiLinkLayerStats(any())).thenReturn(llStats); |
| when(mWifiNative.signalPoll(any())).thenReturn(signalPollResults); |
| when(mClock.getWallClockMillis()).thenReturn(startMillis + 0); |
| mCmi.enableRssiPolling(true); |
| connect(); |
| mLooper.dispatchAll(); |
| assertRssiChangeBroadcastSent(1); |
| when(mClock.getWallClockMillis()).thenReturn(startMillis + 3333); |
| mLooper.dispatchAll(); |
| WifiInfo wifiInfo = mWifiInfo; |
| assertEquals(llStats.txmpdu_be, wifiInfo.txSuccess); |
| assertEquals(llStats.rxmpdu_bk, wifiInfo.rxSuccess); |
| assertEquals(signalPollResults.getRssi(), wifiInfo.getRssi()); |
| assertEquals(signalPollResults.getTxLinkSpeed(), wifiInfo.getLinkSpeed()); |
| assertEquals(signalPollResults.getTxLinkSpeed(), wifiInfo.getTxLinkSpeedMbps()); |
| assertEquals(signalPollResults.getRxLinkSpeed(), wifiInfo.getRxLinkSpeedMbps()); |
| assertEquals(sFreq, wifiInfo.getFrequency()); |
| verify(mPerNetwork, atLeastOnce()).getTxLinkBandwidthKbps(); |
| verify(mPerNetwork, atLeastOnce()).getRxLinkBandwidthKbps(); |
| verify(mWifiScoreCard).noteSignalPoll(any()); |
| } |
| |
| /** |
| * Verify that RSSI polling will send RSSI broadcasts if the RSSI signal level has changed |
| */ |
| @Test |
| public void verifyConnectedModeRssiPollingWithSameSignalLevel() throws Exception { |
| final long startMillis = 1_500_000_000_100L; |
| WifiLinkLayerStats llStats = new WifiLinkLayerStats(); |
| llStats.txmpdu_be = 1000; |
| llStats.rxmpdu_bk = 2000; |
| WifiSignalPollResults signalPollResults = new WifiSignalPollResults(); |
| signalPollResults.addEntry(0, -42, 65, 54, sFreq); |
| when(mWifiNative.getSupportedFeatureSet(WIFI_IFACE_NAME)).thenReturn( |
| WifiManager.WIFI_FEATURE_LINK_LAYER_STATS); |
| when(mWifiNative.getWifiLinkLayerStats(any())).thenReturn(llStats); |
| when(mWifiNative.signalPoll(any())).thenReturn(signalPollResults); |
| when(mClock.getWallClockMillis()).thenReturn(startMillis + 0); |
| mCmi.enableRssiPolling(true); |
| connect(); |
| mLooper.dispatchAll(); |
| when(mClock.getWallClockMillis()).thenReturn(startMillis + 3333); |
| mLooper.moveTimeForward(mWifiGlobals.getPollRssiIntervalMillis()); |
| mLooper.dispatchAll(); |
| // Two broadcasts, one when enabling RSSI polling and the other for RSSI polling after |
| // IP configuration success resets the current level. |
| assertRssiChangeBroadcastSent(2); |
| |
| // Same RSSI should not send another broadcast |
| mLooper.moveTimeForward(mWifiGlobals.getPollRssiIntervalMillis()); |
| mLooper.dispatchAll(); |
| assertRssiChangeBroadcastSent(2); |
| |
| // Same signal level should not send another broadcast |
| signalPollResults.addEntry(0, -43, 65, 54, sFreq); |
| mLooper.moveTimeForward(mWifiGlobals.getPollRssiIntervalMillis()); |
| mLooper.dispatchAll(); |
| assertRssiChangeBroadcastSent(2); |
| |
| // Different signal level should send another broadcast |
| signalPollResults.addEntry(0, -70, 65, 54, sFreq); |
| mLooper.moveTimeForward(mWifiGlobals.getPollRssiIntervalMillis()); |
| mLooper.dispatchAll(); |
| assertRssiChangeBroadcastSent(3); |
| } |
| |
| /** |
| * Verify that RSSI polling with verbose logging enabled by the user will send RSSI broadcasts |
| * if the RSSI has changed at all. |
| */ |
| @Test |
| public void verifyConnectedModeRssiPollingWithSameSignalLevelVerboseLoggingEnabled() |
| throws Exception { |
| when(mWifiGlobals.getVerboseLoggingLevel()) |
| .thenReturn(WifiManager.VERBOSE_LOGGING_LEVEL_ENABLED); |
| final long startMillis = 1_500_000_000_100L; |
| WifiLinkLayerStats llStats = new WifiLinkLayerStats(); |
| llStats.txmpdu_be = 1000; |
| llStats.rxmpdu_bk = 2000; |
| WifiSignalPollResults signalPollResults = new WifiSignalPollResults(); |
| signalPollResults.addEntry(0, -42, 65, 54, sFreq); |
| when(mWifiNative.getSupportedFeatureSet(WIFI_IFACE_NAME)).thenReturn( |
| WifiManager.WIFI_FEATURE_LINK_LAYER_STATS); |
| when(mWifiNative.getWifiLinkLayerStats(any())).thenReturn(llStats); |
| when(mWifiNative.signalPoll(any())).thenReturn(signalPollResults); |
| when(mClock.getWallClockMillis()).thenReturn(startMillis + 0); |
| mCmi.enableRssiPolling(true); |
| connect(); |
| mLooper.dispatchAll(); |
| when(mClock.getWallClockMillis()).thenReturn(startMillis + 3333); |
| mLooper.moveTimeForward(mWifiGlobals.getPollRssiIntervalMillis()); |
| mLooper.dispatchAll(); |
| // Two broadcasts, one when enabling RSSI polling and the other for RSSI polling after |
| // IP configuration success resets the current level. |
| assertRssiChangeBroadcastSent(2); |
| |
| // Same RSSI should not send another broadcast |
| mLooper.moveTimeForward(mWifiGlobals.getPollRssiIntervalMillis()); |
| mLooper.dispatchAll(); |
| assertRssiChangeBroadcastSent(2); |
| |
| // Different RSSI but same signal level should send another broadcast since we're verbose. |
| signalPollResults.addEntry(0, -43, 65, 54, sFreq); |
| mLooper.moveTimeForward(mWifiGlobals.getPollRssiIntervalMillis()); |
| mLooper.dispatchAll(); |
| assertRssiChangeBroadcastSent(3); |
| } |
| |
| /** |
| * Verify link bandwidth update in connected mode |
| */ |
| @Test |
| public void verifyConnectedModeNetworkCapabilitiesBandwidthUpdate() throws Exception { |
| when(mPerNetwork.getTxLinkBandwidthKbps()).thenReturn(40_000); |
| when(mPerNetwork.getRxLinkBandwidthKbps()).thenReturn(50_000); |
| when(mWifiNetworkFactory.getSpecificNetworkRequestUids(any(), any())) |
| .thenReturn(Collections.emptySet()); |
| when(mWifiNetworkFactory.getSpecificNetworkRequestUidAndPackageName(any(), any())) |
| .thenReturn(Pair.create(Process.INVALID_UID, "")); |
| mCmi.enableRssiPolling(true); |
| // Simulate the first connection. |
| connectWithValidInitRssi(-42); |
| |
| // NetworkCapabilities should be always updated after the connection |
| ArgumentCaptor<NetworkCapabilities> networkCapabilitiesCaptor = |
| ArgumentCaptor.forClass(NetworkCapabilities.class); |
| verify(mWifiInjector).makeWifiNetworkAgent( |
| networkCapabilitiesCaptor.capture(), any(), any(), any(), any()); |
| NetworkCapabilities networkCapabilities = networkCapabilitiesCaptor.getValue(); |
| assertNotNull(networkCapabilities); |
| assertEquals(-42, mWifiInfo.getRssi()); |
| assertEquals(40_000, networkCapabilities.getLinkUpstreamBandwidthKbps()); |
| assertEquals(50_000, networkCapabilities.getLinkDownstreamBandwidthKbps()); |
| verify(mCmi.mNetworkAgent, times(2)) |
| .sendNetworkCapabilitiesAndCache(networkCapabilitiesCaptor.capture()); |
| |
| // Enable RSSI polling |
| final long startMillis = 1_500_000_000_100L; |
| WifiLinkLayerStats llStats = new WifiLinkLayerStats(); |
| WifiSignalPollResults signalPollResults = new WifiSignalPollResults(); |
| signalPollResults.addEntry(0, -42, 65, 54, sFreq); |
| when(mWifiNative.getSupportedFeatureSet(WIFI_IFACE_NAME)).thenReturn( |
| WifiManager.WIFI_FEATURE_LINK_LAYER_STATS); |
| when(mWifiNative.getWifiLinkLayerStats(any())).thenReturn(llStats); |
| when(mWifiNative.signalPoll(any())).thenReturn(signalPollResults); |
| when(mClock.getWallClockMillis()).thenReturn(startMillis + 0); |
| when(mPerNetwork.getTxLinkBandwidthKbps()).thenReturn(82_000); |
| when(mPerNetwork.getRxLinkBandwidthKbps()).thenReturn(92_000); |
| mCmi.sendMessage(ClientModeImpl.CMD_RSSI_POLL, 1); |
| mLooper.dispatchAll(); |
| when(mClock.getWallClockMillis()).thenReturn(startMillis + 3333); |
| mLooper.dispatchAll(); |
| |
| // NetworkCapabilities should be updated after a big change of bandwidth |
| verify(mCmi.mNetworkAgent, times(3)) |
| .sendNetworkCapabilitiesAndCache(networkCapabilitiesCaptor.capture()); |
| networkCapabilities = networkCapabilitiesCaptor.getValue(); |
| assertEquals(82_000, networkCapabilities.getLinkUpstreamBandwidthKbps()); |
| assertEquals(92_000, networkCapabilities.getLinkDownstreamBandwidthKbps()); |
| |
| // NetworkCapabilities should be updated when the connected channel frequency is changed |
| // For example due to AP channel switch announcement(CSA). |
| WifiSignalPollResults signalPollResults1 = new WifiSignalPollResults(); |
| signalPollResults1.addEntry(0, -42, 65, 54, sFreq1); |
| when(mWifiNative.signalPoll(any())).thenReturn(signalPollResults1); |
| mCmi.sendMessage(ClientModeImpl.CMD_RSSI_POLL, 1); |
| mLooper.dispatchAll(); |
| |
| verify(mCmi.mNetworkAgent, times(4)) |
| .sendNetworkCapabilitiesAndCache(networkCapabilitiesCaptor.capture()); |
| assertEquals(sFreq1, mWifiInfo.getFrequency()); |
| |
| // No update after a small change of bandwidth |
| when(mPerNetwork.getTxLinkBandwidthKbps()).thenReturn(72_000); |
| when(mPerNetwork.getRxLinkBandwidthKbps()).thenReturn(82_000); |
| when(mClock.getWallClockMillis()).thenReturn(startMillis + 3333); |
| mLooper.dispatchAll(); |
| verify(mCmi.mNetworkAgent, times(4)) |
| .sendNetworkCapabilitiesAndCache(networkCapabilitiesCaptor.capture()); |
| networkCapabilities = networkCapabilitiesCaptor.getValue(); |
| assertEquals(82_000, networkCapabilities.getLinkUpstreamBandwidthKbps()); |
| assertEquals(92_000, networkCapabilities.getLinkDownstreamBandwidthKbps()); |
| } |
| |
| /** |
| * Verify RSSI polling with verbose logging |
| */ |
| @Test |
| public void verifyConnectedModeRssiPollingWithVerboseLogging() throws Exception { |
| mCmi.enableVerboseLogging(true); |
| verifyConnectedModeRssiPolling(); |
| } |
| |
| /** |
| * Verify that calls to start and stop filtering multicast packets are passed on to the IpClient |
| * instance. |
| */ |
| @Test |
| public void verifyMcastLockManagerFilterControllerCallsUpdateIpClient() throws Exception { |
| 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_AUTO 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 { |
| // reset mWifiNative since initializeCmi() was called in setup() |
| resetWifiNative(); |
| |
| when(mWifiGlobals.isConnectedMacRandomizationEnabled()).thenReturn(false); |
| initializeCmi(); |
| initializeAndAddNetworkAndVerifySuccess(); |
| |
| connect(); |
| assertEquals(TEST_GLOBAL_MAC_ADDRESS.toString(), mWifiInfo.getMacAddress()); |
| verify(mWifiNative, never()).setStaMacAddress(any(), any()); |
| // try to retrieve factory MAC address (once at bootup, once for this connection) |
| verify(mSettingsConfigStore, times(2)).get(any()); |
| } |
| |
| /** |
| * Verifies that when |
| * 1. connected MAC randomization is on and |
| * 2. macRandomizationSetting of the WifiConfiguration is RANDOMIZATION_AUTO and |
| * 3. current MAC set to the driver is a randomized MAC address. |
| * 4. SSID of network to connect is in the MAC randomization forced disable list. |
| * |
| * Then the current MAC will be set to the factory MAC when CMD_START_CONNECT executes. |
| */ |
| @Test |
| public void testConnectedMacRandomizationRandomizationForceDisabled() throws Exception { |
| initializeAndAddNetworkAndVerifySuccess(); |
| |
| when(mWifiNative.getMacAddress(WIFI_IFACE_NAME)) |
| .thenReturn(TEST_LOCAL_MAC_ADDRESS.toString()); |
| mResources.setStringArray(R.array.config_wifiForceDisableMacRandomizationSsidList, |
| new String[]{mConnectedNetwork.SSID}); |
| |
| connect(); |
| verify(mWifiNative).setStaMacAddress(WIFI_IFACE_NAME, TEST_GLOBAL_MAC_ADDRESS); |
| verify(mWifiMetrics).logStaEvent( |
| eq(WIFI_IFACE_NAME), eq(StaEvent.TYPE_MAC_CHANGE), any(WifiConfiguration.class)); |
| verify(mWifiConfigManager).addOrUpdateNetwork(any(), eq(Process.SYSTEM_UID)); |
| assertEquals(TEST_GLOBAL_MAC_ADDRESS.toString(), mWifiInfo.getMacAddress()); |
| } |
| |
| @Test |
| public void testConnectedMacRandomizationForceDisabledSsidPrefix() throws Exception { |
| initializeAndAddNetworkAndVerifySuccess(); |
| |
| when(mWifiNative.getMacAddress(WIFI_IFACE_NAME)) |
| .thenReturn(TEST_LOCAL_MAC_ADDRESS.toString()); |
| |
| Set<String> ssidPrefixSet = new ArraySet<>(); |
| // Take the prefix of 2 characters, which should include the " and the first letter of |
| // actual SSID |
| ssidPrefixSet.add(mConnectedNetwork.SSID.substring(0, 2)); |
| when(mWifiGlobals.getMacRandomizationUnsupportedSsidPrefixes()).thenReturn(ssidPrefixSet); |
| |
| // Verify MAC randomization is disabled |
| connect(); |
| verify(mWifiNative).setStaMacAddress(WIFI_IFACE_NAME, TEST_GLOBAL_MAC_ADDRESS); |
| verify(mWifiMetrics).logStaEvent( |
| eq(WIFI_IFACE_NAME), eq(StaEvent.TYPE_MAC_CHANGE), any(WifiConfiguration.class)); |
| verify(mWifiConfigManager).addOrUpdateNetwork(any(), eq(Process.SYSTEM_UID)); |
| assertEquals(TEST_GLOBAL_MAC_ADDRESS.toString(), mWifiInfo.getMacAddress()); |
| } |
| |
| /** |
| * Verifies that when |
| * 1. connected MAC randomization is on and |
| * 2. macRandomizationSetting of the WifiConfiguration is RANDOMIZATION_AUTO 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(); |
| |
| connect(); |
| verify(mWifiNative).setStaMacAddress(WIFI_IFACE_NAME, TEST_LOCAL_MAC_ADDRESS); |
| verify(mWifiMetrics).logStaEvent( |
| eq(WIFI_IFACE_NAME), eq(StaEvent.TYPE_MAC_CHANGE), any(WifiConfiguration.class)); |
| assertEquals(TEST_LOCAL_MAC_ADDRESS.toString(), mWifiInfo.getMacAddress()); |
| } |
| |
| /** |
| * Verifies that when |
| * 1. connected MAC randomization is on and |
| * 2. macRandomizationSetting of the WifiConfiguration is RANDOMIZATION_AUTO 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(); |
| |
| when(mWifiNative.getMacAddress(WIFI_IFACE_NAME)) |
| .thenReturn(TEST_LOCAL_MAC_ADDRESS.toString()); |
| |
| connect(); |
| verify(mWifiNative, never()).setStaMacAddress(WIFI_IFACE_NAME, TEST_LOCAL_MAC_ADDRESS); |
| verify(mWifiMetrics, never()).logStaEvent( |
| any(), eq(StaEvent.TYPE_MAC_CHANGE), any(WifiConfiguration.class)); |
| assertEquals(TEST_LOCAL_MAC_ADDRESS.toString(), mWifiInfo.getMacAddress()); |
| } |
| |
| /** Verifies connecting to secondary DBS network with Mac randomization, the MAC address is |
| * the expected secondary Mac address. |
| * @throws Exception |
| */ |
| @Test |
| public void testConnectedMacRandomizationRandomizationSecondaryDbs() |
| throws Exception { |
| when(mClientModeManager.getRole()).thenReturn(ROLE_CLIENT_SECONDARY_LONG_LIVED); |
| when(mClientModeManager.isSecondaryInternet()).thenReturn(true); |
| when(mClientModeManager.isSecondaryInternetDbsAp()).thenReturn(true); |
| initializeAndAddNetworkAndVerifySuccess(); |
| connect(); |
| verify(mWifiNative).setStaMacAddress(WIFI_IFACE_NAME, TEST_LOCAL_MAC_ADDRESS_SECONDARY_DBS); |
| verify(mWifiMetrics).logStaEvent( |
| eq(WIFI_IFACE_NAME), eq(StaEvent.TYPE_MAC_CHANGE), any(WifiConfiguration.class)); |
| assertEquals(TEST_LOCAL_MAC_ADDRESS_SECONDARY_DBS.toString(), mWifiInfo.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(); |
| |
| when(mWifiNative.getMacAddress(WIFI_IFACE_NAME)) |
| .thenReturn(TEST_LOCAL_MAC_ADDRESS.toString()); |
| |
| WifiConfiguration config = new WifiConfiguration(); |
| config.SSID = TEST_SSID; |
| 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, TEST_BSSID_STR); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiNative).setStaMacAddress(WIFI_IFACE_NAME, TEST_GLOBAL_MAC_ADDRESS); |
| verify(mWifiMetrics).logStaEvent( |
| eq(WIFI_IFACE_NAME), eq(StaEvent.TYPE_MAC_CHANGE), any(WifiConfiguration.class)); |
| assertEquals(TEST_GLOBAL_MAC_ADDRESS.toString(), mWifiInfo.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(); |
| |
| clearInvocations(mWifiNative, mSettingsConfigStore); |
| |
| WifiConfiguration config = new WifiConfiguration(); |
| config.SSID = TEST_SSID; |
| 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, TEST_BSSID_STR); |
| mLooper.dispatchAll(); |
| |
| verify(mSettingsConfigStore).get(WIFI_STA_FACTORY_MAC_ADDRESS); |
| verify(mWifiNative, never()).getStaFactoryMacAddress(WIFI_IFACE_NAME); |
| verify(mSettingsConfigStore, never()).put( |
| WIFI_STA_FACTORY_MAC_ADDRESS, TEST_GLOBAL_MAC_ADDRESS.toString()); |
| |
| assertEquals(TEST_GLOBAL_MAC_ADDRESS.toString(), mWifiInfo.getMacAddress()); |
| |
| // Now disconnect & reconnect - should use the cached factory MAC address. |
| mCmi.disconnect(); |
| mLooper.dispatchAll(); |
| |
| mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, 0, 0, TEST_BSSID_STR); |
| mLooper.dispatchAll(); |
| |
| verify(mSettingsConfigStore, times(2)).get(WIFI_STA_FACTORY_MAC_ADDRESS); |
| // No new call to retrieve & store factory MAC address. |
| verify(mWifiNative, never()).getStaFactoryMacAddress(WIFI_IFACE_NAME); |
| verify(mSettingsConfigStore, never()).put( |
| WIFI_STA_FACTORY_MAC_ADDRESS, TEST_GLOBAL_MAC_ADDRESS.toString()); |
| } |
| |
| /** |
| * 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(), mWifiInfo.getMacAddress()); |
| |
| DisconnectEventInfo disconnectEventInfo = |
| new DisconnectEventInfo(mConnectedNetwork.SSID, TEST_BSSID_STR, 0, false); |
| mCmi.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, disconnectEventInfo); |
| mLooper.dispatchAll(); |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(0, WifiSsid.fromUtf8Text(mConnectedNetwork.SSID), |
| TEST_BSSID_STR, sFreq, SupplicantState.DISCONNECTED)); |
| mLooper.dispatchAll(); |
| |
| assertEquals("DisconnectedState", getCurrentState().getName()); |
| assertEquals(WifiInfo.DEFAULT_MAC_ADDRESS, mWifiInfo.getMacAddress()); |
| assertFalse(mWifiInfo.hasRealMacAddress()); |
| } |
| |
| /** |
| * Verifies that we don't set MAC address when config returns an invalid MAC address. |
| */ |
| @Test |
| public void testDoNotSetMacWhenInvalid() throws Exception { |
| initializeAndAddNetworkAndVerifySuccess(); |
| |
| WifiConfiguration config = new WifiConfiguration(); |
| config.SSID = TEST_SSID; |
| config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); |
| config.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_AUTO; |
| config.setRandomizedMacAddress(MacAddress.fromString(WifiInfo.DEFAULT_MAC_ADDRESS)); |
| when(mWifiConfigManager.getConfiguredNetworkWithoutMasking(0)).thenReturn(config); |
| |
| mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, 0, 0, TEST_BSSID_STR); |
| mLooper.dispatchAll(); |
| |
| // setStaMacAddress is invoked once when ClientModeImpl starts to prevent leak of factory |
| // MAC. |
| verify(mWifiNative).setStaMacAddress(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(); |
| |
| connect(); |
| verify(mWifiNative).setStaMacAddress(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, TEST_BSSID_STR); |
| mCmi.sendMessage(WifiMonitor.AUTHENTICATION_FAILURE_EVENT, |
| new AuthenticationFailureEventInfo(TEST_SSID, MacAddress.fromString(TEST_BSSID_STR), |
| WifiManager.ERROR_AUTH_FAILURE_TIMEOUT, -1)); |
| mLooper.dispatchAll(); |
| |
| WifiConfiguration config = mCmi.getConnectedWifiConfiguration(); |
| 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, TEST_BSSID_STR); |
| mCmi.sendMessage(WifiMonitor.AUTHENTICATION_FAILURE_EVENT, |
| new AuthenticationFailureEventInfo(TEST_SSID, MacAddress.fromString(TEST_BSSID_STR), |
| WifiManager.ERROR_AUTH_FAILURE_WRONG_PSWD, -1)); |
| 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, TEST_BSSID_STR); |
| verify(mWifiDiagnostics, never()).reportConnectionEvent( |
| eq(WifiDiagnostics.CONNECTION_EVENT_STARTED), any()); |
| mLooper.dispatchAll(); |
| verify(mWifiDiagnostics).reportConnectionEvent( |
| eq(WifiDiagnostics.CONNECTION_EVENT_STARTED), any()); |
| } |
| |
| @Test |
| public void testApNoResponseTriggerTimeout() throws Exception { |
| initializeAndAddNetworkAndVerifySuccess(); |
| mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, 0, 0, TEST_BSSID_STR); |
| mLooper.dispatchAll(); |
| verify(mWifiBlocklistMonitor, never()).handleBssidConnectionFailure(any(), any(), |
| anyInt(), anyInt()); |
| |
| mLooper.moveTimeForward(ClientModeImpl.CONNECTING_WATCHDOG_TIMEOUT_MS); |
| mLooper.dispatchAll(); |
| verify(mWifiBlocklistMonitor).handleBssidConnectionFailure(any(), any(), |
| eq(WifiBlocklistMonitor.REASON_FAILURE_NO_RESPONSE), anyInt()); |
| verify(mWifiDiagnostics).reportConnectionEvent( |
| eq(WifiDiagnostics.CONNECTION_EVENT_TIMEOUT), any()); |
| } |
| |
| @Test |
| public void testApNoResponseTimerCanceledAfterConnectionSuccess() throws Exception { |
| connect(); |
| mLooper.moveTimeForward(ClientModeImpl.CONNECTING_WATCHDOG_TIMEOUT_MS); |
| mLooper.dispatchAll(); |
| verify(mWifiBlocklistMonitor, never()).handleBssidConnectionFailure(any(), any(), |
| anyInt(), anyInt()); |
| verify(mWifiDiagnostics, never()).reportConnectionEvent( |
| eq(WifiDiagnostics.CONNECTION_EVENT_TIMEOUT), any()); |
| } |
| |
| private void verifyConnectionEventTimeoutDoesNotOccur() { |
| mLooper.moveTimeForward(ClientModeImpl.CONNECTING_WATCHDOG_TIMEOUT_MS); |
| mLooper.dispatchAll(); |
| verify(mWifiDiagnostics, never()).reportConnectionEvent( |
| eq(WifiDiagnostics.CONNECTION_EVENT_TIMEOUT), any()); |
| } |
| |
| /** |
| * 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, TEST_BSSID_STR, sFreq) |
| .getScanResult()); |
| // Setup CONNECT_MODE & a WifiConfiguration |
| initializeAndAddNetworkAndVerifySuccess(); |
| mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, 0, 0, TEST_BSSID_STR); |
| mCmi.sendMessage(WifiMonitor.ASSOCIATION_REJECTION_EVENT, |
| new AssocRejectEventInfo(TEST_SSID, TEST_BSSID_STR, |
| ISupplicantStaIfaceCallback.StatusCode.AP_UNABLE_TO_HANDLE_NEW_STA, false)); |
| verify(mWifiDiagnostics, never()).reportConnectionEvent( |
| eq(WifiDiagnostics.CONNECTION_EVENT_FAILED), any()); |
| mLooper.dispatchAll(); |
| verify(mWifiDiagnostics).reportConnectionEvent( |
| eq(WifiDiagnostics.CONNECTION_EVENT_FAILED), any()); |
| verify(mWifiConnectivityManager).handleConnectionAttemptEnded( |
| mClientModeManager, |
| WifiMetrics.ConnectionEvent.FAILURE_ASSOCIATION_REJECTION, |
| WifiMetricsProto.ConnectionEvent.ASSOCIATION_REJECTION_AP_UNABLE_TO_HANDLE_NEW_STA, |
| TEST_BSSID_STR, |
| mTestConfig); |
| verify(mWifiNetworkFactory).handleConnectionAttemptEnded( |
| eq(WifiMetrics.ConnectionEvent.FAILURE_ASSOCIATION_REJECTION), |
| eq(mTestConfig), eq(TEST_BSSID_STR), eq(WifiMetricsProto.ConnectionEvent |
| .ASSOCIATION_REJECTION_AP_UNABLE_TO_HANDLE_NEW_STA)); |
| verify(mWifiNetworkSuggestionsManager).handleConnectionAttemptEnded( |
| eq(WifiMetrics.ConnectionEvent.FAILURE_ASSOCIATION_REJECTION), |
| eq(mTestConfig), eq(null)); |
| verify(mWifiMetrics, never()) |
| .incrementNumBssidDifferentSelectionBetweenFrameworkAndFirmware(); |
| verifyConnectionEventTimeoutDoesNotOccur(); |
| |
| clearInvocations(mWifiDiagnostics, mWifiConfigManager, mWifiNetworkFactory, |
| mWifiNetworkSuggestionsManager); |
| |
| // Now trigger a disconnect event from supplicant, this should be ignored since the |
| // connection tracking should have already ended. |
| mCmi.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, |
| new DisconnectEventInfo(TEST_SSID, TEST_BSSID_STR, 0, false)); |
| mLooper.dispatchAll(); |
| |
| verifyNoMoreInteractions(mWifiDiagnostics, mWifiConfigManager, mWifiNetworkFactory, |
| mWifiNetworkSuggestionsManager); |
| } |
| |
| /** |
| * 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, TEST_BSSID_STR, sFreq) |
| .getScanResult()); |
| // Setup CONNECT_MODE & a WifiConfiguration |
| initializeAndAddNetworkAndVerifySuccess(); |
| mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, 0, 0, TEST_BSSID_STR); |
| mCmi.sendMessage(WifiMonitor.AUTHENTICATION_FAILURE_EVENT, |
| new AuthenticationFailureEventInfo(TEST_SSID, MacAddress.fromString(TEST_BSSID_STR), |
| WifiManager.ERROR_AUTH_FAILURE_WRONG_PSWD, -1)); |
| mLooper.dispatchAll(); |
| verify(mWifiDiagnostics).reportConnectionEvent( |
| eq(WifiDiagnostics.CONNECTION_EVENT_FAILED), any()); |
| verify(mWifiConnectivityManager).handleConnectionAttemptEnded( |
| mClientModeManager, |
| WifiMetrics.ConnectionEvent.FAILURE_AUTHENTICATION_FAILURE, |
| WifiMetricsProto.ConnectionEvent.AUTH_FAILURE_WRONG_PSWD, TEST_BSSID_STR, |
| mTestConfig); |
| verify(mWifiNetworkFactory).handleConnectionAttemptEnded( |
| eq(WifiMetrics.ConnectionEvent.FAILURE_AUTHENTICATION_FAILURE), |
| eq(mTestConfig), eq(TEST_BSSID_STR), eq(WifiMetricsProto.ConnectionEvent |
| .AUTH_FAILURE_WRONG_PSWD)); |
| verify(mWifiNetworkSuggestionsManager).handleConnectionAttemptEnded( |
| eq(WifiMetrics.ConnectionEvent.FAILURE_AUTHENTICATION_FAILURE), |
| eq(mTestConfig), eq(null)); |
| verify(mWifiMetrics, never()) |
| .incrementNumBssidDifferentSelectionBetweenFrameworkAndFirmware(); |
| verifyConnectionEventTimeoutDoesNotOccur(); |
| |
| clearInvocations(mWifiDiagnostics, mWifiConfigManager, mWifiNetworkFactory, |
| mWifiNetworkSuggestionsManager); |
| |
| // Now trigger a disconnect event from supplicant, this should be ignored since the |
| // connection tracking should have already ended. |
| mCmi.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, |
| new DisconnectEventInfo(TEST_SSID, TEST_BSSID_STR, 0, false)); |
| mLooper.dispatchAll(); |
| |
| verifyNoMoreInteractions(mWifiDiagnostics, mWifiConfigManager, mWifiNetworkFactory, |
| mWifiNetworkSuggestionsManager); |
| } |
| |
| /** |
| * Verify that if a NETWORK_DISCONNECTION_EVENT is received in L3ConnectedState, then an |
| * abnormal disconnect is reported to WifiBlocklistMonitor. |
| */ |
| @Test |
| public void testAbnormalDisconnectNotifiesWifiBlocklistMonitor() throws Exception { |
| // trigger RSSI poll to update WifiInfo |
| mCmi.enableRssiPolling(true); |
| WifiLinkLayerStats llStats = new WifiLinkLayerStats(); |
| llStats.txmpdu_be = 1000; |
| llStats.rxmpdu_bk = 2000; |
| WifiSignalPollResults signalPollResults = new WifiSignalPollResults(); |
| signalPollResults.addEntry(0, TEST_RSSI, 65, 54, sFreq); |
| when(mWifiNative.getSupportedFeatureSet(WIFI_IFACE_NAME)).thenReturn( |
| WifiManager.WIFI_FEATURE_LINK_LAYER_STATS); |
| when(mWifiNative.getWifiLinkLayerStats(any())).thenReturn(llStats); |
| when(mWifiNative.signalPoll(any())).thenReturn(signalPollResults); |
| |
| connect(); |
| mLooper.dispatchAll(); |
| DisconnectEventInfo disconnectEventInfo = |
| new DisconnectEventInfo(TEST_SSID, TEST_BSSID_STR, 0, false); |
| mCmi.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, disconnectEventInfo); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiBlocklistMonitor).handleBssidConnectionFailure(eq(TEST_BSSID_STR), |
| eq(mConnectedNetwork), eq(WifiBlocklistMonitor.REASON_ABNORMAL_DISCONNECT), |
| anyInt()); |
| } |
| |
| @Test |
| public void testAbnormalDisconnectIpReachabilityLostNotifiesWifiBlocklistMonitor() |
| throws Exception { |
| // trigger RSSI poll to update WifiInfo |
| mCmi.enableRssiPolling(true); |
| WifiLinkLayerStats llStats = new WifiLinkLayerStats(); |
| llStats.txmpdu_be = 1000; |
| llStats.rxmpdu_bk = 2000; |
| WifiSignalPollResults signalPollResults = new WifiSignalPollResults(); |
| signalPollResults.addEntry(0, TEST_RSSI, 65, 54, sFreq); |
| when(mWifiNative.getSupportedFeatureSet(WIFI_IFACE_NAME)).thenReturn( |
| WifiManager.WIFI_FEATURE_LINK_LAYER_STATS); |
| when(mWifiNative.getWifiLinkLayerStats(any())).thenReturn(llStats); |
| when(mWifiNative.signalPoll(any())).thenReturn(signalPollResults); |
| |
| // Connect, trigger CMD_IP_REACHABILITY_LOST and verifiy REASON_ABNORMAL_DISCONNECT is |
| // sent to mWifiBlocklistMonitor |
| connect(); |
| mLooper.dispatchAll(); |
| DisconnectEventInfo disconnectEventInfo = |
| new DisconnectEventInfo(TEST_SSID, TEST_BSSID_STR, 0, false); |
| mIpClientCallback.onReachabilityLost("CMD_IP_REACHABILITY_LOST"); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiBlocklistMonitor).handleBssidConnectionFailure(eq(TEST_BSSID_STR), |
| eq(mConnectedNetwork), eq(WifiBlocklistMonitor.REASON_ABNORMAL_DISCONNECT), |
| anyInt()); |
| } |
| |
| /** |
| * Verify that ClientModeImpl notifies WifiBlocklistMonitor correctly when the RSSI is |
| * too low. |
| */ |
| @Test |
| public void testNotifiesWifiBlocklistMonitorLowRssi() 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, |
| new AssocRejectEventInfo(TEST_SSID, TEST_BSSID_STR, 0, true)); |
| when(mWifiConfigManager.findScanRssi(eq(FRAMEWORK_NETWORK_ID), anyInt())) |
| .thenReturn(testLowRssi); |
| when(mWifiConfigManager.getScanDetailCacheForNetwork(FRAMEWORK_NETWORK_ID)) |
| .thenReturn(mScanDetailCache); |
| when(mScanDetailCache.getScanDetail(TEST_BSSID_STR)).thenReturn( |
| getGoogleGuestScanDetail(testLowRssi, TEST_BSSID_STR, sFreq)); |
| when(mScanDetailCache.getScanResult(TEST_BSSID_STR)).thenReturn( |
| getGoogleGuestScanDetail(testLowRssi, TEST_BSSID_STR, sFreq).getScanResult()); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiBlocklistMonitor).handleBssidConnectionFailure(TEST_BSSID_STR, mTestConfig, |
| WifiBlocklistMonitor.REASON_ASSOCIATION_TIMEOUT, testLowRssi); |
| } |
| |
| /** |
| * Verify that the recent failure association status is updated properly when |
| * ASSOC_REJECTED_TEMPORARILY occurs. |
| */ |
| @Test |
| public void testAssocRejectedTemporarilyUpdatesRecentAssociationFailureStatus() |
| throws Exception { |
| initializeAndAddNetworkAndVerifySuccess(); |
| mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, 0, 0, TEST_BSSID_STR); |
| mCmi.sendMessage(WifiMonitor.ASSOCIATION_REJECTION_EVENT, |
| new AssocRejectEventInfo(TEST_SSID, TEST_BSSID_STR, |
| ISupplicantStaIfaceCallback.StatusCode.ASSOC_REJECTED_TEMPORARILY, |
| false)); |
| mLooper.dispatchAll(); |
| verify(mWifiConfigManager).setRecentFailureAssociationStatus(anyInt(), |
| eq(WifiConfiguration.RECENT_FAILURE_REFUSED_TEMPORARILY)); |
| } |
| |
| /** |
| * Verify that WifiScoreCard and WifiBlocklistMonitor are notified properly when |
| * disconnection occurs in middle of connection states. |
| */ |
| @Test |
| public void testDisconnectConnecting() throws Exception { |
| initializeAndAddNetworkAndVerifySuccess(); |
| mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, 0, 0, TEST_BSSID_STR); |
| mCmi.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, |
| new DisconnectEventInfo(TEST_SSID, TEST_BSSID_STR, |
| ISupplicantStaIfaceCallback.ReasonCode.FOURWAY_HANDSHAKE_TIMEOUT, |
| false)); |
| mLooper.dispatchAll(); |
| verify(mWifiScoreCard).noteConnectionFailure(any(), anyInt(), anyString(), anyInt()); |
| verify(mWifiScoreCard).resetConnectionState(WIFI_IFACE_NAME); |
| // Verify that the WifiBlocklistMonitor is notified of a non-locally generated disconnect |
| // that occurred mid connection attempt. |
| verify(mWifiBlocklistMonitor).handleBssidConnectionFailure(anyString(), eq(mTestConfig), |
| eq(WifiBlocklistMonitor.REASON_NONLOCAL_DISCONNECT_CONNECTING), anyInt()); |
| verify(mWifiConfigManager, never()).updateNetworkSelectionStatus(anyInt(), |
| eq(WifiConfiguration.NetworkSelectionStatus.DISABLED_CONSECUTIVE_FAILURES)); |
| } |
| |
| private void triggerConnectionWithConsecutiveFailure() throws Exception { |
| when(mPerNetworkRecentStats.getCount(WifiScoreCard.CNT_CONSECUTIVE_CONNECTION_FAILURE)) |
| .thenReturn(WifiBlocklistMonitor.NUM_CONSECUTIVE_FAILURES_PER_NETWORK_EXP_BACKOFF); |
| mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, FRAMEWORK_NETWORK_ID, 0, TEST_BSSID_STR); |
| mCmi.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, |
| new DisconnectEventInfo(TEST_SSID, TEST_BSSID_STR, |
| ISupplicantStaIfaceCallback.ReasonCode.FOURWAY_HANDSHAKE_TIMEOUT, |
| false)); |
| mLooper.dispatchAll(); |
| } |
| |
| /** |
| * Verify that the WifiConfigManager is notified when a network experiences consecutive |
| * connection failures. |
| */ |
| @Test |
| public void testDisableNetworkConsecutiveFailures() throws Exception { |
| initializeAndAddNetworkAndVerifySuccess(); |
| triggerConnectionWithConsecutiveFailure(); |
| |
| verify(mWifiScoreCard).noteConnectionFailure(any(), anyInt(), anyString(), anyInt()); |
| verify(mWifiScoreCard).resetConnectionState(WIFI_IFACE_NAME); |
| verify(mWifiBlocklistMonitor).handleBssidConnectionFailure(anyString(), eq(mTestConfig), |
| eq(WifiBlocklistMonitor.REASON_NONLOCAL_DISCONNECT_CONNECTING), anyInt()); |
| verify(mWifiConfigManager).updateNetworkSelectionStatus(FRAMEWORK_NETWORK_ID, |
| WifiConfiguration.NetworkSelectionStatus.DISABLED_CONSECUTIVE_FAILURES); |
| } |
| |
| @Test |
| public void testDisableNetworkConsecutiveFailuresDoNotOverrideDisabledNetworks() |
| throws Exception { |
| initializeAndAddNetworkAndVerifySuccess(); |
| // Now mock the connecting network to be already disabled |
| mTestConfig.getNetworkSelectionStatus().setNetworkSelectionStatus( |
| NETWORK_SELECTION_PERMANENTLY_DISABLED); |
| triggerConnectionWithConsecutiveFailure(); |
| |
| verify(mWifiScoreCard).noteConnectionFailure(any(), anyInt(), anyString(), anyInt()); |
| verify(mWifiScoreCard).resetConnectionState(WIFI_IFACE_NAME); |
| verify(mWifiBlocklistMonitor).handleBssidConnectionFailure(anyString(), eq(mTestConfig), |
| eq(WifiBlocklistMonitor.REASON_NONLOCAL_DISCONNECT_CONNECTING), anyInt()); |
| |
| // should not disable due to DISABLED_CONSECUTIVE_FAILURES |
| verify(mWifiConfigManager, never()).updateNetworkSelectionStatus(FRAMEWORK_NETWORK_ID, |
| WifiConfiguration.NetworkSelectionStatus.DISABLED_CONSECUTIVE_FAILURES); |
| } |
| |
| /** |
| * Verify that a network that was successfully connected to before will get permanently disabled |
| * for wrong password when the number of wrong password failures exceed a threshold. |
| */ |
| @Test |
| public void testUpgradeMultipleWrongPasswordFailuresToPermanentWrongPassword() |
| throws Exception { |
| initializeAndAddNetworkAndVerifySuccess(); |
| startConnectSuccess(); |
| |
| // mock the target network to be something that had been successfully connected before |
| WifiConfiguration config = createTestNetwork(false); |
| config.getNetworkSelectionStatus().setHasEverConnected(true); |
| when(mWifiConfigManager.getConfiguredNetwork(anyInt())).thenReturn(config); |
| |
| // mock number of wrong password failures to be less than the threshold |
| when(mPerNetworkRecentStats.getCount(WifiScoreCard.CNT_CONSECUTIVE_WRONG_PASSWORD_FAILURE)) |
| .thenReturn(ClientModeImpl.THRESHOLD_TO_PERM_WRONG_PASSWORD - 1); |
| |
| // trigger the wrong password failure |
| mCmi.sendMessage(WifiMonitor.AUTHENTICATION_FAILURE_EVENT, |
| new AuthenticationFailureEventInfo(TEST_SSID, MacAddress.fromString(TEST_BSSID_STR), |
| WifiManager.ERROR_AUTH_FAILURE_WRONG_PSWD, -1)); |
| DisconnectEventInfo disconnectEventInfo = |
| new DisconnectEventInfo(TEST_SSID, TEST_BSSID_STR, 0, false); |
| mCmi.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, disconnectEventInfo); |
| mLooper.dispatchAll(); |
| |
| // should not disable network permanently |
| verify(mWifiConfigManager, never()).updateNetworkSelectionStatus(FRAMEWORK_NETWORK_ID, |
| WifiConfiguration.NetworkSelectionStatus.DISABLED_BY_WRONG_PASSWORD); |
| |
| // Bump up the wrong password count to reach the threshold and verify the network is |
| // disabled permanently. |
| when(mPerNetworkRecentStats.getCount(WifiScoreCard.CNT_CONSECUTIVE_WRONG_PASSWORD_FAILURE)) |
| .thenReturn(ClientModeImpl.THRESHOLD_TO_PERM_WRONG_PASSWORD); |
| |
| startConnectSuccess(); |
| // trigger the wrong password failure |
| mCmi.sendMessage(WifiMonitor.AUTHENTICATION_FAILURE_EVENT, |
| new AuthenticationFailureEventInfo(TEST_SSID, MacAddress.fromString(TEST_BSSID_STR), |
| WifiManager.ERROR_AUTH_FAILURE_WRONG_PSWD, -1)); |
| mCmi.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, disconnectEventInfo); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiConfigManager).updateNetworkSelectionStatus(FRAMEWORK_NETWORK_ID, |
| WifiConfiguration.NetworkSelectionStatus.DISABLED_BY_WRONG_PASSWORD); |
| } |
| |
| /** |
| * Verify that the recent failure association status is updated properly when |
| * DENIED_POOR_CHANNEL_CONDITIONS occurs. |
| */ |
| @Test |
| public void testAssocRejectedPoorChannelConditionsUpdatesRecentAssociationFailureStatus() |
| throws Exception { |
| initializeAndAddNetworkAndVerifySuccess(); |
| mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, 0, 0, TEST_BSSID_STR); |
| mCmi.sendMessage(WifiMonitor.ASSOCIATION_REJECTION_EVENT, |
| new AssocRejectEventInfo(TEST_SSID, TEST_BSSID_STR, |
| ISupplicantStaIfaceCallback.StatusCode.DENIED_POOR_CHANNEL_CONDITIONS, |
| false)); |
| mLooper.dispatchAll(); |
| verify(mWifiConfigManager).setRecentFailureAssociationStatus(anyInt(), |
| eq(WifiConfiguration.RECENT_FAILURE_POOR_CHANNEL_CONDITIONS)); |
| } |
| |
| /** |
| * Verify that the recent failure association status is updated properly when a disconnection |
| * with reason code DISASSOC_AP_BUSY occurs. |
| */ |
| @Test |
| public void testNetworkDisconnectionApBusyUpdatesRecentAssociationFailureStatus() |
| throws Exception { |
| connect(); |
| // Disconnection with reason = DISASSOC_AP_BUSY |
| DisconnectEventInfo disconnectEventInfo = |
| new DisconnectEventInfo(TEST_SSID, TEST_BSSID_STR, |
| SupplicantStaIfaceHal.StaIfaceReasonCode.DISASSOC_AP_BUSY, false); |
| mCmi.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, disconnectEventInfo); |
| mLooper.dispatchAll(); |
| verify(mWifiConfigManager).setRecentFailureAssociationStatus(anyInt(), |
| eq(WifiConfiguration.RECENT_FAILURE_DISCONNECTION_AP_BUSY)); |
| verify(mWifiBlocklistMonitor).blockBssidForDurationMs(any(), any(), |
| anyLong(), eq(WifiBlocklistMonitor.REASON_AP_UNABLE_TO_HANDLE_NEW_STA), anyInt()); |
| } |
| |
| /** |
| * Verify that the recent failure association status is updated properly when a disconnection |
| * with reason code DISASSOC_AP_BUSY occurs. |
| */ |
| @Test |
| public void testMidConnectionDisconnectionApBusyUpdatesRecentAssociationFailureStatus() |
| throws Exception { |
| initializeAndAddNetworkAndVerifySuccess(); |
| startConnectSuccess(); |
| assertEquals("L2ConnectingState", getCurrentState().getName()); |
| |
| // Disconnection with reason = DISASSOC_AP_BUSY |
| DisconnectEventInfo disconnectEventInfo = |
| new DisconnectEventInfo(TEST_SSID, TEST_BSSID_STR, 5, false); |
| mCmi.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, disconnectEventInfo); |
| mLooper.dispatchAll(); |
| verify(mWifiConfigManager).setRecentFailureAssociationStatus(anyInt(), |
| eq(WifiConfiguration.RECENT_FAILURE_DISCONNECTION_AP_BUSY)); |
| } |
| |
| /** |
| * Verify that the recent failure association status is updated properly when |
| * ASSOCIATION_REJECTION_EVENT with OCE RSSI based association rejection attribute is received. |
| */ |
| @Test |
| public void testOceRssiBasedAssociationRejectionUpdatesRecentAssociationFailureStatus() |
| throws Exception { |
| assumeTrue(SdkLevel.isAtLeastS()); |
| initializeAndAddNetworkAndVerifySuccess(); |
| AssociationRejectionData assocRejectData = new AssociationRejectionData(); |
| assocRejectData.ssid = NativeUtil.decodeSsid(TEST_SSID); |
| assocRejectData.bssid = NativeUtil.macAddressToByteArray(TEST_BSSID_STR); |
| assocRejectData.statusCode = |
| ISupplicantStaIfaceCallback.StatusCode.DENIED_POOR_CHANNEL_CONDITIONS; |
| assocRejectData.isOceRssiBasedAssocRejectAttrPresent = true; |
| assocRejectData.oceRssiBasedAssocRejectData.retryDelayS = 10; |
| assocRejectData.oceRssiBasedAssocRejectData.deltaRssi = 20; |
| mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, 0, 0, TEST_BSSID_STR); |
| mCmi.sendMessage(WifiMonitor.ASSOCIATION_REJECTION_EVENT, |
| new AssocRejectEventInfo(assocRejectData)); |
| mLooper.dispatchAll(); |
| verify(mWifiConfigManager).setRecentFailureAssociationStatus(anyInt(), |
| eq(WifiConfiguration.RECENT_FAILURE_OCE_RSSI_BASED_ASSOCIATION_REJECTION)); |
| } |
| |
| /** |
| * Verify that the recent failure association status is updated properly when |
| * ASSOCIATION_REJECTION_EVENT with MBO association disallowed attribute is received. |
| */ |
| @Test |
| public void testMboAssocDisallowedIndInAssocRejectUpdatesRecentAssociationFailureStatus() |
| throws Exception { |
| assumeTrue(SdkLevel.isAtLeastS()); |
| initializeAndAddNetworkAndVerifySuccess(); |
| AssociationRejectionData assocRejectData = new AssociationRejectionData(); |
| assocRejectData.ssid = NativeUtil.decodeSsid(TEST_SSID); |
| assocRejectData.bssid = NativeUtil.macAddressToByteArray(TEST_BSSID_STR); |
| assocRejectData.statusCode = |
| ISupplicantStaIfaceCallback.StatusCode.DENIED_POOR_CHANNEL_CONDITIONS; |
| assocRejectData.isMboAssocDisallowedReasonCodePresent = true; |
| assocRejectData.mboAssocDisallowedReason = MboAssocDisallowedReasonCode |
| .MAX_NUM_STA_ASSOCIATED; |
| mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, 0, 0, TEST_BSSID_STR); |
| mCmi.sendMessage(WifiMonitor.ASSOCIATION_REJECTION_EVENT, |
| new AssocRejectEventInfo(assocRejectData)); |
| mLooper.dispatchAll(); |
| verify(mWifiConfigManager).setRecentFailureAssociationStatus(anyInt(), |
| eq(WifiConfiguration.RECENT_FAILURE_MBO_ASSOC_DISALLOWED_MAX_NUM_STA_ASSOCIATED)); |
| } |
| |
| /** |
| * Verifies that the WifiBlocklistMonitor 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, TEST_BSSID_STR); |
| mCmi.sendMessage(WifiMonitor.ASSOCIATION_REJECTION_EVENT, |
| new AssocRejectEventInfo(TEST_SSID, TEST_BSSID_STR, |
| ISupplicantStaIfaceCallback.StatusCode.AP_UNABLE_TO_HANDLE_NEW_STA, false)); |
| mLooper.dispatchAll(); |
| verify(mWifiLastResortWatchdog, never()).noteConnectionFailureAndTriggerIfNeeded( |
| anyString(), anyString(), anyInt(), anyBoolean()); |
| verify(mWifiBlocklistMonitor).handleBssidConnectionFailure(eq(TEST_BSSID_STR), |
| eq(mTestConfig), eq(WifiBlocklistMonitor.REASON_AP_UNABLE_TO_HANDLE_NEW_STA), |
| anyInt()); |
| } |
| |
| /** |
| * Verifies that the WifiBlocklistMonitor is notified, but the WifiLastResortWatchdog is |
| * not notified of association rejections of type DENIED_INSUFFICIENT_BANDWIDTH. |
| * @throws Exception |
| */ |
| @Test |
| public void testAssociationRejectionWithReasonDeniedInsufficientBandwidth() |
| throws Exception { |
| initializeAndAddNetworkAndVerifySuccess(); |
| mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, 0, 0, TEST_BSSID_STR); |
| mCmi.sendMessage(WifiMonitor.ASSOCIATION_REJECTION_EVENT, |
| new AssocRejectEventInfo(TEST_SSID, TEST_BSSID_STR, ISupplicantStaIfaceCallback |
| .StatusCode.DENIED_INSUFFICIENT_BANDWIDTH, false)); |
| mLooper.dispatchAll(); |
| verify(mWifiLastResortWatchdog, never()).noteConnectionFailureAndTriggerIfNeeded( |
| anyString(), anyString(), anyInt(), anyBoolean()); |
| verify(mWifiBlocklistMonitor).handleBssidConnectionFailure(eq(TEST_BSSID_STR), |
| eq(mTestConfig), eq(WifiBlocklistMonitor.REASON_AP_UNABLE_TO_HANDLE_NEW_STA), |
| anyInt()); |
| } |
| |
| /** |
| * Verifies that WifiLastResortWatchdog and WifiBlocklistMonitor 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, TEST_BSSID_STR); |
| mCmi.sendMessage(WifiMonitor.ASSOCIATION_REJECTION_EVENT, |
| new AssocRejectEventInfo(TEST_SSID, TEST_BSSID_STR, 0, false)); |
| mLooper.dispatchAll(); |
| verify(mWifiLastResortWatchdog).noteConnectionFailureAndTriggerIfNeeded( |
| anyString(), anyString(), anyInt(), anyBoolean()); |
| verify(mWifiBlocklistMonitor).handleBssidConnectionFailure(eq(TEST_BSSID_STR), |
| eq(mTestConfig), eq(WifiBlocklistMonitor.REASON_ASSOCIATION_REJECTION), anyInt()); |
| verify(mWifiMetrics).incrementNumOfCarrierWifiConnectionNonAuthFailure(); |
| } |
| |
| private void testAssociationRejectionForRole(boolean isSecondary) throws Exception { |
| if (isSecondary) { |
| when(mClientModeManager.getRole()).thenReturn(ROLE_CLIENT_SECONDARY_LONG_LIVED); |
| when(mClientModeManager.isSecondaryInternet()).thenReturn(true); |
| } |
| when(mPerNetworkRecentStats.getCount(WifiScoreCard.CNT_CONSECUTIVE_CONNECTION_FAILURE)) |
| .thenReturn(WifiBlocklistMonitor.NUM_CONSECUTIVE_FAILURES_PER_NETWORK_EXP_BACKOFF); |
| initializeAndAddNetworkAndVerifySuccess(); |
| mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, 0, 0, TEST_BSSID_STR); |
| mCmi.sendMessage(WifiMonitor.ASSOCIATION_REJECTION_EVENT, |
| new AssocRejectEventInfo(TEST_SSID, TEST_BSSID_STR, |
| ISupplicantStaIfaceCallback.StatusCode.SUPPORTED_CHANNEL_NOT_VALID, false)); |
| mLooper.dispatchAll(); |
| verify(mWifiBlocklistMonitor).handleBssidConnectionFailure(eq(TEST_BSSID_STR), |
| eq(mTestConfig), eq(WifiBlocklistMonitor.REASON_ASSOCIATION_REJECTION), |
| anyInt()); |
| if (isSecondary) { |
| verify(mWifiConfigManager, never()).updateNetworkSelectionStatus(FRAMEWORK_NETWORK_ID, |
| WifiConfiguration.NetworkSelectionStatus.DISABLED_ASSOCIATION_REJECTION); |
| verify(mWifiConfigManager, never()).updateNetworkSelectionStatus(FRAMEWORK_NETWORK_ID, |
| WifiConfiguration.NetworkSelectionStatus.DISABLED_CONSECUTIVE_FAILURES); |
| verify(mWifiLastResortWatchdog, never()).noteConnectionFailureAndTriggerIfNeeded( |
| anyString(), anyString(), anyInt(), anyBoolean()); |
| } |
| else { |
| verify(mWifiConfigManager).updateNetworkSelectionStatus(FRAMEWORK_NETWORK_ID, |
| WifiConfiguration.NetworkSelectionStatus.DISABLED_ASSOCIATION_REJECTION); |
| verify(mWifiConfigManager).updateNetworkSelectionStatus(FRAMEWORK_NETWORK_ID, |
| WifiConfiguration.NetworkSelectionStatus.DISABLED_CONSECUTIVE_FAILURES); |
| verify(mWifiLastResortWatchdog).noteConnectionFailureAndTriggerIfNeeded( |
| anyString(), anyString(), anyInt(), anyBoolean()); |
| } |
| } |
| |
| @Test |
| public void testAssociationRejectionPrimary() throws Exception { |
| testAssociationRejectionForRole(false); |
| } |
| |
| @Test |
| public void testAssociationRejectionSecondary() throws Exception { |
| testAssociationRejectionForRole(true); |
| } |
| |
| /** |
| * 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, TEST_BSSID_STR); |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(0, TEST_WIFI_SSID, TEST_BSSID_STR, sFreq, |
| SupplicantState.COMPLETED)); |
| mCmi.sendMessage(WifiMonitor.AUTHENTICATION_FAILURE_EVENT, |
| new AuthenticationFailureEventInfo(TEST_SSID, MacAddress.fromString(TEST_BSSID_STR), |
| WifiManager.ERROR_AUTH_FAILURE_WRONG_PSWD, -1)); |
| mLooper.dispatchAll(); |
| verify(mWifiLastResortWatchdog, never()).noteConnectionFailureAndTriggerIfNeeded( |
| anyString(), anyString(), anyInt(), anyBoolean()); |
| verify(mWifiBlocklistMonitor).handleBssidConnectionFailure(eq(TEST_BSSID_STR), |
| eq(mTestConfig), eq(WifiBlocklistMonitor.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, TEST_BSSID_STR); |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(0, TEST_WIFI_SSID, TEST_BSSID_STR, sFreq, |
| SupplicantState.COMPLETED)); |
| mCmi.sendMessage(WifiMonitor.AUTHENTICATION_FAILURE_EVENT, |
| new AuthenticationFailureEventInfo(TEST_SSID, MacAddress.fromString(TEST_BSSID_STR), |
| WifiManager.ERROR_AUTH_FAILURE_EAP_FAILURE, -1)); |
| mLooper.dispatchAll(); |
| verify(mWifiLastResortWatchdog, never()).noteConnectionFailureAndTriggerIfNeeded( |
| anyString(), anyString(), anyInt(), anyBoolean()); |
| verify(mWifiBlocklistMonitor).handleBssidConnectionFailure(eq(TEST_BSSID_STR), |
| eq(mTestConfig), eq(WifiBlocklistMonitor.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, TEST_BSSID_STR); |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(0, TEST_WIFI_SSID, TEST_BSSID_STR, sFreq, |
| SupplicantState.COMPLETED)); |
| mCmi.sendMessage(WifiMonitor.AUTHENTICATION_FAILURE_EVENT, |
| new AuthenticationFailureEventInfo(TEST_SSID, MacAddress.fromString(TEST_BSSID_STR), |
| WifiManager.ERROR_AUTH_FAILURE_TIMEOUT, -1)); |
| mLooper.dispatchAll(); |
| verify(mWifiLastResortWatchdog).noteConnectionFailureAndTriggerIfNeeded( |
| anyString(), anyString(), anyInt(), anyBoolean()); |
| verify(mWifiBlocklistMonitor).handleBssidConnectionFailure(eq(TEST_BSSID_STR), |
| eq(mTestConfig), eq(WifiBlocklistMonitor.REASON_AUTHENTICATION_FAILURE), anyInt()); |
| } |
| |
| /** |
| * Verify that WifiBlocklistMonitor 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, TEST_BSSID_STR); |
| verify(mWifiBlocklistMonitor, never()).updateFirmwareRoamingConfiguration( |
| Set.of(TEST_SSID)); |
| mLooper.dispatchAll(); |
| verify(mWifiBlocklistMonitor).updateFirmwareRoamingConfiguration(Set.of(TEST_SSID)); |
| // 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, TEST_BSSID_STR, sFreq) |
| .getScanResult()); |
| testDhcpFailure(); |
| verify(mWifiDiagnostics).reportConnectionEvent( |
| eq(WifiDiagnostics.CONNECTION_EVENT_FAILED), any()); |
| verify(mWifiConnectivityManager).handleConnectionAttemptEnded( |
| mClientModeManager, |
| WifiMetrics.ConnectionEvent.FAILURE_DHCP, |
| WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, TEST_BSSID_STR, |
| mTestConfig); |
| verify(mWifiNetworkFactory).handleConnectionAttemptEnded( |
| eq(WifiMetrics.ConnectionEvent.FAILURE_DHCP), any(WifiConfiguration.class), |
| eq(TEST_BSSID_STR), eq(WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN)); |
| verify(mWifiNetworkSuggestionsManager).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}. |
| */ |
| @Test |
| public void testReportConnectionEventIsCalledAfterSuccessfulConnection() throws Exception { |
| mConnectedNetwork.getNetworkSelectionStatus() |
| .setCandidate(getGoogleGuestScanDetail(TEST_RSSI, TEST_BSSID_STR1, sFreq) |
| .getScanResult()); |
| connect(); |
| verify(mWifiInjector).makeWifiNetworkAgent(any(), any(), any(), any(), |
| mWifiNetworkAgentCallbackCaptor.capture()); |
| mWifiNetworkAgentCallbackCaptor.getValue().onValidationStatus( |
| NetworkAgent.VALIDATION_STATUS_VALID, null /* captivePortalUrl */); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiDiagnostics).reportConnectionEvent( |
| eq(WifiDiagnostics.CONNECTION_EVENT_SUCCEEDED), any()); |
| verify(mWifiConnectivityManager).handleConnectionAttemptEnded( |
| mClientModeManager, |
| WifiMetrics.ConnectionEvent.FAILURE_NONE, |
| WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, TEST_BSSID_STR, |
| mConnectedNetwork); |
| verify(mWifiNetworkFactory).handleConnectionAttemptEnded( |
| eq(WifiMetrics.ConnectionEvent.FAILURE_NONE), eq(mConnectedNetwork), |
| eq(TEST_BSSID_STR), eq(WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN)); |
| verify(mWifiNetworkSuggestionsManager).handleConnectionAttemptEnded( |
| eq(WifiMetrics.ConnectionEvent.FAILURE_NONE), eq(mConnectedNetwork), |
| any(String.class)); |
| verify(mCmiMonitor).onInternetValidated(mClientModeManager); |
| // 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, TEST_BSSID_STR); |
| 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"); |
| } |
| |
| /** |
| * Verifies that Layer2 information is updated only when supplicant state change moves |
| * to COMPLETED and on Wi-Fi disconnection. ie when device connect, roam & disconnect. |
| */ |
| @Test |
| public void testLayer2InformationUpdate() throws Exception { |
| InOrder inOrder = inOrder(mIpClient); |
| when(mWifiScoreCard.getL2KeyAndGroupHint(any())).thenReturn( |
| Pair.create("Wad", "Gab")); |
| // Simulate connection |
| connect(); |
| |
| inOrder.verify(mIpClient).updateLayer2Information(any()); |
| inOrder.verify(mIpClient).startProvisioning(any()); |
| |
| // Simulate Roaming |
| when(mWifiScoreCard.getL2KeyAndGroupHint(any())).thenReturn( |
| Pair.create("aaa", "bbb")); |
| // Now send a network connection (indicating a roam) event |
| mCmi.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, |
| new NetworkConnectionEventInfo(0, TEST_WIFI_SSID, TEST_BSSID_STR1, false, null)); |
| mLooper.dispatchAll(); |
| |
| inOrder.verify(mIpClient).updateLayer2Information(any()); |
| |
| // Simulate disconnection |
| when(mWifiScoreCard.getL2KeyAndGroupHint(any())).thenReturn(new Pair<>(null, null)); |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(0, TEST_WIFI_SSID, TEST_BSSID_STR1, sFreq1, |
| SupplicantState.DISCONNECTED)); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiScoreCard, times(3)).getL2KeyAndGroupHint(any()); |
| verify(mIpClient, times(3)).updateLayer2Information(any()); |
| |
| |
| |
| } |
| |
| /** |
| * 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(WIFI_IFACE_NAME); |
| verify(mWifiScoreCard, never()).noteWifiDisabled(any()); |
| |
| // disabling while disconnected should note wifi disabled |
| mCmi.stop(); |
| mLooper.dispatchAll(); |
| verify(mWifiScoreCard, times(2)).resetConnectionState(WIFI_IFACE_NAME); |
| } |
| |
| /** |
| * 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()); |
| |
| // disabling while connected should note wifi disabled |
| mCmi.stop(); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiScoreCard).noteWifiDisabled(any()); |
| verify(mWifiScoreCard).resetConnectionState(WIFI_IFACE_NAME); |
| } |
| |
| /** |
| * Verify that IPClient instance is shutdown when wifi is disabled. |
| */ |
| @Test |
| public void verifyIpClientShutdownWhenDisabled() throws Exception { |
| mCmi.stop(); |
| mLooper.dispatchAll(); |
| verify(mIpClient).shutdown(); |
| } |
| |
| /** |
| * Verify that WifiInfo's MAC address is updated when the state machine receives |
| * NETWORK_CONNECTION_EVENT while in L3ConnectedState. |
| */ |
| @Test |
| public void verifyWifiInfoMacUpdatedWithNetworkConnectionWhileConnected() throws Exception { |
| connect(); |
| assertEquals("L3ConnectedState", getCurrentState().getName()); |
| assertEquals(TEST_LOCAL_MAC_ADDRESS.toString(), mWifiInfo.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, |
| new NetworkConnectionEventInfo(0, TEST_WIFI_SSID, TEST_BSSID_STR, false, null)); |
| mLooper.dispatchAll(); |
| assertEquals(TEST_GLOBAL_MAC_ADDRESS.toString(), mWifiInfo.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, mWifiInfo.getMacAddress()); |
| |
| setupAndStartConnectSequence(mConnectedNetwork); |
| when(mWifiNative.getMacAddress(WIFI_IFACE_NAME)) |
| .thenReturn(TEST_LOCAL_MAC_ADDRESS.toString()); |
| mCmi.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, |
| new NetworkConnectionEventInfo(0, TEST_WIFI_SSID, TEST_BSSID_STR, false, null)); |
| mLooper.dispatchAll(); |
| assertEquals(TEST_LOCAL_MAC_ADDRESS.toString(), mWifiInfo.getMacAddress()); |
| } |
| |
| @Test |
| public void internetValidationFailure_notUserSelected_expectTemporarilyDisabled() |
| 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; |
| WifiSignalPollResults signalPollResults = new WifiSignalPollResults(); |
| signalPollResults.addEntry(0, RSSI_THRESHOLD_BREACH_MIN, 65, 54, sFreq); |
| when(mWifiNative.getSupportedFeatureSet(WIFI_IFACE_NAME)).thenReturn( |
| WifiManager.WIFI_FEATURE_LINK_LAYER_STATS); |
| when(mWifiNative.getWifiLinkLayerStats(any())).thenReturn(llStats); |
| when(mWifiNative.signalPoll(any())).thenReturn(signalPollResults); |
| |
| connect(); |
| verify(mWifiInjector).makeWifiNetworkAgent(any(), any(), any(), any(), |
| mWifiNetworkAgentCallbackCaptor.capture()); |
| |
| WifiConfiguration currentNetwork = new WifiConfiguration(); |
| currentNetwork.networkId = FRAMEWORK_NETWORK_ID; |
| currentNetwork.SSID = DEFAULT_TEST_SSID; |
| currentNetwork.noInternetAccessExpected = false; |
| currentNetwork.numNoInternetAccessReports = 1; |
| currentNetwork.getNetworkSelectionStatus().setHasEverValidatedInternetAccess(true); |
| |
| // not user selected |
| when(mWifiConfigManager.getConfiguredNetwork(FRAMEWORK_NETWORK_ID)) |
| .thenReturn(currentNetwork); |
| when(mWifiConfigManager.getConfiguredNetworkWithoutMasking(FRAMEWORK_NETWORK_ID)) |
| .thenReturn(currentNetwork); |
| |
| when(mWifiConfigManager.getLastSelectedNetwork()).thenReturn(FRAMEWORK_NETWORK_ID + 1); |
| |
| // internet validation failure |
| mWifiNetworkAgentCallbackCaptor.getValue().onValidationStatus( |
| NetworkAgent.VALIDATION_STATUS_NOT_VALID, null /* captivePortalUr; */); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiConfigManager) |
| .incrementNetworkNoInternetAccessReports(FRAMEWORK_NETWORK_ID); |
| // expect temporarily disabled |
| verify(mWifiConfigManager).updateNetworkSelectionStatus( |
| FRAMEWORK_NETWORK_ID, DISABLED_NO_INTERNET_TEMPORARY); |
| verify(mWifiBlocklistMonitor).handleBssidConnectionFailure(TEST_BSSID_STR, |
| currentNetwork, WifiBlocklistMonitor.REASON_NETWORK_VALIDATION_FAILURE, |
| RSSI_THRESHOLD_BREACH_MIN); |
| verify(mWifiScoreCard).noteValidationFailure(any()); |
| } |
| |
| @Test |
| public void testMbbInternetValidationErrorExpectDisconnect() throws Exception { |
| connect(); |
| verify(mWifiInjector).makeWifiNetworkAgent(any(), any(), any(), any(), |
| mWifiNetworkAgentCallbackCaptor.capture()); |
| |
| // Make Before Break CMM |
| when(mClientModeManager.getRole()).thenReturn(ROLE_CLIENT_SECONDARY_TRANSIENT); |
| when(mActiveModeWarden.getClientModeManagerInRole( |
| ROLE_CLIENT_SECONDARY_TRANSIENT)).thenReturn(mock(ConcreteClientModeManager.class)); |
| |
| // internet validation failure without detecting captive portal |
| mWifiNetworkAgentCallbackCaptor.getValue().onValidationStatus( |
| NetworkAgent.VALIDATION_STATUS_NOT_VALID, null /* captivePortalUr; */); |
| mLooper.dispatchAll(); |
| verify(mCmiMonitor).onInternetValidationFailed(mClientModeManager, false); |
| } |
| |
| @Test |
| public void testMbbNetworkUnwantedExpectDisconnect() throws Exception { |
| connect(); |
| verify(mWifiInjector).makeWifiNetworkAgent(any(), any(), any(), any(), |
| mWifiNetworkAgentCallbackCaptor.capture()); |
| |
| // Make Before Break CMM |
| when(mClientModeManager.getRole()).thenReturn(ROLE_CLIENT_SECONDARY_TRANSIENT); |
| when(mActiveModeWarden.getClientModeManagerInRole( |
| ROLE_CLIENT_SECONDARY_TRANSIENT)).thenReturn(mock(ConcreteClientModeManager.class)); |
| |
| // internet was lost and network got unwanted. |
| mWifiNetworkAgentCallbackCaptor.getValue().onNetworkUnwanted(); |
| mLooper.dispatchAll(); |
| // expect disconnection |
| verify(mWifiNative).disconnect(WIFI_IFACE_NAME); |
| } |
| |
| @Test |
| public void testMbbInternetValidationErrorCaptivePortalNoDisconnect() throws Exception { |
| connect(); |
| verify(mWifiInjector).makeWifiNetworkAgent(any(), any(), any(), any(), |
| mWifiNetworkAgentCallbackCaptor.capture()); |
| |
| // Make Before Break CMM |
| when(mClientModeManager.getRole()).thenReturn(ROLE_CLIENT_SECONDARY_TRANSIENT); |
| |
| // mock internet validation failure with captive portal detected |
| when(mMockUri.toString()).thenReturn("TEST_URI"); |
| mWifiNetworkAgentCallbackCaptor.getValue().onValidationStatus( |
| NetworkAgent.VALIDATION_STATUS_NOT_VALID, mMockUri); |
| mLooper.dispatchAll(); |
| |
| // expect no disconnection |
| verify(mWifiNative, never()).disconnect(WIFI_IFACE_NAME); |
| } |
| |
| @Test |
| public void testCaptivePortalDetectedNotifiesCmiMonitor() throws Exception { |
| connect(); |
| verify(mWifiInjector).makeWifiNetworkAgent(any(), any(), any(), any(), |
| mWifiNetworkAgentCallbackCaptor.capture()); |
| |
| // captive portal detected |
| when(mMockUri.toString()).thenReturn("TEST_URI"); |
| mWifiNetworkAgentCallbackCaptor.getValue().onValidationStatus( |
| NetworkAgent.VALIDATION_STATUS_NOT_VALID, mMockUri); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiConfigManager).noteCaptivePortalDetected(anyInt()); |
| verify(mCmiMonitor).onCaptivePortalDetected(mClientModeManager); |
| } |
| |
| @Test |
| public void testInternetValidationFailureUserSelectedRecently_ExpectDisabled() |
| throws Exception { |
| testInternetValidationUserSelectedRecently(false); |
| // expect disabled because the network has never had internet validation passed |
| verify(mWifiConfigManager).updateNetworkSelectionStatus( |
| FRAMEWORK_NETWORK_ID, DISABLED_NO_INTERNET_PERMANENT); |
| } |
| |
| @Test |
| public void testInternetValidationFailureUserSelectedRecently_ExpectNotDisabled() |
| throws Exception { |
| testInternetValidationUserSelectedRecently(true); |
| // expect not disabled |
| verify(mWifiConfigManager, never()).updateNetworkSelectionStatus( |
| FRAMEWORK_NETWORK_ID, DISABLED_NO_INTERNET_PERMANENT); |
| verify(mWifiConfigManager, never()).updateNetworkSelectionStatus( |
| FRAMEWORK_NETWORK_ID, DISABLED_NO_INTERNET_TEMPORARY); |
| } |
| |
| private void testInternetValidationUserSelectedRecently(boolean hasEverValidatedInternetAccess) |
| throws Exception { |
| connect(); |
| verify(mWifiInjector).makeWifiNetworkAgent(any(), any(), any(), any(), |
| mWifiNetworkAgentCallbackCaptor.capture()); |
| |
| WifiConfiguration currentNetwork = new WifiConfiguration(); |
| currentNetwork.networkId = FRAMEWORK_NETWORK_ID; |
| currentNetwork.noInternetAccessExpected = false; |
| currentNetwork.numNoInternetAccessReports = 1; |
| currentNetwork.getNetworkSelectionStatus().setHasEverValidatedInternetAccess( |
| hasEverValidatedInternetAccess); |
| |
| // user last picked this network |
| when(mWifiConfigManager.getConfiguredNetwork(FRAMEWORK_NETWORK_ID)) |
| .thenReturn(currentNetwork); |
| when(mWifiConfigManager.getLastSelectedNetwork()).thenReturn(FRAMEWORK_NETWORK_ID); |
| |
| // user recently picked this network |
| when(mWifiConfigManager.getLastSelectedTimeStamp()).thenReturn(1234L); |
| when(mClock.getElapsedSinceBootMillis()).thenReturn(1235L); |
| |
| // internet validation failure |
| mWifiNetworkAgentCallbackCaptor.getValue().onValidationStatus( |
| NetworkAgent.VALIDATION_STATUS_NOT_VALID, null /* captivePortalUrl */); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiConfigManager) |
| .incrementNetworkNoInternetAccessReports(FRAMEWORK_NETWORK_ID); |
| } |
| |
| @Test |
| public void testInternetValidationFailureUserSelectedTooLongAgoExpectTemporarilyDisabled() |
| throws Exception { |
| connect(); |
| verify(mWifiInjector).makeWifiNetworkAgent(any(), any(), any(), any(), |
| mWifiNetworkAgentCallbackCaptor.capture()); |
| |
| WifiConfiguration currentNetwork = new WifiConfiguration(); |
| currentNetwork.networkId = FRAMEWORK_NETWORK_ID; |
| currentNetwork.noInternetAccessExpected = false; |
| currentNetwork.numNoInternetAccessReports = 1; |
| currentNetwork.getNetworkSelectionStatus().setHasEverValidatedInternetAccess(true); |
| |
| // user last picked this network |
| when(mWifiConfigManager.getConfiguredNetwork(FRAMEWORK_NETWORK_ID)) |
| .thenReturn(currentNetwork); |
| when(mWifiConfigManager.getLastSelectedNetwork()).thenReturn(FRAMEWORK_NETWORK_ID); |
| |
| // user picked this network a long time ago |
| when(mWifiConfigManager.getLastSelectedTimeStamp()).thenReturn(1234L); |
| when(mClock.getElapsedSinceBootMillis()) |
| .thenReturn(1235L + ClientModeImpl.LAST_SELECTED_NETWORK_EXPIRATION_AGE_MILLIS); |
| |
| // internet validation failure |
| mWifiNetworkAgentCallbackCaptor.getValue().onValidationStatus( |
| NetworkAgent.VALIDATION_STATUS_NOT_VALID, null /* captivePortalUrl */); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiConfigManager) |
| .incrementNetworkNoInternetAccessReports(FRAMEWORK_NETWORK_ID); |
| // expect temporarily disabled |
| verify(mWifiConfigManager).updateNetworkSelectionStatus( |
| FRAMEWORK_NETWORK_ID, DISABLED_NO_INTERNET_TEMPORARY); |
| } |
| |
| @Test |
| public void testNetworkInternetValidationFailureNoInternetExpected_ExpectNotDisabled() |
| throws Exception { |
| connect(); |
| verify(mWifiInjector).makeWifiNetworkAgent(any(), any(), any(), any(), |
| mWifiNetworkAgentCallbackCaptor.capture()); |
| |
| WifiConfiguration currentNetwork = new WifiConfiguration(); |
| currentNetwork.networkId = FRAMEWORK_NETWORK_ID; |
| // no internet expected |
| currentNetwork.noInternetAccessExpected = true; |
| currentNetwork.numNoInternetAccessReports = 1; |
| |
| // user didn't pick this network |
| when(mWifiConfigManager.getConfiguredNetwork(FRAMEWORK_NETWORK_ID)) |
| .thenReturn(currentNetwork); |
| when(mWifiConfigManager.getConfiguredNetworkWithoutMasking(FRAMEWORK_NETWORK_ID)) |
| .thenReturn(currentNetwork); |
| when(mWifiConfigManager.getLastSelectedNetwork()).thenReturn(FRAMEWORK_NETWORK_ID + 1); |
| |
| // internet validation failure |
| mWifiNetworkAgentCallbackCaptor.getValue().onValidationStatus( |
| NetworkAgent.VALIDATION_STATUS_NOT_VALID, null /* captivePortalUrl */); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiConfigManager) |
| .incrementNetworkNoInternetAccessReports(FRAMEWORK_NETWORK_ID); |
| // expect not disabled since no internet is expected on this network |
| verify(mWifiConfigManager, never()).updateNetworkSelectionStatus( |
| FRAMEWORK_NETWORK_ID, DISABLED_NO_INTERNET_TEMPORARY); |
| } |
| |
| /** |
| * Verify that we do not set the user connect choice after a successful connection if the |
| * connection is not made by the user. |
| */ |
| @Test |
| public void testNonSettingsConnectionNotSetUserConnectChoice() throws Exception { |
| when(mWifiPermissionsUtil.checkNetworkSettingsPermission(anyInt())).thenReturn(false); |
| connect(); |
| verify(mWifiBlocklistMonitor).handleBssidConnectionSuccess(TEST_BSSID_STR, TEST_SSID); |
| verify(mWifiConfigManager).updateNetworkAfterConnect(eq(FRAMEWORK_NETWORK_ID), |
| eq(false), eq(false), |
| anyInt()); |
| } |
| |
| /** |
| * Verify that we do not set the user connect choice after connecting to a newly added saved |
| * network. |
| */ |
| @Test |
| public void testNoSetUserConnectChoiceOnFirstConnection() throws Exception { |
| when(mWifiPermissionsUtil.checkNetworkSettingsPermission(anyInt())).thenReturn(true); |
| connect(); |
| verify(mWifiBlocklistMonitor).handleBssidConnectionSuccess(TEST_BSSID_STR, TEST_SSID); |
| verify(mWifiConfigManager).updateNetworkAfterConnect(eq(FRAMEWORK_NETWORK_ID), |
| eq(true), eq(false), |
| anyInt()); |
| } |
| |
| /** |
| * Verify that on the second successful connection to a saved network we set the user connect |
| * choice. |
| */ |
| @Test |
| public void testConnectionSetUserConnectChoiceOnSecondConnection() throws Exception { |
| when(mWifiPermissionsUtil.checkNetworkSettingsPermission(anyInt())).thenReturn(true); |
| mTestNetworkParams.hasEverConnected = true; |
| connect(); |
| verify(mWifiBlocklistMonitor).handleBssidConnectionSuccess(TEST_BSSID_STR, TEST_SSID); |
| verify(mWifiConfigManager).updateNetworkAfterConnect(eq(FRAMEWORK_NETWORK_ID), |
| eq(true), eq(true), |
| anyInt()); |
| } |
| |
| /** |
| * Verify that on the first successful connection to an ephemeral network we set the user |
| * connect choice. |
| */ |
| @Test |
| public void testConnectionSetUserConnectChoiceOnEphemeralConfig() throws Exception { |
| when(mWifiPermissionsUtil.checkNetworkSettingsPermission(anyInt())).thenReturn(true); |
| mConnectedNetwork.ephemeral = true; |
| connect(); |
| verify(mWifiBlocklistMonitor).handleBssidConnectionSuccess(TEST_BSSID_STR, TEST_SSID); |
| verify(mWifiConfigManager).updateNetworkAfterConnect(eq(FRAMEWORK_NETWORK_ID), |
| eq(true), eq(true), |
| anyInt()); |
| } |
| |
| /** |
| * Verify that we enable the network when we detect validated internet access. |
| */ |
| @Test |
| public void verifyNetworkSelectionEnableOnInternetValidation() throws Exception { |
| // Simulate the first connection. |
| connect(); |
| verify(mWifiBlocklistMonitor).handleBssidConnectionSuccess(TEST_BSSID_STR, TEST_SSID); |
| verify(mWifiBlocklistMonitor).handleDhcpProvisioningSuccess(TEST_BSSID_STR, TEST_SSID); |
| verify(mWifiBlocklistMonitor, never()).handleNetworkValidationSuccess( |
| TEST_BSSID_STR, TEST_SSID); |
| |
| verify(mWifiInjector).makeWifiNetworkAgent(any(), any(), any(), any(), |
| mWifiNetworkAgentCallbackCaptor.capture()); |
| |
| when(mWifiConfigManager.getLastSelectedNetwork()).thenReturn(FRAMEWORK_NETWORK_ID + 1); |
| |
| mWifiNetworkAgentCallbackCaptor.getValue().onValidationStatus( |
| NetworkAgent.VALIDATION_STATUS_VALID, null /* captivePortalUrl */); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiConfigManager) |
| .setNetworkValidatedInternetAccess(FRAMEWORK_NETWORK_ID, true); |
| verify(mWifiConfigManager).updateNetworkSelectionStatus( |
| FRAMEWORK_NETWORK_ID, DISABLED_NONE); |
| verify(mWifiScoreCard).noteValidationSuccess(any()); |
| verify(mWifiBlocklistMonitor).handleNetworkValidationSuccess(TEST_BSSID_STR, TEST_SSID); |
| } |
| |
| /** |
| * Verify that the logic clears the terms and conditions URL after we got a notification that |
| * the network was validated (i.e. the user accepted and internt access is available). |
| */ |
| @Test |
| public void testTermsAndConditionsClearUrlAfterNetworkValidation() throws Exception { |
| assumeTrue(SdkLevel.isAtLeastS()); |
| InOrder inOrder = inOrder(mWifiNetworkAgent); |
| |
| // Simulate the first connection. |
| mConnectedNetwork = spy(WifiConfigurationTestUtil.createPasspointNetwork()); |
| WnmData wnmData = WnmData.createTermsAndConditionsAccetanceRequiredEvent(TEST_BSSID, |
| TEST_TERMS_AND_CONDITIONS_URL); |
| when(mPasspointManager.handleTermsAndConditionsEvent(eq(wnmData), |
| any(WifiConfiguration.class))).thenReturn(new URL(TEST_TERMS_AND_CONDITIONS_URL)); |
| connect(wnmData); |
| // Verify that link properties contains the T&C URL and captive is set to true |
| inOrder.verify(mWifiNetworkAgent) |
| .sendLinkProperties(argThat(linkProperties -> TEST_TERMS_AND_CONDITIONS_URL.equals( |
| linkProperties.getCaptivePortalData().getUserPortalUrl().toString()) |
| && linkProperties.getCaptivePortalData().isCaptive())); |
| verify(mWifiBlocklistMonitor).handleBssidConnectionSuccess(TEST_BSSID_STR, TEST_SSID); |
| verify(mWifiBlocklistMonitor).handleDhcpProvisioningSuccess(TEST_BSSID_STR, TEST_SSID); |
| verify(mWifiBlocklistMonitor, never()) |
| .handleNetworkValidationSuccess(TEST_BSSID_STR, TEST_SSID); |
| verify(mWifiInjector).makeWifiNetworkAgent(any(), any(), any(), any(), |
| mWifiNetworkAgentCallbackCaptor.capture()); |
| |
| when(mWifiConfigManager.getLastSelectedNetwork()).thenReturn(FRAMEWORK_NETWORK_ID + 1); |
| mWifiNetworkAgentCallbackCaptor.getValue().onValidationStatus( |
| NetworkAgent.VALIDATION_STATUS_VALID, null /* captivePortalUrl */); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiConfigManager) |
| .setNetworkValidatedInternetAccess(FRAMEWORK_NETWORK_ID, true); |
| verify(mWifiConfigManager).updateNetworkSelectionStatus( |
| FRAMEWORK_NETWORK_ID, DISABLED_NONE); |
| verify(mWifiScoreCard).noteValidationSuccess(any()); |
| verify(mWifiBlocklistMonitor).handleNetworkValidationSuccess(TEST_BSSID_STR, TEST_SSID); |
| |
| // Now that the network has been validated, link properties must not have a T&C URL anymore |
| // and captive is set to false |
| inOrder.verify(mWifiNetworkAgent) |
| .sendLinkProperties(argThat(linkProperties -> |
| linkProperties.getCaptivePortalData().getUserPortalUrl() == null |
| && !linkProperties.getCaptivePortalData().isCaptive())); |
| } |
| |
| private void connectWithValidInitRssi(int initRssiDbm) throws Exception { |
| triggerConnect(); |
| mWifiInfo.setRssi(initRssiDbm); |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(0, TEST_WIFI_SSID, TEST_BSSID_STR, 0, |
| SupplicantState.ASSOCIATED)); |
| mLooper.dispatchAll(); |
| |
| WifiSsid wifiSsid = WifiSsid.fromBytes( |
| NativeUtil.byteArrayFromArrayList(NativeUtil.decodeSsid(mConnectedNetwork.SSID))); |
| mCmi.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, |
| new NetworkConnectionEventInfo(0, wifiSsid, TEST_BSSID_STR, false, null)); |
| mLooper.dispatchAll(); |
| |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(0, TEST_WIFI_SSID, TEST_BSSID_STR, 0, |
| SupplicantState.COMPLETED)); |
| mLooper.dispatchAll(); |
| |
| assertEquals("L3ProvisioningState", 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 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 { |
| mWifiInfo.setFrequency(5825); |
| when(mPerNetwork.getTxLinkBandwidthKbps()).thenReturn(40_000); |
| when(mPerNetwork.getRxLinkBandwidthKbps()).thenReturn(50_000); |
| when(mWifiNetworkFactory.getSpecificNetworkRequestUids(any(), any())) |
| .thenReturn(Collections.emptySet()); |
| when(mWifiNetworkFactory.getSpecificNetworkRequestUidAndPackageName(any(), any())) |
| .thenReturn(Pair.create(Process.INVALID_UID, "")); |
| // Simulate the first connection. |
| connectWithValidInitRssi(-42); |
| |
| ArgumentCaptor<NetworkCapabilities> networkCapabilitiesCaptor = |
| ArgumentCaptor.forClass(NetworkCapabilities.class); |
| verify(mWifiInjector).makeWifiNetworkAgent( |
| networkCapabilitiesCaptor.capture(), any(), any(), any(), any()); |
| |
| NetworkCapabilities networkCapabilities = networkCapabilitiesCaptor.getValue(); |
| assertNotNull(networkCapabilities); |
| |
| // Should have internet capability. |
| assertTrue(networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)); |
| |
| assertEquals(mConnectedNetwork.creatorUid, networkCapabilities.getOwnerUid()); |
| if (SdkLevel.isAtLeastT()) { |
| assertEquals(Set.of(mConnectedNetwork.creatorUid), |
| networkCapabilities.getAllowedUids()); |
| } |
| assertArrayEquals( |
| new int[] {mConnectedNetwork.creatorUid}, |
| networkCapabilities.getAdministratorUids()); |
| |
| // Should set bandwidth correctly |
| assertEquals(-42, mWifiInfo.getRssi()); |
| assertEquals(40_000, networkCapabilities.getLinkUpstreamBandwidthKbps()); |
| assertEquals(50_000, networkCapabilities.getLinkDownstreamBandwidthKbps()); |
| |
| // Should set band correctly. |
| // There is no accessor to get the band from the WifiNetworkAgentSpecifier, so match against |
| // a WifiNetworkSpecifier. |
| // TODO: should there be? |
| final NetworkSpecifier spec = networkCapabilities.getNetworkSpecifier(); |
| assertTrue(spec instanceof WifiNetworkAgentSpecifier); |
| final WifiNetworkAgentSpecifier wnas = (WifiNetworkAgentSpecifier) spec; |
| assertTrue(wnas.satisfiesNetworkSpecifier( |
| new WifiNetworkSpecifier.Builder().setBand(ScanResult.WIFI_BAND_5_GHZ).build())); |
| } |
| |
| /** |
| * 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 verifyNetworkCapabilitiesForSpecificRequestWithInternet() throws Exception { |
| mWifiInfo.setFrequency(2437); |
| when(mPerNetwork.getTxLinkBandwidthKbps()).thenReturn(30_000); |
| when(mPerNetwork.getRxLinkBandwidthKbps()).thenReturn(40_000); |
| when(mWifiNetworkFactory.getSpecificNetworkRequestUids(any(), any())) |
| .thenReturn(Set.of(TEST_UID)); |
| when(mWifiNetworkFactory.getSpecificNetworkRequestUidAndPackageName(any(), any())) |
| .thenReturn(Pair.create(TEST_UID, OP_PACKAGE_NAME)); |
| when(mWifiConnectivityManager.hasMultiInternetConnection()).thenReturn(true); |
| // Simulate the first connection. |
| connectWithValidInitRssi(-42); |
| ArgumentCaptor<NetworkCapabilities> networkCapabilitiesCaptor = |
| ArgumentCaptor.forClass(NetworkCapabilities.class); |
| |
| verify(mWifiInjector).makeWifiNetworkAgent( |
| networkCapabilitiesCaptor.capture(), any(), any(), any(), any()); |
| |
| 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; |
| |
| // createNetworkAgentSpecifier does not write the BSSID to the current wifi configuration. |
| WifiConfiguration expectedConfig = new WifiConfiguration( |
| mCmi.getConnectedWifiConfiguration()); |
| expectedConfig.BSSID = TEST_BSSID_STR; |
| WifiNetworkAgentSpecifier expectedWifiNetworkAgentSpecifier = |
| new WifiNetworkAgentSpecifier(expectedConfig, ScanResult.WIFI_BAND_24_GHZ, |
| true /* matchLocalOnlySpecifiers */); |
| assertEquals(expectedWifiNetworkAgentSpecifier, wifiNetworkAgentSpecifier); |
| if (SdkLevel.isAtLeastS()) { |
| assertEquals(Set.of(new Range<Integer>(TEST_UID, TEST_UID)), |
| networkCapabilities.getUids()); |
| } else { |
| assertEquals(TEST_UID, networkCapabilities.getRequestorUid()); |
| assertEquals(OP_PACKAGE_NAME, networkCapabilities.getRequestorPackageName()); |
| } |
| assertEquals(30_000, networkCapabilities.getLinkUpstreamBandwidthKbps()); |
| assertEquals(40_000, networkCapabilities.getLinkDownstreamBandwidthKbps()); |
| } |
| |
| /** |
| * Verify that we set the INTERNET capability in the network agent when connected |
| * as a result of the new network which indicate the internet capabilites should be set. |
| */ |
| @Test |
| public void verifyNetworkCapabilitiesForSpecificRequest() throws Exception { |
| mWifiInfo.setFrequency(2437); |
| when(mPerNetwork.getTxLinkBandwidthKbps()).thenReturn(30_000); |
| when(mPerNetwork.getRxLinkBandwidthKbps()).thenReturn(40_000); |
| when(mWifiNetworkFactory.getSpecificNetworkRequestUids(any(), any())) |
| .thenReturn(Set.of(TEST_UID)); |
| when(mWifiNetworkFactory.getSpecificNetworkRequestUidAndPackageName(any(), any())) |
| .thenReturn(Pair.create(TEST_UID, OP_PACKAGE_NAME)); |
| when(mWifiNetworkFactory.shouldHaveInternetCapabilities()).thenReturn(true); |
| when(mWifiConnectivityManager.hasMultiInternetConnection()).thenReturn(true); |
| // Simulate the first connection. |
| connectWithValidInitRssi(-42); |
| ArgumentCaptor<NetworkCapabilities> networkCapabilitiesCaptor = |
| ArgumentCaptor.forClass(NetworkCapabilities.class); |
| |
| verify(mWifiInjector).makeWifiNetworkAgent( |
| networkCapabilitiesCaptor.capture(), any(), any(), any(), any()); |
| |
| NetworkCapabilities networkCapabilities = networkCapabilitiesCaptor.getValue(); |
| assertNotNull(networkCapabilities); |
| |
| // should not have internet capability. |
| assertTrue(networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)); |
| |
| NetworkSpecifier networkSpecifier = networkCapabilities.getNetworkSpecifier(); |
| assertTrue(networkSpecifier instanceof WifiNetworkAgentSpecifier); |
| WifiNetworkAgentSpecifier wifiNetworkAgentSpecifier = |
| (WifiNetworkAgentSpecifier) networkSpecifier; |
| |
| // createNetworkAgentSpecifier does not write the BSSID to the current wifi configuration. |
| WifiConfiguration expectedConfig = new WifiConfiguration( |
| mCmi.getConnectedWifiConfiguration()); |
| expectedConfig.BSSID = TEST_BSSID_STR; |
| WifiNetworkAgentSpecifier expectedWifiNetworkAgentSpecifier = |
| new WifiNetworkAgentSpecifier(expectedConfig, ScanResult.WIFI_BAND_24_GHZ, |
| true /* matchLocalOnlySpecifiers */); |
| assertEquals(expectedWifiNetworkAgentSpecifier, wifiNetworkAgentSpecifier); |
| if (SdkLevel.isAtLeastS()) { |
| assertEquals(Set.of(new Range<Integer>(TEST_UID, TEST_UID)), |
| networkCapabilities.getUids()); |
| } else { |
| assertEquals(TEST_UID, networkCapabilities.getRequestorUid()); |
| assertEquals(OP_PACKAGE_NAME, networkCapabilities.getRequestorPackageName()); |
| } |
| assertEquals(30_000, networkCapabilities.getLinkUpstreamBandwidthKbps()); |
| assertEquals(40_000, networkCapabilities.getLinkDownstreamBandwidthKbps()); |
| } |
| |
| /** |
| * Verifies that no RSSI change broadcast should be sent |
| */ |
| private void failOnRssiChangeBroadcast() throws Exception { |
| mCmi.enableVerboseLogging(true); |
| doAnswer(invocation -> { |
| final Intent intent = invocation.getArgument(0); |
| if (WifiManager.RSSI_CHANGED_ACTION.equals(intent.getAction())) { |
| fail("Should not send RSSI_CHANGED broadcast!"); |
| } |
| return null; |
| }).when(mContext).sendBroadcastAsUser(any(), any()); |
| |
| doAnswer(invocation -> { |
| final Intent intent = invocation.getArgument(0); |
| if (WifiManager.RSSI_CHANGED_ACTION.equals(intent.getAction())) { |
| fail("Should not send RSSI_CHANGED broadcast!"); |
| } |
| return null; |
| }).when(mContext).sendBroadcastAsUser(any(), any(), anyString()); |
| |
| doAnswer(invocation -> { |
| final Intent intent = invocation.getArgument(0); |
| if (WifiManager.RSSI_CHANGED_ACTION.equals(intent.getAction())) { |
| fail("Should not send RSSI_CHANGED broadcast!"); |
| } |
| return null; |
| }).when(mContext).sendBroadcastAsUser(any(), any(), anyString(), any()); |
| } |
| |
| /** |
| * Verifies that RSSI change broadcast is sent. |
| */ |
| private void assertRssiChangeBroadcastSent(int times) throws Exception { |
| ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class); |
| verify(mContext, times(times)).sendBroadcastAsUser(intentCaptor.capture(), |
| eq(UserHandle.ALL), eq(Manifest.permission.ACCESS_WIFI_STATE), any()); |
| if (times > 0) { |
| Intent intent = intentCaptor.getValue(); |
| assertNotNull(intent); |
| assertEquals(WifiManager.RSSI_CHANGED_ACTION, intent.getAction()); |
| } |
| } |
| |
| /** |
| * 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(); |
| |
| failOnRssiChangeBroadcast(); |
| when(mWifiNative.getSupportedFeatureSet(WIFI_IFACE_NAME)).thenReturn( |
| WifiManager.WIFI_FEATURE_LINK_LAYER_STATS); |
| 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(WIFI_IFACE_NAME, |
| mConnectionCapabilities, oldLLStats, newLLStats, mWifiInfo, TEST_TX_BYTES, |
| TEST_RX_BYTES); |
| verify(mWifiMetrics).incrementWifiLinkLayerUsageStats(WIFI_IFACE_NAME, 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(); |
| |
| failOnRssiChangeBroadcast(); |
| when(mWifiNative.getSupportedFeatureSet(WIFI_IFACE_NAME)).thenReturn( |
| WifiManager.WIFI_FEATURE_LINK_LAYER_STATS); |
| WifiLinkLayerStats stats = new WifiLinkLayerStats(); |
| when(mWifiNative.getWifiLinkLayerStats(any())).thenReturn(stats); |
| when(mWifiDataStall.checkDataStallAndThroughputSufficiency(any(), |
| any(), any(), any(), any(), anyLong(), anyLong())) |
| .thenReturn(WifiIsUnusableEvent.TYPE_UNKNOWN); |
| mCmi.sendMessage(ClientModeImpl.CMD_RSSI_POLL, 1); |
| mLooper.dispatchAll(); |
| verify(mWifiMetrics).updateWifiUsabilityStatsEntries(any(), any(), eq(stats)); |
| verify(mWifiMetrics, never()).addToWifiUsabilityStatsList(any(), |
| WifiUsabilityStats.LABEL_BAD, eq(anyInt()), eq(-1)); |
| |
| when(mWifiDataStall.checkDataStallAndThroughputSufficiency(any(), any(), any(), any(), |
| any(), anyLong(), anyLong())) |
| .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(), 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(WIFI_IFACE_NAME, |
| 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 { |
| // called once during setup() |
| verify(mWifiNative).setPowerSave(WIFI_IFACE_NAME, true); |
| |
| assertTrue(mCmi.setPowerSave(ClientMode.POWER_SAVE_CLIENT_WIFI_LOCK, true)); |
| assertTrue(mCmi.enablePowerSave()); |
| verify(mWifiNative, times(3)).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 { |
| assertTrue(mCmi.setPowerSave(ClientMode.POWER_SAVE_CLIENT_DHCP, false)); |
| verify(mWifiNative).setPowerSave(WIFI_IFACE_NAME, false); |
| } |
| |
| /** |
| * Verify that using setPowerSave with multiple clients (DHCP/WifiLock) operates correctly: |
| * - Disable power save if ANY client disables it |
| * - Enable power save only if ALL clients no longer disable it |
| */ |
| @Test |
| public void verifySetPowerSaveMultipleSources() { |
| InOrder inOrderWifiNative = inOrder(mWifiNative); |
| |
| // #1 Disable-> #2 Disable-> #2 Enable-> #1 Enable |
| assertTrue(mCmi.setPowerSave(ClientMode.POWER_SAVE_CLIENT_WIFI_LOCK, false)); |
| inOrderWifiNative.verify(mWifiNative).setPowerSave(WIFI_IFACE_NAME, false); |
| |
| assertTrue(mCmi.setPowerSave(ClientMode.POWER_SAVE_CLIENT_DHCP, false)); |
| inOrderWifiNative.verify(mWifiNative).setPowerSave(WIFI_IFACE_NAME, false); |
| |
| assertTrue(mCmi.setPowerSave(ClientMode.POWER_SAVE_CLIENT_DHCP, true)); |
| inOrderWifiNative.verify(mWifiNative).setPowerSave(WIFI_IFACE_NAME, false); |
| |
| assertTrue(mCmi.setPowerSave(ClientMode.POWER_SAVE_CLIENT_WIFI_LOCK, true)); |
| inOrderWifiNative.verify(mWifiNative).setPowerSave(WIFI_IFACE_NAME, true); |
| |
| // #1 Disable-> #2 Disable-> #1 Enable-> #2 Enable |
| assertTrue(mCmi.setPowerSave(ClientMode.POWER_SAVE_CLIENT_WIFI_LOCK, false)); |
| inOrderWifiNative.verify(mWifiNative).setPowerSave(WIFI_IFACE_NAME, false); |
| |
| assertTrue(mCmi.setPowerSave(ClientMode.POWER_SAVE_CLIENT_DHCP, false)); |
| inOrderWifiNative.verify(mWifiNative).setPowerSave(WIFI_IFACE_NAME, false); |
| |
| assertTrue(mCmi.setPowerSave(ClientMode.POWER_SAVE_CLIENT_WIFI_LOCK, true)); |
| inOrderWifiNative.verify(mWifiNative).setPowerSave(WIFI_IFACE_NAME, false); |
| |
| assertTrue(mCmi.setPowerSave(ClientMode.POWER_SAVE_CLIENT_DHCP, true)); |
| inOrderWifiNative.verify(mWifiNative).setPowerSave(WIFI_IFACE_NAME, true); |
| |
| // #1 Disable-> #2 Disable-> global enable |
| assertTrue(mCmi.setPowerSave(ClientMode.POWER_SAVE_CLIENT_WIFI_LOCK, false)); |
| inOrderWifiNative.verify(mWifiNative).setPowerSave(WIFI_IFACE_NAME, false); |
| |
| assertTrue(mCmi.setPowerSave(ClientMode.POWER_SAVE_CLIENT_DHCP, false)); |
| inOrderWifiNative.verify(mWifiNative).setPowerSave(WIFI_IFACE_NAME, false); |
| |
| assertTrue(mCmi.enablePowerSave()); |
| inOrderWifiNative.verify(mWifiNative).setPowerSave(WIFI_IFACE_NAME, true); |
| } |
| |
| /** |
| * 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 L2L3ConnectedState |
| 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 L2L3ConnectedState (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 the wifi module can be confiured to always get the factory MAC address from |
| * WifiNative instead of using saved value in WifiConfigStore. |
| */ |
| @Test |
| public void testGetFactoryMacAddressAlwaysFromWifiNative() throws Exception { |
| // Configure overlay to always retrieve the MAC address from WifiNative. |
| when(mWifiGlobals.isSaveFactoryMacToConfigStoreEnabled()).thenReturn(false); |
| initializeAndAddNetworkAndVerifySuccess(); |
| |
| clearInvocations(mWifiNative, mSettingsConfigStore); |
| assertEquals(TEST_GLOBAL_MAC_ADDRESS.toString(), mCmi.getFactoryMacAddress()); |
| assertEquals(TEST_GLOBAL_MAC_ADDRESS.toString(), mCmi.getFactoryMacAddress()); |
| assertEquals(TEST_GLOBAL_MAC_ADDRESS.toString(), mCmi.getFactoryMacAddress()); |
| |
| verify(mWifiNative, times(3)).getStaFactoryMacAddress(WIFI_IFACE_NAME); |
| } |
| |
| /** |
| * Verify getting the factory MAC address success case. |
| */ |
| @Test |
| public void testGetFactoryMacAddressSuccess() throws Exception { |
| initializeAndAddNetworkAndVerifySuccess(); |
| |
| clearInvocations(mWifiNative, mSettingsConfigStore); |
| |
| assertEquals(TEST_GLOBAL_MAC_ADDRESS.toString(), mCmi.getFactoryMacAddress()); |
| verify(mSettingsConfigStore).get(WIFI_STA_FACTORY_MAC_ADDRESS); // try config store. |
| verify(mWifiNative, never()).getStaFactoryMacAddress(WIFI_IFACE_NAME); // not native |
| verify(mSettingsConfigStore, never()).put(eq(WIFI_STA_FACTORY_MAC_ADDRESS), any()); |
| |
| clearInvocations(mWifiNative, mSettingsConfigStore); |
| |
| // get it again, should now use the config store MAC address, not native. |
| assertEquals(TEST_GLOBAL_MAC_ADDRESS.toString(), mCmi.getFactoryMacAddress()); |
| verify(mSettingsConfigStore).get(WIFI_STA_FACTORY_MAC_ADDRESS); |
| |
| // Verify secondary MAC is not stored |
| verify(mSettingsConfigStore, never()).put( |
| eq(SECONDARY_WIFI_STA_FACTORY_MAC_ADDRESS), any()); |
| verify(mSettingsConfigStore, never()).get(SECONDARY_WIFI_STA_FACTORY_MAC_ADDRESS); |
| |
| // Query again as secondary STA, and then verify the result is saved to secondary. |
| when(mClientModeManager.getRole()).thenReturn(ROLE_CLIENT_SECONDARY_LONG_LIVED); |
| mCmi.getFactoryMacAddress(); |
| verify(mWifiNative).getStaFactoryMacAddress(WIFI_IFACE_NAME); |
| verify(mSettingsConfigStore).put(eq(SECONDARY_WIFI_STA_FACTORY_MAC_ADDRESS), any()); |
| verify(mSettingsConfigStore).get(SECONDARY_WIFI_STA_FACTORY_MAC_ADDRESS); |
| |
| verifyNoMoreInteractions(mWifiNative, mSettingsConfigStore); |
| } |
| |
| /** |
| * Verify getting the factory MAC address failure case. |
| */ |
| @Test |
| public void testGetFactoryMacAddressFail() throws Exception { |
| initializeAndAddNetworkAndVerifySuccess(); |
| |
| clearInvocations(mWifiNative, mSettingsConfigStore); |
| |
| when(mSettingsConfigStore.get(WIFI_STA_FACTORY_MAC_ADDRESS)).thenReturn(null); |
| when(mWifiNative.getStaFactoryMacAddress(WIFI_IFACE_NAME)).thenReturn(null); |
| assertNull(mCmi.getFactoryMacAddress()); |
| verify(mSettingsConfigStore).get(WIFI_STA_FACTORY_MAC_ADDRESS); |
| verify(mWifiNative).getStaFactoryMacAddress(WIFI_IFACE_NAME); |
| |
| verifyNoMoreInteractions(mWifiNative, mSettingsConfigStore); |
| } |
| |
| /** |
| * Verify that when WifiNative#getStaFactoryMacAddress fails, if the device does not support |
| * MAC randomization then the currently programmed MAC address gets returned. |
| */ |
| @Test |
| public void testGetFactoryMacAddressFailWithNoMacRandomizationSupport() throws Exception { |
| // reset mWifiNative since initializeCmi() was called in setup() |
| resetWifiNative(); |
| |
| when(mWifiGlobals.isConnectedMacRandomizationEnabled()).thenReturn(false); |
| initializeCmi(); |
| initializeAndAddNetworkAndVerifySuccess(); |
| |
| clearInvocations(mWifiNative, mSettingsConfigStore); |
| |
| when(mSettingsConfigStore.get(WIFI_STA_FACTORY_MAC_ADDRESS)).thenReturn(null); |
| when(mWifiNative.getStaFactoryMacAddress(WIFI_IFACE_NAME)).thenReturn(null); |
| when(mWifiNative.getMacAddress(WIFI_IFACE_NAME)) |
| .thenReturn(TEST_DEFAULT_MAC_ADDRESS.toString()); |
| assertEquals(TEST_DEFAULT_MAC_ADDRESS.toString(), mCmi.getFactoryMacAddress()); |
| |
| verify(mSettingsConfigStore).get(WIFI_STA_FACTORY_MAC_ADDRESS); |
| verify(mWifiNative).getStaFactoryMacAddress(WIFI_IFACE_NAME); |
| verify(mWifiNative).getMacAddress(WIFI_IFACE_NAME); |
| |
| verifyNoMoreInteractions(mWifiNative, mSettingsConfigStore); |
| } |
| |
| /** |
| * 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); |
| verify(mWifiNative).setStaMacAddress(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 interface up will not re-randomize the MAC address again. |
| mCmi.onUpChanged(true); |
| verify(mWifiNative).setStaMacAddress(anyString(), macAddressCaptor.capture()); |
| } |
| |
| /** |
| * Verify if re-randomizing had failed, then we will retry the next time the interface comes up. |
| */ |
| @Test |
| public void testRandomizeMacAddressFailedRetryOnInterfaceUp() throws Exception { |
| // mock setting the MAC address to fail |
| when(mWifiNative.setStaMacAddress(eq(WIFI_IFACE_NAME), anyObject())).thenReturn(false); |
| initializeCmi(); |
| |
| ArgumentCaptor<MacAddress> macAddressCaptor = ArgumentCaptor.forClass(MacAddress.class); |
| verify(mWifiNative, times(2)).setStaMacAddress(anyString(), macAddressCaptor.capture()); |
| MacAddress currentMac = macAddressCaptor.getValue(); |
| |
| // mock setting the MAC address to succeed |
| when(mWifiNative.setStaMacAddress(eq(WIFI_IFACE_NAME), anyObject())) |
| .then(new AnswerWithArguments() { |
| public boolean answer(String iface, MacAddress mac) { |
| when(mWifiNative.getMacAddress(iface)).thenReturn(mac.toString()); |
| return true; |
| } |
| }); |
| |
| // Verify interface up will re-randomize the MAC address since the last attempt failed. |
| mCmi.onUpChanged(true); |
| verify(mWifiNative, times(3)).setStaMacAddress(anyString(), macAddressCaptor.capture()); |
| assertNotEquals("The currently programmed MAC address should be different from the factory " |
| + "MAC address after ClientModeImpl starts", |
| mCmi.getFactoryMacAddress(), currentMac.toString()); |
| |
| // Verify interface up will not re-randomize the MAC address since the last attempt |
| // succeeded. |
| mCmi.onUpChanged(true); |
| verify(mWifiNative, times(3)).setStaMacAddress(anyString(), macAddressCaptor.capture()); |
| } |
| |
| /** |
| * Verify the MAC address is being randomized at start to prevent leaking the factory MAC. |
| */ |
| @Test |
| public void testNoRandomizeMacAddressOnStartIfMacRandomizationNotEnabled() throws Exception { |
| // reset mWifiNative since initializeCmi() was called in setup() |
| resetWifiNative(); |
| |
| when(mWifiGlobals.isConnectedMacRandomizationEnabled()).thenReturn(false); |
| initializeCmi(); |
| verify(mWifiNative, never()).setStaMacAddress(anyString(), any()); |
| } |
| |
| /** |
| * Verify bugreport will be taken when get IP_REACHABILITY_LOST |
| */ |
| @Test |
| public void testTakebugreportbyIpReachabilityLost() throws Exception { |
| connect(); |
| |
| mIpClientCallback.onReachabilityLost("CMD_IP_REACHABILITY_LOST"); |
| mLooper.dispatchAll(); |
| verify(mWifiDiagnostics).triggerBugReportDataCapture( |
| eq(WifiDiagnostics.REPORT_REASON_REACHABILITY_LOST)); |
| } |
| |
| /** |
| * Verify bugreport will be taken when get IP_REACHABILITY_FAILURE |
| */ |
| @Test |
| public void testTakeBugReportByIpReachabilityFailure() throws Exception { |
| assumeTrue(SdkLevel.isAtLeastT()); |
| connect(); |
| |
| ReachabilityLossInfoParcelable lossInfo = |
| new ReachabilityLossInfoParcelable("", ReachabilityLossReason.ROAM); |
| mIpClientCallback.onReachabilityFailure(lossInfo); |
| mLooper.dispatchAll(); |
| verify(mWifiDiagnostics).triggerBugReportDataCapture( |
| eq(WifiDiagnostics.REPORT_REASON_REACHABILITY_FAILURE)); |
| } |
| /** |
| * 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, TEST_BSSID_STR); |
| mLooper.dispatchAll(); |
| // Verifies that WifiLastResortWatchdog won't be notified |
| // by other reason code |
| DisconnectEventInfo disconnectEventInfo = |
| new DisconnectEventInfo(TEST_SSID, TEST_BSSID_STR, 2, false); |
| mCmi.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, disconnectEventInfo); |
| mLooper.dispatchAll(); |
| |
| assertEquals("DisconnectedState", getCurrentState().getName()); |
| verify(mWifiLastResortWatchdog, never()).noteConnectionFailureAndTriggerIfNeeded( |
| eq(TEST_SSID), eq(TEST_BSSID_STR), |
| eq(WifiLastResortWatchdog.FAILURE_CODE_AUTHENTICATION), anyBoolean()); |
| |
| mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, 0, 0, TEST_BSSID_STR); |
| mLooper.dispatchAll(); |
| // Verifies that WifiLastResortWatchdog be notified |
| // for FOURWAY_HANDSHAKE_TIMEOUT. |
| disconnectEventInfo = new DisconnectEventInfo(TEST_SSID, TEST_BSSID_STR, 15, false); |
| mCmi.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, disconnectEventInfo); |
| mLooper.dispatchAll(); |
| |
| assertEquals("DisconnectedState", getCurrentState().getName()); |
| verify(mWifiLastResortWatchdog).noteConnectionFailureAndTriggerIfNeeded( |
| eq(TEST_SSID), eq(TEST_BSSID_STR), |
| eq(WifiLastResortWatchdog.FAILURE_CODE_AUTHENTICATION), anyBoolean()); |
| |
| } |
| |
| /** |
| * 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; |
| mConnectedNetwork.restricted = true; |
| connect(); |
| |
| assertTrue(mWifiInfo.isEphemeral()); |
| assertTrue(mWifiInfo.isTrusted()); |
| assumeTrue(mWifiInfo.isRestricted()); |
| assertEquals(OP_PACKAGE_NAME, |
| mWifiInfo.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; |
| mConnectedNetwork.restricted = true; |
| connect(); |
| |
| assertTrue(mWifiInfo.isEphemeral()); |
| assertTrue(mWifiInfo.isTrusted()); |
| assumeTrue(mWifiInfo.isRestricted()); |
| assertEquals(OP_PACKAGE_NAME, |
| mWifiInfo.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(); |
| |
| mIpClientCallback.onReachabilityLost("CMD_IP_REACHABILITY_LOST"); |
| mLooper.dispatchAll(); |
| verify(mWifiMetrics).logWifiIsUnusableEvent(WIFI_IFACE_NAME, |
| WifiIsUnusableEvent.TYPE_IP_REACHABILITY_LOST); |
| verify(mWifiMetrics).addToWifiUsabilityStatsList(WIFI_IFACE_NAME, |
| WifiUsabilityStats.LABEL_BAD, |
| WifiUsabilityStats.TYPE_IP_REACHABILITY_LOST, -1); |
| } |
| |
| /** |
| * Verify that a WifiIsUnusableEvent is logged and the current list of usability stats entries |
| * are labeled and saved when receiving an IP reachability failure message with non roam type. |
| * @throws Exception |
| */ |
| @Test |
| public void verifyIpReachabilityFailureMsgUpdatesWifiUsabilityMetrics() throws Exception { |
| assumeTrue(SdkLevel.isAtLeastT()); |
| connect(); |
| |
| ReachabilityLossInfoParcelable lossInfo = |
| new ReachabilityLossInfoParcelable("", ReachabilityLossReason.CONFIRM); |
| mIpClientCallback.onReachabilityFailure(lossInfo); |
| mLooper.dispatchAll(); |
| verify(mWifiMetrics).logWifiIsUnusableEvent(WIFI_IFACE_NAME, |
| WifiIsUnusableEvent.TYPE_IP_REACHABILITY_LOST); |
| verify(mWifiMetrics).addToWifiUsabilityStatsList(WIFI_IFACE_NAME, |
| WifiUsabilityStats.LABEL_BAD, |
| WifiUsabilityStats.TYPE_IP_REACHABILITY_LOST, -1); |
| } |
| |
| /** |
| * Verify that a WifiIsUnusableEvent isn't logged and the current list of usability stats |
| * entries are not labeled and saved when receiving an IP reachability failure message with |
| * non roam type but {@link isHandleRssiOrganicKernelFailuresEnabled} is enabled. |
| * @throws Exception |
| */ |
| @Test |
| public void verifyIpReachabilityFailureMsgUpdatesWifiUsabilityMetrics_enableFlag() |
| throws Exception { |
| when(mDeviceConfigFacade.isHandleRssiOrganicKernelFailuresEnabled()).thenReturn(true); |
| assumeTrue(SdkLevel.isAtLeastU()); |
| connect(); |
| |
| ReachabilityLossInfoParcelable lossInfo = |
| new ReachabilityLossInfoParcelable("", ReachabilityLossReason.CONFIRM); |
| mCmi.sendMessage(ClientModeImpl.CMD_IP_REACHABILITY_FAILURE, lossInfo); |
| mLooper.dispatchAll(); |
| verify(mWifiMetrics, never()).logWifiIsUnusableEvent(WIFI_IFACE_NAME, |
| WifiIsUnusableEvent.TYPE_IP_REACHABILITY_LOST); |
| verify(mWifiMetrics, never()).addToWifiUsabilityStatsList(WIFI_IFACE_NAME, |
| 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; |
| String expectetIdentity = "13214561234567890@wlan.mnc456.mcc321.3gppnetwork.org"; |
| |
| when(mWifiCarrierInfoManager.getBestMatchSubscriptionId(any(WifiConfiguration.class))) |
| .thenReturn(DATA_SUBID); |
| when(mWifiCarrierInfoManager.isSimReady(DATA_SUBID)).thenReturn(true); |
| when(mWifiCarrierInfoManager.getSimIdentity(any())) |
| .thenReturn(Pair.create(expectetIdentity, "")); |
| |
| triggerConnect(); |
| |
| mCmi.sendMessage(WifiMonitor.SUP_REQUEST_IDENTITY, |
| 0, FRAMEWORK_NETWORK_ID, DEFAULT_TEST_SSID); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiNative).simIdentityResponse(WIFI_IFACE_NAME, |
| expectetIdentity, ""); |
| } |
| |
| /** |
| * Verifies that WifiLastResortWatchdog is notified of DHCP failures when recevied |
| * NETWORK_DISCONNECTION_EVENT while in L3ProvisioningState. |
| */ |
| @Test |
| public void testDhcpFailureUpdatesWatchdog_WhenDisconnectedWhileObtainingIpAddr() |
| throws Exception { |
| initializeAndAddNetworkAndVerifySuccess(); |
| |
| verify(mWifiNative).removeAllNetworks(WIFI_IFACE_NAME); |
| |
| startConnectSuccess(); |
| |
| mCmi.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, |
| new NetworkConnectionEventInfo(0, TEST_WIFI_SSID, TEST_BSSID_STR, false, null)); |
| mLooper.dispatchAll(); |
| |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(0, TEST_WIFI_SSID, TEST_BSSID_STR, sFreq, |
| SupplicantState.COMPLETED)); |
| mLooper.dispatchAll(); |
| |
| assertEquals("L3ProvisioningState", getCurrentState().getName()); |
| |
| // Verifies that WifiLastResortWatchdog be notified. |
| DisconnectEventInfo disconnectEventInfo = |
| new DisconnectEventInfo(TEST_SSID, TEST_BSSID_STR, 0, false); |
| mCmi.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, disconnectEventInfo); |
| mLooper.dispatchAll(); |
| |
| assertEquals("DisconnectedState", getCurrentState().getName()); |
| verify(mWifiLastResortWatchdog).noteConnectionFailureAndTriggerIfNeeded( |
| eq(TEST_SSID), eq(TEST_BSSID_STR), |
| eq(WifiLastResortWatchdog.FAILURE_CODE_DHCP), anyBoolean()); |
| } |
| |
| /** |
| * 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; |
| for (WifiConfigManager.OnNetworkUpdateListener listener : mConfigUpdateListenerCaptor |
| .getAllValues()) { |
| listener.onNetworkRemoved(removedNetwork); |
| } |
| mLooper.dispatchAll(); |
| |
| verify(mWifiNative).removeNetworkCachedData(FRAMEWORK_NETWORK_ID); |
| verify(mWifiNative).disconnect(WIFI_IFACE_NAME); |
| } |
| |
| /** |
| * 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; |
| for (WifiConfigManager.OnNetworkUpdateListener listener : mConfigUpdateListenerCaptor |
| .getAllValues()) { |
| listener.onNetworkPermanentlyDisabled(disabledNetwork, |
| WifiConfiguration.NetworkSelectionStatus.DISABLED_BY_WRONG_PASSWORD); |
| } |
| mLooper.dispatchAll(); |
| |
| verify(mWifiNative).disconnect(WIFI_IFACE_NAME); |
| } |
| |
| @Test |
| public void testUnwantedDisconnectTemporarilyDisableNetwork() throws Exception { |
| connect(); |
| when(mWifiGlobals.disableUnwantedNetworkOnLowRssi()).thenReturn(true); |
| when(mWifiConfigManager.getLastSelectedNetwork()).thenReturn(-1); |
| mWifiInfo.setRssi(RSSI_THRESHOLD_MIN); |
| mCmi.sendMessage(CMD_UNWANTED_NETWORK, |
| ClientModeImpl.NETWORK_STATUS_UNWANTED_DISCONNECT); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiConfigManager).updateNetworkSelectionStatus(anyInt(), |
| eq(DISABLED_UNWANTED_LOW_RSSI)); |
| } |
| |
| /** |
| * Verify that the current network is permanently disabled when |
| * NETWORK_STATUS_UNWANTED_DISABLE_AUTOJOIN is received and percent internet availability is |
| * less than the threshold. |
| */ |
| @Test |
| public void testLowPrababilityInternetPermanentlyDisableNetwork() throws Exception { |
| connect(); |
| when(mPerBssid.estimatePercentInternetAvailability()).thenReturn( |
| ClientModeImpl.PROBABILITY_WITH_INTERNET_TO_PERMANENTLY_DISABLE_NETWORK - 1); |
| mCmi.sendMessage(CMD_UNWANTED_NETWORK, |
| ClientModeImpl.NETWORK_STATUS_UNWANTED_DISABLE_AUTOJOIN); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiConfigManager).updateNetworkSelectionStatus(anyInt(), |
| eq(DISABLED_NO_INTERNET_PERMANENT)); |
| } |
| |
| /** |
| * Verify that the current network is temporarily disabled when |
| * NETWORK_STATUS_UNWANTED_DISABLE_AUTOJOIN is received and percent internet availability is |
| * over the threshold. |
| */ |
| @Test |
| public void testHighProbabilityInternetTemporarilyDisableNetwork() throws Exception { |
| connect(); |
| when(mPerBssid.estimatePercentInternetAvailability()).thenReturn( |
| ClientModeImpl.PROBABILITY_WITH_INTERNET_TO_PERMANENTLY_DISABLE_NETWORK); |
| mCmi.sendMessage(CMD_UNWANTED_NETWORK, |
| ClientModeImpl.NETWORK_STATUS_UNWANTED_DISABLE_AUTOJOIN); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiConfigManager).updateNetworkSelectionStatus(anyInt(), |
| eq(DISABLED_NO_INTERNET_TEMPORARY)); |
| } |
| |
| /** |
| * 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; |
| for (WifiConfigManager.OnNetworkUpdateListener listener : mConfigUpdateListenerCaptor |
| .getAllValues()) { |
| listener.onNetworkPermanentlyDisabled(disabledNetwork, |
| WifiConfiguration.NetworkSelectionStatus.DISABLED_NO_INTERNET_PERMANENT); |
| } |
| mLooper.dispatchAll(); |
| |
| assertEquals("L3ConnectedState", 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; |
| for (WifiConfigManager.OnNetworkUpdateListener listener : mConfigUpdateListenerCaptor |
| .getAllValues()) { |
| listener.onNetworkTemporarilyDisabled(disabledNetwork, |
| WifiConfiguration.NetworkSelectionStatus.DISABLED_NO_INTERNET_TEMPORARY); |
| } |
| mLooper.dispatchAll(); |
| |
| assertEquals("L3ConnectedState", getCurrentState().getName()); |
| } |
| |
| /** |
| * Verify that MboOce/WifiDataStall enable/disable methods are called in ClientMode. |
| */ |
| @Test |
| public void verifyMboOceWifiDataStallSetupInClientMode() throws Exception { |
| verify(mMboOceController).enable(); |
| mCmi.stop(); |
| mLooper.dispatchAll(); |
| verify(mMboOceController).disable(); |
| } |
| |
| @Test |
| public void verifyWifiMonitorHandlersDeregisteredOnStop() throws Exception { |
| verify(mWifiMonitor, atLeastOnce()) |
| .registerHandler(eq(WIFI_IFACE_NAME), anyInt(), any()); |
| verify(mWifiMetrics).registerForWifiMonitorEvents(WIFI_IFACE_NAME); |
| verify(mWifiLastResortWatchdog).registerForWifiMonitorEvents(WIFI_IFACE_NAME); |
| |
| mCmi.stop(); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiMonitor, atLeastOnce()) |
| .deregisterHandler(eq(WIFI_IFACE_NAME), anyInt(), any()); |
| verify(mWifiMetrics).deregisterForWifiMonitorEvents(WIFI_IFACE_NAME); |
| verify(mWifiLastResortWatchdog).deregisterForWifiMonitorEvents(WIFI_IFACE_NAME); |
| } |
| |
| @Test |
| public void onBluetoothConnectionStateChanged() throws Exception { |
| // reset mWifiNative since initializeCmi() was called in setup() |
| resetWifiNative(); |
| |
| when(mWifiGlobals.isBluetoothConnected()).thenReturn(false); |
| initializeCmi(); |
| verify(mWifiNative).setBluetoothCoexistenceScanMode(any(), eq(false)); |
| |
| when(mWifiGlobals.isBluetoothConnected()).thenReturn(true); |
| mCmi.onBluetoothConnectionStateChanged(); |
| mLooper.dispatchAll(); |
| verify(mWifiNative).setBluetoothCoexistenceScanMode(any(), eq(true)); |
| |
| when(mWifiGlobals.isBluetoothConnected()).thenReturn(false); |
| mCmi.onBluetoothConnectionStateChanged(); |
| mLooper.dispatchAll(); |
| verify(mWifiNative, times(2)).setBluetoothCoexistenceScanMode(any(), eq(false)); |
| } |
| |
| /** |
| * 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 |TEST_BSSID_STR|, |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(mWifiBlocklistMonitor).blockBssidForDurationMs(eq(TEST_BSSID_STR), any(), |
| 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 |TEST_BSSID_STR|, |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(mWifiBlocklistMonitor).blockBssidForDurationMs(eq(TEST_BSSID_STR), any(), |
| 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 |TEST_BSSID_STR|, |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(mWifiBlocklistMonitor, never()).blockBssidForDurationMs(eq(TEST_BSSID_STR), |
| any(), 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 |TEST_BSSID_STR|, |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); |
| } |
| |
| private static ScanResult.InformationElement createIE(int id, byte[] bytes) { |
| ScanResult.InformationElement ie = new ScanResult.InformationElement(); |
| ie.id = id; |
| ie.bytes = bytes; |
| return ie; |
| } |
| |
| /** |
| * Helper function for setting up fils test. |
| * |
| * @param isDriverSupportFils true if driver support fils. |
| * @return wifi configuration. |
| */ |
| private WifiConfiguration setupFilsTest(boolean isDriverSupportFils) { |
| 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_AUTO; |
| |
| 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/SHA1+EAP/SHA256+EAP-FILS-SHA256-CCMP]" |
| + "[RSN-EAP/SHA1+EAP/SHA256+EAP-FILS-SHA256-CCMP][ESS]"; |
| ScanResult scanResult = new ScanResult(WifiSsid.fromUtf8Text(sFilsSsid), |
| sFilsSsid, TEST_BSSID_STR, 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}; |
| when(mScanRequestProxy.getScanResults()).thenReturn(Arrays.asList(scanResult)); |
| when(mScanRequestProxy.getScanResult(eq(TEST_BSSID_STR))).thenReturn(scanResult); |
| } |
| |
| |
| /** |
| * 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}; |
| mIpClientCallback.onPreconnectionStart(Collections.singletonList(l2Packet)); |
| mLooper.dispatchAll(); |
| assertEquals("L2ConnectingState", mCmi.getCurrentState().getName()); |
| } |
| |
| /** |
| * 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, TEST_BSSID_STR); |
| 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, TEST_BSSID_STR); |
| 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, TEST_BSSID_STR); |
| 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, TEST_BSSID_STR); |
| mLooper.dispatchAll(); |
| |
| prepareFilsHlpPktAndSendStartConnect(); |
| |
| verify(mWifiNative, times(1)).connectToNetwork(eq(WIFI_IFACE_NAME), eq(config)); |
| |
| mCmi.sendMessage(WifiMonitor.ASSOCIATION_REJECTION_EVENT, |
| new AssocRejectEventInfo(TEST_SSID, TEST_BSSID_STR, 2, false)); |
| mLooper.dispatchAll(); |
| |
| mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, 0, 0, TEST_BSSID_STR); |
| 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, TEST_BSSID_STR); |
| mLooper.dispatchAll(); |
| assertEquals("L2ConnectingState", mCmi.getCurrentState().getName()); |
| |
| prepareFilsHlpPktAndSendStartConnect(); |
| |
| verify(mWifiMetrics, times(1)).incrementConnectRequestWithFilsAkmCount(); |
| |
| mCmi.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, |
| new NetworkConnectionEventInfo(0, TEST_WIFI_SSID, TEST_BSSID_STR, true, null)); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiMetrics, times(1)).incrementL2ConnectionThroughFilsAuthCount(); |
| |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(0, WifiSsid.fromUtf8Text(sFilsSsid), |
| TEST_BSSID_STR, sFreq, SupplicantState.COMPLETED)); |
| mLooper.dispatchAll(); |
| |
| assertEquals("L3ProvisioningState", 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 = mWifiInfo; |
| assertNotNull(wifiInfo); |
| assertEquals(TEST_BSSID_STR, wifiInfo.getBSSID()); |
| assertTrue(WifiSsid.fromUtf8Text(sFilsSsid).equals(wifiInfo.getWifiSsid())); |
| assertEquals("L3ConnectedState", getCurrentState().getName()); |
| } |
| |
| /** |
| * Verifies that while connecting to secondary STA, framework doesn't change the roaming |
| * configuration. |
| * @throws Exception |
| */ |
| @Test |
| public void testConnectSecondaryStaNotChangeRoamingConfig() throws Exception { |
| initializeAndAddNetworkAndVerifySuccess(); |
| when(mClientModeManager.getRole()).thenReturn(ROLE_CLIENT_SECONDARY_LONG_LIVED); |
| mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, 0, 0, TEST_BSSID_STR); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiNative).connectToNetwork(eq(WIFI_IFACE_NAME), eq(mTestConfig)); |
| verify(mWifiBlocklistMonitor, never()).setAllowlistSsids(anyString(), any()); |
| } |
| |
| /** |
| * Tests the wifi info is updated correctly for connecting network. |
| */ |
| @Test |
| public void testWifiInfoOnConnectingNextNetwork() throws Exception { |
| mConnectedNetwork.ephemeral = true; |
| mConnectedNetwork.trusted = true; |
| mConnectedNetwork.oemPaid = true; |
| mConnectedNetwork.oemPrivate = true; |
| mConnectedNetwork.carrierMerged = true; |
| mConnectedNetwork.osu = true; |
| mConnectedNetwork.subscriptionId = DATA_SUBID; |
| |
| triggerConnect(); |
| when(mWifiConfigManager.getScanDetailCacheForNetwork(FRAMEWORK_NETWORK_ID)) |
| .thenReturn(mScanDetailCache); |
| |
| when(mScanDetailCache.getScanDetail(TEST_BSSID_STR)).thenReturn( |
| getGoogleGuestScanDetail(TEST_RSSI, TEST_BSSID_STR, sFreq)); |
| when(mScanDetailCache.getScanResult(TEST_BSSID_STR)).thenReturn( |
| getGoogleGuestScanDetail(TEST_RSSI, TEST_BSSID_STR, sFreq).getScanResult()); |
| |
| // before the fist success connection, there is no valid wifi info. |
| assertEquals(WifiConfiguration.INVALID_NETWORK_ID, mWifiInfo.getNetworkId()); |
| |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(FRAMEWORK_NETWORK_ID, |
| TEST_WIFI_SSID, TEST_BSSID_STR, sFreq, SupplicantState.ASSOCIATED)); |
| mLooper.dispatchAll(); |
| |
| // retrieve correct wifi info on receiving the supplicant state change event. |
| assertEquals(FRAMEWORK_NETWORK_ID, mWifiInfo.getNetworkId()); |
| assertEquals(mConnectedNetwork.ephemeral, mWifiInfo.isEphemeral()); |
| assertEquals(mConnectedNetwork.trusted, mWifiInfo.isTrusted()); |
| assertEquals(mConnectedNetwork.osu, mWifiInfo.isOsuAp()); |
| assertEquals(mConnectedNetwork.restricted, mWifiInfo.isRestricted()); |
| if (SdkLevel.isAtLeastS()) { |
| assertEquals(mConnectedNetwork.oemPaid, mWifiInfo.isOemPaid()); |
| assertEquals(mConnectedNetwork.oemPrivate, mWifiInfo.isOemPrivate()); |
| assertEquals(mConnectedNetwork.carrierMerged, mWifiInfo.isCarrierMerged()); |
| assertEquals(DATA_SUBID, mWifiInfo.getSubscriptionId()); |
| } |
| } |
| |
| /** |
| * 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; |
| |
| for (WifiConfigManager.OnNetworkUpdateListener listener : mConfigUpdateListenerCaptor |
| .getAllValues()) { |
| listener.onNetworkUpdated(mConnectedNetwork, oldConfig, false); |
| } |
| mLooper.dispatchAll(); |
| verify(mWifiNative).disconnect(WIFI_IFACE_NAME); |
| verify(mWifiMetrics).logStaEvent(anyString(), eq(StaEvent.TYPE_FRAMEWORK_DISCONNECT), |
| eq(StaEvent.DISCONNECT_NETWORK_METERED)); |
| } |
| |
| /** |
| * 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(mWifiNetworkAgent); |
| |
| WifiConfiguration oldConfig = new WifiConfiguration(mConnectedNetwork); |
| mConnectedNetwork.meteredOverride = METERED_OVERRIDE_NOT_METERED; |
| |
| for (WifiConfigManager.OnNetworkUpdateListener listener : mConfigUpdateListenerCaptor |
| .getAllValues()) { |
| listener.onNetworkUpdated(mConnectedNetwork, oldConfig, false); |
| } |
| mLooper.dispatchAll(); |
| assertEquals("L3ConnectedState", 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(mWifiNetworkAgent); |
| |
| // 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 = mWifiInfo; |
| wifiInfo.setMeteredHint(true); |
| |
| for (WifiConfigManager.OnNetworkUpdateListener listener : mConfigUpdateListenerCaptor |
| .getAllValues()) { |
| listener.onNetworkUpdated(mConnectedNetwork, oldConfig, false); |
| } |
| mLooper.dispatchAll(); |
| verify(mWifiNative).disconnect(WIFI_IFACE_NAME); |
| } |
| |
| /** |
| * 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(mWifiNetworkAgent); |
| |
| WifiConfiguration oldConfig = new WifiConfiguration(mConnectedNetwork); |
| mConnectedNetwork.meteredOverride = METERED_OVERRIDE_NONE; |
| |
| // Reset metered hint in WifiInfo. |
| WifiInfo wifiInfo = mWifiInfo; |
| wifiInfo.setMeteredHint(false); |
| |
| for (WifiConfigManager.OnNetworkUpdateListener listener : mConfigUpdateListenerCaptor |
| .getAllValues()) { |
| listener.onNetworkUpdated(mConnectedNetwork, oldConfig, false); |
| } |
| mLooper.dispatchAll(); |
| assertEquals("L3ConnectedState", 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(mWifiNetworkAgent); |
| |
| // 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 = mWifiInfo; |
| wifiInfo.setMeteredHint(true); |
| |
| for (WifiConfigManager.OnNetworkUpdateListener listener : mConfigUpdateListenerCaptor |
| .getAllValues()) { |
| listener.onNetworkUpdated(mConnectedNetwork, oldConfig, false); |
| } |
| mLooper.dispatchAll(); |
| assertEquals("L3ConnectedState", getCurrentState().getName()); |
| |
| verifyNoMoreInteractions(mWifiNetworkAgent); |
| } |
| |
| /** |
| * 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(mWifiNetworkAgent); |
| |
| // Mark network metered none. |
| WifiConfiguration oldConfig = new WifiConfiguration(mConnectedNetwork); |
| mConnectedNetwork.meteredOverride = METERED_OVERRIDE_NONE; |
| |
| // Reset metered hint in WifiInfo. |
| WifiInfo wifiInfo = mWifiInfo; |
| wifiInfo.setMeteredHint(false); |
| |
| for (WifiConfigManager.OnNetworkUpdateListener listener : mConfigUpdateListenerCaptor |
| .getAllValues()) { |
| listener.onNetworkUpdated(mConnectedNetwork, oldConfig, false); |
| } |
| mLooper.dispatchAll(); |
| assertEquals("L3ConnectedState", getCurrentState().getName()); |
| |
| verifyNoMoreInteractions(mWifiNetworkAgent); |
| } |
| |
| /* |
| * 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, TEST_BSSID_STR); |
| mLooper.dispatchAll(); |
| |
| // got UNSPECIFIED during this connection attempt |
| DisconnectEventInfo disconnectEventInfo = |
| new DisconnectEventInfo(TEST_SSID, TEST_BSSID_STR, 1, false); |
| mCmi.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, disconnectEventInfo); |
| mLooper.dispatchAll(); |
| |
| assertEquals("DisconnectedState", getCurrentState().getName()); |
| verify(mWifiNative, never()).removeNetworkCachedData(anyInt()); |
| |
| mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, 0, 0, TEST_BSSID_STR); |
| mLooper.dispatchAll(); |
| // got 4WAY_HANDSHAKE_TIMEOUT during this connection attempt |
| disconnectEventInfo = new DisconnectEventInfo(TEST_SSID, TEST_BSSID_STR, 15, false); |
| mCmi.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, disconnectEventInfo); |
| 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 testNetworkCachedDataIsClearedCorrectlyInL3ProvisioningState() throws Exception { |
| initializeAndAddNetworkAndVerifySuccess(); |
| |
| verify(mWifiNative).removeAllNetworks(WIFI_IFACE_NAME); |
| |
| startConnectSuccess(); |
| |
| mCmi.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, |
| new NetworkConnectionEventInfo(0, TEST_WIFI_SSID, TEST_BSSID_STR, false, null)); |
| mLooper.dispatchAll(); |
| |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(0, TEST_WIFI_SSID, TEST_BSSID_STR, sFreq, |
| SupplicantState.COMPLETED)); |
| mLooper.dispatchAll(); |
| |
| assertEquals("L3ProvisioningState", getCurrentState().getName()); |
| |
| // got 4WAY_HANDSHAKE_TIMEOUT during this connection attempt |
| DisconnectEventInfo disconnectEventInfo = |
| new DisconnectEventInfo(TEST_SSID, TEST_BSSID_STR, 15, false); |
| mCmi.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, disconnectEventInfo); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiNative).removeNetworkCachedData(FRAMEWORK_NETWORK_ID); |
| } |
| |
| /* |
| * Verify that network cached data is NOT cleared in L3ConnectedState. |
| */ |
| @Test |
| public void testNetworkCachedDataIsClearedIf4WayHandshakeFailure() throws Exception { |
| when(mWifiScoreCard.detectAbnormalDisconnection(any())) |
| .thenReturn(WifiHealthMonitor.REASON_SHORT_CONNECTION_NONLOCAL); |
| InOrder inOrderWifiLockManager = inOrder(mWifiLockManager); |
| connect(); |
| inOrderWifiLockManager.verify(mWifiLockManager) |
| .updateWifiClientConnected(mClientModeManager, true); |
| |
| // got 4WAY_HANDSHAKE_TIMEOUT |
| DisconnectEventInfo disconnectEventInfo = |
| new DisconnectEventInfo(TEST_SSID, TEST_BSSID_STR, 15, false); |
| mCmi.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, disconnectEventInfo); |
| mLooper.dispatchAll(); |
| verify(mWifiNative, never()).removeNetworkCachedData(anyInt()); |
| } |
| |
| /** |
| * Verify that network cached data is cleared on changing the credential. |
| */ |
| @Test |
| public void testNetworkCachedDataIsClearedOnChangingTheCredential() throws Exception { |
| mConnectedNetwork = spy(WifiConfigurationTestUtil.createPasspointNetwork()); |
| WifiConfiguration oldConfig = new WifiConfiguration(mConnectedNetwork); |
| mConnectedNetwork.enterpriseConfig.setPassword("fakePassword"); |
| |
| for (WifiConfigManager.OnNetworkUpdateListener listener : mConfigUpdateListenerCaptor |
| .getAllValues()) { |
| listener.onNetworkUpdated(mConnectedNetwork, oldConfig, true); |
| } |
| mLooper.dispatchAll(); |
| verify(mWifiNative).removeNetworkCachedData(eq(oldConfig.networkId)); |
| } |
| |
| |
| @Test |
| public void testVerifyWifiInfoStateOnFrameworkDisconnect() throws Exception { |
| connect(); |
| |
| assertEquals(mWifiInfo.getSupplicantState(), SupplicantState.COMPLETED); |
| |
| // Now trigger disconnect |
| mCmi.disconnect(); |
| mLooper.dispatchAll(); |
| |
| // get disconnect event |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(0, WifiSsid.fromUtf8Text(mConnectedNetwork.SSID), |
| TEST_BSSID_STR, sFreq, SupplicantState.DISCONNECTED)); |
| mLooper.dispatchAll(); |
| |
| assertEquals(mWifiInfo.getSupplicantState(), SupplicantState.DISCONNECTED); |
| } |
| |
| @Test |
| public void testVerifyWifiInfoStateOnFrameworkDisconnectButMissingDisconnectEvent() |
| throws Exception { |
| connect(); |
| |
| assertEquals(mWifiInfo.getSupplicantState(), SupplicantState.COMPLETED); |
| |
| // Now trigger disconnect |
| mCmi.disconnect(); |
| mLooper.dispatchAll(); |
| |
| // missing disconnect event, but got supplicant state change with disconnect state instead. |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(0, WifiSsid.fromUtf8Text(mConnectedNetwork.SSID), |
| TEST_BSSID_STR, sFreq, SupplicantState.DISCONNECTED)); |
| mLooper.dispatchAll(); |
| |
| assertEquals(mWifiInfo.getSupplicantState(), SupplicantState.DISCONNECTED); |
| } |
| |
| /** |
| * Ensures that we only disable the current network & set MAC address only when we exit |
| * ConnectingState. |
| * @throws Exception |
| */ |
| @Test |
| public void testDisableNetworkOnExitingConnectingOrConnectedState() throws Exception { |
| connect(); |
| String oldSsid = mConnectedNetwork.SSID; |
| |
| // Trigger connection to a different network |
| mConnectedNetwork.SSID = "\"" + oldSsid.concat("blah") + "\""; |
| mConnectedNetwork.networkId++; |
| mConnectedNetwork.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_NONE; |
| setupAndStartConnectSequence(mConnectedNetwork); |
| |
| // Send disconnect event for the old network. |
| DisconnectEventInfo disconnectEventInfo = |
| new DisconnectEventInfo(oldSsid, TEST_BSSID_STR, 0, false); |
| mCmi.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, disconnectEventInfo); |
| mLooper.dispatchAll(); |
| |
| assertEquals("L2ConnectingState", getCurrentState().getName()); |
| // Since we remain in connecting state, we should not disable the network or set random MAC |
| // address on disconnect. |
| verify(mWifiNative, never()).disableNetwork(WIFI_IFACE_NAME); |
| // Set MAC address thrice - once at bootup, twice for the 2 connections. |
| verify(mWifiNative, times(3)).setStaMacAddress(eq(WIFI_IFACE_NAME), any()); |
| |
| // Send disconnect event for the new network. |
| disconnectEventInfo = |
| new DisconnectEventInfo(mConnectedNetwork.SSID, TEST_BSSID_STR, 0, false); |
| mCmi.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, disconnectEventInfo); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiNative).disableNetwork(WIFI_IFACE_NAME); |
| // Set MAC address thrice - once at bootup, twice for the connections, |
| // once for the disconnect. |
| verify(mWifiNative, times(4)).setStaMacAddress(eq(WIFI_IFACE_NAME), any()); |
| } |
| |
| @Test |
| public void testIpReachabilityFailureConfirmTriggersDisconnection() throws Exception { |
| assumeTrue(SdkLevel.isAtLeastT()); |
| connect(); |
| expectRegisterNetworkAgent((agentConfig) -> { }, (cap) -> { }); |
| reset(mWifiNetworkAgent); |
| |
| // normal behavior w/o overlay |
| when(mWifiGlobals.disableNudDisconnectsForWapiInSpecificCc()).thenReturn(false); |
| when(mWifiCountryCode.getCountryCode()).thenReturn("CN"); |
| |
| // Trigger ip reachability failure and ensure we trigger a disconnect. |
| ReachabilityLossInfoParcelable lossInfo = |
| new ReachabilityLossInfoParcelable("", ReachabilityLossReason.CONFIRM); |
| mIpClientCallback.onReachabilityFailure(lossInfo); |
| mLooper.dispatchAll(); |
| verify(mWifiNative).disconnect(WIFI_IFACE_NAME); |
| } |
| |
| @Test |
| public void testIpReachabilityFailureConfirmDoesNotTriggersDisconnectionForWapi() |
| throws Exception { |
| mConnectedNetwork = spy(WifiConfigurationTestUtil.createWapiPskNetwork()); |
| |
| assumeTrue(SdkLevel.isAtLeastT()); |
| connect(); |
| expectRegisterNetworkAgent((agentConfig) -> { }, (cap) -> { }); |
| reset(mWifiNetworkAgent); |
| |
| when(mWifiGlobals.disableNudDisconnectsForWapiInSpecificCc()).thenReturn(true); |
| when(mWifiCountryCode.getCountryCode()).thenReturn("CN"); |
| |
| // Trigger ip reachability failure and ensure we trigger a disconnect. |
| ReachabilityLossInfoParcelable lossInfo = |
| new ReachabilityLossInfoParcelable("", ReachabilityLossReason.CONFIRM); |
| mIpClientCallback.onReachabilityFailure(lossInfo); |
| mLooper.dispatchAll(); |
| if (SdkLevel.isAtLeastV()) { |
| verify(mWifiNative).disconnect(WIFI_IFACE_NAME); |
| } else { |
| verify(mWifiNative, never()).disconnect(WIFI_IFACE_NAME); |
| } |
| } |
| |
| @Test |
| public void testIpReachabilityFailureStaticIpOrganicTriggersDisconnection() throws Exception { |
| when(mDeviceConfigFacade.isHandleRssiOrganicKernelFailuresEnabled()).thenReturn(true); |
| assumeTrue(SdkLevel.isAtLeastT()); |
| |
| final List<InetAddress> dnsServers = new ArrayList<>(); |
| dnsServers.add(InetAddresses.parseNumericAddress("8.8.8.8")); |
| dnsServers.add(InetAddresses.parseNumericAddress("4.4.4.4")); |
| final StaticIpConfiguration staticIpConfig = |
| new StaticIpConfiguration.Builder() |
| .setIpAddress(new LinkAddress("192.0.2.2/25")) |
| .setGateway(InetAddresses.parseNumericAddress("192.0.2.1")) |
| .setDnsServers(dnsServers) |
| .build(); |
| final IpConfiguration ipConfig = new IpConfiguration(); |
| ipConfig.setStaticIpConfiguration(staticIpConfig); |
| ipConfig.setIpAssignment(IpConfiguration.IpAssignment.STATIC); |
| mConnectedNetwork.setIpConfiguration(ipConfig); |
| |
| triggerConnect(); |
| validateConnectionInfo(); |
| |
| // Simulate L2 connection. |
| final WifiSsid wifiSsid = |
| WifiSsid.fromBytes( |
| NativeUtil.byteArrayFromArrayList( |
| NativeUtil.decodeSsid(mConnectedNetwork.SSID))); |
| mCmi.sendMessage( |
| WifiMonitor.NETWORK_CONNECTION_EVENT, |
| new NetworkConnectionEventInfo(0, wifiSsid, TEST_BSSID_STR, false, null)); |
| mLooper.dispatchAll(); |
| |
| // Simulate L3 connection. |
| final DhcpResultsParcelable dhcpResults = new DhcpResultsParcelable(); |
| dhcpResults.baseConfiguration = staticIpConfig; |
| injectDhcpSuccess(dhcpResults); |
| mLooper.dispatchAll(); |
| expectRegisterNetworkAgent((agentConfig) -> {}, (cap) -> {}); |
| reset(mWifiNetworkAgent); |
| |
| // normal behavior outside specific CC |
| when(mWifiGlobals.disableNudDisconnectsForWapiInSpecificCc()).thenReturn(true); |
| when(mWifiCountryCode.getCountryCode()).thenReturn("US"); |
| |
| // Trigger IP reachability failure and ensure we trigger a disconnection due to static IP. |
| ReachabilityLossInfoParcelable lossInfo = |
| new ReachabilityLossInfoParcelable("", ReachabilityLossReason.ORGANIC); |
| mIpClientCallback.onReachabilityFailure(lossInfo); |
| mLooper.dispatchAll(); |
| verify(mWifiNative).disconnect(WIFI_IFACE_NAME); |
| verify(mWifiNetworkAgent, never()).unregisterAfterReplacement(anyInt()); |
| } |
| |
| private void doIpReachabilityFailureTest(int lossReason, boolean shouldWifiDisconnect) |
| throws Exception { |
| assumeTrue(SdkLevel.isAtLeastU()); |
| connect(); |
| expectRegisterNetworkAgent((agentConfig) -> { }, (cap) -> { }); |
| reset(mWifiNetworkAgent); |
| |
| // Trigger ip reachability failure from cmd_confirm or organic kernel probe and ensure |
| // wifi never disconnects and eventually state machine transits to L3ProvisioningState |
| // on U and above, but wifi should still disconnect on previous platforms. |
| ReachabilityLossInfoParcelable lossInfo = |
| new ReachabilityLossInfoParcelable("", lossReason); |
| mIpClientCallback.onReachabilityFailure(lossInfo); |
| mLooper.dispatchAll(); |
| if (!shouldWifiDisconnect) { |
| verify(mWifiNative, never()).disconnect(WIFI_IFACE_NAME); |
| assertEquals("L3ProvisioningState", getCurrentState().getName()); |
| } else { |
| verify(mWifiNative).disconnect(WIFI_IFACE_NAME); |
| } |
| } |
| |
| @Test |
| public void testIpReachabilityFailureConfirm_enableHandleRssiOrganicKernelFailuresFlag() |
| throws Exception { |
| when(mDeviceConfigFacade.isHandleRssiOrganicKernelFailuresEnabled()).thenReturn(true); |
| |
| doIpReachabilityFailureTest(ReachabilityLossReason.CONFIRM, |
| false /* shouldWifiDisconnect */); |
| } |
| |
| @Test |
| public void testIpReachabilityFailureConfirm_disableHandleRssiOrganicKernelFailuresFlag() |
| throws Exception { |
| when(mDeviceConfigFacade.isHandleRssiOrganicKernelFailuresEnabled()).thenReturn(false); |
| |
| // should still disconnect in following scenario |
| mConnectedNetwork = spy(WifiConfigurationTestUtil.createPskNetwork()); |
| when(mWifiGlobals.disableNudDisconnectsForWapiInSpecificCc()).thenReturn(true); |
| when(mWifiCountryCode.getCurrentDriverCountryCode()).thenReturn("CN"); |
| |
| doIpReachabilityFailureTest(ReachabilityLossReason.CONFIRM, |
| true /* shouldWifiDisconnect */); |
| } |
| |
| @Test |
| public void testIpReachabilityFailureOrganicTriggersDisconnection() throws Exception { |
| assumeTrue(SdkLevel.isAtLeastT()); |
| connect(); |
| expectRegisterNetworkAgent((agentConfig) -> { }, (cap) -> { }); |
| reset(mWifiNetworkAgent); |
| |
| // Trigger ip reachability failure and ensure we trigger a disconnect. |
| ReachabilityLossInfoParcelable lossInfo = |
| new ReachabilityLossInfoParcelable("", ReachabilityLossReason.ORGANIC); |
| mIpClientCallback.onReachabilityFailure(lossInfo); |
| mLooper.dispatchAll(); |
| verify(mWifiNative).disconnect(WIFI_IFACE_NAME); |
| } |
| |
| @Test |
| public void testIpReachabilityFailureOrganic_enableHandleRssiOrganicKernelFailuresFlag() |
| throws Exception { |
| when(mDeviceConfigFacade.isHandleRssiOrganicKernelFailuresEnabled()).thenReturn(true); |
| |
| doIpReachabilityFailureTest(ReachabilityLossReason.ORGANIC, |
| false /* shouldWifiDisconnect */); |
| } |
| |
| @Test |
| public void testIpReachabilityFailureOrganic_disableHandleRssiOrganicKernelFailuresFlag() |
| throws Exception { |
| when(mDeviceConfigFacade.isHandleRssiOrganicKernelFailuresEnabled()).thenReturn(false); |
| |
| doIpReachabilityFailureTest(ReachabilityLossReason.ORGANIC, |
| true /* shouldWifiDisconnect */); |
| } |
| |
| @Test |
| public void testIpReachabilityFailureRoamWithNullConfigDisconnect() throws Exception { |
| assumeTrue(SdkLevel.isAtLeastT()); |
| connect(); |
| expectRegisterNetworkAgent((agentConfig) -> { }, (cap) -> { }); |
| reset(mWifiNetworkAgent); |
| |
| // mock the current network as removed |
| when(mWifiConfigManager.getConfiguredNetwork(anyInt())).thenReturn(null); |
| |
| // Trigger ip reachability failure and ensure we disconnect. |
| ReachabilityLossInfoParcelable lossInfo = |
| new ReachabilityLossInfoParcelable("", ReachabilityLossReason.ROAM); |
| mIpClientCallback.onReachabilityFailure(lossInfo); |
| mLooper.dispatchAll(); |
| verify(mWifiNative).disconnect(WIFI_IFACE_NAME); |
| } |
| |
| @Test |
| public void testIpReachabilityFailureRoamL3ProvisioningState() throws Exception { |
| assumeTrue(SdkLevel.isAtLeastT()); |
| connect(); |
| expectRegisterNetworkAgent((agentConfig) -> { }, (cap) -> { }); |
| reset(mWifiNetworkAgent); |
| |
| // Trigger ip reachability failure and ensure we do not trigger a disconnect. |
| ReachabilityLossInfoParcelable lossInfo = |
| new ReachabilityLossInfoParcelable("", ReachabilityLossReason.ROAM); |
| mIpClientCallback.onReachabilityFailure(lossInfo); |
| mLooper.dispatchAll(); |
| verify(mWifiNetworkAgent).unregisterAfterReplacement(anyInt()); |
| verify(mWifiNative, never()).disconnect(WIFI_IFACE_NAME); |
| assertEquals("L3ProvisioningState", getCurrentState().getName()); |
| } |
| |
| @Test |
| public void testIpReachabilityFailureRoamL3ProvisioningState_recreateIpClient() |
| throws Exception { |
| assumeTrue(SdkLevel.isAtLeastT()); |
| connect(); |
| expectRegisterNetworkAgent((agentConfig) -> { }, (cap) -> { }); |
| reset(mWifiNetworkAgent); |
| |
| // Save current IpClientCallbacks instance and verify the onProvisioningFailure |
| // from this instance won't trigger wifi disconnection after recreating a new |
| // IpClient and IpClientCallbacks in WaitBeforeL3ProvisioningState. |
| final IpClientCallbacks callback = mIpClientCallback; |
| |
| // Trigger ip reachability failure and ensure we do not trigger a disconnect. |
| ReachabilityLossInfoParcelable lossInfo = |
| new ReachabilityLossInfoParcelable("", ReachabilityLossReason.ROAM); |
| callback.onReachabilityFailure(lossInfo); |
| mLooper.dispatchAll(); |
| verify(mWifiNetworkAgent).unregisterAfterReplacement(anyInt()); |
| verify(mWifiNative, never()).disconnect(WIFI_IFACE_NAME); |
| assertEquals("L3ProvisioningState", getCurrentState().getName()); |
| |
| // Verify that onProvisioningFailure from the legacy IpClientCallbacks instance |
| // doesn't trigger wifi disconnection. |
| callback.onProvisioningFailure(new LinkProperties()); |
| mLooper.dispatchAll(); |
| verify(mWifiNative, never()).disconnect(WIFI_IFACE_NAME); |
| |
| // Verify that onProvisioningFailure from the current IpClientCallbacks instance |
| // triggers wifi disconnection. |
| mIpClientCallback.onProvisioningFailure(new LinkProperties()); |
| mLooper.dispatchAll(); |
| verify(mWifiNative).disconnect(WIFI_IFACE_NAME); |
| } |
| |
| private void runProvisioningFailureCalledAfterReachabilityFailureTest(int lossReason) |
| throws Exception { |
| assumeTrue(SdkLevel.isAtLeastT()); |
| connect(); |
| expectRegisterNetworkAgent((agentConfig) -> { }, (cap) -> { }); |
| reset(mWifiNetworkAgent); |
| |
| // Save current IpClientCallbacks instance and verify an immediate IP provisioning |
| // failure event from this instance after IP reachability failure still triggers a |
| // WiFi disconnection due to the race, for example: |
| // - IpClient sends onReachabilityFailure callback, wifi module receives this callback |
| // and post CMD_IP_REACHABILITY_FAILURE, return; |
| // - IpClient sends onProvisioningFailure callback, wifi module receives this callback |
| // and post CMD_IP_CONFIGURATION_LOST, return; |
| // - state machine processes CMD_IP_REACHABILITY_FAILURE first and eventually transition |
| // to WaitBeforeL3ProvisioningState and recreate an IpClient instance there. |
| // - state machine receives CMD_IP_CONFIGURATION_LOST from the old IpClientCallbacks |
| // instanceat at WaitBeforeL3ProvisioningState, but defer the command to its parent state |
| // until transition to L3ProvisioningState. |
| // - CMD_IP_CONFIGURATION_LOST is processed at L2ConnectedState eventually, and trigger |
| // WiFi disconnection there. |
| final IpClientCallbacks callback = mIpClientCallback; |
| final ReachabilityLossInfoParcelable lossInfo = |
| new ReachabilityLossInfoParcelable("", lossReason); |
| callback.onReachabilityFailure(lossInfo); |
| callback.onProvisioningFailure(new LinkProperties()); |
| mLooper.dispatchAll(); |
| verify(mWifiNetworkAgent).unregisterAfterReplacement(anyInt()); |
| verify(mWifiNative, never()).disconnect(WIFI_IFACE_NAME); |
| assertEquals("L3ProvisioningState", getCurrentState().getName()); |
| |
| // Verify that onProvisioningFailure from the current IpClientCallbacks instance |
| // triggers wifi disconnection. |
| mIpClientCallback.onProvisioningFailure(new LinkProperties()); |
| mLooper.dispatchAll(); |
| verify(mWifiNative).disconnect(WIFI_IFACE_NAME); |
| } |
| |
| @Test |
| public void testProvisioningFailureCalledAfterReachabilityFailure_postRoam() |
| throws Exception { |
| runProvisioningFailureCalledAfterReachabilityFailureTest(ReachabilityLossReason.ROAM); |
| } |
| |
| @Test |
| public void testProvisioningFailureCalledAfterReachabilityFailure_rssiConfirm() |
| throws Exception { |
| when(mDeviceConfigFacade.isHandleRssiOrganicKernelFailuresEnabled()).thenReturn(true); |
| |
| runProvisioningFailureCalledAfterReachabilityFailureTest(ReachabilityLossReason.CONFIRM); |
| } |
| |
| @Test |
| public void testProvisioningFailureCalledAfterReachabilityFailure_organicKernel() |
| throws Exception { |
| when(mDeviceConfigFacade.isHandleRssiOrganicKernelFailuresEnabled()).thenReturn(true); |
| |
| runProvisioningFailureCalledAfterReachabilityFailureTest(ReachabilityLossReason.ORGANIC); |
| } |
| |
| @Test |
| public void testIpReachabilityLostAndRoamEventsRace() throws Exception { |
| connect(); |
| expectRegisterNetworkAgent((agentConfig) -> { }, (cap) -> { }); |
| reset(mWifiNetworkAgent); |
| |
| // Trigger ip reachability loss and ensure we trigger a disconnect. |
| mIpClientCallback.onReachabilityLost("CMD_IP_REACHABILITY_LOST"); |
| mLooper.dispatchAll(); |
| verify(mWifiNative).disconnect(WIFI_IFACE_NAME); |
| |
| // Now send a network connection (indicating a roam) event before we get the disconnect |
| // event. |
| mCmi.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, |
| new NetworkConnectionEventInfo(0, TEST_WIFI_SSID, TEST_BSSID_STR, false, null)); |
| mLooper.dispatchAll(); |
| // ensure that we ignored the transient roam while we're disconnecting. |
| verifyNoMoreInteractions(mWifiNetworkAgent); |
| |
| // Now send the disconnect event and ensure that we transition to "DisconnectedState". |
| DisconnectEventInfo disconnectEventInfo = |
| new DisconnectEventInfo(TEST_SSID, TEST_BSSID_STR, 0, false); |
| mCmi.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, disconnectEventInfo); |
| mLooper.dispatchAll(); |
| assertEquals("DisconnectedState", getCurrentState().getName()); |
| verify(mWifiNetworkAgent).unregister(); |
| |
| verifyNoMoreInteractions(mWifiNetworkAgent); |
| } |
| |
| @Test |
| public void testConnectionWhileDisconnecting() throws Exception { |
| connect(); |
| |
| // Trigger a disconnect event. |
| mCmi.disconnect(); |
| mLooper.dispatchAll(); |
| assertEquals("L3ConnectedState", getCurrentState().getName()); |
| |
| // Trigger a new connection before the NETWORK_DISCONNECTION_EVENT comes in. |
| WifiConfiguration config = WifiConfigurationTestUtil.createOpenNetwork(); |
| config.networkId = FRAMEWORK_NETWORK_ID + 1; |
| setupAndStartConnectSequence(config); |
| // Ensure that we triggered the connection attempt. |
| validateSuccessfulConnectSequence(config); |
| |
| // Now trigger the disconnect event for the previous disconnect and ensure we handle it |
| // correctly and remain in ConnectingState. |
| DisconnectEventInfo disconnectEventInfo = |
| new DisconnectEventInfo(mConnectedNetwork.SSID, TEST_BSSID_STR, 0, false); |
| mCmi.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, disconnectEventInfo); |
| mLooper.dispatchAll(); |
| assertEquals("L2ConnectingState", mCmi.getCurrentState().getName()); |
| } |
| |
| @Test |
| public void testConnectionWatchdog() throws Exception { |
| triggerConnect(); |
| Log.i(TAG, "Triggering Connect done"); |
| |
| // Simulate watchdog timeout and ensure we retuned to disconnected state. |
| mLooper.moveTimeForward(ClientModeImpl.CONNECTING_WATCHDOG_TIMEOUT_MS + 5L); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiNative).disableNetwork(WIFI_IFACE_NAME); |
| assertEquals("DisconnectedState", mCmi.getCurrentState().getName()); |
| } |
| |
| @Test |
| public void testRoamAfterConnectDoesNotChangeNetworkInfoInNetworkStateChangeBroadcast() |
| throws Exception { |
| connect(); |
| |
| // The last NETWORK_STATE_CHANGED_ACTION should be to mark the network connected. |
| ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class); |
| |
| verifyNetworkStateChangedBroadcast(atLeastOnce(), intentCaptor); |
| Intent intent = intentCaptor.getValue(); |
| assertNotNull(intent); |
| assertEquals(WifiManager.NETWORK_STATE_CHANGED_ACTION, intent.getAction()); |
| NetworkInfo networkInfo = (NetworkInfo) intent.getExtra(WifiManager.EXTRA_NETWORK_INFO); |
| assertTrue(networkInfo.isConnected()); |
| |
| reset(mContext); |
| when(mContext.getResources()).thenReturn(mResources); |
| when(mContext.createContextAsUser(UserHandle.ALL, 0)).thenReturn(mUserAllContext); |
| |
| // send roam event |
| mCmi.sendMessage(WifiMonitor.ASSOCIATED_BSSID_EVENT, 0, 0, TEST_BSSID_STR1); |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(0, TEST_WIFI_SSID, TEST_BSSID_STR1, sFreq1, |
| SupplicantState.COMPLETED)); |
| mLooper.dispatchAll(); |
| |
| verifyNetworkStateChangedBroadcast(atLeastOnce(), intentCaptor); |
| intent = intentCaptor.getValue(); |
| assertNotNull(intent); |
| assertEquals(WifiManager.NETWORK_STATE_CHANGED_ACTION, intent.getAction()); |
| networkInfo = (NetworkInfo) intent.getExtra(WifiManager.EXTRA_NETWORK_INFO); |
| assertTrue(networkInfo.isConnected()); |
| } |
| |
| |
| /** |
| * Ensure that {@link ClientModeImpl#dump(FileDescriptor, PrintWriter, String[])} |
| * {@link WifiNative#getWifiLinkLayerStats(String)}, at least once before calling |
| * {@link WifiScoreReport#dump(FileDescriptor, PrintWriter, String[])}. |
| * |
| * This ensures that WifiScoreReport will always get updated RSSI and link layer stats before |
| * dumping during a bug report, no matter if the screen is on or not. |
| */ |
| @Test |
| public void testWifiScoreReportDump() throws Exception { |
| when(mWifiNative.getSupportedFeatureSet(WIFI_IFACE_NAME)).thenReturn( |
| WifiManager.WIFI_FEATURE_LINK_LAYER_STATS); |
| InOrder inOrder = inOrder(mWifiNative, mWifiScoreReport); |
| inOrder.verify(mWifiNative, never()).getWifiLinkLayerStats(any()); |
| connect(); |
| |
| mCmi.dump(new FileDescriptor(), new PrintWriter(new StringWriter()), null); |
| mLooper.dispatchAll(); |
| |
| inOrder.verify(mWifiNative, atLeastOnce()).getWifiLinkLayerStats(any()); |
| inOrder.verify(mWifiScoreReport).dump(any(), any(), any()); |
| } |
| |
| @Test |
| public void testHandleScreenChangedDontUpdateLinkLayerStatsWhenDisconnected() { |
| setScreenState(true); |
| setScreenState(false); |
| setScreenState(true); |
| verify(mWifiNative, never()).getWifiLinkLayerStats(any()); |
| } |
| |
| @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, TEST_WIFI_SSID, TEST_BSSID_STR, sFreq, |
| SupplicantState.ASSOCIATED)); |
| mLooper.dispatchAll(); |
| |
| assertTrue(mWifiInfo.isEphemeral()); |
| assertEquals(OP_PACKAGE_NAME, mWifiInfo.getRequestingPackageName()); |
| |
| // fail the connection. |
| DisconnectEventInfo disconnectEventInfo = |
| new DisconnectEventInfo(mConnectedNetwork.SSID, TEST_BSSID_STR, 0, false); |
| mCmi.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, disconnectEventInfo); |
| mLooper.dispatchAll(); |
| |
| assertFalse(mWifiInfo.isEphemeral()); |
| assertNull(mWifiInfo.getRequestingPackageName()); |
| } |
| |
| @Test |
| public void handleAssociationRejectionWhenRoaming() throws Exception { |
| connect(); |
| |
| assertTrue(SupplicantState.isConnecting(mWifiInfo.getSupplicantState())); |
| |
| when(mWifiNative.roamToNetwork(any(), any())).thenReturn(true); |
| |
| // Trigger roam to a BSSID. |
| mCmi.startRoamToNetwork(FRAMEWORK_NETWORK_ID, TEST_BSSID_STR1); |
| mLooper.dispatchAll(); |
| |
| |
| assertEquals(TEST_BSSID_STR1, mCmi.getConnectingBssid()); |
| assertEquals(FRAMEWORK_NETWORK_ID, mCmi.getConnectingWifiConfiguration().networkId); |
| |
| verify(mWifiNative).roamToNetwork(any(), any()); |
| assertEquals("RoamingState", getCurrentState().getName()); |
| |
| // fail the connection. |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(0, TEST_WIFI_SSID, TEST_BSSID_STR, 0, |
| SupplicantState.DISCONNECTED)); |
| mLooper.dispatchAll(); |
| |
| // Ensure we reset WifiInfo fields. |
| assertFalse(SupplicantState.isConnecting(mWifiInfo.getSupplicantState())); |
| } |
| |
| @Test |
| public void testOemPaidNetworkCapability() throws Exception { |
| // oemPaid introduced in S, not applicable to R |
| assumeTrue(SdkLevel.isAtLeastS()); |
| mConnectedNetwork.oemPaid = true; |
| connect(); |
| expectRegisterNetworkAgent((agentConfig) -> { }, |
| (cap) -> { |
| assertTrue(cap.hasCapability(NetworkCapabilities.NET_CAPABILITY_OEM_PAID)); |
| assertFalse(cap.hasCapability(NetworkCapabilities |
| .NET_CAPABILITY_NOT_RESTRICTED)); |
| }); |
| } |
| @Test |
| public void testNotOemPaidNetworkCapability() throws Exception { |
| // oemPaid introduced in S, not applicable to R |
| assumeTrue(SdkLevel.isAtLeastS()); |
| mConnectedNetwork.oemPaid = false; |
| connect(); |
| expectRegisterNetworkAgent((agentConfig) -> { }, |
| (cap) -> { |
| assertFalse(cap.hasCapability(NetworkCapabilities.NET_CAPABILITY_OEM_PAID)); |
| assertTrue(cap.hasCapability(NetworkCapabilities |
| .NET_CAPABILITY_NOT_RESTRICTED)); |
| }); |
| } |
| |
| @Test |
| public void testRestrictedetworkCapability() throws Exception { |
| // oemPaid introduced in S, not applicable to R |
| assumeTrue(SdkLevel.isAtLeastS()); |
| mConnectedNetwork.restricted = true; |
| connect(); |
| expectRegisterNetworkAgent((agentConfig) -> { }, |
| (cap) -> { |
| assertFalse(cap.hasCapability(NetworkCapabilities |
| .NET_CAPABILITY_NOT_RESTRICTED)); |
| }); |
| } |
| |
| @Test |
| public void testOemPrivateNetworkCapability() throws Exception { |
| // oemPrivate introduced in S, not applicable to R |
| assumeTrue(SdkLevel.isAtLeastS()); |
| mConnectedNetwork.oemPrivate = true; |
| connect(); |
| expectRegisterNetworkAgent((agentConfig) -> { }, |
| (cap) -> { |
| assertTrue(cap.hasCapability(NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE)); |
| assertFalse(cap.hasCapability(NetworkCapabilities |
| .NET_CAPABILITY_NOT_RESTRICTED)); |
| }); |
| } |
| |
| @Test |
| public void testNotOemPrivateNetworkCapability() throws Exception { |
| // oemPrivate introduced in S, not applicable to R |
| assumeTrue(SdkLevel.isAtLeastS()); |
| mConnectedNetwork.oemPrivate = false; |
| connect(); |
| expectRegisterNetworkAgent((agentConfig) -> { }, |
| (cap) -> { |
| assertFalse(cap.hasCapability(NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE)); |
| assertTrue(cap.hasCapability(NetworkCapabilities |
| .NET_CAPABILITY_NOT_RESTRICTED)); |
| }); |
| } |
| |
| @Test |
| public void testSendLinkProbeFailure() throws Exception { |
| mCmi.probeLink(mLinkProbeCallback, -1); |
| |
| verify(mLinkProbeCallback).onFailure(LinkProbeCallback.LINK_PROBE_ERROR_NOT_CONNECTED); |
| verify(mLinkProbeCallback, never()).onAck(anyInt()); |
| verify(mWifiNative, never()).probeLink(any(), any(), any(), anyInt()); |
| } |
| |
| @Test |
| public void testSendLinkProbeSuccess() throws Exception { |
| connect(); |
| |
| mCmi.probeLink(mLinkProbeCallback, -1); |
| |
| verify(mWifiNative).probeLink(any(), any(), eq(mLinkProbeCallback), eq(-1)); |
| verify(mLinkProbeCallback, never()).onFailure(anyInt()); |
| verify(mLinkProbeCallback, never()).onAck(anyInt()); |
| } |
| |
| private void setupPasspointConnection() throws Exception { |
| mConnectedNetwork = spy(WifiConfigurationTestUtil.createPasspointNetwork()); |
| mConnectedNetwork.carrierId = CARRIER_ID_1; |
| when(mWifiCarrierInfoManager.getBestMatchSubscriptionId(any(WifiConfiguration.class))) |
| .thenReturn(DATA_SUBID); |
| when(mWifiCarrierInfoManager.isSimReady(DATA_SUBID)).thenReturn(true); |
| mConnectedNetwork.enterpriseConfig.setAnonymousIdentity(""); |
| triggerConnect(); |
| |
| when(mWifiConfigManager.getConfiguredNetwork(anyInt())).thenReturn(mConnectedNetwork); |
| when(mWifiConfigManager.getScanDetailCacheForNetwork(FRAMEWORK_NETWORK_ID)) |
| .thenReturn(mScanDetailCache); |
| when(mScanRequestProxy.getScanResult(TEST_BSSID_STR)).thenReturn( |
| getGoogleGuestScanDetail(TEST_RSSI, TEST_BSSID_STR, sFreq).getScanResult()); |
| when(mScanDetailCache.getScanDetail(TEST_BSSID_STR)).thenReturn( |
| getGoogleGuestScanDetail(TEST_RSSI, TEST_BSSID_STR, sFreq)); |
| when(mScanDetailCache.getScanResult(TEST_BSSID_STR)).thenReturn( |
| getGoogleGuestScanDetail(TEST_RSSI, TEST_BSSID_STR, sFreq).getScanResult()); |
| |
| WifiSsid wifiSsid = WifiSsid.fromBytes( |
| NativeUtil.byteArrayFromArrayList(NativeUtil.decodeSsid(mConnectedNetwork.SSID))); |
| mCmi.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, |
| new NetworkConnectionEventInfo(0, wifiSsid, TEST_BSSID_STR, false, null)); |
| mLooper.dispatchAll(); |
| assertEquals("L3ProvisioningState", getCurrentState().getName()); |
| } |
| |
| /** |
| * When connecting to a Passpoint network, verify that the Venue URL ANQP request is sent. |
| */ |
| @Test |
| public void testVenueUrlRequestForPasspointNetworks() throws Exception { |
| setupPasspointConnection(); |
| verify(mPasspointManager).requestVenueUrlAnqpElement(any(ScanResult.class)); |
| assertEquals("L3ProvisioningState", getCurrentState().getName()); |
| } |
| |
| /** |
| * Verify that the Venue URL ANQP request is not sent for non-Passpoint EAP networks |
| */ |
| @Test |
| public void testVenueUrlNotRequestedForNonPasspointNetworks() throws Exception { |
| setupEapSimConnection(); |
| verify(mPasspointManager, never()).requestVenueUrlAnqpElement(any(ScanResult.class)); |
| assertEquals("L3ProvisioningState", getCurrentState().getName()); |
| } |
| |
| @Test |
| public void testFirmwareRoam() throws Exception { |
| connect(); |
| |
| // Now send a network connection (indicating a roam) event |
| mCmi.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, |
| new NetworkConnectionEventInfo(0, TEST_WIFI_SSID, TEST_BSSID_STR1, false, null)); |
| mLooper.dispatchAll(); |
| |
| verifyNetworkStateChangedBroadcast(times(2), |
| new NetworkStateChangedIntentMatcher(CONNECTED)); |
| } |
| |
| @Test |
| public void testFirmwareRoamDisconnectSecondaryInternetOnSameBand() throws Exception { |
| connect(); |
| // After connection, the primary should be on 2.4Ghz. |
| assertTrue(mWifiInfo.is24GHz()); |
| |
| // Verify no disconnection of secondary if it's on another band. |
| ConcreteClientModeManager secondaryCmm = mock(ConcreteClientModeManager.class); |
| WifiInfo secondaryWifiInfo = mock(WifiInfo.class); |
| when(secondaryWifiInfo.is24GHz()).thenReturn(false); |
| when(secondaryWifiInfo.is5GHz()).thenReturn(true); |
| when(secondaryCmm.isConnected()).thenReturn(true); |
| when(secondaryCmm.isSecondaryInternet()).thenReturn(true); |
| when(secondaryCmm.getConnectionInfo()).thenReturn(secondaryWifiInfo); |
| when(mActiveModeWarden.getClientModeManagerInRole(ROLE_CLIENT_SECONDARY_LONG_LIVED)) |
| .thenReturn(secondaryCmm); |
| |
| // Now send a network connection (indicating a roam) event, and verify no disconnection |
| // of secondary |
| mCmi.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, |
| new NetworkConnectionEventInfo(0, TEST_WIFI_SSID, TEST_BSSID_STR1, false, null)); |
| mLooper.dispatchAll(); |
| |
| assertTrue(mWifiInfo.is24GHz()); |
| verify(secondaryCmm, never()).disconnect(); |
| |
| // Set the secondary to the same band as primary and then verify a disconnect after the |
| // primary roams. |
| when(secondaryWifiInfo.is24GHz()).thenReturn(true); |
| when(secondaryWifiInfo.is5GHz()).thenReturn(false); |
| mCmi.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, |
| new NetworkConnectionEventInfo(0, TEST_WIFI_SSID, TEST_BSSID_STR1, false, null)); |
| mLooper.dispatchAll(); |
| |
| assertTrue(mWifiInfo.is24GHz()); |
| verify(secondaryCmm).disconnect(); |
| } |
| |
| @Test |
| public void testProvisioningUpdateAfterConnect() throws Exception { |
| connect(); |
| |
| // Trigger a IP params update (maybe a dhcp lease renewal). |
| 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(); |
| |
| verifyNetworkStateChangedBroadcast(times(2), |
| new NetworkStateChangedIntentMatcher(CONNECTED)); |
| } |
| |
| /** |
| * Verify that the Deauth-Imminent WNM-Notification is handled by relaying to the Passpoint |
| * Manager. |
| */ |
| @Test |
| public void testHandlePasspointDeauthImminentWnmNotification() throws Exception { |
| setupEapSimConnection(); |
| WnmData wnmData = WnmData.createDeauthImminentEvent(TEST_BSSID, "", false, |
| TEST_DELAY_IN_SECONDS); |
| mCmi.sendMessage(WifiMonitor.HS20_DEAUTH_IMMINENT_EVENT, 0, 0, wnmData); |
| mLooper.dispatchAll(); |
| verify(mPasspointManager).handleDeauthImminentEvent(eq(wnmData), |
| any(WifiConfiguration.class)); |
| } |
| |
| /** |
| * Verify that the network selection status will be updated and the function onEapFailure() |
| * in EapFailureNotifier is called when a EAP Authentication failure is detected |
| * with carrier erroe code. |
| */ |
| @Test |
| public void testCarrierEapFailure() throws Exception { |
| initializeAndAddNetworkAndVerifySuccess(); |
| WifiBlocklistMonitor.CarrierSpecificEapFailureConfig eapFailureConfig = |
| new WifiBlocklistMonitor.CarrierSpecificEapFailureConfig(1, -1, true); |
| |
| startConnectSuccess(); |
| |
| WifiConfiguration config = new WifiConfiguration(); |
| config.SSID = TEST_SSID; |
| config.getNetworkSelectionStatus().setHasEverConnected(true); |
| config.enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.SIM); |
| when(mWifiConfigManager.getConfiguredNetwork(anyInt())).thenReturn(config); |
| when(mEapFailureNotifier.onEapFailure(anyInt(), eq(config), anyBoolean())).thenReturn( |
| eapFailureConfig); |
| |
| mCmi.sendMessage(WifiMonitor.AUTHENTICATION_FAILURE_EVENT, |
| new AuthenticationFailureEventInfo(TEST_SSID, MacAddress.fromString(TEST_BSSID_STR), |
| WifiManager.ERROR_AUTH_FAILURE_EAP_FAILURE, DEFINED_ERROR_CODE) |
| ); |
| mLooper.dispatchAll(); |
| |
| verify(mEapFailureNotifier).onEapFailure(DEFINED_ERROR_CODE, config, true); |
| verify(mWifiBlocklistMonitor).loadCarrierConfigsForDisableReasonInfos(eapFailureConfig); |
| verify(mWifiConfigManager).updateNetworkSelectionStatus(anyInt(), |
| eq(WifiConfiguration.NetworkSelectionStatus |
| .DISABLED_AUTHENTICATION_PRIVATE_EAP_ERROR)); |
| } |
| |
| /** |
| * When connected to a Passpoint network, verify that the Venue URL and T&C URL are updated in |
| * the {@link LinkProperties} object when provisioning complete and when link properties change |
| * events are received. |
| */ |
| @Test |
| public void testVenueAndTCUrlsUpdateForPasspointNetworks() throws Exception { |
| // This tests new S functionality/APIs, not applicable to R. |
| assumeTrue(SdkLevel.isAtLeastS()); |
| setupPasspointConnection(); |
| when(mPasspointManager.getVenueUrl(any(ScanResult.class))).thenReturn(new URL(VENUE_URL)); |
| WnmData wnmData = WnmData.createTermsAndConditionsAccetanceRequiredEvent(TEST_BSSID, |
| TEST_TERMS_AND_CONDITIONS_URL); |
| when(mPasspointManager.handleTermsAndConditionsEvent(eq(wnmData), |
| any(WifiConfiguration.class))).thenReturn(new URL(TEST_TERMS_AND_CONDITIONS_URL)); |
| mCmi.sendMessage(WifiMonitor.HS20_TERMS_AND_CONDITIONS_ACCEPTANCE_REQUIRED_EVENT, |
| 0, 0, wnmData); |
| 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); |
| mCmi.mNetworkAgent = null; |
| mLooper.dispatchAll(); |
| LinkProperties linkProperties = mock(LinkProperties.class); |
| mIpClientCallback.onLinkPropertiesChange(linkProperties); |
| mLooper.dispatchAll(); |
| verify(mPasspointManager, times(2)).getVenueUrl(any(ScanResult.class)); |
| final ArgumentCaptor<CaptivePortalData> captivePortalDataCaptor = |
| ArgumentCaptor.forClass(CaptivePortalData.class); |
| verify(linkProperties).setCaptivePortalData(captivePortalDataCaptor.capture()); |
| assertEquals(WifiConfigurationTestUtil.TEST_PROVIDER_FRIENDLY_NAME, |
| captivePortalDataCaptor.getValue().getVenueFriendlyName()); |
| assertEquals(VENUE_URL, captivePortalDataCaptor.getValue().getVenueInfoUrl().toString()); |
| assertEquals(TEST_TERMS_AND_CONDITIONS_URL, captivePortalDataCaptor.getValue() |
| .getUserPortalUrl().toString()); |
| } |
| |
| /** |
| * Verify that the T&C WNM-Notification is handled by relaying to the Passpoint |
| * Manager. |
| */ |
| @Test |
| public void testHandlePasspointTermsAndConditionsWnmNotification() throws Exception { |
| setupEapSimConnection(); |
| WnmData wnmData = WnmData.createTermsAndConditionsAccetanceRequiredEvent(TEST_BSSID, |
| TEST_TERMS_AND_CONDITIONS_URL); |
| when(mPasspointManager.handleTermsAndConditionsEvent(eq(wnmData), |
| any(WifiConfiguration.class))).thenReturn(new URL(TEST_TERMS_AND_CONDITIONS_URL)); |
| mCmi.sendMessage(WifiMonitor.HS20_TERMS_AND_CONDITIONS_ACCEPTANCE_REQUIRED_EVENT, |
| 0, 0, wnmData); |
| mLooper.dispatchAll(); |
| verify(mPasspointManager).handleTermsAndConditionsEvent(eq(wnmData), |
| any(WifiConfiguration.class)); |
| verify(mWifiNative, never()).disconnect(anyString()); |
| } |
| |
| /** |
| * Verify that when a bad URL is received in the T&C WNM-Notification, the connection is |
| * disconnected. |
| */ |
| @Test |
| public void testHandlePasspointTermsAndConditionsWnmNotificationWithBadUrl() throws Exception { |
| setupEapSimConnection(); |
| WnmData wnmData = WnmData.createTermsAndConditionsAccetanceRequiredEvent(TEST_BSSID, |
| TEST_TERMS_AND_CONDITIONS_URL); |
| when(mPasspointManager.handleTermsAndConditionsEvent(eq(wnmData), |
| any(WifiConfiguration.class))).thenReturn(null); |
| mCmi.sendMessage(WifiMonitor.HS20_TERMS_AND_CONDITIONS_ACCEPTANCE_REQUIRED_EVENT, |
| 0, 0, wnmData); |
| mLooper.dispatchAll(); |
| verify(mPasspointManager).handleTermsAndConditionsEvent(eq(wnmData), |
| any(WifiConfiguration.class)); |
| verify(mWifiNative).disconnect(eq(WIFI_IFACE_NAME)); |
| verify(mWifiMetrics).logStaEvent(anyString(), eq(StaEvent.TYPE_FRAMEWORK_DISCONNECT), |
| eq(StaEvent.DISCONNECT_PASSPOINT_TAC)); |
| } |
| |
| private void verifyTransitionDisableEvent(String caps, int indication, boolean shouldUpdate) |
| throws Exception { |
| final int networkId = FRAMEWORK_NETWORK_ID; |
| ScanResult scanResult = new ScanResult(WifiSsid.fromUtf8Text(sFilsSsid), |
| sFilsSsid, TEST_BSSID_STR, 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}; |
| when(mScanRequestProxy.getScanResults()).thenReturn(Arrays.asList(scanResult)); |
| when(mScanRequestProxy.getScanResult(eq(TEST_BSSID_STR))).thenReturn(scanResult); |
| |
| initializeAndAddNetworkAndVerifySuccess(); |
| |
| startConnectSuccess(); |
| |
| mCmi.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, |
| new NetworkConnectionEventInfo(0, TEST_WIFI_SSID, TEST_BSSID_STR, false, null)); |
| mLooper.dispatchAll(); |
| |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(0, TEST_WIFI_SSID, TEST_BSSID_STR, sFreq, |
| SupplicantState.COMPLETED)); |
| mLooper.dispatchAll(); |
| |
| assertEquals("L3ProvisioningState", getCurrentState().getName()); |
| |
| mCmi.sendMessage(WifiMonitor.TRANSITION_DISABLE_INDICATION, |
| networkId, indication); |
| mLooper.dispatchAll(); |
| |
| if (shouldUpdate) { |
| verify(mWifiConfigManager).updateNetworkTransitionDisable( |
| eq(networkId), eq(indication)); |
| } else { |
| verify(mWifiConfigManager, never()).updateNetworkTransitionDisable( |
| anyInt(), anyInt()); |
| } |
| } |
| |
| /** |
| * Verify that the Transition Disable event is routed correctly. |
| */ |
| @Test |
| public void testUpdateSaeOnlyTransitionDisableIndicationFromPskSaeBss() throws Exception { |
| String caps = "[PSK][SAE]"; |
| int indication = WifiMonitor.TDI_USE_WPA3_PERSONAL; |
| boolean shouldUpdate = true; |
| verifyTransitionDisableEvent(caps, indication, shouldUpdate); |
| } |
| |
| /** |
| * Verify that the Transition Disable event is routed correctly. |
| */ |
| @Test |
| public void testUpdateSaeOnlyTransitionDisableIndicationFromSaeBss() throws Exception { |
| String caps = "[SAE]"; |
| int indication = WifiMonitor.TDI_USE_WPA3_PERSONAL; |
| boolean shouldUpdate = true; |
| verifyTransitionDisableEvent(caps, indication, shouldUpdate); |
| } |
| |
| /** |
| * Verify that the Transition Disable event is routed correctly. |
| */ |
| @Test |
| public void testDropSaeOnlyTransitionDisableIndicationFromPskBss() throws Exception { |
| String caps = "[PSK]"; |
| int indication = WifiMonitor.TDI_USE_WPA3_PERSONAL; |
| boolean shouldUpdate = false; |
| verifyTransitionDisableEvent(caps, indication, shouldUpdate); |
| } |
| |
| /** |
| * Verify that the Transition Disable event is routed correctly. |
| */ |
| @Test |
| public void testUpdateSaePkTransitionDisableIndicationFromPskSaeBss() throws Exception { |
| String caps = "[PSK][SAE]"; |
| int indication = WifiMonitor.TDI_USE_SAE_PK; |
| boolean shouldUpdate = true; |
| verifyTransitionDisableEvent(caps, indication, shouldUpdate); |
| } |
| |
| /** |
| * Verify that the Transition Disable event is routed correctly. |
| */ |
| @Test |
| public void testUpdateSaePkTransitionDisableIndicationFromSaeBss() throws Exception { |
| String caps = "[SAE]"; |
| int indication = WifiMonitor.TDI_USE_SAE_PK; |
| boolean shouldUpdate = true; |
| verifyTransitionDisableEvent(caps, indication, shouldUpdate); |
| } |
| |
| /** |
| * Verify that the Transition Disable event is routed correctly. |
| */ |
| @Test |
| public void testDropSaePkTransitionDisableIndicationFromPskBss() throws Exception { |
| String caps = "[PSK]"; |
| int indication = WifiMonitor.TDI_USE_SAE_PK; |
| boolean shouldUpdate = false; |
| verifyTransitionDisableEvent(caps, indication, shouldUpdate); |
| } |
| |
| /** |
| * Verify that the Transition Disable event is routed correctly. |
| */ |
| @Test |
| public void testUpdateOweOnlyTransitionDisableIndicationFromOpenOweBss() throws Exception { |
| String caps = "[OWE_TRANSITION]"; |
| int indication = WifiMonitor.TDI_USE_ENHANCED_OPEN; |
| boolean shouldUpdate = true; |
| verifyTransitionDisableEvent(caps, indication, shouldUpdate); |
| } |
| |
| /** |
| * Verify that the Transition Disable event is routed correctly. |
| */ |
| @Test |
| public void testUpdateOweOnlyTransitionDisableIndicationFromOweBss() throws Exception { |
| String caps = "[OWE]"; |
| int indication = WifiMonitor.TDI_USE_ENHANCED_OPEN; |
| boolean shouldUpdate = true; |
| verifyTransitionDisableEvent(caps, indication, shouldUpdate); |
| } |
| |
| /** |
| * Verify that the Transition Disable event is routed correctly. |
| */ |
| @Test |
| public void testDropOweOnlyTransitionDisableIndicationFromOpenBss() throws Exception { |
| String caps = ""; |
| int indication = WifiMonitor.TDI_USE_ENHANCED_OPEN; |
| boolean shouldUpdate = false; |
| verifyTransitionDisableEvent(caps, indication, shouldUpdate); |
| } |
| |
| /** |
| * Verify that the Transition Disable event is routed correctly. |
| */ |
| @Test |
| public void testUpdateWpa3EnterpriseTransitionDisableIndicationFromTransitionBss() |
| throws Exception { |
| String caps = "[EAP/SHA1-EAP/SHA256][RSN][MFPC]"; |
| int indication = WifiMonitor.TDI_USE_WPA3_ENTERPRISE; |
| boolean shouldUpdate = true; |
| verifyTransitionDisableEvent(caps, indication, shouldUpdate); |
| } |
| |
| /** |
| * Verify that the Transition Disable event is routed correctly. |
| */ |
| @Test |
| public void testUpdateWpa3EnterpriseTransitionDisableIndicationFromWpa3EnterpriseBss() |
| throws Exception { |
| String caps = "[EAP/SHA256][RSN][MFPC][MFPR]"; |
| int indication = WifiMonitor.TDI_USE_WPA3_ENTERPRISE; |
| boolean shouldUpdate = true; |
| verifyTransitionDisableEvent(caps, indication, shouldUpdate); |
| } |
| |
| /** |
| * Verify that the Transition Disable event is routed correctly. |
| */ |
| @Test |
| public void testDropWpa3EnterpriseTransitionDisableIndicationFromWpa2EnterpriseBss() |
| throws Exception { |
| String caps = "[EAP/SHA1]"; |
| int indication = WifiMonitor.TDI_USE_WPA3_ENTERPRISE; |
| boolean shouldUpdate = false; |
| verifyTransitionDisableEvent(caps, indication, shouldUpdate); |
| } |
| |
| /** |
| * Verify that the network selection status will be updated with DISABLED_NETWORK_NOT_FOUND |
| * when number of NETWORK_NOT_FOUND_EVENT event reaches the threshold. |
| */ |
| @Test |
| public void testNetworkNotFoundEventUpdatesAssociationFailureStatus() |
| throws Exception { |
| assumeTrue(SdkLevel.isAtLeastS()); |
| initializeAndAddNetworkAndVerifySuccess(); |
| mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, 0, 0, TEST_BSSID_STR); |
| for (int i = 0; i < mWifiGlobals.getNetworkNotFoundEventThreshold(); i++) { |
| mCmi.sendMessage(WifiMonitor.NETWORK_NOT_FOUND_EVENT, DEFAULT_TEST_SSID); |
| } |
| mLooper.dispatchAll(); |
| verify(mWifiConfigManager).updateNetworkSelectionStatus(anyInt(), |
| eq(WifiConfiguration.NetworkSelectionStatus.DISABLED_NETWORK_NOT_FOUND)); |
| verify(mWifiConfigManager).setRecentFailureAssociationStatus(anyInt(), |
| eq(WifiConfiguration.RECENT_FAILURE_NETWORK_NOT_FOUND)); |
| |
| verify(mWifiDiagnostics).reportConnectionEvent( |
| eq(WifiDiagnostics.CONNECTION_EVENT_FAILED), any()); |
| verify(mWifiConnectivityManager).handleConnectionAttemptEnded( |
| mClientModeManager, |
| WifiMetrics.ConnectionEvent.FAILURE_NETWORK_NOT_FOUND, |
| WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, TEST_BSSID_STR, |
| mTestConfig); |
| verify(mWifiNetworkFactory).handleConnectionAttemptEnded( |
| eq(WifiMetrics.ConnectionEvent.FAILURE_NETWORK_NOT_FOUND), |
| eq(mTestConfig), eq(TEST_BSSID_STR), |
| eq(WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN)); |
| verify(mWifiNetworkSuggestionsManager).handleConnectionAttemptEnded( |
| eq(WifiMetrics.ConnectionEvent.FAILURE_NETWORK_NOT_FOUND), |
| eq(mTestConfig), eq(null)); |
| verify(mWifiMetrics, never()) |
| .incrementNumBssidDifferentSelectionBetweenFrameworkAndFirmware(); |
| verifyConnectionEventTimeoutDoesNotOccur(); |
| |
| clearInvocations(mWifiDiagnostics, mWifiConfigManager, mWifiNetworkFactory, |
| mWifiNetworkSuggestionsManager); |
| |
| // Now trigger a disconnect event from supplicant, this should be ignored since the |
| // connection tracking should have already ended. |
| mCmi.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, |
| new DisconnectEventInfo(TEST_SSID, TEST_BSSID_STR, 0, false)); |
| mLooper.dispatchAll(); |
| |
| verifyNoMoreInteractions(mWifiDiagnostics, mWifiConfigManager, mWifiNetworkFactory, |
| mWifiNetworkSuggestionsManager); |
| } |
| |
| /** |
| * Verify that the subscriberId will be filled in NetworkAgentConfig |
| * after connecting to a merged network. And also VCN policy will be checked. |
| */ |
| @Test |
| public void triggerConnectToMergedNetwork() throws Exception { |
| assumeTrue(SdkLevel.isAtLeastS()); |
| VcnManager vcnManager = mock(VcnManager.class); |
| VcnNetworkPolicyResult vcnUnderlyingNetworkPolicy = mock(VcnNetworkPolicyResult.class); |
| when(mContext.getSystemService(VcnManager.class)).thenReturn(vcnManager); |
| ArgumentCaptor<VcnManager.VcnNetworkPolicyChangeListener> policyChangeListenerCaptor = |
| ArgumentCaptor.forClass(VcnManager.VcnNetworkPolicyChangeListener.class); |
| InOrder inOrder = inOrder(vcnManager, vcnUnderlyingNetworkPolicy); |
| doAnswer(new AnswerWithArguments() { |
| public VcnNetworkPolicyResult answer(NetworkCapabilities networkCapabilities, |
| LinkProperties linkProperties) throws Exception { |
| networkCapabilities.removeCapability( |
| NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED); |
| when(vcnUnderlyingNetworkPolicy.getNetworkCapabilities()) |
| .thenReturn(networkCapabilities); |
| return vcnUnderlyingNetworkPolicy; |
| } |
| }).when(vcnManager).applyVcnNetworkPolicy(any(), any()); |
| when(vcnUnderlyingNetworkPolicy.isTeardownRequested()).thenReturn(false); |
| |
| String testSubscriberId = "TestSubscriberId"; |
| when(mTelephonyManager.createForSubscriptionId(anyInt())).thenReturn(mDataTelephonyManager); |
| when(mDataTelephonyManager.getSubscriberId()).thenReturn(testSubscriberId); |
| mConnectedNetwork.carrierMerged = true; |
| mConnectedNetwork.subscriptionId = DATA_SUBID; |
| connect(); |
| expectRegisterNetworkAgent((agentConfig) -> { |
| assertEquals(testSubscriberId, agentConfig.subscriberId); |
| }, (cap) -> { |
| assertFalse(cap.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED)); |
| assertEquals(Collections.singleton(DATA_SUBID), cap.getSubscriptionIds()); |
| }); |
| // Verify VCN policy listener is registered |
| inOrder.verify(vcnManager).addVcnNetworkPolicyChangeListener(any(), |
| policyChangeListenerCaptor.capture()); |
| assertNotNull(policyChangeListenerCaptor.getValue()); |
| |
| // Verify getting new capability from VcnManager |
| inOrder.verify(vcnManager).applyVcnNetworkPolicy(any(NetworkCapabilities.class), |
| any(LinkProperties.class)); |
| inOrder.verify(vcnUnderlyingNetworkPolicy).isTeardownRequested(); |
| inOrder.verify(vcnUnderlyingNetworkPolicy).getNetworkCapabilities(); |
| |
| // Update policy with tear down request. |
| when(vcnUnderlyingNetworkPolicy.isTeardownRequested()).thenReturn(true); |
| policyChangeListenerCaptor.getValue().onPolicyChanged(); |
| mLooper.dispatchAll(); |
| |
| // The merged carrier network should be disconnected. |
| inOrder.verify(vcnManager).applyVcnNetworkPolicy(any(NetworkCapabilities.class), |
| any(LinkProperties.class)); |
| inOrder.verify(vcnUnderlyingNetworkPolicy).isTeardownRequested(); |
| inOrder.verify(vcnUnderlyingNetworkPolicy).getNetworkCapabilities(); |
| verify(mWifiNative).disconnect(WIFI_IFACE_NAME); |
| verify(mWifiMetrics).logStaEvent(anyString(), eq(StaEvent.TYPE_FRAMEWORK_DISCONNECT), |
| eq(StaEvent.DISCONNECT_VCN_REQUEST)); |
| DisconnectEventInfo disconnectEventInfo = |
| new DisconnectEventInfo(TEST_SSID, TEST_BSSID_STR, 0, false); |
| mCmi.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, disconnectEventInfo); |
| mLooper.dispatchAll(); |
| assertEquals("DisconnectedState", getCurrentState().getName()); |
| |
| // In DisconnectedState, policy update should result no capability update. |
| reset(mWifiConfigManager, vcnManager); |
| policyChangeListenerCaptor.getValue().onPolicyChanged(); |
| verifyNoMoreInteractions(mWifiConfigManager, vcnManager); |
| } |
| |
| /** |
| * Verify when connect to a unmerged network, will not mark it as a VCN network. |
| */ |
| @Test |
| public void triggerConnectToUnmergedNetwork() throws Exception { |
| assumeTrue(SdkLevel.isAtLeastS()); |
| VcnManager vcnManager = mock(VcnManager.class); |
| when(mContext.getSystemService(VcnManager.class)).thenReturn(vcnManager); |
| VcnNetworkPolicyResult vcnUnderlyingNetworkPolicy = mock(VcnNetworkPolicyResult.class); |
| ArgumentCaptor<VcnManager.VcnNetworkPolicyChangeListener> policyChangeListenerCaptor = |
| ArgumentCaptor.forClass(VcnManager.VcnNetworkPolicyChangeListener.class); |
| doAnswer(new AnswerWithArguments() { |
| public VcnNetworkPolicyResult answer(NetworkCapabilities networkCapabilities, |
| LinkProperties linkProperties) throws Exception { |
| networkCapabilities.removeCapability( |
| NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED); |
| when(vcnUnderlyingNetworkPolicy.getNetworkCapabilities()) |
| .thenReturn(networkCapabilities); |
| return vcnUnderlyingNetworkPolicy; |
| } |
| }).when(vcnManager).applyVcnNetworkPolicy(any(), any()); |
| when(vcnUnderlyingNetworkPolicy.isTeardownRequested()).thenReturn(false); |
| |
| String testSubscriberId = "TestSubscriberId"; |
| when(mTelephonyManager.createForSubscriptionId(anyInt())).thenReturn(mDataTelephonyManager); |
| when(mDataTelephonyManager.getSubscriberId()).thenReturn(testSubscriberId); |
| connect(); |
| expectRegisterNetworkAgent((agentConfig) -> { |
| assertEquals(null, agentConfig.subscriberId); |
| }, (cap) -> { |
| assertTrue(cap.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED)); |
| assertTrue(cap.getSubscriptionIds().isEmpty()); |
| }); |
| |
| // Verify VCN policy listener is registered |
| verify(vcnManager).addVcnNetworkPolicyChangeListener(any(), |
| policyChangeListenerCaptor.capture()); |
| assertNotNull(policyChangeListenerCaptor.getValue()); |
| |
| policyChangeListenerCaptor.getValue().onPolicyChanged(); |
| mLooper.dispatchAll(); |
| |
| verifyNoMoreInteractions(vcnManager, vcnUnderlyingNetworkPolicy); |
| } |
| |
| /** |
| * Verifies that we trigger a disconnect when the {@link WifiConfigManager}. |
| * OnNetworkUpdateListener#onNetworkRemoved(WifiConfiguration)} is invoked. |
| */ |
| @Test |
| public void testOnCarrierOffloadDisabled() throws Exception { |
| mConnectedNetwork.subscriptionId = DATA_SUBID; |
| connect(); |
| |
| mOffloadDisabledListenerArgumentCaptor.getValue() |
| .onCarrierOffloadDisabled(DATA_SUBID, false); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiNative).disconnect(WIFI_IFACE_NAME); |
| verify(mWifiMetrics).logStaEvent(anyString(), eq(StaEvent.TYPE_FRAMEWORK_DISCONNECT), |
| eq(StaEvent.DISCONNECT_CARRIER_OFFLOAD_DISABLED)); |
| verify(mWifiConnectivityManager).clearCachedCandidates(); |
| } |
| |
| @Test |
| public void testPacketFilter() throws Exception { |
| connect(); |
| |
| verify(mIpClient).startProvisioning(mProvisioningConfigurationCaptor.capture()); |
| assertEquals(APF_CAP, mProvisioningConfigurationCaptor.getValue().apfCapabilities); |
| |
| byte[] filter = new byte[20]; |
| new Random().nextBytes(filter); |
| mIpClientCallback.installPacketFilter(filter); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiNative).installPacketFilter(WIFI_IFACE_NAME, filter); |
| |
| when(mWifiNative.readPacketFilter(WIFI_IFACE_NAME)).thenReturn(filter); |
| mIpClientCallback.startReadPacketFilter(); |
| mLooper.dispatchAll(); |
| verify(mIpClient).readPacketFilterComplete(filter); |
| verify(mWifiNative).readPacketFilter(WIFI_IFACE_NAME); |
| } |
| |
| @Test |
| public void testPacketFilterOnSecondarySupported() throws Exception { |
| mResources.setBoolean(R.bool.config_wifiEnableApfOnNonPrimarySta, true); |
| when(mClientModeManager.getRole()).thenReturn(ROLE_CLIENT_SECONDARY_TRANSIENT); |
| connect(); |
| |
| verify(mIpClient).startProvisioning(mProvisioningConfigurationCaptor.capture()); |
| assertEquals(APF_CAP, mProvisioningConfigurationCaptor.getValue().apfCapabilities); |
| } |
| |
| @Test |
| public void testPacketFilterOnSecondaryNotSupported() throws Exception { |
| mResources.setBoolean(R.bool.config_wifiEnableApfOnNonPrimarySta, false); |
| when(mClientModeManager.getRole()).thenReturn(ROLE_CLIENT_SECONDARY_TRANSIENT); |
| connect(); |
| |
| verify(mIpClient).startProvisioning(mProvisioningConfigurationCaptor.capture()); |
| assertNull(mProvisioningConfigurationCaptor.getValue().apfCapabilities); |
| } |
| |
| @Test |
| public void testPacketFilterOnRoleChangeOnSecondaryCmm() throws Exception { |
| when(mClientModeManager.getRole()).thenReturn(ROLE_CLIENT_SECONDARY_TRANSIENT); |
| connect(); |
| |
| verify(mWifiScoreReport).onRoleChanged(ROLE_CLIENT_PRIMARY); |
| |
| byte[] filter = new byte[20]; |
| new Random().nextBytes(filter); |
| mIpClientCallback.installPacketFilter(filter); |
| mLooper.dispatchAll(); |
| |
| // packet filter will not be installed if the secondary STA doesn't support APF. |
| verify(mWifiNative, never()).installPacketFilter(WIFI_IFACE_NAME, filter); |
| |
| mIpClientCallback.startReadPacketFilter(); |
| mLooper.dispatchAll(); |
| // Return null as packet filter is not installed. |
| verify(mIpClient).readPacketFilterComplete(eq(null)); |
| verify(mWifiNative, never()).readPacketFilter(WIFI_IFACE_NAME); |
| |
| // Now invoke role change, that should apply the APF |
| when(mWifiNative.readPacketFilter(WIFI_IFACE_NAME)).thenReturn(filter); |
| when(mClientModeManager.getRole()).thenReturn(ROLE_CLIENT_PRIMARY); |
| mCmi.onRoleChanged(); |
| mIpClientCallback.installPacketFilter(filter); |
| mLooper.dispatchAll(); |
| verify(mWifiScoreReport, times(2)).onRoleChanged(ROLE_CLIENT_PRIMARY); |
| // verify that the APF capabilities are updated in IpClient. |
| verify(mWifiNative, times(1)).getApfCapabilities(WIFI_IFACE_NAME); |
| verify(mIpClient, times(1)).updateApfCapabilities(eq(APF_CAP)); |
| verify(mWifiNative, times(1)).installPacketFilter(WIFI_IFACE_NAME, filter); |
| } |
| |
| @Test |
| public void testPacketFilterOnRoleChangeOnSecondaryCmmWithSupportForNonPrimaryApf() |
| throws Exception { |
| mResources.setBoolean(R.bool.config_wifiEnableApfOnNonPrimarySta, true); |
| when(mClientModeManager.getRole()).thenReturn(ROLE_CLIENT_SECONDARY_TRANSIENT); |
| connect(); |
| |
| byte[] filter = new byte[20]; |
| new Random().nextBytes(filter); |
| mIpClientCallback.installPacketFilter(filter); |
| mLooper.dispatchAll(); |
| |
| // apply the data. |
| verify(mWifiNative).installPacketFilter(WIFI_IFACE_NAME, filter); |
| |
| when(mWifiNative.readPacketFilter(WIFI_IFACE_NAME)).thenReturn(filter); |
| mIpClientCallback.startReadPacketFilter(); |
| mLooper.dispatchAll(); |
| verify(mIpClient).readPacketFilterComplete(filter); |
| // return the applied data. |
| verify(mWifiNative).readPacketFilter(WIFI_IFACE_NAME); |
| |
| // Now invoke role change, that should not apply the APF |
| when(mClientModeManager.getRole()).thenReturn(ROLE_CLIENT_PRIMARY); |
| mCmi.onRoleChanged(); |
| // ignore (since it was already applied) |
| verify(mIpClient, never()).updateApfCapabilities(eq(APF_CAP)); |
| verify(mWifiNative, times(1)).installPacketFilter(WIFI_IFACE_NAME, filter); |
| } |
| |
| @Test |
| public void testSetMaxDtimMultiplier_IPv4OnlyNetwork() throws Exception { |
| connect(); |
| |
| mIpClientCallback.setMaxDtimMultiplier(TEST_IPV4_ONLY_NETWORK_MAX_DTIM_MULTIPLIER); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiNative).setDtimMultiplier(WIFI_IFACE_NAME, |
| TEST_IPV4_ONLY_NETWORK_MAX_DTIM_MULTIPLIER); |
| } |
| |
| @Test |
| public void testSetMaxDtimMultiplier_IPv6OnlyNetwork() throws Exception { |
| connect(); |
| |
| mIpClientCallback.setMaxDtimMultiplier(TEST_IPV6_ONLY_NETWORK_MAX_DTIM_MULTIPLIER); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiNative).setDtimMultiplier(WIFI_IFACE_NAME, |
| TEST_IPV6_ONLY_NETWORK_MAX_DTIM_MULTIPLIER); |
| } |
| |
| private void runSetMaxDtimMultiplierInDualStackTest() throws Exception { |
| connect(); |
| |
| mIpClientCallback.setMaxDtimMultiplier(TEST_DUAL_STACK_NETWORK_MAX_DTIM_MULTIPLIER); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiNative).setDtimMultiplier(WIFI_IFACE_NAME, |
| TEST_DUAL_STACK_NETWORK_MAX_DTIM_MULTIPLIER); |
| } |
| |
| @Test |
| public void testSetMaxDtimMultiplier_DualStackNetwork() throws Exception { |
| runSetMaxDtimMultiplierInDualStackTest(); |
| } |
| |
| @Test |
| public void testSetMaxDtimMultiplier_EnableMulticastLock() throws Exception { |
| runSetMaxDtimMultiplierInDualStackTest(); |
| reset(mIpClient); |
| |
| // simulate the multicast lock is held. |
| WifiMulticastLockManager.FilterController filterController = |
| mCmi.getMcastLockManagerFilterController(); |
| filterController.startFilteringMulticastPackets(); |
| verify(mIpClient).setMulticastFilter(eq(true)); |
| |
| reset(mWifiNative); |
| |
| mIpClientCallback.setMaxDtimMultiplier(TEST_MULTICAST_LOCK_MAX_DTIM_MULTIPLIER); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiNative).setDtimMultiplier(WIFI_IFACE_NAME, |
| TEST_MULTICAST_LOCK_MAX_DTIM_MULTIPLIER); |
| } |
| |
| @Test |
| public void testWifiInfoUpdateOnRoleChange() throws Exception { |
| when(mClientModeManager.getRole()).thenReturn(ROLE_CLIENT_SECONDARY_TRANSIENT); |
| connect(); |
| // Should not set WifiInfo.isPrimary |
| expectRegisterNetworkAgent((config) -> { }, (cap) -> { |
| if (SdkLevel.isAtLeastS()) { |
| WifiInfo wifiInfoFromTi = (WifiInfo) cap.getTransportInfo(); |
| assertFalse(wifiInfoFromTi.isPrimary()); |
| } |
| }); |
| reset(mWifiNetworkAgent); |
| |
| // Now invoke role change, that should set WifiInfo.isPrimary |
| when(mClientModeManager.getRole()).thenReturn(ROLE_CLIENT_PRIMARY); |
| mCmi.onRoleChanged(); |
| expectNetworkAgentUpdateCapabilities((cap) -> { |
| if (SdkLevel.isAtLeastS()) { |
| WifiInfo wifiInfoFromTi = (WifiInfo) cap.getTransportInfo(); |
| assertTrue(wifiInfoFromTi.isPrimary()); |
| } |
| }); |
| } |
| |
| /** |
| * Verify onCellularConnectivityChanged plumbs the information to the right locations. |
| */ |
| @Test |
| public void testOnCellularConnectivityChanged() { |
| mCmi.onCellularConnectivityChanged(WifiDataStall.CELLULAR_DATA_AVAILABLE); |
| verify(mWifiConfigManager).onCellularConnectivityChanged( |
| WifiDataStall.CELLULAR_DATA_AVAILABLE); |
| |
| mCmi.onCellularConnectivityChanged(WifiDataStall.CELLULAR_DATA_NOT_AVAILABLE); |
| verify(mWifiConfigManager).onCellularConnectivityChanged( |
| WifiDataStall.CELLULAR_DATA_NOT_AVAILABLE); |
| } |
| |
| /** |
| * Verify that when cellular data is lost and wifi is not connected, we force a connectivity |
| * scan. |
| */ |
| @Test |
| public void testOnCellularConnectivityChangedForceConnectivityScan() throws Exception { |
| mResources.setBoolean(R.bool.config_wifiScanOnCellularDataLossEnabled, true); |
| // verify a connectivity scan is forced since wifi is not connected |
| mCmi.onCellularConnectivityChanged(WifiDataStall.CELLULAR_DATA_NOT_AVAILABLE); |
| verify(mWifiConnectivityManager).forceConnectivityScan(WIFI_WORK_SOURCE); |
| |
| // verify that after wifi is connected, loss of cellular data will not trigger scans. |
| connect(); |
| mCmi.onCellularConnectivityChanged(WifiDataStall.CELLULAR_DATA_NOT_AVAILABLE); |
| verify(mWifiConnectivityManager).forceConnectivityScan(WIFI_WORK_SOURCE); |
| } |
| |
| private void setScreenState(boolean screenOn) { |
| WifiDeviceStateChangeManager.StateChangeCallback callback = |
| mStateChangeCallbackArgumentCaptor.getValue(); |
| assertNotNull(callback); |
| callback.onScreenStateChanged(screenOn); |
| } |
| |
| @Test |
| public void verifyRssiPollOnScreenStateChange() throws Exception { |
| setScreenState(true); |
| connect(); |
| clearInvocations(mWifiNative, mWifiMetrics, mWifiDataStall); |
| |
| when(mWifiNative.getSupportedFeatureSet(WIFI_IFACE_NAME)).thenReturn( |
| WifiManager.WIFI_FEATURE_LINK_LAYER_STATS); |
| WifiLinkLayerStats oldLLStats = new WifiLinkLayerStats(); |
| when(mWifiNative.getWifiLinkLayerStats(any())).thenReturn(oldLLStats); |
| mLooper.moveTimeForward(mWifiGlobals.getPollRssiIntervalMillis()); |
| mLooper.dispatchAll(); |
| verify(mWifiNative).getWifiLinkLayerStats(WIFI_IFACE_NAME); |
| verify(mWifiDataStall).checkDataStallAndThroughputSufficiency(WIFI_IFACE_NAME, |
| mConnectionCapabilities, null, oldLLStats, mWifiInfo, TEST_TX_BYTES, TEST_RX_BYTES); |
| verify(mWifiMetrics).incrementWifiLinkLayerUsageStats(WIFI_IFACE_NAME, oldLLStats); |
| |
| WifiLinkLayerStats newLLStats = new WifiLinkLayerStats(); |
| when(mWifiNative.getWifiLinkLayerStats(any())).thenReturn(newLLStats); |
| mLooper.moveTimeForward(mWifiGlobals.getPollRssiIntervalMillis()); |
| mLooper.dispatchAll(); |
| verify(mWifiNative, times(2)).getWifiLinkLayerStats(WIFI_IFACE_NAME); |
| |
| verify(mWifiDataStall).checkDataStallAndThroughputSufficiency(WIFI_IFACE_NAME, |
| mConnectionCapabilities, oldLLStats, newLLStats, mWifiInfo, TEST_TX_BYTES, |
| TEST_RX_BYTES); |
| verify(mWifiMetrics).incrementWifiLinkLayerUsageStats(WIFI_IFACE_NAME, newLLStats); |
| |
| // Now set the screen state to false & move time forward, ensure no more link layer stats |
| // collection. |
| setScreenState(false); |
| mLooper.dispatchAll(); |
| clearInvocations(mWifiNative, mWifiMetrics, mWifiDataStall); |
| |
| mLooper.moveTimeForward(mWifiGlobals.getPollRssiIntervalMillis()); |
| mLooper.dispatchAll(); |
| |
| verifyNoMoreInteractions(mWifiNative, mWifiMetrics, mWifiDataStall); |
| } |
| |
| @Test |
| public void verifyRssiPollOnSecondaryCmm() throws Exception { |
| when(mClientModeManager.getRole()).thenReturn(ROLE_CLIENT_SECONDARY_TRANSIENT); |
| mCmi.onRoleChanged(); |
| setScreenState(true); |
| connect(); |
| clearInvocations(mWifiNative, mWifiMetrics, mWifiDataStall); |
| |
| verifyNoMoreInteractions(mWifiNative, mWifiMetrics, mWifiDataStall); |
| |
| when(mWifiNative.getSupportedFeatureSet(WIFI_IFACE_NAME)).thenReturn( |
| WifiManager.WIFI_FEATURE_LINK_LAYER_STATS); |
| when(mWifiNative.getWifiLinkLayerStats(any())).thenReturn(new WifiLinkLayerStats()); |
| |
| // No link layer stats collection on secondary CMM. |
| mLooper.moveTimeForward(mWifiGlobals.getPollRssiIntervalMillis()); |
| mLooper.dispatchAll(); |
| |
| verifyNoMoreInteractions(mWifiNative, mWifiMetrics, mWifiDataStall); |
| } |
| |
| @Test |
| public void verifyRssiPollOnOnRoleChangeToPrimary() throws Exception { |
| when(mClientModeManager.getRole()).thenReturn(ROLE_CLIENT_SECONDARY_TRANSIENT); |
| mCmi.onRoleChanged(); |
| setScreenState(true); |
| connect(); |
| clearInvocations(mWifiNative, mWifiMetrics, mWifiDataStall); |
| |
| when(mWifiNative.getSupportedFeatureSet(WIFI_IFACE_NAME)).thenReturn( |
| WifiManager.WIFI_FEATURE_LINK_LAYER_STATS); |
| when(mWifiNative.getWifiLinkLayerStats(any())).thenReturn(new WifiLinkLayerStats()); |
| |
| // No link layer stats collection on secondary CMM. |
| mLooper.moveTimeForward(mWifiGlobals.getPollRssiIntervalMillis()); |
| mLooper.dispatchAll(); |
| |
| verifyNoMoreInteractions(mWifiNative, mWifiMetrics, mWifiDataStall); |
| |
| // Now invoke role change, that should start rssi polling on the new primary. |
| when(mClientModeManager.getRole()).thenReturn(ROLE_CLIENT_PRIMARY); |
| mCmi.onRoleChanged(); |
| mLooper.dispatchAll(); |
| clearInvocations(mWifiNative, mWifiMetrics, mWifiDataStall); |
| |
| WifiLinkLayerStats oldLLStats = new WifiLinkLayerStats(); |
| when(mWifiNative.getWifiLinkLayerStats(any())).thenReturn(oldLLStats); |
| mLooper.moveTimeForward(mWifiGlobals.getPollRssiIntervalMillis()); |
| mLooper.dispatchAll(); |
| verify(mWifiNative).getWifiLinkLayerStats(WIFI_IFACE_NAME); |
| verify(mWifiDataStall).checkDataStallAndThroughputSufficiency(WIFI_IFACE_NAME, |
| mConnectionCapabilities, null, oldLLStats, mWifiInfo, TEST_TX_BYTES, TEST_RX_BYTES); |
| verify(mWifiMetrics).incrementWifiLinkLayerUsageStats(WIFI_IFACE_NAME, oldLLStats); |
| } |
| |
| @Test |
| public void verifyRssiPollOnOnRoleChangeToSecondary() throws Exception { |
| setScreenState(true); |
| connect(); |
| clearInvocations(mWifiNative, mWifiMetrics, mWifiDataStall); |
| |
| // RSSI polling is enabled on primary. |
| when(mWifiNative.getSupportedFeatureSet(WIFI_IFACE_NAME)).thenReturn( |
| WifiManager.WIFI_FEATURE_LINK_LAYER_STATS); |
| WifiLinkLayerStats oldLLStats = new WifiLinkLayerStats(); |
| when(mWifiNative.getWifiLinkLayerStats(any())).thenReturn(oldLLStats); |
| mLooper.moveTimeForward(mWifiGlobals.getPollRssiIntervalMillis()); |
| mLooper.dispatchAll(); |
| verify(mWifiNative).getWifiLinkLayerStats(WIFI_IFACE_NAME); |
| verify(mWifiDataStall).checkDataStallAndThroughputSufficiency(WIFI_IFACE_NAME, |
| mConnectionCapabilities, null, oldLLStats, mWifiInfo, TEST_TX_BYTES, TEST_RX_BYTES); |
| verify(mWifiMetrics).incrementWifiLinkLayerUsageStats(WIFI_IFACE_NAME, oldLLStats); |
| |
| // Now invoke role change, that should stop rssi polling on the secondary. |
| when(mClientModeManager.getRole()).thenReturn(ROLE_CLIENT_SECONDARY_TRANSIENT); |
| mCmi.onRoleChanged(); |
| mLooper.dispatchAll(); |
| clearInvocations(mWifiNative, mWifiMetrics, mWifiDataStall); |
| |
| // No link layer stats collection on secondary CMM. |
| mLooper.moveTimeForward(mWifiGlobals.getPollRssiIntervalMillis()); |
| mLooper.dispatchAll(); |
| |
| verifyNoMoreInteractions(mWifiNative, mWifiMetrics, mWifiDataStall); |
| } |
| |
| @Test |
| public void verifyRssiPollWhenLinkLayerStatsIsNotSupported() throws Exception { |
| setScreenState(true); |
| connect(); |
| clearInvocations(mWifiNative, mWifiMetrics, mWifiDataStall); |
| |
| when(mWifiNative.getSupportedFeatureSet(WIFI_IFACE_NAME)).thenReturn(0L); |
| WifiLinkLayerStats stats = new WifiLinkLayerStats(); |
| when(mWifiNative.getWifiLinkLayerStats(any())).thenReturn(stats); |
| mLooper.moveTimeForward(mWifiGlobals.getPollRssiIntervalMillis()); |
| mLooper.dispatchAll(); |
| verify(mWifiNative, never()).getWifiLinkLayerStats(any()); |
| verify(mWifiDataStall).checkDataStallAndThroughputSufficiency(WIFI_IFACE_NAME, |
| mConnectionCapabilities, null, null, mWifiInfo, TEST_TX_BYTES, TEST_RX_BYTES); |
| verify(mWifiMetrics).incrementWifiLinkLayerUsageStats(WIFI_IFACE_NAME, null); |
| } |
| |
| @Test |
| public void testClientModeImplWhenIpClientIsNotReady() throws Exception { |
| WifiConfiguration config = mConnectedNetwork; |
| config.networkId = FRAMEWORK_NETWORK_ID; |
| config.setRandomizedMacAddress(TEST_LOCAL_MAC_ADDRESS); |
| config.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_AUTO; |
| config.getNetworkSelectionStatus().setHasEverConnected(mTestNetworkParams.hasEverConnected); |
| assertNull(config.getNetworkSelectionStatus().getCandidateSecurityParams()); |
| |
| mFrameworkFacade = mock(FrameworkFacade.class); |
| ArgumentCaptor<IpClientCallbacks> captor = ArgumentCaptor.forClass(IpClientCallbacks.class); |
| // reset mWifiNative since initializeCmi() was called in setup() |
| resetWifiNative(); |
| |
| // reinitialize ClientModeImpl with IpClient is not ready. |
| initializeCmi(); |
| verify(mFrameworkFacade).makeIpClient(any(), anyString(), captor.capture()); |
| |
| // Manually connect should fail. |
| IActionListener connectActionListener = mock(IActionListener.class); |
| mCmi.connectNetwork( |
| new NetworkUpdateResult(config.networkId), |
| new ActionListenerWrapper(connectActionListener), |
| Binder.getCallingUid(), OP_PACKAGE_NAME); |
| mLooper.dispatchAll(); |
| verify(connectActionListener).onFailure(WifiManager.ActionListener.FAILURE_INTERNAL_ERROR); |
| verify(mWifiConfigManager, never()) |
| .getConfiguredNetworkWithoutMasking(eq(config.networkId)); |
| verify(mWifiNative, never()).connectToNetwork(eq(WIFI_IFACE_NAME), eq(config)); |
| |
| // Auto connect should also fail |
| mCmi.startConnectToNetwork(config.networkId, MANAGED_PROFILE_UID, config.BSSID); |
| mLooper.dispatchAll(); |
| verify(mWifiConfigManager, never()) |
| .getConfiguredNetworkWithoutMasking(eq(config.networkId)); |
| verify(mWifiNative, never()).connectToNetwork(eq(WIFI_IFACE_NAME), eq(config)); |
| |
| // Make IpClient ready connection should succeed. |
| captor.getValue().onIpClientCreated(mIpClient); |
| mLooper.dispatchAll(); |
| |
| triggerConnect(); |
| } |
| |
| private void testNetworkRemovedUpdatesLinkedNetworks(boolean isSecondary) throws Exception { |
| if (isSecondary) { |
| when(mClientModeManager.getRole()).thenReturn(ROLE_CLIENT_SECONDARY_LONG_LIVED); |
| } |
| mResources.setBoolean(R.bool.config_wifiEnableLinkedNetworkRoaming, true); |
| WifiConfiguration connectedConfig = WifiConfigurationTestUtil.createPskNetwork("\"ssid1\""); |
| connectedConfig.networkId = FRAMEWORK_NETWORK_ID; |
| WifiConfiguration removeConfig = WifiConfigurationTestUtil.createPskNetwork("\"ssid2\""); |
| removeConfig.networkId = FRAMEWORK_NETWORK_ID + 1; |
| connectedConfig.linkedConfigurations = new HashMap<>(); |
| connectedConfig.linkedConfigurations.put(removeConfig.getProfileKey(), 1); |
| removeConfig.linkedConfigurations = new HashMap<>(); |
| removeConfig.linkedConfigurations.put(connectedConfig.getProfileKey(), 1); |
| when(mWifiConfigManager.getConfiguredNetwork(connectedConfig.networkId)) |
| .thenReturn(connectedConfig); |
| when(mWifiConfigManager.getConfiguredNetwork(removeConfig.networkId)) |
| .thenReturn(removeConfig); |
| mConnectedNetwork = connectedConfig; |
| connect(); |
| |
| when(mWifiNative.getCurrentNetworkSecurityParams(any())).thenReturn( |
| SecurityParams.createSecurityParamsBySecurityType( |
| WifiConfiguration.SECURITY_TYPE_PSK)); |
| for (WifiConfigManager.OnNetworkUpdateListener listener : mConfigUpdateListenerCaptor |
| .getAllValues()) { |
| listener.onNetworkRemoved(removeConfig); |
| } |
| mLooper.dispatchAll(); |
| |
| if (!isSecondary) { |
| verify(mWifiConfigManager).updateLinkedNetworks(connectedConfig.networkId); |
| } else { |
| verify(mWifiConfigManager, never()).updateLinkedNetworks(connectedConfig.networkId); |
| } |
| } |
| |
| @Test |
| public void testNetworkRemovedUpdatesLinkedNetworksPrimary() throws Exception { |
| testNetworkRemovedUpdatesLinkedNetworks(false); |
| } |
| |
| @Test |
| public void testNetworkRemovedUpdatesLinkedNetworksSecondary() throws Exception { |
| testNetworkRemovedUpdatesLinkedNetworks(true); |
| } |
| |
| @Test |
| public void testConnectClearsAllowlistSsids() throws Exception { |
| connect(); |
| verify(mWifiBlocklistMonitor) |
| .setAllowlistSsids(eq(mConnectedNetwork.SSID), eq(Collections.emptyList())); |
| } |
| |
| @Test |
| public void testNetworkUpdatedUpdatesLinkedNetworks() throws Exception { |
| mResources.setBoolean(R.bool.config_wifiEnableLinkedNetworkRoaming, true); |
| WifiConfiguration connectedConfig = WifiConfigurationTestUtil.createPskNetwork("\"ssid1\""); |
| connectedConfig.networkId = FRAMEWORK_NETWORK_ID; |
| WifiConfiguration updatedConfig = WifiConfigurationTestUtil.createPskNetwork("\"ssid2\""); |
| updatedConfig.networkId = FRAMEWORK_NETWORK_ID + 1; |
| connectedConfig.linkedConfigurations = new HashMap<>(); |
| connectedConfig.linkedConfigurations.put(updatedConfig.getProfileKey(), 1); |
| updatedConfig.linkedConfigurations = new HashMap<>(); |
| updatedConfig.linkedConfigurations.put(connectedConfig.getProfileKey(), 1); |
| when(mWifiConfigManager.getConfiguredNetwork(connectedConfig.networkId)) |
| .thenReturn(connectedConfig); |
| when(mWifiConfigManager.getConfiguredNetwork(updatedConfig.networkId)) |
| .thenReturn(updatedConfig); |
| mConnectedNetwork = connectedConfig; |
| connect(); |
| |
| when(mWifiNative.getCurrentNetworkSecurityParams(any())).thenReturn( |
| SecurityParams.createSecurityParamsBySecurityType( |
| WifiConfiguration.SECURITY_TYPE_PSK)); |
| IActionListener connectActionListener = mock(IActionListener.class); |
| mCmi.saveNetwork( |
| new NetworkUpdateResult( |
| updatedConfig.networkId, STATUS_SUCCESS, false, false, true, false), |
| new ActionListenerWrapper(connectActionListener), |
| Binder.getCallingUid(), OP_PACKAGE_NAME); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiConfigManager).updateLinkedNetworks(connectedConfig.networkId); |
| } |
| |
| @Test |
| public void testNetworkValidationUpdatesLinkedNetworks() throws Exception { |
| mResources.setBoolean(R.bool.config_wifiEnableLinkedNetworkRoaming, true); |
| BufferedReader reader = mock(BufferedReader.class); |
| WifiConfiguration connectedConfig = WifiConfigurationTestUtil.createPskNetwork("\"ssid1\""); |
| connectedConfig.networkId = FRAMEWORK_NETWORK_ID; |
| when(mWifiConfigManager.getConfiguredNetwork(connectedConfig.networkId)) |
| .thenReturn(connectedConfig); |
| mConnectedNetwork = connectedConfig; |
| connect(); |
| verify(mWifiInjector).makeWifiNetworkAgent(any(), any(), any(), any(), |
| mWifiNetworkAgentCallbackCaptor.capture()); |
| verify(mWifiBlocklistMonitor).setAllowlistSsids( |
| eq(connectedConfig.SSID), eq(Collections.emptyList())); |
| verify(mWifiBlocklistMonitor).updateFirmwareRoamingConfiguration( |
| eq(Set.of(connectedConfig.SSID))); |
| |
| LinkProperties linkProperties = mock(LinkProperties.class); |
| RouteInfo routeInfo = mock(RouteInfo.class); |
| IpPrefix ipPrefix = mock(IpPrefix.class); |
| Inet4Address destinationAddress = mock(Inet4Address.class); |
| InetAddress gatewayAddress = mock(InetAddress.class); |
| String hostAddress = "127.0.0.1"; |
| String gatewayMac = "192.168.0.1"; |
| when(linkProperties.getRoutes()).thenReturn(Arrays.asList(routeInfo)); |
| when(routeInfo.isDefaultRoute()).thenReturn(true); |
| when(routeInfo.getDestination()).thenReturn(ipPrefix); |
| when(ipPrefix.getAddress()).thenReturn(destinationAddress); |
| when(routeInfo.hasGateway()).thenReturn(true); |
| when(routeInfo.getGateway()).thenReturn(gatewayAddress); |
| when(gatewayAddress.getHostAddress()).thenReturn(hostAddress); |
| when(mWifiInjector.createBufferedReader(ARP_TABLE_PATH)).thenReturn(reader); |
| when(reader.readLine()).thenReturn(new StringJoiner(" ") |
| .add(hostAddress) |
| .add("HWType") |
| .add("Flags") |
| .add(gatewayMac) |
| .add("Mask") |
| .add("Device") |
| .toString()); |
| |
| mIpClientCallback.onLinkPropertiesChange(linkProperties); |
| mLooper.dispatchAll(); |
| when(mWifiNative.getCurrentNetworkSecurityParams(any())).thenReturn( |
| SecurityParams.createSecurityParamsBySecurityType( |
| WifiConfiguration.SECURITY_TYPE_PSK)); |
| when(mWifiConfigManager.setNetworkDefaultGwMacAddress(anyInt(), any())).thenReturn(true); |
| when(mWifiConfigManager.saveToStore(anyBoolean())).thenReturn(true); |
| WifiConfiguration linkedConfig = WifiConfigurationTestUtil.createPskNetwork("\"ssid2\""); |
| linkedConfig.networkId = connectedConfig.networkId + 1; |
| Map<String, WifiConfiguration> linkedNetworks = new HashMap<>(); |
| linkedNetworks.put(linkedConfig.getProfileKey(), linkedConfig); |
| when(mWifiConfigManager.getLinkedNetworksWithoutMasking(connectedConfig.networkId)) |
| .thenReturn(linkedNetworks); |
| when(mWifiNative.updateLinkedNetworks(any(), anyInt(), any())).thenReturn(true); |
| mWifiNetworkAgentCallbackCaptor.getValue().onValidationStatus( |
| NetworkAgent.VALIDATION_STATUS_VALID, null /* captivePortalUrl */); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiConfigManager) |
| .setNetworkDefaultGwMacAddress(mConnectedNetwork.networkId, gatewayMac); |
| verify(mWifiConfigManager).updateLinkedNetworks(connectedConfig.networkId); |
| verify(mWifiNative).updateLinkedNetworks( |
| any(), eq(connectedConfig.networkId), eq(linkedNetworks)); |
| List<String> allowlistSsids = new ArrayList<>(); |
| allowlistSsids.add(linkedConfig.SSID); |
| allowlistSsids.add(connectedConfig.SSID); |
| verify(mWifiBlocklistMonitor).setAllowlistSsids( |
| eq(connectedConfig.SSID), eq(allowlistSsids)); |
| verify(mWifiBlocklistMonitor).updateFirmwareRoamingConfiguration( |
| eq(new ArraySet<>(allowlistSsids))); |
| } |
| |
| @Test |
| public void testNonPskNetworkDoesNotUpdateLinkedNetworks() throws Exception { |
| mResources.setBoolean(R.bool.config_wifiEnableLinkedNetworkRoaming, true); |
| BufferedReader reader = mock(BufferedReader.class); |
| WifiConfiguration connectedConfig = WifiConfigurationTestUtil.createPskNetwork("\"ssid1\""); |
| connectedConfig.networkId = FRAMEWORK_NETWORK_ID; |
| when(mWifiConfigManager.getConfiguredNetwork(connectedConfig.networkId)) |
| .thenReturn(connectedConfig); |
| mConnectedNetwork = connectedConfig; |
| connect(); |
| verify(mWifiInjector).makeWifiNetworkAgent(any(), any(), any(), any(), |
| mWifiNetworkAgentCallbackCaptor.capture()); |
| |
| LinkProperties linkProperties = mock(LinkProperties.class); |
| RouteInfo routeInfo = mock(RouteInfo.class); |
| IpPrefix ipPrefix = mock(IpPrefix.class); |
| Inet4Address destinationAddress = mock(Inet4Address.class); |
| InetAddress gatewayAddress = mock(InetAddress.class); |
| String hostAddress = "127.0.0.1"; |
| String gatewayMac = "192.168.0.1"; |
| when(linkProperties.getRoutes()).thenReturn(Arrays.asList(routeInfo)); |
| when(routeInfo.isDefaultRoute()).thenReturn(true); |
| when(routeInfo.getDestination()).thenReturn(ipPrefix); |
| when(ipPrefix.getAddress()).thenReturn(destinationAddress); |
| when(routeInfo.hasGateway()).thenReturn(true); |
| when(routeInfo.getGateway()).thenReturn(gatewayAddress); |
| when(gatewayAddress.getHostAddress()).thenReturn(hostAddress); |
| when(mWifiInjector.createBufferedReader(ARP_TABLE_PATH)).thenReturn(reader); |
| when(reader.readLine()).thenReturn(new StringJoiner(" ") |
| .add(hostAddress) |
| .add("HWType") |
| .add("Flags") |
| .add(gatewayMac) |
| .add("Mask") |
| .add("Device") |
| .toString()); |
| |
| mIpClientCallback.onLinkPropertiesChange(linkProperties); |
| mLooper.dispatchAll(); |
| when(mWifiNative.getCurrentNetworkSecurityParams(any())).thenReturn( |
| SecurityParams.createSecurityParamsBySecurityType( |
| WifiConfiguration.SECURITY_TYPE_SAE)); |
| when(mWifiConfigManager.setNetworkDefaultGwMacAddress(anyInt(), any())).thenReturn(true); |
| when(mWifiConfigManager.saveToStore(anyBoolean())).thenReturn(true); |
| mWifiNetworkAgentCallbackCaptor.getValue().onValidationStatus( |
| NetworkAgent.VALIDATION_STATUS_VALID, null /* captivePortalUrl */); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiConfigManager) |
| .setNetworkDefaultGwMacAddress(mConnectedNetwork.networkId, gatewayMac); |
| verify(mWifiConfigManager, never()).updateLinkedNetworks(connectedConfig.networkId); |
| } |
| |
| @Test |
| public void testInvalidScanResultDoesNotUpdateLinkedNetworks() throws Exception { |
| mResources.setBoolean(R.bool.config_wifiEnableLinkedNetworkRoaming, true); |
| BufferedReader reader = mock(BufferedReader.class); |
| WifiConfiguration connectedConfig = WifiConfigurationTestUtil.createPskNetwork("\"ssid1\""); |
| connectedConfig.networkId = FRAMEWORK_NETWORK_ID; |
| when(mWifiConfigManager.getConfiguredNetwork(connectedConfig.networkId)) |
| .thenReturn(connectedConfig); |
| mConnectedNetwork = connectedConfig; |
| connect(); |
| verify(mWifiInjector).makeWifiNetworkAgent(any(), any(), any(), any(), |
| mWifiNetworkAgentCallbackCaptor.capture()); |
| |
| LinkProperties linkProperties = mock(LinkProperties.class); |
| RouteInfo routeInfo = mock(RouteInfo.class); |
| IpPrefix ipPrefix = mock(IpPrefix.class); |
| Inet4Address destinationAddress = mock(Inet4Address.class); |
| InetAddress gatewayAddress = mock(InetAddress.class); |
| String hostAddress = "127.0.0.1"; |
| String gatewayMac = "192.168.0.1"; |
| when(linkProperties.getRoutes()).thenReturn(Arrays.asList(routeInfo)); |
| when(routeInfo.isDefaultRoute()).thenReturn(true); |
| when(routeInfo.getDestination()).thenReturn(ipPrefix); |
| when(ipPrefix.getAddress()).thenReturn(destinationAddress); |
| when(routeInfo.hasGateway()).thenReturn(true); |
| when(routeInfo.getGateway()).thenReturn(gatewayAddress); |
| when(gatewayAddress.getHostAddress()).thenReturn(hostAddress); |
| when(mWifiInjector.createBufferedReader(ARP_TABLE_PATH)).thenReturn(reader); |
| when(reader.readLine()).thenReturn(new StringJoiner(" ") |
| .add(hostAddress) |
| .add("HWType") |
| .add("Flags") |
| .add(gatewayMac) |
| .add("Mask") |
| .add("Device") |
| .toString()); |
| |
| mIpClientCallback.onLinkPropertiesChange(linkProperties); |
| mLooper.dispatchAll(); |
| when(mWifiNative.getCurrentNetworkSecurityParams(any())).thenReturn( |
| SecurityParams.createSecurityParamsBySecurityType( |
| WifiConfiguration.SECURITY_TYPE_PSK)); |
| when(mWifiConfigManager.setNetworkDefaultGwMacAddress(anyInt(), any())).thenReturn(true); |
| when(mWifiConfigManager.saveToStore(anyBoolean())).thenReturn(true); |
| |
| // FT/PSK scan, do not update linked networks |
| ScanResult ftPskScan = new ScanResult(); |
| ftPskScan.capabilities = "FT/PSK"; |
| when(mScanDetailCache.getScanResult(any())).thenReturn(ftPskScan); |
| mWifiNetworkAgentCallbackCaptor.getValue().onValidationStatus( |
| NetworkAgent.VALIDATION_STATUS_VALID, null /* captivePortalUrl */); |
| mLooper.dispatchAll(); |
| verify(mWifiConfigManager) |
| .setNetworkDefaultGwMacAddress(mConnectedNetwork.networkId, gatewayMac); |
| verify(mWifiConfigManager, never()).updateLinkedNetworks(connectedConfig.networkId); |
| |
| // FT/SAE scan, do not update linked networks |
| ScanResult ftSaeScan = new ScanResult(); |
| ftSaeScan.capabilities = "FT/SAE"; |
| when(mScanRequestProxy.getScanResult(any())).thenReturn(ftSaeScan); |
| mWifiNetworkAgentCallbackCaptor.getValue().onValidationStatus( |
| NetworkAgent.VALIDATION_STATUS_VALID, null /* captivePortalUrl */); |
| mLooper.dispatchAll(); |
| verify(mWifiConfigManager) |
| .setNetworkDefaultGwMacAddress(mConnectedNetwork.networkId, gatewayMac); |
| verify(mWifiConfigManager, never()).updateLinkedNetworks(connectedConfig.networkId); |
| |
| // Null scan, do not update linked networks |
| mWifiNetworkAgentCallbackCaptor.getValue().onValidationStatus( |
| NetworkAgent.VALIDATION_STATUS_VALID, null /* captivePortalUrl */); |
| mLooper.dispatchAll(); |
| verify(mWifiConfigManager) |
| .setNetworkDefaultGwMacAddress(mConnectedNetwork.networkId, gatewayMac); |
| verify(mWifiConfigManager, never()).updateLinkedNetworks(connectedConfig.networkId); |
| } |
| |
| @Test |
| public void testLinkedNetworksFiltersOutAutojoinDisabledNetworks() throws Exception { |
| mResources.setBoolean(R.bool.config_wifiEnableLinkedNetworkRoaming, true); |
| BufferedReader reader = mock(BufferedReader.class); |
| WifiConfiguration connectedConfig = WifiConfigurationTestUtil.createPskNetwork("\"ssid1\""); |
| connectedConfig.networkId = FRAMEWORK_NETWORK_ID; |
| when(mWifiConfigManager.getConfiguredNetwork(connectedConfig.networkId)) |
| .thenReturn(connectedConfig); |
| mConnectedNetwork = connectedConfig; |
| connect(); |
| verify(mWifiInjector).makeWifiNetworkAgent(any(), any(), any(), any(), |
| mWifiNetworkAgentCallbackCaptor.capture()); |
| verify(mWifiBlocklistMonitor).setAllowlistSsids( |
| eq(connectedConfig.SSID), eq(Collections.emptyList())); |
| verify(mWifiBlocklistMonitor).updateFirmwareRoamingConfiguration( |
| eq(Set.of(connectedConfig.SSID))); |
| |
| LinkProperties linkProperties = mock(LinkProperties.class); |
| RouteInfo routeInfo = mock(RouteInfo.class); |
| IpPrefix ipPrefix = mock(IpPrefix.class); |
| Inet4Address destinationAddress = mock(Inet4Address.class); |
| InetAddress gatewayAddress = mock(InetAddress.class); |
| String hostAddress = "127.0.0.1"; |
| String gatewayMac = "192.168.0.1"; |
| when(linkProperties.getRoutes()).thenReturn(Arrays.asList(routeInfo)); |
| when(routeInfo.isDefaultRoute()).thenReturn(true); |
| when(routeInfo.getDestination()).thenReturn(ipPrefix); |
| when(ipPrefix.getAddress()).thenReturn(destinationAddress); |
| when(routeInfo.hasGateway()).thenReturn(true); |
| when(routeInfo.getGateway()).thenReturn(gatewayAddress); |
| when(gatewayAddress.getHostAddress()).thenReturn(hostAddress); |
| when(mWifiInjector.createBufferedReader(ARP_TABLE_PATH)).thenReturn(reader); |
| when(reader.readLine()).thenReturn(new StringJoiner(" ") |
| .add(hostAddress) |
| .add("HWType") |
| .add("Flags") |
| .add(gatewayMac) |
| .add("Mask") |
| .add("Device") |
| .toString()); |
| |
| mIpClientCallback.onLinkPropertiesChange(linkProperties); |
| mLooper.dispatchAll(); |
| when(mWifiNative.getCurrentNetworkSecurityParams(any())).thenReturn( |
| SecurityParams.createSecurityParamsBySecurityType( |
| WifiConfiguration.SECURITY_TYPE_PSK)); |
| when(mWifiConfigManager.setNetworkDefaultGwMacAddress(anyInt(), any())).thenReturn(true); |
| when(mWifiConfigManager.saveToStore(anyBoolean())).thenReturn(true); |
| WifiConfiguration linkedConfig = WifiConfigurationTestUtil.createPskNetwork("\"ssid2\""); |
| linkedConfig.networkId = connectedConfig.networkId + 1; |
| linkedConfig.allowAutojoin = false; |
| Map<String, WifiConfiguration> linkedNetworks = new HashMap<>(); |
| linkedNetworks.put(linkedConfig.getProfileKey(), linkedConfig); |
| when(mWifiConfigManager.getLinkedNetworksWithoutMasking(connectedConfig.networkId)) |
| .thenReturn(linkedNetworks); |
| when(mWifiNative.updateLinkedNetworks(any(), anyInt(), any())).thenReturn(true); |
| mWifiNetworkAgentCallbackCaptor.getValue().onValidationStatus( |
| NetworkAgent.VALIDATION_STATUS_VALID, null /* captivePortalUrl */); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiConfigManager) |
| .setNetworkDefaultGwMacAddress(mConnectedNetwork.networkId, gatewayMac); |
| verify(mWifiConfigManager).updateLinkedNetworks(connectedConfig.networkId); |
| verify(mWifiNative).updateLinkedNetworks( |
| any(), eq(connectedConfig.networkId), eq(linkedNetworks)); |
| verify(mWifiBlocklistMonitor, times(2)).setAllowlistSsids( |
| eq(connectedConfig.SSID), eq(Collections.emptyList())); |
| verify(mWifiBlocklistMonitor).updateFirmwareRoamingConfiguration( |
| eq(Collections.emptySet())); |
| } |
| |
| /** |
| * Verify that we disconnect when we mark a previous trusted network untrusted. |
| */ |
| @Test |
| public void verifyDisconnectOnMarkingNetworkUntrusted() throws Exception { |
| assumeTrue(SdkLevel.isAtLeastT()); |
| connect(); |
| expectRegisterNetworkAgent((config) -> { }, (cap) -> { |
| assertTrue(cap.hasCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)); |
| }); |
| |
| WifiConfiguration oldConfig = new WifiConfiguration(mConnectedNetwork); |
| mConnectedNetwork.trusted = false; |
| |
| for (WifiConfigManager.OnNetworkUpdateListener listener : mConfigUpdateListenerCaptor |
| .getAllValues()) { |
| listener.onNetworkUpdated(mConnectedNetwork, oldConfig, false); |
| } |
| mLooper.dispatchAll(); |
| verify(mWifiNative).disconnect(WIFI_IFACE_NAME); |
| verify(mWifiMetrics).logStaEvent(anyString(), eq(StaEvent.TYPE_FRAMEWORK_DISCONNECT), |
| eq(StaEvent.DISCONNECT_NETWORK_UNTRUSTED)); |
| } |
| |
| /** |
| * Verify that we only update capabilities when we mark a previous untrusted network trusted. |
| */ |
| @Test |
| public void verifyUpdateCapabilitiesOnMarkingNetworkTrusted() throws Exception { |
| assumeTrue(SdkLevel.isAtLeastT()); |
| mConnectedNetwork.trusted = false; |
| connect(); |
| expectRegisterNetworkAgent((config) -> { }, (cap) -> { |
| assertFalse(cap.hasCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)); |
| }); |
| reset(mWifiNetworkAgent); |
| |
| WifiConfiguration oldConfig = new WifiConfiguration(mConnectedNetwork); |
| mConnectedNetwork.trusted = true; |
| |
| for (WifiConfigManager.OnNetworkUpdateListener listener : mConfigUpdateListenerCaptor |
| .getAllValues()) { |
| listener.onNetworkUpdated(mConnectedNetwork, oldConfig, false); |
| } |
| mLooper.dispatchAll(); |
| assertEquals("L3ConnectedState", getCurrentState().getName()); |
| |
| expectNetworkAgentUpdateCapabilities((cap) -> { |
| assertTrue(cap.hasCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)); |
| }); |
| } |
| |
| /** |
| * Verify on device before T we will not disconnect when we mark a previous trusted network |
| * untrusted. |
| */ |
| @Test |
| public void verifyNoDisconnectOnMarkingNetworkUntrustedBeforeT() throws Exception { |
| assumeFalse(SdkLevel.isAtLeastT()); |
| connect(); |
| expectRegisterNetworkAgent((config) -> { }, (cap) -> { |
| assertTrue(cap.hasCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)); |
| }); |
| |
| WifiConfiguration oldConfig = new WifiConfiguration(mConnectedNetwork); |
| mConnectedNetwork.trusted = false; |
| |
| for (WifiConfigManager.OnNetworkUpdateListener listener : mConfigUpdateListenerCaptor |
| .getAllValues()) { |
| listener.onNetworkUpdated(mConnectedNetwork, oldConfig, false); |
| } |
| mLooper.dispatchAll(); |
| verify(mWifiNative, never()).disconnect(WIFI_IFACE_NAME); |
| verify(mWifiMetrics, never()).logStaEvent(anyString(), anyInt(), anyInt()); |
| } |
| |
| /** |
| * Verify on a build before T we will not update capabilities or disconnect when we mark a |
| * previous untrusted network trusted. |
| */ |
| @Test |
| public void verifyNoUpdateCapabilitiesOnMarkingNetworkTrustedBeforeT() throws Exception { |
| assumeFalse(SdkLevel.isAtLeastT()); |
| mConnectedNetwork.trusted = false; |
| connect(); |
| expectRegisterNetworkAgent((config) -> { }, (cap) -> { |
| assertFalse(cap.hasCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)); |
| }); |
| reset(mWifiNetworkAgent); |
| |
| WifiConfiguration oldConfig = new WifiConfiguration(mConnectedNetwork); |
| mConnectedNetwork.trusted = true; |
| |
| for (WifiConfigManager.OnNetworkUpdateListener listener : mConfigUpdateListenerCaptor |
| .getAllValues()) { |
| listener.onNetworkUpdated(mConnectedNetwork, oldConfig, false); |
| } |
| mLooper.dispatchAll(); |
| assertEquals("L3ConnectedState", getCurrentState().getName()); |
| verifyNoMoreInteractions(mWifiNetworkAgent); |
| } |
| |
| private void verifyUpdateAutoUpgradeFlagForSaeOnR( |
| boolean isWpa3SaeUpgradeEnabled, boolean isWpa2PersonalOnlyNetworkInRange, |
| boolean isWpa2Wpa3PersonalTransitionNetworkInRange, |
| boolean isWpa3PersonalOnlyNetworkInRange, boolean shouldBeUpdated) |
| throws Exception { |
| |
| when(mWifiGlobals.isWpa3SaeUpgradeEnabled()).thenReturn(isWpa3SaeUpgradeEnabled); |
| when(mScanRequestProxy.isWpa2PersonalOnlyNetworkInRange(any())) |
| .thenReturn(isWpa2PersonalOnlyNetworkInRange); |
| when(mScanRequestProxy.isWpa2Wpa3PersonalTransitionNetworkInRange(any())) |
| .thenReturn(isWpa2Wpa3PersonalTransitionNetworkInRange); |
| when(mScanRequestProxy.isWpa3PersonalOnlyNetworkInRange(any())) |
| .thenReturn(isWpa3PersonalOnlyNetworkInRange); |
| initializeAndAddNetworkAndVerifySuccess(); |
| |
| WifiConfiguration config = WifiConfigurationTestUtil.createPskSaeNetwork(); |
| config.networkId = TEST_NETWORK_ID; |
| when(mWifiConfigManager.getConfiguredNetwork(TEST_NETWORK_ID)).thenReturn(config); |
| |
| IActionListener connectActionListener = mock(IActionListener.class); |
| mCmi.connectNetwork( |
| new NetworkUpdateResult(TEST_NETWORK_ID), |
| new ActionListenerWrapper(connectActionListener), |
| Process.SYSTEM_UID, OP_PACKAGE_NAME); |
| mLooper.dispatchAll(); |
| verify(connectActionListener).onSuccess(); |
| if (shouldBeUpdated) { |
| verify(mWifiConfigManager).updateIsAddedByAutoUpgradeFlag( |
| eq(TEST_NETWORK_ID), eq(WifiConfiguration.SECURITY_TYPE_SAE), |
| eq(false)); |
| } else { |
| verify(mWifiConfigManager, never()).updateIsAddedByAutoUpgradeFlag( |
| anyInt(), anyInt(), anyBoolean()); |
| } |
| } |
| |
| /** |
| * Tests that manual connection to a network (from settings app) updates |
| * the auto upgrade flag for SAE on R. |
| * - SAE auto-upgrade is disabled. |
| * - No WPA2 PSK network |
| * - No WPA2/WPA3 network |
| * - A WPA3-SAE-only network exists. |
| */ |
| @Test |
| public void testManualConnectUpdateAutoUpgradeFlagForSaeOnR() throws Exception { |
| assumeFalse(SdkLevel.isAtLeastS()); |
| |
| verifyUpdateAutoUpgradeFlagForSaeOnR(false, false, false, true, true); |
| } |
| |
| /** |
| * Tests that manual connection to a network (from settings app) does not update |
| * the auto upgrade flag for SAE on R if auto-upgrade is enabled. |
| */ |
| @Test |
| public void testManualConnectNotUpdateAutoUpgradeFlagForSaeOnRWhenAutoUpgradeEnabled() |
| throws Exception { |
| assumeFalse(SdkLevel.isAtLeastS()); |
| |
| verifyUpdateAutoUpgradeFlagForSaeOnR(true, false, false, true, false); |
| } |
| |
| /** |
| * Tests that manual connection to a network (from settings app) does not update |
| * the auto upgrade flag for SAE on R if there are psk networks. |
| */ |
| @Test |
| public void testManualConnectNotUpdateAutoUpgradeFlagForSaeOnRWithPskNetworks() |
| throws Exception { |
| assumeFalse(SdkLevel.isAtLeastS()); |
| |
| verifyUpdateAutoUpgradeFlagForSaeOnR(false, true, false, true, false); |
| } |
| |
| /** |
| * Tests that manual connection to a network (from settings app) does not update |
| * the auto upgrade flag for SAE on R if there are psk/ase networks. |
| */ |
| @Test |
| public void testManualConnectNotUpdateAutoUpgradeFlagForSaeOnRWithPskSaeNetworks() |
| throws Exception { |
| assumeFalse(SdkLevel.isAtLeastS()); |
| |
| verifyUpdateAutoUpgradeFlagForSaeOnR(false, false, true, true, false); |
| } |
| |
| /** |
| * Tests that manual connection to a network (from settings app) does not update |
| * the auto upgrade flag for SAE on R if there is no WPA3 SAE only network.. |
| */ |
| @Test |
| public void testManualConnectNotUpdateAutoUpgradeFlagForSaeOnRWithoutSaeOnlyNetworks() |
| throws Exception { |
| assumeFalse(SdkLevel.isAtLeastS()); |
| |
| verifyUpdateAutoUpgradeFlagForSaeOnR(false, false, true, true, false); |
| } |
| |
| private WifiConfiguration setupLegacyEapNetworkTest(boolean isUserSelected) throws Exception { |
| return setupTrustOnFirstUse(false, false, isUserSelected); |
| } |
| |
| private WifiConfiguration setupTrustOnFirstUse( |
| boolean isAtLeastT, boolean isTrustOnFirstUseSupported, boolean isUserSelected) |
| throws Exception { |
| if (isTrustOnFirstUseSupported) { |
| when(mWifiNative.getSupportedFeatureSet(WIFI_IFACE_NAME)).thenReturn( |
| WifiManager.WIFI_FEATURE_TRUST_ON_FIRST_USE); |
| } |
| mCmi.mInsecureEapNetworkHandler = mInsecureEapNetworkHandler; |
| |
| WifiConfiguration eapTlsConfig = spy(WifiConfigurationTestUtil.createEapNetwork( |
| WifiEnterpriseConfig.Eap.TLS, WifiEnterpriseConfig.Phase2.NONE)); |
| eapTlsConfig.networkId = FRAMEWORK_NETWORK_ID; |
| eapTlsConfig.SSID = TEST_SSID; |
| if (isAtLeastT && isTrustOnFirstUseSupported) { |
| eapTlsConfig.enterpriseConfig.enableTrustOnFirstUse(true); |
| } |
| eapTlsConfig.enterpriseConfig.setCaPath(""); |
| eapTlsConfig.enterpriseConfig.setDomainSuffixMatch(""); |
| eapTlsConfig.setRandomizedMacAddress(TEST_LOCAL_MAC_ADDRESS); |
| eapTlsConfig.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_AUTO; |
| |
| initializeAndAddNetworkAndVerifySuccess(eapTlsConfig); |
| |
| if (isUserSelected) { |
| startConnectSuccess(); |
| } else { |
| mCmi.startConnectToNetwork( |
| eapTlsConfig.networkId, |
| Process.SYSTEM_UID, |
| ClientModeImpl.SUPPLICANT_BSSID_ANY); |
| mLooper.dispatchAll(); |
| } |
| verify(mInsecureEapNetworkHandler).prepareConnection(eq(eapTlsConfig)); |
| |
| if (isTrustOnFirstUseSupported) { |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(0, TEST_WIFI_SSID, TEST_BSSID_STR, sFreq, |
| SupplicantState.ASSOCIATED)); |
| mLooper.dispatchAll(); |
| |
| CertificateEventInfo certificateEventInfo = |
| spy(new CertificateEventInfo(FakeKeys.CA_CERT0, "1234")); |
| mCmi.sendMessage(WifiMonitor.TOFU_CERTIFICATE_EVENT, |
| FRAMEWORK_NETWORK_ID, 0, certificateEventInfo); |
| mLooper.dispatchAll(); |
| verify(mInsecureEapNetworkHandler).addPendingCertificate( |
| eq(eapTlsConfig.networkId), eq(0), eq(certificateEventInfo)); |
| |
| // Adding a certificate in depth 0 will cause a disconnection when TOFU is supported |
| DisconnectEventInfo disconnectEventInfo = |
| new DisconnectEventInfo(TEST_SSID, TEST_BSSID_STR, 3, true); |
| mCmi.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, disconnectEventInfo); |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(0, TEST_WIFI_SSID, TEST_BSSID_STR, sFreq, |
| SupplicantState.DISCONNECTED)); |
| mLooper.dispatchAll(); |
| } |
| |
| verify(mInsecureEapNetworkHandler).startUserApprovalIfNecessary(eq(isUserSelected)); |
| if (isTrustOnFirstUseSupported) { |
| assertEquals("DisconnectedState", getCurrentState().getName()); |
| } |
| return eapTlsConfig; |
| } |
| |
| /** |
| * Verify Trust On First Use support. |
| * - This network is selected by a user. |
| * - Accept the connection. |
| */ |
| @Test |
| public void verifyTrustOnFirstUseAcceptWhenConnectByUser() throws Exception { |
| assumeTrue(SdkLevel.isAtLeastT()); |
| WifiConfiguration testConfig = setupTrustOnFirstUse(true, true, true); |
| |
| mCmi.mInsecureEapNetworkHandlerCallbacksImpl.onAccept(testConfig.SSID, |
| testConfig.networkId); |
| mLooper.dispatchAll(); |
| ArgumentCaptor<WifiConfiguration> wifiConfigurationArgumentCaptor = |
| ArgumentCaptor.forClass(WifiConfiguration.class); |
| |
| // TOFU will first connect to get the certificates, and then connect once approved |
| verify(mWifiNative, times(2)).connectToNetwork(eq(WIFI_IFACE_NAME), |
| wifiConfigurationArgumentCaptor.capture()); |
| assertEquals(testConfig.networkId, wifiConfigurationArgumentCaptor.getValue().networkId); |
| } |
| |
| /** |
| * Verify Trust On First Use support. |
| * - This network is selected by a user. |
| * - Reject the connection. |
| */ |
| @Test |
| public void verifyTrustOnFirstUseRejectWhenConnectByUser() throws Exception { |
| assumeTrue(SdkLevel.isAtLeastT()); |
| WifiConfiguration testConfig = setupTrustOnFirstUse(true, true, true); |
| |
| mCmi.mInsecureEapNetworkHandlerCallbacksImpl.onReject(testConfig.SSID, false); |
| mLooper.dispatchAll(); |
| verify(mWifiConnectivityManager, never()) |
| .forceConnectivityScan(eq(ClientModeImpl.WIFI_WORK_SOURCE)); |
| verify(mWifiMetrics).endConnectionEvent( |
| any(), eq(WifiMetrics.ConnectionEvent.FAILURE_NETWORK_DISCONNECTION), |
| eq(WifiMetricsProto.ConnectionEvent.HLF_NONE), |
| eq(WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN), |
| anyInt(), anyInt()); |
| ArgumentCaptor<WifiConfiguration> wifiConfigurationArgumentCaptor = |
| ArgumentCaptor.forClass(WifiConfiguration.class); |
| |
| // TOFU will connect only once to get the certificates, but will not proceed |
| verify(mWifiNative).connectToNetwork(eq(WIFI_IFACE_NAME), |
| wifiConfigurationArgumentCaptor.capture()); |
| assertEquals(testConfig.networkId, wifiConfigurationArgumentCaptor.getValue().networkId); |
| |
| } |
| |
| /** |
| * Verify Trust On First Use support. |
| * - This network is selected by a user. |
| * - Errors occur in InsecureEapNetworkHandler. |
| */ |
| @Test |
| public void verifyTrustOnFirstUseErrorWhenConnectByUser() throws Exception { |
| assumeTrue(SdkLevel.isAtLeastT()); |
| WifiConfiguration testConfig = setupTrustOnFirstUse(true, true, true); |
| |
| mCmi.mInsecureEapNetworkHandlerCallbacksImpl.onError(testConfig.SSID); |
| mLooper.dispatchAll(); |
| verify(mWifiConnectivityManager, never()) |
| .forceConnectivityScan(eq(ClientModeImpl.WIFI_WORK_SOURCE)); |
| verify(mWifiMetrics).endConnectionEvent( |
| any(), eq(WifiMetrics.ConnectionEvent.FAILURE_NETWORK_DISCONNECTION), |
| eq(WifiMetricsProto.ConnectionEvent.HLF_NONE), |
| eq(WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN), |
| anyInt(), anyInt()); |
| } |
| |
| /** |
| * Verify Trust On First Use support. |
| * - this network is automatically connected. |
| * - Tap the notification. |
| * - Accept the connection. |
| */ |
| @Test |
| public void verifyTrustOnFirstUseAcceptWhenAutoConnect() throws Exception { |
| assumeTrue(SdkLevel.isAtLeastT()); |
| WifiConfiguration testConfig = setupTrustOnFirstUse(true, true, false); |
| |
| mCmi.mInsecureEapNetworkHandlerCallbacksImpl.onAccept(testConfig.SSID, |
| testConfig.networkId); |
| mLooper.dispatchAll(); |
| ArgumentCaptor<WifiConfiguration> wifiConfigurationArgumentCaptor = |
| ArgumentCaptor.forClass(WifiConfiguration.class); |
| |
| // TOFU will first connect to get the certificates, and then connect once approved |
| verify(mWifiNative, times(2)).connectToNetwork(eq(WIFI_IFACE_NAME), |
| wifiConfigurationArgumentCaptor.capture()); |
| assertEquals(testConfig.networkId, wifiConfigurationArgumentCaptor.getValue().networkId); |
| } |
| |
| /** |
| * Verify Trust On First Use support. |
| * - this network is automatically connected. |
| * - Tap the notification |
| * - Reject the connection. |
| */ |
| @Test |
| public void verifyTrustOnFirstUseRejectWhenAutoConnect() throws Exception { |
| assumeTrue(SdkLevel.isAtLeastT()); |
| WifiConfiguration testConfig = setupTrustOnFirstUse(true, true, false); |
| |
| mCmi.mInsecureEapNetworkHandlerCallbacksImpl.onReject(testConfig.SSID, false); |
| mLooper.dispatchAll(); |
| verify(mWifiConnectivityManager, never()) |
| .forceConnectivityScan(eq(ClientModeImpl.WIFI_WORK_SOURCE)); |
| verify(mWifiMetrics).endConnectionEvent( |
| any(), eq(WifiMetrics.ConnectionEvent.FAILURE_NETWORK_DISCONNECTION), |
| eq(WifiMetricsProto.ConnectionEvent.HLF_NONE), |
| eq(WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN), |
| anyInt(), anyInt()); |
| ArgumentCaptor<WifiConfiguration> wifiConfigurationArgumentCaptor = |
| ArgumentCaptor.forClass(WifiConfiguration.class); |
| |
| // TOFU will connect only once to get the certificates, but will not proceed |
| verify(mWifiNative).connectToNetwork(eq(WIFI_IFACE_NAME), |
| wifiConfigurationArgumentCaptor.capture()); |
| assertEquals(testConfig.networkId, wifiConfigurationArgumentCaptor.getValue().networkId); |
| } |
| |
| /** |
| * Verify Trust On First Use support. |
| * - This network is automatically connected. |
| * - Errors occur in InsecureEapNetworkHandler. |
| */ |
| @Test |
| public void verifyTrustOnFirstUseErrorWhenAutoConnect() throws Exception { |
| assumeTrue(SdkLevel.isAtLeastT()); |
| WifiConfiguration testConfig = setupTrustOnFirstUse(true, true, false); |
| |
| mCmi.mInsecureEapNetworkHandlerCallbacksImpl.onError(testConfig.SSID); |
| mLooper.dispatchAll(); |
| verify(mWifiConnectivityManager, never()) |
| .forceConnectivityScan(eq(ClientModeImpl.WIFI_WORK_SOURCE)); |
| verify(mWifiMetrics).endConnectionEvent( |
| any(), eq(WifiMetrics.ConnectionEvent.FAILURE_NETWORK_DISCONNECTION), |
| eq(WifiMetricsProto.ConnectionEvent.HLF_NONE), |
| eq(WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN), |
| anyInt(), anyInt()); |
| } |
| |
| /** |
| * Verify that InsecureEapNetworkHandler cleanup() is called if wifi is disabled. |
| */ |
| @Test |
| public void verifyInsecureEapNetworkCleanUpWhenDisabled() throws Exception { |
| mCmi.stop(); |
| mLooper.dispatchAll(); |
| verify(mInsecureEapNetworkHandler).cleanup(); |
| } |
| |
| /** |
| * Verify legacy EAP network handling. |
| * - This network is selected by a user. |
| * - Accept the connection. |
| */ |
| @Test |
| public void verifyLegacyEapNetworkAcceptWhenConnectByUser() throws Exception { |
| assumeFalse(SdkLevel.isAtLeastT()); |
| WifiConfiguration testConfig = setupLegacyEapNetworkTest(true); |
| |
| mCmi.mInsecureEapNetworkHandlerCallbacksImpl.onAccept(testConfig.SSID, |
| testConfig.networkId); |
| mLooper.dispatchAll(); |
| verify(mWifiMetrics, never()).endConnectionEvent( |
| any(), anyInt(), anyInt(), anyInt(), anyInt(), anyInt()); |
| } |
| |
| /** |
| * Verify legacy EAP network handling. |
| * - This network is selected by a user. |
| * - Reject the connection. |
| */ |
| @Test |
| public void verifyLegacyEapNetworkRejectWhenConnectByUser() throws Exception { |
| assumeFalse(SdkLevel.isAtLeastT()); |
| WifiConfiguration testConfig = setupLegacyEapNetworkTest(true); |
| |
| mCmi.mInsecureEapNetworkHandlerCallbacksImpl.onReject(testConfig.SSID, true); |
| mLooper.dispatchAll(); |
| verify(mWifiConnectivityManager, never()) |
| .forceConnectivityScan(eq(ClientModeImpl.WIFI_WORK_SOURCE)); |
| verify(mWifiNative).disconnect(eq(WIFI_IFACE_NAME)); |
| } |
| |
| /** |
| * Verify legacy EAP network handling. |
| * - This network is automatically connected. |
| * - Tap "connect anyway" on the notification. |
| */ |
| @Test |
| public void verifyLegacyEapNetworkAcceptOnNotificationWhenAutoConnect() throws Exception { |
| assumeFalse(SdkLevel.isAtLeastT()); |
| WifiConfiguration testConfig = setupLegacyEapNetworkTest(false); |
| |
| mCmi.mInsecureEapNetworkHandlerCallbacksImpl.onAccept(testConfig.SSID, |
| testConfig.networkId); |
| mLooper.dispatchAll(); |
| verify(mWifiMetrics, never()).endConnectionEvent( |
| any(), anyInt(), anyInt(), anyInt(), anyInt(), anyInt()); |
| } |
| |
| /** |
| * Verify legacy EAP network handling. |
| * - This network is automatically connected. |
| * - Tap "Disconnect now" on the notification |
| */ |
| @Test |
| public void verifyLegacyEapNetworkRejectOnNotificationWhenAutoConnect() throws Exception { |
| assumeFalse(SdkLevel.isAtLeastT()); |
| WifiConfiguration testConfig = setupLegacyEapNetworkTest(false); |
| |
| mCmi.mInsecureEapNetworkHandlerCallbacksImpl.onReject(testConfig.SSID, true); |
| mLooper.dispatchAll(); |
| verify(mFrameworkFacade, never()).makeAlertDialogBuilder(any()); |
| verify(mWifiConnectivityManager, never()) |
| .forceConnectivityScan(eq(ClientModeImpl.WIFI_WORK_SOURCE)); |
| verify(mWifiNative).disconnect(eq(WIFI_IFACE_NAME)); |
| } |
| |
| private void setScanResultWithMloInfo() { |
| List<MloLink> mloLinks = new ArrayList<>(); |
| MloLink link1 = new MloLink(); |
| link1.setBand(WifiScanner.WIFI_BAND_24_GHZ); |
| link1.setChannel(TEST_CHANNEL); |
| link1.setApMacAddress(MacAddress.fromString(TEST_BSSID_STR)); |
| link1.setLinkId(TEST_MLO_LINK_ID); |
| MloLink link2 = new MloLink(); |
| link2.setBand(WifiScanner.WIFI_BAND_5_GHZ); |
| link2.setChannel(TEST_CHANNEL_1); |
| link2.setApMacAddress(MacAddress.fromString(TEST_BSSID_STR1)); |
| link2.setLinkId(TEST_MLO_LINK_ID_1); |
| mloLinks.add(link1); |
| mloLinks.add(link2); |
| |
| when(mScanResult.getApMldMacAddress()).thenReturn(TEST_AP_MLD_MAC_ADDRESS); |
| when(mScanResult.getApMloLinkId()).thenReturn(TEST_MLO_LINK_ID); |
| when(mScanResult.getAffiliatedMloLinks()).thenReturn(mloLinks); |
| |
| when(mWifiConfigManager.getScanDetailCacheForNetwork(FRAMEWORK_NETWORK_ID)) |
| .thenReturn(mScanDetailCache); |
| when(mScanDetailCache.getScanResult(any())).thenReturn(mScanResult); |
| |
| } |
| |
| private void setScanResultWithoutMloInfo() { |
| mConnectionCapabilities.wifiStandard = ScanResult.WIFI_STANDARD_11AC; |
| |
| when(mScanResult.getApMldMacAddress()).thenReturn(null); |
| when(mScanResult.getApMloLinkId()).thenReturn(MloLink.INVALID_MLO_LINK_ID); |
| when(mScanResult.getAffiliatedMloLinks()).thenReturn(Collections.emptyList()); |
| |
| when(mScanRequestProxy.getScanResults()).thenReturn(Arrays.asList(mScanResult)); |
| when(mScanRequestProxy.getScanResult(any())).thenReturn(mScanResult); |
| } |
| |
| private void setConnection() throws Exception { |
| WifiConfiguration config = createTestNetwork(false); |
| setupAndStartConnectSequence(config); |
| validateSuccessfulConnectSequence(config); |
| } |
| |
| private void setConnectionMloLinksInfo() { |
| mConnectionCapabilities.wifiStandard = ScanResult.WIFI_STANDARD_11BE; |
| WifiNative.ConnectionMloLinksInfo info = new WifiNative.ConnectionMloLinksInfo(); |
| info.links = new WifiNative.ConnectionMloLink[2]; |
| info.links[0] = new WifiNative.ConnectionMloLink(TEST_MLO_LINK_ID, TEST_MLO_LINK_ADDR, |
| TEST_AP_MLD_MAC_ADDRESS, Byte.MIN_VALUE, Byte.MAX_VALUE, 5160); |
| info.links[1] = new WifiNative.ConnectionMloLink(TEST_MLO_LINK_ID_1, TEST_MLO_LINK_ADDR_1, |
| TEST_AP_MLD_MAC_ADDRESS, Byte.MAX_VALUE, Byte.MIN_VALUE, 2437); |
| when(mWifiNative.getConnectionMloLinksInfo(WIFI_IFACE_NAME)).thenReturn(info); |
| } |
| |
| /** |
| * Verify Affiliated link BSSID matching |
| */ |
| @Test |
| public void testAffiliatedLinkBssidMatch() throws Exception { |
| setConnection(); |
| setScanResultWithMloInfo(); |
| // Associate |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(FRAMEWORK_NETWORK_ID, TEST_WIFI_SSID, TEST_BSSID_STR, sFreq, |
| SupplicantState.ASSOCIATED)); |
| mLooper.dispatchAll(); |
| // Validate Affiliated BSSID match |
| assertTrue(mCmi.isAffiliatedLinkBssid(MacAddress.fromString(TEST_BSSID_STR))); |
| assertTrue(mCmi.isAffiliatedLinkBssid(MacAddress.fromString(TEST_BSSID_STR1))); |
| assertFalse(mCmi.isAffiliatedLinkBssid(MacAddress.fromString(TEST_BSSID_STR2))); |
| } |
| |
| /** |
| * Verify MLO parameters update from ScanResult at association |
| */ |
| @Test |
| public void verifyMloParametersUpdateAssoc() throws Exception { |
| setConnection(); |
| setScanResultWithMloInfo(); |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(FRAMEWORK_NETWORK_ID, TEST_WIFI_SSID, TEST_BSSID_STR, sFreq, |
| SupplicantState.ASSOCIATED)); |
| mLooper.dispatchAll(); |
| |
| WifiInfo connectionInfo = mCmi.getConnectionInfo(); |
| assertNotNull(connectionInfo.getApMldMacAddress()); |
| assertEquals(TEST_AP_MLD_MAC_ADDRESS_STR, connectionInfo.getApMldMacAddress().toString()); |
| assertEquals(TEST_MLO_LINK_ID, connectionInfo.getApMloLinkId()); |
| assertEquals(2, connectionInfo.getAffiliatedMloLinks().size()); |
| // None of the links are active or idle. |
| assertTrue(connectionInfo.getAssociatedMloLinks().isEmpty()); |
| } |
| |
| /** |
| * Verify MLO parameters update when roaming to a MLD ap, and then get cleared when roaming to |
| * a non MLD supported AP. |
| */ |
| @Test |
| public void verifyMloParametersUpdateRoam() throws Exception { |
| connect(); |
| setScanResultWithMloInfo(); |
| |
| // Roam to an MLD AP |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(FRAMEWORK_NETWORK_ID, TEST_WIFI_SSID, TEST_BSSID_STR, sFreq, |
| SupplicantState.ASSOCIATED)); |
| mLooper.dispatchAll(); |
| |
| WifiInfo connectionInfo = mCmi.getConnectionInfo(); |
| assertNotNull(connectionInfo.getApMldMacAddress()); |
| assertEquals(TEST_AP_MLD_MAC_ADDRESS_STR, connectionInfo.getApMldMacAddress().toString()); |
| assertEquals(TEST_MLO_LINK_ID, connectionInfo.getApMloLinkId()); |
| assertEquals(2, connectionInfo.getAffiliatedMloLinks().size()); |
| |
| // Now perform Roaming to a non-MLD AP |
| setScanResultWithoutMloInfo(); |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(FRAMEWORK_NETWORK_ID, TEST_WIFI_SSID, TEST_BSSID_STR, sFreq, |
| SupplicantState.ASSOCIATED)); |
| mLooper.dispatchAll(); |
| |
| connectionInfo = mCmi.getConnectionInfo(); |
| assertNull(connectionInfo.getApMldMacAddress()); |
| assertEquals(MloLink.INVALID_MLO_LINK_ID, connectionInfo.getApMloLinkId()); |
| assertTrue(connectionInfo.getAffiliatedMloLinks().isEmpty()); |
| assertTrue(connectionInfo.getAssociatedMloLinks().isEmpty()); |
| } |
| |
| /** |
| * Verify API calls on affiliated BSSIDs during association and disconnect. |
| */ |
| @Test |
| public void verifyAffiliatedBssidsAssocDisconnect() throws Exception { |
| List<String> affiliatedBssids = Arrays.asList(TEST_BSSID_STR1); |
| |
| connect(); |
| setScanResultWithMloInfo(); |
| setConnectionMloLinksInfo(); |
| mLooper.dispatchAll(); |
| |
| // Association |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(FRAMEWORK_NETWORK_ID, TEST_WIFI_SSID, TEST_BSSID_STR, sFreq, |
| SupplicantState.ASSOCIATED)); |
| mLooper.dispatchAll(); |
| verify(mWifiBlocklistMonitor).setAffiliatedBssids(eq(TEST_BSSID_STR), eq(affiliatedBssids)); |
| |
| // Disconnect |
| DisconnectEventInfo disconnectEventInfo = |
| new DisconnectEventInfo(TEST_SSID, TEST_BSSID_STR, 0, false); |
| mCmi.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, disconnectEventInfo); |
| mLooper.dispatchAll(); |
| verify(mWifiBlocklistMonitor).removeAffiliatedBssids(eq(TEST_BSSID_STR)); |
| } |
| |
| private void configureMloLinksInfoWithIdleLinks() { |
| mConnectionCapabilities.wifiStandard = ScanResult.WIFI_STANDARD_11BE; |
| WifiNative.ConnectionMloLinksInfo info = new WifiNative.ConnectionMloLinksInfo(); |
| info.links = new WifiNative.ConnectionMloLink[2]; |
| info.links[0] = new WifiNative.ConnectionMloLink(TEST_MLO_LINK_ID, TEST_MLO_LINK_ADDR, |
| TEST_AP_MLD_MAC_ADDRESS, (byte) 0xFF, (byte) 0xFF, 2437); |
| info.links[1] = new WifiNative.ConnectionMloLink(TEST_MLO_LINK_ID_1, TEST_MLO_LINK_ADDR_1, |
| TEST_AP_MLD_MAC_ADDRESS, (byte) 0, (byte) 0, 5160); |
| when(mWifiNative.getConnectionMloLinksInfo(WIFI_IFACE_NAME)).thenReturn(info); |
| } |
| |
| private void reconfigureMloLinksInfoWithOneLink() { |
| mConnectionCapabilities.wifiStandard = ScanResult.WIFI_STANDARD_11BE; |
| WifiNative.ConnectionMloLinksInfo info = new WifiNative.ConnectionMloLinksInfo(); |
| info.links = new WifiNative.ConnectionMloLink[1]; |
| info.links[0] = new WifiNative.ConnectionMloLink(TEST_MLO_LINK_ID, TEST_MLO_LINK_ADDR, |
| TEST_AP_MLD_MAC_ADDRESS, (byte) 0xFF, (byte) 0xFF, 2437); |
| when(mWifiNative.getConnectionMloLinksInfo(WIFI_IFACE_NAME)).thenReturn(info); |
| } |
| |
| @Test |
| public void verifyMloLinkChangeTidToLinkMapping() throws Exception { |
| // Initialize |
| connect(); |
| setScanResultWithMloInfo(); |
| setConnectionMloLinksInfo(); |
| mLooper.dispatchAll(); |
| |
| // Association |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(FRAMEWORK_NETWORK_ID, TEST_WIFI_SSID, TEST_BSSID_STR, sFreq, |
| SupplicantState.ASSOCIATED)); |
| mLooper.dispatchAll(); |
| // Make sure all links are active |
| for (MloLink link: mWifiInfo.getAffiliatedMloLinks()) { |
| assertEquals(link.getState(), MloLink.MLO_LINK_STATE_ACTIVE); |
| } |
| // All links are associated (ACTIVE) |
| assertEquals(mWifiInfo.getAssociatedMloLinks().size(), |
| mWifiInfo.getAffiliatedMloLinks().size()); |
| |
| // TID to link mapping. Make sure one link is IDLE. |
| configureMloLinksInfoWithIdleLinks(); |
| mCmi.sendMessage(WifiMonitor.MLO_LINKS_INFO_CHANGED, |
| WifiMonitor.MloLinkInfoChangeReason.TID_TO_LINK_MAP); |
| mLooper.dispatchAll(); |
| List<MloLink> links = mWifiInfo.getAffiliatedMloLinks(); |
| assertEquals(links.get(0).getState(), MloLink.MLO_LINK_STATE_ACTIVE); |
| assertEquals(links.get(1).getState(), MloLink.MLO_LINK_STATE_IDLE); |
| // All links are associated (ACTIVE & IDLE) |
| assertEquals(mWifiInfo.getAssociatedMloLinks().size(), |
| mWifiInfo.getAffiliatedMloLinks().size()); |
| |
| // LInk Removal. Make sure removed link is UNASSOCIATED. |
| reconfigureMloLinksInfoWithOneLink(); |
| mCmi.sendMessage(WifiMonitor.MLO_LINKS_INFO_CHANGED, |
| WifiMonitor.MloLinkInfoChangeReason.MULTI_LINK_RECONFIG_AP_REMOVAL); |
| mLooper.dispatchAll(); |
| links = mWifiInfo.getAffiliatedMloLinks(); |
| assertEquals(links.get(0).getState(), MloLink.MLO_LINK_STATE_ACTIVE); |
| assertEquals(links.get(1).getState(), MloLink.MLO_LINK_STATE_UNASSOCIATED); |
| // One link is unassociated. |
| assertEquals(2, mWifiInfo.getAffiliatedMloLinks().size()); |
| assertEquals(1, mWifiInfo.getAssociatedMloLinks().size()); |
| |
| } |
| |
| @Test |
| public void verifyMloLinkChangeAfterAssociation() throws Exception { |
| // Initialize |
| connect(); |
| setScanResultWithMloInfo(); |
| setConnectionMloLinksInfo(); |
| mLooper.dispatchAll(); |
| |
| // Association |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(FRAMEWORK_NETWORK_ID, TEST_WIFI_SSID, TEST_BSSID_STR, sFreq, |
| SupplicantState.ASSOCIATED)); |
| mLooper.dispatchAll(); |
| // Verify all links are associated (ACTIVE) after assciation. |
| assertEquals(mWifiInfo.getAssociatedMloLinks().size(), |
| mWifiInfo.getAffiliatedMloLinks().size()); |
| |
| // verify that affiliated link state the scan results are still in unassociated state. |
| for (MloLink link : mScanResult.getAffiliatedMloLinks()) { |
| assertEquals(MloLink.MLO_LINK_STATE_UNASSOCIATED, link.getState()); |
| } |
| // verify that affiliated link state are active |
| for (MloLink link : mWifiInfo.getAffiliatedMloLinks()) { |
| assertEquals(MloLink.MLO_LINK_STATE_ACTIVE, link.getState()); |
| } |
| |
| setScanResultWithMloInfo(); |
| // Send FOUR_WAY_HANDSHAKE, GROUP_HANDSHAKE and COMPLETED and verify all links are still |
| // asscoaited. |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(FRAMEWORK_NETWORK_ID, TEST_WIFI_SSID, TEST_BSSID_STR, sFreq, |
| SupplicantState.FOUR_WAY_HANDSHAKE)); |
| |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(FRAMEWORK_NETWORK_ID, TEST_WIFI_SSID, TEST_BSSID_STR, sFreq, |
| SupplicantState.GROUP_HANDSHAKE)); |
| |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(FRAMEWORK_NETWORK_ID, TEST_WIFI_SSID, TEST_BSSID_STR, sFreq, |
| SupplicantState.COMPLETED)); |
| mLooper.dispatchAll(); |
| // Verify all links are still associated (ACTIVE). |
| assertEquals(mWifiInfo.getAssociatedMloLinks().size(), |
| mWifiInfo.getAffiliatedMloLinks().size()); |
| } |
| /** |
| * Verify that an event that occurs on a managed network is handled by |
| * logEventIfManagedNetwork. |
| */ |
| @Test |
| public void verifyEventHandledByLogEventIfManagedNetwork() throws Exception { |
| assumeTrue(SdkLevel.isAtLeastT()); |
| MacAddress bssid = MacAddress.fromString(TEST_BSSID_STR); |
| int numMaskedOctets = 4; |
| |
| mResources.setInteger( |
| R.integer.config_wifiNumMaskedBssidOctetsInSecurityLog, numMaskedOctets); |
| when(mWifiPermissionsUtil.isAdmin(anyInt(), any())).thenReturn(true); |
| MockitoSession scanResultUtilSession = ExtendedMockito.mockitoSession() |
| .strictness(Strictness.LENIENT) |
| .mockStatic(ScanResultUtil.class, withSettings().lenient()) |
| .startMocking(); |
| connect(); |
| |
| // Connect will generate the Associated and Connected events. Confirm the events were |
| // handled by checking that redactBssid() was called for each one. |
| ExtendedMockito.verify(() -> |
| ScanResultUtil.redactBssid(bssid, numMaskedOctets), times(2)); |
| scanResultUtilSession.finishMocking(); |
| } |
| |
| /** |
| * Verify that QoS policy reset events are handled when mNetworkAgent is non-null. |
| */ |
| @Test |
| public void verifyQosPolicyResetEventWithNonNullNetworkAgent() throws Exception { |
| assumeTrue(SdkLevel.isAtLeastT()); |
| connect(); |
| assertNotNull(mCmi.mNetworkAgent); |
| mCmi.sendMessage(WifiMonitor.QOS_POLICY_RESET_EVENT, 0, 0, null); |
| mLooper.dispatchAll(); |
| verify(mWifiNetworkAgent).sendRemoveAllDscpPolicies(); |
| } |
| |
| /** |
| * Verify that QoS policy reset events are not handled when mNetworkAgent is null. |
| */ |
| @Test |
| public void verifyQosPolicyResetEventWithNullNetworkAgent() throws Exception { |
| assumeTrue(SdkLevel.isAtLeastT()); |
| assertNull(mCmi.mNetworkAgent); |
| mCmi.sendMessage(WifiMonitor.QOS_POLICY_RESET_EVENT, 0, 0, null); |
| mLooper.dispatchAll(); |
| verify(mWifiNetworkAgent, never()).sendRemoveAllDscpPolicies(); |
| } |
| |
| /** |
| * Verify that QoS policy reset events are not handled if the QoS policy feature was |
| * not enabled in WifiNative. |
| */ |
| @Test |
| public void verifyQosPolicyResetEventWhenQosFeatureNotEnabled() throws Exception { |
| assumeTrue(SdkLevel.isAtLeastT()); |
| connect(); |
| assertNotNull(mCmi.mNetworkAgent); |
| when(mWifiNative.isQosPolicyFeatureEnabled()).thenReturn(false); |
| mCmi.sendMessage(WifiMonitor.QOS_POLICY_RESET_EVENT, 0, 0, null); |
| mLooper.dispatchAll(); |
| verify(mWifiNetworkAgent, never()).sendRemoveAllDscpPolicies(); |
| } |
| |
| /** |
| * Verify that QoS policy request events are handled when mNetworkAgent is non-null. |
| */ |
| @Test |
| public void verifyQosPolicyRequestEvent() throws Exception { |
| assumeTrue(SdkLevel.isAtLeastT()); |
| final int dialogToken = 124; |
| |
| // Event should be ignored by the QosPolicyRequestHandler if no network is connected, |
| // since mNetworkAgent is null. |
| assertNull(mCmi.mNetworkAgent); |
| mCmi.sendMessage(WifiMonitor.QOS_POLICY_REQUEST_EVENT, dialogToken, 0, |
| new ArrayList<SupplicantStaIfaceHal.QosPolicyRequest>()); |
| mLooper.dispatchAll(); |
| |
| // New events should be processed after connecting to a network, |
| // since mNetworkAgent is not null. |
| connect(); |
| assertNotNull(mCmi.mNetworkAgent); |
| mCmi.sendMessage(WifiMonitor.QOS_POLICY_REQUEST_EVENT, dialogToken + 1, 0, |
| new ArrayList<SupplicantStaIfaceHal.QosPolicyRequest>()); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiNative, never()).sendQosPolicyResponse( |
| eq(WIFI_IFACE_NAME), eq(dialogToken), eq(true), any()); |
| verify(mWifiNative).sendQosPolicyResponse( |
| eq(WIFI_IFACE_NAME), eq(dialogToken + 1), eq(true), any()); |
| } |
| |
| private void verifyConnectWithDisabledType( |
| WifiConfiguration config, List<ScanResult> results, |
| boolean shouldDropRequest) throws Exception { |
| WifiDialogManager mockWifiDialogManager = mock(WifiDialogManager.class); |
| WifiDialogManager.DialogHandle mockDialogHandle = |
| mock(WifiDialogManager.DialogHandle.class); |
| when(mockWifiDialogManager.createLegacySimpleDialog(any(), any(), any(), any(), any(), |
| any(), any())).thenReturn(mockDialogHandle); |
| when(mWifiInjector.getWifiDialogManager()).thenReturn(mockWifiDialogManager); |
| // Add some irrelevant networks to ensure they are filtered. |
| final String irrelevantSsid = "IrrelevantSsid"; |
| results.add(makeScanResult(irrelevantSsid, "[PSK]")); |
| results.add(makeScanResult(irrelevantSsid, "[SAE]")); |
| results.add(makeScanResult(irrelevantSsid, "[PSK][SAE]")); |
| results.add(makeScanResult(irrelevantSsid, "")); |
| results.add(makeScanResult(irrelevantSsid, "[OWE_TRANSITION]")); |
| results.add(makeScanResult(irrelevantSsid, "[OWE]")); |
| results.add(makeScanResult(irrelevantSsid, "[RSN][EAP/SHA1][MFPC]")); |
| results.add(makeScanResult(irrelevantSsid, "[RSN][EAP/SHA1][EAP/SHA256][MFPC]")); |
| results.add(makeScanResult(irrelevantSsid, "[RSN][EAP/SHA256][MFPC][MFPR]")); |
| when(mScanRequestProxy.getScanResults()).thenReturn(results); |
| |
| initializeAndAddNetworkAndVerifySuccess(); |
| |
| when(mWifiConfigManager.getConfiguredNetworkWithoutMasking(TEST_NETWORK_ID)) |
| .thenReturn(config); |
| when(mWifiConfigManager.getConfiguredNetwork(TEST_NETWORK_ID)).thenReturn(config); |
| |
| IActionListener connectActionListener = mock(IActionListener.class); |
| mCmi.connectNetwork( |
| new NetworkUpdateResult(TEST_NETWORK_ID), |
| new ActionListenerWrapper(connectActionListener), |
| Process.SYSTEM_UID, OP_PACKAGE_NAME); |
| mLooper.dispatchAll(); |
| verify(connectActionListener).onSuccess(); |
| if (shouldDropRequest) { |
| verify(mockDialogHandle).launchDialog(); |
| verify(mWifiNative, never()).connectToNetwork(any(), any()); |
| } else { |
| verify(mockDialogHandle, never()).launchDialog(); |
| verify(mWifiNative).connectToNetwork(eq(WIFI_IFACE_NAME), eq(config)); |
| } |
| } |
| |
| private ScanResult makeScanResult(String ssid, String caps) { |
| ScanResult scanResult = new ScanResult(WifiSsid.fromUtf8Text(ssid.replace("\"", "")), |
| ssid, TEST_BSSID_STR, 1245, 0, caps, -78, 2412, 1025, 22, 33, 20, 0, 0, true); |
| ScanResult.InformationElement ie = createIE(ScanResult.InformationElement.EID_SSID, |
| ssid.getBytes(StandardCharsets.UTF_8)); |
| scanResult.informationElements = new ScanResult.InformationElement[]{ie}; |
| return scanResult; |
| |
| } |
| |
| private void verifyConnectWithDisabledPskType( |
| boolean isWpa2PersonalOnlyNetworkInRange, |
| boolean isWpa2Wpa3PersonalTransitionNetworkInRange, |
| boolean isWpa3PersonalOnlyNetworkInRange, boolean shouldDropRequest) |
| throws Exception { |
| WifiConfiguration config = WifiConfigurationTestUtil.createPskSaeNetwork(); |
| config.networkId = TEST_NETWORK_ID; |
| config.setSecurityParamsEnabled(WifiConfiguration.SECURITY_TYPE_PSK, false); |
| List<ScanResult> results = new ArrayList<>(); |
| if (isWpa2PersonalOnlyNetworkInRange) { |
| results.add(makeScanResult(config.SSID, "[PSK]")); |
| } |
| if (isWpa2Wpa3PersonalTransitionNetworkInRange) { |
| results.add(makeScanResult(config.SSID, "[PSK][SAE]")); |
| } |
| if (isWpa3PersonalOnlyNetworkInRange) { |
| results.add(makeScanResult(config.SSID, "[SAE]")); |
| } |
| |
| verifyConnectWithDisabledType(config, results, shouldDropRequest); |
| } |
| |
| @Test |
| public void testConnectDropWithOnlyDisabledPskNetwork() throws Exception { |
| boolean isWpa2PersonalOnlyNetworkInRange = true; |
| boolean isWpa2Wpa3PersonalTransitionNetworkInRange = false; |
| boolean isWpa3PersonalOnlyNetworkInRange = false; |
| boolean shouldDropRequest = true; |
| |
| verifyConnectWithDisabledPskType(isWpa2PersonalOnlyNetworkInRange, |
| isWpa2Wpa3PersonalTransitionNetworkInRange, |
| isWpa3PersonalOnlyNetworkInRange, shouldDropRequest); |
| } |
| |
| @Test |
| public void testConnectAllowedWithPskSaeNetwork() throws Exception { |
| boolean isWpa2PersonalOnlyNetworkInRange = true; |
| boolean isWpa2Wpa3PersonalTransitionNetworkInRange = true; |
| boolean isWpa3PersonalOnlyNetworkInRange = false; |
| boolean shouldDropRequest = false; |
| |
| verifyConnectWithDisabledPskType(isWpa2PersonalOnlyNetworkInRange, |
| isWpa2Wpa3PersonalTransitionNetworkInRange, |
| isWpa3PersonalOnlyNetworkInRange, shouldDropRequest); |
| } |
| |
| @Test |
| public void testConnectAllowedWithSaeNetwork() throws Exception { |
| boolean isWpa2PersonalOnlyNetworkInRange = true; |
| boolean isWpa2Wpa3PersonalTransitionNetworkInRange = false; |
| boolean isWpa3PersonalOnlyNetworkInRange = true; |
| boolean shouldDropRequest = false; |
| |
| verifyConnectWithDisabledPskType(isWpa2PersonalOnlyNetworkInRange, |
| isWpa2Wpa3PersonalTransitionNetworkInRange, |
| isWpa3PersonalOnlyNetworkInRange, shouldDropRequest); |
| } |
| |
| @Test |
| public void testConnectAllowedWithoutPskNetwork() throws Exception { |
| boolean isWpa2PersonalOnlyNetworkInRange = false; |
| boolean isWpa2Wpa3PersonalTransitionNetworkInRange = true; |
| boolean isWpa3PersonalOnlyNetworkInRange = false; |
| boolean shouldDropRequest = false; |
| |
| verifyConnectWithDisabledPskType(isWpa2PersonalOnlyNetworkInRange, |
| isWpa2Wpa3PersonalTransitionNetworkInRange, |
| isWpa3PersonalOnlyNetworkInRange, shouldDropRequest); |
| } |
| |
| @Test |
| public void testConnectDisabledPskAllowedForNonUserSelectedNetwork() throws Exception { |
| boolean isWpa2PersonalOnlyNetworkInRange = true; |
| boolean isWpa2Wpa3PersonalTransitionNetworkInRange = false; |
| boolean isWpa3PersonalOnlyNetworkInRange = false; |
| boolean shouldDropRequest = false; |
| |
| // Only a request from settings or setup wizard are considered a user selected network. |
| when(mWifiPermissionsUtil.checkNetworkSettingsPermission(anyInt())).thenReturn(false); |
| when(mWifiPermissionsUtil.checkNetworkSetupWizardPermission(anyInt())).thenReturn(false); |
| |
| verifyConnectWithDisabledPskType(isWpa2PersonalOnlyNetworkInRange, |
| isWpa2Wpa3PersonalTransitionNetworkInRange, |
| isWpa3PersonalOnlyNetworkInRange, shouldDropRequest); |
| } |
| |
| private void verifyConnectWithDisabledOpenType( |
| boolean isOpenOnlyNetworkInRange, |
| boolean isOpenOweTransitionNetworkInRange, |
| boolean isOweOnlyNetworkInRange, boolean shouldDropRequest) |
| throws Exception { |
| WifiConfiguration config = WifiConfigurationTestUtil.createOpenOweNetwork(); |
| config.networkId = TEST_NETWORK_ID; |
| config.setSecurityParamsEnabled(WifiConfiguration.SECURITY_TYPE_OPEN, false); |
| List<ScanResult> results = new ArrayList<>(); |
| if (isOpenOnlyNetworkInRange) { |
| results.add(makeScanResult(config.SSID, "")); |
| } |
| if (isOpenOweTransitionNetworkInRange) { |
| results.add(makeScanResult(config.SSID, "[OWE_TRANSITION]")); |
| } |
| if (isOweOnlyNetworkInRange) { |
| results.add(makeScanResult(config.SSID, "[OWE]")); |
| } |
| |
| verifyConnectWithDisabledType(config, results, shouldDropRequest); |
| } |
| |
| @Test |
| public void testConnectDropWithOnlyDisabledOpenNetwork() throws Exception { |
| boolean isOpenOnlyNetworkInRange = true; |
| boolean isOpenOweTransitionNetworkInRange = false; |
| boolean isOweOnlyNetworkInRange = false; |
| boolean shouldDropRequest = true; |
| |
| verifyConnectWithDisabledOpenType(isOpenOnlyNetworkInRange, |
| isOpenOweTransitionNetworkInRange, |
| isOweOnlyNetworkInRange, shouldDropRequest); |
| } |
| |
| @Test |
| public void testConnectAllowedWithOpenOweNetwork() throws Exception { |
| boolean isOpenOnlyNetworkInRange = true; |
| boolean isOpenOweTransitionNetworkInRange = true; |
| boolean isOweOnlyNetworkInRange = false; |
| boolean shouldDropRequest = false; |
| |
| verifyConnectWithDisabledOpenType(isOpenOnlyNetworkInRange, |
| isOpenOweTransitionNetworkInRange, |
| isOweOnlyNetworkInRange, shouldDropRequest); |
| } |
| |
| @Test |
| public void testConnectAllowedWithOweNetwork() throws Exception { |
| boolean isOpenOnlyNetworkInRange = true; |
| boolean isOpenOweTransitionNetworkInRange = false; |
| boolean isOweOnlyNetworkInRange = true; |
| boolean shouldDropRequest = false; |
| |
| verifyConnectWithDisabledOpenType(isOpenOnlyNetworkInRange, |
| isOpenOweTransitionNetworkInRange, |
| isOweOnlyNetworkInRange, shouldDropRequest); |
| } |
| |
| @Test |
| public void testConnectAllowedWithoutOpenNetwork() throws Exception { |
| boolean isOpenOnlyNetworkInRange = false; |
| boolean isOpenOweTransitionNetworkInRange = true; |
| boolean isOweOnlyNetworkInRange = false; |
| boolean shouldDropRequest = false; |
| |
| verifyConnectWithDisabledOpenType(isOpenOnlyNetworkInRange, |
| isOpenOweTransitionNetworkInRange, |
| isOweOnlyNetworkInRange, shouldDropRequest); |
| } |
| |
| @Test |
| public void testConnectDisabledOpenAllowedForNonUserSelectedNetwork() throws Exception { |
| boolean isOpenOnlyNetworkInRange = true; |
| boolean isOpenOweTransitionNetworkInRange = false; |
| boolean isOweOnlyNetworkInRange = false; |
| boolean shouldDropRequest = false; |
| |
| // Only a request from settings or setup wizard are considered a user selected network. |
| when(mWifiPermissionsUtil.checkNetworkSettingsPermission(anyInt())).thenReturn(false); |
| when(mWifiPermissionsUtil.checkNetworkSetupWizardPermission(anyInt())).thenReturn(false); |
| |
| verifyConnectWithDisabledOpenType(isOpenOnlyNetworkInRange, |
| isOpenOweTransitionNetworkInRange, |
| isOweOnlyNetworkInRange, shouldDropRequest); |
| } |
| |
| private void verifyConnectWithDisabledWpa2EnterpriseType( |
| boolean isWpa2EnterpriseOnlyNetworkInRange, |
| boolean isWpa2Wpa3EnterpriseTransitionNetworkInRange, |
| boolean isWpa3EnterpriseOnlyNetworkInRange, boolean shouldDropRequest) |
| throws Exception { |
| WifiConfiguration config = WifiConfigurationTestUtil.createWpa2Wpa3EnterpriseNetwork(); |
| config.networkId = TEST_NETWORK_ID; |
| config.setSecurityParamsEnabled(WifiConfiguration.SECURITY_TYPE_EAP, false); |
| List<ScanResult> results = new ArrayList<>(); |
| if (isWpa2EnterpriseOnlyNetworkInRange) { |
| results.add(makeScanResult(config.SSID, "[RSN][EAP/SHA1][MFPC]")); |
| } |
| if (isWpa2Wpa3EnterpriseTransitionNetworkInRange) { |
| results.add(makeScanResult(config.SSID, "[RSN][EAP/SHA1][EAP/SHA256][MFPC]")); |
| } |
| if (isWpa3EnterpriseOnlyNetworkInRange) { |
| results.add(makeScanResult(config.SSID, "[RSN][EAP/SHA256][MFPC][MFPR]")); |
| } |
| |
| verifyConnectWithDisabledType(config, results, shouldDropRequest); |
| } |
| |
| @Test |
| public void testConnectDropWithOnlyDisabledWpa2EnterpriseNetwork() throws Exception { |
| boolean isWpa2EnterpriseOnlyNetworkInRange = true; |
| boolean isWpa2Wpa3EnterpriseTransitionNetworkInRange = false; |
| boolean isWpa3EnterpriseOnlyNetworkInRange = false; |
| boolean shouldDropRequest = true; |
| |
| verifyConnectWithDisabledWpa2EnterpriseType(isWpa2EnterpriseOnlyNetworkInRange, |
| isWpa2Wpa3EnterpriseTransitionNetworkInRange, |
| isWpa3EnterpriseOnlyNetworkInRange, shouldDropRequest); |
| } |
| |
| @Test |
| public void testConnectAllowedWithWpa2Wpa3EnterpriseNetwork() throws Exception { |
| boolean isWpa2EnterpriseOnlyNetworkInRange = true; |
| boolean isWpa2Wpa3EnterpriseTransitionNetworkInRange = true; |
| boolean isWpa3EnterpriseOnlyNetworkInRange = false; |
| boolean shouldDropRequest = false; |
| |
| verifyConnectWithDisabledWpa2EnterpriseType(isWpa2EnterpriseOnlyNetworkInRange, |
| isWpa2Wpa3EnterpriseTransitionNetworkInRange, |
| isWpa3EnterpriseOnlyNetworkInRange, shouldDropRequest); |
| } |
| |
| @Test |
| public void testConnectAllowedWithWpa3EnterpriseNetwork() throws Exception { |
| boolean isWpa2EnterpriseOnlyNetworkInRange = true; |
| boolean isWpa2Wpa3EnterpriseTransitionNetworkInRange = false; |
| boolean isWpa3EnterpriseOnlyNetworkInRange = true; |
| boolean shouldDropRequest = false; |
| |
| verifyConnectWithDisabledWpa2EnterpriseType(isWpa2EnterpriseOnlyNetworkInRange, |
| isWpa2Wpa3EnterpriseTransitionNetworkInRange, |
| isWpa3EnterpriseOnlyNetworkInRange, shouldDropRequest); |
| } |
| |
| @Test |
| public void testConnectAllowedWithoutWpa2EnterpriseNetwork() throws Exception { |
| boolean isWpa2EnterpriseOnlyNetworkInRange = false; |
| boolean isWpa2Wpa3EnterpriseTransitionNetworkInRange = true; |
| boolean isWpa3EnterpriseOnlyNetworkInRange = false; |
| boolean shouldDropRequest = false; |
| |
| verifyConnectWithDisabledWpa2EnterpriseType(isWpa2EnterpriseOnlyNetworkInRange, |
| isWpa2Wpa3EnterpriseTransitionNetworkInRange, |
| isWpa3EnterpriseOnlyNetworkInRange, shouldDropRequest); |
| } |
| |
| @Test |
| public void testConnectDisabledWpa2EnterpriseAllowedForNonUserSelectedNetwork() |
| throws Exception { |
| boolean isWpa2EnterpriseOnlyNetworkInRange = true; |
| boolean isWpa2Wpa3EnterpriseTransitionNetworkInRange = false; |
| boolean isWpa3EnterpriseOnlyNetworkInRange = false; |
| boolean shouldDropRequest = false; |
| |
| // Only a request from settings or setup wizard are considered a user selected network. |
| when(mWifiPermissionsUtil.checkNetworkSettingsPermission(anyInt())).thenReturn(false); |
| when(mWifiPermissionsUtil.checkNetworkSetupWizardPermission(anyInt())).thenReturn(false); |
| |
| verifyConnectWithDisabledWpa2EnterpriseType(isWpa2EnterpriseOnlyNetworkInRange, |
| isWpa2Wpa3EnterpriseTransitionNetworkInRange, |
| isWpa3EnterpriseOnlyNetworkInRange, shouldDropRequest); |
| } |
| |
| @Test |
| public void testUpdateAkmByConnectionInfo() throws Exception { |
| mConnectedNetwork = spy(WifiConfigurationTestUtil.createPskSaeNetwork()); |
| |
| triggerConnect(); |
| |
| // WifiInfo is not updated yet. |
| assertEquals(WifiInfo.SECURITY_TYPE_UNKNOWN, mWifiInfo.getCurrentSecurityType()); |
| |
| WifiSsid wifiSsid = WifiSsid.fromBytes( |
| NativeUtil.byteArrayFromArrayList(NativeUtil.decodeSsid(mConnectedNetwork.SSID))); |
| BitSet akm = new BitSet(); |
| akm.set(WifiConfiguration.KeyMgmt.SAE); |
| mCmi.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, |
| new NetworkConnectionEventInfo(0, wifiSsid, TEST_BSSID_STR, false, akm)); |
| mLooper.dispatchAll(); |
| // WifiInfo is updated to the actual used type. |
| assertEquals(WifiInfo.SECURITY_TYPE_SAE, mWifiInfo.getCurrentSecurityType()); |
| |
| // Roam to a PSK BSS. |
| akm.clear(); |
| akm.set(WifiConfiguration.KeyMgmt.WPA_PSK); |
| mCmi.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, |
| new NetworkConnectionEventInfo(0, wifiSsid, TEST_BSSID_STR, false, akm)); |
| mLooper.dispatchAll(); |
| // WifiInfo is updated to the actual used type. |
| assertEquals(WifiInfo.SECURITY_TYPE_PSK, mWifiInfo.getCurrentSecurityType()); |
| |
| // Roam back to a SAE BSS. |
| akm.clear(); |
| akm.set(WifiConfiguration.KeyMgmt.SAE); |
| mCmi.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, |
| new NetworkConnectionEventInfo(0, wifiSsid, TEST_BSSID_STR, false, akm)); |
| mLooper.dispatchAll(); |
| // WifiInfo is updated to the actual used type. |
| assertEquals(WifiInfo.SECURITY_TYPE_SAE, mWifiInfo.getCurrentSecurityType()); |
| } |
| |
| @Test |
| public void testUpdateWpa3EnterpriseSecurityTypeByConnectionInfo() throws Exception { |
| // Create a WifiConfiguration with WPA2 enterprise and WPA3 enterprise security params |
| WifiConfiguration config = spy(WifiConfigurationTestUtil.createWpa2Wpa3EnterpriseNetwork()); |
| config.networkId = FRAMEWORK_NETWORK_ID; |
| |
| SecurityParams wpa3EnterpriseParams = |
| config.getSecurityParams(SECURITY_TYPE_EAP_WPA3_ENTERPRISE); |
| assertNotNull(wpa3EnterpriseParams); |
| // Trigger connection with security params as WPA3 enterprise where PMF is mandatory |
| config.getNetworkSelectionStatus().setLastUsedSecurityParams(wpa3EnterpriseParams); |
| setupAndStartConnectSequence(config); |
| validateSuccessfulConnectSequence(config); |
| |
| // WifiInfo is not updated yet. |
| assertEquals(WifiInfo.SECURITY_TYPE_UNKNOWN, mWifiInfo.getCurrentSecurityType()); |
| |
| WifiSsid wifiSsid = |
| WifiSsid.fromBytes( |
| NativeUtil.byteArrayFromArrayList(NativeUtil.decodeSsid(config.SSID))); |
| |
| BitSet akm = new BitSet(); |
| akm.set(WifiConfiguration.KeyMgmt.WPA_EAP_SHA256); |
| // Send network connection event with WPA_EAP_SHA256 AKM |
| mCmi.sendMessage( |
| WifiMonitor.NETWORK_CONNECTION_EVENT, |
| new NetworkConnectionEventInfo(0, wifiSsid, TEST_BSSID_STR, false, akm)); |
| mLooper.dispatchAll(); |
| // WifiInfo is updated with WPA3-Enterprise security type derived from the AKM sent in |
| // network connection event and the PMF settings used in connection. |
| assertEquals( |
| WifiInfo.SECURITY_TYPE_EAP_WPA3_ENTERPRISE, mWifiInfo.getCurrentSecurityType()); |
| } |
| |
| @Test |
| public void testEnableTdls() throws Exception { |
| connect(); |
| when(mWifiNative.getMaxSupportedConcurrentTdlsSessions(WIFI_IFACE_NAME)).thenReturn(5); |
| when(mWifiNative.getSupportedFeatureSet(WIFI_IFACE_NAME)) |
| .thenReturn(WifiManager.WIFI_FEATURE_TDLS); |
| when(mWifiNative.startTdls(eq(WIFI_IFACE_NAME), eq(TEST_TDLS_PEER_ADDR_STR), anyBoolean())) |
| .thenReturn(true); |
| assertEquals(5, mCmi.getMaxSupportedConcurrentTdlsSessions()); |
| assertTrue(mCmi.isTdlsOperationCurrentlyAvailable()); |
| mCmi.enableTdls(TEST_TDLS_PEER_ADDR_STR, true); |
| assertEquals(1, mCmi.getNumberOfEnabledTdlsSessions()); |
| verify(mWifiNative).startTdls(eq(WIFI_IFACE_NAME), eq(TEST_TDLS_PEER_ADDR_STR), |
| eq(true)); |
| mCmi.enableTdls(TEST_TDLS_PEER_ADDR_STR, false); |
| verify(mWifiNative).startTdls(eq(WIFI_IFACE_NAME), eq(TEST_TDLS_PEER_ADDR_STR), |
| eq(false)); |
| assertEquals(0, mCmi.getNumberOfEnabledTdlsSessions()); |
| } |
| |
| /** |
| * Verify that the frequency will be updated on receiving BSS_FREQUENCY_CHANGED_EVENT event. |
| */ |
| @Test |
| public void testBssFrequencyChangedUpdatesFrequency() throws Exception { |
| connect(); |
| assertEquals(sFreq, mWifiInfo.getFrequency()); |
| mCmi.sendMessage(WifiMonitor.BSS_FREQUENCY_CHANGED_EVENT, sFreq1); |
| mLooper.dispatchAll(); |
| assertEquals(sFreq1, mWifiInfo.getFrequency()); |
| } |
| |
| /** |
| * Verify that the frequency is updated on roaming even when the scan result doesn't have the |
| * roamed BSS scan entry. |
| */ |
| @Test |
| public void testFrequencyUpdateOnRoamWithoutScanResultEntry() throws Exception { |
| // Connect to network with |TEST_BSSID_STR|, |sFreq|. |
| connect(); |
| |
| // Return null scan detail cache for roaming target. |
| when(mWifiConfigManager.getScanDetailCacheForNetwork(FRAMEWORK_NETWORK_ID)) |
| .thenReturn(mScanDetailCache); |
| when(mScanDetailCache.getScanDetail(TEST_BSSID_STR1)).thenReturn(null); |
| |
| // This simulates the behavior of roaming to network with |TEST_BSSID_STR1|, |sFreq1|. |
| // Send a SUPPLICANT_STATE_CHANGE_EVENT, verify WifiInfo is updated with new frequency. |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(0, TEST_WIFI_SSID, TEST_BSSID_STR1, sFreq1, |
| SupplicantState.COMPLETED)); |
| mLooper.dispatchAll(); |
| |
| WifiInfo wifiInfo = mWifiInfo; |
| assertEquals(sFreq1, wifiInfo.getFrequency()); |
| } |
| |
| /** |
| * Verify that static attributes (channel, state, link id and band) of the affiliated MLO links |
| * are not changed after signal poll. |
| */ |
| @Test |
| public void testMloLinkAttributesAfterSignalPoll() throws Exception { |
| connect(); |
| setScanResultWithMloInfo(); |
| |
| // Associate to one link only |
| mConnectionCapabilities.wifiStandard = ScanResult.WIFI_STANDARD_11BE; |
| WifiNative.ConnectionMloLinksInfo info = new WifiNative.ConnectionMloLinksInfo(); |
| info.links = new WifiNative.ConnectionMloLink[1]; |
| info.links[0] = new WifiNative.ConnectionMloLink(TEST_MLO_LINK_ID, TEST_MLO_LINK_ADDR, |
| TEST_AP_MLD_MAC_ADDRESS, Byte.MAX_VALUE, Byte.MAX_VALUE, 2437); |
| when(mWifiNative.getConnectionMloLinksInfo(WIFI_IFACE_NAME)).thenReturn(info); |
| |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(FRAMEWORK_NETWORK_ID, TEST_WIFI_SSID, TEST_BSSID_STR, sFreq, |
| SupplicantState.ASSOCIATED)); |
| mLooper.dispatchAll(); |
| |
| // Verify the link attributes |
| List<MloLink> links = mWifiInfo.getAffiliatedMloLinks(); |
| assertEquals(links.get(0).getState(), MloLink.MLO_LINK_STATE_ACTIVE); |
| assertEquals(links.get(0).getBand(), WifiScanner.WIFI_BAND_24_GHZ); |
| assertEquals(links.get(0).getChannel(), TEST_CHANNEL); |
| assertEquals(links.get(0).getLinkId(), TEST_MLO_LINK_ID); |
| assertEquals(links.get(1).getState(), MloLink.MLO_LINK_STATE_UNASSOCIATED); |
| assertEquals(links.get(1).getBand(), WifiScanner.WIFI_BAND_5_GHZ); |
| assertEquals(links.get(1).getChannel(), TEST_CHANNEL_1); |
| assertEquals(links.get(1).getLinkId(), TEST_MLO_LINK_ID_1); |
| |
| // Signal poll for associated link only |
| final long startMillis = 1_500_000_000_100L; |
| WifiLinkLayerStats llStats = new WifiLinkLayerStats(); |
| llStats.txmpdu_be = 1000; |
| llStats.rxmpdu_bk = 2000; |
| WifiSignalPollResults signalPollResults = new WifiSignalPollResults(); |
| signalPollResults.addEntry(TEST_MLO_LINK_ID, -42, 65, 54, sFreq); |
| when(mWifiNative.getSupportedFeatureSet(WIFI_IFACE_NAME)).thenReturn( |
| WifiManager.WIFI_FEATURE_LINK_LAYER_STATS); |
| when(mWifiNative.getWifiLinkLayerStats(any())).thenReturn(llStats); |
| when(mWifiNative.signalPoll(any())).thenReturn(signalPollResults); |
| when(mClock.getWallClockMillis()).thenReturn(startMillis + 0); |
| mCmi.enableRssiPolling(true); |
| mCmi.sendMessage(ClientModeImpl.CMD_RSSI_POLL, 1); |
| mLooper.dispatchAll(); |
| |
| // Make sure static link attributes has not changed |
| links = mWifiInfo.getAffiliatedMloLinks(); |
| assertEquals(links.get(0).getState(), MloLink.MLO_LINK_STATE_ACTIVE); |
| assertEquals(links.get(0).getBand(), WifiScanner.WIFI_BAND_24_GHZ); |
| assertEquals(links.get(0).getChannel(), TEST_CHANNEL); |
| assertEquals(links.get(0).getLinkId(), TEST_MLO_LINK_ID); |
| assertEquals(links.get(1).getState(), MloLink.MLO_LINK_STATE_UNASSOCIATED); |
| assertEquals(links.get(1).getBand(), WifiScanner.WIFI_BAND_5_GHZ); |
| assertEquals(links.get(1).getChannel(), TEST_CHANNEL_1); |
| assertEquals(links.get(1).getLinkId(), TEST_MLO_LINK_ID_1); |
| } |
| |
| /** |
| * Verify that during DHCP process, 1. If P2P is in waiting state, clientModeImpl doesn't send a |
| * message to block P2P discovery. 2. If P2P is not in waiting state, clientModeImpl sends a |
| * message to block P2P discovery. 3. On DHCP completion, clientModeImpl sends a message to |
| * unblock P2P discovery. |
| */ |
| @Test |
| public void testP2pBlockDiscoveryDuringDhcp() throws Exception { |
| initializeAndAddNetworkAndVerifySuccess(); |
| |
| startConnectSuccess(); |
| |
| mCmi.sendMessage( |
| WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, |
| 0, |
| 0, |
| new StateChangeResult( |
| 0, TEST_WIFI_SSID, TEST_BSSID_STR, sFreq, SupplicantState.ASSOCIATED)); |
| mLooper.dispatchAll(); |
| |
| mCmi.sendMessage( |
| WifiMonitor.NETWORK_CONNECTION_EVENT, |
| new NetworkConnectionEventInfo(0, TEST_WIFI_SSID, TEST_BSSID_STR, false, null)); |
| mLooper.dispatchAll(); |
| verify(mWifiBlocklistMonitor).handleBssidConnectionSuccess(TEST_BSSID_STR, TEST_SSID); |
| |
| mCmi.sendMessage( |
| WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, |
| 0, |
| 0, |
| new StateChangeResult( |
| 0, TEST_WIFI_SSID, TEST_BSSID_STR, sFreq, SupplicantState.COMPLETED)); |
| mLooper.dispatchAll(); |
| |
| assertEquals("L3ProvisioningState", getCurrentState().getName()); |
| |
| when(mWifiP2pConnection.isConnected()).thenReturn(true); |
| when(mWifiP2pConnection.isP2pInWaitingState()).thenReturn(true); |
| |
| mIpClientCallback.onPreDhcpAction(); |
| mLooper.dispatchAll(); |
| verify(mWifiP2pConnection, never()).sendMessage(anyInt(), anyInt(), anyInt()); |
| verify(mIpClient).completedPreDhcpAction(); |
| |
| when(mWifiP2pConnection.isConnected()).thenReturn(true); |
| when(mWifiP2pConnection.isP2pInWaitingState()).thenReturn(false); |
| |
| mIpClientCallback.onPreDhcpAction(); |
| mLooper.dispatchAll(); |
| verify(mWifiP2pConnection) |
| .sendMessage( |
| eq(WifiP2pServiceImpl.BLOCK_DISCOVERY), |
| eq(WifiP2pServiceImpl.ENABLED), |
| eq(CMD_PRE_DHCP_ACTION_COMPLETE)); |
| verify(mIpClient).completedPreDhcpAction(); |
| |
| mIpClientCallback.onPostDhcpAction(); |
| mLooper.dispatchAll(); |
| verify(mWifiP2pConnection) |
| .sendMessage( |
| eq(WifiP2pServiceImpl.BLOCK_DISCOVERY), eq(WifiP2pServiceImpl.DISABLED)); |
| } |
| |
| /** |
| * Verify that MLO link will be updated on receiving BSS_FREQUENCY_CHANGED_EVENT event. |
| */ |
| @Test |
| public void testBssFrequencyChangedUpdatesMloLink() throws Exception { |
| connect(); |
| mLooper.dispatchAll(); |
| |
| mConnectionCapabilities.wifiStandard = ScanResult.WIFI_STANDARD_11BE; |
| WifiNative.ConnectionMloLinksInfo info = new WifiNative.ConnectionMloLinksInfo(); |
| info.links = new WifiNative.ConnectionMloLink[2]; |
| info.links[0] = new WifiNative.ConnectionMloLink(TEST_MLO_LINK_ID, TEST_MLO_LINK_ADDR, |
| TEST_AP_MLD_MAC_ADDRESS, Byte.MIN_VALUE, Byte.MAX_VALUE, TEST_MLO_LINK_FREQ); |
| info.links[1] = new WifiNative.ConnectionMloLink(TEST_MLO_LINK_ID_1, TEST_MLO_LINK_ADDR_1, |
| TEST_AP_MLD_MAC_ADDRESS, Byte.MAX_VALUE, Byte.MIN_VALUE, TEST_MLO_LINK_FREQ_1); |
| when(mWifiNative.getConnectionMloLinksInfo(WIFI_IFACE_NAME)).thenReturn(info); |
| mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, |
| new StateChangeResult(FRAMEWORK_NETWORK_ID, TEST_WIFI_SSID, TEST_BSSID_STR, sFreq, |
| SupplicantState.ASSOCIATED)); |
| mLooper.dispatchAll(); |
| assertEquals(mWifiInfo.getAssociatedMloLinks().size(), |
| mWifiInfo.getAffiliatedMloLinks().size()); |
| |
| info.links[0] = new WifiNative.ConnectionMloLink(TEST_MLO_LINK_ID, TEST_MLO_LINK_ADDR, |
| TEST_AP_MLD_MAC_ADDRESS, Byte.MIN_VALUE, Byte.MAX_VALUE, 6215); |
| when(mWifiNative.getConnectionMloLinksInfo(WIFI_IFACE_NAME)).thenReturn(info); |
| mCmi.sendMessage(WifiMonitor.BSS_FREQUENCY_CHANGED_EVENT, 6215); |
| mLooper.dispatchAll(); |
| List<MloLink> links = mWifiInfo.getAffiliatedMloLinks(); |
| assertEquals(53, links.get(0).getChannel()); |
| assertEquals(WifiScanner.WIFI_BAND_6_GHZ, links.get(0).getBand()); |
| } |
| } |