1/* 2 * Copyright (C) 2017 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.junit.Assert.*; 20import static org.mockito.Mockito.*; 21 22import android.net.wifi.WifiConfiguration; 23import android.net.wifi.WifiEnterpriseConfig; 24import android.test.suitebuilder.annotation.SmallTest; 25import android.util.Xml; 26 27import com.android.internal.util.FastXmlSerializer; 28import com.android.server.wifi.util.XmlUtilTest; 29 30import org.junit.Before; 31import org.junit.Test; 32import org.xmlpull.v1.XmlPullParser; 33import org.xmlpull.v1.XmlPullParserException; 34import org.xmlpull.v1.XmlSerializer; 35 36import java.io.ByteArrayInputStream; 37import java.io.ByteArrayOutputStream; 38import java.nio.charset.StandardCharsets; 39import java.util.ArrayList; 40import java.util.Arrays; 41import java.util.List; 42 43/** 44 * Unit tests for {@link com.android.server.wifi.NetworksListStoreData}. 45 */ 46@SmallTest 47public class NetworkListStoreDataTest { 48 49 private static final String TEST_SSID = "WifiConfigStoreDataSSID_"; 50 private static final String TEST_CONNECT_CHOICE = "XmlUtilConnectChoice"; 51 private static final long TEST_CONNECT_CHOICE_TIMESTAMP = 0x4566; 52 private static final String SINGLE_OPEN_NETWORK_DATA_XML_STRING_FORMAT = 53 "<Network>\n" 54 + "<WifiConfiguration>\n" 55 + "<string name=\"ConfigKey\">%s</string>\n" 56 + "<string name=\"SSID\">%s</string>\n" 57 + "<null name=\"BSSID\" />\n" 58 + "<null name=\"PreSharedKey\" />\n" 59 + "<null name=\"WEPKeys\" />\n" 60 + "<int name=\"WEPTxKeyIndex\" value=\"0\" />\n" 61 + "<boolean name=\"HiddenSSID\" value=\"false\" />\n" 62 + "<boolean name=\"RequirePMF\" value=\"false\" />\n" 63 + "<byte-array name=\"AllowedKeyMgmt\" num=\"1\">01</byte-array>\n" 64 + "<byte-array name=\"AllowedProtocols\" num=\"0\"></byte-array>\n" 65 + "<byte-array name=\"AllowedAuthAlgos\" num=\"0\"></byte-array>\n" 66 + "<byte-array name=\"AllowedGroupCiphers\" num=\"0\"></byte-array>\n" 67 + "<byte-array name=\"AllowedPairwiseCiphers\" num=\"0\"></byte-array>\n" 68 + "<boolean name=\"Shared\" value=\"%s\" />\n" 69 + "<int name=\"Status\" value=\"2\" />\n" 70 + "<null name=\"FQDN\" />\n" 71 + "<null name=\"ProviderFriendlyName\" />\n" 72 + "<null name=\"LinkedNetworksList\" />\n" 73 + "<null name=\"DefaultGwMacAddress\" />\n" 74 + "<boolean name=\"ValidatedInternetAccess\" value=\"false\" />\n" 75 + "<boolean name=\"NoInternetAccessExpected\" value=\"false\" />\n" 76 + "<int name=\"UserApproved\" value=\"0\" />\n" 77 + "<boolean name=\"MeteredHint\" value=\"false\" />\n" 78 + "<boolean name=\"UseExternalScores\" value=\"false\" />\n" 79 + "<int name=\"NumAssociation\" value=\"0\" />\n" 80 + "<int name=\"CreatorUid\" value=\"%d\" />\n" 81 + "<null name=\"CreatorName\" />\n" 82 + "<null name=\"CreationTime\" />\n" 83 + "<int name=\"LastUpdateUid\" value=\"-1\" />\n" 84 + "<null name=\"LastUpdateName\" />\n" 85 + "<int name=\"LastConnectUid\" value=\"0\" />\n" 86 + "<boolean name=\"IsLegacyPasspointConfig\" value=\"false\" />\n" 87 + "<long-array name=\"RoamingConsortiumOIs\" num=\"0\" />\n" 88 + "</WifiConfiguration>\n" 89 + "<NetworkStatus>\n" 90 + "<string name=\"SelectionStatus\">NETWORK_SELECTION_ENABLED</string>\n" 91 + "<string name=\"DisableReason\">NETWORK_SELECTION_ENABLE</string>\n" 92 + "<null name=\"ConnectChoice\" />\n" 93 + "<long name=\"ConnectChoiceTimeStamp\" value=\"-1\" />\n" 94 + "<boolean name=\"HasEverConnected\" value=\"false\" />\n" 95 + "</NetworkStatus>\n" 96 + "<IpConfiguration>\n" 97 + "<string name=\"IpAssignment\">DHCP</string>\n" 98 + "<string name=\"ProxySettings\">NONE</string>\n" 99 + "</IpConfiguration>\n" 100 + "</Network>\n"; 101 102 private static final String SINGLE_EAP_NETWORK_DATA_XML_STRING_FORMAT = 103 "<Network>\n" 104 + "<WifiConfiguration>\n" 105 + "<string name=\"ConfigKey\">%s</string>\n" 106 + "<string name=\"SSID\">%s</string>\n" 107 + "<null name=\"BSSID\" />\n" 108 + "<null name=\"PreSharedKey\" />\n" 109 + "<null name=\"WEPKeys\" />\n" 110 + "<int name=\"WEPTxKeyIndex\" value=\"0\" />\n" 111 + "<boolean name=\"HiddenSSID\" value=\"false\" />\n" 112 + "<boolean name=\"RequirePMF\" value=\"false\" />\n" 113 + "<byte-array name=\"AllowedKeyMgmt\" num=\"1\">0c</byte-array>\n" 114 + "<byte-array name=\"AllowedProtocols\" num=\"0\"></byte-array>\n" 115 + "<byte-array name=\"AllowedAuthAlgos\" num=\"0\"></byte-array>\n" 116 + "<byte-array name=\"AllowedGroupCiphers\" num=\"0\"></byte-array>\n" 117 + "<byte-array name=\"AllowedPairwiseCiphers\" num=\"0\"></byte-array>\n" 118 + "<boolean name=\"Shared\" value=\"%s\" />\n" 119 + "<int name=\"Status\" value=\"2\" />\n" 120 + "<null name=\"FQDN\" />\n" 121 + "<null name=\"ProviderFriendlyName\" />\n" 122 + "<null name=\"LinkedNetworksList\" />\n" 123 + "<null name=\"DefaultGwMacAddress\" />\n" 124 + "<boolean name=\"ValidatedInternetAccess\" value=\"false\" />\n" 125 + "<boolean name=\"NoInternetAccessExpected\" value=\"false\" />\n" 126 + "<int name=\"UserApproved\" value=\"0\" />\n" 127 + "<boolean name=\"MeteredHint\" value=\"false\" />\n" 128 + "<boolean name=\"UseExternalScores\" value=\"false\" />\n" 129 + "<int name=\"NumAssociation\" value=\"0\" />\n" 130 + "<int name=\"CreatorUid\" value=\"%d\" />\n" 131 + "<null name=\"CreatorName\" />\n" 132 + "<null name=\"CreationTime\" />\n" 133 + "<int name=\"LastUpdateUid\" value=\"-1\" />\n" 134 + "<null name=\"LastUpdateName\" />\n" 135 + "<int name=\"LastConnectUid\" value=\"0\" />\n" 136 + "<boolean name=\"IsLegacyPasspointConfig\" value=\"false\" />\n" 137 + "<long-array name=\"RoamingConsortiumOIs\" num=\"0\" />\n" 138 + "</WifiConfiguration>\n" 139 + "<NetworkStatus>\n" 140 + "<string name=\"SelectionStatus\">NETWORK_SELECTION_ENABLED</string>\n" 141 + "<string name=\"DisableReason\">NETWORK_SELECTION_ENABLE</string>\n" 142 + "<null name=\"ConnectChoice\" />\n" 143 + "<long name=\"ConnectChoiceTimeStamp\" value=\"-1\" />\n" 144 + "<boolean name=\"HasEverConnected\" value=\"false\" />\n" 145 + "</NetworkStatus>\n" 146 + "<IpConfiguration>\n" 147 + "<string name=\"IpAssignment\">DHCP</string>\n" 148 + "<string name=\"ProxySettings\">NONE</string>\n" 149 + "</IpConfiguration>\n" 150 + "<WifiEnterpriseConfiguration>\n" 151 + "<string name=\"Identity\"></string>\n" 152 + "<string name=\"AnonIdentity\"></string>\n" 153 + "<string name=\"Password\"></string>\n" 154 + "<string name=\"ClientCert\"></string>\n" 155 + "<string name=\"CaCert\"></string>\n" 156 + "<string name=\"SubjectMatch\"></string>\n" 157 + "<string name=\"Engine\"></string>\n" 158 + "<string name=\"EngineId\"></string>\n" 159 + "<string name=\"PrivateKeyId\"></string>\n" 160 + "<string name=\"AltSubjectMatch\"></string>\n" 161 + "<string name=\"DomSuffixMatch\"></string>\n" 162 + "<string name=\"CaPath\"></string>\n" 163 + "<int name=\"EapMethod\" value=\"2\" />\n" 164 + "<int name=\"Phase2Method\" value=\"0\" />\n" 165 + "<string name=\"PLMN\"></string>\n" 166 + "<string name=\"Realm\"></string>\n" 167 + "</WifiEnterpriseConfiguration>\n" 168 + "</Network>\n"; 169 170 private NetworkListStoreData mNetworkListStoreData; 171 172 @Before 173 public void setUp() throws Exception { 174 mNetworkListStoreData = new NetworkListStoreData(); 175 } 176 177 /** 178 * Helper function for serializing configuration data to a XML block. 179 * 180 * @param shared Flag indicating serializing shared or user configurations 181 * @return byte[] of the XML data 182 * @throws Exception 183 */ 184 private byte[] serializeData(boolean shared) throws Exception { 185 final XmlSerializer out = new FastXmlSerializer(); 186 final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); 187 out.setOutput(outputStream, StandardCharsets.UTF_8.name()); 188 mNetworkListStoreData.serializeData(out, shared); 189 out.flush(); 190 return outputStream.toByteArray(); 191 } 192 193 /** 194 * Helper function for parsing configuration data from a XML block. 195 * 196 * @param data XML data to parse from 197 * @param shared Flag indicating parsing of shared or user configurations 198 * @return List of WifiConfiguration parsed 199 * @throws Exception 200 */ 201 private List<WifiConfiguration> deserializeData(byte[] data, boolean shared) throws Exception { 202 final XmlPullParser in = Xml.newPullParser(); 203 final ByteArrayInputStream inputStream = new ByteArrayInputStream(data); 204 in.setInput(inputStream, StandardCharsets.UTF_8.name()); 205 mNetworkListStoreData.deserializeData(in, in.getDepth(), shared); 206 if (shared) { 207 return mNetworkListStoreData.getSharedConfigurations(); 208 } else { 209 return mNetworkListStoreData.getUserConfigurations(); 210 } 211 } 212 213 /** 214 * Helper function for generating a network list for testing purpose. The network list 215 * will contained an open and an EAP network. 216 * 217 * @param shared Flag indicating shared network 218 * @return List of WifiConfiguration 219 */ 220 private List<WifiConfiguration> getTestNetworksConfig(boolean shared) { 221 WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork(); 222 openNetwork.shared = shared; 223 openNetwork.setIpConfiguration( 224 WifiConfigurationTestUtil.createDHCPIpConfigurationWithNoProxy()); 225 WifiConfiguration eapNetwork = WifiConfigurationTestUtil.createEapNetwork(); 226 eapNetwork.shared = shared; 227 eapNetwork.setIpConfiguration( 228 WifiConfigurationTestUtil.createDHCPIpConfigurationWithNoProxy()); 229 List<WifiConfiguration> networkList = new ArrayList<>(); 230 networkList.add(openNetwork); 231 networkList.add(eapNetwork); 232 return networkList; 233 } 234 235 /** 236 * Helper function for generating XML block containing two networks, an open and an EAP 237 * network. 238 * 239 * @param openNetwork The WifiConfiguration for an open network 240 * @param eapNetwork The WifiConfiguration for an EAP network 241 * @return byte[] of the XML data 242 */ 243 private byte[] getTestNetworksXmlBytes(WifiConfiguration openNetwork, 244 WifiConfiguration eapNetwork) { 245 String openNetworkXml = String.format(SINGLE_OPEN_NETWORK_DATA_XML_STRING_FORMAT, 246 openNetwork.configKey().replaceAll("\"", """), 247 openNetwork.SSID.replaceAll("\"", """), 248 openNetwork.shared, openNetwork.creatorUid); 249 String eapNetworkXml = String.format(SINGLE_EAP_NETWORK_DATA_XML_STRING_FORMAT, 250 eapNetwork.configKey().replaceAll("\"", """), 251 eapNetwork.SSID.replaceAll("\"", """), 252 eapNetwork.shared, eapNetwork.creatorUid); 253 return (openNetworkXml + eapNetworkXml).getBytes(StandardCharsets.UTF_8); 254 } 255 256 /** 257 * Verify that serializing the store data without any configuration doesn't cause any crash 258 * and no data should be serialized. 259 * 260 * @throws Exception 261 */ 262 @Test 263 public void serializeEmptyConfigs() throws Exception { 264 assertEquals(0, serializeData(true /* shared */).length); 265 assertEquals(0, serializeData(false /* shared */).length); 266 } 267 268 /** 269 * Verify that parsing an empty data doesn't cause any crash and no configuration should 270 * be parsed. 271 * 272 * @throws Exception 273 */ 274 @Test 275 public void deserializeEmptyData() throws Exception { 276 assertTrue(deserializeData(new byte[0], true /* shared */).isEmpty()); 277 assertTrue(deserializeData(new byte[0], false /* shared */).isEmpty()); 278 } 279 280 /** 281 * Verify that NetworkListStoreData does support share data. 282 * 283 * @throws Exception 284 */ 285 @Test 286 public void supportShareData() throws Exception { 287 assertTrue(mNetworkListStoreData.supportShareData()); 288 } 289 290 /** 291 * Verify that the shared configurations (containing an open and an EAP network) are serialized 292 * correctly, matching the expected XML string. 293 * 294 * @throws Exception 295 */ 296 @Test 297 public void serializeSharedConfigurations() throws Exception { 298 List<WifiConfiguration> networkList = getTestNetworksConfig(true /* shared */); 299 mNetworkListStoreData.setSharedConfigurations(networkList); 300 byte[] expectedData = getTestNetworksXmlBytes(networkList.get(0), networkList.get(1)); 301 assertTrue(Arrays.equals(expectedData, serializeData(true /* shared */))); 302 } 303 304 /** 305 * Verify that the shared configurations are parsed correctly from a XML string containing 306 * test networks (an open and an EAP network). 307 * @throws Exception 308 */ 309 @Test 310 public void deserializeSharedConfigurations() throws Exception { 311 List<WifiConfiguration> networkList = getTestNetworksConfig(true /* shared */); 312 byte[] xmlData = getTestNetworksXmlBytes(networkList.get(0), networkList.get(1)); 313 WifiConfigurationTestUtil.assertConfigurationsEqualForConfigStore( 314 networkList, deserializeData(xmlData, true /* shared */)); 315 } 316 317 /** 318 * Verify that the user configurations (containing an open and an EAP network) are serialized 319 * correctly, matching the expected XML string. 320 * 321 * @throws Exception 322 */ 323 @Test 324 public void serializeUserConfigurations() throws Exception { 325 List<WifiConfiguration> networkList = getTestNetworksConfig(false /* shared */); 326 mNetworkListStoreData.setUserConfigurations(networkList); 327 byte[] expectedData = getTestNetworksXmlBytes(networkList.get(0), networkList.get(1)); 328 assertTrue(Arrays.equals(expectedData, serializeData(false /* shared */))); 329 } 330 331 /** 332 * Verify that the user configurations are parsed correctly from a XML string containing 333 * test networks (an open and an EAP network). 334 * @throws Exception 335 */ 336 @Test 337 public void deserializeUserConfigurations() throws Exception { 338 List<WifiConfiguration> networkList = getTestNetworksConfig(false /* shared */); 339 byte[] xmlData = getTestNetworksXmlBytes(networkList.get(0), networkList.get(1)); 340 WifiConfigurationTestUtil.assertConfigurationsEqualForConfigStore( 341 networkList, deserializeData(xmlData, false /* shared */)); 342 } 343 344 /** 345 * Verify that a XmlPullParserException will be thrown when parsing a <Network> block 346 * containing an unknown tag. 347 * 348 * @throws Exception 349 */ 350 @Test(expected = XmlPullParserException.class) 351 public void parseNetworkWithUnknownTag() throws Exception { 352 String configFormat = 353 "<Network>\n" 354 + "<WifiConfiguration>\n" 355 + "<string name=\"ConfigKey\">%s</string>\n" 356 + "<string name=\"SSID\">%s</string>\n" 357 + "<null name=\"BSSID\" />\n" 358 + "<null name=\"PreSharedKey\" />\n" 359 + "<null name=\"WEPKeys\" />\n" 360 + "<int name=\"WEPTxKeyIndex\" value=\"0\" />\n" 361 + "<boolean name=\"HiddenSSID\" value=\"false\" />\n" 362 + "<boolean name=\"RequirePMF\" value=\"false\" />\n" 363 + "<byte-array name=\"AllowedKeyMgmt\" num=\"1\">01</byte-array>\n" 364 + "<byte-array name=\"AllowedProtocols\" num=\"0\"></byte-array>\n" 365 + "<byte-array name=\"AllowedAuthAlgos\" num=\"0\"></byte-array>\n" 366 + "<byte-array name=\"AllowedGroupCiphers\" num=\"0\"></byte-array>\n" 367 + "<byte-array name=\"AllowedPairwiseCiphers\" num=\"0\"></byte-array>\n" 368 + "<boolean name=\"Shared\" value=\"%s\" />\n" 369 + "<null name=\"FQDN\" />\n" 370 + "<null name=\"ProviderFriendlyName\" />\n" 371 + "<null name=\"LinkedNetworksList\" />\n" 372 + "<null name=\"DefaultGwMacAddress\" />\n" 373 + "<boolean name=\"ValidatedInternetAccess\" value=\"false\" />\n" 374 + "<boolean name=\"NoInternetAccessExpected\" value=\"false\" />\n" 375 + "<int name=\"UserApproved\" value=\"0\" />\n" 376 + "<boolean name=\"MeteredHint\" value=\"false\" />\n" 377 + "<boolean name=\"UseExternalScores\" value=\"false\" />\n" 378 + "<int name=\"NumAssociation\" value=\"0\" />\n" 379 + "<int name=\"CreatorUid\" value=\"%d\" />\n" 380 + "<null name=\"CreatorName\" />\n" 381 + "<null name=\"CreationTime\" />\n" 382 + "<int name=\"LastUpdateUid\" value=\"-1\" />\n" 383 + "<null name=\"LastUpdateName\" />\n" 384 + "<int name=\"LastConnectUid\" value=\"0\" />\n" 385 + "</WifiConfiguration>\n" 386 + "<NetworkStatus>\n" 387 + "<string name=\"SelectionStatus\">NETWORK_SELECTION_ENABLED</string>\n" 388 + "<string name=\"DisableReason\">NETWORK_SELECTION_ENABLE</string>\n" 389 + "<null name=\"ConnectChoice\" />\n" 390 + "<long name=\"ConnectChoiceTimeStamp\" value=\"-1\" />\n" 391 + "<boolean name=\"HasEverConnected\" value=\"false\" />\n" 392 + "</NetworkStatus>\n" 393 + "<IpConfiguration>\n" 394 + "<string name=\"IpAssignment\">DHCP</string>\n" 395 + "<string name=\"ProxySettings\">NONE</string>\n" 396 + "</IpConfiguration>\n" 397 + "<Unknown>" // Unknown tag. 398 + "<int name=\"test\" value=\"0\" />\n" 399 + "</Unknown>" 400 + "</Network>\n"; 401 WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork(); 402 byte[] xmlData = String.format(configFormat, 403 openNetwork.configKey().replaceAll("\"", """), 404 openNetwork.SSID.replaceAll("\"", """), 405 openNetwork.shared, openNetwork.creatorUid).getBytes(StandardCharsets.UTF_8); 406 deserializeData(xmlData, true); 407 } 408 409 /** 410 * Verify that a XmlPullParseException will be thrown when parsing a network configuration 411 * containing a mismatched config key. 412 * 413 * @throws Exception 414 */ 415 @Test(expected = XmlPullParserException.class) 416 public void parseNetworkWithMismatchConfigKey() throws Exception { 417 WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork(); 418 byte[] xmlData = String.format(SINGLE_OPEN_NETWORK_DATA_XML_STRING_FORMAT, 419 "InvalidConfigKey", 420 openNetwork.SSID.replaceAll("\"", """), 421 openNetwork.shared, openNetwork.creatorUid).getBytes(StandardCharsets.UTF_8); 422 deserializeData(xmlData, true); 423 } 424 425 /** 426 * Tests that an invalid data in one of the WifiConfiguration object parsing would be skipped 427 * gracefully. The other networks in the XML should still be parsed out correctly. 428 */ 429 @Test 430 public void parseNetworkListWithOneNetworkIllegalArgException() throws Exception { 431 WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork(); 432 WifiConfiguration eapNetwork = WifiConfigurationTestUtil.createEapNetwork(); 433 String xmlString = new String(getTestNetworksXmlBytes(openNetwork, eapNetwork)); 434 // Manipulate the XML data to set the EAP method to None, this should raise an Illegal 435 // argument exception in WifiEnterpriseConfig.setEapMethod(). 436 xmlString = xmlString.replaceAll( 437 String.format(XmlUtilTest.XML_STRING_EAP_METHOD_REPLACE_FORMAT, 438 eapNetwork.enterpriseConfig.getEapMethod()), 439 String.format(XmlUtilTest.XML_STRING_EAP_METHOD_REPLACE_FORMAT, 440 WifiEnterpriseConfig.Eap.NONE)); 441 List<WifiConfiguration> retrievedNetworkList = 442 deserializeData(xmlString.getBytes(StandardCharsets.UTF_8), true /* shared */); 443 // Retrieved network should not contain the eap network. 444 assertEquals(1, retrievedNetworkList.size()); 445 for (WifiConfiguration network : retrievedNetworkList) { 446 assertNotEquals(eapNetwork.SSID, network.SSID); 447 } 448 } 449} 450