1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.wifitrackerlib; 18 19 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; 20 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; 21 22 import static com.google.common.truth.Truth.assertThat; 23 24 import static org.mockito.ArgumentMatchers.any; 25 import static org.mockito.Mockito.mock; 26 import static org.mockito.Mockito.spy; 27 import static org.mockito.Mockito.times; 28 import static org.mockito.Mockito.when; 29 30 import android.content.Context; 31 import android.content.res.Resources; 32 import android.net.ConnectivityDiagnosticsManager; 33 import android.net.ConnectivityManager; 34 import android.net.Network; 35 import android.net.NetworkCapabilities; 36 import android.net.wifi.WifiConfiguration; 37 import android.net.wifi.WifiInfo; 38 import android.net.wifi.WifiManager; 39 import android.net.wifi.hotspot2.PasspointConfiguration; 40 import android.net.wifi.hotspot2.pps.Credential; 41 import android.net.wifi.hotspot2.pps.HomeSp; 42 import android.os.Handler; 43 import android.os.test.TestLooper; 44 45 import org.junit.Before; 46 import org.junit.Test; 47 import org.mockito.Mock; 48 import org.mockito.Mockito; 49 import org.mockito.MockitoAnnotations; 50 import org.mockito.MockitoSession; 51 52 import java.util.Arrays; 53 54 public class PasspointWifiEntryTest { 55 @Mock private WifiTrackerInjector mMockInjector; 56 @Mock private Context mMockContext; 57 @Mock private WifiManager mMockWifiManager; 58 @Mock private Resources mMockResources; 59 @Mock private WifiInfo mMockWifiInfo; 60 @Mock private Network mMockNetwork; 61 @Mock private NetworkCapabilities mMockNetworkCapabilities; 62 @Mock private ConnectivityManager mMockConnectivityManager; 63 64 private TestLooper mTestLooper; 65 private Handler mTestHandler; 66 67 private static final String FQDN = "fqdn"; 68 69 @Before setUp()70 public void setUp() { 71 MockitoAnnotations.initMocks(this); 72 73 mTestLooper = new TestLooper(); 74 mTestHandler = new Handler(mTestLooper.getLooper()); 75 76 when(mMockWifiInfo.isPrimary()).thenReturn(true); 77 when(mMockWifiInfo.getNetworkId()).thenReturn(WifiConfiguration.INVALID_NETWORK_ID); 78 when(mMockWifiInfo.getRssi()).thenReturn(WifiInfo.INVALID_RSSI); 79 when(mMockNetworkCapabilities.getTransportInfo()).thenReturn(mMockWifiInfo); 80 when(mMockInjector.getContext()).thenReturn(mMockContext); 81 when(mMockContext.getResources()).thenReturn(mMockResources); 82 when(mMockContext.getString(R.string.wifitrackerlib_summary_separator)).thenReturn("/"); 83 84 when(mMockContext.getSystemService(ConnectivityManager.class)) 85 .thenReturn(mMockConnectivityManager); 86 } 87 88 @Test testGetSummary_expiredTimeNotAvailable_notShowExpired()89 public void testGetSummary_expiredTimeNotAvailable_notShowExpired() { 90 // default SubscriptionExpirationTimeInMillis is unset 91 PasspointConfiguration passpointConfiguration = getPasspointConfiguration(); 92 String expired = "Expired"; 93 when(mMockContext.getString(R.string.wifitrackerlib_wifi_passpoint_expired)) 94 .thenReturn(expired); 95 96 PasspointWifiEntry passpointWifiEntry = new PasspointWifiEntry(mMockInjector, 97 mTestHandler, passpointConfiguration, mMockWifiManager, 98 false /* forSavedNetworksPage */); 99 100 assertThat(passpointWifiEntry.getSummary()).isNotEqualTo(expired); 101 } 102 103 @Test testGetSummary_expired_showExpired()104 public void testGetSummary_expired_showExpired() { 105 PasspointConfiguration passpointConfiguration = getPasspointConfiguration(); 106 String expired = "Expired"; 107 when(mMockContext.getString(R.string.wifitrackerlib_wifi_passpoint_expired)) 108 .thenReturn(expired); 109 PasspointWifiEntry passpointWifiEntry = new PasspointWifiEntry(mMockInjector, 110 mTestHandler, passpointConfiguration, mMockWifiManager, 111 false /* forSavedNetworksPage */); 112 PasspointWifiEntry spyEntry = spy(passpointWifiEntry); 113 when(spyEntry.isExpired()).thenReturn(true); 114 115 assertThat(spyEntry.getSummary()).isEqualTo(expired); 116 } 117 getPasspointConfiguration()118 private PasspointConfiguration getPasspointConfiguration() { 119 PasspointConfiguration passpointConfiguration = new PasspointConfiguration(); 120 HomeSp homeSp = new HomeSp(); 121 homeSp.setFqdn(FQDN); 122 passpointConfiguration.setHomeSp(homeSp); 123 passpointConfiguration.setCredential(new Credential()); 124 return passpointConfiguration; 125 } 126 127 @Test testGetMeteredChoice_afterSetMeteredChoice_getCorrectValue()128 public void testGetMeteredChoice_afterSetMeteredChoice_getCorrectValue() { 129 PasspointWifiEntry entry = new PasspointWifiEntry(mMockInjector, mTestHandler, 130 getPasspointConfiguration(), mMockWifiManager, 131 false /* forSavedNetworksPage */); 132 133 entry.setMeteredChoice(WifiEntry.METERED_CHOICE_UNMETERED); 134 135 assertThat(entry.getMeteredChoice()).isEqualTo(WifiEntry.METERED_CHOICE_UNMETERED); 136 } 137 138 @Test testGetSummary_connectedWifiNetwork_showsConnected()139 public void testGetSummary_connectedWifiNetwork_showsConnected() { 140 String summarySeparator = " / "; 141 String[] wifiStatusArray = new String[]{"", "Scanning", "Connecting", 142 "Authenticating", "Obtaining IP address", "Connected"}; 143 144 when(mMockContext.getString(R.string.wifitrackerlib_summary_separator)) 145 .thenReturn(summarySeparator); 146 when(mMockResources.getStringArray(R.array.wifitrackerlib_wifi_status)) 147 .thenReturn(wifiStatusArray); 148 when(mMockWifiInfo.isPasspointAp()).thenReturn(true); 149 when(mMockWifiInfo.getPasspointFqdn()).thenReturn(FQDN); 150 when(mMockNetworkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)) 151 .thenReturn(true); 152 153 PasspointWifiEntry entry = new PasspointWifiEntry(mMockInjector, mTestHandler, 154 getPasspointConfiguration(), mMockWifiManager, 155 false /* forSavedNetworksPage */); 156 entry.onNetworkCapabilitiesChanged(mMockNetwork, mMockNetworkCapabilities); 157 entry.onDefaultNetworkCapabilitiesChanged(mMockNetwork, mMockNetworkCapabilities); 158 159 assertThat(entry.getSummary()).isEqualTo("Connected"); 160 } 161 162 @Test testGetSummary_connectedButNotDefault_doesNotShowConnected()163 public void testGetSummary_connectedButNotDefault_doesNotShowConnected() { 164 String summarySeparator = " / "; 165 String[] wifiStatusArray = new String[]{"", "Scanning", "Connecting", 166 "Authenticating", "Obtaining IP address", "Connected"}; 167 168 when(mMockContext.getString(R.string.wifitrackerlib_summary_separator)) 169 .thenReturn(summarySeparator); 170 when(mMockResources.getStringArray(R.array.wifitrackerlib_wifi_status)) 171 .thenReturn(wifiStatusArray); 172 when(mMockWifiInfo.isPasspointAp()).thenReturn(true); 173 when(mMockWifiInfo.getPasspointFqdn()).thenReturn(FQDN); 174 when(mMockNetworkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)) 175 .thenReturn(true); 176 177 PasspointWifiEntry entry = new PasspointWifiEntry(mMockInjector, mTestHandler, 178 getPasspointConfiguration(), mMockWifiManager, 179 false /* forSavedNetworksPage */); 180 entry.onNetworkCapabilitiesChanged(mMockNetwork, mMockNetworkCapabilities); 181 182 assertThat(entry.getSummary()).isEqualTo(""); 183 } 184 185 @Test testGetSecurityTypes_connectedWifiNetwork_showsCurrentSecurityType()186 public void testGetSecurityTypes_connectedWifiNetwork_showsCurrentSecurityType() { 187 when(mMockWifiInfo.isPasspointAp()).thenReturn(true); 188 when(mMockWifiInfo.getPasspointFqdn()).thenReturn(FQDN); 189 when(mMockWifiInfo.getCurrentSecurityType()) 190 .thenReturn(WifiInfo.SECURITY_TYPE_PASSPOINT_R1_R2); 191 192 PasspointWifiEntry entry = new PasspointWifiEntry(mMockInjector, mTestHandler, 193 getPasspointConfiguration(), mMockWifiManager, 194 false /* forSavedNetworksPage */); 195 196 assertThat(entry.getSecurityTypes()).containsExactlyElementsIn(Arrays.asList( 197 WifiInfo.SECURITY_TYPE_PASSPOINT_R1_R2, 198 WifiInfo.SECURITY_TYPE_PASSPOINT_R3)); 199 200 entry.onNetworkCapabilitiesChanged(mMockNetwork, mMockNetworkCapabilities); 201 202 assertThat(entry.getSecurityTypes()) 203 .containsExactly(WifiInfo.SECURITY_TYPE_PASSPOINT_R1_R2); 204 205 when(mMockWifiInfo.getCurrentSecurityType()) 206 .thenReturn(WifiInfo.SECURITY_TYPE_PASSPOINT_R3); 207 entry.onNetworkCapabilitiesChanged(mMockNetwork, mMockNetworkCapabilities); 208 209 assertThat(entry.getSecurityTypes()).containsExactly(WifiInfo.SECURITY_TYPE_PASSPOINT_R3); 210 } 211 212 @Test testGetSecurityString_showsPasspoint()213 public void testGetSecurityString_showsPasspoint() { 214 PasspointConfiguration passpointConfiguration = getPasspointConfiguration(); 215 String passpointSecurity = "Passpoint"; 216 when(mMockContext.getString(R.string.wifitrackerlib_wifi_security_passpoint)) 217 .thenReturn(passpointSecurity); 218 219 PasspointWifiEntry passpointWifiEntry = new PasspointWifiEntry(mMockInjector, 220 mTestHandler, passpointConfiguration, mMockWifiManager, 221 false /* forSavedNetworksPage */); 222 223 assertThat(passpointWifiEntry.getSecurityString(false)).isEqualTo(passpointSecurity); 224 } 225 226 @Test testShouldShowXLevelIcon_unvalidatedOrLowQuality_returnsTrue()227 public void testShouldShowXLevelIcon_unvalidatedOrLowQuality_returnsTrue() { 228 when(mMockWifiInfo.isPasspointAp()).thenReturn(true); 229 when(mMockWifiInfo.getPasspointFqdn()).thenReturn(FQDN); 230 231 PasspointWifiEntry entry = new PasspointWifiEntry(mMockInjector, mTestHandler, 232 getPasspointConfiguration(), mMockWifiManager, 233 false /* forSavedNetworksPage */); 234 235 // Disconnected should return false; 236 assertThat(entry.shouldShowXLevelIcon()).isEqualTo(false); 237 238 // Connected but validation attempt not complete, should not show X level icon yet. 239 entry.onNetworkCapabilitiesChanged(mMockNetwork, mMockNetworkCapabilities); 240 assertThat(entry.shouldShowXLevelIcon()).isEqualTo(false); 241 242 // Validation attempt complete, should show X level icon. 243 ConnectivityDiagnosticsManager.ConnectivityReport connectivityReport = mock( 244 ConnectivityDiagnosticsManager.ConnectivityReport.class); 245 when(connectivityReport.getNetwork()).thenReturn(mMockNetwork); 246 entry.updateConnectivityReport(connectivityReport); 247 assertThat(entry.shouldShowXLevelIcon()).isEqualTo(true); 248 249 // Internet validated, should not show X level icon. 250 when(mMockNetworkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)) 251 .thenReturn(true); 252 entry.onNetworkCapabilitiesChanged(mMockNetwork, mMockNetworkCapabilities); 253 assertThat(entry.shouldShowXLevelIcon()).isEqualTo(false); 254 255 // Cell becomes default (i.e. low quality wifi), show X level icon. 256 entry.onDefaultNetworkCapabilitiesChanged(Mockito.mock(Network.class), 257 new NetworkCapabilities.Builder() 258 .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR).build()); 259 assertThat(entry.shouldShowXLevelIcon()).isEqualTo(true); 260 } 261 262 @Test testGetMacAddress_wifiInfoAvailable_usesWifiInfoMacAddress()263 public void testGetMacAddress_wifiInfoAvailable_usesWifiInfoMacAddress() { 264 final String factoryMac = "01:23:45:67:89:ab"; 265 final String wifiInfoMac = "11:23:45:67:89:ab"; 266 final WifiConfiguration config = new WifiConfiguration(); 267 config.SSID = "\"ssid\""; 268 config.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_NONE; 269 config.FQDN = FQDN; 270 when(mMockWifiManager.getFactoryMacAddresses()).thenReturn(new String[]{factoryMac}); 271 when(mMockWifiInfo.isPasspointAp()).thenReturn(true); 272 when(mMockWifiInfo.getPasspointFqdn()).thenReturn(FQDN); 273 when(mMockWifiInfo.getMacAddress()).thenReturn(wifiInfoMac); 274 PasspointWifiEntry entry = new PasspointWifiEntry(mMockInjector, mTestHandler, 275 getPasspointConfiguration(), mMockWifiManager, 276 false /* forSavedNetworksPage */); 277 278 entry.onNetworkCapabilitiesChanged(mMockNetwork, mMockNetworkCapabilities); 279 280 assertThat(entry.getMacAddress()).isEqualTo(wifiInfoMac); 281 } 282 283 @Test testIsAutoJoinEnabled_nullConfigs_returnsFalse()284 public void testIsAutoJoinEnabled_nullConfigs_returnsFalse() { 285 PasspointWifiEntry entry = new PasspointWifiEntry(mMockInjector, mTestHandler, 286 getPasspointConfiguration(), mMockWifiManager, 287 false /* forSavedNetworksPage */); 288 289 entry.updatePasspointConfig(null); 290 291 assertThat(entry.isAutoJoinEnabled()).isFalse(); 292 } 293 294 @Test testCanSignIn_captivePortalCapability_returnsTrue()295 public void testCanSignIn_captivePortalCapability_returnsTrue() { 296 PasspointWifiEntry entry = new PasspointWifiEntry(mMockInjector, mTestHandler, 297 getPasspointConfiguration(), mMockWifiManager, 298 false /* forSavedNetworksPage */); 299 300 when(mMockWifiInfo.isPasspointAp()).thenReturn(true); 301 when(mMockWifiInfo.getPasspointFqdn()).thenReturn(FQDN); 302 when(mMockNetworkCapabilities.hasCapability( 303 NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL)).thenReturn(true); 304 entry.onNetworkCapabilitiesChanged(mMockNetwork, mMockNetworkCapabilities); 305 306 assertThat(entry.canSignIn()).isTrue(); 307 } 308 309 @Test testUpdateNetworkCapabilities_userConnect_autoOpenCaptivePortalOnce()310 public void testUpdateNetworkCapabilities_userConnect_autoOpenCaptivePortalOnce() { 311 PasspointWifiEntry entry = new PasspointWifiEntry(mMockInjector, mTestHandler, 312 getPasspointConfiguration(), mMockWifiManager, 313 false /* forSavedNetworksPage */); 314 315 MockitoSession session = mockitoSession().spyStatic(NonSdkApiWrapper.class).startMocking(); 316 try { 317 // Simulate user tapping on the network and receiving captive portal capabilities. 318 // This should trigger the captive portal app. 319 entry.connect(null /* callback */); 320 when(mMockWifiInfo.isPasspointAp()).thenReturn(true); 321 when(mMockWifiInfo.getPasspointFqdn()).thenReturn(FQDN); 322 when(mMockNetworkCapabilities.hasCapability( 323 NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL)).thenReturn(true); 324 entry.onNetworkCapabilitiesChanged(mMockNetwork, mMockNetworkCapabilities); 325 326 verify(() -> NonSdkApiWrapper.startCaptivePortalApp(any(), any()), times(1)); 327 328 // Update network capabilities again. This should not trigger the captive portal app. 329 entry.onNetworkCapabilitiesChanged(mMockNetwork, mMockNetworkCapabilities); 330 331 verify(() -> NonSdkApiWrapper.startCaptivePortalApp(any(), any()), times(1)); 332 } finally { 333 session.finishMocking(); 334 } 335 336 } 337 338 @Test testDisconnect_noScansOrWifiConfig_disconnectIsSuccessful()339 public void testDisconnect_noScansOrWifiConfig_disconnectIsSuccessful() { 340 // Setup a connected PasspointWifiEntry 341 String summarySeparator = " / "; 342 String[] wifiStatusArray = new String[]{"", "Scanning", "Connecting", 343 "Authenticating", "Obtaining IP address", "Connected"}; 344 when(mMockContext.getString(R.string.wifitrackerlib_summary_separator)) 345 .thenReturn(summarySeparator); 346 when(mMockResources.getStringArray(R.array.wifitrackerlib_wifi_status)) 347 .thenReturn(wifiStatusArray); 348 when(mMockWifiInfo.isPasspointAp()).thenReturn(true); 349 when(mMockWifiInfo.getPasspointFqdn()).thenReturn(FQDN); 350 when(mMockNetworkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)) 351 .thenReturn(true); 352 PasspointWifiEntry entry = new PasspointWifiEntry(mMockInjector, mTestHandler, 353 getPasspointConfiguration(), mMockWifiManager, 354 false /* forSavedNetworksPage */); 355 entry.onNetworkCapabilitiesChanged(mMockNetwork, mMockNetworkCapabilities); 356 357 // Disconnect the entry before it can be updated with scans and a WifiConfiguration 358 entry.disconnect(null); 359 360 verify(mMockWifiManager).disableEphemeralNetwork(FQDN); 361 verify(mMockWifiManager).disconnect(); 362 } 363 } 364