PasspointManagerTest.java revision ec28f863c5e46c0a75e8bdb92283304b875ee0f2
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.ACTION_PASSPOINT_DEAUTH_IMMINENT;
20import static android.net.wifi.WifiManager.ACTION_PASSPOINT_ICON;
21import static android.net.wifi.WifiManager.ACTION_PASSPOINT_SUBSCRIPTION_REMEDIATION;
22import static android.net.wifi.WifiManager.EXTRA_BSSID_LONG;
23import static android.net.wifi.WifiManager.EXTRA_DELAY;
24import static android.net.wifi.WifiManager.EXTRA_ESS;
25import static android.net.wifi.WifiManager.EXTRA_ICON_INFO;
26import static android.net.wifi.WifiManager.EXTRA_SUBSCRIPTION_REMEDIATION_METHOD;
27import static android.net.wifi.WifiManager.EXTRA_URL;
28
29import static org.junit.Assert.assertEquals;
30import static org.junit.Assert.assertFalse;
31import static org.junit.Assert.assertTrue;
32import static org.mockito.Mockito.any;
33import static org.mockito.Mockito.anyBoolean;
34import static org.mockito.Mockito.anyLong;
35import static org.mockito.Mockito.anyMap;
36import static org.mockito.Mockito.eq;
37import static org.mockito.Mockito.mock;
38import static org.mockito.Mockito.never;
39import static org.mockito.Mockito.reset;
40import static org.mockito.Mockito.verify;
41import static org.mockito.Mockito.when;
42import static org.mockito.MockitoAnnotations.initMocks;
43
44import android.content.Context;
45import android.content.Intent;
46import android.net.wifi.EAPConstants;
47import android.net.wifi.IconInfo;
48import android.net.wifi.ScanResult;
49import android.net.wifi.WifiConfiguration;
50import android.net.wifi.WifiEnterpriseConfig;
51import android.net.wifi.hotspot2.PasspointConfiguration;
52import android.net.wifi.hotspot2.pps.Credential;
53import android.net.wifi.hotspot2.pps.HomeSp;
54import android.os.UserHandle;
55import android.test.suitebuilder.annotation.SmallTest;
56import android.util.Base64;
57import android.util.Pair;
58
59import com.android.server.wifi.Clock;
60import com.android.server.wifi.FakeKeys;
61import com.android.server.wifi.IMSIParameter;
62import com.android.server.wifi.SIMAccessor;
63import com.android.server.wifi.WifiConfigManager;
64import com.android.server.wifi.WifiConfigStore;
65import com.android.server.wifi.WifiKeyStore;
66import com.android.server.wifi.WifiNative;
67import com.android.server.wifi.hotspot2.anqp.ANQPElement;
68import com.android.server.wifi.hotspot2.anqp.Constants.ANQPElementType;
69import com.android.server.wifi.hotspot2.anqp.DomainNameElement;
70
71import org.junit.Before;
72import org.junit.Test;
73import org.mockito.ArgumentCaptor;
74import org.mockito.Mock;
75
76import java.nio.charset.StandardCharsets;
77import java.util.ArrayList;
78import java.util.Arrays;
79import java.util.HashMap;
80import java.util.List;
81import java.util.Map;
82
83/**
84 * Unit tests for {@link com.android.server.wifi.hotspot2.PasspointManager}.
85 */
86@SmallTest
87public class PasspointManagerTest {
88    private static final long BSSID = 0x112233445566L;
89    private static final String ICON_FILENAME = "test";
90    private static final String TEST_FQDN = "test1.test.com";
91    private static final String TEST_FRIENDLY_NAME = "friendly name";
92    private static final String TEST_REALM = "realm.test.com";
93    private static final String TEST_IMSI = "1234*";
94    private static final IMSIParameter TEST_IMSI_PARAM = IMSIParameter.build(TEST_IMSI);
95
96    private static final String TEST_SSID = "TestSSID";
97    private static final long TEST_BSSID = 0x112233445566L;
98    private static final String TEST_BSSID_STRING = "11:22:33:44:55:66";
99    private static final long TEST_HESSID = 0x5678L;
100    private static final int TEST_ANQP_DOMAIN_ID = 0;
101    private static final ANQPNetworkKey TEST_ANQP_KEY = ANQPNetworkKey.buildKey(
102            TEST_SSID, TEST_BSSID, TEST_HESSID, TEST_ANQP_DOMAIN_ID);
103
104    @Mock Context mContext;
105    @Mock WifiNative mWifiNative;
106    @Mock WifiKeyStore mWifiKeyStore;
107    @Mock Clock mClock;
108    @Mock SIMAccessor mSimAccessor;
109    @Mock PasspointObjectFactory mObjectFactory;
110    @Mock PasspointEventHandler.Callbacks mCallbacks;
111    @Mock AnqpCache mAnqpCache;
112    @Mock ANQPRequestManager mAnqpRequestManager;
113    @Mock WifiConfigManager mWifiConfigManager;
114    @Mock WifiConfigStore mWifiConfigStore;
115    @Mock PasspointConfigStoreData.DataSource mDataSource;
116    PasspointManager mManager;
117
118    /** Sets up test. */
119    @Before
120    public void setUp() throws Exception {
121        initMocks(this);
122        when(mObjectFactory.makeAnqpCache(mClock)).thenReturn(mAnqpCache);
123        when(mObjectFactory.makeANQPRequestManager(any(PasspointEventHandler.class), eq(mClock)))
124                .thenReturn(mAnqpRequestManager);
125        mManager = new PasspointManager(mContext, mWifiNative, mWifiKeyStore, mClock,
126                mSimAccessor, mObjectFactory, mWifiConfigManager, mWifiConfigStore);
127        ArgumentCaptor<PasspointEventHandler.Callbacks> callbacks =
128                ArgumentCaptor.forClass(PasspointEventHandler.Callbacks.class);
129        verify(mObjectFactory).makePasspointEventHandler(any(WifiNative.class),
130                                                         callbacks.capture());
131        ArgumentCaptor<PasspointConfigStoreData.DataSource> dataSource =
132                ArgumentCaptor.forClass(PasspointConfigStoreData.DataSource.class);
133        verify(mObjectFactory).makePasspointConfigStoreData(
134                any(WifiKeyStore.class), any(SIMAccessor.class), dataSource.capture());
135        mCallbacks = callbacks.getValue();
136        mDataSource = dataSource.getValue();
137    }
138
139    /**
140     * Verify {@link WifiManager#ACTION_PASSPOINT_ICON} broadcast intent.
141     * @param bssid BSSID of the AP
142     * @param fileName Name of the icon file
143     * @param data icon data byte array
144     */
145    private void verifyIconIntent(long bssid, String fileName, byte[] data) {
146        ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class);
147        verify(mContext).sendBroadcastAsUser(intent.capture(), eq(UserHandle.ALL),
148                eq(android.Manifest.permission.ACCESS_WIFI_STATE));
149        assertEquals(ACTION_PASSPOINT_ICON, intent.getValue().getAction());
150        assertTrue(intent.getValue().getExtras().containsKey(EXTRA_BSSID_LONG));
151        assertEquals(bssid, intent.getValue().getExtras().getLong(EXTRA_BSSID_LONG));
152        assertTrue(intent.getValue().getExtras().containsKey(EXTRA_ICON_INFO));
153        IconInfo expectedInfo = new IconInfo(fileName, data);
154        assertEquals(new IconInfo(fileName, data),
155                (IconInfo) intent.getValue().getExtras().getParcelable(EXTRA_ICON_INFO));
156    }
157
158    /**
159     * Verify that the given Passpoint configuration matches the one that's added to
160     * the PasspointManager.
161     *
162     * @param expectedConfig The expected installed Passpoint configuration
163     */
164    private void verifyInstalledConfig(PasspointConfiguration expectedConfig) {
165        List<PasspointConfiguration> installedConfigs = mManager.getProviderConfigs();
166        assertEquals(1, installedConfigs.size());
167        assertEquals(expectedConfig, installedConfigs.get(0));
168    }
169
170    /**
171     * Create a mock PasspointProvider with default expectations.
172     *
173     * @param config The configuration associated with the provider
174     * @return {@link com.android.server.wifi.hotspot2.PasspointProvider}
175     */
176    private PasspointProvider createMockProvider(PasspointConfiguration config) {
177        PasspointProvider provider = mock(PasspointProvider.class);
178        when(provider.installCertsAndKeys()).thenReturn(true);
179        when(provider.getConfig()).thenReturn(config);
180        return provider;
181    }
182
183    /**
184     * Helper function for creating a test configuration with user credential.
185     *
186     * @return {@link PasspointConfiguration}
187     */
188    private PasspointConfiguration createTestConfigWithUserCredential() {
189        PasspointConfiguration config = new PasspointConfiguration();
190        HomeSp homeSp = new HomeSp();
191        homeSp.setFqdn(TEST_FQDN);
192        homeSp.setFriendlyName(TEST_FRIENDLY_NAME);
193        config.setHomeSp(homeSp);
194        Credential credential = new Credential();
195        credential.setRealm(TEST_REALM);
196        credential.setCaCertificate(FakeKeys.CA_CERT0);
197        Credential.UserCredential userCredential = new Credential.UserCredential();
198        userCredential.setUsername("username");
199        userCredential.setPassword("password");
200        userCredential.setEapType(EAPConstants.EAP_TTLS);
201        userCredential.setNonEapInnerMethod(Credential.UserCredential.AUTH_METHOD_MSCHAP);
202        credential.setUserCredential(userCredential);
203        config.setCredential(credential);
204        return config;
205    }
206
207    /**
208     * Helper function for creating a test configuration with SIM credential.
209     *
210     * @return {@link PasspointConfiguration}
211     */
212    private PasspointConfiguration createTestConfigWithSimCredential() {
213        PasspointConfiguration config = new PasspointConfiguration();
214        HomeSp homeSp = new HomeSp();
215        homeSp.setFqdn(TEST_FQDN);
216        homeSp.setFriendlyName(TEST_FRIENDLY_NAME);
217        config.setHomeSp(homeSp);
218        Credential credential = new Credential();
219        credential.setRealm(TEST_REALM);
220        Credential.SimCredential simCredential = new Credential.SimCredential();
221        simCredential.setImsi(TEST_IMSI);
222        simCredential.setEapType(EAPConstants.EAP_SIM);
223        credential.setSimCredential(simCredential);
224        config.setCredential(credential);
225        return config;
226    }
227
228    /**
229     * Helper function for adding a test provider to the manager.  Return the mock
230     * provider that's added to the manager.
231     *
232     * @return {@link PasspointProvider}
233     */
234    private PasspointProvider addTestProvider() {
235        PasspointConfiguration config = createTestConfigWithUserCredential();
236        PasspointProvider provider = createMockProvider(config);
237        when(mObjectFactory.makePasspointProvider(eq(config), eq(mWifiKeyStore),
238                eq(mSimAccessor), anyLong())).thenReturn(provider);
239        assertTrue(mManager.addOrUpdateProvider(config));
240
241        return provider;
242    }
243
244    /**
245     * Helper function for creating a ScanResult for testing.
246     *
247     * @return {@link ScanResult}
248     */
249    private ScanResult createTestScanResult() {
250        ScanResult scanResult = new ScanResult();
251        scanResult.SSID = TEST_SSID;
252        scanResult.BSSID = TEST_BSSID_STRING;
253        scanResult.hessid = TEST_HESSID;
254        return scanResult;
255    }
256
257    /**
258     * Verify that the ANQP elements will be added to the ANQP cache on receiving a successful
259     * response.
260     *
261     * @throws Exception
262     */
263    @Test
264    public void anqpResponseSuccess() throws Exception {
265        Map<ANQPElementType, ANQPElement> anqpElementMap = new HashMap<>();
266        anqpElementMap.put(ANQPElementType.ANQPDomName,
267                new DomainNameElement(Arrays.asList(new String[] {"test.com"})));
268
269        when(mAnqpRequestManager.onRequestCompleted(TEST_BSSID, true)).thenReturn(TEST_ANQP_KEY);
270        mCallbacks.onANQPResponse(TEST_BSSID, anqpElementMap);
271        verify(mAnqpCache).addEntry(TEST_ANQP_KEY, anqpElementMap);
272    }
273
274    /**
275     * Verify that no ANQP elements will be added to the ANQP cache on receiving a successful
276     * response for a request that's not sent by us.
277     *
278     * @throws Exception
279     */
280    @Test
281    public void anqpResponseSuccessWithUnknownRequest() throws Exception {
282        Map<ANQPElementType, ANQPElement> anqpElementMap = new HashMap<>();
283        anqpElementMap.put(ANQPElementType.ANQPDomName,
284                new DomainNameElement(Arrays.asList(new String[] {"test.com"})));
285
286        when(mAnqpRequestManager.onRequestCompleted(TEST_BSSID, true)).thenReturn(null);
287        mCallbacks.onANQPResponse(TEST_BSSID, anqpElementMap);
288        verify(mAnqpCache, never()).addEntry(any(ANQPNetworkKey.class), anyMap());
289    }
290
291    /**
292     * Verify that no ANQP elements will be added to the ANQP cache on receiving a failure response.
293     *
294     * @throws Exception
295     */
296    @Test
297    public void anqpResponseFailure() throws Exception {
298        when(mAnqpRequestManager.onRequestCompleted(TEST_BSSID, false)).thenReturn(TEST_ANQP_KEY);
299        mCallbacks.onANQPResponse(TEST_BSSID, null);
300        verify(mAnqpCache, never()).addEntry(any(ANQPNetworkKey.class), anyMap());
301
302    }
303
304    /**
305     * Validate the broadcast intent when icon file retrieval succeeded.
306     *
307     * @throws Exception
308     */
309    @Test
310    public void iconResponseSuccess() throws Exception {
311        byte[] iconData = new byte[] {0x00, 0x11};
312        mCallbacks.onIconResponse(BSSID, ICON_FILENAME, iconData);
313        verifyIconIntent(BSSID, ICON_FILENAME, iconData);
314    }
315
316    /**
317     * Validate the broadcast intent when icon file retrieval failed.
318     *
319     * @throws Exception
320     */
321    @Test
322    public void iconResponseFailure() throws Exception {
323        mCallbacks.onIconResponse(BSSID, ICON_FILENAME, null);
324        verifyIconIntent(BSSID, ICON_FILENAME, null);
325    }
326
327    /**
328     * Validate the broadcast intent {@link WifiManager#ACTION_PASSPOINT_DEAUTH_IMMINENT} when
329     * Deauth Imminent WNM frame is received.
330     *
331     * @throws Exception
332     */
333    @Test
334    public void onDeauthImminentReceived() throws Exception {
335        String reasonUrl = "test.com";
336        int delay = 123;
337        boolean ess = true;
338
339        mCallbacks.onWnmFrameReceived(new WnmData(BSSID, reasonUrl, ess, delay));
340        // Verify the broadcast intent.
341        ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class);
342        verify(mContext).sendBroadcastAsUser(intent.capture(), eq(UserHandle.ALL),
343                eq(android.Manifest.permission.ACCESS_WIFI_STATE));
344        assertEquals(ACTION_PASSPOINT_DEAUTH_IMMINENT, intent.getValue().getAction());
345        assertTrue(intent.getValue().getExtras().containsKey(EXTRA_BSSID_LONG));
346        assertEquals(BSSID, intent.getValue().getExtras().getLong(EXTRA_BSSID_LONG));
347        assertTrue(intent.getValue().getExtras().containsKey(EXTRA_ESS));
348        assertEquals(ess, intent.getValue().getExtras().getBoolean(EXTRA_ESS));
349        assertTrue(intent.getValue().getExtras().containsKey(EXTRA_DELAY));
350        assertEquals(delay, intent.getValue().getExtras().getInt(EXTRA_DELAY));
351        assertTrue(intent.getValue().getExtras().containsKey(EXTRA_URL));
352        assertEquals(reasonUrl, intent.getValue().getExtras().getString(EXTRA_URL));
353    }
354
355    /**
356     * Validate the broadcast intent {@link WifiManager#ACTION_PASSPOINT_SUBSCRIPTION_REMEDIATION}
357     * when Subscription Remediation WNM frame is received.
358     *
359     * @throws Exception
360     */
361    @Test
362    public void onSubscriptionRemediationReceived() throws Exception {
363        int serverMethod = 1;
364        String serverUrl = "testUrl";
365
366        mCallbacks.onWnmFrameReceived(new WnmData(BSSID, serverUrl, serverMethod));
367        // Verify the broadcast intent.
368        ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class);
369        verify(mContext).sendBroadcastAsUser(intent.capture(), eq(UserHandle.ALL),
370                eq(android.Manifest.permission.ACCESS_WIFI_STATE));
371        assertEquals(ACTION_PASSPOINT_SUBSCRIPTION_REMEDIATION, intent.getValue().getAction());
372        assertTrue(intent.getValue().getExtras().containsKey(EXTRA_BSSID_LONG));
373        assertEquals(BSSID, intent.getValue().getExtras().getLong(EXTRA_BSSID_LONG));
374        assertTrue(intent.getValue().getExtras().containsKey(
375                EXTRA_SUBSCRIPTION_REMEDIATION_METHOD));
376        assertEquals(serverMethod, intent.getValue().getExtras().getInt(
377                EXTRA_SUBSCRIPTION_REMEDIATION_METHOD));
378        assertTrue(intent.getValue().getExtras().containsKey(EXTRA_URL));
379        assertEquals(serverUrl, intent.getValue().getExtras().getString(EXTRA_URL));
380    }
381
382    /**
383     * Verify that adding a provider with a null configuration will fail.
384     *
385     * @throws Exception
386     */
387    @Test
388    public void addProviderWithNullConfig() throws Exception {
389        assertFalse(mManager.addOrUpdateProvider(null));
390    }
391
392    /**
393     * Verify that adding a provider with a empty configuration will fail.
394     *
395     * @throws Exception
396     */
397    @Test
398    public void addProviderWithEmptyConfig() throws Exception {
399        assertFalse(mManager.addOrUpdateProvider(new PasspointConfiguration()));
400    }
401
402    /**
403     * Verify taht adding a provider with an invalid credential will fail (using EAP-TLS
404     * for user credential).
405     *
406     * @throws Exception
407     */
408    @Test
409    public void addProviderWithInvalidCredential() throws Exception {
410        PasspointConfiguration config = createTestConfigWithUserCredential();
411        // EAP-TLS not allowed for user credential.
412        config.getCredential().getUserCredential().setEapType(EAPConstants.EAP_TLS);
413        assertFalse(mManager.addOrUpdateProvider(config));
414    }
415
416    /**
417     * Verify that adding a provider with a valid configuration and user credential will succeed.
418     *
419     * @throws Exception
420     */
421    @Test
422    public void addRemoveProviderWithValidUserCredential() throws Exception {
423        PasspointConfiguration config = createTestConfigWithUserCredential();
424        PasspointProvider provider = createMockProvider(config);
425        when(mObjectFactory.makePasspointProvider(eq(config), eq(mWifiKeyStore),
426                eq(mSimAccessor), anyLong())).thenReturn(provider);
427        assertTrue(mManager.addOrUpdateProvider(config));
428        verifyInstalledConfig(config);
429        verify(mWifiConfigManager).saveToStore(true);
430        reset(mWifiConfigManager);
431
432        // Verify content in the data source.
433        List<PasspointProvider> providers = mDataSource.getProviders();
434        assertEquals(1, providers.size());
435        assertEquals(config, providers.get(0).getConfig());
436        // Provider index start with 0, should be 1 after adding a provider.
437        assertEquals(1, mDataSource.getProviderIndex());
438
439        // Remove the provider.
440        assertTrue(mManager.removeProvider(TEST_FQDN));
441        verify(provider).uninstallCertsAndKeys();
442        verify(mWifiConfigManager).saveToStore(true);
443        assertTrue(mManager.getProviderConfigs().isEmpty());
444
445        // Verify content in the data source.
446        assertTrue(mDataSource.getProviders().isEmpty());
447        // Removing a provider should not change the provider index.
448        assertEquals(1, mDataSource.getProviderIndex());
449    }
450
451    /**
452     * Verify that adding a provider with a valid configuration and SIM credential will succeed.
453     *
454     * @throws Exception
455     */
456    @Test
457    public void addRemoveProviderWithValidSimCredential() throws Exception {
458        PasspointConfiguration config = createTestConfigWithSimCredential();
459        PasspointProvider provider = createMockProvider(config);
460        when(mObjectFactory.makePasspointProvider(eq(config), eq(mWifiKeyStore),
461                eq(mSimAccessor), anyLong())).thenReturn(provider);
462        assertTrue(mManager.addOrUpdateProvider(config));
463        verifyInstalledConfig(config);
464        verify(mWifiConfigManager).saveToStore(true);
465        reset(mWifiConfigManager);
466
467        // Verify content in the data source.
468        List<PasspointProvider> providers = mDataSource.getProviders();
469        assertEquals(1, providers.size());
470        assertEquals(config, providers.get(0).getConfig());
471        // Provider index start with 0, should be 1 after adding a provider.
472        assertEquals(1, mDataSource.getProviderIndex());
473
474        // Remove the provider.
475        assertTrue(mManager.removeProvider(TEST_FQDN));
476        verify(provider).uninstallCertsAndKeys();
477        verify(mWifiConfigManager).saveToStore(true);
478        assertTrue(mManager.getProviderConfigs().isEmpty());
479
480        // Verify content in the data source.
481        assertTrue(mDataSource.getProviders().isEmpty());
482        // Removing a provider should not change the provider index.
483        assertEquals(1, mDataSource.getProviderIndex());
484    }
485
486    /**
487     * Verify that adding a provider with the same base domain as the existing provider will
488     * succeed, and verify that the existing provider is replaced by the new provider with
489     * the new configuration.
490     *
491     * @throws Exception
492     */
493    @Test
494    public void addProviderWithExistingConfig() throws Exception {
495        // Add a provider with the original configuration.
496        PasspointConfiguration origConfig = createTestConfigWithSimCredential();
497        PasspointProvider origProvider = createMockProvider(origConfig);
498        when(mObjectFactory.makePasspointProvider(eq(origConfig), eq(mWifiKeyStore),
499                eq(mSimAccessor), anyLong())).thenReturn(origProvider);
500        assertTrue(mManager.addOrUpdateProvider(origConfig));
501        verifyInstalledConfig(origConfig);
502        verify(mWifiConfigManager).saveToStore(true);
503        reset(mWifiConfigManager);
504
505        // Verify data source content.
506        List<PasspointProvider> origProviders = mDataSource.getProviders();
507        assertEquals(1, origProviders.size());
508        assertEquals(origConfig, origProviders.get(0).getConfig());
509        assertEquals(1, mDataSource.getProviderIndex());
510
511        // Add another provider with the same base domain as the existing provider.
512        // This should replace the existing provider with the new configuration.
513        PasspointConfiguration newConfig = createTestConfigWithUserCredential();
514        PasspointProvider newProvider = createMockProvider(newConfig);
515        when(mObjectFactory.makePasspointProvider(eq(newConfig), eq(mWifiKeyStore),
516                eq(mSimAccessor), anyLong())).thenReturn(newProvider);
517        assertTrue(mManager.addOrUpdateProvider(newConfig));
518        verifyInstalledConfig(newConfig);
519        verify(mWifiConfigManager).saveToStore(true);
520
521        // Verify data source content.
522        List<PasspointProvider> newProviders = mDataSource.getProviders();
523        assertEquals(1, newProviders.size());
524        assertEquals(newConfig, newProviders.get(0).getConfig());
525        assertEquals(2, mDataSource.getProviderIndex());
526    }
527
528    /**
529     * Verify that adding a provider will fail when failing to install certificates and
530     * key to the keystore.
531     *
532     * @throws Exception
533     */
534    @Test
535    public void addProviderOnKeyInstallationFailiure() throws Exception {
536        PasspointConfiguration config = createTestConfigWithUserCredential();
537        PasspointProvider provider = mock(PasspointProvider.class);
538        when(provider.installCertsAndKeys()).thenReturn(false);
539        when(mObjectFactory.makePasspointProvider(eq(config), eq(mWifiKeyStore),
540                eq(mSimAccessor), anyLong())).thenReturn(provider);
541        assertFalse(mManager.addOrUpdateProvider(config));
542    }
543
544    /**
545     * Verify that removing a non-existing provider will fail.
546     *
547     * @throws Exception
548     */
549    @Test
550    public void removeNonExistingProvider() throws Exception {
551        assertFalse(mManager.removeProvider(TEST_FQDN));
552    }
553
554    /**
555     * Verify that an empty list will be returned when no providers are installed.
556     *
557     * @throws Exception
558     */
559    @Test
560    public void matchProviderWithNoProvidersInstalled() throws Exception {
561        List<Pair<PasspointProvider, PasspointMatch>> result =
562                mManager.matchProvider(createTestScanResult());
563        assertTrue(result.isEmpty());
564    }
565
566    /**
567     * Verify that an empty list will be returned when ANQP entry doesn't exist in the cache.
568     *
569     * @throws Exception
570     */
571    @Test
572    public void matchProviderWithAnqpCacheMissed() throws Exception {
573        addTestProvider();
574
575        when(mAnqpCache.getEntry(TEST_ANQP_KEY)).thenReturn(null);
576        List<Pair<PasspointProvider, PasspointMatch>> result =
577                mManager.matchProvider(createTestScanResult());
578        // Verify that a request for ANQP elements is initiated.
579        verify(mAnqpRequestManager).requestANQPElements(eq(TEST_BSSID), any(ANQPNetworkKey.class),
580                anyBoolean(), anyBoolean());
581        assertTrue(result.isEmpty());
582    }
583
584    /**
585     * Verify that the returned list will contained an expected provider when a HomeProvider
586     * is matched.
587     *
588     * @throws Exception
589     */
590    @Test
591    public void matchProviderAsHomeProvider() throws Exception {
592        PasspointProvider provider = addTestProvider();
593        ANQPData entry = new ANQPData(mClock, null);
594
595        when(mAnqpCache.getEntry(TEST_ANQP_KEY)).thenReturn(entry);
596        when(provider.match(anyMap())).thenReturn(PasspointMatch.HomeProvider);
597        List<Pair<PasspointProvider, PasspointMatch>> result =
598                mManager.matchProvider(createTestScanResult());
599        assertEquals(1, result.size());
600        assertEquals(PasspointMatch.HomeProvider, result.get(0).second);
601    }
602
603    /**
604     * Verify that the returned list will contained an expected provider when a RoamingProvider
605     * is matched.
606     *
607     * @throws Exception
608     */
609    @Test
610    public void matchProviderAsRoamingProvider() throws Exception {
611        PasspointProvider provider = addTestProvider();
612        ANQPData entry = new ANQPData(mClock, null);
613
614        when(mAnqpCache.getEntry(TEST_ANQP_KEY)).thenReturn(entry);
615        when(provider.match(anyMap())).thenReturn(PasspointMatch.RoamingProvider);
616        List<Pair<PasspointProvider, PasspointMatch>> result =
617                mManager.matchProvider(createTestScanResult());
618        assertEquals(1, result.size());
619        assertEquals(PasspointMatch.RoamingProvider, result.get(0).second);
620        assertEquals(TEST_FQDN, provider.getConfig().getHomeSp().getFqdn());
621    }
622
623    /**
624     * Verify that an empty list will be returned when there is no matching provider.
625     *
626     * @throws Exception
627     */
628    @Test
629    public void matchProviderWithNoMatch() throws Exception {
630        PasspointProvider provider = addTestProvider();
631        ANQPData entry = new ANQPData(mClock, null);
632
633        when(mAnqpCache.getEntry(TEST_ANQP_KEY)).thenReturn(entry);
634        when(provider.match(anyMap())).thenReturn(PasspointMatch.None);
635        List<Pair<PasspointProvider, PasspointMatch>> result =
636                mManager.matchProvider(createTestScanResult());
637        assertEquals(0, result.size());
638    }
639
640    /**
641     * Verify the expectations for sweepCache.
642     *
643     * @throws Exception
644     */
645    @Test
646    public void sweepCache() throws Exception {
647        mManager.sweepCache();
648        verify(mAnqpCache).sweep();
649    }
650
651    /**
652     * Verify that an empty map will be returned if ANQP elements are not cached for the given AP.
653     *
654     * @throws Exception
655     */
656    @Test
657    public void getANQPElementsWithNoMatchFound() throws Exception {
658        when(mAnqpCache.getEntry(TEST_ANQP_KEY)).thenReturn(null);
659        assertTrue(mManager.getANQPElements(createTestScanResult()).isEmpty());
660    }
661
662    /**
663     * Verify that an expected ANQP elements will be returned if ANQP elements are cached for the
664     * given AP.
665     *
666     * @throws Exception
667     */
668    @Test
669    public void getANQPElementsWithMatchFound() throws Exception {
670        Map<ANQPElementType, ANQPElement> anqpElementMap = new HashMap<>();
671        anqpElementMap.put(ANQPElementType.ANQPDomName,
672                new DomainNameElement(Arrays.asList(new String[] {"test.com"})));
673        ANQPData entry = new ANQPData(mClock, anqpElementMap);
674
675        when(mAnqpCache.getEntry(TEST_ANQP_KEY)).thenReturn(entry);
676        assertEquals(anqpElementMap, mManager.getANQPElements(createTestScanResult()));
677    }
678
679    /**
680     * Verify that the provider list maintained by the PasspointManager after the list is updated
681     * in the data source.
682     *
683     * @throws Exception
684     */
685    @Test
686    public void verifyProvidersAfterDataSourceUpdate() throws Exception {
687        // Update the provider list in the data source.
688        PasspointConfiguration config = createTestConfigWithUserCredential();
689        PasspointProvider provider = createMockProvider(config);
690        List<PasspointProvider> providers = new ArrayList<>();
691        providers.add(provider);
692        mDataSource.setProviders(providers);
693
694        // Verify the providers maintained by PasspointManager.
695        assertEquals(1, mManager.getProviderConfigs().size());
696        assertEquals(config, mManager.getProviderConfigs().get(0));
697    }
698
699    /**
700     * Verify that the provider index used by PasspointManager is updated after it is updated in
701     * the data source.
702     *
703     * @throws Exception
704     */
705    @Test
706    public void verifyProviderIndexAfterDataSourceUpdate() throws Exception {
707        long providerIndex = 9;
708        mDataSource.setProviderIndex(providerIndex);
709        assertEquals(providerIndex, mDataSource.getProviderIndex());
710
711        // Add a provider.
712        PasspointConfiguration config = createTestConfigWithUserCredential();
713        PasspointProvider provider = createMockProvider(config);
714        // Verify the provider ID used to create the new provider.
715        when(mObjectFactory.makePasspointProvider(eq(config), eq(mWifiKeyStore),
716                eq(mSimAccessor), eq(providerIndex))).thenReturn(provider);
717        assertTrue(mManager.addOrUpdateProvider(config));
718        verifyInstalledConfig(config);
719        verify(mWifiConfigManager).saveToStore(true);
720        reset(mWifiConfigManager);
721    }
722
723    /**
724     * Verify that a PasspointProvider with expected PasspointConfiguration will be installed when
725     * adding a legacy Passpoint configuration containing a valid user credential.
726     *
727     * @throws Exception
728     */
729    @Test
730    public void addLegacyPasspointConfigWithUserCredential() throws Exception {
731        // Test data.
732        String fqdn = "test.com";
733        String friendlyName = "Friendly Name";
734        long[] rcOIs = new long[] {0x1234L, 0x2345L};
735        String realm = "realm.com";
736        String username = "username";
737        String password = "password";
738        byte[] base64EncodedPw =
739                Base64.encode(password.getBytes(StandardCharsets.UTF_8), Base64.DEFAULT);
740        String encodedPasswordStr = new String(base64EncodedPw, StandardCharsets.UTF_8);
741        String caCertificateAlias = "CaCert";
742
743        // Setup WifiConfiguration for legacy Passpoint configuraiton.
744        WifiConfiguration wifiConfig = new WifiConfiguration();
745        wifiConfig.FQDN = fqdn;
746        wifiConfig.providerFriendlyName = friendlyName;
747        wifiConfig.roamingConsortiumIds = rcOIs;
748        wifiConfig.enterpriseConfig.setIdentity(username);
749        wifiConfig.enterpriseConfig.setPassword(password);
750        wifiConfig.enterpriseConfig.setRealm(realm);
751        wifiConfig.enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.TTLS);
752        wifiConfig.enterpriseConfig.setPhase2Method(WifiEnterpriseConfig.Phase2.PAP);
753        wifiConfig.enterpriseConfig.setCaCertificateAlias(caCertificateAlias);
754
755        // Setup expected {@link PasspointConfiguration}
756        PasspointConfiguration passpointConfig = new PasspointConfiguration();
757        HomeSp homeSp = new HomeSp();
758        homeSp.setFqdn(fqdn);
759        homeSp.setFriendlyName(friendlyName);
760        homeSp.setRoamingConsortiumOis(rcOIs);
761        passpointConfig.setHomeSp(homeSp);
762        Credential credential = new Credential();
763        Credential.UserCredential userCredential = new Credential.UserCredential();
764        userCredential.setUsername(username);
765        userCredential.setPassword(encodedPasswordStr);
766        userCredential.setEapType(EAPConstants.EAP_TTLS);
767        userCredential.setNonEapInnerMethod("PAP");
768        credential.setUserCredential(userCredential);
769        credential.setRealm(realm);
770        passpointConfig.setCredential(credential);
771
772        assertTrue(PasspointManager.addLegacyPasspointConfig(wifiConfig));
773        verifyInstalledConfig(passpointConfig);
774    }
775
776    /**
777     * Verify that adding a legacy Passpoint configuration containing user credential will
778     * fail when client certificate is not provided.
779     *
780     * @throws Exception
781     */
782    @Test
783    public void addLegacyPasspointConfigWithUserCredentialWithoutCaCert() throws Exception {
784        // Test data.
785        String fqdn = "test.com";
786        String friendlyName = "Friendly Name";
787        long[] rcOIs = new long[] {0x1234L, 0x2345L};
788        String realm = "realm.com";
789        String username = "username";
790        String password = "password";
791        byte[] base64EncodedPw =
792                Base64.encode(password.getBytes(StandardCharsets.UTF_8), Base64.DEFAULT);
793        String encodedPasswordStr = new String(base64EncodedPw, StandardCharsets.UTF_8);
794
795        // Setup WifiConfiguration for legacy Passpoint configuraiton.
796        WifiConfiguration wifiConfig = new WifiConfiguration();
797        wifiConfig.FQDN = fqdn;
798        wifiConfig.providerFriendlyName = friendlyName;
799        wifiConfig.roamingConsortiumIds = rcOIs;
800        wifiConfig.enterpriseConfig.setIdentity(username);
801        wifiConfig.enterpriseConfig.setPassword(password);
802        wifiConfig.enterpriseConfig.setRealm(realm);
803        wifiConfig.enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.TTLS);
804        wifiConfig.enterpriseConfig.setPhase2Method(WifiEnterpriseConfig.Phase2.PAP);
805
806        assertFalse(PasspointManager.addLegacyPasspointConfig(wifiConfig));
807    }
808
809    /**
810     * Verify that a PasspointProvider with expected PasspointConfiguration will be installed when
811     * adding a legacy Passpoint configuration containing a valid SIM credential.
812     *
813     * @throws Exception
814     */
815    @Test
816    public void addLegacyPasspointConfigWithSimCredential() throws Exception {
817        // Test data.
818        String fqdn = "test.com";
819        String friendlyName = "Friendly Name";
820        long[] rcOIs = new long[] {0x1234L, 0x2345L};
821        String realm = "realm.com";
822        String imsi = "1234";
823
824        // Setup WifiConfiguration for legacy Passpoint configuraiton.
825        WifiConfiguration wifiConfig = new WifiConfiguration();
826        wifiConfig.FQDN = fqdn;
827        wifiConfig.providerFriendlyName = friendlyName;
828        wifiConfig.roamingConsortiumIds = rcOIs;
829        wifiConfig.enterpriseConfig.setRealm(realm);
830        wifiConfig.enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.SIM);
831        wifiConfig.enterpriseConfig.setPlmn(imsi);
832
833        // Setup expected {@link PasspointConfiguration}
834        PasspointConfiguration passpointConfig = new PasspointConfiguration();
835        HomeSp homeSp = new HomeSp();
836        homeSp.setFqdn(fqdn);
837        homeSp.setFriendlyName(friendlyName);
838        homeSp.setRoamingConsortiumOis(rcOIs);
839        passpointConfig.setHomeSp(homeSp);
840        Credential credential = new Credential();
841        Credential.SimCredential simCredential = new Credential.SimCredential();
842        simCredential.setEapType(EAPConstants.EAP_SIM);
843        simCredential.setImsi(imsi);
844        credential.setSimCredential(simCredential);
845        credential.setRealm(realm);
846        passpointConfig.setCredential(credential);
847
848        assertTrue(PasspointManager.addLegacyPasspointConfig(wifiConfig));
849        verifyInstalledConfig(passpointConfig);
850    }
851
852    /**
853     * Verify that a PasspointProvider with expected PasspointConfiguration will be installed when
854     * adding a legacy Passpoint configuration containing a valid certificate credential.
855     *
856     * @throws Exception
857     */
858    @Test
859    public void addLegacyPasspointConfigWithCertCredential() throws Exception {
860        // Test data.
861        String fqdn = "test.com";
862        String friendlyName = "Friendly Name";
863        long[] rcOIs = new long[] {0x1234L, 0x2345L};
864        String realm = "realm.com";
865        String caCertificateAlias = "CaCert";
866        String clientCertificateAlias = "ClientCert";
867
868        // Setup WifiConfiguration for legacy Passpoint configuraiton.
869        WifiConfiguration wifiConfig = new WifiConfiguration();
870        wifiConfig.FQDN = fqdn;
871        wifiConfig.providerFriendlyName = friendlyName;
872        wifiConfig.roamingConsortiumIds = rcOIs;
873        wifiConfig.enterpriseConfig.setRealm(realm);
874        wifiConfig.enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.TLS);
875        wifiConfig.enterpriseConfig.setCaCertificateAlias(caCertificateAlias);
876        wifiConfig.enterpriseConfig.setClientCertificateAlias(clientCertificateAlias);
877
878        // Setup expected {@link PasspointConfiguration}
879        PasspointConfiguration passpointConfig = new PasspointConfiguration();
880        HomeSp homeSp = new HomeSp();
881        homeSp.setFqdn(fqdn);
882        homeSp.setFriendlyName(friendlyName);
883        homeSp.setRoamingConsortiumOis(rcOIs);
884        passpointConfig.setHomeSp(homeSp);
885        Credential credential = new Credential();
886        Credential.CertificateCredential certCredential = new Credential.CertificateCredential();
887        certCredential.setCertType(Credential.CertificateCredential.CERT_TYPE_X509V3);
888        credential.setCertCredential(certCredential);
889        credential.setRealm(realm);
890        passpointConfig.setCredential(credential);
891
892        assertTrue(PasspointManager.addLegacyPasspointConfig(wifiConfig));
893        verifyInstalledConfig(passpointConfig);
894    }
895
896    /**
897     * Verify that adding a legacy Passpoint configuration containing certificate credential will
898     * fail when CA certificate is not provided.
899     *
900     * @throws Exception
901     */
902    @Test
903    public void addLegacyPasspointConfigWithCertCredentialWithoutCaCert() throws Exception {
904        // Test data.
905        String fqdn = "test.com";
906        String friendlyName = "Friendly Name";
907        long[] rcOIs = new long[] {0x1234L, 0x2345L};
908        String realm = "realm.com";
909        String clientCertificateAlias = "ClientCert";
910
911        // Setup WifiConfiguration for legacy Passpoint configuraiton.
912        WifiConfiguration wifiConfig = new WifiConfiguration();
913        wifiConfig.FQDN = fqdn;
914        wifiConfig.providerFriendlyName = friendlyName;
915        wifiConfig.roamingConsortiumIds = rcOIs;
916        wifiConfig.enterpriseConfig.setRealm(realm);
917        wifiConfig.enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.TLS);
918        wifiConfig.enterpriseConfig.setClientCertificateAlias(clientCertificateAlias);
919
920        assertFalse(PasspointManager.addLegacyPasspointConfig(wifiConfig));
921    }
922
923    /**
924     * Verify that adding a legacy Passpoint configuration containing certificate credential will
925     * fail when client certificate is not provided.
926     *
927     * @throws Exception
928     */
929    @Test
930    public void addLegacyPasspointConfigWithCertCredentialWithoutClientCert() throws Exception {
931        // Test data.
932        String fqdn = "test.com";
933        String friendlyName = "Friendly Name";
934        long[] rcOIs = new long[] {0x1234L, 0x2345L};
935        String realm = "realm.com";
936        String caCertificateAlias = "CaCert";
937
938        // Setup WifiConfiguration for legacy Passpoint configuraiton.
939        WifiConfiguration wifiConfig = new WifiConfiguration();
940        wifiConfig.FQDN = fqdn;
941        wifiConfig.providerFriendlyName = friendlyName;
942        wifiConfig.roamingConsortiumIds = rcOIs;
943        wifiConfig.enterpriseConfig.setRealm(realm);
944        wifiConfig.enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.TLS);
945        wifiConfig.enterpriseConfig.setCaCertificateAlias(caCertificateAlias);
946
947        assertFalse(PasspointManager.addLegacyPasspointConfig(wifiConfig));
948    }
949}
950