1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.wifi;
18 
19 import android.annotation.Nullable;
20 import android.net.InetAddresses;
21 import android.net.IpConfiguration;
22 import android.net.IpConfiguration.IpAssignment;
23 import android.net.IpConfiguration.ProxySettings;
24 import android.net.LinkAddress;
25 import android.net.ProxyInfo;
26 import android.net.RouteInfo;
27 import android.net.StaticIpConfiguration;
28 import android.net.Uri;
29 import android.net.wifi.SecurityParams;
30 import android.net.wifi.WifiConfiguration;
31 import android.util.Log;
32 import android.util.Pair;
33 
34 import com.android.server.wifi.util.XmlUtil;
35 import com.android.server.wifi.util.XmlUtil.IpConfigurationXmlUtil;
36 import com.android.server.wifi.util.XmlUtil.WifiConfigurationXmlUtil;
37 
38 import org.xmlpull.v1.XmlPullParser;
39 import org.xmlpull.v1.XmlPullParserException;
40 
41 import java.io.IOException;
42 import java.net.Inet4Address;
43 import java.net.InetAddress;
44 import java.util.ArrayList;
45 import java.util.Arrays;
46 import java.util.BitSet;
47 import java.util.Collections;
48 import java.util.HashSet;
49 import java.util.List;
50 import java.util.Locale;
51 import java.util.Set;
52 
53 /**
54  * Parser for major version 1 of WiFi backup data.
55  * Contains whitelists of tags for WifiConfiguration and IpConfiguration sections for each of
56  * the minor versions.
57  *
58  * Overall structure of the major version 1 XML schema:
59  * <?xml version='1.0' encoding='utf-8' standalone='yes' ?>
60  * <WifiConfigStore>
61  *  <float name="Version" value="1.0" />
62  *  <NetworkList>
63  *   <Network>
64  *    <WifiConfiguration>
65  *     <string name="ConfigKey">value</string>
66  *     <string name="SSID">value</string>
67  *     <string name="PreSharedKey" />value</string>
68  *     <string-array name="WEPKeys" num="4">
69  *      <item value="WifiConfigStoreWep1" />
70  *      <item value="WifiConfigStoreWep2" />
71  *      <item value="WifiConfigStoreWep3" />
72  *      <item value="WifiConfigStoreWep3" />
73  *     </string-array>
74  *     ... (other supported tag names in minor version 1: "WEPTxKeyIndex", "HiddenSSID",
75  *          "RequirePMF", "AllowedKeyMgmt", "AllowedProtocols", "AllowedAuthAlgos",
76  *          "AllowedGroupCiphers", "AllowedPairwiseCiphers", "Shared")
77  *    </WifiConfiguration>
78  *    <IpConfiguration>
79  *     <string name="IpAssignment">value</string>
80  *     <string name="ProxySettings">value</string>
81  *      ... (other supported tag names in minor version 1: "LinkAddress", "LinkPrefixLength",
82  *           "GatewayAddress", "DNSServers", "ProxyHost", "ProxyPort", "ProxyPac",
83  *           "ProxyExclusionList")
84  *    </IpConfiguration>
85  *   </Network>
86  *   <Network>
87  *    ... (format as above)
88  *   </Network>
89  *  </NetworkList>
90  * </WifiConfigStore>
91  */
92 class WifiBackupDataV1Parser implements WifiBackupDataParser {
93 
94     private static final String TAG = "WifiBackupDataV1Parser";
95 
96     private static final int HIGHEST_SUPPORTED_MINOR_VERSION = 3;
97 
98     // List of tags supported for <WifiConfiguration> section in minor version 0
99     private static final Set<String> WIFI_CONFIGURATION_MINOR_V0_SUPPORTED_TAGS =
100             new HashSet<String>(Arrays.asList(new String[] {
101                 WifiConfigurationXmlUtil.XML_TAG_CONFIG_KEY,
102                 WifiConfigurationXmlUtil.XML_TAG_SSID,
103                 WifiConfigurationXmlUtil.XML_TAG_PRE_SHARED_KEY,
104                 WifiConfigurationXmlUtil.XML_TAG_WEP_KEYS,
105                 WifiConfigurationXmlUtil.XML_TAG_WEP_TX_KEY_INDEX,
106                 WifiConfigurationXmlUtil.XML_TAG_HIDDEN_SSID,
107                 WifiConfigurationXmlUtil.XML_TAG_REQUIRE_PMF,
108                 WifiConfigurationXmlUtil.XML_TAG_ALLOWED_KEY_MGMT,
109                 WifiConfigurationXmlUtil.XML_TAG_ALLOWED_PROTOCOLS,
110                 WifiConfigurationXmlUtil.XML_TAG_ALLOWED_AUTH_ALGOS,
111                 WifiConfigurationXmlUtil.XML_TAG_ALLOWED_GROUP_CIPHERS,
112                 WifiConfigurationXmlUtil.XML_TAG_ALLOWED_PAIRWISE_CIPHERS,
113                 WifiConfigurationXmlUtil.XML_TAG_SHARED,
114             }));
115 
116     // List of tags supported for <WifiConfiguration> section in minor version 1
117     private static final Set<String> WIFI_CONFIGURATION_MINOR_V1_SUPPORTED_TAGS =
118             new HashSet<String>() {{
119                 addAll(WIFI_CONFIGURATION_MINOR_V0_SUPPORTED_TAGS);
120                 add(WifiConfigurationXmlUtil.XML_TAG_METERED_OVERRIDE);
121             }};
122 
123     // List of tags supported for <WifiConfiguration> section in minor version 2
124     private static final Set<String> WIFI_CONFIGURATION_MINOR_V2_SUPPORTED_TAGS =
125             new HashSet<String>() {{
126                 addAll(WIFI_CONFIGURATION_MINOR_V1_SUPPORTED_TAGS);
127                 add(WifiConfigurationXmlUtil.XML_TAG_IS_AUTO_JOIN);
128             }};
129 
130     // List of tags supported for <WifiConfiguration> section in minor version 3
131     private static final Set<String> WIFI_CONFIGURATION_MINOR_V3_SUPPORTED_TAGS =
132             new HashSet<String>() {{
133                 addAll(WIFI_CONFIGURATION_MINOR_V2_SUPPORTED_TAGS);
134                 add(WifiConfigurationXmlUtil.XML_TAG_SECURITY_PARAMS_LIST);
135                 add(WifiConfigurationXmlUtil.XML_TAG_SECURITY_PARAMS);
136                 add(WifiConfigurationXmlUtil.XML_TAG_SECURITY_TYPE);
137                 add(WifiConfigurationXmlUtil.XML_TAG_SAE_IS_H2E_ONLY_MODE);
138                 add(WifiConfigurationXmlUtil.XML_TAG_SAE_IS_PK_ONLY_MODE);
139                 add(WifiConfigurationXmlUtil.XML_TAG_IS_ADDED_BY_AUTO_UPGRADE);
140                 add(WifiConfigurationXmlUtil.XML_TAG_DELETION_PRIORITY);
141                 add(WifiConfigurationXmlUtil.XML_TAG_NUM_REBOOTS_SINCE_LAST_USE);
142             }};
143 
144     // List of tags supported for <IpConfiguration> section in minor version 0 to 3
145     private static final Set<String> IP_CONFIGURATION_MINOR_V0_V1_V2_V3_SUPPORTED_TAGS =
146             new HashSet<String>(Arrays.asList(new String[] {
147                 IpConfigurationXmlUtil.XML_TAG_IP_ASSIGNMENT,
148                 IpConfigurationXmlUtil.XML_TAG_LINK_ADDRESS,
149                 IpConfigurationXmlUtil.XML_TAG_LINK_PREFIX_LENGTH,
150                 IpConfigurationXmlUtil.XML_TAG_GATEWAY_ADDRESS,
151                 IpConfigurationXmlUtil.XML_TAG_DNS_SERVER_ADDRESSES,
152                 IpConfigurationXmlUtil.XML_TAG_PROXY_SETTINGS,
153                 IpConfigurationXmlUtil.XML_TAG_PROXY_HOST,
154                 IpConfigurationXmlUtil.XML_TAG_PROXY_PORT,
155                 IpConfigurationXmlUtil.XML_TAG_PROXY_EXCLUSION_LIST,
156                 IpConfigurationXmlUtil.XML_TAG_PROXY_PAC_FILE,
157             }));
158 
159     @Override
parseNetworkConfigurationsFromXml(XmlPullParser in, int outerTagDepth, int minorVersion)160     public List<WifiConfiguration> parseNetworkConfigurationsFromXml(XmlPullParser in,
161             int outerTagDepth, int minorVersion) throws XmlPullParserException, IOException {
162         // clamp down the minorVersion to the highest one that this parser version supports
163         if (minorVersion > HIGHEST_SUPPORTED_MINOR_VERSION) {
164             minorVersion = HIGHEST_SUPPORTED_MINOR_VERSION;
165         }
166         // Find the configuration list section.
167         XmlUtil.gotoNextSectionWithName(in, WifiBackupRestore.XML_TAG_SECTION_HEADER_NETWORK_LIST,
168                 outerTagDepth);
169         // Find all the configurations within the configuration list section.
170         int networkListTagDepth = outerTagDepth + 1;
171         List<WifiConfiguration> configurations = new ArrayList<>();
172         while (XmlUtil.gotoNextSectionWithNameOrEnd(
173                 in, WifiBackupRestore.XML_TAG_SECTION_HEADER_NETWORK, networkListTagDepth)) {
174             WifiConfiguration configuration =
175                     parseNetworkConfigurationFromXml(in, minorVersion, networkListTagDepth);
176             if (configuration != null) {
177                 Log.v(TAG, "Parsed Configuration: " + configuration.getKey());
178                 configurations.add(configuration);
179             }
180         }
181         return configurations;
182     }
183 
184     @Override
getHighestSupportedMinorVersion()185     public int getHighestSupportedMinorVersion() {
186         return HIGHEST_SUPPORTED_MINOR_VERSION;
187     }
188 
189     /**
190      * Parses the configuration data elements from the provided XML stream to a Configuration.
191      *
192      * @param in            XmlPullParser instance pointing to the XML stream.
193      * @param minorVersion  minor version number parsed from incoming data.
194      * @param outerTagDepth depth of the outer tag in the XML document.
195      * @return WifiConfiguration object if parsing is successful, null otherwise.
196      */
parseNetworkConfigurationFromXml(XmlPullParser in, int minorVersion, int outerTagDepth)197     private WifiConfiguration parseNetworkConfigurationFromXml(XmlPullParser in, int minorVersion,
198             int outerTagDepth) throws XmlPullParserException, IOException {
199         WifiConfiguration configuration = null;
200         int networkTagDepth = outerTagDepth + 1;
201         // Retrieve WifiConfiguration object first.
202         XmlUtil.gotoNextSectionWithName(
203                 in, WifiBackupRestore.XML_TAG_SECTION_HEADER_WIFI_CONFIGURATION,
204                 networkTagDepth);
205         int configTagDepth = networkTagDepth + 1;
206         configuration = parseWifiConfigurationFromXml(in, configTagDepth, minorVersion);
207         if (configuration == null) {
208             return null;
209         }
210         // Now retrieve any IP configuration info.
211         XmlUtil.gotoNextSectionWithName(
212                 in, WifiBackupRestore.XML_TAG_SECTION_HEADER_IP_CONFIGURATION, networkTagDepth);
213         IpConfiguration ipConfiguration = parseIpConfigurationFromXml(in, configTagDepth,
214                 minorVersion);
215         configuration.setIpConfiguration(ipConfiguration);
216         return configuration;
217     }
218 
219     /**
220      * Helper method to parse the WifiConfiguration object.
221      */
parseWifiConfigurationFromXml(XmlPullParser in, int outerTagDepth, int minorVersion)222     private WifiConfiguration parseWifiConfigurationFromXml(XmlPullParser in,
223             int outerTagDepth, int minorVersion) throws XmlPullParserException, IOException {
224         Pair<String, WifiConfiguration> parsedConfig =
225                 parseWifiConfigurationFromXmlInternal(in, outerTagDepth, minorVersion);
226         if (parsedConfig == null || parsedConfig.first == null || parsedConfig.second == null) {
227             return null;
228         }
229         String configKeyParsed = parsedConfig.first;
230         WifiConfiguration configuration = parsedConfig.second;
231         String configKeyCalculated = configuration.getKey();
232         if (!configKeyParsed.equals(configKeyCalculated)) {
233             // configKey is not part of the SDK. So, we can't expect this to be the same
234             // across OEM's. Just log a warning & continue.
235             Log.w(TAG, "Configuration key does not match. Retrieved: " + configKeyParsed
236                     + ", Calculated: " + configKeyCalculated);
237         }
238         return configuration;
239     }
240 
241     /**
242      * Helper method to mask out any invalid data in parsed WifiConfiguration.
243      *
244      * This is a compatibility layer added to the parsing logic to try and weed out any known
245      * issues in the backup data format from other OEM's.
246      */
clearAnyKnownIssuesInParsedConfiguration(WifiConfiguration config)247     private static void clearAnyKnownIssuesInParsedConfiguration(WifiConfiguration config) {
248         /**
249          * Fix for b/73987207. Clear any invalid bits in the bitsets.
250          */
251         // |allowedKeyManagement|
252         if (config.allowedKeyManagement.length()
253                 > WifiConfiguration.KeyMgmt.strings.length) {
254             config.allowedKeyManagement.clear(
255                     WifiConfiguration.KeyMgmt.strings.length,
256                     config.allowedKeyManagement.length());
257         }
258         // |allowedProtocols|
259         if (config.allowedProtocols.length()
260                 > WifiConfiguration.Protocol.strings.length) {
261             config.allowedProtocols.clear(
262                     WifiConfiguration.Protocol.strings.length,
263                     config.allowedProtocols.length());
264         }
265         // |allowedAuthAlgorithms|
266         if (config.allowedAuthAlgorithms.length()
267                 > WifiConfiguration.AuthAlgorithm.strings.length) {
268             config.allowedAuthAlgorithms.clear(
269                     WifiConfiguration.AuthAlgorithm.strings.length,
270                     config.allowedAuthAlgorithms.length());
271         }
272         // |allowedGroupCiphers|
273         if (config.allowedGroupCiphers.length()
274                 > WifiConfiguration.GroupCipher.strings.length) {
275             config.allowedGroupCiphers.clear(
276                     WifiConfiguration.GroupCipher.strings.length,
277                     config.allowedGroupCiphers.length());
278         }
279         // |allowedPairwiseCiphers|
280         if (config.allowedPairwiseCiphers.length()
281                 > WifiConfiguration.PairwiseCipher.strings.length) {
282             config.allowedPairwiseCiphers.clear(
283                     WifiConfiguration.PairwiseCipher.strings.length,
284                     config.allowedPairwiseCiphers.length());
285         }
286         // Add any other fixable issues discovered from other OEM's here.
287     }
288 
289     /**
290      * Parses the configuration data elements from the provided XML stream to a
291      * WifiConfiguration object.
292      * Looping through the tags makes it easy to add elements in the future minor versions if
293      * needed. Unsupported elements will be ignored.
294      *
295      * @param in            XmlPullParser instance pointing to the XML stream.
296      * @param outerTagDepth depth of the outer tag in the XML document.
297      * @param minorVersion  minor version number parsed from incoming data.
298      * @return Pair<Config key, WifiConfiguration object> if parsing is successful, null otherwise.
299      */
parseWifiConfigurationFromXmlInternal( XmlPullParser in, int outerTagDepth, int minorVersion)300     private static Pair<String, WifiConfiguration> parseWifiConfigurationFromXmlInternal(
301             XmlPullParser in, int outerTagDepth, int minorVersion)
302             throws XmlPullParserException, IOException {
303         WifiConfiguration configuration = new WifiConfiguration();
304         String configKeyInData = null;
305         Set<String> supportedTags = getSupportedWifiConfigurationTags(minorVersion);
306 
307         // Loop through and parse out all the elements from the stream within this section.
308         while (!XmlUtil.isNextSectionEnd(in, outerTagDepth)) {
309             String tagName = null;
310             Object value = null;
311             if (in.getAttributeValue(null, "name") != null) {
312                 String[] valueName = new String[1];
313                 value = XmlUtil.readCurrentValue(in, valueName);
314                 tagName = valueName[0];
315                 if (tagName == null) {
316                     throw new XmlPullParserException("Missing value name");
317                 }
318             } else {
319                 tagName = in.getName();
320                 if (tagName == null) {
321                     throw new XmlPullParserException("Unexpected null tag found");
322                 }
323             }
324 
325             // ignore the tags that are not supported up until the current minor version
326             if (!supportedTags.contains(tagName)) {
327                 Log.w(TAG, "Unsupported tag + \"" + tagName + "\" found in <WifiConfiguration>"
328                         + " section, ignoring.");
329                 continue;
330             }
331 
332             // note: the below switch case list should contain all tags supported up until the
333             // highest minor version supported by this parser
334             switch (tagName) {
335                 case WifiConfigurationXmlUtil.XML_TAG_CONFIG_KEY:
336                     configKeyInData = (String) value;
337                     break;
338                 case WifiConfigurationXmlUtil.XML_TAG_SSID:
339                     configuration.SSID = (String) value;
340                     break;
341                 case WifiConfigurationXmlUtil.XML_TAG_PRE_SHARED_KEY:
342                     configuration.preSharedKey = (String) value;
343                     break;
344                 case WifiConfigurationXmlUtil.XML_TAG_WEP_KEYS:
345                     populateWepKeysFromXmlValue(value, configuration.wepKeys);
346                     break;
347                 case WifiConfigurationXmlUtil.XML_TAG_WEP_TX_KEY_INDEX:
348                     configuration.wepTxKeyIndex = (int) value;
349                     break;
350                 case WifiConfigurationXmlUtil.XML_TAG_HIDDEN_SSID:
351                     configuration.hiddenSSID = (boolean) value;
352                     break;
353                 case WifiConfigurationXmlUtil.XML_TAG_REQUIRE_PMF:
354                     configuration.requirePmf = (boolean) value;
355                     break;
356                 case WifiConfigurationXmlUtil.XML_TAG_ALLOWED_KEY_MGMT:
357                     byte[] allowedKeyMgmt = (byte[]) value;
358                     configuration.allowedKeyManagement = BitSet.valueOf(allowedKeyMgmt);
359                     break;
360                 case WifiConfigurationXmlUtil.XML_TAG_ALLOWED_PROTOCOLS:
361                     byte[] allowedProtocols = (byte[]) value;
362                     configuration.allowedProtocols = BitSet.valueOf(allowedProtocols);
363                     break;
364                 case WifiConfigurationXmlUtil.XML_TAG_ALLOWED_AUTH_ALGOS:
365                     byte[] allowedAuthAlgorithms = (byte[]) value;
366                     configuration.allowedAuthAlgorithms = BitSet.valueOf(allowedAuthAlgorithms);
367                     break;
368                 case WifiConfigurationXmlUtil.XML_TAG_ALLOWED_GROUP_CIPHERS:
369                     byte[] allowedGroupCiphers = (byte[]) value;
370                     configuration.allowedGroupCiphers = BitSet.valueOf(allowedGroupCiphers);
371                     break;
372                 case WifiConfigurationXmlUtil.XML_TAG_ALLOWED_PAIRWISE_CIPHERS:
373                     byte[] allowedPairwiseCiphers = (byte[]) value;
374                     configuration.allowedPairwiseCiphers =
375                             BitSet.valueOf(allowedPairwiseCiphers);
376                     break;
377                 case WifiConfigurationXmlUtil.XML_TAG_SHARED:
378                     configuration.shared = (boolean) value;
379                     break;
380                 case WifiConfigurationXmlUtil.XML_TAG_METERED_OVERRIDE:
381                     configuration.meteredOverride = (int) value;
382                     break;
383                 case WifiConfigurationXmlUtil.XML_TAG_IS_AUTO_JOIN:
384                     configuration.allowAutojoin = (boolean) value;
385                     break;
386                 case WifiConfigurationXmlUtil.XML_TAG_DELETION_PRIORITY:
387                     configuration.setDeletionPriority((int) value);
388                     break;
389                 case WifiConfigurationXmlUtil.XML_TAG_NUM_REBOOTS_SINCE_LAST_USE:
390                     configuration.numRebootsSinceLastUse = (int) value;
391                     break;
392                 case WifiConfigurationXmlUtil.XML_TAG_SECURITY_PARAMS_LIST:
393                     parseSecurityParamsListFromXml(in, outerTagDepth + 1, configuration);
394                     break;
395                 default:
396                     // should never happen, since other tags are filtered out earlier
397                     throw new XmlPullParserException(
398                             "Unknown value name found: " + tagName);
399             }
400         }
401         clearAnyKnownIssuesInParsedConfiguration(configuration);
402         return Pair.create(configKeyInData, configuration);
403     }
404 
405     /**
406      * Returns a set of supported tags of <WifiConfiguration> element for all minor versions of
407      * this major version up to and including the specified minorVersion (only adding tags is
408      * supported in minor versions, removal or changing the meaning of tags requires bumping
409      * the major version and reseting the minor to 0).
410      *
411      * @param minorVersion  minor version number parsed from incoming data.
412      */
getSupportedWifiConfigurationTags(int minorVersion)413     private static Set<String> getSupportedWifiConfigurationTags(int minorVersion) {
414         switch (minorVersion) {
415             case 0:
416                 return WIFI_CONFIGURATION_MINOR_V0_SUPPORTED_TAGS;
417             case 1:
418                 return WIFI_CONFIGURATION_MINOR_V1_SUPPORTED_TAGS;
419             case 2:
420                 return WIFI_CONFIGURATION_MINOR_V2_SUPPORTED_TAGS;
421             case 3:
422                 return WIFI_CONFIGURATION_MINOR_V3_SUPPORTED_TAGS;
423             default:
424                 Log.e(TAG, "Invalid minorVersion: " + minorVersion);
425                 return Collections.<String>emptySet();
426         }
427     }
428 
429     /**
430      * Populate wepKeys array elements only if they were non-empty in the backup data.
431      *
432      * @throws XmlPullParserException if parsing errors occur.
433      */
populateWepKeysFromXmlValue(Object value, String[] wepKeys)434     private static void populateWepKeysFromXmlValue(Object value, String[] wepKeys)
435             throws XmlPullParserException, IOException {
436         String[] wepKeysInData = (String[]) value;
437         if (wepKeysInData == null) {
438             return;
439         }
440         if (wepKeysInData.length != wepKeys.length) {
441             throw new XmlPullParserException(
442                     "Invalid Wep Keys length: " + wepKeysInData.length);
443         }
444         for (int i = 0; i < wepKeys.length; i++) {
445             if (wepKeysInData[i].isEmpty()) {
446                 wepKeys[i] = null;
447             } else {
448                 wepKeys[i] = wepKeysInData[i];
449             }
450         }
451     }
452 
parseSecurityParamsFromXml( XmlPullParser in, int outerTagDepth)453     private static SecurityParams parseSecurityParamsFromXml(
454             XmlPullParser in, int outerTagDepth) throws XmlPullParserException, IOException {
455         SecurityParams params = null;
456         while (!XmlUtil.isNextSectionEnd(in, outerTagDepth)) {
457             String[] valueName = new String[1];
458             Object value = XmlUtil.readCurrentValue(in, valueName);
459             String tagName = valueName[0];
460             if (tagName == null) {
461                 throw new XmlPullParserException("Missing value name");
462             }
463             switch (tagName) {
464                 case WifiConfigurationXmlUtil.XML_TAG_SECURITY_TYPE:
465                     params = SecurityParams.createSecurityParamsBySecurityType((int) value);
466                     break;
467                 case WifiConfigurationXmlUtil.XML_TAG_SAE_IS_H2E_ONLY_MODE:
468                     if (null == params) throw new XmlPullParserException("Missing security type.");
469                     params.enableSaeH2eOnlyMode((boolean) value);
470                     break;
471                 case WifiConfigurationXmlUtil.XML_TAG_SAE_IS_PK_ONLY_MODE:
472                     if (null == params) throw new XmlPullParserException("Missing security type.");
473                     params.enableSaePkOnlyMode((boolean) value);
474                     break;
475                 case WifiConfigurationXmlUtil.XML_TAG_IS_ADDED_BY_AUTO_UPGRADE:
476                     if (null == params) throw new XmlPullParserException("Missing security type.");
477                     params.setIsAddedByAutoUpgrade((boolean) value);
478                     break;
479             }
480         }
481         return params;
482     }
483 
484     /**
485      * Populate security params list elements only if they were non-empty in the backup data.
486      *
487      * @throws XmlPullParserException if parsing errors occur.
488      */
parseSecurityParamsListFromXml( XmlPullParser in, int outerTagDepth, WifiConfiguration configuration)489     private static void parseSecurityParamsListFromXml(
490             XmlPullParser in, int outerTagDepth,
491             WifiConfiguration configuration)
492             throws XmlPullParserException, IOException {
493 
494         List<SecurityParams> paramsList = new ArrayList<>();
495         while (!XmlUtil.isNextSectionEnd(in, outerTagDepth)) {
496             switch (in.getName()) {
497                 case WifiConfigurationXmlUtil.XML_TAG_SECURITY_PARAMS:
498                     SecurityParams params = parseSecurityParamsFromXml(in, outerTagDepth + 1);
499                     if (params != null) {
500                         paramsList.add(params);
501                     }
502                     break;
503             }
504         }
505         if (!paramsList.isEmpty()) {
506             configuration.setSecurityParams(paramsList);
507         }
508     }
509 
parseProxyExclusionListString( @ullable String exclusionListString)510     private static List<String> parseProxyExclusionListString(
511             @Nullable String exclusionListString) {
512         if (exclusionListString == null) {
513             return Collections.emptyList();
514         } else {
515             return Arrays.asList(exclusionListString.toLowerCase(Locale.ROOT).split(","));
516         }
517     }
518 
519     /**
520      * Parses the IP configuration data elements from the provided XML stream to an
521      * IpConfiguration object.
522      *
523      * @param in            XmlPullParser instance pointing to the XML stream.
524      * @param outerTagDepth depth of the outer tag in the XML document.
525      * @param minorVersion  minor version number parsed from incoming data.
526      * @return IpConfiguration object if parsing is successful, null otherwise.
527      */
parseIpConfigurationFromXml(XmlPullParser in, int outerTagDepth, int minorVersion)528     private static IpConfiguration parseIpConfigurationFromXml(XmlPullParser in,
529             int outerTagDepth, int minorVersion) throws XmlPullParserException, IOException {
530         // First parse *all* of the tags in <IpConfiguration> section
531         Set<String> supportedTags = getSupportedIpConfigurationTags(minorVersion);
532 
533         String ipAssignmentString = null;
534         String linkAddressString = null;
535         Integer linkPrefixLength = null;
536         String gatewayAddressString = null;
537         String[] dnsServerAddressesString = null;
538         String proxySettingsString = null;
539         String proxyHost = null;
540         int proxyPort = -1;
541         String proxyExclusionList = null;
542         String proxyPacFile = null;
543 
544         // Loop through and parse out all the elements from the stream within this section.
545         while (!XmlUtil.isNextSectionEnd(in, outerTagDepth)) {
546             String[] valueName = new String[1];
547             Object value = XmlUtil.readCurrentValue(in, valueName);
548             String tagName = valueName[0];
549             if (tagName == null) {
550                 throw new XmlPullParserException("Missing value name");
551             }
552 
553             // ignore the tags that are not supported up until the current minor version
554             if (!supportedTags.contains(tagName)) {
555                 Log.w(TAG, "Unsupported tag + \"" + tagName + "\" found in <IpConfiguration>"
556                         + " section, ignoring.");
557                 continue;
558             }
559 
560             // note: the below switch case list should contain all tags supported up until the
561             // highest minor version supported by this parser
562             // should any tags be added in next minor versions, conditional processing of them
563             // also needs to be added in the below code (processing into IpConfiguration object)
564             switch (tagName) {
565                 case IpConfigurationXmlUtil.XML_TAG_IP_ASSIGNMENT:
566                     ipAssignmentString = (String) value;
567                     break;
568                 case IpConfigurationXmlUtil.XML_TAG_LINK_ADDRESS:
569                     linkAddressString = (String) value;
570                     break;
571                 case IpConfigurationXmlUtil.XML_TAG_LINK_PREFIX_LENGTH:
572                     linkPrefixLength = (Integer) value;
573                     break;
574                 case IpConfigurationXmlUtil.XML_TAG_GATEWAY_ADDRESS:
575                     gatewayAddressString = (String) value;
576                     break;
577                 case IpConfigurationXmlUtil.XML_TAG_DNS_SERVER_ADDRESSES:
578                     dnsServerAddressesString = (String[]) value;
579                     break;
580                 case IpConfigurationXmlUtil.XML_TAG_PROXY_SETTINGS:
581                     proxySettingsString = (String) value;
582                     break;
583                 case IpConfigurationXmlUtil.XML_TAG_PROXY_HOST:
584                     proxyHost = (String) value;
585                     break;
586                 case IpConfigurationXmlUtil.XML_TAG_PROXY_PORT:
587                     proxyPort = (int) value;
588                     break;
589                 case IpConfigurationXmlUtil.XML_TAG_PROXY_EXCLUSION_LIST:
590                     proxyExclusionList = (String) value;
591                     break;
592                 case IpConfigurationXmlUtil.XML_TAG_PROXY_PAC_FILE:
593                     proxyPacFile = (String) value;
594                     break;
595                 default:
596                     // should never happen, since other tags are filtered out earlier
597                     throw new XmlPullParserException(
598                             "Unknown value name found: " + valueName[0]);
599             }
600         }
601 
602         // Now process the values into IpConfiguration object
603         IpConfiguration ipConfiguration = new IpConfiguration();
604         if (ipAssignmentString == null) {
605             throw new XmlPullParserException("IpAssignment was missing in IpConfiguration section");
606         }
607         IpAssignment ipAssignment = IpAssignment.valueOf(ipAssignmentString);
608         ipConfiguration.setIpAssignment(ipAssignment);
609         switch (ipAssignment) {
610             case STATIC:
611                 StaticIpConfiguration.Builder builder = new StaticIpConfiguration.Builder();
612                 if (linkAddressString != null && linkPrefixLength != null) {
613                     LinkAddress linkAddress = new LinkAddress(
614                             InetAddresses.parseNumericAddress(linkAddressString), linkPrefixLength);
615                     if (linkAddress.getAddress() instanceof Inet4Address) {
616                         builder.setIpAddress(linkAddress);
617                     } else {
618                         Log.w(TAG, "Non-IPv4 address: " + linkAddress);
619                     }
620                 }
621                 if (gatewayAddressString != null) {
622                     InetAddress gateway = InetAddresses.parseNumericAddress(gatewayAddressString);
623                     RouteInfo route = new RouteInfo(null, gateway, null, RouteInfo.RTN_UNICAST);
624                     if (route.isDefaultRoute()
625                             && route.getDestination().getAddress() instanceof Inet4Address) {
626                         builder.setGateway(gateway);
627                     } else {
628                         Log.w(TAG, "Non-IPv4 default route: " + route);
629                     }
630                 }
631                 if (dnsServerAddressesString != null) {
632                     List<InetAddress> dnsServerAddresses = new ArrayList<>();
633                     for (String dnsServerAddressString : dnsServerAddressesString) {
634                         InetAddress dnsServerAddress =
635                                 InetAddresses.parseNumericAddress(dnsServerAddressString);
636                         dnsServerAddresses.add(dnsServerAddress);
637                     }
638                     builder.setDnsServers(dnsServerAddresses);
639                 }
640                 ipConfiguration.setStaticIpConfiguration(builder.build());
641                 break;
642             case DHCP:
643             case UNASSIGNED:
644                 break;
645             default:
646                 throw new XmlPullParserException("Unknown ip assignment type: " + ipAssignment);
647         }
648 
649         // Process the proxy settings next
650         if (proxySettingsString == null) {
651             throw new XmlPullParserException("ProxySettings was missing in"
652                     + " IpConfiguration section");
653         }
654         ProxySettings proxySettings = ProxySettings.valueOf(proxySettingsString);
655         ipConfiguration.setProxySettings(proxySettings);
656         switch (proxySettings) {
657             case STATIC:
658                 if (proxyHost == null) {
659                     throw new XmlPullParserException("ProxyHost was missing in"
660                             + " IpConfiguration section");
661                 }
662                 if (proxyPort == -1) {
663                     throw new XmlPullParserException("ProxyPort was missing in"
664                             + " IpConfiguration section");
665                 }
666                 if (proxyExclusionList == null) {
667                     throw new XmlPullParserException("ProxyExclusionList was missing in"
668                             + " IpConfiguration section");
669                 }
670                 ipConfiguration.setHttpProxy(
671                         ProxyInfo.buildDirectProxy(
672                                 proxyHost, proxyPort,
673                                 parseProxyExclusionListString(proxyExclusionList)));
674                 break;
675             case PAC:
676                 if (proxyPacFile == null) {
677                     throw new XmlPullParserException("ProxyPac was missing in"
678                             + " IpConfiguration section");
679                 }
680                 ipConfiguration.setHttpProxy(
681                         ProxyInfo.buildPacProxy(Uri.parse(proxyPacFile)));
682                 break;
683             case NONE:
684             case UNASSIGNED:
685                 break;
686             default:
687                 throw new XmlPullParserException(
688                         "Unknown proxy settings type: " + proxySettings);
689         }
690 
691         return ipConfiguration;
692     }
693 
694     /**
695      * Returns a set of supported tags of <IpConfiguration> element for all minor versions of
696      * this major version up to and including the specified minorVersion (only adding tags is
697      * supported in minor versions, removal or changing the meaning of tags requires bumping
698      * the major version and reseting the minor to 0).
699      *
700      * @param minorVersion  minor version number parsed from incoming data.
701      */
getSupportedIpConfigurationTags(int minorVersion)702     private static Set<String> getSupportedIpConfigurationTags(int minorVersion) {
703         switch (minorVersion) {
704             case 0:
705             case 1:
706             case 2:
707             case 3:
708                 return IP_CONFIGURATION_MINOR_V0_V1_V2_V3_SUPPORTED_TAGS;
709             default:
710                 Log.e(TAG, "Invalid minorVersion: " + minorVersion);
711                 return Collections.<String>emptySet();
712         }
713     }
714 }
715