WifiConfigStoreTest.java revision 75a766bae85ee8e94fbaad8fb5214804e7ff97e4
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;
18
19import static org.hamcrest.CoreMatchers.not;
20import static org.mockito.Mockito.anyBoolean;
21import static org.mockito.Mockito.anyInt;
22import static org.mockito.Mockito.anyObject;
23import static org.mockito.Mockito.anyString;
24import static org.mockito.Mockito.doAnswer;
25import static org.mockito.Mockito.eq;
26import static org.mockito.Mockito.intThat;
27import static org.mockito.Mockito.mock;
28import static org.mockito.Mockito.never;
29import static org.mockito.Mockito.verify;
30import static org.mockito.Mockito.when;
31
32import android.content.Context;
33import android.net.wifi.WifiConfiguration;
34import android.os.UserHandle;
35import android.test.AndroidTestCase;
36
37import com.android.server.net.DelayedDiskWrite;
38import com.android.server.wifi.MockAnswerUtil.AnswerWithArguments;
39import com.android.server.wifi.hotspot2.omadm.MOManager;
40import com.android.server.wifi.hotspot2.pps.Credential;
41import com.android.server.wifi.hotspot2.pps.HomeSP;
42
43import org.mockito.Mock;
44import org.mockito.MockitoAnnotations;
45
46import java.io.ByteArrayInputStream;
47import java.io.ByteArrayOutputStream;
48import java.io.DataInputStream;
49import java.io.DataOutputStream;
50import java.io.EOFException;
51import java.io.File;
52import java.io.FileOutputStream;
53import java.lang.reflect.Field;
54import java.math.BigInteger;
55import java.util.ArrayList;
56import java.util.Arrays;
57import java.util.Collection;
58import java.util.HashMap;
59import java.util.HashSet;
60import java.util.List;
61import java.util.Map;
62import java.util.Set;
63
64/**
65 * Unit tests for {@link com.android.server.wifi.WifiConfigStore}.
66 */
67public class WifiConfigStoreTest extends AndroidTestCase {
68    private static final List<WifiConfiguration> CONFIGS = Arrays.asList(
69            WifiConfigurationUtil.generateWifiConfig(
70                    0, 1000000, "\"red\"", true, true, null, null),
71            WifiConfigurationUtil.generateWifiConfig(
72                    1, 1000001, "\"green\"", true, true, "example.com", "Green"),
73            WifiConfigurationUtil.generateWifiConfig(
74                    2, 1100000, "\"blue\"", false, true, "example.org", "Blue"));
75
76    private static final int[] USER_IDS = {0, 10, 11};
77    private static final Map<Integer, List<WifiConfiguration>> VISIBLE_CONFIGS = new HashMap<>();
78    static {
79        for (int userId : USER_IDS) {
80            List<WifiConfiguration> configs = new ArrayList<>();
81            for (int i = 0; i < CONFIGS.size(); ++i) {
82                if (CONFIGS.get(i).isVisibleToUser(userId)) {
83                    configs.add(CONFIGS.get(i));
84                }
85            }
86            VISIBLE_CONFIGS.put(userId, configs);
87        }
88    }
89
90    @Mock private Context mContext;
91    @Mock private WifiStateMachine mWifiStateMachine;
92    @Mock private WifiNative mWifiNative;
93    @Mock private FrameworkFacade mFrameworkFacade;
94    @Mock private DelayedDiskWrite mWriter;
95    @Mock private MOManager mMOManager;
96    private WifiConfigStore mConfigStore;
97    private ConfigurationMap mConfiguredNetworks;
98    public byte[] mNetworkHistory;
99
100    @Override
101    public void setUp() throws Exception {
102        MockitoAnnotations.initMocks(this);
103
104        final Context realContext = getContext();
105        when(mContext.getPackageName()).thenReturn(realContext.getPackageName());
106        when(mContext.getResources()).thenReturn(realContext.getResources());
107        when(mContext.getPackageManager()).thenReturn(realContext.getPackageManager());
108
109        when(mWifiStateMachine.getCurrentUserId()).thenReturn(UserHandle.USER_SYSTEM);
110
111        mConfigStore = new WifiConfigStore(mContext, mWifiStateMachine, mWifiNative,
112                mFrameworkFacade);
113
114        final Field configuredNetworksField =
115                WifiConfigStore.class.getDeclaredField("mConfiguredNetworks");
116        configuredNetworksField.setAccessible(true);
117        mConfiguredNetworks = (ConfigurationMap) configuredNetworksField.get(mConfigStore);
118
119        // Intercept writes to networkHistory.txt.
120        doAnswer(new AnswerWithArguments<Void>() {
121            public void answer(String filePath, DelayedDiskWrite.Writer writer) throws Exception {
122                final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
123                final DataOutputStream stream = new DataOutputStream(buffer);
124                writer.onWriteCalled(stream);
125                stream.close();
126                mNetworkHistory = buffer.toByteArray();
127            }}).when(mWriter).write(anyString(), (DelayedDiskWrite.Writer) anyObject());
128        final Field writerField = WifiConfigStore.class.getSuperclass().getDeclaredField("mWriter");
129        writerField.setAccessible(true);
130        writerField.set(mConfigStore, mWriter);
131
132        when(mMOManager.isEnabled()).thenReturn(true);
133        final Field moManagerField = WifiConfigStore.class.getDeclaredField("mMOManager");
134        moManagerField.setAccessible(true);
135        moManagerField.set(mConfigStore, mMOManager);
136    }
137
138    private void switchUser(int newUserId) {
139        when(mWifiStateMachine.getCurrentUserId()).thenReturn(newUserId);
140        mConfigStore.handleUserSwitch();
141    }
142
143    private void switchUserToCreatorOf(WifiConfiguration config) {
144        switchUser(UserHandle.getUserId(config.creatorUid));
145    }
146
147    private void addNetworks() throws Exception {
148        final int originalUserId = mWifiStateMachine.getCurrentUserId();
149
150        when(mWifiNative.setNetworkVariable(anyInt(), anyString(), anyString())).thenReturn(true);
151        when(mWifiNative.setNetworkExtra(anyInt(), anyString(), (Map<String, String>) anyObject()))
152                .thenReturn(true);
153        for (int i = 0; i < CONFIGS.size(); ++i) {
154            assertEquals(i, CONFIGS.get(i).networkId);
155            switchUserToCreatorOf(CONFIGS.get(i));
156            final WifiConfiguration config = new WifiConfiguration(CONFIGS.get(i));
157            config.networkId = -1;
158            when(mWifiNative.addNetwork()).thenReturn(i);
159            when(mWifiNative.getNetworkVariable(i, WifiConfiguration.ssidVarName))
160                .thenReturn(encodeConfigSSID(CONFIGS.get(i)));
161            mConfigStore.saveNetwork(config, config.creatorUid);
162        }
163
164        switchUser(originalUserId);
165    }
166
167    private String encodeConfigSSID(WifiConfiguration config) throws Exception {
168        return new BigInteger(1, config.SSID.substring(1, config.SSID.length() - 1)
169                .getBytes("UTF-8")).toString(16);
170    }
171
172    private WifiNative createNewWifiNativeMock() throws Exception {
173        final WifiNative wifiNative = mock(WifiNative.class);
174        final Field wifiNativeField = WifiConfigStore.class.getDeclaredField("mWifiNative");
175        wifiNativeField.setAccessible(true);
176        wifiNativeField.set(mConfigStore, wifiNative);
177        return wifiNative;
178    }
179
180    /**
181     * Verifies that getConfiguredNetworksSize() returns the number of network configurations
182     * visible to the current user.
183     */
184    public void testGetConfiguredNetworksSize() throws Exception {
185        addNetworks();
186        for (Map.Entry<Integer, List<WifiConfiguration>> entry : VISIBLE_CONFIGS.entrySet()) {
187            switchUser(entry.getKey());
188            assertEquals(entry.getValue().size(), mConfigStore.getConfiguredNetworksSize());
189        }
190    }
191
192    private void verifyNetworkConfig(WifiConfiguration expectedConfig,
193            WifiConfiguration actualConfig) {
194        assertNotNull(actualConfig);
195        assertEquals(expectedConfig.SSID, actualConfig.SSID);
196        assertEquals(expectedConfig.FQDN, actualConfig.FQDN);
197        assertEquals(expectedConfig.providerFriendlyName,
198                actualConfig.providerFriendlyName);
199        assertEquals(expectedConfig.configKey(), actualConfig.configKey(false));
200    }
201
202    private void verifyNetworkConfigs(Collection<WifiConfiguration> expectedConfigs,
203            Collection<WifiConfiguration> actualConfigs) {
204        assertEquals(expectedConfigs.size(), actualConfigs.size());
205        for (WifiConfiguration expectedConfig : expectedConfigs) {
206            WifiConfiguration actualConfig = null;
207            // Find the network configuration to test (assume that |actualConfigs| contains them in
208            // undefined order).
209            for (final WifiConfiguration candidate : actualConfigs) {
210                if (candidate.networkId == expectedConfig.networkId) {
211                    actualConfig = candidate;
212                    break;
213                }
214            }
215            verifyNetworkConfig(expectedConfig, actualConfig);
216        }
217    }
218
219    /**
220     * Verifies that getConfiguredNetworksSize() returns the network configurations visible to the
221     * current user.
222     */
223    public void testGetConfiguredNetworks() throws Exception {
224        addNetworks();
225        for (Map.Entry<Integer, List<WifiConfiguration>> entry : VISIBLE_CONFIGS.entrySet()) {
226            switchUser(entry.getKey());
227            verifyNetworkConfigs(entry.getValue(), mConfigStore.getConfiguredNetworks());
228        }
229    }
230
231    /**
232     * Verifies that getPrivilegedConfiguredNetworks() returns the network configurations visible to
233     * the current user.
234     */
235    public void testGetPrivilegedConfiguredNetworks() throws Exception {
236        addNetworks();
237        for (Map.Entry<Integer, List<WifiConfiguration>> entry : VISIBLE_CONFIGS.entrySet()) {
238            switchUser(entry.getKey());
239            verifyNetworkConfigs(entry.getValue(), mConfigStore.getPrivilegedConfiguredNetworks());
240        }
241    }
242
243    /**
244     * Verifies that getWifiConfiguration(int netId) can be used to access network configurations
245     * visible to the current user only.
246     */
247    public void testGetWifiConfigurationByNetworkId() throws Exception {
248        addNetworks();
249        for (int userId : USER_IDS) {
250            switchUser(userId);
251            for (WifiConfiguration expectedConfig: CONFIGS) {
252                final WifiConfiguration actualConfig =
253                        mConfigStore.getWifiConfiguration(expectedConfig.networkId);
254                if (expectedConfig.isVisibleToUser(userId)) {
255                    verifyNetworkConfig(expectedConfig, actualConfig);
256                } else {
257                    assertNull(actualConfig);
258                }
259            }
260        }
261    }
262
263    /**
264     * Verifies that getWifiConfiguration(String key) can be used to access network configurations
265     * visible to the current user only.
266     */
267    public void testGetWifiConfigurationByConfigKey() throws Exception {
268        addNetworks();
269        for (int userId : USER_IDS) {
270            switchUser(userId);
271            for (WifiConfiguration expectedConfig: CONFIGS) {
272                final WifiConfiguration actualConfig =
273                        mConfigStore.getWifiConfiguration(expectedConfig.configKey());
274                if (expectedConfig.isVisibleToUser(userId)) {
275                    verifyNetworkConfig(expectedConfig, actualConfig);
276                } else {
277                    assertNull(actualConfig);
278                }
279            }
280        }
281    }
282
283    /**
284     * Verifies that enableAllNetworks() enables all temporarily disabled network configurations
285     * visible to the current user.
286     */
287    public void testEnableAllNetworks() throws Exception {
288        addNetworks();
289        when(mWifiNative.enableNetwork(anyInt(), anyBoolean())).thenReturn(true);
290        for (int userId : USER_IDS) {
291            switchUser(userId);
292
293            for (WifiConfiguration config : mConfiguredNetworks.valuesForAllUsers()) {
294                final WifiConfiguration.NetworkSelectionStatus status =
295                        config.getNetworkSelectionStatus();
296                status.setNetworkSelectionStatus(WifiConfiguration.NetworkSelectionStatus
297                        .NETWORK_SELECTION_TEMPORARY_DISABLED);
298                status.setNetworkSelectionDisableReason(
299                        WifiConfiguration.NetworkSelectionStatus.DISABLED_AUTHENTICATION_FAILURE);
300                status.setDisableTime(System.currentTimeMillis() - 60 * 60 * 1000);
301            }
302
303            mConfigStore.enableAllNetworks();
304
305            for (WifiConfiguration config : mConfiguredNetworks.valuesForAllUsers()) {
306                assertEquals(config.isVisibleToUser(userId),
307                        config.getNetworkSelectionStatus().isNetworkEnabled());
308            }
309        }
310    }
311
312    /**
313     * Verifies that selectNetwork() disables all network configurations visible to the current user
314     * except the selected one.
315     */
316    public void testSelectNetwork() throws Exception {
317        addNetworks();
318
319        for (int userId : USER_IDS) {
320            switchUser(userId);
321
322            for (WifiConfiguration config : mConfiguredNetworks.valuesForAllUsers()) {
323                // Enable all network configurations.
324                for (WifiConfiguration config2 : mConfiguredNetworks.valuesForAllUsers()) {
325                    config2.status = WifiConfiguration.Status.ENABLED;
326                }
327
328                // Try to select a network configuration.
329                final WifiNative wifiNative = createNewWifiNativeMock();
330                final boolean success =
331                        mConfigStore.selectNetwork(config, false, config.creatorUid);
332                if (!config.isVisibleToUser(userId)) {
333                    // If the network configuration is not visible to the current user, verify that
334                    // nothing changed.
335                    assertFalse(success);
336                    verify(wifiNative, never()).selectNetwork(anyInt());
337                    verify(wifiNative, never()).enableNetwork(anyInt(), anyBoolean());
338                    for (WifiConfiguration config2 : mConfiguredNetworks.valuesForAllUsers()) {
339                        assertEquals(WifiConfiguration.Status.ENABLED, config2.status);
340                    }
341                } else {
342                    // If the network configuration is visible to the current user, verify that it
343                    // was enabled and all other network configurations visible to the user were
344                    // disabled.
345                    assertTrue(success);
346                    verify(wifiNative).selectNetwork(config.networkId);
347                    verify(wifiNative, never()).selectNetwork(intThat(not(config.networkId)));
348                    verify(wifiNative).enableNetwork(config.networkId, true);
349                    verify(wifiNative, never()).enableNetwork(config.networkId, false);
350                    verify(wifiNative, never()).enableNetwork(intThat(not(config.networkId)),
351                            anyBoolean());
352                    for (WifiConfiguration config2 : mConfiguredNetworks.valuesForAllUsers()) {
353                        if (config2.isVisibleToUser(userId)
354                                && config2.networkId != config.networkId) {
355                            assertEquals(WifiConfiguration.Status.DISABLED, config2.status);
356                        } else {
357                            assertEquals(WifiConfiguration.Status.ENABLED, config2.status);
358                        }
359                    }
360                }
361            }
362        }
363    }
364
365    /**
366     * Verifies that saveNetwork() correctly stores a network configuration in wpa_supplicant
367     * variables and the networkHistory.txt file.
368     * TODO: Test all variables. Currently, only the following variables are tested:
369     * - In the wpa_supplicant: "ssid", "id_str"
370     * - In networkHistory.txt: "CONFIG", "CREATOR_UID_KEY", "SHARED"
371     */
372    private void verifySaveNetwork(int network) throws Exception {
373        // Switch to the correct user.
374        switchUserToCreatorOf(CONFIGS.get(network));
375
376        // Set up wpa_supplicant.
377        when(mWifiNative.addNetwork()).thenReturn(0);
378        when(mWifiNative.setNetworkVariable(eq(network), anyString(), anyString()))
379                .thenReturn(true);
380        when(mWifiNative.setNetworkExtra(eq(network), anyString(),
381                (Map<String, String>) anyObject())).thenReturn(true);
382        when(mWifiNative.getNetworkVariable(network, WifiConfiguration.ssidVarName))
383                .thenReturn(encodeConfigSSID(CONFIGS.get(network)));
384
385        // Store a network configuration.
386        mConfigStore.saveNetwork(CONFIGS.get(network), CONFIGS.get(network).creatorUid);
387
388        // Verify that wpa_supplicant variables were written correctly for the network
389        // configuration.
390        final Map<String, String> metadata = new HashMap<String, String>();
391        if (CONFIGS.get(network).FQDN != null) {
392            metadata.put(WifiConfigStore.ID_STRING_KEY_FQDN, CONFIGS.get(network).FQDN);
393        }
394        metadata.put(WifiConfigStore.ID_STRING_KEY_CONFIG_KEY, CONFIGS.get(network).configKey());
395        metadata.put(WifiConfigStore.ID_STRING_KEY_CREATOR_UID,
396                Integer.toString(CONFIGS.get(network).creatorUid));
397        verify(mWifiNative).setNetworkExtra(network, WifiConfigStore.ID_STRING_VAR_NAME,
398                metadata);
399
400        // Verify that no wpa_supplicant variables were read or written for any other network
401        // configurations.
402        verify(mWifiNative, never()).setNetworkExtra(intThat(not(network)), anyString(),
403                (Map<String, String>) anyObject());
404        verify(mWifiNative, never()).setNetworkVariable(intThat(not(network)), anyString(),
405                anyString());
406        verify(mWifiNative, never()).getNetworkVariable(intThat(not(network)), anyString());
407
408        // Parse networkHistory.txt.
409        assertNotNull(mNetworkHistory);
410        final DataInputStream stream =
411                new DataInputStream(new ByteArrayInputStream(mNetworkHistory));
412        List<String> keys = new ArrayList<>();
413        List<String> values = new ArrayList<>();
414        try {
415            while (true) {
416                final String[] tokens = stream.readUTF().split(":", 2);
417                if (tokens.length == 2) {
418                    keys.add(tokens[0].trim());
419                    values.add(tokens[1].trim());
420                }
421            }
422        } catch (EOFException e) {
423            // Ignore. This is expected.
424        }
425
426        // Verify that a networkHistory.txt entry was written correctly for the network
427        // configuration.
428        assertTrue(keys.size() >= 3);
429        assertEquals(WifiConfigStore.CONFIG_KEY, keys.get(0));
430        assertEquals(CONFIGS.get(network).configKey(), values.get(0));
431        final int creatorUidIndex = keys.indexOf(WifiConfigStore.CREATOR_UID_KEY);
432        assertTrue(creatorUidIndex != -1);
433        assertEquals(Integer.toString(CONFIGS.get(network).creatorUid),
434                values.get(creatorUidIndex));
435        final int sharedIndex = keys.indexOf(WifiConfigStore.SHARED_KEY);
436        assertTrue(sharedIndex != -1);
437        assertEquals(Boolean.toString(CONFIGS.get(network).shared), values.get(sharedIndex));
438
439        // Verify that no networkHistory.txt entries were written for any other network
440        // configurations.
441        final int lastConfigIndex = keys.lastIndexOf(WifiConfigStore.CONFIG_KEY);
442        assertEquals(0, lastConfigIndex);
443    }
444
445    /**
446     * Verifies that saveNetwork() correctly stores a regular network configuration.
447     */
448    public void testSaveNetworkRegular() throws Exception {
449        verifySaveNetwork(0);
450    }
451
452    /**
453     * Verifies that saveNetwork() correctly stores a HotSpot 2.0 network configuration.
454     */
455    public void testSaveNetworkHotspot20() throws Exception {
456        verifySaveNetwork(1);
457    }
458
459    /**
460     * Verifies that saveNetwork() correctly stores a private network configuration.
461     */
462    public void testSaveNetworkPrivate() throws Exception {
463        verifySaveNetwork(2);
464    }
465
466    /**
467     * Verifies that loadConfiguredNetworks() correctly reads data from the wpa_supplicant, the
468     * networkHistory.txt file and the MOManager, correlating the three sources based on the
469     * configKey and the FQDN for HotSpot 2.0 networks.
470     * TODO: Test all variables. Currently, only the following variables are tested:
471     * - In the wpa_supplicant: "ssid", "id_str"
472     * - In networkHistory.txt: "CONFIG", "CREATOR_UID_KEY", "SHARED"
473     */
474    public void testLoadConfiguredNetworks() throws Exception {
475        // Set up list of network configurations returned by wpa_supplicant.
476        final String header = "network id / ssid / bssid / flags";
477        String networks = header;
478        for (WifiConfiguration config : CONFIGS) {
479            networks += "\n" + Integer.toString(config.networkId) + "\t" + config.SSID + "\tany";
480        }
481        when(mWifiNative.listNetworks(anyInt())).thenReturn(header);
482        when(mWifiNative.listNetworks(-1)).thenReturn(networks);
483
484        // Set up variables returned by wpa_supplicant for the individual network configurations.
485        for (int i = 0; i < CONFIGS.size(); ++i) {
486            when(mWifiNative.getNetworkVariable(i, WifiConfiguration.ssidVarName))
487                .thenReturn(encodeConfigSSID(CONFIGS.get(i)));
488        }
489        // Legacy regular network configuration: No "id_str".
490        when(mWifiNative.getNetworkExtra(0, WifiConfigStore.ID_STRING_VAR_NAME))
491            .thenReturn(null);
492        // Legacy Hotspot 2.0 network configuration: Quoted FQDN in "id_str".
493        when(mWifiNative.getNetworkExtra(1, WifiConfigStore.ID_STRING_VAR_NAME))
494            .thenReturn(null);
495        when(mWifiNative.getNetworkVariable(1, WifiConfigStore.ID_STRING_VAR_NAME))
496            .thenReturn('"' + CONFIGS.get(1).FQDN + '"');
497        // Up-to-date configuration: Metadata in "id_str".
498        final Map<String, String> metadata = new HashMap<String, String>();
499        metadata.put(WifiConfigStore.ID_STRING_KEY_CONFIG_KEY, CONFIGS.get(2).configKey());
500        metadata.put(WifiConfigStore.ID_STRING_KEY_CREATOR_UID,
501                Integer.toString(CONFIGS.get(2).creatorUid));
502        metadata.put(WifiConfigStore.ID_STRING_KEY_FQDN, CONFIGS.get(2).FQDN);
503        when(mWifiNative.getNetworkExtra(2, WifiConfigStore.ID_STRING_VAR_NAME))
504            .thenReturn(metadata);
505
506        // Set up networkHistory.txt file.
507        final File file = File.createTempFile("networkHistory.txt", null);
508        file.deleteOnExit();
509        Field wifiConfigStoreNetworkHistoryConfigFile =
510                WifiConfigStore.class.getDeclaredField("networkHistoryConfigFile");
511        wifiConfigStoreNetworkHistoryConfigFile.setAccessible(true);
512        wifiConfigStoreNetworkHistoryConfigFile.set(null, file.getAbsolutePath());
513        final DataOutputStream stream = new DataOutputStream(new FileOutputStream(file));
514        for (WifiConfiguration config : CONFIGS) {
515            stream.writeUTF(WifiConfigStore.CONFIG_KEY + ":  " + config.configKey() + '\n');
516            stream.writeUTF(WifiConfigStore.CREATOR_UID_KEY + ":  "
517                    + Integer.toString(config.creatorUid) + '\n');
518            stream.writeUTF(WifiConfigStore.SHARED_KEY + ":  "
519                    + Boolean.toString(config.shared) + '\n');
520        }
521        stream.close();
522
523        // Set up list of home service providers returned by MOManager.
524        final List<HomeSP> homeSPs = new ArrayList<HomeSP>();
525        for (WifiConfiguration config : CONFIGS) {
526            if (config.FQDN != null) {
527                homeSPs.add(new HomeSP(null, config.FQDN, new HashSet<Long>(),
528                        new HashSet<String>(), new HashSet<Long>(), new ArrayList<Long>(),
529                        config.providerFriendlyName, null, new Credential(0, 0, null, false, null,
530                        null), null, 0, null, null, null));
531            }
532        }
533        when(mMOManager.loadAllSPs()).thenReturn(homeSPs);
534
535        // Load network configurations.
536        mConfigStore.loadConfiguredNetworks();
537
538        // Verify that network configurations were loaded and correlated correctly across the three
539        // sources.
540        verifyNetworkConfigs(CONFIGS, mConfiguredNetworks.valuesForAllUsers());
541    }
542
543    /**
544     * Verifies that loadConfiguredNetworks() correctly handles duplicates when reading network
545     * configurations from the wpa_supplicant: The second configuration overwrites the first.
546     */
547    public void testLoadConfiguredNetworksEliminatesDuplicates() throws Exception {
548        final WifiConfiguration config = new WifiConfiguration(CONFIGS.get(0));
549        config.networkId = 1;
550
551        // Set up list of network configurations returned by wpa_supplicant. The two configurations
552        // are identical except for their network IDs.
553        final String header = "network id / ssid / bssid / flags";
554        final String networks =
555                header + "\n0\t" + config.SSID + "\tany\n1\t" + config.SSID + "\tany";
556        when(mWifiNative.listNetworks(anyInt())).thenReturn(header);
557        when(mWifiNative.listNetworks(-1)).thenReturn(networks);
558
559        // Set up variables returned by wpa_supplicant.
560        when(mWifiNative.getNetworkVariable(anyInt(), eq(WifiConfiguration.ssidVarName)))
561            .thenReturn(encodeConfigSSID(config));
562        final Map<String, String> metadata = new HashMap<String, String>();
563        metadata.put(WifiConfigStore.ID_STRING_KEY_CONFIG_KEY, config.configKey());
564        metadata.put(WifiConfigStore.ID_STRING_KEY_CREATOR_UID,
565                Integer.toString(config.creatorUid));
566        when(mWifiNative.getNetworkExtra(anyInt(), eq(WifiConfigStore.ID_STRING_VAR_NAME)))
567            .thenReturn(metadata);
568
569        // Load network configurations.
570        mConfigStore.loadConfiguredNetworks();
571
572        // Verify that the second network configuration (network ID 1) overwrote the first (network
573        // ID 0).
574        verifyNetworkConfigs(Arrays.asList(config), mConfiguredNetworks.valuesForAllUsers());
575    }
576
577    /**
578     * Verifies that handleUserSwitch() removes ephemeral network configurations, disables network
579     * configurations that should no longer be visible and enables network configurations that
580     * should become visible.
581     */
582    private void verifyHandleUserSwitch(int oldUserId, int newUserId,
583            boolean makeOneConfigEphemeral) throws Exception {
584        addNetworks();
585        switchUser(oldUserId);
586
587        final WifiNative wifiNative = createNewWifiNativeMock();
588        final Field lastSelectedConfigurationField =
589                WifiConfigStore.class.getDeclaredField("lastSelectedConfiguration");
590        lastSelectedConfigurationField.setAccessible(true);
591        WifiConfiguration removedEphemeralConfig = null;
592        final Set<WifiConfiguration> oldUserOnlyConfigs = new HashSet<>();
593        final Set<WifiConfiguration> newUserOnlyConfigs = new HashSet<>();
594        final Set<WifiConfiguration> neitherUserConfigs = new HashSet<>();
595        final Collection<WifiConfiguration> oldConfigs = mConfiguredNetworks.valuesForAllUsers();
596        int expectedNumberOfConfigs = oldConfigs.size();
597        for (WifiConfiguration config : oldConfigs) {
598            if (config.isVisibleToUser(oldUserId)) {
599                config.status = WifiConfiguration.Status.ENABLED;
600                if (config.isVisibleToUser(newUserId)) {
601                    if (makeOneConfigEphemeral && removedEphemeralConfig == null) {
602                        config.ephemeral = true;
603                        lastSelectedConfigurationField.set(mConfigStore, config.configKey());
604                        removedEphemeralConfig = config;
605                    }
606                } else {
607                    oldUserOnlyConfigs.add(config);
608                }
609            } else {
610                config.status = WifiConfiguration.Status.DISABLED;
611                if (config.isVisibleToUser(newUserId)) {
612                    newUserOnlyConfigs.add(config);
613                } else {
614                    neitherUserConfigs.add(config);
615                }
616            }
617        }
618        when(wifiNative.disableNetwork(anyInt())).thenReturn(true);
619
620        switchUser(newUserId);
621        if (makeOneConfigEphemeral) {
622            // Verify that the ephemeral network configuration was removed.
623            assertNotNull(removedEphemeralConfig);
624            assertNull(mConfiguredNetworks.getForAllUsers(removedEphemeralConfig.networkId));
625            assertNull(lastSelectedConfigurationField.get(mConfigStore));
626            verify(wifiNative).removeNetwork(removedEphemeralConfig.networkId);
627            --expectedNumberOfConfigs;
628        } else {
629            assertNull(removedEphemeralConfig);
630        }
631
632        // Verify that the other network configurations were revealed/hidden and enabled/disabled as
633        // appropriate.
634        final Collection<WifiConfiguration> newConfigs = mConfiguredNetworks.valuesForAllUsers();
635        assertEquals(expectedNumberOfConfigs, newConfigs.size());
636        for (WifiConfiguration config : newConfigs) {
637            if (oldUserOnlyConfigs.contains(config)) {
638                verify(wifiNative).disableNetwork(config.networkId);
639                assertEquals(WifiConfiguration.Status.DISABLED, config.status);
640            } else {
641                verify(wifiNative, never()).disableNetwork(config.networkId);
642                if (neitherUserConfigs.contains(config)) {
643                    assertEquals(WifiConfiguration.Status.DISABLED, config.status);
644                } else {
645                    assertEquals(WifiConfiguration.Status.ENABLED, config.status);
646                }
647            }
648        }
649    }
650
651    /**
652     * Verifies that handleUserSwitch() behaves correctly when the user switch removes an ephemeral
653     * network configuration and reveals a private network configuration.
654     */
655    public void testHandleUserSwitchWithEphemeral() throws Exception {
656        verifyHandleUserSwitch(USER_IDS[2], USER_IDS[0], true);
657    }
658
659    /**
660     * Verifies that handleUserSwitch() behaves correctly when the user switch hides a private
661     * network configuration.
662     */
663    public void testHandleUserSwitchWithoutEphemeral() throws Exception {
664        verifyHandleUserSwitch(USER_IDS[0], USER_IDS[2], false);
665    }
666}
667