blob: b55e2dc06777202fdacc35e2da353bc93c5f2c15 [file] [log] [blame]
/*
* 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.IpConfiguration;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiConfiguration.NetworkSelectionStatus;
import android.net.wifi.WifiEnterpriseConfig;
import android.util.Pair;
import android.util.Xml;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FastXmlSerializer;
import com.android.server.wifi.util.XmlUtil;
import com.android.server.wifi.util.XmlUtil.IpConfigurationXmlUtil;
import com.android.server.wifi.util.XmlUtil.NetworkSelectionStatusXmlUtil;
import com.android.server.wifi.util.XmlUtil.WifiConfigurationXmlUtil;
import com.android.server.wifi.util.XmlUtil.WifiEnterpriseConfigXmlUtil;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* Class to encapsulate all the data to be stored across all the stores. This is a snapshot
* of all the settings passed from {@link WifiConfigManager} to persistent store.
* Instances of this class are passed from/to WifiConfigManager and WifiConfigStore for
* writing/parsing data to/from the store files.
*
* Note: Nesting of objects during serialization makes it hard to deserialize data especially
* when we have elements added to the parent object in future revisions. So, when we serialize
* {@link WifiConfiguration} objects (representing saved networks), we add separate sections in the
* XML for each nested object (such as {@link IpConfiguration} and {@link NetworkSelectionStatus})
* within WifiConfiguration object.
*/
public class WifiConfigStoreData {
/**
* Current config store data version. This will be incremented for any additions.
*/
private static final int CURRENT_CONFIG_STORE_DATA_VERSION = 1;
/** This list of older versions will be used to restore data from older config store. */
/**
* First version of the config store data format.
*/
private static final int INITIAL_CONFIG_STORE_DATA_VERSION = 1;
/**
* List of XML section header tags in the config store data.
*/
private static final String XML_TAG_DOCUMENT_HEADER = "WifiConfigStoreData";
private static final String XML_TAG_VERSION = "Version";
private static final String XML_TAG_SECTION_HEADER_NETWORK_LIST = "NetworkList";
private static final String XML_TAG_SECTION_HEADER_NETWORK = "Network";
private static final String XML_TAG_SECTION_HEADER_WIFI_CONFIGURATION = "WifiConfiguration";
private static final String XML_TAG_SECTION_HEADER_NETWORK_STATUS = "NetworkStatus";
private static final String XML_TAG_SECTION_HEADER_IP_CONFIGURATION = "IpConfiguration";
private static final String XML_TAG_SECTION_HEADER_WIFI_ENTERPRISE_CONFIGURATION =
"WifiEnterpriseConfiguration";
private static final String XML_TAG_SECTION_HEADER_DELETED_EPHEMERAL_SSID_LIST =
"DeletedEphemeralSSIDList";
/**
* List of saved shared networks visible to all the users to be stored in the shared store file.
*/
private final List<WifiConfiguration> mSharedConfigurations;
/**
* List of saved private networks only visible to the current user to be stored in the user
* specific store file.
*/
private final List<WifiConfiguration> mUserConfigurations;
/**
* List of deleted ephemeral ssids to be stored.
*/
private final Set<String> mDeletedEphemeralSSIDs;
/**
* Create a new instance of store data to be written to the store files.
*
* @param userConfigurations list of saved private networks to be stored.
* See {@link WifiConfigManager#mConfiguredNetworks}.
* @param sharedConfigurations list of saved shared networks to be stored.
* See {@link WifiConfigManager#mConfiguredNetworks}.
* @param deletedEphemeralSSIDs list of deleted ephemeral ssids to be stored.
* See {@link WifiConfigManager#mDeletedEphemeralSSIDs}
*/
public WifiConfigStoreData(
List<WifiConfiguration> sharedConfigurations,
List<WifiConfiguration> userConfigurations,
Set<String> deletedEphemeralSSIDs) {
this.mSharedConfigurations = sharedConfigurations;
this.mUserConfigurations = userConfigurations;
this.mDeletedEphemeralSSIDs = deletedEphemeralSSIDs;
}
/**
* Returns the list of all network configurations in the store data instance. This includes both
* the shared networks and user private networks.
*
* @return List of WifiConfiguration objects corresponding to the networks.
*/
@VisibleForTesting
public List<WifiConfiguration> getConfigurations() {
List<WifiConfiguration> configurations = new ArrayList<>();
configurations.addAll(mSharedConfigurations);
configurations.addAll(mUserConfigurations);
return configurations;
}
/**
* Returns the list of shared network configurations in the store data instance.
*
* @return List of WifiConfiguration objects corresponding to the networks.
*/
public List<WifiConfiguration> getSharedConfigurations() {
return mSharedConfigurations;
}
/**
* Returns the list of user network configurations in the store data instance.
*
* @return List of WifiConfiguration objects corresponding to the networks.
*/
public List<WifiConfiguration> getUserConfigurations() {
return mUserConfigurations;
}
/**
* Returns the set of all deleted ephemeral SSIDs in the store data instance.
*
* @return List of Strings corresponding to the SSIDs of deleted ephemeral networks.
*/
public Set<String> getDeletedEphemeralSSIDs() {
return mDeletedEphemeralSSIDs;
}
/**
* Create a new instance of the store data parsed from the store file data.
*
* Note: If any of the raw data is null or empty, will create an empty corresponding store data.
* This is to handle fresh install devices where these stores are not yet created.
*
* @param sharedDataBytes raw data retrieved from the shared store file.
* @param userDataBytes raw data retrieved from the user store file.
* @return new instance of store data.
*/
public static WifiConfigStoreData parseRawData(byte[] sharedDataBytes, byte[] userDataBytes)
throws XmlPullParserException, IOException {
SharedData sharedData;
UserData userData;
try {
if (sharedDataBytes != null && sharedDataBytes.length > 0) {
sharedData = SharedData.parseRawData(sharedDataBytes);
} else {
sharedData = new SharedData(new ArrayList<WifiConfiguration>());
}
if (userDataBytes != null && userDataBytes.length > 0) {
userData = UserData.parseRawData(userDataBytes);
} else {
userData = new UserData(new ArrayList<WifiConfiguration>(), new HashSet<String>());
}
return getStoreData(sharedData, userData);
} catch (ClassCastException e) {
throw new XmlPullParserException("Wrong value type parsed: " + e);
}
}
/**
* Create a WifiConfigStoreData instance from the retrieved UserData & SharedData instance.
*/
private static WifiConfigStoreData getStoreData(SharedData sharedData, UserData userData) {
return new WifiConfigStoreData(
sharedData.configurations, userData.configurations, userData.deletedEphemeralSSIDs);
}
/**
* Write the list of networks to the XML stream.
*
* @param out XmlSerializer instance pointing to the XML stream.
* @param configurations list of WifiConfiguration objects corresponding to the networks.
*/
private static void writeNetworksToXml(
XmlSerializer out, List<WifiConfiguration> configurations)
throws XmlPullParserException, IOException {
XmlUtil.writeNextSectionStart(out, XML_TAG_SECTION_HEADER_NETWORK_LIST);
for (WifiConfiguration configuration : configurations) {
// Write this configuration data now.
XmlUtil.writeNextSectionStart(out, XML_TAG_SECTION_HEADER_NETWORK);
writeNetworkToXml(out, configuration);
XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_NETWORK);
}
XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_NETWORK_LIST);
}
/**
* Write a network to the XML stream.
* Nested objects within the provided WifiConfiguration object are written into separate XML
* sections.
*
* @param out XmlSerializer instance pointing to the XML stream.
* @param configuration WifiConfiguration object corresponding to the network.
*/
private static void writeNetworkToXml(
XmlSerializer out, WifiConfiguration configuration)
throws XmlPullParserException, IOException {
XmlUtil.writeNextSectionStart(out, XML_TAG_SECTION_HEADER_WIFI_CONFIGURATION);
WifiConfigurationXmlUtil.writeToXmlForConfigStore(out, configuration);
XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_WIFI_CONFIGURATION);
XmlUtil.writeNextSectionStart(out, XML_TAG_SECTION_HEADER_NETWORK_STATUS);
NetworkSelectionStatusXmlUtil.writeToXml(out, configuration.getNetworkSelectionStatus());
XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_NETWORK_STATUS);
XmlUtil.writeNextSectionStart(out, XML_TAG_SECTION_HEADER_IP_CONFIGURATION);
IpConfigurationXmlUtil.writeToXml(out, configuration.getIpConfiguration());
XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_IP_CONFIGURATION);
// Store the enterprise configuration for enterprise networks.
if (configuration.enterpriseConfig != null
&& configuration.enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.NONE) {
XmlUtil.writeNextSectionStart(
out, XML_TAG_SECTION_HEADER_WIFI_ENTERPRISE_CONFIGURATION);
WifiEnterpriseConfigXmlUtil.writeToXml(out, configuration.enterpriseConfig);
XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_WIFI_ENTERPRISE_CONFIGURATION);
}
}
/**
* Parses the list of networks from the provided XML stream.
*
* @param in XmlPullParser instance pointing to the XML stream.
* @param outerTagDepth depth of the outer tag in the XML document.
* @param dataVersion version number parsed from incoming data.
* @return list of WifiConfiguration objects corresponding to the networks if parsing is
* successful, null otherwise.
*/
private static List<WifiConfiguration> parseNetworksFromXml(
XmlPullParser in, int outerTagDepth, int dataVersion)
throws XmlPullParserException, IOException {
// Find the configuration list section.
XmlUtil.gotoNextSectionWithName(in, XML_TAG_SECTION_HEADER_NETWORK_LIST, outerTagDepth);
// Find all the configurations within the configuration list section.
int networkListTagDepth = outerTagDepth + 1;
List<WifiConfiguration> configurations = new ArrayList<>();
while (XmlUtil.gotoNextSectionWithNameOrEnd(
in, XML_TAG_SECTION_HEADER_NETWORK, networkListTagDepth)) {
WifiConfiguration configuration =
parseNetworkFromXml(in, networkListTagDepth, dataVersion);
if (configuration != null) {
configurations.add(configuration);
}
}
return configurations;
}
/**
* Helper method to parse the WifiConfiguration object and validate the configKey parsed.
*/
private static WifiConfiguration parseWifiConfigurationFromXmlAndValidateConfigKey(
XmlPullParser in, int outerTagDepth)
throws XmlPullParserException, IOException {
Pair<String, WifiConfiguration> parsedConfig =
WifiConfigurationXmlUtil.parseFromXml(in, outerTagDepth);
if (parsedConfig == null || parsedConfig.first == null || parsedConfig.second == null) {
throw new XmlPullParserException("XML parsing of wifi configuration failed");
}
String configKeyParsed = parsedConfig.first;
WifiConfiguration configuration = parsedConfig.second;
String configKeyCalculated = configuration.configKey();
if (!configKeyParsed.equals(configKeyCalculated)) {
throw new XmlPullParserException(
"Configuration key does not match. Retrieved: " + configKeyParsed
+ ", Calculated: " + configKeyCalculated);
}
return configuration;
}
/**
* Parses a network from the provided XML stream.
*
* @param in XmlPullParser instance pointing to the XML stream.
* @param outerTagDepth depth of the outer tag in the XML document.
* @param dataVersion version number parsed from incoming data.
* @return WifiConfiguration object corresponding to the network if parsing is successful,
* null otherwise.
*/
private static WifiConfiguration parseNetworkFromXml(
XmlPullParser in, int outerTagDepth, int dataVersion)
throws XmlPullParserException, IOException {
// Any version migration needs to be handled here in future.
if (dataVersion == INITIAL_CONFIG_STORE_DATA_VERSION) {
WifiConfiguration configuration = null;
int networkTagDepth = outerTagDepth + 1;
XmlUtil.gotoNextSectionWithName(
in, XML_TAG_SECTION_HEADER_WIFI_CONFIGURATION, networkTagDepth);
int configTagDepth = networkTagDepth + 1;
configuration = parseWifiConfigurationFromXmlAndValidateConfigKey(in, configTagDepth);
XmlUtil.gotoNextSectionWithName(
in, XML_TAG_SECTION_HEADER_NETWORK_STATUS, networkTagDepth);
NetworkSelectionStatus status =
NetworkSelectionStatusXmlUtil.parseFromXml(in, configTagDepth);
configuration.setNetworkSelectionStatus(status);
XmlUtil.gotoNextSectionWithName(
in, XML_TAG_SECTION_HEADER_IP_CONFIGURATION, networkTagDepth);
IpConfiguration ipConfiguration =
IpConfigurationXmlUtil.parseFromXml(in, configTagDepth);
configuration.setIpConfiguration(ipConfiguration);
// Check if there is an enterprise configuration section.
if (XmlUtil.gotoNextSectionWithNameOrEnd(
in, XML_TAG_SECTION_HEADER_WIFI_ENTERPRISE_CONFIGURATION, networkTagDepth)) {
WifiEnterpriseConfig enterpriseConfig =
WifiEnterpriseConfigXmlUtil.parseFromXml(in, configTagDepth);
configuration.enterpriseConfig = enterpriseConfig;
}
return configuration;
}
return null;
}
/**
* Write the document start and version to the XML stream.
* This is used for both the shared and user config store data.
*
* @param out XmlSerializer instance pointing to the XML stream.
*/
private static void writeDocumentStartAndVersionToXml(XmlSerializer out)
throws XmlPullParserException, IOException {
XmlUtil.writeDocumentStart(out, XML_TAG_DOCUMENT_HEADER);
XmlUtil.writeNextValue(out, XML_TAG_VERSION, CURRENT_CONFIG_STORE_DATA_VERSION);
}
/**
* Parse the document start and version from the XML stream.
* This is used for both the shared and user config store data.
*
* @param in XmlPullParser instance pointing to the XML stream.
* @return version number retrieved from the Xml stream.
*/
private static int parseDocumentStartAndVersionFromXml(XmlPullParser in)
throws XmlPullParserException, IOException {
XmlUtil.gotoDocumentStart(in, XML_TAG_DOCUMENT_HEADER);
int version = (int) XmlUtil.readNextValueWithName(in, XML_TAG_VERSION);
if (version < INITIAL_CONFIG_STORE_DATA_VERSION
|| version > CURRENT_CONFIG_STORE_DATA_VERSION) {
throw new XmlPullParserException("Invalid version of data: " + version);
}
return version;
}
/**
* Create raw byte array to be stored in the share store file.
* This method serializes the data to a byte array in XML format.
*
* @return byte array with the serialized output.
*/
public byte[] createSharedRawData() throws XmlPullParserException, IOException {
SharedData sharedData = getSharedData();
return sharedData.createRawData();
}
/**
* Create raw byte array to be stored in the user store file.
* This method serializes the data to a byte array in XML format.
*
* @return byte array with the serialized output.
*/
public byte[] createUserRawData() throws XmlPullParserException, IOException {
UserData userData = getUserData();
return userData.createRawData();
}
/**
* Retrieve the shared data to be stored in the shared config store file.
*
* @return SharedData instance.
*/
private SharedData getSharedData() {
return new SharedData(mSharedConfigurations);
}
/**
* Retrieve the user specific data to be stored in the user config store file.
*
* @return UserData instance.
*/
private UserData getUserData() {
return new UserData(mUserConfigurations, mDeletedEphemeralSSIDs);
}
/**
* Class to encapsulate all the data to be stored in the shared store.
*/
public static class SharedData {
public List<WifiConfiguration> configurations;
/**
* Create a new instance of shared store data to be written to the store files.
*
* @param configurations list of shared saved networks to be stored.
*/
public SharedData(List<WifiConfiguration> configurations) {
this.configurations = configurations;
}
/**
* Create a new instance of the shared store data parsed from the store file.
* This method deserializes the provided byte array in XML format to a new SharedData
* instance.
*
* @param sharedDataBytes raw data retrieved from the shared store file.
* @return new instance of store data.
*/
public static SharedData parseRawData(byte[] sharedDataBytes)
throws XmlPullParserException, IOException {
final XmlPullParser in = Xml.newPullParser();
final ByteArrayInputStream inputStream = new ByteArrayInputStream(sharedDataBytes);
in.setInput(inputStream, StandardCharsets.UTF_8.name());
// Start parsing the XML stream.
int rootTagDepth = in.getDepth() + 1;
int version = parseDocumentStartAndVersionFromXml(in);
List<WifiConfiguration> configurations =
parseNetworksFromXml(in, rootTagDepth, version);
return new SharedData(configurations);
}
/**
* Create raw byte array to be stored in the store file.
* This method serializes the data to a byte array in XML format.
*
* @return byte array with the serialized output.
*/
public byte[] createRawData() throws XmlPullParserException, IOException {
final XmlSerializer out = new FastXmlSerializer();
final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
out.setOutput(outputStream, StandardCharsets.UTF_8.name());
// Start writing the XML stream.
writeDocumentStartAndVersionToXml(out);
// Write all the shared network configurations.
writeNetworksToXml(out, configurations);
XmlUtil.writeDocumentEnd(out, XML_TAG_DOCUMENT_HEADER);
byte[] data = outputStream.toByteArray();
return data;
}
}
/**
* Class to encapsulate all the data to be stored in the user specific store.
*/
public static class UserData {
private static final String XML_TAG_SSID_LIST = "SSIDList";
public List<WifiConfiguration> configurations;
public Set<String> deletedEphemeralSSIDs;
/**
* Create a new instance of user specific store data to be written to the store files.
*
* @param configurations list of user specific saved networks to be stored.
* @param deletedEphemeralSSIDs list of deleted ephemeral ssids to be stored.
*/
public UserData(
List<WifiConfiguration> configurations, Set<String> deletedEphemeralSSIDs) {
this.configurations = configurations;
this.deletedEphemeralSSIDs = deletedEphemeralSSIDs;
}
/**
* Create a new instance of the user store data parsed from the store file.
* This method deserializes the provided byte array in XML format to a new UserData
* instance.
*
* @param userDataBytes raw data retrieved from the user store file.
* @return new instance of store data.
*/
public static UserData parseRawData(byte[] userDataBytes)
throws XmlPullParserException, IOException {
final XmlPullParser in = Xml.newPullParser();
final ByteArrayInputStream inputStream = new ByteArrayInputStream(userDataBytes);
in.setInput(inputStream, StandardCharsets.UTF_8.name());
// Start parsing the XML stream.
int rootTagDepth = in.getDepth() + 1;
int version = parseDocumentStartAndVersionFromXml(in);
List<WifiConfiguration> configurations =
parseNetworksFromXml(in, rootTagDepth, version);
XmlUtil.gotoNextSectionWithName(
in, XML_TAG_SECTION_HEADER_DELETED_EPHEMERAL_SSID_LIST, rootTagDepth);
Set<String> deletedEphemralList =
(Set<String>) XmlUtil.readNextValueWithName(in, XML_TAG_SSID_LIST);
return new UserData(configurations, deletedEphemralList);
}
/**
* Create raw byte array to be stored in the store file.
* This method serializes the data to a byte array in XML format.
*
* @return byte array with the serialized output.
*/
public byte[] createRawData() throws XmlPullParserException, IOException {
final XmlSerializer out = new FastXmlSerializer();
final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
out.setOutput(outputStream, StandardCharsets.UTF_8.name());
// Start writing the XML stream.
writeDocumentStartAndVersionToXml(out);
// Write all the user network configurations.
writeNetworksToXml(out, configurations);
XmlUtil.writeNextSectionStart(
out, XML_TAG_SECTION_HEADER_DELETED_EPHEMERAL_SSID_LIST);
XmlUtil.writeNextValue(out, XML_TAG_SSID_LIST, deletedEphemeralSSIDs);
XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_DELETED_EPHEMERAL_SSID_LIST);
XmlUtil.writeDocumentEnd(out, XML_TAG_DOCUMENT_HEADER);
byte[] data = outputStream.toByteArray();
return data;
}
}
}