diff options
author | Treehugger Robot <treehugger-gerrit@google.com> | 2017-02-10 19:37:37 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2017-02-10 19:37:38 +0000 |
commit | 2f74844643d30a69e77436e93ad904c7701bac5e (patch) | |
tree | 391cbc5fbfb3b59a549215c03c4ce68a36df0f73 | |
parent | 1b1bafd2b0278e2079f6ece9081ddb25cdf2c254 (diff) | |
parent | 0a0b5035ce8013ed327a0802357a1b7df3061912 (diff) |
Merge "Wifi configuration store data refactor"
11 files changed, 1682 insertions, 1193 deletions
diff --git a/service/java/com/android/server/wifi/DeletedEphemeralSsidsStoreData.java b/service/java/com/android/server/wifi/DeletedEphemeralSsidsStoreData.java new file mode 100644 index 000000000..201a132fb --- /dev/null +++ b/service/java/com/android/server/wifi/DeletedEphemeralSsidsStoreData.java @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2017 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 com.android.server.wifi.util.XmlUtil; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; + +import java.io.IOException; +import java.util.HashSet; +import java.util.Set; + +/** + * This class performs serialization and parsing of XML data block that contain the list of + * deleted ephemeral SSIDs (XML block data inside <DeletedEphemeralSSIDList> tag). + */ +public class DeletedEphemeralSsidsStoreData implements WifiConfigStore.StoreData { + private static final String XML_TAG_SECTION_HEADER_DELETED_EPHEMERAL_SSID_LIST = + "DeletedEphemeralSSIDList"; + private static final String XML_TAG_SSID_LIST = "SSIDList"; + + private Set<String> mSsidList; + + DeletedEphemeralSsidsStoreData() {} + + @Override + public void serializeData(XmlSerializer out, boolean shared) + throws XmlPullParserException, IOException { + if (shared) { + throw new XmlPullParserException("Share data not supported"); + } + if (mSsidList != null) { + XmlUtil.writeNextValue(out, XML_TAG_SSID_LIST, mSsidList); + } + } + + @Override + public void deserializeData(XmlPullParser in, int outerTagDepth, boolean shared) + throws XmlPullParserException, IOException { + if (shared) { + throw new XmlPullParserException("Share data not supported"); + } + + while (!XmlUtil.isNextSectionEnd(in, outerTagDepth)) { + String[] valueName = new String[1]; + Object value = XmlUtil.readCurrentValue(in, valueName); + if (valueName[0] == null) { + throw new XmlPullParserException("Missing value name"); + } + switch (valueName[0]) { + case XML_TAG_SSID_LIST: + mSsidList = (Set<String>) value; + break; + default: + throw new XmlPullParserException("Unknown tag under " + + XML_TAG_SECTION_HEADER_DELETED_EPHEMERAL_SSID_LIST + + ": " + valueName[0]); + } + } + } + + @Override + public void resetData(boolean shared) { + if (!shared) { + mSsidList = null; + } + } + + @Override + public String getName() { + return XML_TAG_SECTION_HEADER_DELETED_EPHEMERAL_SSID_LIST; + } + + @Override + public boolean supportShareData() { + return false; + } + + /** + * An empty set will be returned for null SSID list. + * + * @return Set of SSIDs + */ + public Set<String> getSsidList() { + if (mSsidList == null) { + return new HashSet<String>(); + } + return mSsidList; + } + + public void setSsidList(Set<String> ssidList) { + mSsidList = ssidList; + } +} + diff --git a/service/java/com/android/server/wifi/NetworkListStoreData.java b/service/java/com/android/server/wifi/NetworkListStoreData.java new file mode 100644 index 000000000..f9ecab132 --- /dev/null +++ b/service/java/com/android/server/wifi/NetworkListStoreData.java @@ -0,0 +1,284 @@ +/* + * Copyright (C) 2017 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 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.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * This class performs serialization and parsing of XML data block that contain the list of WiFi + * network configurations (XML block data inside <NetworkList> tag). + */ +public class NetworkListStoreData implements WifiConfigStore.StoreData { + 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"; + + /** + * List of saved shared networks visible to all the users to be stored in the shared store file. + */ + private List<WifiConfiguration> mSharedConfigurations; + /** + * List of saved private networks only visible to the current user to be stored in the user + * specific store file. + */ + private List<WifiConfiguration> mUserConfigurations; + + NetworkListStoreData() {} + + @Override + public void serializeData(XmlSerializer out, boolean shared) + throws XmlPullParserException, IOException { + if (shared) { + serializeNetworkList(out, mSharedConfigurations); + } else { + serializeNetworkList(out, mUserConfigurations); + } + } + + @Override + public void deserializeData(XmlPullParser in, int outerTagDepth, boolean shared) + throws XmlPullParserException, IOException { + if (shared) { + mSharedConfigurations = parseNetworkList(in, outerTagDepth); + } else { + mUserConfigurations = parseNetworkList(in, outerTagDepth); + } + } + + @Override + public void resetData(boolean shared) { + if (shared) { + mSharedConfigurations = null; + } else { + mUserConfigurations = null; + } + } + + @Override + public String getName() { + return XML_TAG_SECTION_HEADER_NETWORK_LIST; + } + + @Override + public boolean supportShareData() { + return true; + } + + public void setSharedConfigurations(List<WifiConfiguration> configs) { + mSharedConfigurations = configs; + } + + /** + * An empty list will be returned if no shared configurations. + * + * @return List of {@link WifiConfiguration} + */ + public List<WifiConfiguration> getSharedConfigurations() { + if (mSharedConfigurations == null) { + return new ArrayList<WifiConfiguration>(); + } + return mSharedConfigurations; + } + + public void setUserConfigurations(List<WifiConfiguration> configs) { + mUserConfigurations = configs; + } + + /** + * An empty list will be returned if no user configurations. + * + * @return List of {@link WifiConfiguration} + */ + public List<WifiConfiguration> getUserConfigurations() { + if (mUserConfigurations == null) { + return new ArrayList<WifiConfiguration>(); + } + return mUserConfigurations; + } + + /** + * Serialize the list of {@link WifiConfiguration} to an output stream in XML format. + * + * @param out The output stream to serialize the data to + * @param networkList The network list to serialize + * @throws XmlPullParserException + * @throws IOException + */ + private void serializeNetworkList(XmlSerializer out, List<WifiConfiguration> networkList) + throws XmlPullParserException, IOException { + if (networkList == null) { + return; + } + for (WifiConfiguration network : networkList) { + serializeWifiConfig(out, network); + } + } + + /** + * Serialize a {@link WifiConfiguration} to an output stream in XML format. + * @param out + * @param config + * @throws XmlPullParserException + * @throws IOException + */ + private void serializeWifiConfig(XmlSerializer out, WifiConfiguration config) + throws XmlPullParserException, IOException { + XmlUtil.writeNextSectionStart(out, XML_TAG_SECTION_HEADER_NETWORK); + + // Serialize WifiConfiguration. + XmlUtil.writeNextSectionStart(out, XML_TAG_SECTION_HEADER_WIFI_CONFIGURATION); + WifiConfigurationXmlUtil.writeToXmlForConfigStore(out, config); + XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_WIFI_CONFIGURATION); + + // Serialize network selection status. + XmlUtil.writeNextSectionStart(out, XML_TAG_SECTION_HEADER_NETWORK_STATUS); + NetworkSelectionStatusXmlUtil.writeToXml(out, config.getNetworkSelectionStatus()); + XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_NETWORK_STATUS); + + // Serialize IP configuration. + XmlUtil.writeNextSectionStart(out, XML_TAG_SECTION_HEADER_IP_CONFIGURATION); + IpConfigurationXmlUtil.writeToXml(out, config.getIpConfiguration()); + XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_IP_CONFIGURATION); + + // Serialize enterprise configuration for enterprise networks. + if (config.enterpriseConfig != null + && config.enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.NONE) { + XmlUtil.writeNextSectionStart( + out, XML_TAG_SECTION_HEADER_WIFI_ENTERPRISE_CONFIGURATION); + WifiEnterpriseConfigXmlUtil.writeToXml(out, config.enterpriseConfig); + XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_WIFI_ENTERPRISE_CONFIGURATION); + } + + XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_NETWORK); + } + + /** + * Parse a list of {@link WifiConfiguration} from an input stream in XML format. + * + * @param in The input stream to read from + * @param outerTagDepth The XML tag depth of the outer XML block + * @return List of {@link WifiConfiguration} + * @throws XmlPullParserException + * @throws IOException + */ + private List<WifiConfiguration> parseNetworkList(XmlPullParser in, int outerTagDepth) + throws XmlPullParserException, IOException { + List<WifiConfiguration> networkList = new ArrayList<>(); + while (XmlUtil.gotoNextSectionWithNameOrEnd(in, XML_TAG_SECTION_HEADER_NETWORK, + outerTagDepth)) { + WifiConfiguration config = parseWifiConfiguration(in, outerTagDepth + 1); + networkList.add(config); + } + return networkList; + } + + /** + * Parse a {@link WifiConfiguration} from an input stream in XML format. + * + * @param in The input stream to read from + * @param outerTagDepth The XML tag depth of the outer XML block + * @return {@link WifiConfiguration} + * @throws XmlPullParserException + * @throws IOException + */ + private WifiConfiguration parseWifiConfiguration(XmlPullParser in, int outerTagDepth) + throws XmlPullParserException, IOException { + Pair<String, WifiConfiguration> parsedConfig = null; + NetworkSelectionStatus status = null; + IpConfiguration ipConfiguration = null; + WifiEnterpriseConfig enterpriseConfig = null; + + String[] headerName = new String[1]; + while (XmlUtil.gotoNextSectionOrEnd(in, headerName, outerTagDepth)) { + switch (headerName[0]) { + case XML_TAG_SECTION_HEADER_WIFI_CONFIGURATION: + if (parsedConfig != null) { + throw new XmlPullParserException("Detected duplicate tag for: " + + XML_TAG_SECTION_HEADER_WIFI_CONFIGURATION); + } + parsedConfig = WifiConfigurationXmlUtil.parseFromXml(in, outerTagDepth + 1); + break; + case XML_TAG_SECTION_HEADER_NETWORK_STATUS: + if (status != null) { + throw new XmlPullParserException("Detected duplicate tag for: " + + XML_TAG_SECTION_HEADER_NETWORK_STATUS); + } + status = NetworkSelectionStatusXmlUtil.parseFromXml(in, outerTagDepth + 1); + break; + case XML_TAG_SECTION_HEADER_IP_CONFIGURATION: + if (ipConfiguration != null) { + throw new XmlPullParserException("Detected duplicate tag for: " + + XML_TAG_SECTION_HEADER_IP_CONFIGURATION); + } + ipConfiguration = IpConfigurationXmlUtil.parseFromXml(in, outerTagDepth + 1); + break; + case XML_TAG_SECTION_HEADER_WIFI_ENTERPRISE_CONFIGURATION: + if (enterpriseConfig != null) { + throw new XmlPullParserException("Detected duplicate tag for: " + + XML_TAG_SECTION_HEADER_WIFI_ENTERPRISE_CONFIGURATION); + } + enterpriseConfig = + WifiEnterpriseConfigXmlUtil.parseFromXml(in, outerTagDepth + 1); + break; + default: + throw new XmlPullParserException("Unknown tag under " + + XML_TAG_SECTION_HEADER_NETWORK + ": " + headerName[0]); + } + } + 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); + } + + configuration.setNetworkSelectionStatus(status); + configuration.setIpConfiguration(ipConfiguration); + if (enterpriseConfig != null) { + configuration.enterpriseConfig = enterpriseConfig; + } + return configuration; + } +} + diff --git a/service/java/com/android/server/wifi/WifiConfigManager.java b/service/java/com/android/server/wifi/WifiConfigManager.java index eb25711cf..7b704ad50 100644 --- a/service/java/com/android/server/wifi/WifiConfigManager.java +++ b/service/java/com/android/server/wifi/WifiConfigManager.java @@ -291,6 +291,11 @@ public class WifiConfigManager { private long mLastSelectedTimeStamp = WifiConfiguration.NetworkSelectionStatus.INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP; + // Store data for network list and deleted ephemeral SSID list. Used for serializing + // parsing data to/from the config store. + private final NetworkListStoreData mNetworkListStoreData; + private final DeletedEphemeralSsidsStoreData mDeletedEphemeralSsidsStoreData; + /** * Create new instance of WifiConfigManager. */ @@ -298,7 +303,9 @@ public class WifiConfigManager { Context context, FrameworkFacade facade, Clock clock, UserManager userManager, TelephonyManager telephonyManager, WifiKeyStore wifiKeyStore, WifiConfigStore wifiConfigStore, WifiConfigStoreLegacy wifiConfigStoreLegacy, - WifiPermissionsWrapper wifiPermissionsWrapper) { + WifiPermissionsWrapper wifiPermissionsWrapper, + NetworkListStoreData networkListStoreData, + DeletedEphemeralSsidsStoreData deletedEphemeralSsidsStoreData) { mContext = context; mFacade = facade; mClock = clock; @@ -314,6 +321,12 @@ public class WifiConfigManager { mScanDetailCaches = new HashMap<>(16, 0.75f); mDeletedEphemeralSSIDs = new HashSet<>(); + // Register store data for network list and deleted ephemeral SSIDs. + mNetworkListStoreData = networkListStoreData; + mDeletedEphemeralSsidsStoreData = deletedEphemeralSsidsStoreData; + mWifiConfigStore.registerStoreData(mNetworkListStoreData); + mWifiConfigStore.registerStoreData(mDeletedEphemeralSsidsStoreData); + mOnlyLinkSameCredentialConfigurations = mContext.getResources().getBoolean( R.bool.config_wifi_only_link_same_credential_configurations); mMaxNumActiveChannelsForPartialScans = mContext.getResources().getInteger( @@ -2561,9 +2574,8 @@ public class WifiConfigManager { } return true; } - WifiConfigStoreData storeData; try { - storeData = mWifiConfigStore.read(); + mWifiConfigStore.read(); } catch (IOException e) { Log.wtf(TAG, "Reading from new store failed. All saved networks are lost!", e); return false; @@ -2571,8 +2583,9 @@ public class WifiConfigManager { Log.wtf(TAG, "XML deserialization of store failed. All saved networks are lost!", e); return false; } - loadInternalData(storeData.getSharedConfigurations(), storeData.getUserConfigurations(), - storeData.getDeletedEphemeralSSIDs()); + loadInternalData(mNetworkListStoreData.getSharedConfigurations(), + mNetworkListStoreData.getUserConfigurations(), + mDeletedEphemeralSsidsStoreData.getSsidList()); // If the user unlock comes in before we load from store, we defer the handling until // the load from store is triggered. if (mDeferredUserUnlockRead) { @@ -2596,10 +2609,8 @@ public class WifiConfigManager { * @return true on success, false otherwise. */ public boolean loadFromUserStoreAfterUnlockOrSwitch(int userId) { - WifiConfigStoreData storeData; try { - storeData = mWifiConfigStore.switchUserStoreAndRead( - mWifiConfigStore.createUserFile(userId)); + mWifiConfigStore.switchUserStoreAndRead(WifiConfigStore.createUserFile(userId)); } catch (IOException e) { Log.wtf(TAG, "Reading from new store failed. All saved private networks are lost!", e); return false; @@ -2608,8 +2619,8 @@ public class WifiConfigManager { "lost!", e); return false; } - loadInternalDataFromUserStore(storeData.getUserConfigurations(), - storeData.getDeletedEphemeralSSIDs()); + loadInternalDataFromUserStore(mNetworkListStoreData.getUserConfigurations(), + mDeletedEphemeralSsidsStoreData.getSsidList()); return true; } @@ -2640,12 +2651,14 @@ public class WifiConfigManager { } } } - WifiConfigStoreData storeData = - new WifiConfigStoreData( - sharedConfigurations, userConfigurations, mDeletedEphemeralSSIDs); + + // Setup store data for write. + mNetworkListStoreData.setSharedConfigurations(sharedConfigurations); + mNetworkListStoreData.setUserConfigurations(userConfigurations); + mDeletedEphemeralSsidsStoreData.setSsidList(mDeletedEphemeralSSIDs); try { - mWifiConfigStore.write(forceWrite, storeData); + mWifiConfigStore.write(forceWrite); } catch (IOException e) { Log.wtf(TAG, "Writing to store failed. Saved networks maybe lost!", e); return false; diff --git a/service/java/com/android/server/wifi/WifiConfigStore.java b/service/java/com/android/server/wifi/WifiConfigStore.java index 1eea9d5ca..3f1269c42 100644 --- a/service/java/com/android/server/wifi/WifiConfigStore.java +++ b/service/java/com/android/server/wifi/WifiConfigStore.java @@ -23,16 +23,26 @@ import android.os.FileUtils; import android.os.Handler; import android.os.Looper; import android.util.Log; +import android.util.Xml; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.AtomicFile; +import com.android.internal.util.FastXmlSerializer; +import com.android.server.wifi.util.XmlUtil; +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.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Map; /** * This class provides the API's to save/load/modify network configurations from a persistent @@ -40,6 +50,18 @@ import java.io.IOException; * NOTE: This class should only be used from WifiConfigManager and is not thread-safe! */ public class WifiConfigStore { + private static final String XML_TAG_DOCUMENT_HEADER = "WifiConfigStoreData"; + private static final String XML_TAG_VERSION = "Version"; + /** + * 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; + /** * Alarm tag to use for starting alarms for buffering file writes. */ @@ -105,6 +127,11 @@ public class WifiConfigStore { }; /** + * List of data container. + */ + private final Map<String, StoreData> mStoreDataList; + + /** * Create a new instance of WifiConfigStore. * Note: The store file instances have been made inputs to this class to ease unit-testing. * @@ -120,6 +147,7 @@ public class WifiConfigStore { mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); mEventHandler = new Handler(looper); mClock = clock; + mStoreDataList = new HashMap<>(); // Initialize the store files. mSharedStore = sharedStore; @@ -129,6 +157,23 @@ public class WifiConfigStore { } /** + * Register a {@link StoreData} to store. A {@link StoreData} is responsible + * for a block of data in the store file, and provides serialization/deserialization functions + * for those data. + * + * @param storeData The store data to be registered to the config store + * @return true if succeeded + */ + public boolean registerStoreData(StoreData storeData) { + if (storeData == null) { + Log.e(TAG, "Unable to register null store data"); + return false; + } + mStoreDataList.put(storeData.getName(), storeData); + return true; + } + + /** * Helper method to create a store file instance for either the shared store or user store. * Note: The method creates the store directory if not already present. This may be needed for * user store files. @@ -185,22 +230,21 @@ public class WifiConfigStore { } /** - * API to write the provided store data to config stores. + * API to write the data provided by registered store data to config stores. * The method writes the user specific configurations to user specific config store and the * shared configurations to shared config store. * * @param forceSync boolean to force write the config stores now. if false, the writes are * buffered and written after the configured interval. - * @param storeData The entire data to be stored across all the config store files. */ - public void write(boolean forceSync, WifiConfigStoreData storeData) + public void write(boolean forceSync) throws XmlPullParserException, IOException { // Serialize the provided data and send it to the respective stores. The actual write will // be performed later depending on the |forceSync| flag . - byte[] sharedDataBytes = storeData.createSharedRawData(); + byte[] sharedDataBytes = serializeData(true); mSharedStore.storeRawDataToWrite(sharedDataBytes); if (mUserStore != null) { - byte[] userDataBytes = storeData.createUserRawData(); + byte[] userDataBytes = serializeData(false); mUserStore.storeRawDataToWrite(userDataBytes); } @@ -214,6 +258,39 @@ public class WifiConfigStore { } /** + * Serialize share data or user data from all store data. + * + * @param shareData Flag indicating share data + * @return byte[] of serialized bytes + * @throws XmlPullParserException + * @throws IOException + */ + private byte[] serializeData(boolean shareData) throws XmlPullParserException, IOException { + final XmlSerializer out = new FastXmlSerializer(); + final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + out.setOutput(outputStream, StandardCharsets.UTF_8.name()); + + XmlUtil.writeDocumentStart(out, XML_TAG_DOCUMENT_HEADER); + XmlUtil.writeNextValue(out, XML_TAG_VERSION, CURRENT_CONFIG_STORE_DATA_VERSION); + + for (Map.Entry<String, StoreData> entry : mStoreDataList.entrySet()) { + String tag = entry.getKey(); + StoreData storeData = entry.getValue(); + // Ignore this store data if this is for share file and the store data doesn't support + // share store. + if (shareData && !storeData.supportShareData()) { + continue; + } + XmlUtil.writeNextSectionStart(out, tag); + storeData.serializeData(out, shareData); + XmlUtil.writeNextSectionEnd(out, tag); + } + XmlUtil.writeDocumentEnd(out, XML_TAG_DOCUMENT_HEADER); + + return outputStream.toByteArray(); + } + + /** * Helper method to start a buffered write alarm if one doesn't already exist. */ private void startBufferedWriteAlarm() { @@ -256,10 +333,12 @@ public class WifiConfigStore { * API to read the store data from the config stores. * The method reads the user specific configurations from user specific config store and the * shared configurations from the shared config store. - * - * @return storeData The entire data retrieved across all the config store files. */ - public WifiConfigStoreData read() throws XmlPullParserException, IOException { + public void read() throws XmlPullParserException, IOException { + // Reset both share and user store data. + resetStoreData(true); + resetStoreData(false); + long readStartTime = mClock.getElapsedSinceBootMillis(); byte[] sharedDataBytes = mSharedStore.readRawData(); byte[] userDataBytes = null; @@ -268,8 +347,8 @@ public class WifiConfigStore { } long readTime = mClock.getElapsedSinceBootMillis() - readStartTime; Log.d(TAG, "Reading from stores completed in " + readTime + " ms."); - - return WifiConfigStoreData.parseRawData(sharedDataBytes, userDataBytes); + deserializeData(sharedDataBytes, true); + deserializeData(userDataBytes, false); } /** @@ -279,8 +358,11 @@ public class WifiConfigStore { * @param userStore StoreFile instance pointing to the user specific store file. This should * be retrieved using {@link #createUserFile(int)} method. */ - public WifiConfigStoreData switchUserStoreAndRead(StoreFile userStore) + public void switchUserStoreAndRead(StoreFile userStore) throws XmlPullParserException, IOException { + // Reset user store data. + resetStoreData(false); + // Stop any pending buffered writes, if any. stopBufferedWriteAlarm(); mUserStore = userStore; @@ -290,8 +372,67 @@ public class WifiConfigStore { byte[] userDataBytes = mUserStore.readRawData(); long readTime = mClock.getElapsedSinceBootMillis() - readStartTime; Log.d(TAG, "Reading from user store completed in " + readTime + " ms."); + deserializeData(userDataBytes, false); + } + + /** + * Reset share data or user data in all store data. + * + * @param shareData Flag indicating share data + */ + private void resetStoreData(boolean shareData) { + for (Map.Entry<String, StoreData> entry : mStoreDataList.entrySet()) { + entry.getValue().resetData(shareData); + } + } + + /** + * Deserialize share data or user data into store data. + * + * @param dataBytes The data to parse + * @param shareData The flag indicating share data + * @throws XmlPullParserException + * @throws IOException + */ + private void deserializeData(byte[] dataBytes, boolean shareData) + throws XmlPullParserException, IOException { + if (dataBytes == null) { + return; + } + final XmlPullParser in = Xml.newPullParser(); + final ByteArrayInputStream inputStream = new ByteArrayInputStream(dataBytes); + in.setInput(inputStream, StandardCharsets.UTF_8.name()); + + // Start parsing the XML stream. + int rootTagDepth = in.getDepth() + 1; + parseDocumentStartAndVersionFromXml(in); + + String[] headerName = new String[1]; + while (XmlUtil.gotoNextSectionOrEnd(in, headerName, rootTagDepth)) { + StoreData storeData = mStoreDataList.get(headerName[0]); + if (storeData == null) { + throw new XmlPullParserException("Unknown store data: " + headerName[0]); + } + storeData.deserializeData(in, rootTagDepth + 1, shareData); + } + } - return WifiConfigStoreData.parseRawData(null, userDataBytes); + /** + * 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; } /** @@ -386,4 +527,58 @@ public class WifiConfigStore { mWriteData = null; } } + + /** + * Interface to be implemented by a module that contained data in the config store file. + * + * The module will be responsible for serializing/deserializing their own data. + */ + public interface StoreData { + /** + * Serialize a XML data block to the output stream. The |shared| flag indicates if the + * output stream is backed by a share store or an user store. + * + * @param out The output stream to serialize the data to + * @param shared Flag indicating if the output stream is backed by a share store or an + * user store + */ + void serializeData(XmlSerializer out, boolean shared) + throws XmlPullParserException, IOException; + + /** + * Deserialize a XML data block from the input stream. The |shared| flag indicates if the + * input stream is backed by a share store or an user store. When |shared| is set to true, + * the shared configuration data will be overwritten by the parsed data. Otherwise, + * the user configuration will be overwritten by the parsed data. + * + * @param in The input stream to read the data from + * @param outerTagDepth The depth of the outer tag in the XML document + * @Param shared Flag indicating if the input stream is backed by a share store or an + * user store + */ + void deserializeData(XmlPullParser in, int outerTagDepth, boolean shared) + throws XmlPullParserException, IOException; + + /** + * Reset configuration data. The |shared| flag indicates which configuration data to + * reset. When |shared| is set to true, the shared configuration data will be reset. + * Otherwise, the user configuration data will be reset. + */ + void resetData(boolean shared); + + /** + * Return the name of this store data. The data will be enclosed under this tag in + * the XML block. + * + * @return The name of the store data + */ + String getName(); + + /** + * Flag indicating if shared configuration data is supported. + * + * @return true if shared configuration data is supported + */ + boolean supportShareData(); + } } diff --git a/service/java/com/android/server/wifi/WifiConfigStoreData.java b/service/java/com/android/server/wifi/WifiConfigStoreData.java deleted file mode 100644 index b55e2dc06..000000000 --- a/service/java/com/android/server/wifi/WifiConfigStoreData.java +++ /dev/null @@ -1,560 +0,0 @@ -/* - * 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; - } - } -} - - diff --git a/service/java/com/android/server/wifi/WifiInjector.java b/service/java/com/android/server/wifi/WifiInjector.java index 7c976cf44..a5cb95b98 100644 --- a/service/java/com/android/server/wifi/WifiInjector.java +++ b/service/java/com/android/server/wifi/WifiInjector.java @@ -180,7 +180,8 @@ public class WifiInjector { // Config Manager mWifiConfigManager = new WifiConfigManager(mContext, mFrameworkFacade, mClock, UserManager.get(mContext), TelephonyManager.from(mContext), - mWifiKeyStore, mWifiConfigStore, mWifiConfigStoreLegacy, mWifiPermissionsWrapper); + mWifiKeyStore, mWifiConfigStore, mWifiConfigStoreLegacy, mWifiPermissionsWrapper, + new NetworkListStoreData(), new DeletedEphemeralSsidsStoreData()); mWifiNetworkScoreCache = new WifiNetworkScoreCache(mContext); mWifiNetworkSelector = new WifiNetworkSelector(mContext, mWifiConfigManager, mClock); LocalLog localLog = mWifiNetworkSelector.getLocalLog(); diff --git a/tests/wifitests/src/com/android/server/wifi/DeletedEphemeralSsidsStoreDataTest.java b/tests/wifitests/src/com/android/server/wifi/DeletedEphemeralSsidsStoreDataTest.java new file mode 100644 index 000000000..0db93f598 --- /dev/null +++ b/tests/wifitests/src/com/android/server/wifi/DeletedEphemeralSsidsStoreDataTest.java @@ -0,0 +1,178 @@ +/* + * Copyright (C) 2017 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.*; +import static org.mockito.Mockito.*; + +import android.test.suitebuilder.annotation.SmallTest; +import android.util.Xml; + +import com.android.internal.util.FastXmlSerializer; + +import org.junit.Before; +import org.junit.Test; +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.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +/** + * Unit tests for {@link com.android.server.wifi.DeletedEphemeralSsidsStoreData}. + */ +@SmallTest +public class DeletedEphemeralSsidsStoreDataTest { + private static final String TEST_SSID1 = "SSID 1"; + private static final String TEST_SSID2 = "SSID 2"; + private static final String TEST_SSID_LIST_XML_STRING = + "<set name=\"SSIDList\">\n" + + "<string>" + TEST_SSID1 + "</string>\n" + + "<string>" + TEST_SSID2 + "</string>\n" + + "</set>\n"; + private static final byte[] TEST_SSID_LIST_XML_BYTES = + TEST_SSID_LIST_XML_STRING.getBytes(StandardCharsets.UTF_8); + private DeletedEphemeralSsidsStoreData mDeletedEphemeralSsidsStoreData; + + @Before + public void setUp() throws Exception { + mDeletedEphemeralSsidsStoreData = new DeletedEphemeralSsidsStoreData(); + } + + /** + * Helper function for serializing configuration data to a XML block. + * + * @param shared Flag indicating serializing shared or user configurations + * @return byte[] of the XML data + * @throws Exception + */ + private byte[] serializeData(boolean shared) throws Exception { + final XmlSerializer out = new FastXmlSerializer(); + final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + out.setOutput(outputStream, StandardCharsets.UTF_8.name()); + mDeletedEphemeralSsidsStoreData.serializeData(out, shared); + out.flush(); + return outputStream.toByteArray(); + } + + /** + * Helper function for parsing configuration data from a XML block. + * + * @param data XML data to parse from + * @param shared Flag indicating parsing of shared or user configurations + * @return SSID list + * @throws Exception + */ + private Set<String> deserializeData(byte[] data, boolean shared) throws Exception { + final XmlPullParser in = Xml.newPullParser(); + final ByteArrayInputStream inputStream = new ByteArrayInputStream(data); + in.setInput(inputStream, StandardCharsets.UTF_8.name()); + mDeletedEphemeralSsidsStoreData.deserializeData(in, in.getDepth(), shared); + return mDeletedEphemeralSsidsStoreData.getSsidList(); + } + + /** + * Verify that a XmlPullParserException will be thrown when attempting to serialize SSID list + * to the share store, since the deleted ephemeral SSID list should never be persist + * to the share store. + * + * @throws Exception + */ + @Test(expected = XmlPullParserException.class) + public void serializeShareData() throws Exception { + serializeData(true /* shared */); + } + + /** + * Verify that a XmlPullParserException will be thrown when attempting to parse SSID list + * from the share store, since the deleted ephemeral SSID list should never be persist + * to the share store. + * + * @throws Exception + */ + @Test(expected = XmlPullParserException.class) + public void deserializeShareData() throws Exception { + deserializeData(new byte[0], true /* shared */); + } + + /** + * Verify that serializing the user store data without any configuration doesn't cause any + * crash and no data should be serialized. + * + * @throws Exception + */ + @Test + public void serializeEmptyConfigs() throws Exception { + assertEquals(0, serializeData(false /* shared */).length); + } + + /** + * Verify that parsing an empty data doesn't cause any crash and no configuration should + * be deserialized. + * + * @throws Exception + */ + @Test + public void deserializeEmptyData() throws Exception { + assertTrue(deserializeData(new byte[0], false /* shared */).isEmpty()); + } + + /** + * Verify that DeletedEphemeralSsidsStoreData does not support share data. + * + * @throws Exception + */ + @Test + public void supportShareData() throws Exception { + assertFalse(mDeletedEphemeralSsidsStoreData.supportShareData()); + } + + /** + * Verify that user store SSID list is serialized correctly, matches the predefined test + * XML data. + * + * @throws Exception + */ + @Test + public void serializeSsidList() throws Exception { + Set<String> ssidList = new HashSet<>(); + ssidList.add(TEST_SSID1); + ssidList.add(TEST_SSID2); + mDeletedEphemeralSsidsStoreData.setSsidList(ssidList); + byte[] actualData = serializeData(false /* shared */); + assertTrue(Arrays.equals(TEST_SSID_LIST_XML_BYTES, actualData)); + } + + /** + * Verify that user store SSID list is deserialized correctly using the predefined test XML + * data. + * + * @throws Exception + */ + @Test + public void deserializeSsidList() throws Exception { + Set<String> ssidList = new HashSet<>(); + ssidList.add(TEST_SSID1); + ssidList.add(TEST_SSID2); + assertEquals(ssidList, deserializeData(TEST_SSID_LIST_XML_BYTES, false /* shared */)); + } +} diff --git a/tests/wifitests/src/com/android/server/wifi/NetworkListStoreDataTest.java b/tests/wifitests/src/com/android/server/wifi/NetworkListStoreDataTest.java new file mode 100644 index 000000000..a1f0057e2 --- /dev/null +++ b/tests/wifitests/src/com/android/server/wifi/NetworkListStoreDataTest.java @@ -0,0 +1,414 @@ +/* + * Copyright (C) 2017 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.*; +import static org.mockito.Mockito.*; + +import android.net.wifi.WifiConfiguration; +import android.test.suitebuilder.annotation.SmallTest; +import android.util.Xml; + +import com.android.internal.util.FastXmlSerializer; + +import org.junit.Before; +import org.junit.Test; +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.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Unit tests for {@link com.android.server.wifi.NetworksListStoreData}. + */ +@SmallTest +public class NetworkListStoreDataTest { + + private static final String TEST_SSID = "WifiConfigStoreDataSSID_"; + private static final String TEST_CONNECT_CHOICE = "XmlUtilConnectChoice"; + private static final long TEST_CONNECT_CHOICE_TIMESTAMP = 0x4566; + private static final String SINGLE_OPEN_NETWORK_DATA_XML_STRING_FORMAT = + "<Network>\n" + + "<WifiConfiguration>\n" + + "<string name=\"ConfigKey\">%s</string>\n" + + "<string name=\"SSID\">%s</string>\n" + + "<null name=\"BSSID\" />\n" + + "<null name=\"PreSharedKey\" />\n" + + "<null name=\"WEPKeys\" />\n" + + "<int name=\"WEPTxKeyIndex\" value=\"0\" />\n" + + "<boolean name=\"HiddenSSID\" value=\"false\" />\n" + + "<boolean name=\"RequirePMF\" value=\"false\" />\n" + + "<byte-array name=\"AllowedKeyMgmt\" num=\"1\">01</byte-array>\n" + + "<byte-array name=\"AllowedProtocols\" num=\"0\"></byte-array>\n" + + "<byte-array name=\"AllowedAuthAlgos\" num=\"0\"></byte-array>\n" + + "<byte-array name=\"AllowedGroupCiphers\" num=\"0\"></byte-array>\n" + + "<byte-array name=\"AllowedPairwiseCiphers\" num=\"0\"></byte-array>\n" + + "<boolean name=\"Shared\" value=\"%s\" />\n" + + "<null name=\"FQDN\" />\n" + + "<null name=\"ProviderFriendlyName\" />\n" + + "<null name=\"LinkedNetworksList\" />\n" + + "<null name=\"DefaultGwMacAddress\" />\n" + + "<boolean name=\"ValidatedInternetAccess\" value=\"false\" />\n" + + "<boolean name=\"NoInternetAccessExpected\" value=\"false\" />\n" + + "<int name=\"UserApproved\" value=\"0\" />\n" + + "<boolean name=\"MeteredHint\" value=\"false\" />\n" + + "<boolean name=\"UseExternalScores\" value=\"false\" />\n" + + "<int name=\"NumAssociation\" value=\"0\" />\n" + + "<int name=\"CreatorUid\" value=\"%d\" />\n" + + "<null name=\"CreatorName\" />\n" + + "<null name=\"CreationTime\" />\n" + + "<int name=\"LastUpdateUid\" value=\"-1\" />\n" + + "<null name=\"LastUpdateName\" />\n" + + "<int name=\"LastConnectUid\" value=\"0\" />\n" + + "</WifiConfiguration>\n" + + "<NetworkStatus>\n" + + "<string name=\"SelectionStatus\">NETWORK_SELECTION_ENABLED</string>\n" + + "<string name=\"DisableReason\">NETWORK_SELECTION_ENABLE</string>\n" + + "<null name=\"ConnectChoice\" />\n" + + "<long name=\"ConnectChoiceTimeStamp\" value=\"-1\" />\n" + + "<boolean name=\"HasEverConnected\" value=\"false\" />\n" + + "</NetworkStatus>\n" + + "<IpConfiguration>\n" + + "<string name=\"IpAssignment\">DHCP</string>\n" + + "<string name=\"ProxySettings\">NONE</string>\n" + + "</IpConfiguration>\n" + + "</Network>\n"; + + private static final String SINGLE_EAP_NETWORK_DATA_XML_STRING_FORMAT = + "<Network>\n" + + "<WifiConfiguration>\n" + + "<string name=\"ConfigKey\">%s</string>\n" + + "<string name=\"SSID\">%s</string>\n" + + "<null name=\"BSSID\" />\n" + + "<null name=\"PreSharedKey\" />\n" + + "<null name=\"WEPKeys\" />\n" + + "<int name=\"WEPTxKeyIndex\" value=\"0\" />\n" + + "<boolean name=\"HiddenSSID\" value=\"false\" />\n" + + "<boolean name=\"RequirePMF\" value=\"false\" />\n" + + "<byte-array name=\"AllowedKeyMgmt\" num=\"1\">0c</byte-array>\n" + + "<byte-array name=\"AllowedProtocols\" num=\"0\"></byte-array>\n" + + "<byte-array name=\"AllowedAuthAlgos\" num=\"0\"></byte-array>\n" + + "<byte-array name=\"AllowedGroupCiphers\" num=\"0\"></byte-array>\n" + + "<byte-array name=\"AllowedPairwiseCiphers\" num=\"0\"></byte-array>\n" + + "<boolean name=\"Shared\" value=\"%s\" />\n" + + "<null name=\"FQDN\" />\n" + + "<null name=\"ProviderFriendlyName\" />\n" + + "<null name=\"LinkedNetworksList\" />\n" + + "<null name=\"DefaultGwMacAddress\" />\n" + + "<boolean name=\"ValidatedInternetAccess\" value=\"false\" />\n" + + "<boolean name=\"NoInternetAccessExpected\" value=\"false\" />\n" + + "<int name=\"UserApproved\" value=\"0\" />\n" + + "<boolean name=\"MeteredHint\" value=\"false\" />\n" + + "<boolean name=\"UseExternalScores\" value=\"false\" />\n" + + "<int name=\"NumAssociation\" value=\"0\" />\n" + + "<int name=\"CreatorUid\" value=\"%d\" />\n" + + "<null name=\"CreatorName\" />\n" + + "<null name=\"CreationTime\" />\n" + + "<int name=\"LastUpdateUid\" value=\"-1\" />\n" + + "<null name=\"LastUpdateName\" />\n" + + "<int name=\"LastConnectUid\" value=\"0\" />\n" + + "</WifiConfiguration>\n" + + "<NetworkStatus>\n" + + "<string name=\"SelectionStatus\">NETWORK_SELECTION_ENABLED</string>\n" + + "<string name=\"DisableReason\">NETWORK_SELECTION_ENABLE</string>\n" + + "<null name=\"ConnectChoice\" />\n" + + "<long name=\"ConnectChoiceTimeStamp\" value=\"-1\" />\n" + + "<boolean name=\"HasEverConnected\" value=\"false\" />\n" + + "</NetworkStatus>\n" + + "<IpConfiguration>\n" + + "<string name=\"IpAssignment\">DHCP</string>\n" + + "<string name=\"ProxySettings\">NONE</string>\n" + + "</IpConfiguration>\n" + + "<WifiEnterpriseConfiguration>\n" + + "<string name=\"Identity\"></string>\n" + + "<string name=\"AnonIdentity\"></string>\n" + + "<string name=\"Password\"></string>\n" + + "<string name=\"ClientCert\"></string>\n" + + "<string name=\"CaCert\"></string>\n" + + "<string name=\"SubjectMatch\"></string>\n" + + "<string name=\"Engine\"></string>\n" + + "<string name=\"EngineId\"></string>\n" + + "<string name=\"PrivateKeyId\"></string>\n" + + "<string name=\"AltSubjectMatch\"></string>\n" + + "<string name=\"DomSuffixMatch\"></string>\n" + + "<string name=\"CaPath\"></string>\n" + + "<int name=\"EapMethod\" value=\"2\" />\n" + + "<int name=\"Phase2Method\" value=\"0\" />\n" + + "</WifiEnterpriseConfiguration>\n" + + "</Network>\n"; + + private NetworkListStoreData mNetworkListStoreData; + + @Before + public void setUp() throws Exception { + mNetworkListStoreData = new NetworkListStoreData(); + } + + /** + * Helper function for serializing configuration data to a XML block. + * + * @param shared Flag indicating serializing shared or user configurations + * @return byte[] of the XML data + * @throws Exception + */ + private byte[] serializeData(boolean shared) throws Exception { + final XmlSerializer out = new FastXmlSerializer(); + final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + out.setOutput(outputStream, StandardCharsets.UTF_8.name()); + mNetworkListStoreData.serializeData(out, shared); + out.flush(); + return outputStream.toByteArray(); + } + + /** + * Helper function for parsing configuration data from a XML block. + * + * @param data XML data to parse from + * @param shared Flag indicating parsing of shared or user configurations + * @return List of WifiConfiguration parsed + * @throws Exception + */ + private List<WifiConfiguration> deserializeData(byte[] data, boolean shared) throws Exception { + final XmlPullParser in = Xml.newPullParser(); + final ByteArrayInputStream inputStream = new ByteArrayInputStream(data); + in.setInput(inputStream, StandardCharsets.UTF_8.name()); + mNetworkListStoreData.deserializeData(in, in.getDepth(), shared); + if (shared) { + return mNetworkListStoreData.getSharedConfigurations(); + } else { + return mNetworkListStoreData.getUserConfigurations(); + } + } + + /** + * Helper function for generating a network list for testing purpose. The network list + * will contained an open and an EAP network. + * + * @param shared Flag indicating shared network + * @return List of WifiConfiguration + */ + private List<WifiConfiguration> getTestNetworksConfig(boolean shared) { + WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork(); + openNetwork.shared = shared; + openNetwork.setIpConfiguration( + WifiConfigurationTestUtil.createDHCPIpConfigurationWithNoProxy()); + WifiConfiguration eapNetwork = WifiConfigurationTestUtil.createEapNetwork(); + eapNetwork.shared = shared; + eapNetwork.setIpConfiguration( + WifiConfigurationTestUtil.createDHCPIpConfigurationWithNoProxy()); + List<WifiConfiguration> networkList = new ArrayList<>(); + networkList.add(openNetwork); + networkList.add(eapNetwork); + return networkList; + } + + /** + * Helper function for generating XML block containing two networks, an open and an EAP + * network. + * + * @param openNetwork The WifiConfiguration for an open network + * @param eapNetwork The WifiConfiguration for an EAP network + * @return byte[] of the XML data + */ + private byte[] getTestNetworksXmlBytes(WifiConfiguration openNetwork, + WifiConfiguration eapNetwork) { + String openNetworkXml = String.format(SINGLE_OPEN_NETWORK_DATA_XML_STRING_FORMAT, + openNetwork.configKey().replaceAll("\"", """), + openNetwork.SSID.replaceAll("\"", """), + openNetwork.shared, openNetwork.creatorUid); + String eapNetworkXml = String.format(SINGLE_EAP_NETWORK_DATA_XML_STRING_FORMAT, + eapNetwork.configKey().replaceAll("\"", """), + eapNetwork.SSID.replaceAll("\"", """), + eapNetwork.shared, eapNetwork.creatorUid); + return (openNetworkXml + eapNetworkXml).getBytes(StandardCharsets.UTF_8); + } + + /** + * Verify that serializing the store data without any configuration doesn't cause any crash + * and no data should be serialized. + * + * @throws Exception + */ + @Test + public void serializeEmptyConfigs() throws Exception { + assertEquals(0, serializeData(true /* shared */).length); + assertEquals(0, serializeData(false /* shared */).length); + } + + /** + * Verify that parsing an empty data doesn't cause any crash and no configuration should + * be parsed. + * + * @throws Exception + */ + @Test + public void deserializeEmptyData() throws Exception { + assertTrue(deserializeData(new byte[0], true /* shared */).isEmpty()); + assertTrue(deserializeData(new byte[0], false /* shared */).isEmpty()); + } + + /** + * Verify that NetworkListStoreData does support share data. + * + * @throws Exception + */ + @Test + public void supportShareData() throws Exception { + assertTrue(mNetworkListStoreData.supportShareData()); + } + + /** + * Verify that the shared configurations (containing an open and an EAP network) are serialized + * correctly, matching the expected XML string. + * + * @throws Exception + */ + @Test + public void serializeSharedConfigurations() throws Exception { + List<WifiConfiguration> networkList = getTestNetworksConfig(true /* shared */); + mNetworkListStoreData.setSharedConfigurations(networkList); + byte[] expectedData = getTestNetworksXmlBytes(networkList.get(0), networkList.get(1)); + assertTrue(Arrays.equals(expectedData, serializeData(true /* shared */))); + } + + /** + * Verify that the shared configurations are parsed correctly from a XML string containing + * test networks (an open and an EAP network). + * @throws Exception + */ + @Test + public void deserializeSharedConfigurations() throws Exception { + List<WifiConfiguration> networkList = getTestNetworksConfig(true /* shared */); + byte[] xmlData = getTestNetworksXmlBytes(networkList.get(0), networkList.get(1)); + WifiConfigurationTestUtil.assertConfigurationsEqualForConfigStore( + networkList, deserializeData(xmlData, true /* shared */)); + } + + /** + * Verify that the user configurations (containing an open and an EAP network) are serialized + * correctly, matching the expected XML string. + * + * @throws Exception + */ + @Test + public void serializeUserConfigurations() throws Exception { + List<WifiConfiguration> networkList = getTestNetworksConfig(false /* shared */); + mNetworkListStoreData.setUserConfigurations(networkList); + byte[] expectedData = getTestNetworksXmlBytes(networkList.get(0), networkList.get(1)); + assertTrue(Arrays.equals(expectedData, serializeData(false /* shared */))); + } + + /** + * Verify that the user configurations are parsed correctly from a XML string containing + * test networks (an open and an EAP network). + * @throws Exception + */ + @Test + public void deserializeUserConfigurations() throws Exception { + List<WifiConfiguration> networkList = getTestNetworksConfig(false /* shared */); + byte[] xmlData = getTestNetworksXmlBytes(networkList.get(0), networkList.get(1)); + WifiConfigurationTestUtil.assertConfigurationsEqualForConfigStore( + networkList, deserializeData(xmlData, false /* shared */)); + } + + /** + * Verify that a XmlPullParserException will be thrown when parsing a <Network> block + * containing an unknown tag. + * + * @throws Exception + */ + @Test(expected = XmlPullParserException.class) + public void parseNetworkWithUnknownTag() throws Exception { + String configFormat = + "<Network>\n" + + "<WifiConfiguration>\n" + + "<string name=\"ConfigKey\">%s</string>\n" + + "<string name=\"SSID\">%s</string>\n" + + "<null name=\"BSSID\" />\n" + + "<null name=\"PreSharedKey\" />\n" + + "<null name=\"WEPKeys\" />\n" + + "<int name=\"WEPTxKeyIndex\" value=\"0\" />\n" + + "<boolean name=\"HiddenSSID\" value=\"false\" />\n" + + "<boolean name=\"RequirePMF\" value=\"false\" />\n" + + "<byte-array name=\"AllowedKeyMgmt\" num=\"1\">01</byte-array>\n" + + "<byte-array name=\"AllowedProtocols\" num=\"0\"></byte-array>\n" + + "<byte-array name=\"AllowedAuthAlgos\" num=\"0\"></byte-array>\n" + + "<byte-array name=\"AllowedGroupCiphers\" num=\"0\"></byte-array>\n" + + "<byte-array name=\"AllowedPairwiseCiphers\" num=\"0\"></byte-array>\n" + + "<boolean name=\"Shared\" value=\"%s\" />\n" + + "<null name=\"FQDN\" />\n" + + "<null name=\"ProviderFriendlyName\" />\n" + + "<null name=\"LinkedNetworksList\" />\n" + + "<null name=\"DefaultGwMacAddress\" />\n" + + "<boolean name=\"ValidatedInternetAccess\" value=\"false\" />\n" + + "<boolean name=\"NoInternetAccessExpected\" value=\"false\" />\n" + + "<int name=\"UserApproved\" value=\"0\" />\n" + + "<boolean name=\"MeteredHint\" value=\"false\" />\n" + + "<boolean name=\"UseExternalScores\" value=\"false\" />\n" + + "<int name=\"NumAssociation\" value=\"0\" />\n" + + "<int name=\"CreatorUid\" value=\"%d\" />\n" + + "<null name=\"CreatorName\" />\n" + + "<null name=\"CreationTime\" />\n" + + "<int name=\"LastUpdateUid\" value=\"-1\" />\n" + + "<null name=\"LastUpdateName\" />\n" + + "<int name=\"LastConnectUid\" value=\"0\" />\n" + + "</WifiConfiguration>\n" + + "<NetworkStatus>\n" + + "<string name=\"SelectionStatus\">NETWORK_SELECTION_ENABLED</string>\n" + + "<string name=\"DisableReason\">NETWORK_SELECTION_ENABLE</string>\n" + + "<null name=\"ConnectChoice\" />\n" + + "<long name=\"ConnectChoiceTimeStamp\" value=\"-1\" />\n" + + "<boolean name=\"HasEverConnected\" value=\"false\" />\n" + + "</NetworkStatus>\n" + + "<IpConfiguration>\n" + + "<string name=\"IpAssignment\">DHCP</string>\n" + + "<string name=\"ProxySettings\">NONE</string>\n" + + "</IpConfiguration>\n" + + "<Unknown>" // Unknown tag. + + "<int name=\"test\" value=\"0\" />\n" + + "</Unknown>" + + "</Network>\n"; + WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork(); + byte[] xmlData = String.format(configFormat, + openNetwork.configKey().replaceAll("\"", """), + openNetwork.SSID.replaceAll("\"", """), + openNetwork.shared, openNetwork.creatorUid).getBytes(StandardCharsets.UTF_8); + deserializeData(xmlData, true); + } + + /** + * Verify that a XmlPullParseException will be thrown when parsing a network configuration + * containing a mismatched config key. + * + * @throws Exception + */ + @Test(expected = XmlPullParserException.class) + public void parseNetworkWithMismatchConfigKey() throws Exception { + WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork(); + byte[] xmlData = String.format(SINGLE_OPEN_NETWORK_DATA_XML_STRING_FORMAT, + "InvalidConfigKey", + openNetwork.SSID.replaceAll("\"", """), + openNetwork.shared, openNetwork.creatorUid).getBytes(StandardCharsets.UTF_8); + deserializeData(xmlData, true); + } +} diff --git a/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java b/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java index 8c0163dda..892081423 100644 --- a/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java +++ b/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java @@ -40,6 +40,7 @@ import android.os.UserManager; import android.telephony.TelephonyManager; import android.test.suitebuilder.annotation.SmallTest; import android.text.TextUtils; +import android.util.Pair; import com.android.internal.R; import com.android.server.wifi.WifiConfigStoreLegacy.WifiConfigStoreDataLegacy; @@ -103,9 +104,12 @@ public class WifiConfigManagerTest { @Mock private PackageManager mPackageManager; @Mock private DevicePolicyManagerInternal mDevicePolicyManagerInternal; @Mock private WifiPermissionsWrapper mWifiPermissionsWrapper; + @Mock private NetworkListStoreData mNetworkListStoreData; + @Mock private DeletedEphemeralSsidsStoreData mDeletedEphemeralSsidsStoreData; private MockResources mResources; private InOrder mContextConfigStoreMockOrder; + private InOrder mNetworkListStoreDataMockOrder; private WifiConfigManager mWifiConfigManager; /** @@ -118,6 +122,7 @@ public class WifiConfigManagerTest { // Set up the inorder for verifications. This is needed to verify that the broadcasts, // store writes for network updates followed by network additions are in the expected order. mContextConfigStoreMockOrder = inOrder(mContext, mWifiConfigStore); + mNetworkListStoreDataMockOrder = inOrder(mNetworkListStoreData); // Set up the package name stuff & permission override. when(mContext.getPackageManager()).thenReturn(mPackageManager); @@ -173,9 +178,6 @@ public class WifiConfigManagerTest { .thenReturn(true); when(mWifiConfigStore.areStoresPresent()).thenReturn(true); - when(mWifiConfigStore.read()) - .thenReturn(new WifiConfigStoreData(new ArrayList<WifiConfiguration>(), - new ArrayList<WifiConfiguration>(), new HashSet<String>())); when(mDevicePolicyManagerInternal.isActiveAdminWithPolicy(anyInt(), anyInt())) .thenReturn(false); @@ -285,9 +287,13 @@ public class WifiConfigManagerTest { assertTrue(addNetworkToWifiConfigManager(openNetwork).isSuccess()); // The open network addition should trigger a store write. - WifiConfigStoreData storeData = captureWriteStoreData(); - assertFalse(isNetworkInConfigStoreData(ephemeralNetwork, storeData)); - assertTrue(isNetworkInConfigStoreData(openNetwork, storeData)); + Pair<List<WifiConfiguration>, List<WifiConfiguration>> networkListStoreData = + captureWriteNetworksListStoreData(); + List<WifiConfiguration> networkList = new ArrayList<>(); + networkList.addAll(networkListStoreData.first); + networkList.addAll(networkListStoreData.second); + assertFalse(isNetworkInConfigStoreData(ephemeralNetwork, networkList)); + assertTrue(isNetworkInConfigStoreData(openNetwork, networkList)); } /** @@ -1822,10 +1828,9 @@ public class WifiConfigManagerTest { add(user1Network); } }; - WifiConfigStoreData loadStoreData = - new WifiConfigStoreData(sharedNetworks, user1Networks, new HashSet<String>()); - when(mWifiConfigStore.read()).thenReturn(loadStoreData); + setupStoreDataForRead(sharedNetworks, user1Networks, new HashSet<String>()); assertTrue(mWifiConfigManager.loadFromStore()); + verify(mWifiConfigStore).read(); // Fetch the network ID's assigned to the shared networks initially. int sharedNetwork1Id = WifiConfiguration.INVALID_NETWORK_ID; @@ -1848,14 +1853,11 @@ public class WifiConfigManagerTest { add(user2Network); } }; - WifiConfigStoreData newUserStoreData = - new WifiConfigStoreData(new ArrayList<WifiConfiguration>(), user2Networks, - new HashSet<String>()); - when(mWifiConfigStore.switchUserStoreAndRead(any(WifiConfigStore.StoreFile.class))) - .thenReturn(newUserStoreData); + setupStoreDataForUserRead(user2Networks, new HashSet<String>()); // Now switch the user to user 2 and ensure that shared network's IDs have not changed. when(mUserManager.isUserUnlockingOrUnlocked(user2)).thenReturn(true); mWifiConfigManager.handleUserSwitch(user2); + verify(mWifiConfigStore).switchUserStoreAndRead(any(WifiConfigStore.StoreFile.class)); // Again fetch the network ID's assigned to the shared networks and ensure they have not // changed. @@ -1908,10 +1910,9 @@ public class WifiConfigManagerTest { add(user1Network); } }; - WifiConfigStoreData loadStoreData = - new WifiConfigStoreData(sharedNetworks, user1Networks, new HashSet<String>()); - when(mWifiConfigStore.read()).thenReturn(loadStoreData); + setupStoreDataForRead(sharedNetworks, user1Networks, new HashSet<String>()); assertTrue(mWifiConfigManager.loadFromStore()); + verify(mWifiConfigStore).read(); // Fetch the network ID assigned to the user 1 network initially. int user1NetworkId = WifiConfiguration.INVALID_NETWORK_ID; @@ -1929,14 +1930,11 @@ public class WifiConfigManagerTest { add(user2Network); } }; - WifiConfigStoreData newUserStoreData = - new WifiConfigStoreData(new ArrayList<WifiConfiguration>(), user2Networks, - new HashSet<String>()); - when(mWifiConfigStore.switchUserStoreAndRead(any(WifiConfigStore.StoreFile.class))) - .thenReturn(newUserStoreData); + setupStoreDataForUserRead(user2Networks, new HashSet<String>()); // Now switch the user to user 2 and ensure that user 1's private network has been removed. when(mUserManager.isUserUnlockingOrUnlocked(user2)).thenReturn(true); Set<Integer> removedNetworks = mWifiConfigManager.handleUserSwitch(user2); + verify(mWifiConfigStore).switchUserStoreAndRead(any(WifiConfigStore.StoreFile.class)); assertTrue((removedNetworks.size() == 1) && (removedNetworks.contains(user1NetworkId))); // Set the expected networks to be |sharedNetwork| and |user2Network|. @@ -1983,11 +1981,10 @@ public class WifiConfigManagerTest { add(sharedNetwork); } }; - WifiConfigStoreData loadStoreData = - new WifiConfigStoreData(sharedNetworks, new ArrayList<WifiConfiguration>(), - new HashSet<String>()); - when(mWifiConfigStore.read()).thenReturn(loadStoreData); + setupStoreDataForRead(sharedNetworks, new ArrayList<WifiConfiguration>(), + new HashSet<String>()); assertTrue(mWifiConfigManager.loadFromStore()); + verify(mWifiConfigStore).read(); // Set up the user 2 store data that is loaded at user switch. List<WifiConfiguration> user2Networks = new ArrayList<WifiConfiguration>() { @@ -1995,14 +1992,11 @@ public class WifiConfigManagerTest { add(user2Network); } }; - WifiConfigStoreData newUserStoreData = - new WifiConfigStoreData(new ArrayList<WifiConfiguration>(), user2Networks, - new HashSet<String>()); - when(mWifiConfigStore.switchUserStoreAndRead(any(WifiConfigStore.StoreFile.class))) - .thenReturn(newUserStoreData); + setupStoreDataForUserRead(user2Networks, new HashSet<String>()); // Now switch the user to user 2 and ensure that no private network has been removed. when(mUserManager.isUserUnlockingOrUnlocked(user2)).thenReturn(true); Set<Integer> removedNetworks = mWifiConfigManager.handleUserSwitch(user2); + verify(mWifiConfigStore).switchUserStoreAndRead(any(WifiConfigStore.StoreFile.class)); assertTrue(removedNetworks.isEmpty()); } @@ -2040,11 +2034,10 @@ public class WifiConfigManagerTest { add(user2Network); } }; - WifiConfigStoreData loadStoreData = - new WifiConfigStoreData(sharedNetworks, new ArrayList<WifiConfiguration>(), - new HashSet<String>()); - when(mWifiConfigStore.read()).thenReturn(loadStoreData); + setupStoreDataForRead(sharedNetworks, new ArrayList<WifiConfiguration>(), + new HashSet<String>()); assertTrue(mWifiConfigManager.loadFromStore()); + verify(mWifiConfigStore).read(); // Set up the user store data that is loaded at user unlock. List<WifiConfiguration> userNetworks = new ArrayList<WifiConfiguration>() { @@ -2052,19 +2045,17 @@ public class WifiConfigManagerTest { add(user1Network); } }; - WifiConfigStoreData unlockLoadStoreData = - new WifiConfigStoreData(new ArrayList<WifiConfiguration>(), userNetworks, - new HashSet<String>()); - when(mWifiConfigStore.switchUserStoreAndRead(any(WifiConfigStore.StoreFile.class))) - .thenReturn(unlockLoadStoreData); + setupStoreDataForUserRead(userNetworks, new HashSet<String>()); mWifiConfigManager.handleUserUnlock(user1); + verify(mWifiConfigStore).switchUserStoreAndRead(any(WifiConfigStore.StoreFile.class)); // Capture the written data for the user 1 and ensure that it corresponds to what was // setup. - WifiConfigStoreData writtenStoreData = captureWriteStoreData(); + Pair<List<WifiConfiguration>, List<WifiConfiguration>> writtenNetworkList = + captureWriteNetworksListStoreData(); WifiConfigurationTestUtil.assertConfigurationsEqualForConfigManagerAddOrUpdate( - sharedNetworks, writtenStoreData.getSharedConfigurations()); + sharedNetworks, writtenNetworkList.first); WifiConfigurationTestUtil.assertConfigurationsEqualForConfigManagerAddOrUpdate( - userNetworks, writtenStoreData.getUserConfigurations()); + userNetworks, writtenNetworkList.second); // Now switch the user to user2 and ensure that user 2's private network has been moved to // the user store. @@ -2087,19 +2078,19 @@ public class WifiConfigManagerTest { }; // Capture the first written data triggered for saving the old user's network // configurations. - writtenStoreData = captureWriteStoreData(); + writtenNetworkList = captureWriteNetworksListStoreData(); WifiConfigurationTestUtil.assertConfigurationsEqualForConfigManagerAddOrUpdate( - sharedNetworks, writtenStoreData.getSharedConfigurations()); + sharedNetworks, writtenNetworkList.first); WifiConfigurationTestUtil.assertConfigurationsEqualForConfigManagerAddOrUpdate( - userNetworks, writtenStoreData.getUserConfigurations()); + userNetworks, writtenNetworkList.second); // Now capture the next written data triggered after the switch and ensure that user 2's // network is now in user store data. - writtenStoreData = captureWriteStoreData(); + writtenNetworkList = captureWriteNetworksListStoreData(); WifiConfigurationTestUtil.assertConfigurationsEqualForConfigManagerAddOrUpdate( - expectedSharedNetworks, writtenStoreData.getSharedConfigurations()); + expectedSharedNetworks, writtenNetworkList.first); WifiConfigurationTestUtil.assertConfigurationsEqualForConfigManagerAddOrUpdate( - expectedUserNetworks, writtenStoreData.getUserConfigurations()); + expectedUserNetworks, writtenNetworkList.second); } /** @@ -2116,11 +2107,7 @@ public class WifiConfigManagerTest { // Set up the internal data first. assertTrue(mWifiConfigManager.loadFromStore()); - when(mWifiConfigStore.switchUserStoreAndRead(any(WifiConfigStore.StoreFile.class))) - .thenReturn(new WifiConfigStoreData( - new ArrayList<WifiConfiguration>(), new ArrayList<WifiConfiguration>(), - new HashSet<String>())); - + setupStoreDataForUserRead(new ArrayList<WifiConfiguration>(), new HashSet<String>()); // user2 is unlocked and switched to foreground. when(mUserManager.isUserUnlockingOrUnlocked(user2)).thenReturn(true); mWifiConfigManager.handleUserSwitch(user2); @@ -2155,11 +2142,7 @@ public class WifiConfigManagerTest { mContextConfigStoreMockOrder.verify(mWifiConfigStore, never()) .switchUserStoreAndRead(any(WifiConfigStore.StoreFile.class)); - when(mWifiConfigStore.switchUserStoreAndRead(any(WifiConfigStore.StoreFile.class))) - .thenReturn(new WifiConfigStoreData( - new ArrayList<WifiConfiguration>(), new ArrayList<WifiConfiguration>(), - new HashSet<String>())); - + setupStoreDataForUserRead(new ArrayList<WifiConfiguration>(), new HashSet<String>()); // Unlock the user2 and ensure that we read the data now. mWifiConfigManager.handleUserUnlock(user2); mContextConfigStoreMockOrder.verify(mWifiConfigStore) @@ -2186,8 +2169,7 @@ public class WifiConfigManagerTest { mWifiConfigManager.handleUserStop(user1); mContextConfigStoreMockOrder.verify(mWifiConfigStore, never()) .switchUserStoreAndRead(any(WifiConfigStore.StoreFile.class)); - mContextConfigStoreMockOrder.verify(mWifiConfigStore).write( - anyBoolean(), any(WifiConfigStoreData.class)); + mContextConfigStoreMockOrder.verify(mWifiConfigStore).write(anyBoolean()); } /** @@ -2201,23 +2183,17 @@ public class WifiConfigManagerTest { // Set up the internal data first. assertTrue(mWifiConfigManager.loadFromStore()); mContextConfigStoreMockOrder.verify(mWifiConfigStore).read(); - mContextConfigStoreMockOrder.verify(mWifiConfigStore, never()) - .write(anyBoolean(), any(WifiConfigStoreData.class)); + mContextConfigStoreMockOrder.verify(mWifiConfigStore, never()).write(anyBoolean()); mContextConfigStoreMockOrder.verify(mWifiConfigStore, never()) .switchUserStoreAndRead(any(WifiConfigStore.StoreFile.class)); - when(mWifiConfigStore.switchUserStoreAndRead(any(WifiConfigStore.StoreFile.class))) - .thenReturn(new WifiConfigStoreData( - new ArrayList<WifiConfiguration>(), new ArrayList<WifiConfiguration>(), - new HashSet<String>())); - + setupStoreDataForUserRead(new ArrayList<WifiConfiguration>(), new HashSet<String>()); // Unlock the user1 (default user) for the first time and ensure that we read the data. mWifiConfigManager.handleUserUnlock(user1); mContextConfigStoreMockOrder.verify(mWifiConfigStore, never()).read(); mContextConfigStoreMockOrder.verify(mWifiConfigStore) .switchUserStoreAndRead(any(WifiConfigStore.StoreFile.class)); - mContextConfigStoreMockOrder.verify(mWifiConfigStore) - .write(anyBoolean(), any(WifiConfigStoreData.class)); + mContextConfigStoreMockOrder.verify(mWifiConfigStore).write(anyBoolean()); } /** @@ -2233,23 +2209,17 @@ public class WifiConfigManagerTest { // data. mWifiConfigManager.handleUserUnlock(user1); mContextConfigStoreMockOrder.verify(mWifiConfigStore, never()).read(); - mContextConfigStoreMockOrder.verify(mWifiConfigStore, never()) - .write(anyBoolean(), any(WifiConfigStoreData.class)); + mContextConfigStoreMockOrder.verify(mWifiConfigStore, never()).write(anyBoolean()); mContextConfigStoreMockOrder.verify(mWifiConfigStore, never()) .switchUserStoreAndRead(any(WifiConfigStore.StoreFile.class)); - when(mWifiConfigStore.switchUserStoreAndRead(any(WifiConfigStore.StoreFile.class))) - .thenReturn(new WifiConfigStoreData( - new ArrayList<WifiConfiguration>(), new ArrayList<WifiConfiguration>(), - new HashSet<String>())); - + setupStoreDataForUserRead(new ArrayList<WifiConfiguration>(), new HashSet<String>()); // Read from store now. assertTrue(mWifiConfigManager.loadFromStore()); mContextConfigStoreMockOrder.verify(mWifiConfigStore).read(); mContextConfigStoreMockOrder.verify(mWifiConfigStore) .switchUserStoreAndRead(any(WifiConfigStore.StoreFile.class)); - mContextConfigStoreMockOrder.verify(mWifiConfigStore) - .write(anyBoolean(), any(WifiConfigStoreData.class)); + mContextConfigStoreMockOrder.verify(mWifiConfigStore).write(anyBoolean()); } /** @@ -2265,11 +2235,7 @@ public class WifiConfigManagerTest { // Set up the internal data first. assertTrue(mWifiConfigManager.loadFromStore()); - when(mWifiConfigStore.switchUserStoreAndRead(any(WifiConfigStore.StoreFile.class))) - .thenReturn(new WifiConfigStoreData( - new ArrayList<WifiConfiguration>(), new ArrayList<WifiConfiguration>(), - new HashSet<String>())); - + setupStoreDataForUserRead(new ArrayList<WifiConfiguration>(), new HashSet<String>()); // user2 is unlocked and switched to foreground. when(mUserManager.isUserUnlockingOrUnlocked(user2)).thenReturn(true); mWifiConfigManager.handleUserSwitch(user2); @@ -2297,8 +2263,7 @@ public class WifiConfigManagerTest { mWifiConfigManager.handleUserSwitch(user2); mContextConfigStoreMockOrder.verify(mWifiConfigStore, never()) .switchUserStoreAndRead(any(WifiConfigStore.StoreFile.class)); - mContextConfigStoreMockOrder.verify(mWifiConfigStore, never()) - .write(anyBoolean(), any(WifiConfigStoreData.class)); + mContextConfigStoreMockOrder.verify(mWifiConfigStore, never()).write(anyBoolean()); } /** @@ -2314,8 +2279,7 @@ public class WifiConfigManagerTest { mWifiConfigManager.handleUserUnlock(user1); mContextConfigStoreMockOrder.verify(mWifiConfigStore, never()) .switchUserStoreAndRead(any(WifiConfigStore.StoreFile.class)); - mContextConfigStoreMockOrder.verify(mWifiConfigStore, never()) - .write(anyBoolean(), any(WifiConfigStoreData.class)); + mContextConfigStoreMockOrder.verify(mWifiConfigStore, never()).write(anyBoolean()); } /** @@ -2439,10 +2403,7 @@ public class WifiConfigManagerTest { verify(mWifiConfigStore, never()).read(); verify(mWifiConfigStoreLegacy, never()).read(); - when(mWifiConfigStore.switchUserStoreAndRead(any(WifiConfigStore.StoreFile.class))) - .thenReturn(new WifiConfigStoreData( - new ArrayList<WifiConfiguration>(), new ArrayList<WifiConfiguration>(), - new HashSet<String>())); + setupStoreDataForUserRead(new ArrayList<WifiConfiguration>(), new HashSet<String>()); // Now switch the user to user 2. when(mUserManager.isUserUnlockingOrUnlocked(user2)).thenReturn(true); mWifiConfigManager.handleUserSwitch(user2); @@ -2563,8 +2524,7 @@ public class WifiConfigManagerTest { // This should have triggered 2 buffered writes. 1 for setting the connect choice, 1 for // clearing it after network removal. - mContextConfigStoreMockOrder.verify(mWifiConfigStore, times(2)) - .write(eq(false), any(WifiConfigStoreData.class)); + mContextConfigStoreMockOrder.verify(mWifiConfigStore, times(2)).write(eq(false)); } /** @@ -3147,7 +3107,8 @@ public class WifiConfigManagerTest { new WifiConfigManager( mContext, mFrameworkFacade, mClock, mUserManager, mTelephonyManager, mWifiKeyStore, mWifiConfigStore, mWifiConfigStoreLegacy, - mWifiPermissionsWrapper); + mWifiPermissionsWrapper, mNetworkListStoreData, + mDeletedEphemeralSsidsStoreData); mWifiConfigManager.enableVerboseLogging(1); } @@ -3270,15 +3231,22 @@ public class WifiConfigManagerTest { } /** - * Helper method to capture the store data written in WifiConfigStore.write() method. + * Helper method to capture the networks list store data that will be written by + * WifiConfigStore.write() method. */ - private WifiConfigStoreData captureWriteStoreData() { + private Pair<List<WifiConfiguration>, List<WifiConfiguration>> + captureWriteNetworksListStoreData() { try { - ArgumentCaptor<WifiConfigStoreData> storeDataCaptor = - ArgumentCaptor.forClass(WifiConfigStoreData.class); - mContextConfigStoreMockOrder.verify(mWifiConfigStore) - .write(anyBoolean(), storeDataCaptor.capture()); - return storeDataCaptor.getValue(); + ArgumentCaptor<ArrayList> sharedConfigsCaptor = + ArgumentCaptor.forClass(ArrayList.class); + ArgumentCaptor<ArrayList> userConfigsCaptor = + ArgumentCaptor.forClass(ArrayList.class); + mNetworkListStoreDataMockOrder.verify(mNetworkListStoreData) + .setSharedConfigurations(sharedConfigsCaptor.capture()); + mNetworkListStoreDataMockOrder.verify(mNetworkListStoreData) + .setUserConfigurations(userConfigsCaptor.capture()); + mContextConfigStoreMockOrder.verify(mWifiConfigStore).write(anyBoolean()); + return Pair.create(sharedConfigsCaptor.getValue(), userConfigsCaptor.getValue()); } catch (Exception e) { fail("Exception encountered during write " + e); } @@ -3289,20 +3257,24 @@ public class WifiConfigManagerTest { * Returns whether the provided network was in the store data or not. */ private boolean isNetworkInConfigStoreData(WifiConfiguration configuration) { - WifiConfigStoreData storeData = captureWriteStoreData(); - if (storeData == null) { + Pair<List<WifiConfiguration>, List<WifiConfiguration>> networkListStoreData = + captureWriteNetworksListStoreData(); + if (networkListStoreData == null) { return false; } - return isNetworkInConfigStoreData(configuration, storeData); + List<WifiConfiguration> networkList = new ArrayList<>(); + networkList.addAll(networkListStoreData.first); + networkList.addAll(networkListStoreData.second); + return isNetworkInConfigStoreData(configuration, networkList); } /** * Returns whether the provided network was in the store data or not. */ private boolean isNetworkInConfigStoreData( - WifiConfiguration configuration, WifiConfigStoreData storeData) { + WifiConfiguration configuration, List<WifiConfiguration> networkList) { boolean foundNetworkInStoreData = false; - for (WifiConfiguration retrievedConfig : storeData.getConfigurations()) { + for (WifiConfiguration retrievedConfig : networkList) { if (retrievedConfig.configKey().equals(configuration.configKey())) { foundNetworkInStoreData = true; break; @@ -3312,6 +3284,28 @@ public class WifiConfigManagerTest { } /** + * Setup expectations for WifiNetworksListStoreData and DeletedEphemeralSsidsStoreData + * after WifiConfigStore#read. + */ + private void setupStoreDataForRead(List<WifiConfiguration> sharedConfigurations, + List<WifiConfiguration> userConfigurations, Set<String> deletedEphemeralSsids) { + when(mNetworkListStoreData.getSharedConfigurations()) + .thenReturn(sharedConfigurations); + when(mNetworkListStoreData.getUserConfigurations()).thenReturn(userConfigurations); + when(mDeletedEphemeralSsidsStoreData.getSsidList()).thenReturn(deletedEphemeralSsids); + } + + /** + * Setup expectations for WifiNetworksListStoreData and DeletedEphemeralSsidsStoreData + * after WifiConfigStore#switchUserStoreAndRead. + */ + private void setupStoreDataForUserRead(List<WifiConfiguration> userConfigurations, + Set<String> deletedEphemeralSsids) { + when(mNetworkListStoreData.getUserConfigurations()).thenReturn(userConfigurations); + when(mDeletedEphemeralSsidsStoreData.getSsidList()).thenReturn(deletedEphemeralSsids); + } + + /** * Verifies that the provided network was not present in the last config store write. */ private void verifyNetworkNotInConfigStoreData(WifiConfiguration configuration) { @@ -3441,8 +3435,7 @@ public class WifiConfigManagerTest { verifyNetworkAddBroadcast(configuration); // Ensure that the write was not invoked for ephemeral network addition. - mContextConfigStoreMockOrder.verify(mWifiConfigStore, never()) - .write(anyBoolean(), any(WifiConfigStoreData.class)); + mContextConfigStoreMockOrder.verify(mWifiConfigStore, never()).write(anyBoolean()); return result; } @@ -3462,8 +3455,7 @@ public class WifiConfigManagerTest { any(WifiConfiguration.class)); verifyNetworkAddBroadcast(configuration); // Ensure that the write was not invoked for Passpoint network addition. - mContextConfigStoreMockOrder.verify(mWifiConfigStore, never()) - .write(anyBoolean(), any(WifiConfigStoreData.class)); + mContextConfigStoreMockOrder.verify(mWifiConfigStore, never()).write(anyBoolean()); return result; } @@ -3541,8 +3533,7 @@ public class WifiConfigManagerTest { verifyNetworkRemoveBroadcast(configuration); // Ensure that the write was not invoked for ephemeral network remove. - mContextConfigStoreMockOrder.verify(mWifiConfigStore, never()) - .write(anyBoolean(), any(WifiConfigStoreData.class)); + mContextConfigStoreMockOrder.verify(mWifiConfigStore, never()).write(anyBoolean()); } /** @@ -3556,8 +3547,7 @@ public class WifiConfigManagerTest { verify(mWifiKeyStore, never()).removeKeys(any(WifiEnterpriseConfig.class)); verifyNetworkRemoveBroadcast(configuration); // Ensure that the write was not invoked for Passpoint network remove. - mContextConfigStoreMockOrder.verify(mWifiConfigStore, never()) - .write(anyBoolean(), any(WifiConfigStoreData.class)); + mContextConfigStoreMockOrder.verify(mWifiConfigStore, never()).write(anyBoolean()); } /** diff --git a/tests/wifitests/src/com/android/server/wifi/WifiConfigStoreDataTest.java b/tests/wifitests/src/com/android/server/wifi/WifiConfigStoreDataTest.java deleted file mode 100644 index 3fa5d3dab..000000000 --- a/tests/wifitests/src/com/android/server/wifi/WifiConfigStoreDataTest.java +++ /dev/null @@ -1,446 +0,0 @@ -/* - * 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.*; -import static org.mockito.Mockito.*; - -import android.net.wifi.WifiConfiguration; -import android.net.wifi.WifiEnterpriseConfig; -import android.net.wifi.WifiConfiguration.NetworkSelectionStatus; -import android.test.suitebuilder.annotation.SmallTest; - -import org.junit.Test; -import org.xmlpull.v1.XmlPullParserException; - -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Random; -import java.util.Set; - -/** - * Unit tests for {@link com.android.server.wifi.WifiConfigStoreData}. - */ -@SmallTest -public class WifiConfigStoreDataTest { - - private static final String TEST_SSID = "WifiConfigStoreDataSSID_"; - private static final String TEST_CONNECT_CHOICE = "XmlUtilConnectChoice"; - private static final long TEST_CONNECT_CHOICE_TIMESTAMP = 0x4566; - private static final Set<String> TEST_DELETED_EPHEMERAL_LIST = new HashSet<String>() { - { - add("\"" + TEST_SSID + "1\""); - add("\"" + TEST_SSID + "2\""); - } - }; - private static final String SINGLE_OPEN_NETWORK_LIST_XML_STRING_FORMAT = - "<NetworkList>\n" - + "<Network>\n" - + "<WifiConfiguration>\n" - + "<string name=\"ConfigKey\">%s</string>\n" - + "<string name=\"SSID\">%s</string>\n" - + "<null name=\"BSSID\" />\n" - + "<null name=\"PreSharedKey\" />\n" - + "<null name=\"WEPKeys\" />\n" - + "<int name=\"WEPTxKeyIndex\" value=\"0\" />\n" - + "<boolean name=\"HiddenSSID\" value=\"false\" />\n" - + "<boolean name=\"RequirePMF\" value=\"false\" />\n" - + "<byte-array name=\"AllowedKeyMgmt\" num=\"1\">01</byte-array>\n" - + "<byte-array name=\"AllowedProtocols\" num=\"0\"></byte-array>\n" - + "<byte-array name=\"AllowedAuthAlgos\" num=\"0\"></byte-array>\n" - + "<byte-array name=\"AllowedGroupCiphers\" num=\"0\"></byte-array>\n" - + "<byte-array name=\"AllowedPairwiseCiphers\" num=\"0\"></byte-array>\n" - + "<boolean name=\"Shared\" value=\"%s\" />\n" - + "<null name=\"FQDN\" />\n" - + "<null name=\"ProviderFriendlyName\" />\n" - + "<null name=\"LinkedNetworksList\" />\n" - + "<null name=\"DefaultGwMacAddress\" />\n" - + "<boolean name=\"ValidatedInternetAccess\" value=\"false\" />\n" - + "<boolean name=\"NoInternetAccessExpected\" value=\"false\" />\n" - + "<int name=\"UserApproved\" value=\"0\" />\n" - + "<boolean name=\"MeteredHint\" value=\"false\" />\n" - + "<boolean name=\"UseExternalScores\" value=\"false\" />\n" - + "<int name=\"NumAssociation\" value=\"0\" />\n" - + "<int name=\"CreatorUid\" value=\"%d\" />\n" - + "<null name=\"CreatorName\" />\n" - + "<null name=\"CreationTime\" />\n" - + "<int name=\"LastUpdateUid\" value=\"-1\" />\n" - + "<null name=\"LastUpdateName\" />\n" - + "<int name=\"LastConnectUid\" value=\"0\" />\n" - + "</WifiConfiguration>\n" - + "<NetworkStatus>\n" - + "<string name=\"SelectionStatus\">NETWORK_SELECTION_ENABLED</string>\n" - + "<string name=\"DisableReason\">NETWORK_SELECTION_ENABLE</string>\n" - + "<null name=\"ConnectChoice\" />\n" - + "<long name=\"ConnectChoiceTimeStamp\" value=\"-1\" />\n" - + "<boolean name=\"HasEverConnected\" value=\"false\" />\n" - + "</NetworkStatus>\n" - + "<IpConfiguration>\n" - + "<string name=\"IpAssignment\">DHCP</string>\n" - + "<string name=\"ProxySettings\">NONE</string>\n" - + "</IpConfiguration>\n" - + "</Network>\n" - + "</NetworkList>\n"; - private static final String SINGLE_OPEN_NETWORK_SHARED_DATA_XML_STRING_FORMAT = - "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" - + "<WifiConfigStoreData>\n" - + "<int name=\"Version\" value=\"1\" />\n" - + SINGLE_OPEN_NETWORK_LIST_XML_STRING_FORMAT - + "</WifiConfigStoreData>\n"; - private static final String SINGLE_OPEN_NETWORK_USER_DATA_XML_STRING_FORMAT = - "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" - + "<WifiConfigStoreData>\n" - + "<int name=\"Version\" value=\"1\" />\n" - + SINGLE_OPEN_NETWORK_LIST_XML_STRING_FORMAT - + "<DeletedEphemeralSSIDList>\n" - + "<set name=\"SSIDList\" />\n" - + "</DeletedEphemeralSSIDList>\n" - + "</WifiConfigStoreData>\n"; - - /** - * Asserts that the 2 config store data are equal. - */ - public static void assertConfigStoreDataEqual( - WifiConfigStoreData expected, WifiConfigStoreData actual) { - WifiConfigurationTestUtil.assertConfigurationsEqualForConfigStore( - expected.getConfigurations(), actual.getConfigurations()); - assertEquals(expected.getDeletedEphemeralSSIDs(), actual.getDeletedEphemeralSSIDs()); - } - - /** - * Verify that multiple shared networks with different credential types and IpConfiguration - * types are serialized and deserialized correctly. - */ - @Test - public void testMultipleNetworkAllShared() - throws XmlPullParserException, IOException { - List<WifiConfiguration> configurations = createNetworks(true); - serializeDeserializeConfigStoreData(configurations, new ArrayList<WifiConfiguration>()); - } - - /** - * Verify that multiple user networks with different credential types and IpConfiguration - * types are serialized and deserialized correctly. - */ - @Test - public void testMultipleNetworksAllUser() - throws XmlPullParserException, IOException { - List<WifiConfiguration> configurations = createNetworks(false); - serializeDeserializeConfigStoreData(new ArrayList<WifiConfiguration>(), configurations); - } - - /** - * Verify that multiple networks with different credential types and IpConfiguration - * types are serialized and deserialized correctly when both user & shared networks are present. - */ - @Test - public void testMultipleNetworksSharedAndUserNetworks() - throws XmlPullParserException, IOException { - List<WifiConfiguration> configurations = createNetworks(); - // Let's split the list of networks into 2 and make all the networks in the first list - // shared and the second list all user networks. - int listSize = configurations.size(); - List<WifiConfiguration> sharedConfigurations = configurations.subList(0, listSize / 2); - List<WifiConfiguration> userConfigurations = configurations.subList(listSize / 2, listSize); - for (WifiConfiguration config : sharedConfigurations) { - config.shared = true; - } - for (WifiConfiguration config : userConfigurations) { - config.shared = false; - } - serializeDeserializeConfigStoreData(sharedConfigurations, userConfigurations); - } - - /** - * Verify that multiple shared networks with different credential types and IpConfiguration - * types are serialized and deserialized correctly when the shared data bytes are null in - * |parseRawData| method. - */ - @Test - public void testMultipleNetworksSharedDataNullInParseRawData() - throws XmlPullParserException, IOException { - List<WifiConfiguration> configurations = createNetworks(false); - serializeDeserializeConfigStoreData( - new ArrayList<WifiConfiguration>(), configurations, true, false); - } - - /** - * Verify that multiple shared networks with different credential types and IpConfiguration - * types are serialized and deserialized correctly when the user data bytes are null in - * |parseRawData| method. - */ - @Test - public void testMultipleNetworksUserDataNullInParseRawData() - throws XmlPullParserException, IOException { - List<WifiConfiguration> configurations = createNetworks(true); - serializeDeserializeConfigStoreData( - configurations, new ArrayList<WifiConfiguration>(), false, true); - } - - /** - * Verify that a network with invalid entepriseConfig data is serialized/deserialized - * correctly. - */ - @Test - public void testInvalidEnterpriseConfig() - throws XmlPullParserException, IOException { - WifiConfiguration eapNetwork = WifiConfigurationTestUtil.createEapNetwork(); - eapNetwork.enterpriseConfig = new WifiEnterpriseConfig(); - List<WifiConfiguration> configurations = Arrays.asList(eapNetwork); - serializeDeserializeConfigStoreData( - new ArrayList<WifiConfiguration>(), configurations, false, false); - } - - /** - * Verify that the manually populated xml string for is deserialized/serialized correctly. - * This generates a store data corresponding to the XML string and verifies that the string - * is indeed parsed correctly to the store data. - */ - @Test - public void testManualConfigStoreDataParse() { - WifiConfiguration sharedNetwork = WifiConfigurationTestUtil.createOpenNetwork(); - sharedNetwork.shared = true; - sharedNetwork.setIpConfiguration(WifiConfigurationTestUtil.createDHCPIpConfigurationWithNoProxy()); - WifiConfiguration userNetwork = WifiConfigurationTestUtil.createOpenNetwork(); - userNetwork.setIpConfiguration(WifiConfigurationTestUtil.createDHCPIpConfigurationWithNoProxy()); - userNetwork.shared = false; - - // Create the store data for comparison. - List<WifiConfiguration> sharedNetworks = new ArrayList<>(); - List<WifiConfiguration> userNetworks = new ArrayList<>(); - sharedNetworks.add(sharedNetwork); - userNetworks.add(userNetwork); - WifiConfigStoreData storeData = - new WifiConfigStoreData(sharedNetworks, userNetworks, new HashSet<String>()); - - String sharedStoreXmlString = - String.format(SINGLE_OPEN_NETWORK_SHARED_DATA_XML_STRING_FORMAT, - sharedNetwork.configKey().replaceAll("\"", """), - sharedNetwork.SSID.replaceAll("\"", """), - sharedNetwork.shared, sharedNetwork.creatorUid); - String userStoreXmlString = - String.format(SINGLE_OPEN_NETWORK_USER_DATA_XML_STRING_FORMAT, - userNetwork.configKey().replaceAll("\"", """), - userNetwork.SSID.replaceAll("\"", """), - userNetwork.shared, userNetwork.creatorUid); - byte[] rawSharedData = sharedStoreXmlString.getBytes(); - byte[] rawUserData = userStoreXmlString.getBytes(); - WifiConfigStoreData retrievedStoreData = null; - try { - retrievedStoreData = WifiConfigStoreData.parseRawData(rawSharedData, rawUserData); - } catch (Exception e) { - // Assert if an exception was raised. - fail("Error in parsing the xml data: " + e - + ". Shared data: " + sharedStoreXmlString - + ", User data: " + userStoreXmlString); - } - // Compare the retrieved config store data with the original. - assertConfigStoreDataEqual(storeData, retrievedStoreData); - - // Now convert the store data to XML bytes and compare the output with the expected string. - byte[] retrievedSharedStoreXmlBytes = null; - byte[] retrievedUserStoreXmlBytes = null; - try { - retrievedSharedStoreXmlBytes = retrievedStoreData.createSharedRawData(); - retrievedUserStoreXmlBytes = retrievedStoreData.createUserRawData(); - } catch (Exception e) { - // Assert if an exception was raised. - fail("Error in writing the xml data: " + e); - } - String retrievedSharedStoreXmlString = - new String(retrievedSharedStoreXmlBytes, StandardCharsets.UTF_8); - String retrievedUserStoreXmlString = - new String(retrievedUserStoreXmlBytes, StandardCharsets.UTF_8); - assertEquals("Retrieved: " + retrievedSharedStoreXmlString - + ", Expected: " + sharedStoreXmlString, - sharedStoreXmlString, retrievedSharedStoreXmlString); - assertEquals("Retrieved: " + retrievedUserStoreXmlString - + ", Expected: " + userStoreXmlString, - userStoreXmlString, retrievedUserStoreXmlString); - } - - /** - * Verify that XML with corrupted version provided to WifiConfigStoreData is ignored correctly. - */ - @Test - public void testCorruptVersionConfigStoreData() { - String storeDataAsString = - "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" - + "<WifiConfigStoreData>\n" - + "<int name=\"Version\" value=\"200\" />\n" - + "</WifiConfigStoreData>\n"; - byte[] rawData = storeDataAsString.getBytes(); - try { - WifiConfigStoreData storeData = WifiConfigStoreData.parseRawData(rawData, rawData); - } catch (Exception e) { - return; - } - // Assert if there was no exception was raised. - fail(); - } - - /** - * Verify that XML with no network list provided to WifiConfigStoreData is ignored correctly. - */ - @Test - public void testCorruptNetworkListConfigStoreData() { - String storeDataAsString = - "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" - + "<WifiConfigStoreData>\n" - + "<int name=\"Version\" value=\"1\" />\n" - + "</WifiConfigStoreData>\n"; - byte[] rawData = storeDataAsString.getBytes(); - try { - WifiConfigStoreData storeData = WifiConfigStoreData.parseRawData(rawData, rawData); - } catch (Exception e) { - return; - } - // Assert if there was no exception was raised. - fail(); - } - - /** - * Verify that any corrupted data provided to WifiConfigStoreData is ignored correctly. - */ - @Test - public void testRandomCorruptConfigStoreData() { - Random random = new Random(); - byte[] rawData = new byte[100]; - random.nextBytes(rawData); - try { - WifiConfigStoreData storeData = WifiConfigStoreData.parseRawData(rawData, rawData); - } catch (Exception e) { - return; - } - // Assert if there was no exception was raised. - fail(); - } - - /** - * Helper method to add 4 networks with different credential types, IpConfiguration - * types for all tests in the class. - * - * @return - */ - private List<WifiConfiguration> createNetworks() { - List<WifiConfiguration> configurations = new ArrayList<>(); - - WifiConfiguration wepNetwork = WifiConfigurationTestUtil.createWepNetwork(); - wepNetwork.setIpConfiguration(WifiConfigurationTestUtil.createDHCPIpConfigurationWithPacProxy()); - wepNetwork.getNetworkSelectionStatus().setNetworkSelectionStatus( - NetworkSelectionStatus.NETWORK_SELECTION_ENABLED); - configurations.add(wepNetwork); - - WifiConfiguration pskNetwork = WifiConfigurationTestUtil.createPskNetwork(); - pskNetwork.setIpConfiguration(WifiConfigurationTestUtil.createStaticIpConfigurationWithPacProxy()); - pskNetwork.getNetworkSelectionStatus().setNetworkSelectionStatus( - NetworkSelectionStatus.NETWORK_SELECTION_TEMPORARY_DISABLED); - pskNetwork.getNetworkSelectionStatus().setNetworkSelectionDisableReason( - NetworkSelectionStatus.DISABLED_ASSOCIATION_REJECTION); - configurations.add(pskNetwork); - - WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork(); - openNetwork.setIpConfiguration(WifiConfigurationTestUtil.createStaticIpConfigurationWithStaticProxy()); - openNetwork.getNetworkSelectionStatus().setNetworkSelectionStatus( - NetworkSelectionStatus.NETWORK_SELECTION_PERMANENTLY_DISABLED); - openNetwork.getNetworkSelectionStatus().setNetworkSelectionDisableReason( - NetworkSelectionStatus.DISABLED_BY_WIFI_MANAGER); - configurations.add(openNetwork); - - WifiConfiguration eapNetwork = WifiConfigurationTestUtil.createEapNetwork(); - eapNetwork.setIpConfiguration(WifiConfigurationTestUtil.createPartialStaticIpConfigurationWithPacProxy()); - eapNetwork.getNetworkSelectionStatus().setConnectChoice(TEST_CONNECT_CHOICE); - eapNetwork.getNetworkSelectionStatus().setConnectChoiceTimestamp( - TEST_CONNECT_CHOICE_TIMESTAMP); - eapNetwork.getNetworkSelectionStatus().setHasEverConnected(true); - configurations.add(eapNetwork); - - return configurations; - } - - private List<WifiConfiguration> createNetworks(boolean shared) { - List<WifiConfiguration> configurations = createNetworks(); - for (WifiConfiguration config : configurations) { - config.shared = shared; - } - return configurations; - } - - /** - * Helper method to serialize/deserialize store data. - */ - private void serializeDeserializeConfigStoreData( - List<WifiConfiguration> sharedConfigurations, - List<WifiConfiguration> userConfigurations) - throws XmlPullParserException, IOException { - serializeDeserializeConfigStoreData(sharedConfigurations, userConfigurations, false, false); - } - - /** - * Helper method to ensure the the provided config store data is serialized/deserialized - * correctly. - * This method serialize the provided config store data instance to raw bytes in XML format - * and then deserialzes the raw bytes back to a config store data instance. It then - * compares that the original config store data matches with the deserialzed instance. - * - * @param sharedConfigurations list of configurations to be added in the shared store data instance. - * @param userConfigurations list of configurations to be added in the user store data instance. - * @param setSharedDataNull whether to set the shared data to null to simulate the non-existence - * of the shared store file. - * @param setUserDataNull whether to set the user data to null to simulate the non-existence - * of the user store file. - */ - private void serializeDeserializeConfigStoreData( - List<WifiConfiguration> sharedConfigurations, - List<WifiConfiguration> userConfigurations, - boolean setSharedDataNull, boolean setUserDataNull) - throws XmlPullParserException, IOException { - // Will not work if both the flags are set because then we need to ignore the configuration - // list as well. - assertFalse(setSharedDataNull & setUserDataNull); - - Set<String> deletedEphemeralList; - if (setUserDataNull) { - deletedEphemeralList = new HashSet<>(); - } else { - deletedEphemeralList = TEST_DELETED_EPHEMERAL_LIST; - } - - // Serialize the data. - WifiConfigStoreData storeData = - new WifiConfigStoreData( - sharedConfigurations, userConfigurations, deletedEphemeralList); - - byte[] sharedDataBytes = null; - byte[] userDataBytes = null; - if (!setSharedDataNull) { - sharedDataBytes = storeData.createSharedRawData(); - } - if (!setUserDataNull) { - userDataBytes = storeData.createUserRawData(); - } - - // Deserialize the data. - WifiConfigStoreData retrievedStoreData = - WifiConfigStoreData.parseRawData(sharedDataBytes, userDataBytes); - assertConfigStoreDataEqual(storeData, retrievedStoreData); - } -} diff --git a/tests/wifitests/src/com/android/server/wifi/WifiConfigStoreTest.java b/tests/wifitests/src/com/android/server/wifi/WifiConfigStoreTest.java index f567ef6d5..b7e642c91 100644 --- a/tests/wifitests/src/com/android/server/wifi/WifiConfigStoreTest.java +++ b/tests/wifitests/src/com/android/server/wifi/WifiConfigStoreTest.java @@ -16,8 +16,6 @@ package com.android.server.wifi; -import static com.android.server.wifi.WifiConfigStoreDataTest.assertConfigStoreDataEqual; - import static org.junit.Assert.*; import static org.mockito.Mockito.*; @@ -28,23 +26,99 @@ import android.os.test.TestLooper; import android.test.suitebuilder.annotation.SmallTest; import com.android.server.wifi.WifiConfigStore.StoreFile; +import com.android.server.wifi.util.XmlUtil; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; 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.WifiConfigStore}. */ @SmallTest public class WifiConfigStoreTest { + // Store file content without any data. + private static final String EMPTY_FILE_CONTENT = + "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" + + "<WifiConfigStoreData>\n" + + "<int name=\"Version\" value=\"1\" />\n" + + "</WifiConfigStoreData>\n"; + + private static final String TEST_USER_DATA = "UserData"; + private static final String TEST_SHARE_DATA = "ShareData"; + + private static final String TEST_DATA_XML_STRING_FORMAT = + "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" + + "<WifiConfigStoreData>\n" + + "<int name=\"Version\" value=\"1\" />\n" + + "<NetworkList>\n" + + "<Network>\n" + + "<WifiConfiguration>\n" + + "<string name=\"ConfigKey\">%s</string>\n" + + "<string name=\"SSID\">%s</string>\n" + + "<null name=\"BSSID\" />\n" + + "<null name=\"PreSharedKey\" />\n" + + "<null name=\"WEPKeys\" />\n" + + "<int name=\"WEPTxKeyIndex\" value=\"0\" />\n" + + "<boolean name=\"HiddenSSID\" value=\"false\" />\n" + + "<boolean name=\"RequirePMF\" value=\"false\" />\n" + + "<byte-array name=\"AllowedKeyMgmt\" num=\"1\">01</byte-array>\n" + + "<byte-array name=\"AllowedProtocols\" num=\"0\"></byte-array>\n" + + "<byte-array name=\"AllowedAuthAlgos\" num=\"0\"></byte-array>\n" + + "<byte-array name=\"AllowedGroupCiphers\" num=\"0\"></byte-array>\n" + + "<byte-array name=\"AllowedPairwiseCiphers\" num=\"0\"></byte-array>\n" + + "<boolean name=\"Shared\" value=\"%s\" />\n" + + "<null name=\"FQDN\" />\n" + + "<null name=\"ProviderFriendlyName\" />\n" + + "<null name=\"LinkedNetworksList\" />\n" + + "<null name=\"DefaultGwMacAddress\" />\n" + + "<boolean name=\"ValidatedInternetAccess\" value=\"false\" />\n" + + "<boolean name=\"NoInternetAccessExpected\" value=\"false\" />\n" + + "<int name=\"UserApproved\" value=\"0\" />\n" + + "<boolean name=\"MeteredHint\" value=\"false\" />\n" + + "<boolean name=\"UseExternalScores\" value=\"false\" />\n" + + "<int name=\"NumAssociation\" value=\"0\" />\n" + + "<int name=\"CreatorUid\" value=\"%d\" />\n" + + "<null name=\"CreatorName\" />\n" + + "<null name=\"CreationTime\" />\n" + + "<int name=\"LastUpdateUid\" value=\"-1\" />\n" + + "<null name=\"LastUpdateName\" />\n" + + "<int name=\"LastConnectUid\" value=\"0\" />\n" + + "</WifiConfiguration>\n" + + "<NetworkStatus>\n" + + "<string name=\"SelectionStatus\">NETWORK_SELECTION_ENABLED</string>\n" + + "<string name=\"DisableReason\">NETWORK_SELECTION_ENABLE</string>\n" + + "<null name=\"ConnectChoice\" />\n" + + "<long name=\"ConnectChoiceTimeStamp\" value=\"-1\" />\n" + + "<boolean name=\"HasEverConnected\" value=\"false\" />\n" + + "</NetworkStatus>\n" + + "<IpConfiguration>\n" + + "<string name=\"IpAssignment\">DHCP</string>\n" + + "<string name=\"ProxySettings\">NONE</string>\n" + + "</IpConfiguration>\n" + + "</Network>\n" + + "</NetworkList>\n" + + "<DeletedEphemeralSSIDList>\n" + + "<set name=\"SSIDList\">\n" + + "<string>%s</string>\n" + + "</set>\n" + + "</DeletedEphemeralSSIDList>\n" + + "</WifiConfigStoreData>\n"; + // Test mocks @Mock private Context mContext; private TestAlarmManager mAlarmManager; @@ -52,6 +126,7 @@ public class WifiConfigStoreTest { @Mock private Clock mClock; private MockStoreFile mSharedStore; private MockStoreFile mUserStore; + private MockStoreData mStoreData; /** * Test instance of WifiConfigStore. @@ -69,6 +144,7 @@ public class WifiConfigStoreTest { .thenReturn(mAlarmManager.getAlarmManager()); mUserStore = new MockStoreFile(); mSharedStore = new MockStoreFile(); + mStoreData = new MockStoreData(); } /** @@ -79,7 +155,6 @@ public class WifiConfigStoreTest { setupMocks(); mWifiConfigStore = new WifiConfigStore(mContext, mLooper.getLooper(), mClock, mSharedStore); - // Enable verbose logging before tests. mWifiConfigStore.enableVerboseLogging(true); } @@ -93,6 +168,27 @@ public class WifiConfigStoreTest { } /** + * Verify the contents of the config file with empty data. The data content should be the + * same as {@link #EMPTY_FILE_CONTENT}. + * + * @throws Exception + */ + @Test + public void testWriteWithEmptyData() throws Exception { + // Perform force write to both share and user store file. + mWifiConfigStore.switchUserStoreAndRead(mUserStore); + mWifiConfigStore.write(true); + + assertFalse(mAlarmManager.isPending(WifiConfigStore.BUFFERED_WRITE_ALARM_TAG)); + assertTrue(mSharedStore.isStoreWritten()); + assertTrue(mUserStore.isStoreWritten()); + assertTrue(Arrays.equals(EMPTY_FILE_CONTENT.getBytes(StandardCharsets.UTF_8), + mSharedStore.getStoreBytes())); + assertTrue(Arrays.equals(EMPTY_FILE_CONTENT.getBytes(StandardCharsets.UTF_8), + mUserStore.getStoreBytes())); + } + + /** * Tests the write API with the force flag set to true. * Expected behavior: This should trigger an immediate write to the store files and no alarms * should be started. @@ -100,7 +196,7 @@ public class WifiConfigStoreTest { @Test public void testForceWrite() throws Exception { mWifiConfigStore.switchUserStoreAndRead(mUserStore); - mWifiConfigStore.write(true, getEmptyStoreData()); + mWifiConfigStore.write(true); assertFalse(mAlarmManager.isPending(WifiConfigStore.BUFFERED_WRITE_ALARM_TAG)); assertTrue(mSharedStore.isStoreWritten()); @@ -114,7 +210,7 @@ public class WifiConfigStoreTest { @Test public void testBufferedWrite() throws Exception { mWifiConfigStore.switchUserStoreAndRead(mUserStore); - mWifiConfigStore.write(false, getEmptyStoreData()); + mWifiConfigStore.write(false); assertTrue(mAlarmManager.isPending(WifiConfigStore.BUFFERED_WRITE_ALARM_TAG)); assertFalse(mSharedStore.isStoreWritten()); @@ -134,30 +230,33 @@ public class WifiConfigStoreTest { */ @Test public void testForceWriteAfterBufferedWrite() throws Exception { - WifiConfigStoreData bufferedStoreData = createSingleOpenNetworkStoreData(); + // Register a test data container with bogus data. + mWifiConfigStore.registerStoreData(mStoreData); + mStoreData.setShareData("abcds"); + mStoreData.setUserData("asdfa"); + + // Perform buffered write for both user and share store file. mWifiConfigStore.switchUserStoreAndRead(mUserStore); - mWifiConfigStore.write(false, bufferedStoreData); + mWifiConfigStore.write(false); assertTrue(mAlarmManager.isPending(WifiConfigStore.BUFFERED_WRITE_ALARM_TAG)); assertFalse(mSharedStore.isStoreWritten()); assertFalse(mUserStore.isStoreWritten()); - // Now send a force write and ensure that the writes have been performed and alarms have - // been stopped. - WifiConfigStoreData forcedStoreData = createSinglePskNetworkStoreData(); - mWifiConfigStore.write(true, forcedStoreData); + // Update the container with new set of data. The send a force write and ensure that the + // writes have been performed and alarms have been stopped and updated data are written. + mStoreData.setUserData(TEST_USER_DATA); + mStoreData.setShareData(TEST_SHARE_DATA); + mWifiConfigStore.write(true); assertFalse(mAlarmManager.isPending(WifiConfigStore.BUFFERED_WRITE_ALARM_TAG)); assertTrue(mSharedStore.isStoreWritten()); assertTrue(mUserStore.isStoreWritten()); - // Now deserialize the data and ensure that the configuration retrieved matches the force - // write data. - WifiConfigStoreData retrievedStoreData = - WifiConfigStoreData.parseRawData( - mSharedStore.getStoreBytes(), mUserStore.getStoreBytes()); - - assertConfigStoreDataEqual(forcedStoreData, retrievedStoreData); + // Verify correct data are loaded to the data container after a read. + mWifiConfigStore.read(); + assertEquals(TEST_USER_DATA, mStoreData.getUserData()); + assertEquals(TEST_SHARE_DATA, mStoreData.getShareData()); } /** @@ -166,12 +265,25 @@ public class WifiConfigStoreTest { */ @Test public void testReadAfterWrite() throws Exception { - WifiConfigStoreData writeData = createSingleOpenNetworkStoreData(); + // Register data container. + mWifiConfigStore.registerStoreData(mStoreData); + + // Read both share and user config store. mWifiConfigStore.switchUserStoreAndRead(mUserStore); - mWifiConfigStore.write(true, writeData); - WifiConfigStoreData readData = mWifiConfigStore.read(); - assertConfigStoreDataEqual(writeData, readData); + // Verify no data is read. + assertNull(mStoreData.getUserData()); + assertNull(mStoreData.getShareData()); + + // Write share and user data. + mStoreData.setUserData(TEST_USER_DATA); + mStoreData.setShareData(TEST_SHARE_DATA); + mWifiConfigStore.write(true); + + // Read and verify the data content in the data container. + mWifiConfigStore.read(); + assertEquals(TEST_USER_DATA, mStoreData.getUserData()); + assertEquals(TEST_SHARE_DATA, mStoreData.getShareData()); } /** @@ -183,9 +295,13 @@ public class WifiConfigStoreTest { public void testReadWithNoStoreFile() throws Exception { // Reading the mock store without a write should simulate the file not found case because // |readRawData| would return null. + mWifiConfigStore.registerStoreData(mStoreData); assertFalse(mWifiConfigStore.areStoresPresent()); - WifiConfigStoreData readData = mWifiConfigStore.read(); - assertConfigStoreDataEqual(getEmptyStoreData(), readData); + mWifiConfigStore.read(); + + // Empty data. + assertNull(mStoreData.getUserData()); + assertNull(mStoreData.getShareData()); } /** @@ -195,40 +311,164 @@ public class WifiConfigStoreTest { */ @Test public void testReadAfterWriteWithNoUserStore() throws Exception { - WifiConfigStoreData writeData = createSingleOpenNetworkStoreData(); - mWifiConfigStore.write(true, writeData); - WifiConfigStoreData readData = mWifiConfigStore.read(); + // Setup data container. + mWifiConfigStore.registerStoreData(mStoreData); + mStoreData.setUserData(TEST_USER_DATA); + mStoreData.setShareData(TEST_SHARE_DATA); + + // Perform write for the share store file. + mWifiConfigStore.write(true); + mWifiConfigStore.read(); + // Verify data content for both user and share data. + assertEquals(TEST_SHARE_DATA, mStoreData.getShareData()); + assertNull(mStoreData.getUserData()); + } + + /** + * Verifies that a read operation will reset the data in the data container, to avoid + * any stale data from previous read. + * + * @throws Exception + */ + @Test + public void testReadWillResetStoreData() throws Exception { + // Register and setup store data. + mWifiConfigStore.registerStoreData(mStoreData); + + // Perform force write with empty data content to both user and share store file. + mWifiConfigStore.switchUserStoreAndRead(mUserStore); + mWifiConfigStore.write(true); - assertConfigStoreDataEqual(writeData, readData); + // Setup data container with some value. + mStoreData.setUserData(TEST_USER_DATA); + mStoreData.setShareData(TEST_SHARE_DATA); + + // Perform read of both user and share store file and verify data in the data container + // is in sync (empty) with what is in the file. + mWifiConfigStore.read(); + assertNull(mStoreData.getShareData()); + assertNull(mStoreData.getUserData()); + } + + /** + * Verify that a store file contained WiFi configuration store data (network list and + * deleted ephemeral SSID list) using the predefined test XML data is read and parsed + * correctly. + * + * @throws Exception + */ + @Test + public void testReadWifiConfigStoreData() throws Exception { + // Setup network list. + NetworkListStoreData networkList = new NetworkListStoreData(); + mWifiConfigStore.registerStoreData(networkList); + WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork(); + openNetwork.setIpConfiguration( + WifiConfigurationTestUtil.createDHCPIpConfigurationWithNoProxy()); + List<WifiConfiguration> userConfigs = new ArrayList<>(); + userConfigs.add(openNetwork); + + // Setup deleted ephemeral SSID list. + DeletedEphemeralSsidsStoreData deletedEphemeralSsids = + new DeletedEphemeralSsidsStoreData(); + mWifiConfigStore.registerStoreData(deletedEphemeralSsids); + String testSsid = "Test SSID"; + Set<String> ssidList = new HashSet<>(); + ssidList.add(testSsid); + + // Setup user store XML bytes. + String xmlString = String.format(TEST_DATA_XML_STRING_FORMAT, + openNetwork.configKey().replaceAll("\"", """), + openNetwork.SSID.replaceAll("\"", """), + openNetwork.shared, openNetwork.creatorUid, testSsid); + byte[] xmlBytes = xmlString.getBytes(StandardCharsets.UTF_8); + mUserStore.storeRawDataToWrite(xmlBytes); + + mWifiConfigStore.switchUserStoreAndRead(mUserStore); + WifiConfigurationTestUtil.assertConfigurationsEqualForConfigStore( + userConfigs, networkList.getUserConfigurations()); + assertEquals(ssidList, deletedEphemeralSsids.getSsidList()); } /** - * Returns an empty store data object. + * Verify that the WiFi configuration store data containing network list and deleted + * ephemeral SSID list are serialized correctly, matches the predefined test XML data. + * + * @throws Exception */ - private WifiConfigStoreData getEmptyStoreData() { - return new WifiConfigStoreData( - new ArrayList<WifiConfiguration>(), new ArrayList<WifiConfiguration>(), - new HashSet<String>()); + @Test + public void testWriteWifiConfigStoreData() throws Exception { + // Setup user store. + mWifiConfigStore.switchUserStoreAndRead(mUserStore); + + // Setup network list store data. + NetworkListStoreData networkList = new NetworkListStoreData(); + mWifiConfigStore.registerStoreData(networkList); + WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork(); + openNetwork.setIpConfiguration( + WifiConfigurationTestUtil.createDHCPIpConfigurationWithNoProxy()); + List<WifiConfiguration> userConfigs = new ArrayList<>(); + userConfigs.add(openNetwork); + networkList.setUserConfigurations(userConfigs); + + // Setup deleted ephemeral SSID list store data. + DeletedEphemeralSsidsStoreData deletedEphemeralSsids = + new DeletedEphemeralSsidsStoreData(); + mWifiConfigStore.registerStoreData(deletedEphemeralSsids); + String testSsid = "Test SSID"; + Set<String> ssidList = new HashSet<>(); + ssidList.add(testSsid); + deletedEphemeralSsids.setSsidList(ssidList); + + // Setup expected XML bytes. + String xmlString = String.format(TEST_DATA_XML_STRING_FORMAT, + openNetwork.configKey().replaceAll("\"", """), + openNetwork.SSID.replaceAll("\"", """), + openNetwork.shared, openNetwork.creatorUid, testSsid); + byte[] xmlBytes = xmlString.getBytes(StandardCharsets.UTF_8); + + mWifiConfigStore.write(true); + assertEquals(xmlBytes.length, mUserStore.getStoreBytes().length); + // Verify the user store content. + assertTrue(Arrays.equals(xmlBytes, mUserStore.getStoreBytes())); } /** - * Returns an store data object with a single open network. + * Verify that a XmlPullParserException will be thrown when reading an user store file + * containing unknown data. + * + * @throws Exception */ - private WifiConfigStoreData createSingleOpenNetworkStoreData() { - List<WifiConfiguration> configurations = new ArrayList<>(); - configurations.add(WifiConfigurationTestUtil.createOpenNetwork()); - return new WifiConfigStoreData( - configurations, new ArrayList<WifiConfiguration>(), new HashSet<String>()); + @Test(expected = XmlPullParserException.class) + public void testReadUserStoreContainedUnknownData() throws Exception { + String storeFileData = + "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" + + "<WifiConfigStoreData>\n" + + "<int name=\"Version\" value=\"1\" />\n" + + "<UnknownTag>\n" // No StoreData registered to handle this tag. + + "</UnknownTag>\n" + + "</WifiConfigStoreData>\n"; + mUserStore.storeRawDataToWrite(storeFileData.getBytes(StandardCharsets.UTF_8)); + mWifiConfigStore.switchUserStoreAndRead(mUserStore); } /** - * Returns an store data object with a single psk network. + * Verify that a XmlPullParserException will be thrown when reading the share store file + * containing unknown data. + * + * @throws Exception */ - private WifiConfigStoreData createSinglePskNetworkStoreData() { - List<WifiConfiguration> configurations = new ArrayList<>(); - configurations.add(WifiConfigurationTestUtil.createPskNetwork()); - return new WifiConfigStoreData( - configurations, new ArrayList<WifiConfiguration>(), new HashSet<String>()); + @Test(expected = XmlPullParserException.class) + public void testReadShareStoreContainedUnknownData() throws Exception { + String storeFileData = + "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" + + "<WifiConfigStoreData>\n" + + "<int name=\"Version\" value=\"1\" />\n" + + "<UnknownTag>\n" // No StoreData registered to handle this tag. + + "</UnknownTag>\n" + + "</WifiConfigStoreData>\n"; + mSharedStore.storeRawDataToWrite(storeFileData.getBytes(StandardCharsets.UTF_8)); + mWifiConfigStore.read(); } /** @@ -259,6 +499,7 @@ public class WifiConfigStoreTest { return (mStoreBytes != null); } + @Override public void writeBufferedRawData() { mStoreWritten = true; } @@ -271,4 +512,72 @@ public class WifiConfigStoreTest { return mStoreWritten; } } + + /** + * Mock data container for providing test data for the store file. + */ + private class MockStoreData implements WifiConfigStore.StoreData { + private static final String XML_TAG_TEST_HEADER = "TestHeader"; + private static final String XML_TAG_TEST_DATA = "TestData"; + + private String mShareData; + private String mUserData; + + MockStoreData() {} + + @Override + public void serializeData(XmlSerializer out, boolean shared) + throws XmlPullParserException, IOException { + if (shared) { + XmlUtil.writeNextValue(out, XML_TAG_TEST_DATA, mShareData); + } else { + XmlUtil.writeNextValue(out, XML_TAG_TEST_DATA, mUserData); + } + } + + @Override + public void deserializeData(XmlPullParser in, int outerTagDepth, boolean shared) + throws XmlPullParserException, IOException { + if (shared) { + mShareData = (String) XmlUtil.readNextValueWithName(in, XML_TAG_TEST_DATA); + } else { + mUserData = (String) XmlUtil.readNextValueWithName(in, XML_TAG_TEST_DATA); + } + } + + @Override + public void resetData(boolean shared) { + if (shared) { + mShareData = null; + } else { + mUserData = null; + } + } + + @Override + public String getName() { + return XML_TAG_TEST_HEADER; + } + + @Override + public boolean supportShareData() { + return true; + } + + public String getShareData() { + return mShareData; + } + + public void setShareData(String shareData) { + mShareData = shareData; + } + + public String getUserData() { + return mUserData; + } + + public void setUserData(String userData) { + mUserData = userData; + } + } } |