summaryrefslogtreecommitdiff
path: root/service
diff options
context:
space:
mode:
authorRoshan Pius <rpius@google.com>2016-06-07 20:36:31 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2016-06-07 20:36:31 +0000
commit0b476346f4bfa5879d7287d7b215e0c351d9a6ab (patch)
treec07e946af79ce77e6738238fa30515255a10a978 /service
parent0eeeac78a462f1cc1eaf38bf275a52e259170b9f (diff)
parente3831b70d4a8a967fe8df5496d542a432692c434 (diff)
Merge "WifiBackupRestore: Parse older backup data"
Diffstat (limited to 'service')
-rw-r--r--service/java/com/android/server/wifi/WifiBackupRestore.java331
-rw-r--r--service/java/com/android/server/wifi/WifiNative.java26
-rw-r--r--service/java/com/android/server/wifi/WifiServiceImpl.java36
3 files changed, 385 insertions, 8 deletions
diff --git a/service/java/com/android/server/wifi/WifiBackupRestore.java b/service/java/com/android/server/wifi/WifiBackupRestore.java
index 012d95470..a3bde7b37 100644
--- a/service/java/com/android/server/wifi/WifiBackupRestore.java
+++ b/service/java/com/android/server/wifi/WifiBackupRestore.java
@@ -23,18 +23,24 @@ import android.net.ProxyInfo;
import android.net.RouteInfo;
import android.net.StaticIpConfiguration;
import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiEnterpriseConfig;
import android.util.Log;
+import android.util.SparseArray;
import android.util.Xml;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FastXmlSerializer;
+import com.android.server.net.IpConfigStore;
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.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
+import java.io.CharArrayReader;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.Inet4Address;
@@ -42,7 +48,9 @@ import java.net.InetAddress;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.BitSet;
+import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import static android.net.IpConfiguration.IpAssignment;
import static android.net.IpConfiguration.ProxySettings;
@@ -423,8 +431,8 @@ public class WifiBackupRestore {
// configKey stored in the XML data.
String configKeyCalculated = configuration.configKey();
if (!configKeyInData.equals(configKeyCalculated)) {
- Log.e(TAG, "Configuration key does not match. InData: " + configKeyInData
- + "Calculated: " + configKeyCalculated);
+ Log.e(TAG, "Configuration key does not match. Retrieved: " + configKeyInData
+ + ", Calculated: " + configKeyCalculated);
return null;
}
// Now retrieve any IP configuration info if present.
@@ -566,4 +574,323 @@ public class WifiBackupRestore {
}
Log.d(TAG, logString + ": " + xmlString);
}
+
+ /**
+ * Restore state from the older supplicant back up data.
+ * The old backup data was essentially a backup of wpa_supplicant.conf & ipconfig.txt file.
+ *
+ * @param supplicantData Raw byte stream of wpa_supplicant.conf
+ * @param ipConfigData Raw byte stream of ipconfig.txt
+ * @return list of networks retrieved from the backed up data.
+ */
+ public List<WifiConfiguration> retrieveConfigurationsFromSupplicantBackupData(
+ byte[] supplicantData, byte[] ipConfigData) {
+ if (supplicantData == null || ipConfigData == null
+ || supplicantData.length == 0 || ipConfigData.length == 0) {
+ Log.e(TAG, "Invalid supplicant backup data received");
+ return null;
+ }
+
+ SupplicantBackupMigration.SupplicantNetworks supplicantNetworks =
+ new SupplicantBackupMigration.SupplicantNetworks();
+ // Incorporate the networks present in the backup data.
+ char[] restoredAsBytes = new char[supplicantData.length];
+ for (int i = 0; i < supplicantData.length; i++) {
+ restoredAsBytes[i] = (char) supplicantData[i];
+ }
+
+ BufferedReader in = new BufferedReader(new CharArrayReader(restoredAsBytes));
+ supplicantNetworks.readNetworksFromStream(in);
+ if (mVerboseLoggingEnabled) {
+ Log.d(TAG, "Final network list from old backup:");
+ supplicantNetworks.dump();
+ }
+
+ // Retrieve corresponding WifiConfiguration objects.
+ List<WifiConfiguration> configurations = supplicantNetworks.retrieveWifiConfigurations();
+
+ // Now retrieve all the IpConfiguration objects and set in the corresponding
+ // WifiConfiguration objects.
+ SparseArray<IpConfiguration> networks =
+ IpConfigStore.readIpAndProxyConfigurations(new ByteArrayInputStream(ipConfigData));
+ if (networks != null) {
+ for (int i = 0; i < networks.size(); i++) {
+ int id = networks.keyAt(i);
+ for (WifiConfiguration configuration : configurations) {
+ // This is a dangerous lookup, but that's how it is currently written.
+ if (configuration.configKey().hashCode() == id) {
+ configuration.setIpConfiguration(networks.valueAt(i));
+ }
+ }
+ }
+ } else {
+ Log.e(TAG, "Invalid Ip config data");
+ }
+ return configurations;
+ }
+
+ /**
+ * These sub classes contain the logic to parse older backups and restore wifi state from it.
+ * Most of the code here has been migrated over from BackupSettingsAgent.
+ * This is kind of ugly text parsing, but it is needed to support the migration of this data.
+ */
+ public static class SupplicantBackupMigration {
+ /**
+ * List of keys to look out for in wpa_supplicant.conf parsing.
+ * These key values are declared in different parts of the wifi codebase today.
+ */
+ @VisibleForTesting
+ public static final String SUPPLICANT_KEY_SSID = WifiConfiguration.ssidVarName;
+ public static final String SUPPLICANT_KEY_KEY_MGMT = WifiConfiguration.KeyMgmt.varName;
+ public static final String SUPPLICANT_KEY_CLIENT_CERT = WifiEnterpriseConfig.CLIENT_CERT_KEY;
+ public static final String SUPPLICANT_KEY_CA_CERT = WifiEnterpriseConfig.CA_CERT_KEY;
+ public static final String SUPPLICANT_KEY_CA_PATH = WifiEnterpriseConfig.CA_PATH_KEY;
+ public static final String SUPPLICANT_KEY_EAP = WifiEnterpriseConfig.EAP_KEY;
+ public static final String SUPPLICANT_KEY_PSK = WifiConfiguration.pskVarName;
+ public static final String SUPPLICANT_KEY_WEP_KEY0 = WifiConfiguration.wepKeyVarNames[0];
+ public static final String SUPPLICANT_KEY_WEP_KEY1 = WifiConfiguration.wepKeyVarNames[1];
+ public static final String SUPPLICANT_KEY_WEP_KEY2 = WifiConfiguration.wepKeyVarNames[2];
+ public static final String SUPPLICANT_KEY_WEP_KEY3 = WifiConfiguration.wepKeyVarNames[3];
+ public static final String SUPPLICANT_KEY_WEP_KEY_IDX = WifiConfiguration.wepTxKeyIdxVarName;
+ public static final String SUPPLICANT_KEY_ID_STR = WifiConfigStore.ID_STRING_VAR_NAME;
+
+ /**
+ * Class for capturing a network definition from the wifi supplicant config file.
+ */
+ static class SupplicantNetwork {
+ final ArrayList<String> rawLines = new ArrayList<String>();
+ String ssid;
+ String key_mgmt;
+ String psk;
+ String[] wepKeys = new String[4];
+ String wepTxKeyIdx;
+ String id_str;
+ boolean certUsed = false;
+ boolean isEap = false;
+
+ /**
+ * Read lines from wpa_supplicant.conf stream for this network.
+ */
+ public static SupplicantNetwork readNetworkFromStream(BufferedReader in) {
+ final SupplicantNetwork n = new SupplicantNetwork();
+ String line;
+ try {
+ while (in.ready()) {
+ line = in.readLine();
+ if (line == null || line.startsWith("}")) {
+ break;
+ }
+ n.parseLine(line);
+ }
+ } catch (IOException e) {
+ return null;
+ }
+ return n;
+ }
+
+ /**
+ * Parse a line from wpa_supplicant.conf stream for this network.
+ */
+ void parseLine(String line) {
+ // Can't rely on particular whitespace patterns so strip leading/trailing.
+ line = line.trim();
+ if (line.isEmpty()) return; // only whitespace; drop the line.
+ rawLines.add(line);
+
+ // Now parse the network block within wpa_supplicant.conf and store the important
+ // lines for procesing later.
+ if (line.startsWith(SUPPLICANT_KEY_SSID)) {
+ ssid = line;
+ } else if (line.startsWith(SUPPLICANT_KEY_KEY_MGMT)) {
+ key_mgmt = line;
+ if (line.contains("EAP")) {
+ isEap = true;
+ }
+ } else if (line.startsWith(SUPPLICANT_KEY_CLIENT_CERT)) {
+ certUsed = true;
+ } else if (line.startsWith(SUPPLICANT_KEY_CA_CERT)) {
+ certUsed = true;
+ } else if (line.startsWith(SUPPLICANT_KEY_CA_PATH)) {
+ certUsed = true;
+ } else if (line.startsWith(SUPPLICANT_KEY_EAP)) {
+ isEap = true;
+ } else if (line.startsWith(SUPPLICANT_KEY_PSK)) {
+ psk = line;
+ } else if (line.startsWith(SUPPLICANT_KEY_WEP_KEY0)) {
+ wepKeys[0] = line;
+ } else if (line.startsWith(SUPPLICANT_KEY_WEP_KEY1)) {
+ wepKeys[1] = line;
+ } else if (line.startsWith(SUPPLICANT_KEY_WEP_KEY2)) {
+ wepKeys[2] = line;
+ } else if (line.startsWith(SUPPLICANT_KEY_WEP_KEY3)) {
+ wepKeys[3] = line;
+ } else if (line.startsWith(SUPPLICANT_KEY_WEP_KEY_IDX)) {
+ wepTxKeyIdx = line;
+ } else if (line.startsWith(SUPPLICANT_KEY_ID_STR)) {
+ id_str = line;
+ }
+ }
+
+ /**
+ * Raw dump of wpa_supplicant.conf lines.
+ */
+ public void dump() {
+ Log.v(TAG, "network={");
+ for (String line : rawLines) {
+ Log.v(TAG, " " + line);
+ }
+ Log.v(TAG, "}");
+ }
+
+ /**
+ * Create WifiConfiguration object from the parsed data for this network.
+ */
+ public WifiConfiguration createWifiConfiguration() {
+ if (ssid == null) {
+ // No SSID => malformed network definition
+ return null;
+ }
+ WifiConfiguration configuration = new WifiConfiguration();
+ configuration.SSID = ssid.substring(ssid.indexOf('=') + 1);
+
+ if (key_mgmt == null) {
+ // no key_mgmt specified; this is defined as equivalent to "WPA-PSK WPA-EAP"
+ configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
+ configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP);
+ } else {
+ // Need to parse the key_mgmt line
+ final String bareKeyMgmt = key_mgmt.substring(key_mgmt.indexOf('=') + 1);
+ String[] typeStrings = bareKeyMgmt.split("\\s+");
+
+ // Parse out all the key management regimes permitted for this network.
+ // The literal strings here are the standard values permitted in
+ // wpa_supplicant.conf.
+ for (int i = 0; i < typeStrings.length; i++) {
+ final String ktype = typeStrings[i];
+ if (ktype.equals("NONE")) {
+ configuration.allowedKeyManagement.set(
+ WifiConfiguration.KeyMgmt.NONE);
+ } else if (ktype.equals("WPA-PSK")) {
+ configuration.allowedKeyManagement.set(
+ WifiConfiguration.KeyMgmt.WPA_PSK);
+ } else if (ktype.equals("WPA-EAP")) {
+ configuration.allowedKeyManagement.set(
+ WifiConfiguration.KeyMgmt.WPA_EAP);
+ } else if (ktype.equals("IEEE8021X")) {
+ configuration.allowedKeyManagement.set(
+ WifiConfiguration.KeyMgmt.IEEE8021X);
+ }
+ }
+ }
+ if (psk != null) {
+ configuration.preSharedKey = psk.substring(psk.indexOf('=') + 1);
+ }
+ if (wepKeys[0] != null) {
+ configuration.wepKeys[0] = wepKeys[0].substring(wepKeys[0].indexOf('=') + 1);
+ }
+ if (wepKeys[1] != null) {
+ configuration.wepKeys[1] = wepKeys[1].substring(wepKeys[1].indexOf('=') + 1);
+ }
+ if (wepKeys[2] != null) {
+ configuration.wepKeys[2] = wepKeys[2].substring(wepKeys[2].indexOf('=') + 1);
+ }
+ if (wepKeys[3] != null) {
+ configuration.wepKeys[3] = wepKeys[3].substring(wepKeys[3].indexOf('=') + 1);
+ }
+ if (wepTxKeyIdx != null) {
+ configuration.wepTxKeyIndex =
+ Integer.valueOf(wepTxKeyIdx.substring(wepTxKeyIdx.indexOf('=') + 1));
+ }
+ if (id_str != null) {
+ String id_string = id_str.substring(id_str.indexOf('=') + 1);
+ Map<String, String> extras = WifiNative.parseNetworkExtra(id_string);
+ configuration.creatorUid =
+ Integer.valueOf(extras.get(WifiConfigStore.ID_STRING_KEY_CREATOR_UID));
+ String configKey = extras.get(WifiConfigStore.ID_STRING_KEY_CONFIG_KEY);
+ if (!configKey.equals(configuration.configKey())) {
+ Log.e(TAG, "Configuration key does not match. Retrieved: " + configKey
+ + ", Calculated: " + configuration.configKey());
+ return null;
+ }
+ }
+ return configuration;
+ }
+ }
+
+ /**
+ * Ingest multiple wifi config fragments from wpa_supplicant.conf, looking for network={}
+ * blocks and eliminating duplicates
+ */
+ static class SupplicantNetworks {
+ // One for fast lookup, one for maintaining ordering
+ final HashSet<SupplicantNetwork> mKnownNetworks = new HashSet<>();
+ final ArrayList<SupplicantNetwork> mNetworks = new ArrayList<>(8);
+
+ /**
+ * Parse the wpa_supplicant.conf file stream and add networks.
+ */
+ public void readNetworksFromStream(BufferedReader in) {
+ try {
+ String line;
+ while (in.ready()) {
+ line = in.readLine();
+ if (line != null) {
+ // Parse out 'network=' decls so we can ignore duplicates
+ if (line.startsWith("network")) {
+ SupplicantNetwork net = SupplicantNetwork.readNetworkFromStream(in);
+ // Don't propagate EAP network definitions
+ if (net.isEap) {
+ Log.v(TAG, "Skipping EAP network " + net.ssid + " / "
+ + net.key_mgmt);
+ continue;
+ }
+ Log.v(TAG, "Adding " + net.ssid + " / " + net.key_mgmt);
+ mKnownNetworks.add(net);
+ mNetworks.add(net);
+ }
+ }
+ }
+ } catch (IOException e) {
+ // whatever happened, we're done now
+ }
+ }
+
+ /**
+ * Retrieve a list of WifiConfiguration objects parsed from wpa_supplicant.conf
+ *
+ * @return
+ */
+ public List<WifiConfiguration> retrieveWifiConfigurations() {
+ ArrayList<WifiConfiguration> wifiConfigurations = new ArrayList<>();
+ for (SupplicantNetwork net : mNetworks) {
+ if (net.certUsed) {
+ // Networks that use certificates for authentication can't be restored
+ // because the certificates they need don't get restored (because they
+ // are stored in keystore, and can't be restored)
+ continue;
+ }
+
+ if (net.isEap) {
+ // Similarly, omit EAP network definitions to avoid propagating
+ // controlled enterprise network definitions.
+ continue;
+ }
+ WifiConfiguration wifiConfiguration = net.createWifiConfiguration();
+ if (wifiConfiguration != null) {
+ wifiConfigurations.add(wifiConfiguration);
+ }
+ }
+ return wifiConfigurations;
+ }
+
+ /**
+ * Raw dump of wpa_supplicant.conf lines.
+ */
+ public void dump() {
+ for (SupplicantNetwork net : mNetworks) {
+ net.dump();
+ }
+ }
+ }
+ }
}
diff --git a/service/java/com/android/server/wifi/WifiNative.java b/service/java/com/android/server/wifi/WifiNative.java
index 9828e6610..68397b774 100644
--- a/service/java/com/android/server/wifi/WifiNative.java
+++ b/service/java/com/android/server/wifi/WifiNative.java
@@ -45,6 +45,7 @@ import android.util.LocalLog;
import android.util.Log;
import com.android.internal.annotations.Immutable;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.HexDump;
import com.android.server.connectivity.KeepalivePacketData;
import com.android.server.wifi.hotspot2.NetworkDetail;
@@ -389,17 +390,26 @@ public class WifiNative {
}
public boolean setNetworkExtra(int netId, String name, Map<String, String> values) {
+ String encoded = createNetworkExtra(values);
+ if (encoded == null) {
+ return false;
+ }
+ return setNetworkVariable(netId, name, "\"" + encoded + "\"");
+ }
+
+ @VisibleForTesting
+ public static String createNetworkExtra(Map<String, String> values) {
final String encoded;
try {
encoded = URLEncoder.encode(new JSONObject(values).toString(), "UTF-8");
} catch (NullPointerException e) {
Log.e(TAG, "Unable to serialize networkExtra: " + e.toString());
- return false;
+ return null;
} catch (UnsupportedEncodingException e) {
Log.e(TAG, "Unable to serialize networkExtra: " + e.toString());
- return false;
+ return null;
}
- return setNetworkVariable(netId, name, "\"" + encoded + "\"");
+ return encoded;
}
public boolean setNetworkVariable(int netId, String name, String value) {
@@ -413,12 +423,16 @@ public class WifiNative {
}
public Map<String, String> getNetworkExtra(int netId, String name) {
- final String wrapped = getNetworkVariable(netId, name);
- if (wrapped == null || !wrapped.startsWith("\"") || !wrapped.endsWith("\"")) {
+ final String extraString = getNetworkVariable(netId, name);
+ return parseNetworkExtra(extraString);
+ }
+
+ public static Map<String, String> parseNetworkExtra(String extraSting) {
+ if (extraSting == null || !extraSting.startsWith("\"") || !extraSting.endsWith("\"")) {
return null;
}
try {
- final String encoded = wrapped.substring(1, wrapped.length() - 1);
+ final String encoded = extraSting.substring(1, extraSting.length() - 1);
// This method reads a JSON dictionary that was written by setNetworkExtra(). However,
// on devices that upgraded from Marshmallow, it may encounter a legacy value instead -
// an FQDN stored as a plain string. If such a value is encountered, the JSONObject
diff --git a/service/java/com/android/server/wifi/WifiServiceImpl.java b/service/java/com/android/server/wifi/WifiServiceImpl.java
index 3f8c26985..34c7438f9 100644
--- a/service/java/com/android/server/wifi/WifiServiceImpl.java
+++ b/service/java/com/android/server/wifi/WifiServiceImpl.java
@@ -1974,6 +1974,7 @@ public class WifiServiceImpl extends IWifiManager.Stub {
/**
* Restore state from the backed up data.
+ *
* @param data Raw byte stream of the backed up data.
*/
@Override
@@ -2002,4 +2003,39 @@ public class WifiServiceImpl extends IWifiManager.Stub {
mWifiStateMachine.syncEnableNetwork(mWifiStateMachineChannel, networkId, false);
}
}
+
+ /**
+ * Restore state from the older supplicant back up data.
+ * The old backup data was essentially a backup of wpa_supplicant.conf & ipconfig.txt file.
+ *
+ * @param supplicantData Raw byte stream of wpa_supplicant.conf
+ * @param ipConfigData Raw byte stream of ipconfig.txt
+ * @hide
+ */
+ public void restoreSupplicantBackupData(byte[] supplicantData, byte[] ipConfigData) {
+ enforceChangePermission();
+ if (mWifiStateMachineChannel == null) {
+ Slog.e(TAG, "mWifiStateMachineChannel is not initialized");
+ return;
+ }
+
+ Slog.d(TAG, "Restore supplicant backup data");
+ List<WifiConfiguration> wifiConfigurations =
+ mWifiBackupRestore.retrieveConfigurationsFromSupplicantBackupData(
+ supplicantData, ipConfigData);
+ if (wifiConfigurations == null) {
+ Slog.e(TAG, "Backup data parse failed");
+ return;
+ }
+
+ for (WifiConfiguration configuration : wifiConfigurations) {
+ int networkId =
+ mWifiStateMachine.syncAddOrUpdateNetwork(
+ mWifiStateMachineChannel, configuration);
+ if (networkId == WifiConfiguration.INVALID_NETWORK_ID) {
+ Slog.e(TAG, "Restore network failed: " + configuration.configKey());
+ }
+ mWifiStateMachine.syncEnableNetwork(mWifiStateMachineChannel, networkId, false);
+ }
+ }
}