PasspointManagerTest.java revision 87c6f1b149804685e46c18d2ad11262f611c9255
1/* 2 * Copyright (C) 2016 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 17package com.android.server.wifi.hotspot2; 18 19import static android.net.wifi.WifiManager.EXTRA_PASSPOINT_ICON_BSSID; 20import static android.net.wifi.WifiManager.EXTRA_PASSPOINT_ICON_DATA; 21import static android.net.wifi.WifiManager.EXTRA_PASSPOINT_ICON_FILE; 22import static android.net.wifi.WifiManager.PASSPOINT_ICON_RECEIVED_ACTION; 23 24import static org.junit.Assert.assertEquals; 25import static org.junit.Assert.assertFalse; 26import static org.junit.Assert.assertTrue; 27import static org.mockito.Mockito.any; 28import static org.mockito.Mockito.anyLong; 29import static org.mockito.Mockito.anyMap; 30import static org.mockito.Mockito.eq; 31import static org.mockito.Mockito.mock; 32import static org.mockito.Mockito.verify; 33import static org.mockito.Mockito.when; 34import static org.mockito.MockitoAnnotations.initMocks; 35 36import android.content.Context; 37import android.content.Intent; 38import android.net.wifi.EAPConstants; 39import android.net.wifi.hotspot2.PasspointConfiguration; 40import android.net.wifi.hotspot2.pps.Credential; 41import android.net.wifi.hotspot2.pps.HomeSP; 42import android.os.UserHandle; 43import android.test.suitebuilder.annotation.SmallTest; 44import android.util.Pair; 45 46import com.android.server.wifi.Clock; 47import com.android.server.wifi.FakeKeys; 48import com.android.server.wifi.IMSIParameter; 49import com.android.server.wifi.SIMAccessor; 50import com.android.server.wifi.ScanDetail; 51import com.android.server.wifi.WifiKeyStore; 52import com.android.server.wifi.WifiNative; 53 54import org.junit.Before; 55import org.junit.Test; 56import org.mockito.ArgumentCaptor; 57import org.mockito.Mock; 58 59import java.util.ArrayList; 60import java.util.List; 61 62/** 63 * Unit tests for {@link com.android.server.wifi.hotspot2.PasspointManager}. 64 */ 65@SmallTest 66public class PasspointManagerTest { 67 private static final long BSSID = 0x112233445566L; 68 private static final String ICON_FILENAME = "test"; 69 private static final String TEST_FQDN = "test1.test.com"; 70 private static final String TEST_FRIENDLY_NAME = "friendly name"; 71 private static final String TEST_REALM = "realm.test.com"; 72 private static final String TEST_IMSI = "1234*"; 73 private static final IMSIParameter TEST_IMSI_PARAM = IMSIParameter.build(TEST_IMSI); 74 75 private static final String TEST_SSID = "TestSSID"; 76 private static final long TEST_BSSID = 0x1234L; 77 private static final long TEST_HESSID = 0x5678L; 78 private static final int TEST_ANQP_DOMAIN_ID = 1; 79 80 @Mock Context mContext; 81 @Mock WifiNative mWifiNative; 82 @Mock WifiKeyStore mWifiKeyStore; 83 @Mock Clock mClock; 84 @Mock SIMAccessor mSimAccessor; 85 @Mock PasspointObjectFactory mObjectFactory; 86 @Mock PasspointEventHandler.Callbacks mCallbacks; 87 @Mock AnqpCache mAnqpCache; 88 PasspointManager mManager; 89 90 /** Sets up test. */ 91 @Before 92 public void setUp() throws Exception { 93 initMocks(this); 94 when(mObjectFactory.makeAnqpCache(mClock)).thenReturn(mAnqpCache); 95 mManager = new PasspointManager(mContext, mWifiNative, mWifiKeyStore, mClock, 96 mSimAccessor, mObjectFactory); 97 ArgumentCaptor<PasspointEventHandler.Callbacks> callbacks = 98 ArgumentCaptor.forClass(PasspointEventHandler.Callbacks.class); 99 verify(mObjectFactory).makePasspointEventHandler(any(WifiNative.class), 100 callbacks.capture()); 101 mCallbacks = callbacks.getValue(); 102 } 103 104 /** 105 * Verify PASSPOINT_ICON_RECEIVED_ACTION broadcast intent. 106 * @param bssid BSSID of the AP 107 * @param fileName Name of the icon file 108 * @param data icon data byte array 109 */ 110 private void verifyIconIntent(long bssid, String fileName, byte[] data) { 111 ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class); 112 verify(mContext).sendBroadcastAsUser(intent.capture(), eq(UserHandle.ALL)); 113 assertEquals(PASSPOINT_ICON_RECEIVED_ACTION, intent.getValue().getAction()); 114 assertTrue(intent.getValue().getExtras().containsKey(EXTRA_PASSPOINT_ICON_BSSID)); 115 assertEquals(bssid, intent.getValue().getExtras().getLong(EXTRA_PASSPOINT_ICON_BSSID)); 116 assertTrue(intent.getValue().getExtras().containsKey(EXTRA_PASSPOINT_ICON_FILE)); 117 assertEquals(fileName, intent.getValue().getExtras().getString(EXTRA_PASSPOINT_ICON_FILE)); 118 if (data != null) { 119 assertTrue(intent.getValue().getExtras().containsKey(EXTRA_PASSPOINT_ICON_DATA)); 120 assertEquals(data, 121 intent.getValue().getExtras().getByteArray(EXTRA_PASSPOINT_ICON_DATA)); 122 } 123 } 124 125 /** 126 * Verify that the given Passpoint configuration matches the one that's added to 127 * the PasspointManager. 128 * 129 * @param expectedConfig The expected installed Passpoint configuration 130 */ 131 private void verifyInstalledConfig(PasspointConfiguration expectedConfig) { 132 List<PasspointConfiguration> installedConfigs = mManager.getProviderConfigs(); 133 assertEquals(1, installedConfigs.size()); 134 assertEquals(expectedConfig, installedConfigs.get(0)); 135 } 136 137 /** 138 * Create a mock PasspointProvider with default expectations. 139 * 140 * @param config The configuration associated with the provider 141 * @return {@link com.android.server.wifi.hotspot2.PasspointProvider} 142 */ 143 private PasspointProvider createMockProvider(PasspointConfiguration config) { 144 PasspointProvider provider = mock(PasspointProvider.class); 145 when(provider.installCertsAndKeys()).thenReturn(true); 146 when(provider.getConfig()).thenReturn(config); 147 return provider; 148 } 149 150 /** 151 * Helper function for adding a test provider to the manager. Return the mock 152 * provider that's added to the manager. 153 * 154 * @return {@link PasspointProvider} 155 */ 156 private PasspointProvider addTestProvider() { 157 PasspointConfiguration config = new PasspointConfiguration(); 158 config.homeSp = new HomeSP(); 159 config.homeSp.fqdn = TEST_FQDN; 160 config.homeSp.friendlyName = TEST_FRIENDLY_NAME; 161 config.credential = new Credential(); 162 config.credential.realm = TEST_REALM; 163 config.credential.caCertificate = FakeKeys.CA_CERT0; 164 config.credential.userCredential = new Credential.UserCredential(); 165 config.credential.userCredential.username = "username"; 166 config.credential.userCredential.password = "password"; 167 config.credential.userCredential.eapType = EAPConstants.EAP_TTLS; 168 config.credential.userCredential.nonEapInnerMethod = "MS-CHAP"; 169 PasspointProvider provider = createMockProvider(config); 170 when(mObjectFactory.makePasspointProvider(eq(config), eq(mWifiKeyStore), 171 eq(mSimAccessor), anyLong())).thenReturn(provider); 172 assertTrue(mManager.addOrUpdateProvider(config)); 173 174 return provider; 175 } 176 177 /** 178 * Helper function for creating a mock ScanDetail. 179 * 180 * @return {@link ScanDetail} 181 */ 182 private ScanDetail createMockScanDetail() { 183 NetworkDetail networkDetail = mock(NetworkDetail.class); 184 when(networkDetail.getSSID()).thenReturn(TEST_SSID); 185 when(networkDetail.getBSSID()).thenReturn(TEST_BSSID); 186 when(networkDetail.getHESSID()).thenReturn(TEST_HESSID); 187 when(networkDetail.getAnqpDomainID()).thenReturn(TEST_ANQP_DOMAIN_ID); 188 189 ScanDetail scanDetail = mock(ScanDetail.class); 190 when(scanDetail.getNetworkDetail()).thenReturn(networkDetail); 191 return scanDetail; 192 } 193 194 /** 195 * Validate the broadcast intent when icon file retrieval succeeded. 196 * 197 * @throws Exception 198 */ 199 @Test 200 public void iconResponseSuccess() throws Exception { 201 byte[] iconData = new byte[] {0x00, 0x11}; 202 mCallbacks.onIconResponse(BSSID, ICON_FILENAME, iconData); 203 verifyIconIntent(BSSID, ICON_FILENAME, iconData); 204 } 205 206 /** 207 * Validate the broadcast intent when icon file retrieval failed. 208 * 209 * @throws Exception 210 */ 211 @Test 212 public void iconResponseFailure() throws Exception { 213 mCallbacks.onIconResponse(BSSID, ICON_FILENAME, null); 214 verifyIconIntent(BSSID, ICON_FILENAME, null); 215 } 216 217 /** 218 * Verify that adding a provider with a null configuration will fail. 219 * 220 * @throws Exception 221 */ 222 @Test 223 public void addProviderWithNullConfig() throws Exception { 224 assertFalse(mManager.addOrUpdateProvider(null)); 225 } 226 227 /** 228 * Verify that adding a provider with a empty configuration will fail. 229 * 230 * @throws Exception 231 */ 232 @Test 233 public void addProviderWithEmptyConfig() throws Exception { 234 assertFalse(mManager.addOrUpdateProvider(new PasspointConfiguration())); 235 } 236 237 /** 238 * Verify taht adding a provider with an invalid credential will fail (using EAP-TLS 239 * for user credential). 240 * 241 * @throws Exception 242 */ 243 @Test 244 public void addProviderWithInvalidCredential() throws Exception { 245 PasspointConfiguration config = new PasspointConfiguration(); 246 config.homeSp = new HomeSP(); 247 config.homeSp.fqdn = TEST_FQDN; 248 config.homeSp.friendlyName = TEST_FRIENDLY_NAME; 249 config.credential = new Credential(); 250 config.credential.realm = TEST_REALM; 251 config.credential.caCertificate = FakeKeys.CA_CERT0; 252 config.credential.userCredential = new Credential.UserCredential(); 253 config.credential.userCredential.username = "username"; 254 config.credential.userCredential.password = "password"; 255 // EAP-TLS not allowed for user credential. 256 config.credential.userCredential.eapType = EAPConstants.EAP_TLS; 257 config.credential.userCredential.nonEapInnerMethod = "MS-CHAP"; 258 assertFalse(mManager.addOrUpdateProvider(config)); 259 } 260 261 /** 262 * Verify that adding a provider with a valid configuration and user credential will succeed. 263 * 264 * @throws Exception 265 */ 266 @Test 267 public void addRemoveProviderWithValidUserCredential() throws Exception { 268 PasspointConfiguration config = new PasspointConfiguration(); 269 config.homeSp = new HomeSP(); 270 config.homeSp.fqdn = TEST_FQDN; 271 config.homeSp.friendlyName = TEST_FRIENDLY_NAME; 272 config.credential = new Credential(); 273 config.credential.realm = TEST_REALM; 274 config.credential.caCertificate = FakeKeys.CA_CERT0; 275 config.credential.userCredential = new Credential.UserCredential(); 276 config.credential.userCredential.username = "username"; 277 config.credential.userCredential.password = "password"; 278 config.credential.userCredential.eapType = EAPConstants.EAP_TTLS; 279 config.credential.userCredential.nonEapInnerMethod = "MS-CHAP"; 280 PasspointProvider provider = createMockProvider(config); 281 when(mObjectFactory.makePasspointProvider(eq(config), eq(mWifiKeyStore), 282 eq(mSimAccessor), anyLong())).thenReturn(provider); 283 assertTrue(mManager.addOrUpdateProvider(config)); 284 verifyInstalledConfig(config); 285 286 // Remove the provider. 287 assertTrue(mManager.removeProvider(TEST_FQDN)); 288 verify(provider).uninstallCertsAndKeys(); 289 assertTrue(mManager.getProviderConfigs().isEmpty()); 290 } 291 292 /** 293 * Verify that adding a provider with a valid configuration and SIM credential will succeed. 294 * 295 * @throws Exception 296 */ 297 @Test 298 public void addRemoveProviderWithValidSimCredential() throws Exception { 299 PasspointConfiguration config = new PasspointConfiguration(); 300 config.homeSp = new HomeSP(); 301 config.homeSp.fqdn = TEST_FQDN; 302 config.homeSp.friendlyName = TEST_FRIENDLY_NAME; 303 config.credential = new Credential(); 304 config.credential.realm = TEST_REALM; 305 config.credential.simCredential = new Credential.SimCredential(); 306 config.credential.simCredential.imsi = TEST_IMSI; 307 config.credential.simCredential.eapType = EAPConstants.EAP_SIM; 308 when(mSimAccessor.getMatchingImsis(TEST_IMSI_PARAM)).thenReturn(new ArrayList<String>()); 309 PasspointProvider provider = createMockProvider(config); 310 when(mObjectFactory.makePasspointProvider(eq(config), eq(mWifiKeyStore), 311 eq(mSimAccessor), anyLong())).thenReturn(provider); 312 assertTrue(mManager.addOrUpdateProvider(config)); 313 verifyInstalledConfig(config); 314 315 // Remove the provider. 316 assertTrue(mManager.removeProvider(TEST_FQDN)); 317 verify(provider).uninstallCertsAndKeys(); 318 assertTrue(mManager.getProviderConfigs().isEmpty()); 319 } 320 321 /** 322 * Verify that adding a provider with an invalid SIM credential (configured IMSI doesn't 323 * match the IMSI of the installed SIM cards) will fail. 324 * 325 * @throws Exception 326 */ 327 @Test 328 public void addProviderWithValidSimCredentialWithInvalidIMSI() throws Exception { 329 PasspointConfiguration config = new PasspointConfiguration(); 330 config.homeSp = new HomeSP(); 331 config.homeSp.fqdn = TEST_FQDN; 332 config.homeSp.friendlyName = TEST_FRIENDLY_NAME; 333 config.credential = new Credential(); 334 config.credential.realm = TEST_REALM; 335 config.credential.simCredential = new Credential.SimCredential(); 336 config.credential.simCredential.imsi = TEST_IMSI; 337 config.credential.simCredential.eapType = EAPConstants.EAP_SIM; 338 when(mSimAccessor.getMatchingImsis(TEST_IMSI_PARAM)).thenReturn(null); 339 assertFalse(mManager.addOrUpdateProvider(config)); 340 } 341 342 /** 343 * Verify that adding a provider with the same base domain as the existing provider will 344 * succeed, and verify that the existing provider is replaced by the new provider with 345 * the new configuration. 346 * 347 * @throws Exception 348 */ 349 @Test 350 public void addProviderWithExistingConfig() throws Exception { 351 // Add a provider with the original configuration. 352 PasspointConfiguration origConfig = new PasspointConfiguration(); 353 origConfig.homeSp = new HomeSP(); 354 origConfig.homeSp.fqdn = TEST_FQDN; 355 origConfig.homeSp.friendlyName = TEST_FRIENDLY_NAME; 356 origConfig.credential = new Credential(); 357 origConfig.credential.realm = TEST_REALM; 358 origConfig.credential.simCredential = new Credential.SimCredential(); 359 origConfig.credential.simCredential.imsi = TEST_IMSI; 360 origConfig.credential.simCredential.eapType = EAPConstants.EAP_SIM; 361 when(mSimAccessor.getMatchingImsis(TEST_IMSI_PARAM)).thenReturn(new ArrayList<String>()); 362 PasspointProvider origProvider = createMockProvider(origConfig); 363 when(mObjectFactory.makePasspointProvider(eq(origConfig), eq(mWifiKeyStore), 364 eq(mSimAccessor), anyLong())).thenReturn(origProvider); 365 assertTrue(mManager.addOrUpdateProvider(origConfig)); 366 verifyInstalledConfig(origConfig); 367 368 // Add another provider with the same base domain as the existing provider. 369 // This should replace the existing provider with the new configuration. 370 PasspointConfiguration newConfig = new PasspointConfiguration(); 371 newConfig.homeSp = new HomeSP(); 372 newConfig.homeSp.fqdn = TEST_FQDN; 373 newConfig.homeSp.friendlyName = TEST_FRIENDLY_NAME; 374 newConfig.credential = new Credential(); 375 newConfig.credential.realm = TEST_REALM; 376 newConfig.credential.caCertificate = FakeKeys.CA_CERT0; 377 newConfig.credential.userCredential = new Credential.UserCredential(); 378 newConfig.credential.userCredential.username = "username"; 379 newConfig.credential.userCredential.password = "password"; 380 newConfig.credential.userCredential.eapType = EAPConstants.EAP_TTLS; 381 newConfig.credential.userCredential.nonEapInnerMethod = "MS-CHAP"; 382 PasspointProvider newProvider = createMockProvider(newConfig); 383 when(mObjectFactory.makePasspointProvider(eq(newConfig), eq(mWifiKeyStore), 384 eq(mSimAccessor), anyLong())).thenReturn(newProvider); 385 assertTrue(mManager.addOrUpdateProvider(newConfig)); 386 verifyInstalledConfig(newConfig); 387 } 388 389 /** 390 * Verify that adding a provider will fail when failing to install certificates and 391 * key to the keystore. 392 * 393 * @throws Exception 394 */ 395 @Test 396 public void addProviderOnKeyInstallationFailiure() throws Exception { 397 PasspointConfiguration config = new PasspointConfiguration(); 398 config.homeSp = new HomeSP(); 399 config.homeSp.fqdn = TEST_FQDN; 400 config.homeSp.friendlyName = TEST_FRIENDLY_NAME; 401 config.credential = new Credential(); 402 config.credential.realm = TEST_REALM; 403 config.credential.caCertificate = FakeKeys.CA_CERT0; 404 config.credential.userCredential = new Credential.UserCredential(); 405 config.credential.userCredential.username = "username"; 406 config.credential.userCredential.password = "password"; 407 config.credential.userCredential.eapType = EAPConstants.EAP_TTLS; 408 config.credential.userCredential.nonEapInnerMethod = "MS-CHAP"; 409 PasspointProvider provider = mock(PasspointProvider.class); 410 when(provider.installCertsAndKeys()).thenReturn(false); 411 when(mObjectFactory.makePasspointProvider(eq(config), eq(mWifiKeyStore), 412 eq(mSimAccessor), anyLong())).thenReturn(provider); 413 assertFalse(mManager.addOrUpdateProvider(config)); 414 } 415 416 /** 417 * Verify that removing a non-existing provider will fail. 418 * 419 * @throws Exception 420 */ 421 @Test 422 public void removeNonExistingProvider() throws Exception { 423 assertFalse(mManager.removeProvider(TEST_FQDN)); 424 } 425 426 /** 427 * Verify that an empty list will be returned when no providers are installed. 428 * 429 * @throws Exception 430 */ 431 @Test 432 public void matchProviderWithNoProvidersInstalled() throws Exception { 433 List<Pair<PasspointProvider, PasspointMatch>> result = 434 mManager.matchProvider(createMockScanDetail()); 435 assertTrue(result.isEmpty()); 436 } 437 438 /** 439 * Verify that an empty list will be returned when ANQP entry doesn't exist in the cache. 440 * 441 * @throws Exception 442 */ 443 @Test 444 public void matchProviderWithAnqpCacheMissed() throws Exception { 445 addTestProvider(); 446 447 ANQPNetworkKey anqpKey = ANQPNetworkKey.buildKey(TEST_SSID, TEST_BSSID, TEST_HESSID, 448 TEST_ANQP_DOMAIN_ID); 449 when(mAnqpCache.getEntry(anqpKey)).thenReturn(null); 450 List<Pair<PasspointProvider, PasspointMatch>> result = 451 mManager.matchProvider(createMockScanDetail()); 452 assertTrue(result.isEmpty()); 453 } 454 455 /** 456 * Verify that the returned list will contained an expected provider when a HomeProvider 457 * is matched. 458 * 459 * @throws Exception 460 */ 461 @Test 462 public void matchProviderAsHomeProvider() throws Exception { 463 PasspointProvider provider = addTestProvider(); 464 ANQPData entry = new ANQPData(mClock, null); 465 ANQPNetworkKey anqpKey = ANQPNetworkKey.buildKey(TEST_SSID, TEST_BSSID, TEST_HESSID, 466 TEST_ANQP_DOMAIN_ID); 467 468 when(mAnqpCache.getEntry(anqpKey)).thenReturn(entry); 469 when(provider.match(anyMap())).thenReturn(PasspointMatch.HomeProvider); 470 List<Pair<PasspointProvider, PasspointMatch>> result = 471 mManager.matchProvider(createMockScanDetail()); 472 assertEquals(1, result.size()); 473 assertEquals(PasspointMatch.HomeProvider, result.get(0).second); 474 assertEquals(TEST_FQDN, provider.getConfig().homeSp.fqdn); 475 } 476 477 /** 478 * Verify that the returned list will contained an expected provider when a RoamingProvider 479 * is matched. 480 * 481 * @throws Exception 482 */ 483 @Test 484 public void matchProviderAsRoamingProvider() throws Exception { 485 PasspointProvider provider = addTestProvider(); 486 ANQPData entry = new ANQPData(mClock, null); 487 ANQPNetworkKey anqpKey = ANQPNetworkKey.buildKey(TEST_SSID, TEST_BSSID, TEST_HESSID, 488 TEST_ANQP_DOMAIN_ID); 489 490 when(mAnqpCache.getEntry(anqpKey)).thenReturn(entry); 491 when(provider.match(anyMap())).thenReturn(PasspointMatch.RoamingProvider); 492 List<Pair<PasspointProvider, PasspointMatch>> result = 493 mManager.matchProvider(createMockScanDetail()); 494 assertEquals(1, result.size()); 495 assertEquals(PasspointMatch.RoamingProvider, result.get(0).second); 496 assertEquals(TEST_FQDN, provider.getConfig().homeSp.fqdn); 497 } 498 499 /** 500 * Verify that an empty list will be returned when there is no matching provider. 501 * 502 * @throws Exception 503 */ 504 @Test 505 public void matchProviderWithNoMatch() throws Exception { 506 PasspointProvider provider = addTestProvider(); 507 ANQPData entry = new ANQPData(mClock, null); 508 ANQPNetworkKey anqpKey = ANQPNetworkKey.buildKey(TEST_SSID, TEST_BSSID, TEST_HESSID, 509 TEST_ANQP_DOMAIN_ID); 510 511 when(mAnqpCache.getEntry(anqpKey)).thenReturn(entry); 512 when(provider.match(anyMap())).thenReturn(PasspointMatch.None); 513 List<Pair<PasspointProvider, PasspointMatch>> result = 514 mManager.matchProvider(createMockScanDetail()); 515 assertEquals(0, result.size()); 516 } 517 518 /** 519 * Verify the expectations for sweepCache. 520 * 521 * @throws Exception 522 */ 523 @Test 524 public void sweepCache() throws Exception { 525 mManager.sweepCache(); 526 verify(mAnqpCache).sweep(); 527 } 528} 529