diff options
author | Bartosz Fabianowski <bartfab@google.com> | 2016-01-22 11:10:41 +0100 |
---|---|---|
committer | Bartosz Fabianowski <bartfab@google.com> | 2016-01-25 16:12:47 +0100 |
commit | bcdabb1fa1894fcca610692ec94459fe623afa74 (patch) | |
tree | ae86a32bcf8140780607d2a1d6453b93d3087187 /tests | |
parent | cb8c60a24d9e89f3b1aa601e76e38e05f8db6fae (diff) |
Implement private network configurations in WifiService
* Make ConfigurationMap aware of the current foreground user. Suffix each
accessor with one of:
- ForCurrentUser() (operates on network configurations visible to the
current user only)
- ForAllUsers() (operates on all network configurations)
* Make WifiConfigStore aware of the current foreground user:
- Whenever the user changes, networks that are no longer visible are
disabled and networks that newly became visible are enabled.
- Account for the fact that the configKey cannot be computed from
information stored in wpa_supplicant.conf alone while loading
network configurations from disk.
- Have all public methods that add/remove/modify network configurations
operate on configurations visible to the current foreground user only.
BUG=25600871
Change-Id: Id1e81dd649ac5253c94e65b6a25e239fc3cf09a4
Diffstat (limited to 'tests')
3 files changed, 726 insertions, 70 deletions
diff --git a/tests/wifitests/src/com/android/server/wifi/ConfigurationMapTest.java b/tests/wifitests/src/com/android/server/wifi/ConfigurationMapTest.java new file mode 100644 index 000000000..917fd77a7 --- /dev/null +++ b/tests/wifitests/src/com/android/server/wifi/ConfigurationMapTest.java @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.server.wifi; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +import android.net.wifi.WifiConfiguration; +import android.os.UserHandle; +import android.test.suitebuilder.annotation.SmallTest; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * Unit tests for {@link com.android.server.wifi.ConfigurationMapTest}. + */ +@SmallTest +public class ConfigurationMapTest { + private static final List<WifiConfiguration> CONFIGS = Arrays.asList( + WifiConfigurationUtil.generateWifiConfig(0, 1000000, "\"red\"", true, true, null, null), + WifiConfigurationUtil.generateWifiConfig( + 1, 1000001, "\"green\"", false, true, null, null), + WifiConfigurationUtil.generateWifiConfig( + 2, 1000002, "\"blue\"", true, false, "example.com", "Blue"), + WifiConfigurationUtil.generateWifiConfig( + 3, 1100000, "\"cyan\"", true, true, null, null), + WifiConfigurationUtil.generateWifiConfig( + 4, 1100001, "\"yellow\"", false, false, null, null), + WifiConfigurationUtil.generateWifiConfig( + 5, 1100002, "\"magenta\"", true, true, "example.org", "Magenta")); + + private int mCurrentUserId = UserHandle.USER_SYSTEM; + private final ConfigurationMap mConfigs = new ConfigurationMap(); + + public void switchUser(int newUserId) { + Set<WifiConfiguration> hiddenConfigurations = new HashSet<>(); + for (WifiConfiguration config : mConfigs.valuesForAllUsers()) { + if (config.isVisibleToUser(mCurrentUserId) && !config.isVisibleToUser(newUserId)) { + hiddenConfigurations.add(config); + } + } + + mCurrentUserId = newUserId; + assertEquals(hiddenConfigurations, new HashSet<>(mConfigs.handleUserSwitch(newUserId))); + } + + public void verifyGetters(List<WifiConfiguration> configs) { + final Set<WifiConfiguration> configsForCurrentUser = new HashSet<>(); + final Set<WifiConfiguration> enabledConfigsForCurrentUser = new HashSet<>(); + final List<WifiConfiguration> configsNotForCurrentUser = new ArrayList<>(); + + // Find out which network configurations should be / should not be visible to the current + // user. Also, check that *ForAllUsers() methods can be used to access all network + // configurations, irrespective of their visibility to the current user. + for (WifiConfiguration config : configs) { + if (config.isVisibleToUser(mCurrentUserId)) { + configsForCurrentUser.add(config); + if (config.status != WifiConfiguration.Status.DISABLED) { + enabledConfigsForCurrentUser.add(config); + } + } else { + configsNotForCurrentUser.add(config); + } + + assertEquals(config, mConfigs.getForAllUsers(config.networkId)); + assertEquals(config, + mConfigs.getByConfigKeyIDForAllUsers(config.configKey().hashCode())); + } + + // Verify that *ForCurrentUser() methods can be used to access network configurations + // visible to the current user. + for (WifiConfiguration config : configsForCurrentUser) { + assertEquals(config, mConfigs.getForCurrentUser(config.networkId)); + if (config.FQDN != null) { + assertEquals(config, mConfigs.getByFQDNForCurrentUser(config.FQDN)); + } + assertEquals(config, mConfigs.getByConfigKeyForCurrentUser(config.configKey())); + final boolean wasEphemeral = config.ephemeral; + config.ephemeral = false; + assertNull(mConfigs.getEphemeralForCurrentUser(config.SSID)); + config.ephemeral = true; + assertEquals(config, mConfigs.getEphemeralForCurrentUser(config.SSID)); + config.ephemeral = wasEphemeral; + } + + // Verify that *ForCurrentUser() methods cannot be used to access network configurations not + // visible to the current user. + for (WifiConfiguration config : configsNotForCurrentUser) { + assertNull(mConfigs.getForCurrentUser(config.networkId)); + if (config.FQDN != null) { + assertNull(mConfigs.getByFQDNForCurrentUser(config.FQDN)); + } + assertNull(mConfigs.getByConfigKeyForCurrentUser(config.configKey())); + final boolean wasEphemeral = config.ephemeral; + config.ephemeral = false; + assertNull(mConfigs.getEphemeralForCurrentUser(config.SSID)); + config.ephemeral = true; + assertNull(mConfigs.getEphemeralForCurrentUser(config.SSID)); + config.ephemeral = wasEphemeral; + } + + // Verify that the methods which refer to more than one network configuration return the + // correct sets of networks. + assertEquals(configs.size(), mConfigs.sizeForAllUsers()); + assertEquals(configsForCurrentUser.size(), mConfigs.sizeForCurrentUser()); + assertEquals(enabledConfigsForCurrentUser, + new HashSet<WifiConfiguration>(mConfigs.getEnabledNetworksForCurrentUser())); + assertEquals(new HashSet<>(configs), + new HashSet<WifiConfiguration>(mConfigs.valuesForAllUsers())); + } + + /** + * Verifies that all getters return the correct network configurations, taking into account the + * current user. Also verifies that handleUserSwitch() returns the list of network + * configurations that are no longer visible. + */ + @Test + public void testGettersAndHandleUserSwitch() { + for (WifiConfiguration config : CONFIGS) { + assertNull(mConfigs.put(config)); + } + + verifyGetters(CONFIGS); + + switchUser(10); + verifyGetters(CONFIGS); + + switchUser(11); + verifyGetters(CONFIGS); + } + + /** + * Verifies put(), remove() and clear(). + */ + @Test + public void testPutRemoveClear() { + final List<WifiConfiguration> configs = new ArrayList<>(); + final WifiConfiguration config1 = CONFIGS.get(0); + + // Verify that there are no network configurations to start with. + switchUser(UserHandle.getUserId(config1.creatorUid)); + verifyGetters(configs); + + // Add |config1|. + assertNull(mConfigs.put(config1)); + // Verify that the getters return |config1|. + configs.add(config1); + verifyGetters(configs); + + // Overwrite |config1| with |config2|. + final WifiConfiguration config2 = CONFIGS.get(1); + config2.networkId = config1.networkId; + assertEquals(config1, mConfigs.put(config2)); + // Verify that the getters return |config2| only. + configs.clear(); + configs.add(config2); + verifyGetters(configs); + + // Add |config3|. + final WifiConfiguration config3 = CONFIGS.get(2); + assertNull(mConfigs.put(config3)); + // Verify that the getters return |config2| and |config3|. + configs.add(config3); + verifyGetters(configs); + + // Remove |config2|. + assertEquals(config2, mConfigs.remove(config2.networkId)); + // Verify that the getters return |config3| only. + configs.remove(config2); + verifyGetters(configs); + + // Clear all network configurations. + mConfigs.clear(); + // Verify that the getters do not return any network configurations. + configs.clear(); + verifyGetters(configs); + } +} diff --git a/tests/wifitests/src/com/android/server/wifi/WifiConfigStoreTest.java b/tests/wifitests/src/com/android/server/wifi/WifiConfigStoreTest.java index 0a9af5e0b..638a726e6 100644 --- a/tests/wifitests/src/com/android/server/wifi/WifiConfigStoreTest.java +++ b/tests/wifitests/src/com/android/server/wifi/WifiConfigStoreTest.java @@ -17,20 +17,25 @@ package com.android.server.wifi; import static org.hamcrest.CoreMatchers.not; +import static org.mockito.Mockito.anyBoolean; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.anyObject; import static org.mockito.Mockito.anyString; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.intThat; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.Context; import android.net.wifi.WifiConfiguration; -import android.net.wifi.WifiEnterpriseConfig; +import android.os.UserHandle; import android.test.AndroidTestCase; +import com.android.server.net.DelayedDiskWrite; +import com.android.server.wifi.MockAnswerUtil.AnswerWithArguments; import com.android.server.wifi.hotspot2.omadm.MOManager; import com.android.server.wifi.hotspot2.pps.Credential; import com.android.server.wifi.hotspot2.pps.HomeSP; @@ -38,31 +43,59 @@ import com.android.server.wifi.hotspot2.pps.HomeSP; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.EOFException; +import java.io.File; +import java.io.FileOutputStream; import java.lang.reflect.Field; +import java.math.BigInteger; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; /** * Unit tests for {@link com.android.server.wifi.WifiConfigStore}. */ public class WifiConfigStoreTest extends AndroidTestCase { - private static final int UID = 10; - private static final String[] SSIDS = {"\"red\"", "\"green\"", "\"blue\""}; - private static final String[] ENCODED_SSIDS = {"726564", "677265656e", "626c7565"}; - private static final String[] FQDNS = {null, "example.com", "example.org"}; - private static final String[] PROVIDER_FRIENDLY_NAMES = {null, "Green", "Blue"}; - private static final String[] CONFIG_KEYS = {"\"red\"NONE", "example.comWPA_EAP", - "example.orgWPA_EAP"}; + private static final List<WifiConfiguration> CONFIGS = Arrays.asList( + WifiConfigurationUtil.generateWifiConfig( + 0, 1000000, "\"red\"", true, true, null, null), + WifiConfigurationUtil.generateWifiConfig( + 1, 1000001, "\"green\"", true, true, "example.com", "Green"), + WifiConfigurationUtil.generateWifiConfig( + 2, 1100000, "\"blue\"", false, true, "example.org", "Blue")); + + private static final int[] USER_IDS = {0, 10, 11}; + private static final Map<Integer, List<WifiConfiguration>> VISIBLE_CONFIGS = new HashMap<>(); + static { + for (int userId : USER_IDS) { + List<WifiConfiguration> configs = new ArrayList<>(); + for (int i = 0; i < CONFIGS.size(); ++i) { + if (CONFIGS.get(i).isVisibleToUser(userId)) { + configs.add(CONFIGS.get(i)); + } + } + VISIBLE_CONFIGS.put(userId, configs); + } + } @Mock private Context mContext; @Mock private WifiStateMachine mWifiStateMachine; @Mock private WifiNative mWifiNative; @Mock private FrameworkFacade mFrameworkFacade; + @Mock private DelayedDiskWrite mWriter; @Mock private MOManager mMOManager; private WifiConfigStore mConfigStore; + private ConfigurationMap mConfiguredNetworks; + public byte[] mNetworkHistory; @Override public void setUp() throws Exception { @@ -71,97 +104,387 @@ public class WifiConfigStoreTest extends AndroidTestCase { final Context realContext = getContext(); when(mContext.getPackageName()).thenReturn(realContext.getPackageName()); when(mContext.getResources()).thenReturn(realContext.getResources()); + when(mContext.getPackageManager()).thenReturn(realContext.getPackageManager()); + + when(mWifiStateMachine.getCurrentUserId()).thenReturn(UserHandle.USER_SYSTEM); mConfigStore = new WifiConfigStore(mContext, mWifiStateMachine, mWifiNative, mFrameworkFacade); + final Field configuredNetworksField = + WifiConfigStore.class.getDeclaredField("mConfiguredNetworks"); + configuredNetworksField.setAccessible(true); + mConfiguredNetworks = (ConfigurationMap) configuredNetworksField.get(mConfigStore); + + // Intercept writes to networkHistory.txt. + doAnswer(new AnswerWithArguments<Void>() { + public void answer(String filePath, DelayedDiskWrite.Writer writer) throws Exception { + final ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + final DataOutputStream stream = new DataOutputStream(buffer); + writer.onWriteCalled(stream); + stream.close(); + mNetworkHistory = buffer.toByteArray(); + }}).when(mWriter).write(anyString(), (DelayedDiskWrite.Writer) anyObject()); + final Field writerField = WifiConfigStore.class.getSuperclass().getDeclaredField("mWriter"); + writerField.setAccessible(true); + writerField.set(mConfigStore, mWriter); + when(mMOManager.isEnabled()).thenReturn(true); final Field moManagerField = WifiConfigStore.class.getDeclaredField("mMOManager"); moManagerField.setAccessible(true); moManagerField.set(mConfigStore, mMOManager); } - private WifiConfiguration generateWifiConfig(int network) { - final WifiConfiguration config = new WifiConfiguration(); - config.SSID = SSIDS[network]; - config.creatorUid = UID; - if (FQDNS[network] != null) { - config.FQDN = FQDNS[network]; - config.providerFriendlyName = PROVIDER_FRIENDLY_NAMES[network]; - config.enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.SIM); + private void switchUser(int newUserId) { + when(mWifiStateMachine.getCurrentUserId()).thenReturn(newUserId); + mConfigStore.handleUserSwitch(); + } + + private void switchUserToCreatorOf(WifiConfiguration config) { + switchUser(UserHandle.getUserId(config.creatorUid)); + } + + private void addNetworks() throws Exception { + final int originalUserId = mWifiStateMachine.getCurrentUserId(); + + when(mWifiNative.setNetworkVariable(anyInt(), anyString(), anyString())).thenReturn(true); + when(mWifiNative.setNetworkExtra(anyInt(), anyString(), (Map<String, String>) anyObject())) + .thenReturn(true); + for (int i = 0; i < CONFIGS.size(); ++i) { + assertEquals(i, CONFIGS.get(i).networkId); + switchUserToCreatorOf(CONFIGS.get(i)); + final WifiConfiguration config = new WifiConfiguration(CONFIGS.get(i)); + config.networkId = -1; + when(mWifiNative.addNetwork()).thenReturn(i); + when(mWifiNative.getNetworkVariable(i, WifiConfiguration.ssidVarName)) + .thenReturn(encodeConfigSSID(CONFIGS.get(i))); + mConfigStore.saveNetwork(config, config.creatorUid); + } + + switchUser(originalUserId); + } + + private String encodeConfigSSID(WifiConfiguration config) throws Exception { + return new BigInteger(1, config.SSID.substring(1, config.SSID.length() - 1) + .getBytes("UTF-8")).toString(16); + } + + private WifiNative createNewWifiNativeMock() throws Exception { + final WifiNative wifiNative = mock(WifiNative.class); + final Field wifiNativeField = WifiConfigStore.class.getDeclaredField("mWifiNative"); + wifiNativeField.setAccessible(true); + wifiNativeField.set(mConfigStore, wifiNative); + return wifiNative; + } + + /** + * Verifies that getConfiguredNetworksSize() returns the number of network configurations + * visible to the current user. + */ + public void testGetConfiguredNetworksSize() throws Exception { + addNetworks(); + for (Map.Entry<Integer, List<WifiConfiguration>> entry : VISIBLE_CONFIGS.entrySet()) { + switchUser(entry.getKey()); + assertEquals(entry.getValue().size(), mConfigStore.getConfiguredNetworksSize()); + } + } + + private void verifyNetworkConfig(WifiConfiguration expectedConfig, + WifiConfiguration actualConfig) { + assertNotNull(actualConfig); + assertEquals(expectedConfig.SSID, actualConfig.SSID); + assertEquals(expectedConfig.FQDN, actualConfig.FQDN); + assertEquals(expectedConfig.providerFriendlyName, + actualConfig.providerFriendlyName); + assertEquals(expectedConfig.configKey(), actualConfig.configKey(false)); + } + + private void verifyNetworkConfigs(Collection<WifiConfiguration> expectedConfigs, + Collection<WifiConfiguration> actualConfigs) { + assertEquals(expectedConfigs.size(), actualConfigs.size()); + for (WifiConfiguration expectedConfig : expectedConfigs) { + WifiConfiguration actualConfig = null; + // Find the network configuration to test (assume that |actualConfigs| contains them in + // undefined order). + for (final WifiConfiguration candidate : actualConfigs) { + if (candidate.networkId == expectedConfig.networkId) { + actualConfig = candidate; + break; + } + } + verifyNetworkConfig(expectedConfig, actualConfig); + } + } + + /** + * Verifies that getConfiguredNetworksSize() returns the network configurations visible to the + * current user. + */ + public void testGetConfiguredNetworks() throws Exception { + addNetworks(); + for (Map.Entry<Integer, List<WifiConfiguration>> entry : VISIBLE_CONFIGS.entrySet()) { + switchUser(entry.getKey()); + verifyNetworkConfigs(entry.getValue(), mConfigStore.getConfiguredNetworks()); + } + } + + /** + * Verifies that getPrivilegedConfiguredNetworks() returns the network configurations visible to + * the current user. + */ + public void testGetPrivilegedConfiguredNetworks() throws Exception { + addNetworks(); + for (Map.Entry<Integer, List<WifiConfiguration>> entry : VISIBLE_CONFIGS.entrySet()) { + switchUser(entry.getKey()); + verifyNetworkConfigs(entry.getValue(), mConfigStore.getPrivilegedConfiguredNetworks()); + } + } + + /** + * Verifies that getWifiConfiguration(int netId) can be used to access network configurations + * visible to the current user only. + */ + public void testGetWifiConfigurationByNetworkId() throws Exception { + addNetworks(); + for (int userId : USER_IDS) { + switchUser(userId); + for (WifiConfiguration expectedConfig: CONFIGS) { + final WifiConfiguration actualConfig = + mConfigStore.getWifiConfiguration(expectedConfig.networkId); + if (expectedConfig.isVisibleToUser(userId)) { + verifyNetworkConfig(expectedConfig, actualConfig); + } else { + assertNull(actualConfig); + } + } + } + } + + /** + * Verifies that getWifiConfiguration(String key) can be used to access network configurations + * visible to the current user only. + */ + public void testGetWifiConfigurationByConfigKey() throws Exception { + addNetworks(); + for (int userId : USER_IDS) { + switchUser(userId); + for (WifiConfiguration expectedConfig: CONFIGS) { + final WifiConfiguration actualConfig = + mConfigStore.getWifiConfiguration(expectedConfig.configKey()); + if (expectedConfig.isVisibleToUser(userId)) { + verifyNetworkConfig(expectedConfig, actualConfig); + } else { + assertNull(actualConfig); + } + } + } + } + + /** + * Verifies that enableAllNetworks() enables all temporarily disabled network configurations + * visible to the current user. + */ + public void testEnableAllNetworks() throws Exception { + addNetworks(); + when(mWifiNative.enableNetwork(anyInt(), anyBoolean())).thenReturn(true); + for (int userId : USER_IDS) { + switchUser(userId); + + for (WifiConfiguration config : mConfiguredNetworks.valuesForAllUsers()) { + final WifiConfiguration.NetworkSelectionStatus status = + config.getNetworkSelectionStatus(); + status.setNetworkSelectionStatus(WifiConfiguration.NetworkSelectionStatus + .NETWORK_SELECTION_TEMPORARY_DISABLED); + status.setNetworkSelectionDisableReason( + WifiConfiguration.NetworkSelectionStatus.DISABLED_AUTHENTICATION_FAILURE); + status.setDisableTime(System.currentTimeMillis() - 60 * 60 * 1000); + } + + mConfigStore.enableAllNetworks(); + + for (WifiConfiguration config : mConfiguredNetworks.valuesForAllUsers()) { + assertEquals(config.isVisibleToUser(userId), + config.getNetworkSelectionStatus().isNetworkEnabled()); + } + } + } + + /** + * Verifies that selectNetwork() disables all network configurations visible to the current user + * except the selected one. + */ + public void testSelectNetwork() throws Exception { + addNetworks(); + + for (int userId : USER_IDS) { + switchUser(userId); + + for (WifiConfiguration config : mConfiguredNetworks.valuesForAllUsers()) { + // Enable all network configurations. + for (WifiConfiguration config2 : mConfiguredNetworks.valuesForAllUsers()) { + config2.status = WifiConfiguration.Status.ENABLED; + } + + // Try to select a network configuration. + final WifiNative wifiNative = createNewWifiNativeMock(); + final boolean success = + mConfigStore.selectNetwork(config, false, config.creatorUid); + if (!config.isVisibleToUser(userId)) { + // If the network configuration is not visible to the current user, verify that + // nothing changed. + assertFalse(success); + verify(wifiNative, never()).selectNetwork(anyInt()); + verify(wifiNative, never()).enableNetwork(anyInt(), anyBoolean()); + for (WifiConfiguration config2 : mConfiguredNetworks.valuesForAllUsers()) { + assertEquals(WifiConfiguration.Status.ENABLED, config2.status); + } + } else { + // If the network configuration is visible to the current user, verify that it + // was enabled and all other network configurations visible to the user were + // disabled. + assertTrue(success); + verify(wifiNative).selectNetwork(config.networkId); + verify(wifiNative, never()).selectNetwork(intThat(not(config.networkId))); + verify(wifiNative).enableNetwork(config.networkId, true); + verify(wifiNative, never()).enableNetwork(config.networkId, false); + verify(wifiNative, never()).enableNetwork(intThat(not(config.networkId)), + anyBoolean()); + for (WifiConfiguration config2 : mConfiguredNetworks.valuesForAllUsers()) { + if (config2.isVisibleToUser(userId) + && config2.networkId != config.networkId) { + assertEquals(WifiConfiguration.Status.DISABLED, config2.status); + } else { + assertEquals(WifiConfiguration.Status.ENABLED, config2.status); + } + } + } + } } - return config; } /** * Verifies that saveNetwork() correctly stores a network configuration in wpa_supplicant - * variables. - * TODO: Test all variables. Currently, only "ssid" and "id_str" are tested. + * variables and the networkHistory.txt file. + * TODO: Test all variables. Currently, only the following variables are tested: + * - In the wpa_supplicant: "ssid", "id_str" + * - In networkHistory.txt: "CONFIG", "CREATOR_UID_KEY", "SHARED" */ - public void verifySaveNetwork(int network) { + private void verifySaveNetwork(int network) throws Exception { + // Switch to the correct user. + switchUserToCreatorOf(CONFIGS.get(network)); + // Set up wpa_supplicant. when(mWifiNative.addNetwork()).thenReturn(0); - when(mWifiNative.setNetworkVariable(eq(0), anyString(), anyString())).thenReturn(true); - when(mWifiNative.setNetworkExtra(eq(0), anyString(), (Map<String, String>) anyObject())) + when(mWifiNative.setNetworkVariable(eq(network), anyString(), anyString())) .thenReturn(true); - when(mWifiNative.getNetworkVariable(0, WifiConfiguration.ssidVarName)) - .thenReturn(ENCODED_SSIDS[network]); + when(mWifiNative.setNetworkExtra(eq(network), anyString(), + (Map<String, String>) anyObject())).thenReturn(true); + when(mWifiNative.getNetworkVariable(network, WifiConfiguration.ssidVarName)) + .thenReturn(encodeConfigSSID(CONFIGS.get(network))); // Store a network configuration. - mConfigStore.saveNetwork(generateWifiConfig(network), UID); + mConfigStore.saveNetwork(CONFIGS.get(network), CONFIGS.get(network).creatorUid); // Verify that wpa_supplicant variables were written correctly for the network // configuration. final Map<String, String> metadata = new HashMap<String, String>(); - if (FQDNS[network] != null) { - metadata.put(WifiConfigStore.ID_STRING_KEY_FQDN, FQDNS[network]); + if (CONFIGS.get(network).FQDN != null) { + metadata.put(WifiConfigStore.ID_STRING_KEY_FQDN, CONFIGS.get(network).FQDN); } - metadata.put(WifiConfigStore.ID_STRING_KEY_CONFIG_KEY, CONFIG_KEYS[network]); - metadata.put(WifiConfigStore.ID_STRING_KEY_CREATOR_UID, Integer.toString(UID)); - verify(mWifiNative).setNetworkExtra(0, WifiConfigStore.ID_STRING_VAR_NAME, + metadata.put(WifiConfigStore.ID_STRING_KEY_CONFIG_KEY, CONFIGS.get(network).configKey()); + metadata.put(WifiConfigStore.ID_STRING_KEY_CREATOR_UID, + Integer.toString(CONFIGS.get(network).creatorUid)); + verify(mWifiNative).setNetworkExtra(network, WifiConfigStore.ID_STRING_VAR_NAME, metadata); // Verify that no wpa_supplicant variables were read or written for any other network // configurations. - verify(mWifiNative, never()).setNetworkExtra(intThat(not(0)), anyString(), + verify(mWifiNative, never()).setNetworkExtra(intThat(not(network)), anyString(), (Map<String, String>) anyObject()); - verify(mWifiNative, never()).setNetworkVariable(intThat(not(0)), anyString(), anyString()); - verify(mWifiNative, never()).getNetworkVariable(intThat(not(0)), anyString()); + verify(mWifiNative, never()).setNetworkVariable(intThat(not(network)), anyString(), + anyString()); + verify(mWifiNative, never()).getNetworkVariable(intThat(not(network)), anyString()); + + // Parse networkHistory.txt. + assertNotNull(mNetworkHistory); + final DataInputStream stream = + new DataInputStream(new ByteArrayInputStream(mNetworkHistory)); + List<String> keys = new ArrayList<>(); + List<String> values = new ArrayList<>(); + try { + while (true) { + final String[] tokens = stream.readUTF().split(":", 2); + if (tokens.length == 2) { + keys.add(tokens[0].trim()); + values.add(tokens[1].trim()); + } + } + } catch (EOFException e) { + // Ignore. This is expected. + } + + // Verify that a networkHistory.txt entry was written correctly for the network + // configuration. + assertTrue(keys.size() >= 3); + assertEquals(WifiConfigStore.CONFIG_KEY, keys.get(0)); + assertEquals(CONFIGS.get(network).configKey(), values.get(0)); + final int creatorUidIndex = keys.indexOf(WifiConfigStore.CREATOR_UID_KEY); + assertTrue(creatorUidIndex != -1); + assertEquals(Integer.toString(CONFIGS.get(network).creatorUid), + values.get(creatorUidIndex)); + final int sharedIndex = keys.indexOf(WifiConfigStore.SHARED_KEY); + assertTrue(sharedIndex != -1); + assertEquals(Boolean.toString(CONFIGS.get(network).shared), values.get(sharedIndex)); + + // Verify that no networkHistory.txt entries were written for any other network + // configurations. + final int lastConfigIndex = keys.lastIndexOf(WifiConfigStore.CONFIG_KEY); + assertEquals(0, lastConfigIndex); } /** * Verifies that saveNetwork() correctly stores a regular network configuration. */ - public void testSaveNetworkRegular() { + public void testSaveNetworkRegular() throws Exception { verifySaveNetwork(0); } /** * Verifies that saveNetwork() correctly stores a HotSpot 2.0 network configuration. */ - public void testSaveNetworkHotspot20() { + public void testSaveNetworkHotspot20() throws Exception { verifySaveNetwork(1); } /** - * Verifies that loadConfiguredNetworks() correctly reads data from the wpa_supplicant and - * the MOManager, correlating the two sources based on the FQDN for HotSpot 2.0 networks. - * TODO: Test all variables. Currently, only "ssid" and "id_str" are tested. + * Verifies that saveNetwork() correctly stores a private network configuration. + */ + public void testSaveNetworkPrivate() throws Exception { + verifySaveNetwork(2); + } + + /** + * Verifies that loadConfiguredNetworks() correctly reads data from the wpa_supplicant, the + * networkHistory.txt file and the MOManager, correlating the three sources based on the + * configKey and the FQDN for HotSpot 2.0 networks. + * TODO: Test all variables. Currently, only the following variables are tested: + * - In the wpa_supplicant: "ssid", "id_str" + * - In networkHistory.txt: "CONFIG", "CREATOR_UID_KEY", "SHARED" */ public void testLoadConfiguredNetworks() throws Exception { - // Set up list of networks returned by wpa_supplicant. + // Set up list of network configurations returned by wpa_supplicant. final String header = "network id / ssid / bssid / flags"; String networks = header; - for (int i = 0; i < SSIDS.length; ++i) { - networks += "\n" + Integer.toString(i) + "\t" + SSIDS[i] + "\tany"; + for (WifiConfiguration config : CONFIGS) { + networks += "\n" + Integer.toString(config.networkId) + "\t" + config.SSID + "\tany"; } when(mWifiNative.listNetworks(anyInt())).thenReturn(header); when(mWifiNative.listNetworks(-1)).thenReturn(networks); - // Set up variables returned by wpa_supplicant for the individual networks. - for (int i = 0; i < SSIDS.length; ++i) { + // Set up variables returned by wpa_supplicant for the individual network configurations. + for (int i = 0; i < CONFIGS.size(); ++i) { when(mWifiNative.getNetworkVariable(i, WifiConfiguration.ssidVarName)) - .thenReturn(ENCODED_SSIDS[i]); + .thenReturn(encodeConfigSSID(CONFIGS.get(i))); } // Legacy regular network configuration: No "id_str". when(mWifiNative.getNetworkExtra(0, WifiConfigStore.ID_STRING_VAR_NAME)) @@ -170,47 +493,140 @@ public class WifiConfigStoreTest extends AndroidTestCase { when(mWifiNative.getNetworkExtra(1, WifiConfigStore.ID_STRING_VAR_NAME)) .thenReturn(null); when(mWifiNative.getNetworkVariable(1, WifiConfigStore.ID_STRING_VAR_NAME)) - .thenReturn('"' + FQDNS[1] + '"'); + .thenReturn('"' + CONFIGS.get(1).FQDN + '"'); // Up-to-date configuration: Metadata in "id_str". final Map<String, String> metadata = new HashMap<String, String>(); - metadata.put(WifiConfigStore.ID_STRING_KEY_CONFIG_KEY, CONFIG_KEYS[2]); - metadata.put(WifiConfigStore.ID_STRING_KEY_CREATOR_UID, Integer.toString(UID)); - metadata.put(WifiConfigStore.ID_STRING_KEY_FQDN, FQDNS[2]); + metadata.put(WifiConfigStore.ID_STRING_KEY_CONFIG_KEY, CONFIGS.get(2).configKey()); + metadata.put(WifiConfigStore.ID_STRING_KEY_CREATOR_UID, + Integer.toString(CONFIGS.get(2).creatorUid)); + metadata.put(WifiConfigStore.ID_STRING_KEY_FQDN, CONFIGS.get(2).FQDN); when(mWifiNative.getNetworkExtra(2, WifiConfigStore.ID_STRING_VAR_NAME)) .thenReturn(metadata); + // Set up networkHistory.txt file. + final File file = File.createTempFile("networkHistory.txt", null); + file.deleteOnExit(); + Field wifiConfigStoreNetworkHistoryConfigFile = + WifiConfigStore.class.getDeclaredField("networkHistoryConfigFile"); + wifiConfigStoreNetworkHistoryConfigFile.setAccessible(true); + wifiConfigStoreNetworkHistoryConfigFile.set(null, file.getAbsolutePath()); + final DataOutputStream stream = new DataOutputStream(new FileOutputStream(file)); + for (WifiConfiguration config : CONFIGS) { + stream.writeUTF(WifiConfigStore.CONFIG_KEY + ": " + config.configKey() + '\n'); + stream.writeUTF(WifiConfigStore.CREATOR_UID_KEY + ": " + + Integer.toString(config.creatorUid) + '\n'); + stream.writeUTF(WifiConfigStore.SHARED_KEY + ": " + + Boolean.toString(config.shared) + '\n'); + } + stream.close(); + // Set up list of home service providers returned by MOManager. final List<HomeSP> homeSPs = new ArrayList<HomeSP>(); - for (int i : new int[] {1, 2}) { - homeSPs.add(new HomeSP(null, FQDNS[i], new HashSet<Long>(), new HashSet<String>(), - new HashSet<Long>(), new ArrayList<Long>(), PROVIDER_FRIENDLY_NAMES[i], null, - new Credential(0, 0, null, false, null, null), null, 0, null, null, null)); + for (WifiConfiguration config : CONFIGS) { + if (config.FQDN != null) { + homeSPs.add(new HomeSP(null, config.FQDN, new HashSet<Long>(), + new HashSet<String>(), new HashSet<Long>(), new ArrayList<Long>(), + config.providerFriendlyName, null, new Credential(0, 0, null, false, null, + null), null, 0, null, null, null)); + } } when(mMOManager.loadAllSPs()).thenReturn(homeSPs); // Load network configurations. mConfigStore.loadConfiguredNetworks(); - // Verify that network configurations were loaded. For HotSpot 2.0 networks, this also - // verifies that the data read from the wpa_supplicant was correlated with the data read - // from the MOManager based on the FQDN stored in the wpa_supplicant's "id_str" variable. - final List<WifiConfiguration> configs = mConfigStore.getConfiguredNetworks(); - assertEquals(SSIDS.length, configs.size()); - for (int i = 0; i < SSIDS.length; ++i) { - WifiConfiguration config = null; - // Find the network configuration to test (getConfiguredNetworks() returns them in - // undefined order). - for (final WifiConfiguration candidate : configs) { - if (candidate.networkId == i) { - config = candidate; - break; + // Verify that network configurations were loaded and correlated correctly across the three + // sources. + verifyNetworkConfigs(CONFIGS, mConfiguredNetworks.valuesForAllUsers()); + } + + /** + * Verifies that handleUserSwitch() removes ephemeral network configurations, disables network + * configurations that should no longer be visible and enables network configurations that + * should become visible. + */ + private void verifyHandleUserSwitch(int oldUserId, int newUserId, + boolean makeOneConfigEphemeral) throws Exception { + addNetworks(); + switchUser(oldUserId); + + final WifiNative wifiNative = createNewWifiNativeMock(); + final Field lastSelectedConfigurationField = + WifiConfigStore.class.getDeclaredField("lastSelectedConfiguration"); + lastSelectedConfigurationField.setAccessible(true); + WifiConfiguration removedEphemeralConfig = null; + final Set<WifiConfiguration> oldUserOnlyConfigs = new HashSet<>(); + final Set<WifiConfiguration> newUserOnlyConfigs = new HashSet<>(); + final Set<WifiConfiguration> neitherUserConfigs = new HashSet<>(); + final Collection<WifiConfiguration> oldConfigs = mConfiguredNetworks.valuesForAllUsers(); + int expectedNumberOfConfigs = oldConfigs.size(); + for (WifiConfiguration config : oldConfigs) { + if (config.isVisibleToUser(oldUserId)) { + config.status = WifiConfiguration.Status.ENABLED; + if (config.isVisibleToUser(newUserId)) { + if (makeOneConfigEphemeral && removedEphemeralConfig == null) { + config.ephemeral = true; + lastSelectedConfigurationField.set(mConfigStore, config.configKey()); + removedEphemeralConfig = config; + } + } else { + oldUserOnlyConfigs.add(config); + } + } else { + config.status = WifiConfiguration.Status.DISABLED; + if (config.isVisibleToUser(newUserId)) { + newUserOnlyConfigs.add(config); + } else { + neitherUserConfigs.add(config); } } - assertNotNull(config); - assertEquals(SSIDS[i], config.SSID); - assertEquals(FQDNS[i], config.FQDN); - assertEquals(PROVIDER_FRIENDLY_NAMES[i], config.providerFriendlyName); - assertEquals(CONFIG_KEYS[i], config.configKey(false)); } + when(wifiNative.disableNetwork(anyInt())).thenReturn(true); + + switchUser(newUserId); + if (makeOneConfigEphemeral) { + // Verify that the ephemeral network configuration was removed. + assertNotNull(removedEphemeralConfig); + assertNull(mConfiguredNetworks.getForAllUsers(removedEphemeralConfig.networkId)); + assertNull(lastSelectedConfigurationField.get(mConfigStore)); + verify(wifiNative).removeNetwork(removedEphemeralConfig.networkId); + --expectedNumberOfConfigs; + } else { + assertNull(removedEphemeralConfig); + } + + // Verify that the other network configurations were revealed/hidden and enabled/disabled as + // appropriate. + final Collection<WifiConfiguration> newConfigs = mConfiguredNetworks.valuesForAllUsers(); + assertEquals(expectedNumberOfConfigs, newConfigs.size()); + for (WifiConfiguration config : newConfigs) { + if (oldUserOnlyConfigs.contains(config)) { + verify(wifiNative).disableNetwork(config.networkId); + assertEquals(WifiConfiguration.Status.DISABLED, config.status); + } else { + verify(wifiNative, never()).disableNetwork(config.networkId); + if (neitherUserConfigs.contains(config)) { + assertEquals(WifiConfiguration.Status.DISABLED, config.status); + } else { + assertEquals(WifiConfiguration.Status.ENABLED, config.status); + } + } + } + } + + /** + * Verifies that handleUserSwitch() behaves correctly when the user switch removes an ephemeral + * network configuration and reveals a private network configuration. + */ + public void testHandleUserSwitchWithEphemeral() throws Exception { + verifyHandleUserSwitch(USER_IDS[2], USER_IDS[0], true); + } + + /** + * Verifies that handleUserSwitch() behaves correctly when the user switch hides a private + * network configuration. + */ + public void testHandleUserSwitchWithoutEphemeral() throws Exception { + verifyHandleUserSwitch(USER_IDS[0], USER_IDS[2], false); } } diff --git a/tests/wifitests/src/com/android/server/wifi/WifiConfigurationUtil.java b/tests/wifitests/src/com/android/server/wifi/WifiConfigurationUtil.java new file mode 100644 index 000000000..c786741cf --- /dev/null +++ b/tests/wifitests/src/com/android/server/wifi/WifiConfigurationUtil.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.server.wifi; + +import android.net.wifi.WifiConfiguration; +import android.net.wifi.WifiEnterpriseConfig; + +/** + * Helper for creating and populating WifiConfigurations in unit tests. + */ +public class WifiConfigurationUtil { + public static WifiConfiguration generateWifiConfig(int networkId, int uid, String ssid, + boolean shared, boolean enabled, String fqdn, String providerFriendlyName) { + final WifiConfiguration config = new WifiConfiguration(); + config.SSID = ssid; + config.networkId = networkId; + config.creatorUid = uid; + config.shared = shared; + config.status = enabled ? WifiConfiguration.Status.ENABLED + : WifiConfiguration.Status.DISABLED; + if (fqdn != null) { + config.FQDN = fqdn; + config.providerFriendlyName = providerFriendlyName; + config.enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.SIM); + } + return config; + } +} |